#!/bin/sh # Control menu: ./cs (pick a number) # or directly: ./cs status | map | mode dm|gg | bots | kills | say # ./cs restart | stop | start | logs | console | apply | update D=$(cd "$(dirname "$0")" && pwd) cd "$D" || exit 1 [ -f .env ] || { echo "no .env yet: run ./setup.sh first"; exit 1; } getenv() { sed -n "s/^$1=//p" .env | tr -d '\r'; } setenv() { sed -i "s|^$1=.*|$1=$2|" .env; } NAME=$(getenv CS_NAME); NAME=${NAME:-cs16} PORT=$(getenv CS_PORT); PORT=${PORT:-27015} C=hlds/cstrike status() { st=$(docker inspect -f '{{.State.Status}}' "$NAME" 2>/dev/null || echo "not created") if [ "$st" = running ]; then if out=$(python3 tools/hlq.py info 127.0.0.1 "$PORT" 2>/dev/null); then echo " ${out#A2S_INFO ok: }" else echo " container running, server not answering yet (starting or changing map?)"; fi if [ -f $C/addons/amxmodx/configs/plugins-gungame.ini ]; then echo " mode: GunGame"; else echo " mode: deathmatch"; fi else echo " server is $st (start it: ./cs start)" fi } players() { ./rcon status | grep -a -E '^#|^map|^players' ; } setmap() { m=$1 if [ -z "$m" ]; then echo "Maps on this server:" ls $C/maps/*.bsp 2>/dev/null | sed 's|.*/||; s|\.bsp$||' | nl -w3 -s') ' | pr -3 -t -w 90 printf 'map name or number: '; read -r m case "$m" in ''|*[!0-9]*) ;; *) m=$(ls $C/maps/*.bsp | sed 's|.*/||; s|\.bsp$||' | sed -n "${m}p") ;; esac fi [ -n "$m" ] || return [ -f "$C/maps/$m.bsp" ] || { echo "no map called '$m' in $C/maps"; return; } r=$(./rcon changelevel "$m"); case "$r" in "(no answer"*) echo "$r" ;; *) echo "changing to $m" ;; esac } setmode() { m=$1 [ -n "$m" ] || { printf 'mode: dm = deathmatch, gg = GunGame: '; read -r m; } case "$m" in dm|gg) ./rcon esef_mode "$m" | grep -a esef_mode ;; *) echo "use dm or gg" ;; esac } setbots() { n=$1 [ -n "$n" ] || { printf 'fill the server up to how many players with bots? (0 = no bots, now %s): ' "$(getenv CS_BOTS)"; read -r n; } case "$n" in ''|*[!0-9]*) echo "give a number"; return ;; esac setenv CS_BOTS "$n" # remembered for restarts r=$(./rcon yb_quota "$n"); case "$r" in "(no answer"*) echo "saved in .env, applies when the server is back: $r" ;; *) echo "bots: up to $n players (saved in .env)" ;; esac } run() { case "$1" in status|1) status; players ;; map|2) shift; setmap "$1" ;; mode|3) shift; setmode "$1" ;; bots|4) shift; setbots "$1" ;; kills|5) python3 kills ;; say|6) shift; t="$*"; [ -n "$t" ] || { printf 'message: '; read -r t; }; [ -n "$t" ] && ./rcon say "$t" > /dev/null && echo sent ;; restart|7) docker restart "$NAME" > /dev/null && echo "restarted (back in ~10 s)" ;; stop|8) docker compose stop && echo "stopped (./cs start to start again)" ;; start|9) docker compose up -d ;; logs|10) echo "live server output, Ctrl+C to leave"; docker logs -f --tail 30 "$NAME" 2>&1 | grep --line-buffered -v -E 'rcon_password|sv_password|Rcon:' ;; console|11) ./rcon ;; apply|12) docker compose up -d && echo "applied .env (server restarted if anything changed)" ;; update|13) ./setup.sh --yes --update ;; *) echo "unknown: $1 (try ./cs with no arguments)"; return 1 ;; esac } if [ $# -gt 0 ]; then run "$@"; exit $?; fi while true; do echo echo "== $(getenv CS_HOSTNAME) == (container $NAME, UDP $PORT)" status cat <<'EOF' 1) who's playing 8) stop server 2) change map 9) start server 3) game mode (DM / GG) 10) live log 4) bots 11) console (type server commands) 5) kill counter 12) apply .env changes 6) message to players 13) update game + mods 7) restart server 0) quit EOF printf '> '; read -r c || break case "$c" in 0|q|quit|exit|'') break ;; esac run "$c" done