#!/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}"
