panel public release

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 01:13:02 -07:00
commit 8a94ffd58f
2165 changed files with 301493 additions and 0 deletions
+204
View File
@@ -0,0 +1,204 @@
#!/bin/bash
# panel-native DayZ entrypoint.
#
# Contract:
# /game — SteamCMD-installed DayZ Server (app 223350). Populated
# by the panel's SteamCMD updater sidecar, which uses the
# operator-supplied Steam credentials (the module manifest
# sets requires_steam_login: true).
# /game-saves — serverDZ.cfg + profiles/ (logs, bans, RPTs) + optional
# mpmissions/ override tree.
set -euo pipefail
log() { printf '[panel-dayz] %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 DayZ}" \
SERVER_PORT="${SERVER_PORT:-2302}" \
SERVER_PASSWORD="${SERVER_PASSWORD:-}" \
BE_PASSWORD="${BE_PASSWORD:-panel_be}" \
CPU_COUNT="${CPU_COUNT:-2}" \
LD_LIBRARY_PATH="/game:${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}/DayZServer"
if [[ ! -x "${DEDI_EXE}" ]]; then
log "ERROR: ${DEDI_EXE} not found or not executable."
log " Run 'Update' on this server in the panel — the modal will"
log " prompt for Steam credentials (DayZ is paid content; the"
log " anonymous SteamCMD login won't work)."
exit 78 # EX_CONFIG
fi
# --- persist profiles into /game-saves ---
# DayZ writes RPT crash logs, ADM admin logs, and BattlEye bans to
# <install>/profiles/. Move out to the save volume + symlink back on
# first boot so SteamCMD validate can't wipe them.
mkdir -p "${SAVE_DIR}/profiles"
if [[ -d "${GAME_DIR}/profiles" && ! -L "${GAME_DIR}/profiles" ]]; then
mv "${GAME_DIR}/profiles"/* "${SAVE_DIR}/profiles/" 2>/dev/null || true
rm -rf "${GAME_DIR}/profiles"
fi
ln -sfn "${SAVE_DIR}/profiles" "${GAME_DIR}/profiles"
# --- seed serverDZ.cfg on first boot ---
CFG="${SAVE_DIR}/serverDZ.cfg"
if [[ ! -s "${CFG}" ]]; then
log "seeding serverDZ.cfg (first boot)"
cat > "${CFG}" <<EOF
hostname = "${SERVER_NAME}";
password = "${SERVER_PASSWORD}";
passwordAdmin = "";
enableWhitelist = 0;
disableVoN = 0;
vonCodecQuality = 7;
disable3rdPerson = 0;
disableCrosshair = 0;
serverTime = "SystemTime";
serverTimeAcceleration = 12;
serverNightTimeAcceleration = 1;
serverTimePersistent = 0;
guaranteedUpdates = 1;
loginQueueConcurrentPlayers = 5;
loginQueueMaxPlayers = 500;
instanceId = 1;
storageAutoFix = 1;
verifySignatures = 2;
forceSameBuild = 1;
timeStampFormat = "Short";
logAverageFps = 1;
logMemory = 1;
logPlayers = 1;
logFile = "server_console.log";
adminLogPlayerHitsOnly = 0;
adminLogPlacement = 1;
adminLogBuildActions = 1;
adminLogPlayerList = 1;
disableBanlist = 0;
BattlEye = 1;
respawnTime = 5;
steamQueryPort = ${SERVER_PORT};
maxPing = 200;
MissionTemplate = "dayzOffline.chernarusplus";
class Missions
{
class DayZ
{
template = "dayzOffline.chernarusplus";
};
};
EOF
fi
# Point /game/serverDZ.cfg at the save-volume copy so the binary's default
# relative-path lookup finds it.
ln -sf "${CFG}" "${GAME_DIR}/serverDZ.cfg"
# --- BattlEye RCON config ---
# DayZ ships a `battleye/` dir that contains a NESTED `battleye/` subdir with
# a duplicate `beserver_x64.so`. The DayZ launcher (`-BEpath=battleye`) loads
# BE from there and expects `beserver_x64.cfg` to sit alongside the nested
# lib — i.e. `battleye/battleye/beserver_x64.cfg`, NOT `battleye/beserver_x64.cfg`.
# Writing to the outer dir (which is what most tutorials show) results in BE
# initializing but never binding the RCON port, with no error in the log.
#
# We unconditionally overwrite both locations so operator env changes take
# effect on the next boot. We also purge stale `beserver_x64_active_<token>.cfg`
# files BE writes after first handshake — if the password has changed, the
# active copy will mismatch and BE refuses incoming RCON packets.
#
# Reference: AMP's dayz-experimental.kvp uses `{{$FullBaseDir}}battleye` as
# the BE path and their PreStartStages purge the active file the same way.
BE_DIR="${GAME_DIR}/battleye"
for CFGPATH in "${BE_DIR}/beserver_x64.cfg" "${BE_DIR}/battleye/beserver_x64.cfg"; do
if [[ -d "$(dirname "${CFGPATH}")" ]]; then
log "writing ${CFGPATH} (RCON port 2305, pw from BE_PASSWORD)"
cat > "${CFGPATH}" <<EOF
RConPassword ${BE_PASSWORD}
RConPort 2305
RConIP 0.0.0.0
RestrictRCon 0
RConLog 1
UseLocationsToBan 0
LogFile 0
EOF
fi
done
# Purge stale active-session files (both locations).
rm -f "${BE_DIR}/beserver_x64_active_"*.cfg 2>/dev/null || true
rm -f "${BE_DIR}/battleye/beserver_x64_active_"*.cfg 2>/dev/null || true
cd "${GAME_DIR}"
log "starting DayZ dedicated server"
log " name: ${SERVER_NAME}"
log " game port: ${SERVER_PORT}/udp"
log " rcon port: 2305/udp (internal, BattlEye)"
log " cfg: ${CFG}"
trap 'log "SIGTERM received"; kill -TERM "${DZ_PID:-0}" 2>/dev/null || true; wait "${DZ_PID:-0}" 2>/dev/null || true; exit 0' TERM INT
# --- mod list dynamic build ---
# Panel's mod manager maintains two plain-text files on the save volume:
# /game-saves/panel-mods.txt — one @ModFolder per line → -mod=
# /game-saves/panel-servermods.txt — one @ModFolder per line → -servermod=
# Blank lines and lines starting with '#' are ignored. This lets the mod
# manager edit a human-readable file and have the server pick up the new
# load order on the next start without entrypoint code changes.
build_mod_arg() {
local file="$1" prefix="$2"
[[ -s "$file" ]] || { echo ""; return; }
local joined=""
while IFS= read -r line || [[ -n "$line" ]]; do
line="${line%$'\r'}" # strip trailing CR (Windows edits)
line="${line#"${line%%[![:space:]]*}"}" # ltrim
line="${line%"${line##*[![:space:]]}"}" # rtrim
[[ -z "$line" || "$line" == \#* ]] && continue
if [[ -z "$joined" ]]; then joined="$line"
else joined="${joined};${line}"
fi
done < "$file"
if [[ -n "$joined" ]]; then
echo "${prefix}${joined}"
fi
}
MOD_ARG="$(build_mod_arg "${SAVE_DIR}/panel-mods.txt" '-mod=')"
SERVERMOD_ARG="$(build_mod_arg "${SAVE_DIR}/panel-servermods.txt" '-servermod=')"
[[ -n "${MOD_ARG}" ]] && log "loading mods: ${MOD_ARG}"
[[ -n "${SERVERMOD_ARG}" ]] && log "loading server-mods: ${SERVERMOD_ARG}"
# DayZ's documented launch command:
# ./DayZServer -config=serverDZ.cfg -port=2302 -BEpath=battleye \
# -profiles=profiles -dologs -adminlog -netlog \
# -freezecheck -doScriptLogs -cpuCount=2
# [-mod=@A;@B] [-servermod=@X]
DZ_ARGS=(
-config=serverDZ.cfg
-port="${SERVER_PORT}"
"-BEpath=${GAME_DIR}/battleye"
-profiles=profiles
-dologs
-adminlog
-netlog
-freezecheck
-doScriptLogs
"-cpuCount=${CPU_COUNT}"
)
[[ -n "${MOD_ARG}" ]] && DZ_ARGS+=("${MOD_ARG}")
[[ -n "${SERVERMOD_ARG}" ]] && DZ_ARGS+=("${SERVERMOD_ARG}")
exec stdbuf -oL -eL "${DEDI_EXE}" "${DZ_ARGS[@]}" &
DZ_PID=$!
wait "${DZ_PID}"