4ccccc6fe2
Self-hostable game server control panel: Go controller + agent, 26 game modules, embedded web UI. One-line install via install.sh / install.ps1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
115 lines
4.4 KiB
Bash
115 lines
4.4 KiB
Bash
#!/bin/bash
|
|
# panel-native Rust entrypoint.
|
|
#
|
|
# Contract:
|
|
# /game — SteamCMD-installed Rust Dedicated Server (app 258550).
|
|
# Populated by the panel's SteamCMD updater sidecar.
|
|
# /game-saves — world/save data + logs volume.
|
|
#
|
|
# Stage 1 runs as root: chown volumes, symlink server/<identity> into the
|
|
# save volume, then drop to the `panel` user via setpriv.
|
|
|
|
set -euo pipefail
|
|
|
|
log() { printf '[panel-rust] %s\n' "$*"; }
|
|
|
|
# ----- stage 1: root-only bootstrap -----
|
|
if [[ "$(id -u)" = "0" ]]; then
|
|
chown -R panel:panel /game /game-saves /home/panel 2>/dev/null || true
|
|
|
|
log "dropping privileges to panel (UID 1000)"
|
|
exec setpriv --reuid=panel --regid=panel --clear-groups \
|
|
--inh-caps=-all --bounding-set=-all \
|
|
env \
|
|
HOME=/home/panel \
|
|
SERVER_NAME="${SERVER_NAME:-panel Rust}" \
|
|
SERVER_DESCRIPTION="${SERVER_DESCRIPTION:-Powered by panel}" \
|
|
SERVER_IDENTITY="${SERVER_IDENTITY:-panel-rust}" \
|
|
SERVER_SEED="${SERVER_SEED:-12345}" \
|
|
SERVER_WORLD_SIZE="${SERVER_WORLD_SIZE:-3000}" \
|
|
MAX_PLAYERS="${MAX_PLAYERS:-50}" \
|
|
RCON_PASSWORD="${RCON_PASSWORD:-panel_rcon}" \
|
|
SERVER_URL="${SERVER_URL:-}" \
|
|
SERVER_HEADER_IMAGE="${SERVER_HEADER_IMAGE:-}" \
|
|
SERVER_LEVEL="${SERVER_LEVEL:-Procedural Map}" \
|
|
GAME_PORT="${GAME_PORT:-28015}" \
|
|
RCON_PORT="${RCON_PORT:-28016}" \
|
|
RUSTPLUS_PORT="${RUSTPLUS_PORT:-28082}" \
|
|
LD_LIBRARY_PATH="/game:/game/RustDedicated_Data/Plugins:/game/RustDedicated_Data/Plugins/x86_64:${LD_LIBRARY_PATH:-}" \
|
|
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
|
|
/entrypoint.sh "$@"
|
|
fi
|
|
|
|
GAME_DIR=/game
|
|
SAVE_DIR=/game-saves
|
|
DEDI_EXE="${GAME_DIR}/RustDedicated"
|
|
|
|
if [[ ! -x "${DEDI_EXE}" ]]; then
|
|
log "ERROR: ${DEDI_EXE} not found or not executable."
|
|
log " Run 'Update' on this server in the panel — that launches a"
|
|
log " SteamCMD sidecar which installs Rust (app 258550) here."
|
|
exit 78 # EX_CONFIG
|
|
fi
|
|
|
|
# --- persist server/<identity>/ into the save volume ---
|
|
# RustDedicated writes its world + save + cfg tree to <game>/server/<identity>.
|
|
# First boot moves it to /game-saves + symlinks back, so SteamCMD validate
|
|
# can't clobber world data. Idempotent on re-boot.
|
|
mkdir -p "${SAVE_DIR}/server"
|
|
if [[ -d "${GAME_DIR}/server" && ! -L "${GAME_DIR}/server" ]]; then
|
|
# On fresh install this dir doesn't exist; server creates it on first boot.
|
|
# If it does exist (e.g. from an earlier panel image without the symlink),
|
|
# migrate and replace.
|
|
if [[ ! -d "${SAVE_DIR}/server/${SERVER_IDENTITY}" ]]; then
|
|
mv "${GAME_DIR}/server"/* "${SAVE_DIR}/server/" 2>/dev/null || true
|
|
fi
|
|
rm -rf "${GAME_DIR}/server"
|
|
fi
|
|
ln -sfn "${SAVE_DIR}/server" "${GAME_DIR}/server"
|
|
|
|
# Mirror logs to the bind-mounted logs dir for external access.
|
|
mkdir -p "${SAVE_DIR}/logs"
|
|
|
|
cd "${GAME_DIR}"
|
|
log "starting Rust Dedicated Server"
|
|
log " name: ${SERVER_NAME}"
|
|
log " identity: ${SERVER_IDENTITY}"
|
|
log " level: ${SERVER_LEVEL}"
|
|
log " seed: ${SERVER_SEED}"
|
|
log " size: ${SERVER_WORLD_SIZE}"
|
|
log " max: ${MAX_PLAYERS}"
|
|
log " game port: ${GAME_PORT}/udp"
|
|
log " rcon port: ${RCON_PORT}/tcp (internal, websocket)"
|
|
log " rust+: ${RUSTPLUS_PORT}/tcp (push, optional)"
|
|
|
|
trap 'log "SIGTERM received; stopping server"; kill -TERM "${RUST_PID:-0}" 2>/dev/null || true; wait "${RUST_PID:-0}" 2>/dev/null || true; exit 0' TERM INT
|
|
|
|
# Rust's own flags — all `+` prefixed. `-batchmode` / `-nographics` come
|
|
# before the `+` convars. We intentionally omit `-logFile`: Unity defaults
|
|
# to stdout when absent, and passing `/dev/stdout` or `-` makes RustDedicated
|
|
# fail with "Unable to open log file, exiting." on Linux-container Unity.
|
|
exec stdbuf -oL -eL "${DEDI_EXE}" \
|
|
-batchmode \
|
|
-nographics \
|
|
+server.ip 0.0.0.0 \
|
|
+server.port "${GAME_PORT}" \
|
|
+server.tickrate 10 \
|
|
+server.hostname "${SERVER_NAME}" \
|
|
+server.description "${SERVER_DESCRIPTION}" \
|
|
+server.identity "${SERVER_IDENTITY}" \
|
|
+server.level "${SERVER_LEVEL}" \
|
|
+server.seed "${SERVER_SEED}" \
|
|
+server.worldsize "${SERVER_WORLD_SIZE}" \
|
|
+server.maxplayers "${MAX_PLAYERS}" \
|
|
+server.saveinterval 600 \
|
|
+server.url "${SERVER_URL}" \
|
|
+server.headerimage "${SERVER_HEADER_IMAGE}" \
|
|
+rcon.web 1 \
|
|
+rcon.ip 0.0.0.0 \
|
|
+rcon.port "${RCON_PORT}" \
|
|
+rcon.password "${RCON_PASSWORD}" \
|
|
+app.port "${RUSTPLUS_PORT}" &
|
|
|
|
RUST_PID=$!
|
|
wait "${RUST_PID}"
|