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:
esefandClaude Opus 5.5 committed 2026-10-11 05:03:07 +03:00
commit 7e9cbee14d
33 files changed
+1890

No files matched your search

+24
View File
@@ -0,0 +1,24 @@
#!/bin/sh
# Nightly backup (cron 04:30, see install-cron.sh): settings, secrets, configs, stats and the
# kill tally into backups/cs16-YYYY-MM-DD.tar.gz, kept 14 days. Game files are not included
# (./setup.sh downloads them again). Also saves the kill tally and prunes logs older than 30 days.
. "$(dirname "$0")/env.sh"
set -e
cd "$D"
umask 077
mkdir -p backups
C=hlds/cstrike
# keep only paths that exist (a fresh install has no stats yet)
LIST=
for p in .env secrets kills-tally.json $C/mapcycle.txt $C/custom.cfg $C/reunion.cfg \
$C/addons/amxmodx/configs $C/addons/amxmodx/data $C/gungame.stats $C/gungame.streaks \
$C/addons/yapb/conf; do
[ -e "$p" ] && LIST="$LIST $p"
done
tar czf "backups/cs16-$(date +%F).tar.gz" $LIST
find backups -name "cs16-*.tar.gz" -mtime +14 -delete
# count the kills in the logs before old logs go away
python3 kills > /dev/null || true
find $C/logs $C/addons/amxmodx/logs -name "*.log" -mtime +30 -delete 2>/dev/null || true
echo "$(date '+%F %T') backup ok"
Executable
+5
View File
@@ -0,0 +1,5 @@
# Sourced by the scripts: D = repo folder, NAME = container, PORT = game port (from .env).
D=$(cd "$(dirname "$0")/.." && pwd)
getenv() { sed -n "s/^$1=//p" "$D/.env" 2>/dev/null | tr -d '\r'; }
NAME=$(getenv CS_NAME); NAME=${NAME:-cs16}
PORT=$(getenv CS_PORT); PORT=${PORT:-27015}
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
# Adds the watchdog (every 5 min) and the backup (04:30) to your crontab.
# Safe to run again: replaces its own lines (tagged with this folder's path), leaves others alone.
# ./scripts/install-cron.sh --remove takes them out again.
D=$(cd "$(dirname "$0")/.." && pwd)
TAG="# cs16-dm-server $D"
OLD=$(crontab -l 2>/dev/null | grep -vF "$TAG")
if [ "$1" = "--remove" ]; then
printf '%s\n' "$OLD" | crontab - && echo "removed cron lines for $D"; exit 0
fi
{
[ -n "$OLD" ] && printf '%s\n' "$OLD"
echo "*/5 * * * * $D/scripts/watchdog.sh $TAG"
echo "30 4 * * * $D/scripts/backup.sh >> $D/backups/backup.log 2>&1 $TAG"
} | crontab -
echo "cron installed for $D:"; crontab -l | grep -F "$TAG"
+22
View File
@@ -0,0 +1,22 @@
#!/bin/sh
# Hang watchdog (cron every 5 min, see install-cron.sh). Docker already restarts the server when
# it crashes; this covers the other case: container "running" but the server not answering.
# Restarts it after 3 failed checks in a row (~15 min). Skips a container stopped on purpose.
. "$(dirname "$0")/env.sh"
cd "$D"
STATE=.watchdog-fails
LOG=watchdog.log
[ "$(docker inspect -f '{{.State.Status}}' "$NAME" 2>/dev/null)" = running ] || { rm -f $STATE; exit 0; }
if python3 tools/hlq.py info 127.0.0.1 "$PORT" >/dev/null 2>&1 || \
{ sleep 5; python3 tools/hlq.py info 127.0.0.1 "$PORT" >/dev/null 2>&1; }; then
rm -f $STATE
else
n=$(( $(cat $STATE 2>/dev/null || echo 0) + 1 ))
echo $n > $STATE
echo "$(date '+%F %T') no answer ($n/3)" >> $LOG
if [ $n -ge 3 ]; then
echo "$(date '+%F %T') restarting $NAME" >> $LOG
docker restart "$NAME" >/dev/null 2>&1
rm -f $STATE
fi
fi