ql-lg-server: Quake Live LG vampiric practice server in Docker
One-command setup (SteamCMD + minqlx build), .env settings with a listed/unlisted switch, three factories (lgvamp, lgvamp_nolag, lgvamp100), warmup-text and welcome minqlx plugins, watchdog/backup/daily-restart, docs for admins and players. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
commit
bc5bd09c15
25 files changed
+1009
No files matched your search
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Quake Live RCON over ZMQ. Runs inside the container (through ./rcon), so the password never
|
||||
leaves the server machine.
|
||||
./rcon status one command
|
||||
./rcon interactive prompt (Ctrl+D or 'exit' to quit)
|
||||
Protocol (id's zmq_rcon.py): DEALER socket, ZMQ PLAIN auth user 'rcon', zap_domain 'rcon',
|
||||
first message 'register', then each message is a console command; console output comes back.
|
||||
"""
|
||||
import os, re, sys, uuid
|
||||
import zmq
|
||||
|
||||
PW = open("/ql/secrets/rcon-password.txt").read().strip()
|
||||
HOST = "tcp://127.0.0.1:" + os.environ.get("QL_RCON_PORT", "28960")
|
||||
COLOR = re.compile(r"\^\d")
|
||||
|
||||
def connect():
|
||||
s = zmq.Context.instance().socket(zmq.DEALER)
|
||||
s.plain_username = b"rcon"
|
||||
s.plain_password = PW.encode()
|
||||
s.zap_domain = b"rcon"
|
||||
s.setsockopt(zmq.IDENTITY, uuid.uuid4().hex.encode())
|
||||
s.setsockopt(zmq.LINGER, 0)
|
||||
s.connect(HOST)
|
||||
s.send(b"register")
|
||||
return s
|
||||
|
||||
def drain(s, first_wait, quiet=0.6):
|
||||
out, wait = [], first_wait
|
||||
while s.poll(int(wait * 1000)):
|
||||
out.append(s.recv().decode(errors="replace"))
|
||||
wait = quiet
|
||||
return "".join(out)
|
||||
|
||||
def clean(text):
|
||||
# never show the password, whatever the server echoes
|
||||
lines = [COLOR.sub("", l) for l in text.splitlines()
|
||||
if "zmq_rcon_password" not in l and PW not in l]
|
||||
return "\n".join(lines).strip()
|
||||
|
||||
def run(s, cmd):
|
||||
drain(s, first_wait=0.3, quiet=0.2) # drop console chatter that arrived in between
|
||||
s.send(cmd.encode())
|
||||
out = clean(drain(s, first_wait=3.0))
|
||||
return out or "(no output: ok, or server down / map loading / wrong password)"
|
||||
|
||||
def main(argv):
|
||||
s = connect()
|
||||
if len(argv) > 1:
|
||||
print(run(s, " ".join(argv[1:]))); return 0
|
||||
print("Quake Live rcon - server commands (status, addbot hunter 5, kick allbots, map thunderstruck lgvamp, say hi). Ctrl+D to quit.")
|
||||
while True:
|
||||
try:
|
||||
line = input("ql> ").strip()
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print(); return 0
|
||||
if line in ("exit", "quit"):
|
||||
return 0
|
||||
if line:
|
||||
print(run(s, line))
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
Reference in new issue
Block a user