Panel — public source drop (v0.9.0)
Self-hostable game-server control panel: controller + agent + 26 game modules. One-line install (prebuilt release, no Go required): curl -fsSL https://git.pdxtechs.com/dbledeez/panel/raw/branch/main/install.sh | sudo bash Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
#!/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}"
|
||||
Reference in New Issue
Block a user