#!/usr/bin/env bash # # Game-server role of the combined image. The panel runs the same image with # its default command; this script is reached only when the panel creates a # server container and overrides the entrypoint. set -euo pipefail INSTALL_DIR=/home/steam/pz DATA_DIR=/home/steam/Zomboid STEAMCMD=/opt/steamcmd/steamcmd.sh # Defaults live here rather than as image-level ENV, because this image also # runs the panel and those variables would be meaningless there. With `set -u` # an unset variable is a fatal error, so every one the script reads needs a # default even when the panel normally supplies it. : "${PZ_SERVER_NAME:=servertest}" : "${PZ_ADMIN_PASSWORD:=}" : "${PZ_MEMORY:=4096m}" : "${PZ_STEAM_BRANCH:=}" : "${PZ_SKIP_UPDATE:=false}" : "${PZ_UPDATE_RETRIES:=3}" # steamcmd bootstraps into $HOME/Steam; the game-server uid owns /home/steam. : "${HOME:=/home/steam}" export HOME log() { echo "[entrypoint] $*"; } # --- 0. Fail fast on unwritable mounts -------------------------------------- # SteamCMD does not error on an unwritable force_install_dir; it quietly # installs into ~/Steam instead. Catch that here rather than after a 7 GB # download that lands nowhere useful. for dir in "${INSTALL_DIR}" "${DATA_DIR}"; do if [ ! -w "${dir}" ]; then log "FATAL: ${dir} is not writable by uid $(id -u)." log " Ensure the mounted directory is owned by uid 1000 on the host." exit 1 fi done # Without a password the server prompts on stdin for one, and with no TTY that # is an immediate NoSuchElementException after the whole 7 GB install. if [ -z "${PZ_ADMIN_PASSWORD}" ]; then log "FATAL: PZ_ADMIN_PASSWORD is empty." log " The server requires an admin password and cannot prompt for one" log " in a container. Set PZ_ADMIN_PASSWORD and start again." exit 1 fi # --- 1. Install / update the dedicated server ------------------------------- # App 380870 is the free dedicated server, anonymous login. Build 42 has been # the default branch since 42.20 went stable, so PZ_STEAM_BRANCH is only needed # to pin something else (e.g. "legacy41" for Build 41 mod compatibility). installed() { [ -x "${INSTALL_DIR}/start-server.sh" ]; } if [ "${PZ_SKIP_UPDATE}" != "true" ]; then BRANCH_ARGS=() if [ -n "${PZ_STEAM_BRANCH}" ]; then BRANCH_ARGS=(-beta "${PZ_STEAM_BRANCH}") fi # force_install_dir MUST precede login. With the order reversed, SteamCMD # ignores it and fails with "Missing configuration". # # That same error also shows up intermittently on a correct invocation — it # is a transient Steam backend condition that succeeds on retry. Without this # loop a blip leaves the container to be restarted forever by Docker. attempt=1 updated=false while [ "${attempt}" -le "${PZ_UPDATE_RETRIES}" ]; do log "Updating dedicated server (app 380870), attempt ${attempt}/${PZ_UPDATE_RETRIES}..." if "${STEAMCMD}" \ +force_install_dir "${INSTALL_DIR}" \ +login anonymous \ +app_update 380870 "${BRANCH_ARGS[@]}" validate \ +quit; then updated=true break fi if [ "${attempt}" -lt "${PZ_UPDATE_RETRIES}" ]; then log "Attempt ${attempt} failed, retrying in $((attempt * 15))s..." sleep "$((attempt * 15))" fi attempt=$((attempt + 1)) done if [ "${updated}" = true ]; then log "Update complete." elif installed; then # A Steam outage should not take down a server whose files are already # complete — boot the existing install rather than failing. log "WARNING: update failed, starting the existing install instead." else log "FATAL: no server install at ${INSTALL_DIR} and the update failed." exit 1 fi else log "PZ_SKIP_UPDATE=true, skipping SteamCMD." if ! installed; then log "FATAL: PZ_SKIP_UPDATE=true but ${INSTALL_DIR}/start-server.sh is missing." exit 1 fi fi # --- 2. Apply the heap size -------------------------------------------------- # start-server.sh reads JVM args from ProjectZomboid64.json. Rewriting -Xmx # there is the only override that survives across server versions. PZ_JSON="${INSTALL_DIR}/ProjectZomboid64.json" if [ -f "${PZ_JSON}" ]; then log "Setting max heap to ${PZ_MEMORY}" sed -i -E "s/\"-Xmx[0-9]+[mgMG]\"/\"-Xmx${PZ_MEMORY}\"/" "${PZ_JSON}" fi # --- 3. Hand off to the server ---------------------------------------------- # The panel writes ${DATA_DIR}/Server/.ini before starting us, so the # server picks up its config on first boot instead of generating a default. mkdir -p "${DATA_DIR}/Server" cd "${INSTALL_DIR}" ARGS=( -cachedir="${DATA_DIR}" -servername "${PZ_SERVER_NAME}" -adminpassword "${PZ_ADMIN_PASSWORD}" ) log "Starting server '${PZ_SERVER_NAME}'" exec ./start-server.sh "${ARGS[@]}" "$@"