Files
cs16-dm-server/tools/install-addons.sh
T
esefandClaude Opus 5.5 4b7fdcaa23 Windows version: host the server from your own PC
setup.cmd installs the same DM/GunGame server natively on Windows 10/11
(no Docker or Python) into Documents\cs16, then helps set up how friends
join (LAN, playit.gg, port forwarding, VPN). cs.cmd is the menu.

Also: setup falls back to backup copies of the freely licensed mod
downloads on this Gitea when a main site is down (tools/make-mirror.sh
fills them), and the README is rewritten as a plain guide.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 20:55:00 +03:00

136 lines
6.2 KiB
Bash
Executable File

#!/bin/sh
# Puts the mod stack on top of the Valve game files in hlds/. setup.sh runs this inside the
# image after every game download/update (a Steam "validate" puts Valve's engine back and
# silently removes ReHLDS + Metamod, so this always has to run after it).
# Safe to run again. Kept as they are on re-runs: maps, stats, bot navigation graphs, and your
# edits to users.ini (admins), amxx.cfg, csdm/config.ini, mapcycle.txt and custom.cfg.
# Mounts (see setup.sh): repo at /cs/repo (read-only) with dl/ writable, hlds at /cs/hlds.
set -e
R=/cs/repo; DL=$R/dl; H=/cs/hlds; C=$H/cstrike; A=$C/addons; CF=$C/addons/amxmodx/configs
X=$(mktemp -d)
trap 'rm -rf "$X"' EXIT
step() { printf ' - %s\n' "$*"; }
die() { printf '\033[31merror:\033[0m %s\n' "$*" >&2; exit 1; }
sha() { sha256sum < "$1" | cut -c1-64; }
[ -x $H/hlds_linux ] || die "no game files in hlds/ (the Steam download didn't finish)"
step "downloads (pinned, checked against tools/versions.txt)"
mkdir -p $DL/gg $DL/snd $DL/spawns
MIRROR=$(sed -n 's/^#mirror //p' $R/tools/versions.txt | tr -d '\r')
grep -v '^#' $R/tools/versions.txt | while read -r f sum url; do
[ -n "$f" ] || continue
[ -f "$DL/$f" ] && [ "$(sha "$DL/$f")" = "$sum" ] && continue
# main site first; if that fails, the backup copy on the project's Gitea (same checksum check)
curl -fsSL --retry 3 -o "$DL/$f.part" "$url" ||
{ [ -n "$MIRROR" ] && echo " $f: main site failed, trying the backup copy" &&
curl -fsSL --retry 2 -o "$DL/$f.part" "$MIRROR${f##*/}"; } ||
die "download failed: $url"
got=$(sha "$DL/$f.part")
[ "$got" = "$sum" ] || { rm -f "$DL/$f.part"; die "checksum mismatch for $f: expected $sum, got $got"; }
mv "$DL/$f.part" "$DL/$f"
done
ls $DL/rehlds-bin-*.zip >/dev/null
# Game mode in use right now (re-run) or from .env (first install)
if [ -f $CF/plugins-gungame.ini ]; then MODE=gg
elif [ -f $CF/plugins-CSDM_ReAPI.ini ] || [ -f $CF/plugins-CSDM_ReAPI.ini.off ]; then MODE=dm
else MODE=${CS_MODE:-dm}; fi
case "$MODE" in dm|gg) ;; *) die "CS_MODE must be dm or gg (is: $MODE)" ;; esac
# Files people edit: save them now, put them back after the archives are unpacked
FIRST=; [ -f $C/.cs16-dm-server ] || FIRST=1
KEEP="addons/amxmodx/configs/users.ini addons/amxmodx/configs/amxx.cfg addons/amxmodx/configs/csdm/config.ini"
if [ -z "$FIRST" ]; then
for f in $KEEP; do [ -f "$C/$f" ] && mkdir -p "$X/keep/$(dirname "$f")" && cp -p "$C/$f" "$X/keep/$f"; done
fi
step "ReHLDS (engine)"
unzip -qo $DL/rehlds-bin-3.15.0.896.zip 'bin/linux32/*' -d $X/rehlds
cp -a $X/rehlds/bin/linux32/. $H/
chmod +x $H/hlds_linux $H/hltv # the zip has no exec bits
step "ReGameDLL (game logic)"
unzip -qo $DL/regamedll-bin-5.30.0.814.zip 'bin/linux32/cstrike/*' -d $X/regame
cp -a $X/regame/bin/linux32/cstrike/. $C/
# game.cfg runs at every map start and would force alltalk off: server.cfg decides that instead
sed -i 's|^sv_alltalk "0"|// sv_alltalk "0" // cs16-dm-server: set in server.cfg instead|' $C/game.cfg
step "Metamod-R"
unzip -qo $DL/metamod-bin-1.3.0.149.zip 'addons/*' -d $X/mm
cp -a $X/mm/addons/. $A/
[ -f $C/liblist.gam.orig ] || cp $C/liblist.gam $C/liblist.gam.orig
sed -i 's|^gamedll_linux .*|gamedll_linux "addons/metamod/metamod_i386.so"|' $C/liblist.gam
step "AMX Mod X + ReAPI"
tar xzf $DL/amxmodx-1.10.0-git5486-base-linux.tar.gz -C $C
tar xzf $DL/amxmodx-1.10.0-git5486-cstrike-linux.tar.gz -C $C
unzip -qo $DL/reapi-bin-5.29.0.358.zip 'addons/*' -d $X/reapi
cp -a $X/reapi/addons/. $A/
step "ReUnion (non-Steam clients)"
unzip -qo $DL/reunion-0.2.0.25.zip bin/Linux/reunion_mm_i386.so reunion.cfg -d $X/reu
mkdir -p $A/reunion
cp $X/reu/bin/Linux/reunion_mm_i386.so $A/reunion/
SALT=$(tr -d '\r\n' < /cs/secrets/reunion-salt.txt)
[ -n "$SALT" ] || die "secrets/reunion-salt.txt is empty"
# IDClientsLimit 0: several non-Steam players with the same placeholder ID can all join
( umask 077; sed -e "s|^SteamIdHashSalt =.*|SteamIdHashSalt = $SALT|" -e 's|^IDClientsLimit = .*|IDClientsLimit = 0|' \
$X/reu/reunion.cfg > $C/reunion.cfg )
step "CSDM-ReAPI (deathmatch) + spawn points"
unzip -qo $DL/CSDM-ReAPI-v1.0.8.zip 'cstrike/*' -d $X/csdm
cp -a $X/csdm/cstrike/. $C/
mkdir -p $CF/csdm/spawns
for f in $DL/spawns/*.spawns.cfg; do
b=$(basename "$f")
# old 6-field lines (de_prodigy, de_train) -> the 10 fields CSDM-ReAPI reads (others are skipped)
awk 'NF==6 {print $0" 0 0 0 0"; next} {print}' "$f" > $CF/csdm/spawns/$b
ln -sfn spawns/$b $CF/csdm/$b # GunGame looks for them one folder up
done
step "YaPB (bots)"
tar xJf $DL/yapb-4.4.957-linux.tar.xz -C $C
find $A/yapb -type d -exec chmod 775 {} + # the tarball's dirs have no x bit
sed -i -e 's/^yb_csdm_mode ".*"/yb_csdm_mode "2"/' -e 's/^yb_quota_mode ".*"/yb_quota_mode "fill"/' \
$A/yapb/conf/yapb.cfg # FFA-aware bots; quota = fill up to CS_BOTS players (start.sh sets it)
step "GunGame + announcer sounds"
cp $DL/gg/gungame.sma $A/amxmodx/scripting/
cp $DL/gg/gungame.txt $A/amxmodx/data/lang/
mkdir -p $C/sound/gungame $C/sound/misc
cp $DL/gg/*.wav $C/sound/gungame/
cp $DL/snd/*.wav $C/sound/misc/
step "configs from the repo"
cp -a $R/cstrike/. $C/
if [ -n "$FIRST" ]; then
cp $R/defaults/mapcycle.txt $C/mapcycle.txt
fi
[ -f $C/custom.cfg ] || cp $R/defaults/custom.cfg $C/custom.cfg
if [ -z "$FIRST" ] && [ -d $X/keep ]; then cp -a $X/keep/. $C/; fi
step "compiling plugins"
cp $R/plugins/*.sma $A/amxmodx/scripting/
cd $A/amxmodx/scripting
for p in rtv esef_extras esef_mode gungame; do
./amxxpc $p.sma -o../plugins/$p.amxx > $X/$p.log 2>&1 || { cat $X/$p.log; die "compiling $p.sma failed"; }
done
cd - >/dev/null
step "game mode: $MODE"
# exactly one of the two plugin lists is active (.ini), the other one is .ini.off
for ini in plugins-gungame.ini plugins-CSDM_ReAPI.ini; do
[ -f $CF/$ini ] && rm -f $CF/$ini.off # fresh copy from the archive/repo wins
[ -f $CF/$ini ] || mv $CF/$ini.off $CF/$ini
done
if [ "$MODE" = gg ]; then mv $CF/plugins-CSDM_ReAPI.ini $CF/plugins-CSDM_ReAPI.ini.off
else mv $CF/plugins-gungame.ini $CF/plugins-gungame.ini.off; fi
# hlds looks for steamclient.so here; without it: "Unable to initialize Steam"
mkdir -p /cs/steamcmd-home/.steam/sdk32
ln -sfn /cs/hlds/steamclient.so /cs/steamcmd-home/.steam/sdk32/steamclient.so
touch $C/.cs16-dm-server
echo " mod stack installed"