# How it works (and why) Notes for anyone changing this setup. Each point below was hit while building it. ## Permanent warmup instead of a match Real LG duel servers (x76) report `PRE_GAME` (warmup) even with 9 players on: everyone plays FFA in warmup, which never ends. That's exactly what LG practice needs: no countdown, no score resets, no map reloads. - `sv_warmupReadyPercentage 2`: a match would need 200% of players ready, so F3 / `/readyup` can never start one. This cvar is locked, so it can only be set in the factory, not in server.cfg. - `g_warmupReadyDelay 0`: no auto-start timer. Approaches that don't work: - `g_doWarmup 0` (always in a match): QL **forfeits a running match when fewer than 2 players are on, even with `g_allowForfeit 0`**. With one player you get reload → countdown → a few seconds of play → forfeit, every 15–20 s. A permanent bot (`bot_minplayers 2`) hides that, but players find it weird. - Auto-start (`sv_warmupReadyPercentage 0` + a delay): a countdown and score reset every time a second player joins. `fraglimit 50`, `timelimit 15`, `g_overtime 0` are x76's values; they only matter if a match ever starts. ## The warmup text (minqlx plugin `nowarmuptext`) In warmup the client draws "The match will begin when more players join" / "Press F3 to ready yourself". No cvar hides it. The client draws it only while configstring `CS_WARMUP` (index 5) holds a time (warmup sets `\time\-1`). The plugin hooks `set_configstring` and sends an empty value for index 5 instead. The server itself stays in warmup. The first map is set up before plugins load, so the plugin also clears the value once when it loads. Check: `docker logs ql-lg | grep nowarmuptext` shows `CS_WARMUP=''`. minqlx is started with only our plugins (`qlx_plugins "nowarmuptext,welcome"`). Its default plugin list needs a Redis server, which this setup doesn't run. QL's built-in `access.txt` covers admins and bans without it. ## Vampiric, measured `g_vampiricDamage 1` heals exactly 1:1. Measured with `g_debugVampiricDamage 1` and two bots: an LG hit does 6 damage and the attacker gains 6. When both players hit each other at the same time, neither health changes. Spawning with 200 HP (`g_startingHealthBonus 100`) means health above 100 decays ~1/s, which is where the "I'm losing health" feeling comes from. `lgvamp100` removes it. (The stock `_training` factory uses 0.75.) ## Antilag `g_lagHax 1`, `g_lagHaxMs 240`, `g_lagHaxMsec 10`, `g_lagHaxHistory 12`: hits are checked against where the target was on the shooter's screen, for pings up to 240 ms. `lgvamp_nolag` sets `g_lagHax 0`. ## The RCON password leak qzeroded's built-in ZMQ auth logger (CZMQ zauth) prints `zauth: ... password=` in plain text on every RCON login. The compose command pipes the server's output through `grep -v zauth:` so those lines never reach `docker logs`. `TINI_KILL_PROCESS_GROUP=1` makes stop and restart signals reach the server through that pipe. If the server dies, grep sees end-of-file, the container exits and Docker restarts it. Two more places the password shows up: `home/baseq3/zmqpass.txt` (QL writes it on every start) and the output of `cvarlist`. `tools/qlrcon.py` filters it out of anything it prints. ## Unlisted servers answer no queries With `sv_master 0`, QL ignores `getstatus`, `getinfo` and Steam A2S queries (`sv_serverType` doesn't change that). So the health check (`tools/qlq.py`) uses the connect handshake: send `getchallenge` and the reply is `print "No Steam auth token."`. That works for listed and unlisted servers. Player info comes from RCON `status`, where bots have the address `bot`; that's how the daily restart can tell whether a human is on. ## Small things - SteamCMD needs `+@sSteamCmdForcePlatformType linux`, or it fails with "Missing configuration". - minqlx must be built against the same Python as the image (Debian bookworm, 3.11). setup.sh builds it in a bookworm container, pinned to a tested commit. - The minqlx start script turns on `zmq_stats_enable` (TCP, all interfaces). The compose command turns it off again. - A tied FFA never ends on the timelimit (it goes to overtime); irrelevant in permanent warmup. - Cvars changed over RCON survive map changes until the container restarts. - `home/baseq3/access.txt` is rewritten by QL on map load/shutdown (see ADMIN.md). - Polling RCON from several clients at once while the server restarts once coincided with a crash. Poll from one client, slowly.