-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbb_dst
More file actions
executable file
·346 lines (290 loc) · 11.6 KB
/
bb_dst
File metadata and controls
executable file
·346 lines (290 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#!/usr/bin/env python3
import os
import time
import sys
import logging
from os import path
from datetime import datetime
from dataclasses import dataclass
# set up dependencies
__ROOT__ = path.dirname(path.realpath(__file__))
sys.path.insert(0, path.join(__ROOT__, "python"))
sys.path.insert(0, path.join(__ROOT__, "scripts/python"))
from dst import common, cluster, mods
from bbcode.common import parser, log, bash
from bbcode.common.parser import root_parser, sub_parser, ex_options
@parser.add_option("--preset", type=int, default=1,
help="Use preset to config mods.")
@parser.add_option("-y", "--yes", action="store_true",
help="accept all options.")
@parser.add_option("-u", "--update", action="store_true",
help="update mods after configure.")
@parser.register_command("mods", help="Mod Manager")
def dst_mods(args):
""" Mod Manager: add/del/print mod info. """
mods.config_mods(args)
if args.update:
dst_mods_update()
@parser.register_command("status", help="Summary DST")
def dst_status(args):
""" Print Clusters' Information. """
logging.info("Cluster Running Status:")
for c in common.ALL_CLUSTERS:
status = f"RUNNING" if is_dst_running(c) \
else f"{log.C_ERROR}STOPPED{log.C_INFO}"
logging.info(f"{c:>10}: {status}{' [X]' if common.CLUSTER == c else ''}")
print()
cluster_ini_path = path.join(common.CLUSTER_PATH, "cluster.ini")
if not path.exists(cluster_ini_path):
logging.warning("Not Configured, run config command to setup.")
return
conf = cluster.get_api_config({
"name": common.CLUSTER,
"desc": "None",
"max_players": "Unknown",
"password": None,
"server_port": "Unknown",
})
keys = ["name", "desc", "max_players", "password", "server_port"]
max_key_len = max([len(s) for s in keys])
logging.info("Cluster [%s] Configurations:" % common.CLUSTER)
logging.info("Location: %s" % common.CLUSTER_PATH)
for k in keys:
logging.info("· {:>{}}: {}".format(k, max_key_len, conf[k]))
print()
logging.info("Join Room Now:")
logging.info("1. Search room name: %s in DST room page" % conf["name"])
logging.info("2. Press `~` and enter commmand(use LAN/WAN ip):")
logging.info("\tc_connect(\"192.168.2.163\", %s)" % conf["server_port"])
logging.info("\tc_connect(\"114.66.27.85\", %s)" % conf["server_port"])
def steam_install(args):
with bash.enter(common.STEAM_CMD_HOME):
# If running a 64bit OS
bash.shell_exec("sudo dpkg --add-architecture i386")
bash.shell_exec("sudo apt-get update")
# If running a 64bit OS
bash.shell_exec("sudo apt-get install -y lib32gcc1")
# If running a 64bit OS
bash.shell_exec("sudo apt-get install -y lib32stdc++6")
# If running a 32bit OS
# sudo apt-get install -y libgcc1
# new linux system may miss this lib
bash.shell_exec("sudo apt-get install -y libcurl4-gnutls-dev:i386")
bash.shell_exec("wget https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz")
bash.shell_exec("tar -xvzf steamcmd_linux.tar.gz")
assert path.exists(common.STEAM_CMD)
def dst_mods_update():
if not path.exists(common.DST_BIN_HOME):
logging.error("DST not installed, use update command first.")
return
logging.info("Updating dst mods...")
mods.update_server_mods_setup()
with bash.enter(common.DST_BIN_HOME):
bash.shell_exec(
"./dontstarve_dedicated_server_nullrenderer",
"-cluster", common.CLUSTER,
"-only_update_server_mods",
check_error=False,
)
if mods.is_ready():
logging.info("Updated DST mods succeed.")
else:
logging.error("Mod download failed.")
@parser.add_option("-f", "--force", action="store_true",
help="force to update steamcmd")
@parser.add_option("-m", "--mods", action="store_true",
help="update mods together.")
@parser.register_command("update", help="DST Update")
def dst_update(args):
if args.force or (not path.exists(common.STEAM_CMD)):
steam_install(args)
is_running = is_dst_running()
if is_running:
dst_stop(args)
logging.info("Updating don't starve together...")
bash.shell_exec(
common.STEAM_CMD,
"+@ShutdownOnFailedCommand 1",
"+@NoPromptForPassword 1",
"+force_install_dir", common.DST_HOME,
"+login anonymous",
"+app_update 343050 validate +quit",
)
logging.info("Updated don't starve together...")
if args.mods:
dst_mods_update()
if is_running:
dst_start(args)
@parser.add_option("-y", "--yes", action="store_true",
help="accept all options.")
@parser.add_option("-u", "--update", action="store_true",
help="update mods after configure.")
@parser.register_command("config", help="Cluster Config")
def cluster_config(args):
if not path.exists(common.STEAM_CMD):
steam_install(args)
os.makedirs(common.CLUSTER_PATH, exist_ok=True)
bash.shell_exec("ln -sf %s/adminlist.txt %s/" % (
common.TEMP_ROOT, common.CLUSTER_PATH))
bash.shell_exec("mkdir -p", common.MASTER_PATH)
bash.shell_exec(
("cp -f %s/worldgenoverride_much.lua "
"%s/worldgenoverride.lua") % (
common.TEMP_ROOT, common.MASTER_PATH))
bash.shell_exec("mkdir -p", common.CAVES_PATH)
bash.shell_exec(
("cp -f %s/worldgenoverride_much_cave.lua "
"%s/worldgenoverride.lua") % (
common.TEMP_ROOT, common.CAVES_PATH))
cluster.config(args)
if args.update:
dst_mods_update()
def follow_file(file, sleep_sec=0.1):
""" Yield each line from a file as they are written.
`sleep_sec` is the time to sleep after empty reads. """
line = ''
while True:
tmp = file.readline()
if tmp is not None and tmp != "":
line += tmp
if line.endswith("\n"):
yield line
line = ''
elif sleep_sec:
time.sleep(sleep_sec)
@parser.add_option("-a", "--attach",
action="store_true", help="Attach Log Command Line")
@parser.add_option("-f", "--follow",
action="store_true", help="Follow log file")
@parser.register_command("log", help="Show DST Log")
def dst_log(args):
if getattr(args, "attach", False):
bash.shell_exec("screen -dr %s" % common.SCREEN_NAME)
else:
# with open("%s/dst.log" % common.LOG_DIR, "r") as f:
# for l in follow_file(f):
# print(l, end='')
cmd = "+F" if getattr(args, "follow", False) else ""
bash.shell_exec(
"less %s %s/dst.log" % (cmd, common.LOG_DIR),
check_error=False)
def is_dst_running(cluster = common.CLUSTER, quiet=True):
# check dst running failed and port in use?
out = bash.shell_output(
"screen -list | grep %s" % common.get_screen_name(cluster),
quiet=quiet)
return bool(out)
@parser.add_option("-l", "--log",
action="store_true", help="Show log after started.")
@parser.register_command("start", help="Start DST")
def dst_start(args):
if is_dst_running():
logging.info("DST Server has been started.")
return
def _check_file(fpath):
if not path.exists(fpath):
logging.error("Missing file: %s" % fpath)
logging.error("May you run config command first.")
sys.exit(0)
with bash.enter(common.CLUSTER_PATH):
_check_file("cluster.ini")
_check_file("cluster_token.txt")
_check_file("Master/server.ini")
_check_file("Caves/server.ini")
dst_bin = path.join(common.DST_BIN_HOME,
"dontstarve_dedicated_server_nullrenderer")
_check_file(dst_bin)
if not mods.is_ready():
logging.error("Missing mod, you need to download mod first.")
logging.error("May you run `update -m` or `mods -yu`.")
sys.exit(0)
log_file = path.join(
common.LOG_DIR,
"dst.log.%s" % datetime.today().strftime('%m-%d_%H.%M.%S'),
)
now = time.time()
for f in os.listdir(common.LOG_DIR):
fpath = path.join(common.LOG_DIR, f)
if os.path.isfile(f) and f.startswith("dst.log."):
if os.stat(fpath).st_mtime < now - 7 * 86400:
os.remove(fpath)
logging.info("Starting dst server...")
bash.shell_exec("screen -dmS", common.SCREEN_NAME)
with bash.enter(common.DST_BIN_HOME):
bash.shell_exec(
"screen -dr", common.SCREEN_NAME,
"-X stuff \"cd %s && ./start.sh %s %s %s\\n\"" % (
common.TEMP_ROOT, common.DST_BIN_HOME,
common.CLUSTER, log_file),
)
bash.shell_exec("ln -sf %s %s/dst.log" % (
log_file, common.LOG_DIR))
logging.info("Started dst server.")
if getattr(args, "log", False):
time.sleep(1)
args.follow = True
args.attach = False
dst_log(args)
# mods.summary()
@parser.register_command("restart", help="Restart DST")
def dst_restart(args):
dst_stop(args)
dst_start(args)
@parser.register_command("stop", help="Stop DST")
def dst_stop(args):
if not is_dst_running():
logging.info("DST server has been stopped.")
return
logging.info("Stoping dst server...")
bash.shell_exec("screen -dr %s -X stuff \"c_shutdown()\\n\"" % common.SCREEN_NAME)
bash.shell_exec("screen -dr %s -X stuff \"c_shutdown()\\n\"" % common.SCREEN_NAME)
time.sleep(2)
bash.shell_exec("screen -dr %s -X quit" % common.SCREEN_NAME)
logging.info("Stoped dst server...")
@parser.register_command("remove", help="Remove Cluster")
def dst_remove(args):
""" Remove Cluster Data. """
logging.error("This operation triggers dangerous zone!")
logging.info("Abort this operation or you exactly know this meaning!")
logging.info("remove command will delete the cluster data directory with no recovery:")
logging.info("\t" + common.CLUSTER_PATH)
while True:
logging.info("Enter [Y] to confirm the delete operation, or [N] to abort.")
input_str = input("> ")
if input_str.upper() in ["Y", "YES"]:
break
elif input_str.upper() in ["N", "NO"]:
logger.info("Abort.")
sys.exit(0)
if is_dst_running():
dst_stop(args)
logging.info("Start to delete cluster: %s..." % common.CLUSTER)
while True:
ugc_mods = path.join(common.DST_HOME, "ugc_mods", common.CLUSTER)
logging.info("Remove all data in directory: %s" % common.CLUSTER_PATH)
logging.info("Remove all data in directory: %s" % ugc_mods)
logging.info("Enter [Y] to confirm, or [N] to abort.")
input_str = input("> ")
if input_str.upper() in ["Y", "YES"]:
break
elif input_str.upper() in ["N", "NO"]:
logger.info("Abort.")
sys.exit(0)
bash.shell_exec("rm -rf", common.CLUSTER_PATH)
bash.shell_exec("rm -rf", ugc_mods)
logging.info("Remove cluster: %s done." % common.CLUSTER)
@parser.register_main()
def main(args):
"""
Command line tools to manage DST's status.
Follow these steps to initialize a new dst server:
1. update (install and update steam & dst cmd)
2. config (setup cluster, including port, mods, etc.)
3. mods -yu (download or update dst related mods)
4. start/restart (start dst server and enjoy it.)
"""
pass
if __name__ == "__main__":
log.Init(log.DEBUG)
common.env_check()
parser.Parse(main_print_help=True)