From 5a634016cf218d279628620d89b247f0e1dbb9eb Mon Sep 17 00:00:00 2001 From: Von Random Date: Sun, 7 Feb 2021 05:36:55 +0300 Subject: [PATCH] q3srv: implement a simple wrapper script for q3ded --- q3srv | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100755 q3srv diff --git a/q3srv b/q3srv new file mode 100755 index 0000000..0b4f149 --- /dev/null +++ b/q3srv @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +from argparse import ArgumentParser +from os import system +from random import shuffle +from sys import exit + +# TODO: move this shit to a yaml config somewhere in /etc +USER = 'quake3' # linux user, for sudo -u +BIN = '/usr/bin/q3ded' +CONF = '/srv/q3/.q3a/baseq3/autoexec.cfg' +PARAM = '+exec autoexec.cfg' + +MAPLISTS = { + 'ffa': [ + 'q3dm2', 'q3dm3', 'q3dm4', 'q3dm5', 'q3dm6', + 'q3dm7', 'q3dm8', 'q3dm9', 'q3dm10', 'q3dm11' + ], + 'duel': [ + 'q3dm1', 'q3dm2', 'pro-q3dm6', 'pro-q3dm13', + 'q3tourney1', 'q3tourney3', 'q3tourney5', 'q3tourney6', + 'pro-q3tourney2', 'pro-q3tourney4' + ], + 'ctf': list() +} +BOTS = { + 'level': '3', + 'names': [ + 'Anarki', 'Angel', 'Crash', 'Doom', 'Hunter', + 'Klesk', 'Major', 'Mynx', 'Orbb', 'Slash', 'Xaero' + ] +} + + +# these are saved to the q3config_server.cfg on the first run +# no point in keeping them here afterwards +BOOTSTRAP_OPTS = dict() +# BOOTSTRAP_OPTS = { +# 'sv_pure': '0', # enable client side mods, like better fonts or graphics +# 'sv_maxclients': '8', +# 'sv_hostname': 'myhost.tld' +# +# # this will let your server send maps to clients +# # the downloads are attempted from http://%sv_dlURL%/baseq3/mapname.pk3 +# # note the protocol - no support for https, +# # as well as the need to have baseq3 as a part of the path +# # note that clients have to seta cl_allowDownload 1 for that to work +# 'sv_dlURL': 'anyhost.tld/q3', +# 'sv_allowDownloads': '1' +# } + + +def parse_arguments(): + desc = 'host a q3 server' + parser = ArgumentParser(description=desc) + parser.add_argument('-m', '--gamemode', default='ffa') + parser.add_argument('-f', '--fraglimit', type=int, default=15) + parser.add_argument('-t', '--timelimit', type=int, default=10) + parser.add_argument('-b', '--bots', type=int, default=0) + return parser.parse_args() + + +def gen_confline(param, value): + return 'seta {} "{}"\n'.format(param, value) + + +def gen_maplist(maplist): + shuffle(maplist) + num = 1 + stmpl = 'set d{} "map {} ; set nextmap vstr d{}"\n' + script = str() + while maplist: + nextnum = 1 if len(maplist) == 1 else num + 1 + script += stmpl.format(num, maplist.pop(), nextnum) + num += 1 + script += 'vstr d1\n' + return script + + +def gen_addbots(count, level='3', names=list()): + shuffle(names) + btmpl = 'addbot {} {}\n' + script = str() + for bot in names[:count]: + script += btmpl.format(bot, level) + return script + + +def main(): + args = parse_arguments() + cfg_data = str() + try: + assert args.gamemode in MAPLISTS + except AssertionError: + exit('Wrong game mode specified!') + + for param in BOOTSTRAP_OPTS: + cfg_data += gen_confline(param, BOOTSTRAP_OPTS[param]) + cfg_data += gen_confline('fraglimit', args.fraglimit) + cfg_data += gen_confline('timelimit', args.timelimit) + # this should be last since it launches the actual maplist + cfg_data += gen_maplist(MAPLISTS[args.gamemode]) + if args.bots: + cfg_data += gen_addbots(args.bots, **BOTS) + + with open(CONF, 'w+') as config: + config.write(cfg_data) + system('sudo -u {} {} {}'.format(USER, BIN, PARAM)) + + +if __name__ == '__main__': + main()