diff --git a/.env.example b/.env.example index 2d4fb71fe..f51a179e8 100644 --- a/.env.example +++ b/.env.example @@ -15,7 +15,16 @@ UPDATE_ON_BOOT=true RCON_ENABLED=true RCON_PORT=25575 QUERY_PORT=27015 - +BACKUP_ENABLED=true +DELETE_OLD_BACKUPS=false +OLD_BACKUP_DAYS=30 +BACKUP_CRON_EXPRESSION=0 0 * * * +AUTO_UPDATE_ENABLED=false +AUTO_UPDATE_CRON_EXPRESSION=0 * * * * +AUTO_UPDATE_WARN_MINUTES=30 +AUTO_REBOOT_ENABLED=false +AUTO_REBOOT_WARN_MINUTES=5 +AUTO_REBOOT_CRON_EXPRESSION=0 0 * * * DIFFICULTY=None DAYTIME_SPEEDRATE=1.000000 diff --git a/Dockerfile b/Dockerfile index d6dbde1cb..5187dc946 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,6 +25,7 @@ ENV RCON_MD5SUM="8601c70dcab2f90cd842c127f700e398" \ # install rcon and supercronic SHELL ["/bin/bash", "-o", "pipefail", "-c"] + RUN wget --progress=dot:giga https://github.com/gorcon/rcon-cli/releases/download/v${RCON_VERSION}/rcon-${RCON_VERSION}-amd64_linux.tar.gz -O rcon.tar.gz \ && echo "${RCON_MD5SUM}" rcon.tar.gz | md5sum -c - \ && tar -xzvf rcon.tar.gz \ @@ -60,12 +61,17 @@ ENV PORT= \ BACKUP_CRON_EXPRESSION="0 0 * * *" \ AUTO_UPDATE_ENABLED=false \ AUTO_UPDATE_CRON_EXPRESSION="0 * * * *" \ - AUTO_UPDATE_WARN_MINUTES=30 + AUTO_UPDATE_WARN_MINUTES=30 \ + AUTO_REBOOT_ENABLED=false \ + AUTO_REBOOT_WARN_MINUTES=5 \ + AUTO_REBOOT_CRON_EXPRESSION="0 0 * * *" COPY ./scripts/* /home/steam/server/ -RUN chmod +x /home/steam/server/init.sh /home/steam/server/start.sh /home/steam/server/backup.sh /home/steam/server/update.sh && \ + +RUN chmod +x /home/steam/server/*.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/update.sh /usr/local/bin/update && \ + mv /home/steam/server/restore.sh /usr/local/bin/restore WORKDIR /home/steam/server diff --git a/README.md b/README.md index 9e3dc048e..cf2ab5af5 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,9 @@ It is highly recommended you set the following environment values before startin | AUTO_UPDATE_CRON_EXPRESSION | Setting affects frequency of automatic updates. | 0 \* \* \* \* | Needs a Cron-Expression - See [Configuring Automatic Backups with Cron](#configuring-automatic-backups-with-cron) | | AUTO_UPDATE_ENABLED | Enables automatic updates | false | true/false | | AUTO_UPDATE_WARN_MINUTES | How long to wait to update the server, after the player were informed. | 30 | !0 | +| AUTO_REBOOT_CRON_EXPRESSION | Setting affects frequency of automatic updates. | 0 0 \* \* \* | Needs a Cron-Expression - See [Configuring Automatic Backups with Cron](#configuring-automatic-reboots-with-cron) | +| AUTO_REBOOT_ENABLED | Enables automatic reboots | false | true/false | +| AUTO_REBOOT_WARN_MINUTES | How long to wait to reboot the server, after the player were informed. | 5 | !0 | *highly recommended to set @@ -188,11 +191,6 @@ It is highly recommended you set the following environment values before startin *** Required for docker stop to save and gracefully close the server -> [!IMPORTANT] -> Boolean values used in environment variables are case-sensitive because they are used in the shell script. -> -> They must be set using exactly `true` or `false` for the option to take effect. - ### Game Ports | Port | Info | @@ -247,6 +245,41 @@ This will create a backup at `/palworld/backups/` The server will run a save before the backup if rcon is enabled. +## Restore from a backup + +To restore from a backup, use the command: + +```bash +docker exec -it palworld-server restore +``` + +The `RCON_ENABLED` environment variable must be set to `true` to use this command. +> [!IMPORTANT] +> If docker restart is not set to policy `always` or `unless-stopped` then the server will shutdown and will need to be +> manually restarted. +> +> The example docker run command and docker compose file in [How to Use](#how-to-use) already uses the needed policy + +## Manually restore from a backup + +Locate the backup you want to restore in `/palworld/backups/` and decompress it. + +Delete the old saved data folder located at `palworld/Pal/Saved/SaveGames/0/`. + +Copy the contents of the newly decompressed saved data folder `Saved/SaveGames/0/` to `palworld/Pal/Saved/SaveGames/0/`. + +Replace the DedicatedServerName inside `palworld/Pal/Saved/Config/LinuxServer/GameUserSettings.ini` with the new folder name. + +```ini +DedicatedServerName= # Replace it with your folder name. +``` + +Restart the game. (If you are using Docker Compose) + +```bash +docker compose down && docker compose up -d +``` + ## Configuring Automatic Backups with Cron The server is automatically backed up everynight at midnight according to the timezone set with TZ @@ -259,7 +292,7 @@ BACKUP_CRON_EXPRESSION is a cron expression, in a Cron-Expression you define an > This image uses Supercronic for crons > see [supercronic](https://github.com/aptible/supercronic#crontab-format) > or -> [Crontab Generat](https://crontab-generator.org). +> [Crontab Generator](https://crontab-generator.org). Set BACKUP_CRON_EXPRESSION to change the default schedule. Example Usage: If BACKUP_CRON_EXPRESSION to `0 2 * * *`, the backup script will run every day at 2:00 AM. @@ -286,10 +319,34 @@ AUTO_UPDATE_CRON_EXPRESSION is a cron expression, in a Cron-Expression you defin > This image uses Supercronic for crons > see [supercronic](https://github.com/aptible/supercronic#crontab-format) > or -> [Crontab Generat](https://crontab-generator.org). +> [Crontab Generator](https://crontab-generator.org). Set AUTO_UPDATE_CRON_EXPRESSION to change the default schedule. +## Configuring Automatic Reboots with Cron + +To be able to use automatic reboots with this server RCON_ENABLED enabled. + +> [!IMPORTANT] +> +> If docker restart is not set to policy `always` or `unless-stopped` then the server will shutdown and will need to be +> manually restarted. +> +> The example docker run command and docker compose file in [How to Use](#how-to-use) already use the needed policy + +Set AUTO_REBOOT_ENABLED enable or disable automatic backups (Default is disabled) + +AUTO_REBOOT_CRON_EXPRESSION is a cron expression, in a Cron-Expression you define an interval for when to run jobs. + +> [!TIP] +> This image uses Supercronic for crons +> see [supercronic](https://github.com/aptible/supercronic#crontab-format) +> or +> [Crontab Generator](https://crontab-generator.org). + +Set AUTO_REBOOT_CRON_EXPRESSION to change the set the schedule, default is everynight at midnight according to the +timezone set with TZ + ## Editing Server Settings ### With Environment Variables diff --git a/docs/kr/README.md b/docs/kr/README.md index 820f76801..a9b0dadd6 100644 --- a/docs/kr/README.md +++ b/docs/kr/README.md @@ -13,14 +13,14 @@ [English](/README.md) | [한국어](/docs/kr/README.md) | [简体中文](/docs/zh-CN/README.md) -> [!Tip] +> [!TIP] > 어떻게 시작해야 할지 모르시나요? [제가 작성한 이 가이드](https://tice.tips/containerization/palworld-server-docker/)를 확인해 보세요 [Palworld](https://store.steampowered.com/app/1623730/Palworld/) 전용 서버 호스팅을 시작하는 데 도움이 되는 Docker 컨테이너입니다. 이 도커 컨테이너는 테스트되었으며 Linux(Ubuntu/Debian), Windows 10 및 macOS (Apple Silicon 포함) 모두에서 작동합니다. -> [!Important] +> [!IMPORTANT] > 현재 Xbox Gamepass/Xbox 콘솔 플레이어는 전용 서버에 참여할 수 없습니다. > > 초대 코드를 통해 다른 플레이어들과 함께 게임을 즐길 수 있으며, 게임은 최대 4명의 플레이어로 제한됩니다. @@ -188,7 +188,7 @@ docker exec -it palworld-server rcon-cli ## 백업 만들기 -현재 시점의 게임 세이브 백업을 생성하려면 다음 명령을 사용합니다. +현재 시점의 게임 세이브 백업을 생성하려면 다음 명령을 사용합니다: ```bash docker exec palworld-server backup @@ -198,11 +198,26 @@ docker exec palworld-server backup rcon이 활성화된 경우 서버는 백업 전에 저장을 실행합니다. +## 백업 복원 하기 + +백업을 복원하려면 다음 명령을 사용합니다: + +```bash +docker exec -it palworld-server restore +``` + +복원 명령어를 사용하려면 `RCON_ENABLED` 환경 변수가 `true`여야 합니다. + +> [!IMPORTANT] +> 도커 `restart` 정책이 `always` 또는 `unless-stopped`로 설정 되어있지 않다면 복원 이후 컨테이너가 종료되므로 수동으로 재시작 해야 합니다. +> +> [사용하기](#사용하기)에서 제공된 Docker 실행 명령어와 Docker Compose 파일 예시는 이미 필요한 정책을 적용하고 있습니다. + ## 서버 설정 편집 ### 환경 변수 사용 설정 -> [!Important] +> [!IMPORTANT] > > 게임이 아직 베타버전이므로 이러한 환경 변수/설정은 변경될 수 있습니다 @@ -276,7 +291,7 @@ rcon이 활성화된 경우 서버는 백업 전에 저장을 실행합니다. 서버 설정에 대한 자세한 설명 목록을 보려면 다음을 참조하세요: [shockbyte](https://shockbyte.com/billing/knowledgebase/1189/How-to-Configure-your-Palworld-server.html) -> [!Tip] +> [!TIP] > 만약 `/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini` 파일 내부가 비어 있으면, > 파일을 삭제하고 서버를 다시 시작하면 콘텐츠가 포함된 새 파일이 생성됩니다. diff --git a/docs/zh-CN/README.md b/docs/zh-CN/README.md index 8db4d9b74..d1f72a9cc 100644 --- a/docs/zh-CN/README.md +++ b/docs/zh-CN/README.md @@ -19,7 +19,7 @@ 这是一个 [Docker](https://docs.docker.com/engine/install/) 容器,可帮助您创建自己的 [幻兽帕鲁](https://store.steampowered.com/app/1623730/Palworld/) 专用服务器。 -此容器经测试可正常在 (Ubuntu/Debian) 和 Windows 10 上运行。 +此容器经测试可正常在 (Ubuntu/Debian)、 Windows 10 、macOS (包括使用 Silicon 芯片的 M1 设备,通过 Rosseta2 转译)。 > [!IMPORTANT] > 目前,Xbox Game Pass/Xbox 主机玩家无法加入专用服务器。 @@ -95,6 +95,21 @@ docker run -d \ ``` +作为一个替代方案,你可以复制[.env.example](.env.example)文件,并把文件重命名为 **.env** 。 +根据您的需求,查看[环境变量](#环境变量) 部分并调整。调整你的docker启动命令如下: + +```bash +docker run -d \ + --name palworld-server \ + -p 8211:8211/udp \ + -p 27015:27015/udp \ + -v ./:/palworld/ \ + --env-file .env \ + --restart unless-stopped \ + --stop-timeout 30 \ + thijsvanloef/palworld-server-docker:latest +``` + ### Kubernetes 将此容器部署到 Kubernetes 的所有文件都位于[此文件夹中](/k8s/)。 diff --git a/scripts/auto_reboot.sh b/scripts/auto_reboot.sh new file mode 100644 index 000000000..9fa4e0fde --- /dev/null +++ b/scripts/auto_reboot.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +if [ "${RCON_ENABLED,,}" = true ]; then + if [ -z "${AUTO_REBOOT_WARN_MINUTES}" ]; then + echo "Unable to auto reboot, AUTO_REBOOT_WARN_MINUTES is empty." + elif [[ "${AUTO_REBOOT_WARN_MINUTES}" =~ ^[0-9]+$ ]]; then + for ((i = "${AUTO_REBOOT_WARN_MINUTES}" ; i > 0 ; i--)); do + rcon-cli -c /home/steam/server/rcon.yaml "broadcast The_Server_will_reboot_in_${i}_Minutes" + sleep "1m" + done + + rcon-cli -c /home/steam/server/rcon.yaml save + rcon-cli -c /home/steam/server/rcon.yaml "shutdown 1" + else + echo "Unable to auto reboot, AUTO_REBOOT_WARN_MINUTES is not an integer: ${AUTO_REBOOT_WARN_MINUTES}" + fi +else + echo "Unable to reboot. RCON is required." +fi diff --git a/scripts/backup.sh b/scripts/backup.sh index c659f0c50..805326234 100644 --- a/scripts/backup.sh +++ b/scripts/backup.sh @@ -1,6 +1,6 @@ #!/bin/bash -if [ "${RCON_ENABLED}" = true ]; then +if [ "${RCON_ENABLED,,}" = true ]; then rcon-cli -c /home/steam/server/rcon.yaml save fi @@ -17,7 +17,7 @@ fi echo "backup created at $FILE_PATH" -if [ "${DELETE_OLD_BACKUPS}" = true ]; then +if [ "${DELETE_OLD_BACKUPS,,}" = true ]; then if [ -z "${OLD_BACKUP_DAYS}" ]; then echo "Unable to deleted old backups, OLD_BACKUP_DAYS is empty." elif [[ "${OLD_BACKUP_DAYS}" =~ ^[0-9]+$ ]]; then diff --git a/scripts/init.sh b/scripts/init.sh index 8a216c23c..47102401c 100644 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -13,7 +13,7 @@ mkdir -p /palworld/backups chown -R steam:steam /palworld /home/steam/ term_handler() { - if [ "${RCON_ENABLED}" = true ]; then + if [ "${RCON_ENABLED,,}" = true ]; then rcon-cli save rcon-cli "shutdown 1" else # Does not save @@ -36,3 +36,11 @@ if [ "${#backup_pids[@]}" -ne 0 ]; then tail --pid="$pid" -f 2>/dev/null done fi + +mapfile -t restore_pids < <(pgrep restore) +if [ "${#restore_pids[@]}" -ne 0 ]; then + echo "Waiting for restore to finish" + for pid in "${restore_pids[@]}"; do + tail --pid="$pid" -f 2>/dev/null + done +fi diff --git a/scripts/restore.sh b/scripts/restore.sh new file mode 100644 index 000000000..e0f287273 --- /dev/null +++ b/scripts/restore.sh @@ -0,0 +1,126 @@ +#!/bin/bash + +# Backup file directory path +BACKUP_DIRECTORY_PATH="/palworld/backups" + +# Resotre path +RESTORE_PATH="/palworld/Pal" + +# Copy the save file before restore temporary path +TMP_SAVE_PATH="/palworld/backups/restore-"$(date +"%Y-%m-%d_%H-%M-%S") + +# shellcheck disable=SC2317 +term_error_handler() { + echo "An error occurred during server shutdown." + exit 1 +} + +# shellcheck disable=SC2317 +restore_error_handler() { + printf "\033[0;31mAn error occurred during restore.\033[0m\n" + if [ -d "$TMP_SAVE_PATH/Saved" ]; then + read -rp "I have a backup before recovery can proceed. Do you want to recovery it? (y/n): " RUN_ANSWER + if [[ ${RUN_ANSWER,,} == "y" ]]; then + rm -rf "$RESTORE_PATH/Saved" + mv "$TMP_SAVE_PATH/Saved" "$RESTORE_PATH" + printf "\e[0;32mRecovery complete.\e[0m\n" + fi + fi + + echo "Clean up the temporary directory." + rm -rf "$TMP_PATH" "$TMP_SAVE_PATH" + + exit 1 +} + +if [ "${RCON_ENABLED}" != true ]; then + echo "RCON is not enabled. Please enable RCON to use this feature." + exit 1 +fi + +# Show up backup list +echo "Backup List:" +mapfile -t BACKUP_FILES < <(find "$BACKUP_DIRECTORY_PATH" -type f -name "*.tar.gz" | sort) +select BACKUP_FILE in "${BACKUP_FILES[@]}"; do + if [ -n "$BACKUP_FILE" ]; then + echo "Selected backup: $BACKUP_FILE" + break + else + echo "Invalid selection. Please try again." + fi +done + +if [ -f "$BACKUP_FILE" ]; then + printf "\033[0;31mThis script has been designed to help you restore; however, I am not responsible for any data loss. It is recommended that you create a backup beforehand, and in the event of script failure, be prepared to restore it manually.\033[0m\n" + echo "Do you understand the above and would you like to proceed with this command?" + read -rp "When you run it, the server will be stopped and the recovery will proceed. (y/n): " RUN_ANSWER + if [[ ${RUN_ANSWER,,} == "y" ]]; then + printf "\e[0;32m*****STARTING PROCESS*****\e[0m\n" + # Shutdown server + trap 'term_error_handler' ERR + + if [ "${RCON_ENABLED}" = true ]; then + printf "\e[0;32m*****SHUTDOWN SERVER*****\e[0m\n" + rcon-cli -c /home/steam/server/rcon.yaml save + rcon-cli -c /home/steam/server/rcon.yaml "shutdown 1" + else + echo "RCON is not enabled. Please enable RCON to use this feature. Unable to restore backup." + exit 1 + fi + printf "\e[0;32mShutdown complete.\e[0m\n" + + trap - ERR + + trap 'restore_error_handler' ERR + + printf "\e[0;32m*****START RESTORE*****\e[0m\n" + + # Recheck the backup file + if [ -f "$BACKUP_FILE" ]; then + # Copy the save file before restore + if [ -d "$RESTORE_PATH/Saved" ]; then + echo "Saves the current state before the restore proceeds." + echo "$TMP_SAVE_PATH" + mkdir -p "$TMP_SAVE_PATH" + if [ "$(id -u)" -eq 0 ]; then + chown steam:steam "$TMP_SAVE_PATH" + fi + \cp -rf "$RESTORE_PATH/Saved" "$TMP_SAVE_PATH/Saved" + + while [ ! -d "$TMP_SAVE_PATH/Saved" ]; do + sleep 1 + done + + printf "\e[0;32mSave complete.\e[0m\n" + fi + + # Create tmp directory + TMP_PATH=$(mktemp -d -p "/palworld/backups") + if [ "$(id -u)" -eq 0 ]; then + chown steam:steam "$TMP_PATH" + fi + + # Decompress the backup file in tmp directory + tar -zxvf "$BACKUP_FILE" -C "$TMP_PATH" + + # Move the backup file to the restore directory + \cp -rf -f "$TMP_PATH/Saved/" "$RESTORE_PATH" + + echo "Clean up the temporary directory." + rm -rf "$TMP_PATH" "$TMP_SAVE_PATH" + + printf "\e[0;32mRestore complete!!!! Please restart the Docker container\e[0m\n" + + exit 0 + else + echo "The selected backup file does not exist." + exit 1 + fi + else + echo "Abort the recovery." + exit 1 + fi +else + echo "The selected backup file does not exist." + exit 1 +fi \ No newline at end of file diff --git a/scripts/start.sh b/scripts/start.sh index ab86d3b5f..082d92b1d 100644 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -1,6 +1,6 @@ #!/bin/bash -if [ "${UPDATE_ON_BOOT}" = true ]; then +if [ "${UPDATE_ON_BOOT,,}" = true ]; then printf "\e[0;32m*****STARTING INSTALL/UPDATE*****\e[0m\n" /home/steam/steamcmd/steamcmd.sh +@sSteamCmdForcePlatformType linux +@sSteamCmdForcePlatformBitness 64 +force_install_dir "/palworld" +login anonymous +app_update 2394010 validate +quit fi @@ -15,11 +15,11 @@ if [ -n "${QUERY_PORT}" ]; then STARTCOMMAND+=("-queryport=${QUERY_PORT}") fi -if [ "${COMMUNITY}" = true ]; then +if [ "${COMMUNITY,,}" = true ]; then STARTCOMMAND+=("EpicApp=PalServer") fi -if [ "${MULTITHREADING}" = true ]; then +if [ "${MULTITHREADING,,}" = true ]; then STARTCOMMAND+=("-useperfthreads" "-NoAsyncLoadingThread" "-UseMultithreadForDS") fi @@ -291,8 +291,8 @@ if [ -n "${BAN_LIST_URL}" ]; then echo "BAN_LIST_URL=$BAN_LIST_URL" sed -E -i "s~BanListURL=\"[^\"]*\"~BanListURL=\"$BAN_LIST_URL\"~" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi -if [ -n "${RCON_ENABLED}" ]; then - echo "RCON_ENABLED=${RCON_ENABLED}" +if [ -n "${RCON_ENABLED,,}" ]; then + echo "RCON_ENABLED=${RCON_ENABLED,,}" sed -i "s/RCONEnabled=[a-zA-Z]*/RCONEnabled=$RCON_ENABLED/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${RCON_PORT}" ]; then @@ -301,18 +301,24 @@ if [ -n "${RCON_PORT}" ]; then fi rm -f "/home/steam/server/crontab" -if [ "${BACKUP_ENABLED}" = true ]; then - echo "BACKUP_ENABLED=${BACKUP_ENABLED}" +if [ "${BACKUP_ENABLED,,}" = true ]; then + echo "BACKUP_ENABLED=${BACKUP_ENABLED,,}" echo "$BACKUP_CRON_EXPRESSION bash /usr/local/bin/backup" >> "/home/steam/server/crontab" fi -if [ "${AUTO_UPDATE_ENABLED}" = true ] && [ "${UPDATE_ON_BOOT}" = true ]; then - echo "AUTO_UPDATE_ENABLED=${AUTO_UPDATE_ENABLED}" +if [ "${AUTO_UPDATE_ENABLED,,}" = true ] && [ "${UPDATE_ON_BOOT}" = true ]; then + echo "AUTO_UPDATE_ENABLED=${AUTO_UPDATE_ENABLED,,}" echo "$AUTO_UPDATE_CRON_EXPRESSION bash /usr/local/bin/update" >> "/home/steam/server/crontab" fi -if { [ "${AUTO_UPDATE_ENABLED}" = true ] && [ "${UPDATE_ON_BOOT}" = true ]; } || [ "${BACKUP_ENABLED}" = true ]; then +if [ "${AUTO_REBOOT_ENABLED,,}" = true ] && [ "${RCON_ENABLED,,}" = true ]; then + echo "AUTO_REBOOT_ENABLED=${AUTO_REBOOT_ENABLED,,}" + echo "$AUTO_REBOOT_CRON_EXPRESSION bash /home/steam/server/auto_reboot.sh" >> "/home/steam/server/crontab" +fi + +if { [ "${AUTO_UPDATE_ENABLED,,}" = true ] && [ "${UPDATE_ON_BOOT,,}" = true ]; } || [ "${BACKUP_ENABLED,,}" = true ] || \ + [ "${AUTO_REBOOT_ENABLED,,}" = true ]; then supercronic "/home/steam/server/crontab" & fi @@ -320,7 +326,7 @@ fi cat >/home/steam/server/rcon.yaml <