A simple wrapper for q3ded.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

120 lines
3.6 KiB

#!/usr/bin/env python3
from argparse import ArgumentParser
from os import system
from os.path import basename
from random import shuffle
from sys import exit
from yaml import safe_load
'bots': {
'level': 3,
'names': ['anarki', 'angel', 'crash', 'doom', 'hunter',
'klesk', 'major', 'mynx', 'orbb', 'slash', 'xaero']
'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='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()
def gen_confline(param, value, archive=False):
set_cmd = 'seta' if archive else 'set'
return '{} {} "{}"\n'.format(set_cmd, param, value)
def gen_maplist(maplist, randomize=True):
num = 1
stmpl = 'set d{} "map {} ; set nextmap vstr d{}"\n'
script = str()
if randomize:
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, names):
btmpl = 'addbot {} {}\n'
script = str()
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))
with open(gcfg['exec'], 'w+') as config:
except FileNotFoundError as error:
exit('Config `{}` not found!'.format(error.filename))
def main():
args = parse_arguments()
cfg_data, cmd_list, bvars = str(), list(), dict()
with open(args.config, 'r') as 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 = safe_load(bootstrap_file)
except FileNotFoundError as error:
exit('Config `{}` not found!'.format(error.filename))
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_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__':