diff --git a/Dockerfile b/Dockerfile index 37f8f0770..ce2cba43a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,6 +52,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ xdg-user-dirs=0.18-1 \ jo=1.9-1 \ netcat-traditional=1.10-47 \ + supervisor=4.2.5-1 \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* @@ -71,6 +72,7 @@ RUN case ${TARGETARCH} in \ && mv supercronic /usr/local/bin/supercronic ENV HOME=/home/steam \ + ARCH=${TARGETARCH} \ PORT= \ PUID=1000 \ PGID=1000 \ @@ -115,8 +117,9 @@ ENV HOME=/home/steam \ DISABLE_GENERATE_ENGINE=true COPY ./scripts /home/steam/server/ +COPY ./services /home/steam/server/services/ -RUN chmod +x /home/steam/server/*.sh && \ +RUN chmod +x /home/steam/server/*.sh /home/steam/server/services/listener/*.sh && \ mv /home/steam/server/backup.sh /usr/local/bin/backup && \ mv /home/steam/server/update.sh /usr/local/bin/update && \ mv /home/steam/server/restore.sh /usr/local/bin/restore @@ -133,4 +136,4 @@ HEALTHCHECK --start-period=5m \ CMD pgrep "PalServer-Linux" > /dev/null || exit 1 EXPOSE ${PORT} ${RCON_PORT} -ENTRYPOINT ["/home/steam/server/init.sh"] +ENTRYPOINT [ "/home/steam/server/init.sh" ] \ No newline at end of file diff --git a/scripts/helper_functions.sh b/scripts/helper_functions.sh index 20faa097a..6e586af17 100644 --- a/scripts/helper_functions.sh +++ b/scripts/helper_functions.sh @@ -103,6 +103,37 @@ get_player_count() { return "$return_val" } +# Gets the status of a service via supervisorctl +# Returns 0 if successfull +# Returns 1 if the service was not found +getServiceStatus() { + local processName="$1" + local status + status=$(supervisorctl status "$processName" | awk '{print $2}') + if [ -z "$status" ] || [ "$status" = "ERROR" ]; then + return 1 + fi + echo "$status" + return 0 +} + +# Gets the PID of a service via supervisorctl +# Returns 0 if successfull +# Returns 1 if the service was not found or is not running +getServicePID() { + local processName="$1" + local pid + if [ "$(getServiceStatus "$processName")" != "RUNNING" ]; then + return 1 + fi + pid=$(supervisorctl status "$processName" | awk '{print $4}' | sed 's/,\+$//') + if [ -z "$pid" ] || [ "$pid" = "ERROR" ]; then + return 1 + fi + echo "$pid" + return 0 +} + # # Log Definitions # diff --git a/scripts/helper_install.sh b/scripts/helper_install.sh index 523ab00d4..b4ed59c03 100644 --- a/scripts/helper_install.sh +++ b/scripts/helper_install.sh @@ -141,3 +141,45 @@ InstallServer() { CreateACFFile "$targetManifest" DiscordMessage "Install" "${DISCORD_POST_UPDATE_BOOT_MESSAGE}" "success" } + +# Builds the start command of the palworld server +# Returns 0 and outputs the start command if everything is ok +# Returns 1 if the Server is not found or has the wrong permissions +BuildStartCommand() { + local command + # Check if the architecture is arm64 + if [ "${ARCH}" == "arm64" ]; then + command=("/palworld/PalServer-arm64.sh") + else + command=("/palworld/PalServer.sh") + fi + + #Validate Installation + if ! fileExists "${command[0]}"; then + LogError "Server Not Installed Properly" + return 1 + fi + + isReadable "${command[0]}" || return 1 + isExecutable "${command[0]}" || return 1 + + # Prepare Arguments + if [ -n "${PORT}" ]; then + command+=("-port=${PORT}") + fi + + if [ -n "${QUERY_PORT}" ]; then + command+=("-queryport=${QUERY_PORT}") + fi + + if [ "${COMMUNITY,,}" = true ]; then + command+=("EpicApp=PalServer") + fi + + if [ "${MULTITHREADING,,}" = true ]; then + command+=("-useperfthreads" "-NoAsyncLoadingThread" "-UseMultithreadForDS") + fi + + echo "${command[*]}" + return 0 +} \ No newline at end of file diff --git a/scripts/init.sh b/scripts/init.sh index cf881ee17..7bb91afd7 100644 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -23,42 +23,11 @@ if ! [ -w "/palworld" ]; then fi mkdir -p /palworld/backups - -# shellcheck disable=SC2317 -term_handler() { - DiscordMessage "Shutdown" "${DISCORD_PRE_SHUTDOWN_MESSAGE}" "in-progress" - - if ! shutdown_server; then - # Does not save - kill -SIGTERM "$(pidof PalServer-Linux-Test)" - fi - - tail --pid="$killpid" -f 2>/dev/null -} - -trap 'term_handler' SIGTERM - if [[ "$(id -u)" -eq 0 ]]; then - su steam -c ./start.sh & + su steam -c ./start.sh else - ./start.sh & -fi -# Process ID of su -killpid="$!" -wait "$killpid" - -mapfile -t backup_pids < <(pgrep backup) -if [ "${#backup_pids[@]}" -ne 0 ]; then - LogInfo "Waiting for backup to finish" - for pid in "${backup_pids[@]}"; do - tail --pid="$pid" -f 2>/dev/null - done + ./start.sh fi -mapfile -t restore_pids < <(pgrep restore) -if [ "${#restore_pids[@]}" -ne 0 ]; then - LogInfo "Waiting for restore to finish" - for pid in "${restore_pids[@]}"; do - tail --pid="$pid" -f 2>/dev/null - done -fi +cp /home/steam/server/services/supervisord.conf /etc/supervisor/supervisord.conf +exec /usr/bin/supervisord --configuration=/etc/supervisor/supervisord.conf \ No newline at end of file diff --git a/scripts/server.sh b/scripts/server.sh new file mode 100644 index 000000000..ec31d0e42 --- /dev/null +++ b/scripts/server.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# shellcheck source=scripts/helper_functions.sh +source "/home/steam/server/helper_functions.sh" + +# shellcheck source=scripts/helper_install.sh +source "/home/steam/server/helper_install.sh" + +graceful_shutdown() { + if [ "${RCON_ENABLED,,}" = true ]; then + shutdown_server + wait "$PID" + fi + exit 0 +} + +trap 'graceful_shutdown' TERM INT + +STARTCOMMAND=$(BuildStartCommand) +if [ "$!" == 1 ]; then + LogWarn "Server Not Installed Properly" + exit 1 +fi + +${STARTCOMMAND} & +PID=$! +wait "$PID" \ No newline at end of file diff --git a/scripts/start.sh b/scripts/start.sh index 5f7589d8b..086b38304 100644 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -12,10 +12,7 @@ isExecutable "/palworld" || exit cd /palworld || exit -# Get the architecture using dpkg -architecture=$(dpkg --print-architecture) - -if [ "$architecture" == "arm64" ] && [ "${ARM_COMPATIBILITY_MODE,,}" = true ]; then +if [ "${ARCH}" == "arm64" ] && [ "${ARM_COMPATIBILITY_MODE,,}" = true ]; then LogInfo "ARM compatibility mode enabled" export DEBUGGER="/usr/bin/qemu-i386-static" @@ -31,18 +28,7 @@ if [ "$ServerInstalled" == 1 ]; then InstallServer fi -# Update Only If Already Installed -if [ "$ServerInstalled" == 0 ] && [ "${UPDATE_ON_BOOT,,}" == true ]; then - UpdateRequired - IsUpdateRequired=$? - if [ "$IsUpdateRequired" == 0 ]; then - LogAction "Starting Update" - InstallServer - fi -fi - -# Check if the architecture is arm64 -if [ "$architecture" == "arm64" ]; then +if [ "${ARCH}" == "arm64" ]; then # create an arm64 version of ./PalServer.sh cp ./PalServer.sh ./PalServer-arm64.sh @@ -72,6 +58,18 @@ else fi +# Update Only If Already Installed +if [ "$ServerInstalled" == 0 ] && [ "${UPDATE_ON_BOOT,,}" == true ]; then + UpdateRequired + IsUpdateRequired=$? + if [ "$IsUpdateRequired" == 0 ]; then + LogAction "Starting Update" + InstallServer + fi +fi + + + #Validate Installation if ! fileExists "${STARTCOMMAND[0]}"; then LogError "Server Not Installed Properly" @@ -111,7 +109,7 @@ if [ "${DISABLE_GENERATE_SETTINGS,,}" = true ]; then if [ ! "$(grep -s '[^[:space:]]' /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini)" ]; then LogAction "GENERATING CONFIG" # Server will generate all ini files after first run. - if [ "$architecture" == "arm64" ]; then + if [ "${ARCH}" == "arm64" ]; then timeout --preserve-status 15s ./PalServer-arm64.sh 1> /dev/null else timeout --preserve-status 15s ./PalServer.sh 1> /dev/null @@ -153,14 +151,6 @@ if [ "${AUTO_REBOOT_ENABLED,,}" = true ] && [ "${RCON_ENABLED,,}" = true ]; then supercronic -quiet -test "/home/steam/server/crontab" || exit fi -if { [ "${AUTO_UPDATE_ENABLED,,}" = true ] && [ "${UPDATE_ON_BOOT,,}" = true ]; } || [ "${BACKUP_ENABLED,,}" = true ] || \ - [ "${AUTO_REBOOT_ENABLED,,}" = true ]; then - supercronic "/home/steam/server/crontab" & - LogInfo "Cronjobs started" -else - LogInfo "No Cronjobs found" -fi - # Configure RCON settings cat >/home/steam/server/rcon.yaml <&2 + DiscordMessage "Server is starting" "in-progress" >&2 + ;; + PROCESS_STATE_RUNNING) + LogAction "*****STARTED SERVER*****" >&2 + DiscordMessage "${DISCORD_PRE_START_MESSAGE}" "success" >&2 + ;; + PROCESS_STATE_STOPPING) + LogAction "*****STOPPING SERVER*****" >&2 + DiscordMessage "${DISCORD_PRE_SHUTDOWN_MESSAGE}" "in-progress" >&2 + ;; + PROCESS_STATE_STOPPED|PROCESS_STATE_EXITED) + LogAction "*****STOPPED SERVER*****" >&2 + DiscordMessage "${DISCORD_POST_SHUTDOWN_MESSAGE}" "failure" >&2 + ;; + PROCESS_STATE_BACKOFF) + LogAction "*****FAILED TO START (RETRY)*****" >&2 + ;; + PROCESS_STATE_FATAL) + LogAction "*****COULD NOT START SERVER*****" >&2 + DiscordMessage "Server was not able to Start. Please check your configuration." "failure" >&2 + ;; + *) + LogError "Unkown event occured: $header" "failure" >&2 + ;; + esac + fi + printf "RESULT 2\nOK" +done +exit 0 \ No newline at end of file diff --git a/services/supervisord.conf b/services/supervisord.conf new file mode 100644 index 000000000..9192d6b8d --- /dev/null +++ b/services/supervisord.conf @@ -0,0 +1,24 @@ +[supervisord] +nodaemon=true +logfile=%(ENV_HOME)s/server/services/supervisord.log +logfile_maxbytes=0 +loglevel=warn +pidfile=/tmp/supervisord.pid +user=root + +[supervisorctl] +serverurl=unix:///tmp/supervisor.sock +username=worldofpals +password=worldofpals + +[unix_http_server] +file=/tmp/supervisor.sock +chmod=0700 +username=worldofpals +password=worldofpals + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[include] +files = /home/steam/server/services/conf.d/*.conf