192 lines
6.2 KiB
Bash
192 lines
6.2 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
APPID=${WINDROSE_APP_ID:-4129620}
|
|
SERVERDIR=${SERVERDIR:-/data}
|
|
STEAM_HOME=${STEAM_HOME:-/home/steam}
|
|
WINEPREFIX=${WINEPREFIX:-$STEAM_HOME/.wine}
|
|
STEAM_LOGIN=${STEAM_LOGIN:-anonymous}
|
|
STEAM_PASS=${STEAM_PASS:-}
|
|
UPDATE_ON_START=${UPDATE_ON_START:-true}
|
|
GENERATE_SETTINGS=${GENERATE_SETTINGS:-true}
|
|
PUID=${PUID:-1000}
|
|
PGID=${PGID:-1000}
|
|
|
|
PORT=${PORT:-7777}
|
|
QUERYPORT=${QUERYPORT:-7778}
|
|
MULTIHOME=${MULTIHOME:-0.0.0.0}
|
|
|
|
INVITE_CODE=${INVITE_CODE:-}
|
|
SERVER_NAME=${SERVER_NAME:-}
|
|
SERVER_NOTE=${SERVER_NOTE:-}
|
|
SERVER_PASSWORD=${SERVER_PASSWORD:-}
|
|
MAX_PLAYERS=${MAX_PLAYERS:-4}
|
|
P2P_PROXY_ADDRESS=${P2P_PROXY_ADDRESS:-127.0.0.1}
|
|
FIRST_RUN_TIMEOUT=${FIRST_RUN_TIMEOUT:-180}
|
|
|
|
SERVER_DESC="$SERVERDIR/R5/ServerDescription.json"
|
|
SERVER_PID=""
|
|
XVFB_PID=""
|
|
|
|
log() { echo "[windrose] $*"; }
|
|
quote() { printf '%q' "$1"; }
|
|
run_as_steam() { su -s /bin/bash steam -c "$*"; }
|
|
|
|
ensure_user_mapping() {
|
|
groupmod -o -g "$PGID" steam 2>/dev/null || true
|
|
usermod -o -u "$PUID" steam 2>/dev/null || true
|
|
mkdir -p "$SERVERDIR" "$STEAM_HOME"
|
|
chown -R steam:steam /opt/steamcmd "$STEAM_HOME" "$SERVERDIR" 2>/dev/null || true
|
|
}
|
|
|
|
cleanup_xvfb() {
|
|
if [ -n "$XVFB_PID" ] && kill -0 "$XVFB_PID" 2>/dev/null; then
|
|
kill "$XVFB_PID" 2>/dev/null || true
|
|
fi
|
|
rm -f /tmp/.X99-lock 2>/dev/null || true
|
|
}
|
|
|
|
shutdown_server() {
|
|
log "Stopping Windrose dedicated server"
|
|
pkill -TERM -u steam -f 'WindroseServer-Win64-Shipping.exe' 2>/dev/null || true
|
|
pkill -TERM -u steam -f 'wineserver' 2>/dev/null || true
|
|
for _ in $(seq 1 60); do
|
|
if ! pgrep -u steam -f 'WindroseServer-Win64-Shipping.exe|wineserver' >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
pkill -KILL -u steam -f 'WindroseServer-Win64-Shipping.exe|wineserver' 2>/dev/null || true
|
|
}
|
|
|
|
trap 'shutdown_server; exit 0' TERM INT
|
|
trap 'cleanup_xvfb' EXIT
|
|
|
|
init_xvfb() {
|
|
rm -f /tmp/.X99-lock 2>/dev/null || true
|
|
Xvfb :99 -screen 0 1024x768x16 -nolisten tcp >/dev/null 2>&1 &
|
|
XVFB_PID=$!
|
|
sleep 1
|
|
}
|
|
|
|
init_wine() {
|
|
mkdir -p "$WINEPREFIX"
|
|
chown -R steam:steam "$STEAM_HOME" 2>/dev/null || true
|
|
if [ ! -f "$WINEPREFIX/system.reg" ]; then
|
|
log "Initializing Wine prefix"
|
|
run_as_steam "WINEPREFIX=$(quote "$WINEPREFIX") wineboot -i >/dev/null 2>&1 || true"
|
|
fi
|
|
}
|
|
|
|
update_server() {
|
|
if [ "$UPDATE_ON_START" != "true" ]; then
|
|
log "UPDATE_ON_START=false, skipping SteamCMD update"
|
|
return
|
|
fi
|
|
|
|
local login_cmd
|
|
if [ "$STEAM_LOGIN" = "anonymous" ]; then
|
|
login_cmd='+login anonymous'
|
|
elif [ -n "$STEAM_PASS" ]; then
|
|
login_cmd="+login $(quote "$STEAM_LOGIN") $(quote "$STEAM_PASS")"
|
|
else
|
|
login_cmd="+login $(quote "$STEAM_LOGIN")"
|
|
fi
|
|
|
|
log "Updating or validating server files via SteamCMD (AppID $APPID)"
|
|
run_as_steam "mkdir -p $(quote "$SERVERDIR") && /opt/steamcmd/steamcmd.sh +force_install_dir $(quote "$SERVERDIR") $login_cmd +app_update $(quote "$APPID") validate +quit"
|
|
}
|
|
|
|
find_server_exe() {
|
|
find "$SERVERDIR" -iname 'WindroseServer-Win64-Shipping.exe' 2>/dev/null | head -n 1 || true
|
|
}
|
|
|
|
first_run_generate_config() {
|
|
local exe="$1"
|
|
if [ "$GENERATE_SETTINGS" != "true" ] || [ -f "$SERVER_DESC" ]; then
|
|
return
|
|
fi
|
|
|
|
log "First run detected, generating default server config"
|
|
run_as_steam "WINEPREFIX=$(quote "$WINEPREFIX") wine $(quote "$exe") -log -MULTIHOME=$(quote "$MULTIHOME") -PORT=$(quote "$PORT") -QUERYPORT=$(quote "$QUERYPORT") >/tmp/windrose-first-run.log 2>&1" &
|
|
local warmup_pid=$!
|
|
|
|
local count=0
|
|
while [ ! -f "$SERVER_DESC" ] && [ "$count" -lt "$FIRST_RUN_TIMEOUT" ]; do
|
|
sleep 1
|
|
count=$((count + 1))
|
|
done
|
|
|
|
kill "$warmup_pid" 2>/dev/null || true
|
|
wait "$warmup_pid" 2>/dev/null || true
|
|
pkill -TERM -u steam -f 'wineserver' 2>/dev/null || true
|
|
|
|
if [ ! -f "$SERVER_DESC" ]; then
|
|
log "WARNING: ServerDescription.json was not generated within ${FIRST_RUN_TIMEOUT}s"
|
|
log "Wine output:"
|
|
tail -n 40 /tmp/windrose-first-run.log 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
patch_server_config() {
|
|
if [ "$GENERATE_SETTINGS" != "true" ]; then
|
|
log "GENERATE_SETTINGS=false, skipping JSON patching"
|
|
return
|
|
fi
|
|
if [ ! -f "$SERVER_DESC" ]; then
|
|
log "ServerDescription.json not found, skipping patch"
|
|
return
|
|
fi
|
|
|
|
log "Patching ServerDescription.json from environment"
|
|
tr -d '\r' < "$SERVER_DESC" | jq \
|
|
--arg invite "$INVITE_CODE" \
|
|
--arg name "$SERVER_NAME" \
|
|
--arg note "$SERVER_NOTE" \
|
|
--arg password "$SERVER_PASSWORD" \
|
|
--arg proxy "$P2P_PROXY_ADDRESS" \
|
|
--argjson maxplayers "$MAX_PLAYERS" \
|
|
'
|
|
.ServerDescription_Persistent.P2pProxyAddress = $proxy |
|
|
if $invite != "" then .ServerDescription_Persistent.InviteCode = $invite else . end |
|
|
if $name != "" then .ServerDescription_Persistent.ServerName = $name else . end |
|
|
if $note != "" then .ServerDescription_Persistent.Note = $note else . end |
|
|
if $password != "" then
|
|
.ServerDescription_Persistent.IsPasswordProtected = true |
|
|
.ServerDescription_Persistent.Password = $password
|
|
else
|
|
.ServerDescription_Persistent.IsPasswordProtected = false |
|
|
.ServerDescription_Persistent.Password = ""
|
|
end |
|
|
.ServerDescription_Persistent.MaxPlayerCount = $maxplayers
|
|
' > "$SERVER_DESC.tmp"
|
|
mv "$SERVER_DESC.tmp" "$SERVER_DESC"
|
|
chown steam:steam "$SERVER_DESC" 2>/dev/null || true
|
|
}
|
|
|
|
start_server() {
|
|
local exe="$1"
|
|
log "Starting Windrose dedicated server"
|
|
log "Executable: $exe"
|
|
log "Bind: $MULTIHOME Port: $PORT QueryPort: $QUERYPORT"
|
|
run_as_steam "WINEPREFIX=$(quote "$WINEPREFIX") wine $(quote "$exe") -log -MULTIHOME=$(quote "$MULTIHOME") -PORT=$(quote "$PORT") -QUERYPORT=$(quote "$QUERYPORT")" &
|
|
SERVER_PID=$!
|
|
wait "$SERVER_PID"
|
|
}
|
|
|
|
ensure_user_mapping
|
|
init_xvfb
|
|
init_wine
|
|
update_server
|
|
|
|
SERVER_EXE=$(find_server_exe)
|
|
if [ -z "$SERVER_EXE" ]; then
|
|
log "ERROR: Windrose server executable not found under $SERVERDIR"
|
|
find "$SERVERDIR" -maxdepth 4 2>/dev/null || true
|
|
exit 1
|
|
fi
|
|
|
|
first_run_generate_config "$SERVER_EXE"
|
|
patch_server_config
|
|
start_server "$SERVER_EXE"
|