vq3srv: remade to support a config; other: initial commt, some housekeeping

This commit is contained in:
Von Random 2021-02-07 18:37:53 +03:00
parent 28b5bebc40
commit 3e8932a4f2
4 changed files with 82 additions and 60 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
.vscode
config.yml
bootstrap.yml

14
bootstrap.example.yml Normal file
View file

@ -0,0 +1,14 @@
# these are saved to the q3config_server.cfg, so -B should only be used once really
# add any config variable with value here as needed,
# defaults are mostly sensible, so here is a rather small list
sv_pure: '0'
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'

31
config.example.yml Normal file
View file

@ -0,0 +1,31 @@
user: quake3
cmd: /opt/ioquake3/ioq3ded.x86_64 +exec autoexec.cfg
autoexec: /home/quake3/.q3a/baseq3/autoexec.cfg
gamemodes:
any:
vars:
fraglimit: 100
timelimit: 10
ffa:
maps: [
q3dm2, q3dm3, q3dm4, q3dm5, q3dm6,
q3dm7, q3dm8, q3dm9, q3dm10, q3dm11
]
vars:
fraglimit: 30
timelimit: 15
duel:
maps: [
q3dm1, q3dm2, pro-q3dm6, pro-q3dm13,
q3tourney1, q3tourney3, q3tourney5, q3tourney6,
pro-q3tourney2, pro-q3tourney4
]
vars:
fraglimit: 15
timelimit: 5
bots:
level: '3'
names: [
anarki, angel, crash, doom, hunter,
klesk, major, mynx, orbb, slash, xaero
]

94
vq3srv
View file

@ -3,64 +3,24 @@ from argparse import ArgumentParser
from os import system from os import system
from random import shuffle from random import shuffle
from sys import exit from sys import exit
from yaml import safe_load
# 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(): def parse_arguments():
desc = 'host a q3 server' desc = 'host a q3 server'
parser = ArgumentParser(description=desc) parser = ArgumentParser(description=desc)
parser.add_argument('-m', '--gamemode', default='ffa') parser.add_argument('-m', '--gamemode', default='ffa')
parser.add_argument('-f', '--fraglimit', type=int, default=15) parser.add_argument('-f', '--fraglimit', type=int)
parser.add_argument('-t', '--timelimit', type=int, default=10) parser.add_argument('-t', '--timelimit', type=int)
parser.add_argument('-b', '--bots', type=int, default=0) parser.add_argument('-b', '--bots', type=int, default=0)
parser.add_argument('-c', '--config', default='config.yml')
parser.add_argument('-B', '--bootstrap')
return parser.parse_args() return parser.parse_args()
def gen_confline(param, value): def gen_confline(param, value, archive=False):
return 'seta {} "{}"\n'.format(param, value) set_cmd = 'seta' if archive else 'set'
return '{} {} "{}"\n'.format(set_cmd, param, value)
def gen_maplist(maplist): def gen_maplist(maplist):
@ -87,24 +47,38 @@ def gen_addbots(count, level='3', names=list()):
def main(): def main():
args = parse_arguments() args = parse_arguments()
cfg_data = str() cfg_data, bvars, svars = str(), dict(), dict()
try: try:
assert args.gamemode in MAPLISTS with open(args.config, 'r') as config_file:
cfg = safe_load(config_file)
if args.bootstrap:
with open(args.bootstrap, 'r') as bootstrap_file:
bvars.update(safe_load(bootstrap_file))
assert args.gamemode != 'any' and args.gamemode in cfg['gamemodes']
except FileNotFoundError as error:
exit('Config `{}` not found!'.format(error.filename))
except AssertionError: except AssertionError:
exit('Wrong game mode specified!') exit('Wrong game mode `{}` specified!'.format(args.gamemode))
for param in BOOTSTRAP_OPTS: smaps = cfg['gamemodes'][args.gamemode]['maps']
cfg_data += gen_confline(param, BOOTSTRAP_OPTS[param]) svars.update(cfg['gamemodes']['any']['vars'])
cfg_data += gen_confline('fraglimit', args.fraglimit) svars.update(cfg['gamemodes'][args.gamemode]['vars'])
cfg_data += gen_confline('timelimit', args.timelimit) if args.fraglimit:
# this should be last since it launches the actual maplist svars.update({'fraglimit': args.fraglimit})
cfg_data += gen_maplist(MAPLISTS[args.gamemode]) if args.timelimit:
svars.update({'timelimit': args.timelimit})
for param in bvars:
cfg_data += gen_confline(param, bvars[param], archive=True)
for param in svars:
cfg_data += gen_confline(param, svars[param])
cfg_data += gen_maplist(smaps)
if args.bots: if args.bots:
cfg_data += gen_addbots(args.bots, **BOTS) cfg_data += gen_addbots(args.bots, **cfg['bots'])
with open(CONF, 'w+') as config: with open(cfg['autoexec'], 'w+') as config:
config.write(cfg_data) config.write(cfg_data)
system('sudo -u {} {} {}'.format(USER, BIN, PARAM)) system('sudo -u {} {}'.format(cfg['user'], cfg['cmd']))
if __name__ == '__main__': if __name__ == '__main__':