cs16-dm-server: CS 1.6 deathmatch server in Docker
One-command setup (SteamCMD + pinned, checksum-verified ReHLDS / ReGameDLL / Metamod-R / AMXX / ReAPI / ReUnion / CSDM-ReAPI / YaPB / GunGame), .env settings, ./cs menu (map, DM/GunGame mode, bots, kill counter, restart, update), own plugins (extras, rtv, mode switch), all-time kill counter, watchdog + nightly backup, docs for admins and players. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
commit
7e9cbee14d
33 files changed
+1890
No files matched your search
@@ -0,0 +1,85 @@
|
||||
# How it works
|
||||
|
||||
## The stack
|
||||
|
||||
| Part | Version | Why |
|
||||
|---|---|---|
|
||||
| HLDS app 90, beta `steam_legacy` | (frozen branch) | Valve's dedicated server. ReHLDS needs engine build ≤ 8684, so the legacy branch is used |
|
||||
| [ReHLDS](https://github.com/rehlds/ReHLDS) | 3.15.0.896 | drop-in engine with fixes and better performance; replaces `engine_i486.so`, `hlds_linux` etc. |
|
||||
| [ReGameDLL_CS](https://github.com/rehlds/ReGameDLL_CS) | 5.30.0.814 | drop-in game logic (`cstrike/dlls/cs.so`) with `mp_freeforall`, `mp_forcerespawn` and more |
|
||||
| [Metamod-R](https://github.com/rehlds/Metamod-R) | 1.3.0.149 | plugin loader; `liblist.gam` points the game at it |
|
||||
| [AMX Mod X](https://www.amxmodx.org) | 1.10.0.5486 | scripting platform for the plugins |
|
||||
| [ReAPI](https://github.com/rehlds/ReAPI) | 5.29.0.358 | AMXX module that gives plugins access to ReHLDS/ReGameDLL hooks |
|
||||
| [ReUnion](https://github.com/rehlds/ReUnion) | 0.2.0.25 | lets non-Steam clients (protocol 47/48) join |
|
||||
| [CSDM-ReAPI](https://github.com/wopox1337/CSDM-ReAPI) | 1.0.8 | deathmatch: respawn, gun menu, spawn points, spawn protection |
|
||||
| [YaPB](https://github.com/yapb/yapb) | 4.4.957 | bots (Metamod plugin) |
|
||||
| [GunGame](https://github.com/fysiks1/amxx-gungame_cstrike) | 2.13c | GunGame mode, compiled from source by setup |
|
||||
|
||||
`tools/versions.txt` pins every download by URL and sha256. If anything upstream changes a file, setup stops instead of installing it.
|
||||
|
||||
## Install flow (`setup.sh`)
|
||||
|
||||
1. Creates `.env` and asks the questions. Generates `secrets/rcon-password.txt` and `secrets/reunion-salt.txt`, which are kept forever. The salt derives non-Steam players' IDs, so a new salt would reset their stats and admin entries.
|
||||
2. Builds the image: Debian bookworm, 32-bit libs, SteamCMD, unzip/xz.
|
||||
3. Downloads the game with SteamCMD, anonymously, with up to 4 tries, because app 90 often fails the first time. It runs `validate` only on the very first download. Later, validate would put back Valve's version of files you edited, and it also silently removes ReHLDS and Metamod.
|
||||
4. `tools/install-addons.sh` runs inside the image (repo mounted read-only, only `dl/` writable):
|
||||
- downloads and checks the pinned files
|
||||
- unpacks ReHLDS, then sets its exec bits, which the zip loses
|
||||
- unpacks ReGameDLL and comments out the `sv_alltalk 0` that its `game.cfg` would force on every map
|
||||
- unpacks Metamod and points `liblist.gam` at it (a copy of the original is kept as `liblist.gam.orig`)
|
||||
- unpacks AMXX, ReAPI and ReUnion. ReUnion gets the salt and `IDClientsLimit 0`, so several non-Steam players with the same placeholder ID can all join.
|
||||
- unpacks CSDM-ReAPI and the spawn files. de_prodigy and de_train are in CSDM 2.1's old 6-field format and get converted to the 10 fields CSDM-ReAPI reads, because it silently skips lines with any other count. Symlinks one folder up let GunGame find the files too.
|
||||
- unpacks YaPB, with `chmod 775` on its folders (the tarball's folders have no x bit, so the bots couldn't save their path caches), `yb_csdm_mode 2` (FFA-aware) and `yb_quota_mode fill`
|
||||
- copies the GunGame source, lang file and sounds, plus the 14 announcer sounds
|
||||
- copies `cstrike/` from the repo over the game
|
||||
- compiles `rtv`, `esef_extras`, `esef_mode` and `gungame` with AMXX's `amxxpc`
|
||||
- leaves exactly one of `plugins-CSDM_ReAPI.ini` / `plugins-gungame.ini` active. It keeps the mode that was running before, or uses `CS_MODE` on the first install.
|
||||
- symlinks `steamclient.so` into `~/.steam/sdk32`. Without it you get "Unable to initialize Steam" in Docker.
|
||||
- saves and restores what people edit: `users.ini`, `amxx.cfg`, `csdm/config.ini`. `mapcycle.txt` and `custom.cfg` are only written on the first install.
|
||||
5. Stops the old container if it's running (the engine files are in use), starts the server, and waits until it answers an A2S query.
|
||||
|
||||
## At every container start (`tools/start.sh`)
|
||||
|
||||
- writes `cstrike/env.cfg` (hostname) and `cstrike/secrets.cfg` (RCON + join password, mode 600) from `.env` and `secrets/`
|
||||
- sets `yb_quota` / `yb_difficulty` in `addons/yapb/conf/yapb.cfg` from `CS_BOTS` / `CS_BOT_DIFFICULTY`
|
||||
- runs `hlds_run -game cstrike -port $CS_PORT +maxplayers $CS_MAXPLAYERS +map $CS_MAP -pingboost 2 -norestart +log on`
|
||||
|
||||
`server.cfg` runs `env.cfg`, then `secrets.cfg`, then `custom.cfg`, on every map.
|
||||
|
||||
## Game modes
|
||||
|
||||
`esef_mode` (own plugin) is a server command: `esef_mode`, `esef_mode dm` or `esef_mode gg`. AMXX reads every `configs/plugins-*.ini` at each map change, so a mode is simply which plugin list is named `.ini` and which is named `.ini.off`. The plugin renames them, then reloads the map. The rename stays on disk, so the mode survives restarts.
|
||||
|
||||
GunGame settings (`configs/gungame.cfg`):
|
||||
- free for all: `gg_tk_penalty -1`, plus `mp_freeforall 1` in `gg_map_setup`
|
||||
- bomb and hostages blocked
|
||||
- 30 s knife warmup, 1 s respawn, 30 min maps
|
||||
- 2 kills per level
|
||||
|
||||
`gg_map_setup` also switches off the respawn cvars CSDM leaves behind (`mp_forcerespawn 0`, `mp_respawn_immunitytime 0`). CSDM sets them again in dm mode.
|
||||
|
||||
**Important:** GunGame needs the AMXX modules `cstrike`, `fakemeta` and `hamsandwich`. They're listed in `configs/modules.ini` so they load at server start. If they load later, on the first GunGame map, they miss the game's message registration and the server crashes when a player joins (`PF_MessageBegin_I: bogus message type ( 0 )`). Because of this, Metamod logs `Not loading plugin 'CStrike'; already loaded` on every map. Those errors are harmless. Never add `reapi` to `modules.ini`: AMXX autoloads it, and listing it loads it twice.
|
||||
|
||||
## Own plugins (`plugins/`)
|
||||
|
||||
- **esef_extras:** damage numbers (DHUD; blue = dealt, red = taken, real HP lost), killstreak announcements at 3/5/7/9/11/13/15 (to everyone), multikills within 2 s (to the killer), headshot / knife / first blood sounds, +HP per kill (capped at 100), and a "K D | streak (best)" HUD line that hides itself in GunGame.
|
||||
- **rtv:** a minimal rock-the-vote. 60% of players (`amx_rtv_ratio`) starts a 15 s vote between 5 random mapcycle maps. It's blocked in the first 2 minutes (`amx_rtv_delay`) and in the last 3 minutes, when mapchooser's end-of-map vote runs.
|
||||
- **esef_mode:** the mode switch described above.
|
||||
|
||||
## Server settings worth knowing (`cstrike/server.cfg`)
|
||||
|
||||
- `sv_lan 0`: has to stay 0 even for VPN or tunnel players, because they aren't "LAN" to the engine.
|
||||
- `sv_rehlds_maxclients_from_single_ip 16`: a tunnel like playit delivers every player from one IP.
|
||||
- `sv_rcon_banpenalty 1`: a wrong RCON password bans the source IP for 1 minute instead of forever, which matters when everyone shares the tunnel's IP.
|
||||
- `sv_rcon_condebug 0`: RCON commands contain the password, so they aren't echoed to the console or `docker logs`.
|
||||
- `sys_ticrate 300`: plenty for DM, and it keeps CPU use low.
|
||||
|
||||
## Crash handling
|
||||
|
||||
- **Crash:** the container runs with `init: true`, so tini is PID 1, and hlds runs with `-norestart`. When hlds dies the container exits and Docker's `restart: unless-stopped` starts it again. Without `-norestart`, hlds_run's own restart loop can hang after a crash, with the container still showing "running". Core dumps are off (`ulimits: core: 0`), because a single crash writes ~170 MB.
|
||||
- **Hang:** `scripts/watchdog.sh` runs from cron and A2S-queries the server. After 3 failed checks in a row it runs `docker restart`.
|
||||
- **Logs:** Docker's json log is capped at 3 × 10 MB. `scripts/backup.sh` deletes game logs older than 30 days, after counting their kills.
|
||||
|
||||
## Kill counter
|
||||
|
||||
`./kills` counts the `"X<..>" killed "Y<..>" with "weapon"` lines in `hlds/cstrike/logs/*.log`, keeping the counts per log file in `kills-tally.json`. Because the counts live in that file, the totals survive the 30-day log cleanup.
|
||||
Reference in new issue
Block a user