From 168011bd5a9638756f43c8eb946d6cc78c9f94e0 Mon Sep 17 00:00:00 2001 From: Von Random Date: Sat, 20 Feb 2021 05:39:20 +0300 Subject: vq3srv, config.example.yml: massive refactor, better defaults handling --- config.example.yml | 28 +++++++++------ vq3srv | 99 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 84 insertions(+), 43 deletions(-) diff --git a/config.example.yml b/config.example.yml index 4d8e2b1..e3e79b0 100644 --- a/config.example.yml +++ b/config.example.yml @@ -2,14 +2,20 @@ user: quake3 cmd: /opt/ioquake3/ioq3ded.x86_64 exec: /home/quake3/.q3a/baseq3/autoexec.cfg gamemodes: - ffa: + # names just identifiers, feel free to mix + # map types as you see fit + default: + # the only required option here is maps, + # everything else is optional maps: [ q3dm2, q3dm3, q3dm4, q3dm5, q3dm6, q3dm7, q3dm8, q3dm9, q3dm10, q3dm11 ] + # any supported var can be set from here + # vars is optional, defaults to these: vars: - fraglimit: 30 - timelimit: 15 + fraglimit: 15 + timelimit: 5 duel: maps: [ q3dm1, q3dm2, pro-q3dm6, pro-q3dm13, @@ -22,12 +28,14 @@ gamemodes: cpma_duel: maps: [ q3dm6 ] vars: - sv_game: cpma + # mods can be used as well + fs_game: cpma fraglimit: 15 timelimit: 15 -bots: - level: '3' - names: [ - anarki, angel, crash, doom, hunter, - klesk, major, mynx, orbb, slash, xaero - ] +# defining bots here is optional, defaults +# are already defined in the script +bot_level: 3 +bot_names: [ + anarki, angel, crash, doom, hunter, + klesk, major, mynx, orbb, slash, xaero +] diff --git a/vq3srv b/vq3srv index d095f2d..a2cb338 100755 --- a/vq3srv +++ b/vq3srv @@ -7,15 +7,30 @@ from sys import exit from yaml import safe_load +GDEFAULTS = { + 'bots': { + 'level': 3, + 'names': ['anarki', 'angel', 'crash', 'doom', 'hunter', + 'klesk', 'major', 'mynx', 'orbb', 'slash', 'xaero'] + } +} +MDEFAULTS = { + 'randomize': True, + 'vars': { + 'fraglimit': 30, + 'timelimit': 15 + } +} + + 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) - parser.add_argument('-t', '--timelimit', type=int) + parser.add_argument('-m', '--gamemode', default='default') parser.add_argument('-b', '--bots', type=int, default=0) parser.add_argument('-c', '--config', default='config.yml') parser.add_argument('-B', '--bootstrap') + parser.add_argument('-d', '--debug', action="store_true") return parser.parse_args() @@ -24,11 +39,12 @@ def gen_confline(param, value, archive=False): return '{} {} "{}"\n'.format(set_cmd, param, value) -def gen_maplist(maplist): - shuffle(maplist) +def gen_maplist(maplist, randomize=True): num = 1 stmpl = 'set d{} "map {} ; set nextmap vstr d{}"\n' script = str() + if randomize: + shuffle(maplist) while maplist: nextnum = 1 if len(maplist) == 1 else num + 1 script += stmpl.format(num, maplist.pop(), nextnum) @@ -37,50 +53,67 @@ def gen_maplist(maplist): return script -def gen_addbots(count, level='3', names=list()): - shuffle(names) +def gen_addbots(count, level, names): btmpl = 'addbot {} {}\n' script = str() + shuffle(names) for bot in names[:count]: script += btmpl.format(bot, level) return script +def gen_exec(gcfg, mcfg, bvars=dict(), bot_count=0, debug=False): + cfg_data = str() + for param in bvars: + cfg_data += gen_confline(param, bvars[param], archive=True) + cfg_data += gen_maplist(mcfg['maps'], + mcfg.get('randomize', MDEFAULTS['randomize'])) + if bot_count: + bot_level = gcfg.\ + get('bots', GDEFAULTS['bots']).\ + get('level', GDEFAULTS['bots']['level']) + bot_names = gcfg.\ + get('bots', GDEFAULTS['bots']).\ + get('names', GDEFAULTS['bots']['names']) + cfg_data += gen_addbots(bot_count, bot_level, bot_names) + if debug: + print('Debug {} contents:\n{}'.format(gcfg['exec'], cfg_data)) + return + try: + with open(gcfg['exec'], 'w+') as config: + config.write(cfg_data) + except FileNotFoundError as error: + exit('Config `{}` not found!'.format(error.filename)) + + def main(): args = parse_arguments() - cfg_data, cmd_vars, bvars, svars = str(), list(), dict(), dict() + cfg_data, cmd_list, bvars = str(), list(), dict() try: with open(args.config, 'r') as config_file: - cfg = safe_load(config_file) + gcfg = safe_load(config_file) + assert {'user', 'exec', 'cmd'} <= gcfg.keys() + mcfg = gcfg['gamemodes'][args.gamemode] + assert 'maps' in mcfg if args.bootstrap: with open(args.bootstrap, 'r') as bootstrap_file: - bvars.update(safe_load(bootstrap_file)) - assert args.gamemode in cfg['gamemodes'] - exec_filename = basename(cfg['exec']) + bvars = safe_load(bootstrap_file) except FileNotFoundError as error: exit('Config `{}` not found!'.format(error.filename)) - except AssertionError: - exit('Wrong game mode `{}` specified!'.format(args.gamemode)) - - smaps = cfg['gamemodes'][args.gamemode]['maps'] - svars.update(cfg['gamemodes'][args.gamemode]['vars']) - if args.fraglimit: - svars.update({'fraglimit': args.fraglimit}) - if args.timelimit: - svars.update({'timelimit': args.timelimit}) - for param in bvars: - cfg_data += gen_confline(param, bvars[param], archive=True) + except (AssertionError, KeyError): + exit('Missing required settings in {}!'.format(args.config)) + + svars = mcfg.get('vars', MDEFAULTS['vars']) + exec_filename = basename(gcfg['exec']) + for param in svars: - cmd_vars.append('+set {} {}'.format(param, svars[param])) - cmd_vars.append('+exec {}'.format(exec_filename)) - cmd_opts = ' '.join(cmd_vars) - cfg_data += gen_maplist(smaps) - if args.bots: - cfg_data += gen_addbots(args.bots, **cfg['bots']) - - with open(cfg['exec'], 'w+') as config: - config.write(cfg_data) - system('sudo -u {} {} {}'.format(cfg['user'], cfg['cmd'], cmd_opts)) + cmd_list.append('+set {} {}'.format(param, svars[param])) + cmd_list.append('+exec {}'.format(exec_filename)) + cmd_opts = ' '.join(cmd_list) + + action = print if args.debug else system + gen_exec(gcfg, mcfg, bvars, args.bots, args.debug) + action('sudo -u {} {} {}'.format(gcfg['user'], gcfg['cmd'], cmd_opts)) if __name__ == '__main__': -- cgit v1.2.3