Category - Palworld

자체 광고 때리는 중..
1~5/5
  • Docker로 Palworld 서버 열기 (ARM64) Ver. 2

    2024-02-09 21:34:54 Palworld 서버를 24시간 열어둘 경우 생기는 문제 어제 위 글을 작성하고 또 무슨 기능이 생겼나 구경하려고 들어갔는데.. ARM 버전이 생겼다? 그것도 FEX-Emu를 사용하지 않는다?? 이게 되면 바로 갈아탈 마음에 바로 사용해 봤다. 실행 docker-compose.yml 파일 작성 후 docker compose up -d 실행 version: "3.9" services: palworld: container_name: palworld restart: unless-stopped stop_grace_period: 30s image: thijsvanloef/palworld-server-docker:latest-arm64 ports: - 8211:8211/udp - 27015:27015/udp env_file: - .env - settings.env environment: - PUID=1001 - PGID=1001 - MULTITHREADING=true - UPDATE_ON_BOOT=true - TZ=Asia/Seoul volumes: - ./server:/palworld networks: games: ipv4_address: 172.16.11.11 networks: games: name: games external: true 이 글에서 image 값 끝에 -arm64 추가한 것 외엔 차이가 없다. PUID, PGID는 클라우드로 옮기면서 변경한 것. 문제 발생 처음엔 잘 되는가 싶더니 컨테이너를 그냥 재시작만 했는데 에러가 났다. docker logs -f palworld로 확인한 결과 재시작을 할 때마다 정상적으로 돌아갈 때도 있고, 아래처럼 로그가 찍히면서 혼자 재시작을 하기도 한다. ... *****STARTING SERVER***** ./PalServer-arm64.sh -port=8211 -queryport=27015 EpicApp=PalServer -useperfthreads -NoAsyncLoadingThread -UseMultithreadForDS time="2024-02-08T20:20:05+09:00" level=info msg="read crontab: /home/steam/server/crontab" [S_API] SteamAPI_Init(): Loaded local 'steamclient.so' OK. Shutdown handler: initalize. Increasing per-process limit of core file size to infinity. - Existing per-process limit (soft=18446744073709551615, hard=18446744073709551615) is enough for us (need only 18446744073709551615) CAppInfoCacheReadFromDiskThread took 24 milliseconds to initialize Steam Service Error: Failed to get Steam Service Start function Setting breakpad minidump AppID = 2394010 [S_API FAIL] Tried to access Steam interface SteamUser021 before SteamAPI_Init succeeded. [S_API FAIL] Tried to access Steam interface SteamFriends017 before SteamAPI_Init succeeded. [S_API FAIL] Tried to access Steam interface STEAMAPPS_INTERFACE_VERSION008 before SteamAPI_Init succeeded. [S_API FAIL] Tried to access Steam interface SteamNetworkingUtils004 before SteamAPI_Init succeeded. free(): invalid next size (normal) Signal 6 caught. Malloc Size=262146 LargeMemoryPoolOffset=262162 Malloc Size=131160 LargeMemoryPoolOffset=393352 Malloc Size=131160 LargeMemoryPoolOffset=524536 LogPakFile: Display: Found Pak file ../../../Engine/Programs/CrashReportClient/Content/Paks/CrashReportClient.pak attempting to mount. LogPakFile: Display: Mounting pak file ../../../Engine/Programs/CrashReportClient/Content/Paks/CrashReportClient.pak. LogPakFile: Display: Mounted Pak file '../../../Engine/Programs/CrashReportClient/Content/Paks/CrashReportClient.pak', mount point: '../../../Engine/' LogICUInternationalization: ICU TimeZone Detection - Raw Offset: +9:00, Platform Override: '' LogInit: Build: ++UE5+Release-5.1-CL-0 LogInit: Engine Version: 5.1.1-0+++UE5+Release-5.1 LogInit: Compatible Engine Version: 5.1.0-0+++UE5+Release-5.1 LogInit: Net CL: 0 LogInit: OS: Debian GNU/Linux 11 (bullseye) (5.15.0-1052-realtime), CPU: Box64 on Neoverse-N1 @1000 MHz, GPU: UnknownVendor PCI-id: 108e-0010 LogInit: Compiled (64-bit): Dec 31 2023 20:12:22 LogInit: Compiled with Clang: 13.0.1 (https://github.com/llvm/llvm-project 75e33f71c2dae584b13a7d1186ae0a038ba98838) LogInit: Build Configuration: Shipping LogInit: Branch Name: ++UE5+Release-5.1 LogInit: Command Line: -Abslog="/palworld/Pal/Saved/Logs/Pal-CRC.log" -Unattended -ImplicitSend "/palworld/Pal/Saved/Crashes/crashinfo-Pal-pid-111-AE37D7ED12904D579713C0111B657EBB/" -unattended LogInit: Base Directory: /palworld/Engine/Binaries/Linux/ LogInit: Allocator: Mimalloc LogInit: Installed Engine Build: 1 LogInit: Presizing for max 100000 objects, including 0 objects not considered by GC, pre-allocating 0 bytes for permanent pool. LogInit: Object subsystem initialized LogConfig: Applying CVar settings from Section [ConsoleVariables] File [Engine] [2024.02.08-11.20.29:881][ 0]LogInit: Unix hardware info: [2024.02.08-11.20.29:881][ 0]LogInit: - we are the first instance of this executable [2024.02.08-11.20.29:881][ 0]LogInit: - this process' id (pid) is 167, parent process' id (ppid) is 111 [2024.02.08-11.20.29:882][ 0]LogInit: - we are not running under debugger [2024.02.08-11.20.29:882][ 0]LogInit: - machine network name is 'a4f09a0272ae' [2024.02.08-11.20.29:882][ 0]LogInit: - user name is 'steam' (steam) [2024.02.08-11.20.29:883][ 0]LogInit: - we're logged in locally [2024.02.08-11.20.29:883][ 0]LogInit: - we're running with rendering [2024.02.08-11.20.29:883][ 0]LogInit: - CPU: GenuineIntel 'Box64 on Neoverse-N1 @1000 MHz' (signature: 0x601) [2024.02.08-11.20.29:883][ 0]LogInit: - Number of physical cores available for the process: 4 [2024.02.08-11.20.29:883][ 0]LogInit: - Number of logical cores available for the process: 4 [2024.02.08-11.20.29:883][ 0]LogInit: - Cache line size: 64 [2024.02.08-11.20.29:883][ 0]LogInit: - GPU Brand Info: UnknownVendor PCI-id: 108e-0010 [2024.02.08-11.20.29:883][ 0]LogInit: - Memory allocator used: Mimalloc [2024.02.08-11.20.29:884][ 0]LogInit: - This binary is optimized with LTO: no, PGO: no, instrumented for PGO data collection: no [2024.02.08-11.20.29:884][ 0]LogInit: - This is an internal build. [2024.02.08-11.20.29:885][ 0]LogCore: Skipped benchmarking clocks because the engine is running in a standalone program mode - CLOCK_MONOTONIC will be used. [2024.02.08-11.20.29:885][ 0]LogInit: Unix-specific commandline switches: [2024.02.08-11.20.29:885][ 0]LogInit: -ansimalloc - use malloc()/free() from libc (useful for tools like valgrind and electric fence) [2024.02.08-11.20.29:885][ 0]LogInit: -jemalloc - use jemalloc for all memory allocation [2024.02.08-11.20.29:885][ 0]LogInit: -binnedmalloc - use binned malloc for all memory allocation [2024.02.08-11.20.29:885][ 0]LogInit: -filemapcachesize=NUMBER - set the size for case-sensitive file mapping cache [2024.02.08-11.20.29:885][ 0]LogInit: -useksm - uses kernel same-page mapping (KSM) for mapped memory (OFF) [2024.02.08-11.20.29:885][ 0]LogInit: -ksmmergeall - marks all mmap'd memory pages suitable for KSM (OFF) [2024.02.08-11.20.29:885][ 0]LogInit: -preloadmodulesymbols - Loads the main module symbols file into memory (OFF) [2024.02.08-11.20.29:885][ 0]LogInit: -sigdfl=SIGNAL - Allows a specific signal to be set to its default handler rather then ignoring the signal [2024.02.08-11.20.29:885][ 0]LogInit: -crashhandlerstacksize - Allows setting crash handler stack sizes (204800) [2024.02.08-11.20.29:885][ 0]LogInit: -noexclusivelockonwrite - disables marking files created by the engine as exclusive locked while the engine has them opened [2024.02.08-11.20.29:885][ 0]LogInit: -httpproxy=ADDRESS:PORT - redirects HTTP requests to a proxy (only supported if compiled with libcurl) [2024.02.08-11.20.29:886][ 0]LogInit: -reuseconn - allow libcurl to reuse HTTP connections (only matters if compiled with libcurl) [2024.02.08-11.20.29:886][ 0]LogInit: -virtmemkb=NUMBER - sets process virtual memory (address space) limit (overrides VirtualMemoryLimitInKB value from .ini) [2024.02.08-11.20.29:886][ 0]LogInit: - Physical RAM available (not considering process quota): 24 GB (23989 MB, 24564872 KB, 25154428928 bytes) [2024.02.08-11.20.29:886][ 0]LogInit: - VirtualMemoryAllocator pools will grow at scale 1.4 [2024.02.08-11.20.29:886][ 0]LogInit: - MemoryRangeDecommit() will be a no-op (re-run with -vmapoolevict to change) [2024.02.08-11.20.29:886][ 0]LogInit: - PageSize 4096 [2024.02.08-11.20.29:886][ 0]LogInit: - BinnedPageSize 65536 [2024.02.08-11.20.30:006][ 0]LogUObjectArray: 419 objects as part of root set at end of initial load. [2024.02.08-11.20.30:006][ 0]LogUObjectAllocator: 89056 out of 0 bytes used by permanent object pool. [2024.02.08-11.20.30:006][ 0]LogUObjectArray: CloseDisregardForGC: 0/0 objects in disregard for GC pool [2024.02.08-11.20.30:012][ 0]LogPaths: Warning: No paths for game localization data were specifed in the game configuration. [2024.02.08-11.20.30:013][ 0]LogInit: Using OS detected language (en-US-POSIX). [2024.02.08-11.20.30:013][ 0]LogInit: Using OS detected locale (en-US-POSIX). [2024.02.08-11.20.30:014][ 0]LogInit: Warning: No paths for engine localization data were specifed in the engine configuration. [2024.02.08-11.20.30:020][ 0]LogTextLocalizationManager: No localization for 'en-US-POSIX' exists, so 'en' will be used for the language. [2024.02.08-11.20.30:021][ 0]LogTextLocalizationManager: No localization for 'en-US-POSIX' exists, so 'en' will be used for the locale. [2024.02.08-11.20.30:026][ 0]LogInit: Using OS detected language (en-US-POSIX). [2024.02.08-11.20.30:026][ 0]LogInit: Using OS detected locale (en-US-POSIX). [2024.02.08-11.20.30:026][ 0]LogTextLocalizationManager: No localization for 'en-US-POSIX' exists, so 'en' will be used for the language. [2024.02.08-11.20.30:026][ 0]LogTextLocalizationManager: No localization for 'en-US-POSIX' exists, so 'en' will be used for the locale. [2024.02.08-11.20.30:043][ 0]LogPackageLocalizationCache: Processed 2 localized package path(s) for 1 prioritized culture(s) in 0.004851 seconds [2024.02.08-11.20.30:046][ 0]CrashReportCoreLog: CrashReportClientVersion=1.0 [2024.02.08-11.20.30:046][ 0]CrashReportCoreLog: CrashReportReceiver disabled [2024.02.08-11.20.30:047][ 0]CrashReportCoreLog: DataRouterUrl: https://o1291919.ingest.sentry.io/api/6513339/unreal/4a1a3921f51f4975b4cf8dd19022cb20/ [2024.02.08-11.20.30:080][ 0]CrashReportCoreLog: Initial state = Unknown UploadState value [2024.02.08-11.20.30:080][ 0]CrashReportCoreLog: Initial state = Unknown UploadState value [2024.02.08-11.20.30:096][ 0]LogCrashDebugHelper: DepotName: //UE5/Release-5.1 [2024.02.08-11.20.30:096][ 0]LogCrashDebugHelper: BuiltFromCL: 0 [2024.02.08-11.20.30:096][ 0]LogCrashDebugHelper: EngineVersion: 5.1.1-0+++UE5+Release-5.1 [2024.02.08-11.20.30:096][ 0]LogCrashDebugHelper: BuildVersion: ++UE5+Release-5.1-CL-0 [2024.02.08-11.20.31:081][ 0]CrashReportCoreLog: Got 3 pending files to upload from 'crashinfo-Pal-pid-111-AE37D7ED12904D579713C0111B657EBB' [2024.02.08-11.20.31:081][ 0]CrashReportCoreLog: State change from Ready to SendingFiles [2024.02.08-11.20.31:081][ 0]CrashReportCoreLog: CompressAndSendData have 3 pending files [2024.02.08-11.20.31:082][ 0]CrashReportCoreLog: CompressAndSendData compressing 152 bytes ('/palworld/Pal/Saved/Crashes/crashinfo-Pal-pid-111-AE37D7ED12904D579713C0111B657EBB/CrashReportClient.ini') [2024.02.08-11.20.31:083][ 0]CrashReportCoreLog: CompressAndSendData compressing 4518 bytes ('/palworld/Pal/Saved/Crashes/crashinfo-Pal-pid-111-AE37D7ED12904D579713C0111B657EBB/CrashContext.runtime-xml') [2024.02.08-11.20.31:083][ 0]CrashReportCoreLog: CompressAndSendData compressing 385 bytes ('/palworld/Pal/Saved/Crashes/crashinfo-Pal-pid-111-AE37D7ED12904D579713C0111B657EBB/Diagnostics.txt') [2024.02.08-11.20.31:206][ 0]LogInit: Using libcurl 7.83.1 [2024.02.08-11.20.31:206][ 0]LogInit: - built for Linux [2024.02.08-11.20.31:207][ 0]LogInit: - supports SSL with OpenSSL/1.1.1n [2024.02.08-11.20.31:207][ 0]LogInit: - supports HTTP deflate (compression) using libz 1.2.12 [2024.02.08-11.20.31:207][ 0]LogInit: - other features: [2024.02.08-11.20.31:207][ 0]LogInit: CURL_VERSION_SSL [2024.02.08-11.20.31:207][ 0]LogInit: CURL_VERSION_LIBZ [2024.02.08-11.20.31:207][ 0]LogInit: CURL_VERSION_IPV6 [2024.02.08-11.20.31:207][ 0]LogInit: CURL_VERSION_ASYNCHDNS [2024.02.08-11.20.31:208][ 0]LogInit: CURL_VERSION_LARGEFILE [2024.02.08-11.20.31:212][ 0]LogInit: CurlRequestOptions (configurable via config and command line): [2024.02.08-11.20.31:212][ 0]LogInit: - bVerifyPeer = false - Libcurl will NOT verify peer certificate [2024.02.08-11.20.31:212][ 0]LogInit: - bUseHttpProxy = false - Libcurl will NOT use HTTP proxy [2024.02.08-11.20.31:213][ 0]LogInit: - bDontReuseConnections = false - Libcurl will reuse connections [2024.02.08-11.20.31:213][ 0]LogInit: - MaxHostConnections = 16 - Libcurl will limit the number of connections to a host [2024.02.08-11.20.31:213][ 0]LogInit: - LocalHostAddr = Default [2024.02.08-11.20.31:213][ 0]LogInit: - BufferSize = 65536 [2024.02.08-11.20.31:221][ 0]CrashReportCoreLog: Sending HTTP request: https://o1291919.ingest.sentry.io/api/6513339/unreal/4a1a3921f51f4975b4cf8dd19022cb20/?AppID=CrashReporter&AppVersion=5.1.1-0%2B%2B%2BUE5%2BRelease-5.1&AppEnvironment=Release&UploadType=crashreports&UserID=-000003e9%7C%7C [2024.02.08-11.20.31:563][ 0]CrashReportCoreLog: OnProcessRequestComplete(), State=SendingFiles bSucceeded=1 [2024.02.08-11.20.31:563][ 0]CrashReportCoreLog: State change from SendingFiles to SendingFiles [2024.02.08-11.20.31:564][ 0]CrashReportCoreLog: All uploads done [2024.02.08-11.20.31:564][ 0]CrashReportCoreLog: State change from SendingFiles to Finished [2024.02.08-11.20.32:130][ 0]CrashReportCoreLog: Final state (Receiver) = Finished [2024.02.08-11.20.32:130][ 0]CrashReportCoreLog: Final state (Receiver) = Unknown UploadState value [2024.02.08-11.20.32:155][ 0]LogCore: Engine exit requested (reason: CrashReportClientApp RequestExit) [2024.02.08-11.20.32:155][ 0]LogExit: Preparing to exit. [2024.02.08-11.20.32:192][ 0]LogExit: Object subsystem successfully closed. [2024.02.08-11.20.32:196][ 0]LogModuleManager: Shutting down and abandoning module HTTP (12) [2024.02.08-11.20.32:233][ 0]LogModuleManager: Shutting down and abandoning module SSL (11) [2024.02.08-11.20.32:233][ 0]LogModuleManager: Shutting down and abandoning module CrashDebugHelper (8) [2024.02.08-11.20.32:237][ 0]LogModuleManager: Shutting down and abandoning module CoreUObject (6) [2024.02.08-11.20.32:237][ 0]LogModuleManager: Shutting down and abandoning module PakFile (4) [2024.02.08-11.20.32:238][ 0]LogModuleManager: Shutting down and abandoning module RSA (3) [2024.02.08-11.20.32:247][ 0]LogExit: Exiting. CommonUnixCrashHandler: Signal=6 Engine crash handling finished; re-raising signal 6 for the default handler. Good bye. 또는 18번 라인까지만 뜨면서 재시작이 안될 때도 있는데, 이건 그냥 멈춰버린 듯. 접속 시 타임아웃이 발생한다. 해결 (처음 올린 GitHub Issue) 그냥 원래 쓰던 빌드한 이미지로 쓸까 하다가 그냥 Issues에 한 번 올려나 봤다. # 그리고 답글이 달렸고, 해결됐다! 해결 방법은 environment: - BOX64_DYNAREC_STRONGMEM=3 - BOX64_DYNAREC_BIGBLOCK=0 - BOX64_DYNAREC_BLEEDING_EDGE=0 이런 환경변수를 추가하는 것으로, 최종 docker-compose.yml 파일은 아래와 같다. version: "3.9" services: palworld: container_name: palworld restart: unless-stopped stop_grace_period: 30s image: thijsvanloef/palworld-server-docker:latest-arm64 ports: - 8211:8211/udp - 27015:27015/udp env_file: - .env - settings.env environment: - PUID=1001 - PGID=1001 - MULTITHREADING=true - UPDATE_ON_BOOT=true - TZ=Asia/Seoul - BOX64_DYNAREC_STRONGMEM=3 - BOX64_DYNAREC_BIGBLOCK=0 - BOX64_DYNAREC_BLEEDING_EDGE=0 volumes: - ./server:/palworld networks: games: ipv4_address: 172.16.11.11 networks: games: name: games external: true FEX-Emu를 사용하지 않기 때문에 이 문제도 없으니 이제 나도 이 이미지의 모든 기능들을 귀찮게 새로 빌드하지 않고 쉽게 사용할 수 있게 됐다..!
  • Docker로 Palworld 서버 열기 (ARM64)

    2024-02-09 21:25:01 Docker로 Palworld 서버 열기 (ARM64) Ver. 2 위 방법을 사용하자. 이전 글에서 작성한 과정은 노트북에서 진행했고, 서버를 24시간으로 돌리기 위해 오라클 클라우드로 옮겼는데 문제가 발생했다. 사용한 이미지가 AMD 버전만 지원한다는 것을 몰랐다. 그래서 ARM 버전의 이미지가 있는지 찾아보던 중 이전 글에서 사용한 이미지를 ARM 버전으로 Fork한 저장소를 찾았고, 바로 도전. …하려고 했으나 앞서 말했듯 기존 이미지의 업데이트가 매우 빠르기 때문에 Fork한 저장소도 벌써 매우 구 버전이 되어버렸다. 그래서 이 이미지를 바로 사용하기보다는 어떤 것들을 바꿨는지 확인하고 현재 thijsvanloef/palworld-server-docker 저장소의 최신 버전에서 해당 방법을 적용하여 직접 이미지를 빌드해서 사용하고 있다. Dockerfile 최종 Dockerfile의 내용 주요 내용은 AMD 버전만 지원하는 것을 ARM에서 실행하기 위해 FEX-Emu를 사용하는 것이다. # ARM steamcmd: https://github.com/TeriyakiGod/steamcmd-docker-arm64 # FROM teriyakigod/steamcmd:arm64 # CPU_MHZ Error FROM ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && \ apt-get install -y \ git \ cmake \ ninja-build \ pkg-config \ ccache \ clang \ llvm \ lld \ binfmt-support \ libsdl2-dev \ libepoxy-dev \ libssl-dev \ python-setuptools \ g++-x86-64-linux-gnu \ nasm \ python3-clang \ libstdc++-10-dev-i386-cross \ libstdc++-10-dev-amd64-cross \ libstdc++-10-dev-arm64-cross \ squashfs-tools \ squashfuse \ libc-bin \ expect \ curl \ fuse \ wget WORKDIR /fex RUN git clone --recurse-submodules https://github.com/FEX-Emu/FEX.git && \ cd FEX && \ mkdir Build && \ cd Build && \ CC=clang CXX=clang++ cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DUSE_LINKER=lld -DENABLE_LTO=True -DBUILD_TESTS=False -DENABLE_ASSERTIONS=False -G Ninja .. && \ ninja && \ ninja install && \ ninja binfmt_misc_32 && \ ninja binfmt_misc_64 RUN useradd -m -s /bin/bash steam USER steam WORKDIR /home/steam/.fex-emu/RootFS/ RUN curl -sqL Ubuntu_22_04.tar.gz https://www.dropbox.com/scl/fi/16mhn3jrwvzapdw50gt20/Ubuntu_22_04.tar.gz?rlkey=4m256iahwtcijkpzcv8abn7nf | tar xzf - && \ echo '{"Config":{"RootFS":"Ubuntu_22_04"}}' > ../Config.json WORKDIR /home/steam/steamcmd RUN curl -sqL "https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz" | tar zxvf - \ && FEXBash -c "/home/steam/steamcmd/steamcmd.sh +quit" \ && mkdir -p ~/.steam/sdk64/ \ && ln -sf ../../steamcmd/linux64/steamclient.so ~/.steam/sdk64/ # Palworld Server on Docker: https://github.com/thijsvanloef/palworld-server-docker USER root ENV RCON_MD5SUM="8601c70dcab2f90cd842c127f700e398" \ SUPERCRONIC_SHA1SUM="512f6736450c56555e01b363144c3c9d23abed4c" \ RCON_VERSION="0.10.3" \ SUPERCRONIC_VERSION="0.2.29" 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 \ && rm rcon.tar.gz \ && echo -e "#!/usr/bin/env bash\nsu steam -c \"FEXBash -c '`pwd`/rcon-${RCON_VERSION}-amd64_linux/rcon \$@'\"" > /usr/bin/rcon-cli \ && chmod +x /usr/bin/rcon-cli RUN wget --progress=dot:giga https://github.com/aptible/supercronic/releases/download/v${SUPERCRONIC_VERSION}/supercronic-linux-arm64 -O supercronic \ && echo "${SUPERCRONIC_SHA1SUM}" supercronic | sha1sum -c - \ && chmod +x supercronic \ && mv supercronic /usr/local/bin/supercronic ENV PORT= \ PUID=1000 \ PGID=1000 \ PLAYERS= \ MULTITHREADING=false \ COMMUNITY=false \ PUBLIC_IP= \ PUBLIC_PORT= \ SERVER_PASSWORD= \ SERVER_NAME= \ ADMIN_PASSWORD= \ UPDATE_ON_BOOT=true \ RCON_ENABLED=true \ RCON_PORT=25575 \ QUERY_PORT=27015 \ TZ=UTC \ SERVER_DESCRIPTION= \ 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 * * *" COPY ./scripts/* /home/steam/server/ 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/restore.sh /usr/local/bin/restore WORKDIR /home/steam/server HEALTHCHECK --start-period=5m \ CMD pgrep "PalServer-Linux" > /dev/null || exit 1 EXPOSE ${PORT} ${RCON_PORT} ENTRYPOINT ["/home/steam/server/init.sh"] 내가 변경한 주요 내용은 FEX-Emu 설치를 위해 FEX 유저를 만들고 패스워드 없이 sudo 명령어를 사용하도록 함 => 그냥 root로 진행 rcon-cli 명령어를 기존 이미지에서 사용하는 rcon으로 복구 Fork 버전에서는 ARM64 버전을 지원하는 itzg의 rcon-cli를 사용 => rcon으로 명령어를 전송할 때마다 Weird. This response is for another request. 에러가 같이 딸려 나옴 기존 이미지에서는 gorcon의 rcon-cli를 사용 => ARM 버전은 맥 전용밖에 없기 때문에 AMD로 설치하여 FEX-Emu로 실행하도록 함 기존 이미지의 최신 버전에서 supercronic를 사용 => ARM 버전으로 변경 steamclient.so 에러 해결. 참고 steamcmd를 한 번 실행하기 때문에 이미지 용량이 커짐 => start.sh 파일에서 사용하면 됨 대신 이후에 컨테이너를 실행할 때마다 시간 단축됨 ./scripts/* 이미지 빌드 시 복사되는 스크립트 파일들이 담긴 디렉토리. 기존 이미지에서 이 파일들이 매우 많이 업데이트가 되었기 때문에 Fork 버전이 아닌 기존 저장소의 scripts 폴더를 통째로 복사하여 수정했다. init.sh #!/bin/bash :<< "END" if [[ ! "${PUID}" -eq 0 ]] && [[ ! "${PGID}" -eq 0 ]]; then printf "\e[0;32m*****EXECUTING USERMOD*****\e[0m\n" usermod -o -u "${PUID}" steam groupmod -o -g "${PGID}" steam else printf "\033[31mRunning as root is not supported, please fix your PUID and PGID!\n" exit 1 fi END mkdir -p /palworld/backups chown -R steam:steam /palworld /home/steam/server # /home/steam/ # shellcheck disable=SC2317 term_handler() { if [ "${RCON_ENABLED,,}" = true ]; then rcon-cli save rcon-cli "shutdown 1" else # Does not save kill -SIGTERM "$(pidof PalServer-Linux-Test)" fi tail --pid="$killpid" -f 2>/dev/null } trap 'term_handler' SIGTERM su steam -c ./start.sh & # Process ID of su killpid="$!" wait "$killpid" mapfile -t backup_pids < <(pgrep backup) if [ "${#backup_pids[@]}" -ne 0 ]; then echo "Waiting for backup to finish" for pid in "${backup_pids[@]}"; do 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 FEX-Emu를 사용하는 것 때문에 /home/steam/.fex-emu/RootFS의 용량이 2~3GB 정도 된다. 그래서 groupmod, chown 명령어 실행 시간이 매우 오래 걸려서 결국 PUID, PGID 설정은 주석 처리, chown 명령어는 /home/steam 대신 /home/steam/server에만 적용해서 시간 단축. 따라서 무조건 PUID, PGID는 1000으로 고정 => Dockerfile에서 유저 만들 때 지정하면 됨 start.sh #!/bin/bash dirExists() { local path="$1" local return_val=0 if ! [ -d "${path}" ]; then echo "${path} does not exist." return_val=1 fi return "$return_val" } fileExists() { local path="$1" local return_val=0 if ! [ -f "${path}" ]; then echo "${path} does not exist." return_val=1 fi return "$return_val" } isReadable() { local path="$1" local return_val=0 if ! [ -e "${path}" ]; then echo "${path} is not readable." return_val=1 fi return "$return_val" } isWritable() { local path="$1" local return_val=0 if ! [ -w "${path}" ]; then echo "${path} is not writable." return_val=1 fi return "$return_val" } isExecutable() { local path="$1" local return_val=0 if ! [ -x "${path}" ]; then echo "${path} is not executable." return_val=1 fi return "$return_val" } dirExists "/palworld" || exit isWritable "/palworld" || exit isExecutable "/palworld" || exit cd /palworld || exit if [ "${UPDATE_ON_BOOT,,}" = true ]; then printf "\e[0;32m*****STARTING INSTALL/UPDATE*****\e[0m\n" FEXBash -c '/home/steam/steamcmd/steamcmd.sh +@sSteamCmdForcePlatformType linux +@sSteamCmdForcePlatformBitness 64 +force_install_dir "/palworld" +login anonymous +app_update 2394010 validate +quit' fi STARTCOMMAND=("./PalServer.sh") if ! fileExists "${STARTCOMMAND[0]}"; then echo "Try restarting with UPDATE_ON_BOOT=true" exit 1 fi isReadable "${STARTCOMMAND[0]}" || exit isExecutable "${STARTCOMMAND[0]}" || exit if [ -n "${PORT}" ]; then STARTCOMMAND+=("-port=${PORT}") fi if [ -n "${QUERY_PORT}" ]; then STARTCOMMAND+=("-queryport=${QUERY_PORT}") fi if [ "${COMMUNITY,,}" = true ]; then STARTCOMMAND+=("EpicApp=PalServer") fi if [ "${MULTITHREADING,,}" = true ]; then STARTCOMMAND+=("-useperfthreads" "-NoAsyncLoadingThread" "-UseMultithreadForDS") fi printf "\e[0;32m*****CHECKING FOR EXISTING CONFIG*****\e[0m\n" # shellcheck disable=SC2143 if [ ! "$(grep -s '[^[:space:]]' /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini)" ]; then printf "\e[0;32m*****GENERATING CONFIG*****\e[0m\n" # Server will generate all ini files after first run. FEXBash -c 'timeout --preserve-status 15s ./PalServer.sh 1> /dev/null' # Wait for shutdown sleep 5 cp /palworld/DefaultPalWorldSettings.ini /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi fileExists "/palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini" || exit isWritable "/palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini" || exit escape_sed() { printf '%s\n' "$1" | sed -e 's:[][\/.^$*]:\\&:g' } if [ -n "${SERVER_NAME}" ]; then SERVER_NAME=$(escape_sed "$SERVER_NAME" | sed -e 's/^"\(.*\)"$/\1/' -e "s/^'\(.*\)'$/\1/") echo "SERVER_NAME=${SERVER_NAME}" sed -E -i "s/ServerName=\"[^\"]*\"/ServerName=\"$SERVER_NAME\"/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${SERVER_DESCRIPTION}" ]; then SERVER_DESCRIPTION=$(escape_sed "$SERVER_DESCRIPTION" | sed -e 's/^"\(.*\)"$/\1/' -e "s/^'\(.*\)'$/\1/") echo "SERVER_DESCRIPTION=${SERVER_DESCRIPTION}" sed -E -i "s/ServerDescription=\"[^\"]*\"/ServerDescription=\"$SERVER_DESCRIPTION\"/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${SERVER_PASSWORD}" ]; then SERVER_PASSWORD=$(sed -e 's/^"\(.*\)"$/\1/' -e "s/^'\(.*\)'$/\1/" <<< "$SERVER_PASSWORD") echo "SERVER_PASSWORD=${SERVER_PASSWORD}" sed -E -i "s/ServerPassword=\"[^\"]*\"/ServerPassword=\"$SERVER_PASSWORD\"/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${ADMIN_PASSWORD}" ]; then ADMIN_PASSWORD=$(sed -e 's/^"\(.*\)"$/\1/' -e "s/^'\(.*\)'$/\1/" <<< "$ADMIN_PASSWORD") echo "ADMIN_PASSWORD=${ADMIN_PASSWORD}" sed -E -i "s/AdminPassword=\"[^\"]*\"/AdminPassword=\"$ADMIN_PASSWORD\"/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PLAYERS}" ]; then echo "PLAYERS=${PLAYERS}" sed -E -i "s/ServerPlayerMaxNum=[0-9]*/ServerPlayerMaxNum=$PLAYERS/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PUBLIC_IP}" ]; then PUBLIC_IP=$(sed -e 's/^"\(.*\)"$/\1/' -e "s/^'\(.*\)'$/\1/" <<< "$PUBLIC_IP") echo "PUBLIC_IP=${PUBLIC_IP}" sed -E -i "s/PublicIP=\"[^\"]*\"/PublicIP=\"$PUBLIC_IP\"/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PUBLIC_PORT}" ]; then echo "PUBLIC_PORT=${PUBLIC_PORT}" sed -E -i "s/PublicPort=[0-9]*/PublicPort=$PUBLIC_PORT/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${DIFFICULTY}" ]; then echo "DIFFICULTY=$DIFFICULTY" sed -E -i "s/Difficulty=[a-zA-Z]*/Difficulty=$DIFFICULTY/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${DAYTIME_SPEEDRATE}" ]; then echo "DAYTIME_SPEEDRATE=$DAYTIME_SPEEDRATE" sed -E -i "s/DayTimeSpeedRate=[+-]?([0-9]*[.])?[0-9]+/DayTimeSpeedRate=$DAYTIME_SPEEDRATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${NIGHTTIME_SPEEDRATE}" ]; then echo "NIGHTTIME_SPEEDRATE=$NIGHTTIME_SPEEDRATE" sed -E -i "s/NightTimeSpeedRate=[+-]?([0-9]*[.])?[0-9]+/NightTimeSpeedRate=$NIGHTTIME_SPEEDRATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${EXP_RATE}" ]; then echo "EXP_RATE=$EXP_RATE" sed -E -i "s/ExpRate=[+-]?([0-9]*[.])?[0-9]+/ExpRate=$EXP_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PAL_CAPTURE_RATE}" ]; then echo "PAL_CAPTURE_RATE=$PAL_CAPTURE_RATE" sed -E -i "s/PalCaptureRate=[+-]?([0-9]*[.])?[0-9]+/PalCaptureRate=$PAL_CAPTURE_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PAL_SPAWN_NUM_RATE}" ]; then echo "PAL_SPAWN_NUM_RATE=$PAL_SPAWN_NUM_RATE" sed -E -i "s/PalSpawnNumRate=[+-]?([0-9]*[.])?[0-9]+/PalSpawnNumRate=$PAL_SPAWN_NUM_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PAL_DAMAGE_RATE_ATTACK}" ]; then echo "PAL_DAMAGE_RATE_ATTACK=$PAL_DAMAGE_RATE_ATTACK" sed -E -i "s/PalDamageRateAttack=[+-]?([0-9]*[.])?[0-9]+/PalDamageRateAttack=$PAL_DAMAGE_RATE_ATTACK/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PAL_DAMAGE_RATE_DEFENSE}" ]; then echo "PAL_DAMAGE_RATE_DEFENSE=$PAL_DAMAGE_RATE_DEFENSE" sed -E -i "s/PalDamageRateDefense=[+-]?([0-9]*[.])?[0-9]+/PalDamageRateDefense=$PAL_DAMAGE_RATE_DEFENSE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PLAYER_DAMAGE_RATE_ATTACK}" ]; then echo "PLAYER_DAMAGE_RATE_ATTACK=$PLAYER_DAMAGE_RATE_ATTACK" sed -E -i "s/PlayerDamageRateAttack=[+-]?([0-9]*[.])?[0-9]+/PlayerDamageRateAttack=$PLAYER_DAMAGE_RATE_ATTACK/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PLAYER_DAMAGE_RATE_DEFENSE}" ]; then echo "PLAYER_DAMAGE_RATE_DEFENSE=$PLAYER_DAMAGE_RATE_DEFENSE" sed -E -i "s/PlayerDamageRateDefense=[+-]?([0-9]*[.])?[0-9]+/PlayerDamageRateDefense=$PLAYER_DAMAGE_RATE_DEFENSE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PLAYER_STOMACH_DECREASE_RATE}" ]; then echo "PLAYER_STOMACH_DECREASE_RATE=$PLAYER_STOMACH_DECREASE_RATE" sed -E -i "s/PlayerStomachDecreaceRate=[+-]?([0-9]*[.])?[0-9]+/PlayerStomachDecreaceRate=$PLAYER_STOMACH_DECREASE_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PLAYER_STAMINA_DECREASE_RATE}" ]; then echo "PLAYER_STAMINA_DECREASE_RATE=$PLAYER_STAMINA_DECREASE_RATE" sed -E -i "s/PlayerStaminaDecreaceRate=[+-]?([0-9]*[.])?[0-9]+/PlayerStaminaDecreaceRate=$PLAYER_STAMINA_DECREASE_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PLAYER_AUTO_HP_REGEN_RATE}" ]; then echo "PLAYER_AUTO_HP_REGEN_RATE=$PLAYER_AUTO_HP_REGEN_RATE" sed -E -i "s/PlayerAutoHPRegeneRate=[+-]?([0-9]*[.])?[0-9]+/PlayerAutoHPRegeneRate=$PLAYER_AUTO_HP_REGEN_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PLAYER_AUTO_HP_REGEN_RATE_IN_SLEEP}" ]; then echo "PLAYER_AUTO_HP_REGEN_RATE_IN_SLEEP=$PLAYER_AUTO_HP_REGEN_RATE_IN_SLEEP" sed -E -i "s/PlayerAutoHpRegeneRateInSleep=[+-]?([0-9]*[.])?[0-9]+/PlayerAutoHpRegeneRateInSleep=$PLAYER_AUTO_HP_REGEN_RATE_IN_SLEEP/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PAL_STOMACH_DECREASE_RATE}" ]; then echo "PAL_STOMACH_DECREASE_RATE=$PAL_STOMACH_DECREASE_RATE" sed -E -i "s/PalStomachDecreaceRate=[+-]?([0-9]*[.])?[0-9]+/PalStomachDecreaceRate=$PAL_STOMACH_DECREASE_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PAL_STAMINA_DECREASE_RATE}" ]; then echo "PAL_STAMINA_DECREASE_RATE=$PAL_STAMINA_DECREASE_RATE" sed -E -i "s/PalStaminaDecreaceRate=[+-]?([0-9]*[.])?[0-9]+/PalStaminaDecreaceRate=$PAL_STAMINA_DECREASE_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PAL_AUTO_HP_REGEN_RATE}" ]; then echo "PAL_AUTO_HP_REGEN_RATE=$PAL_AUTO_HP_REGEN_RATE" sed -E -i "s/PalAutoHPRegeneRate=[+-]?([0-9]*[.])?[0-9]+/PalAutoHPRegeneRate=$PAL_AUTO_HP_REGEN_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PAL_AUTO_HP_REGEN_RATE_IN_SLEEP}" ]; then echo "PAL_AUTO_HP_REGEN_RATE_IN_SLEEP=$PAL_AUTO_HP_REGEN_RATE_IN_SLEEP" sed -E -i "s/PalAutoHpRegeneRateInSleep=[+-]?([0-9]*[.])?[0-9]+/PalAutoHpRegeneRateInSleep=$PAL_AUTO_HP_REGEN_RATE_IN_SLEEP/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${BUILD_OBJECT_DAMAGE_RATE}" ]; then echo "BUILD_OBJECT_DAMAGE_RATE=$BUILD_OBJECT_DAMAGE_RATE" sed -E -i "s/BuildObjectDamageRate=[+-]?([0-9]*[.])?[0-9]+/BuildObjectDamageRate=$BUILD_OBJECT_DAMAGE_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${BUILD_OBJECT_DETERIORATION_DAMAGE_RATE}" ]; then echo "BUILD_OBJECT_DETERIORATION_DAMAGE_RATE=$BUILD_OBJECT_DETERIORATION_DAMAGE_RATE" sed -E -i "s/BuildObjectDeteriorationDamageRate=[+-]?([0-9]*[.])?[0-9]+/BuildObjectDeteriorationDamageRate=$BUILD_OBJECT_DETERIORATION_DAMAGE_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${COLLECTION_DROP_RATE}" ]; then echo "COLLECTION_DROP_RATE=$COLLECTION_DROP_RATE" sed -E -i "s/CollectionDropRate=[+-]?([0-9]*[.])?[0-9]+/CollectionDropRate=$COLLECTION_DROP_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${COLLECTION_OBJECT_HP_RATE}" ]; then echo "COLLECTION_OBJECT_HP_RATE=$COLLECTION_OBJECT_HP_RATE" sed -E -i "s/CollectionObjectHpRate=[+-]?([0-9]*[.])?[0-9]+/CollectionObjectHpRate=$COLLECTION_OBJECT_HP_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${COLLECTION_OBJECT_RESPAWN_SPEED_RATE}" ]; then echo "COLLECTION_OBJECT_RESPAWN_SPEED_RATE=$COLLECTION_OBJECT_RESPAWN_SPEED_RATE" sed -E -i "s/CollectionObjectRespawnSpeedRate=[+-]?([0-9]*[.])?[0-9]+/CollectionObjectRespawnSpeedRate=$COLLECTION_OBJECT_RESPAWN_SPEED_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${ENEMY_DROP_ITEM_RATE}" ]; then echo "ENEMY_DROP_ITEM_RATE=$ENEMY_DROP_ITEM_RATE" sed -E -i "s/EnemyDropItemRate=[+-]?([0-9]*[.])?[0-9]+/EnemyDropItemRate=$ENEMY_DROP_ITEM_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${DEATH_PENALTY}" ]; then echo "DEATH_PENALTY=$DEATH_PENALTY" sed -E -i "s/DeathPenalty=[a-zA-Z]*/DeathPenalty=$DEATH_PENALTY/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${ENABLE_PLAYER_TO_PLAYER_DAMAGE}" ]; then echo "ENABLE_PLAYER_TO_PLAYER_DAMAGE=$ENABLE_PLAYER_TO_PLAYER_DAMAGE" sed -E -i "s/bEnablePlayerToPlayerDamage=[a-zA-Z]*/bEnablePlayerToPlayerDamage=$ENABLE_PLAYER_TO_PLAYER_DAMAGE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${ENABLE_FRIENDLY_FIRE}" ]; then echo "ENABLE_FRIENDLY_FIRE=$ENABLE_FRIENDLY_FIRE" sed -E -i "s/bEnableFriendlyFire=[a-zA-Z]*/bEnableFriendlyFire=$ENABLE_FRIENDLY_FIRE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${ENABLE_INVADER_ENEMY}" ]; then echo "ENABLE_INVADER_ENEMY=$ENABLE_INVADER_ENEMY" sed -E -i "s/bEnableInvaderEnemy=[a-zA-Z]*/bEnableInvaderEnemy=$ENABLE_INVADER_ENEMY/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${ACTIVE_UNKO}" ]; then echo "ACTIVE_UNKO=$ACTIVE_UNKO" sed -E -i "s/bActiveUNKO=[a-zA-Z]*/bActiveUNKO=$ACTIVE_UNKO/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${ENABLE_AIM_ASSIST_PAD}" ]; then echo "ENABLE_AIM_ASSIST_PAD=$ENABLE_AIM_ASSIST_PAD" sed -E -i "s/bEnableAimAssistPad=[a-zA-Z]*/bEnableAimAssistPad=$ENABLE_AIM_ASSIST_PAD/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${ENABLE_AIM_ASSIST_KEYBOARD}" ]; then echo "ENABLE_AIM_ASSIST_KEYBOARD=$ENABLE_AIM_ASSIST_KEYBOARD" sed -E -i "s/bEnableAimAssistKeyboard=[a-zA-Z]*/bEnableAimAssistKeyboard=$ENABLE_AIM_ASSIST_KEYBOARD/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${DROP_ITEM_MAX_NUM}" ]; then echo "DROP_ITEM_MAX_NUM=$DROP_ITEM_MAX_NUM" sed -E -i "s/DropItemMaxNum=[0-9]*/DropItemMaxNum=$DROP_ITEM_MAX_NUM/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${DROP_ITEM_MAX_NUM_UNKO}" ]; then echo "DROP_ITEM_MAX_NUM_UNKO=$DROP_ITEM_MAX_NUM_UNKO" sed -E -i "s/DropItemMaxNum_UNKO=[0-9]*/DropItemMaxNum_UNKO=$DROP_ITEM_MAX_NUM_UNKO/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${BASE_CAMP_MAX_NUM}" ]; then echo "BASE_CAMP_MAX_NUM=$BASE_CAMP_MAX_NUM" sed -E -i "s/BaseCampMaxNum=[0-9]*/BaseCampMaxNum=$BASE_CAMP_MAX_NUM/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${BASE_CAMP_WORKER_MAXNUM}" ]; then echo "BASE_CAMP_WORKER_MAXNUM=$BASE_CAMP_WORKER_MAXNUM" sed -E -i "s/BaseCampWorkerMaxNum=[0-9]*/BaseCampWorkerMaxNum=$BASE_CAMP_WORKER_MAXNUM/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${DROP_ITEM_ALIVE_MAX_HOURS}" ]; then echo "DROP_ITEM_ALIVE_MAX_HOURS=$DROP_ITEM_ALIVE_MAX_HOURS" sed -E -i "s/DropItemAliveMaxHours=[+-]?([0-9]*[.])?[0-9]+/DropItemAliveMaxHours=$DROP_ITEM_ALIVE_MAX_HOURS/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${AUTO_RESET_GUILD_NO_ONLINE_PLAYERS}" ]; then echo "AUTO_RESET_GUILD_NO_ONLINE_PLAYERS=$AUTO_RESET_GUILD_NO_ONLINE_PLAYERS" sed -E -i "s/bAutoResetGuildNoOnlinePlayers=[a-zA-Z]*/bAutoResetGuildNoOnlinePlayers=$AUTO_RESET_GUILD_NO_ONLINE_PLAYERS/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${AUTO_RESET_GUILD_TIME_NO_ONLINE_PLAYERS}" ]; then echo "AUTO_RESET_GUILD_TIME_NO_ONLINE_PLAYERS=$AUTO_RESET_GUILD_TIME_NO_ONLINE_PLAYERS" sed -E -i "s/AutoResetGuildTimeNoOnlinePlayers=[+-]?([0-9]*[.])?[0-9]+/AutoResetGuildTimeNoOnlinePlayers=$AUTO_RESET_GUILD_TIME_NO_ONLINE_PLAYERS/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${GUILD_PLAYER_MAX_NUM}" ]; then echo "GUILD_PLAYER_MAX_NUM=$GUILD_PLAYER_MAX_NUM" sed -E -i "s/GuildPlayerMaxNum=[0-9]*/GuildPlayerMaxNum=$GUILD_PLAYER_MAX_NUM/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${PAL_EGG_DEFAULT_HATCHING_TIME}" ]; then echo "PAL_EGG_DEFAULT_HATCHING_TIME=$PAL_EGG_DEFAULT_HATCHING_TIME" sed -E -i "s/PalEggDefaultHatchingTime=[+-]?([0-9]*[.])?[0-9]+/PalEggDefaultHatchingTime=$PAL_EGG_DEFAULT_HATCHING_TIME/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${WORK_SPEED_RATE}" ]; then echo "WORK_SPEED_RATE=$WORK_SPEED_RATE" sed -E -i "s/WorkSpeedRate=[+-]?([0-9]*[.])?[0-9]+/WorkSpeedRate=$WORK_SPEED_RATE/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${IS_MULTIPLAY}" ]; then echo "IS_MULTIPLAY=$IS_MULTIPLAY" sed -E -i "s/bIsMultiplay=[a-zA-Z]*/bIsMultiplay=$IS_MULTIPLAY/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${IS_PVP}" ]; then echo "IS_PVP=$IS_PVP" sed -E -i "s/bIsPvP=[a-zA-Z]*/bIsPvP=$IS_PVP/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${CAN_PICKUP_OTHER_GUILD_DEATH_PENALTY_DROP}" ]; then echo "CAN_PICKUP_OTHER_GUILD_DEATH_PENALTY_DROP=$CAN_PICKUP_OTHER_GUILD_DEATH_PENALTY_DROP" sed -E -i "s/bCanPickupOtherGuildDeathPenaltyDrop=[a-zA-Z]*/bCanPickupOtherGuildDeathPenaltyDrop=$CAN_PICKUP_OTHER_GUILD_DEATH_PENALTY_DROP/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${ENABLE_NON_LOGIN_PENALTY}" ]; then echo "ENABLE_NON_LOGIN_PENALTY=$ENABLE_NON_LOGIN_PENALTY" sed -E -i "s/bEnableNonLoginPenalty=[a-zA-Z]*/bEnableNonLoginPenalty=$ENABLE_NON_LOGIN_PENALTY/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${ENABLE_FAST_TRAVEL}" ]; then echo "ENABLE_FAST_TRAVEL=$ENABLE_FAST_TRAVEL" sed -E -i "s/bEnableFastTravel=[a-zA-Z]*/bEnableFastTravel=$ENABLE_FAST_TRAVEL/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${IS_START_LOCATION_SELECT_BY_MAP}" ]; then echo "IS_START_LOCATION_SELECT_BY_MAP=$IS_START_LOCATION_SELECT_BY_MAP" sed -E -i "s/bIsStartLocationSelectByMap=[a-zA-Z]*/bIsStartLocationSelectByMap=$IS_START_LOCATION_SELECT_BY_MAP/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${EXIST_PLAYER_AFTER_LOGOUT}" ]; then echo "EXIST_PLAYER_AFTER_LOGOUT=$EXIST_PLAYER_AFTER_LOGOUT" sed -E -i "s/bExistPlayerAfterLogout=[a-zA-Z]*/bExistPlayerAfterLogout=$EXIST_PLAYER_AFTER_LOGOUT/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${ENABLE_DEFENSE_OTHER_GUILD_PLAYER}" ]; then echo "ENABLE_DEFENSE_OTHER_GUILD_PLAYER=$ENABLE_DEFENSE_OTHER_GUILD_PLAYER" sed -E -i "s/bEnableDefenseOtherGuildPlayer=[a-zA-Z]*/bEnableDefenseOtherGuildPlayer=$ENABLE_DEFENSE_OTHER_GUILD_PLAYER/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${COOP_PLAYER_MAX_NUM}" ]; then echo "COOP_PLAYER_MAX_NUM=$COOP_PLAYER_MAX_NUM" sed -E -i "s/CoopPlayerMaxNum=[0-9]*/CoopPlayerMaxNum=$COOP_PLAYER_MAX_NUM/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${REGION}" ]; then REGION=$(sed -e 's/^"\(.*\)"$/\1/' -e "s/^'\(.*\)'$/\1/" <<< "$REGION") echo "REGION=$REGION" sed -E -i "s/Region=\"[^\"]*\"/Region=\"$REGION\"/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${USEAUTH}" ]; then echo "USEAUTH=$USEAUTH" sed -E -i "s/bUseAuth=[a-zA-Z]*/bUseAuth=$USEAUTH/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${BAN_LIST_URL}" ]; then BAN_LIST_URL=$(sed -e 's/^"\(.*\)"$/\1/' -e "s/^'\(.*\)'$/\1/" <<< "$BAN_LIST_URL") 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,,}" sed -i "s/RCONEnabled=[a-zA-Z]*/RCONEnabled=$RCON_ENABLED/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi if [ -n "${RCON_PORT}" ]; then echo "RCON_PORT=${RCON_PORT}" sed -i "s/RCONPort=[0-9]*/RCONPort=$RCON_PORT/" /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini fi rm -f "/home/steam/server/crontab" 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,,}" echo "$AUTO_UPDATE_CRON_EXPRESSION bash /usr/local/bin/update" >> "/home/steam/server/crontab" fi 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 # Configure RCON settings cat >/home/steam/server/rcon.yaml <<EOL default: address: 127.0.0.1:${RCON_PORT} password: "${ADMIN_PASSWORD}" EOL printf "\e[0;32m*****STARTING SERVER*****\e[0m\n" echo "${STARTCOMMAND[*]}" FEXBash -c "${STARTCOMMAND[*]}" exit 0 61, 97, 398 라인: FEX-Emu를 통해 ARM에서 문제없이 돌아가도록 함 docker-compose.yml version: "3.9" services: palworld: container_name: palworld restart: unless-stopped stop_grace_period: 30s build: context: . ports: - 8211:8211/udp - 27015:27015/udp env_file: - .env - settings.env environment: - PUID=1001 - PGID=1001 - MULTITHREADING=true - UPDATE_ON_BOOT=false - TZ=Asia/Seoul volumes: - ./server:/palworld networks: games: ipv4_address: 172.16.11.11 networks: games: name: games external: true Build 한 15~20분 정도 걸리는 듯.. docker compose up -d --build 이후부터는 --build 제거 후 사용. 다른 명령어는 이전 글 참고.
  • Docker로 Palworld 서버 열기

    2024-02-09 21:23:49 Docker로 Palworld 서버 열기 (ARM64) Ver. 2 ARM64에 올리는 방법은 위 글 참고. Palworld에서는 공식적으로 도커 이미지를 제공하지 않지만, 역시 누군가 만들어둔 이미지(GitHub)가 있어서 사용해 봤다. 업데이트도 매우 빠르고 각종 기능도 계속 추가되고 있다. 솔직히 GitHub와 Docker 사이트에 실행 방법이 다 나와있기 때문에 별로 설명할 건 없고, docker-compose.yml 파일 내용을 올리기 위해 작성하는 글이다. 리눅스에 도커가 설치되어 있다는 가정 하에 진행 이미지 다운로드 이 과정은 생략해도 어차피 나중에 자동으로 다운로드가 진행된다. docker pull thijsvanloef/palworld-server-docker 서버 실행 준비 docker-compose.yml 파일 작성 mkdir -p ~/docker/palworld cd ~/docker/palworld vi docker-compose.yml version: "3.9" services: palworld: container_name: palworld restart: unless-stopped stop_grace_period: 30s image: thijsvanloef/palworld-server-docker:latest ports: - 8211:8211/udp - 27015:27015/udp env_file: - .env - settings.env environment: - PUID=1000 - PGID=1000 - MULTITHREADING=true - UPDATE_ON_BOOT=true - TZ=Asia/Seoul volumes: - ./server:/palworld networks: games: ipv4_address: 172.16.11.11 networks: games: name: games external: true .env 파일에는 포트 번호, 서버명, 서버 설명, 패스워드 등의 내용이 들어있고, settings.env 파일에는 게임에 적용되는 설정들이 저장되어 있다. 서버 실행 docker compose up -d # 실행 docker logs -f palworld # 컨테이너 로그 확인 아래와 같은 로그가 뜨면 서버 실행 완료. 마지막에 [S_API FAIL] ...이라고 뜨는데, 상관없는지 제대로 돌아간다. *****STARTING SERVER***** ./PalServer.sh -port=8211 -queryport=27015 EpicApp=PalServer -useperfthreads -NoAsyncLoadingThread -UseMultithreadForDS time="2024-02-07T18:14:21+09:00" level=info msg="read crontab: /home/steam/server/crontab" ... Setting breakpad minidump AppID = 2394010 [S_API FAIL] Tried to access Steam interface SteamUser021 before SteamAPI_Init succeeded. [S_API FAIL] Tried to access Steam interface SteamFriends017 before SteamAPI_Init succeeded. [S_API FAIL] Tried to access Steam interface STEAMAPPS_INTERFACE_VERSION008 before SteamAPI_Init succeeded. [S_API FAIL] Tried to access Steam interface SteamNetworkingUtils004 before SteamAPI_Init succeeded. + 처음 실행하거나 Palworld 서버가 설치된 디렉토리를 삭제 또는 이동했다면 서버를 설치해야 하기 때문에 조금 오래 걸린다. 이때 서버를 설치하는 동안의 로그가 실시간으로 뜨지 않고 조금씩 모아져서 한 번에 뜨는 문제가 있기 때문에 특별한 에러가 없다면 멈춘 것이 아니니 기다리면 된다. 그 외 docker compose 명령어는 docker-compose.yml 파일이 있는 위치 또는 그 하위에서 실행해야 한다. docker exec -it palworld rcon-cli # rcon 연결 docker restart palworld # 재시작 docker compose down # 종료 docker pause palworld # 일시정지 docker unpause palworld # 일시정지 해제
  • Palworld 서버를 24시간 열어둘 경우 생기는 문제

    2024-02-08 18:04:01 어제 도커로 서버 올려놓고 닫지 않고 잤더니 애들이 다 죽기 직전이네.. 원인 플레이어가 팰 근처인지 거점 근처인지 거기서부터 멀어질수록 얘들이 하는 행동이 느려진다고 한다. 그래서 서버에 아무도 없으면 전혀 움직이지 않아서 생기는 문제라고.. 임시 대처 방법 도커 이미지 GitHub의 Issues에 해당 문제가 올라와 있다. 해당 페이지의 하단에 어느 한국인이 파이썬 스크립트를 짜놓은 게 있는데, rcon을 통해 접속 중인 플레이어가 있는지 체크하고, 없다면 컨테이너 자체를 중지한 후 tcpdump 명령어로 서버 연결 포트를 탐지하여 연결 요청이 들어오면 다시 컨테이너가 돌아가게 만드는 코드다. 코드는 컨테이너가 아닌 호스트에서 실행해야 한다. 아래 코드는 조금 수정한 코드. import subprocess import time TARGET_PORT = 8211 CONTAINER_NAME = "palworld-server" sudo_password = "your_sudo_password_here" # sudo 명령어 사용 시 패스워드 입력이 필요없다면 삭제 def check_players(): players_output = run_command( f"docker exec {CONTAINER_NAME} rcon-cli ShowPlayers" ) print(f"player: {players_output}") return "," in players_output.replace("name,playeruid,steamid", "") # itzg/rcon-cli을 사용할 때 문제 발생 해결 def run_command(command): """Helper function to run a shell command and return its output""" result = subprocess.run( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, text=True ) return result.stdout.strip() def main(): if not check_players(): print("No players found. Restart the server.") run_command(f"docker restart {CONTAINER_NAME}") for sec in range(30, 0, -1): print(f"Waiting for restart to complete... {sec} seconds left") time.sleep(1) # if not players, pause server if not check_players(): print("Restart complete. Pause the server.") run_command(f"docker pause {CONTAINER_NAME}") player = False print("Waiting for players to connect") else: player = True while not player: # Use tcpdump to capture incoming traffic on the target port #capture_command = f"echo {sudo_password} | sudo -S tcpdump -n -c 1 -i any port {TARGET_PORT} 2>/dev/null" capture_command = f"sudo -S tcpdump -n -c 1 -i any port {TARGET_PORT} 2>/dev/null" # 패스워드 입력 필요없는 경우 capture_result = run_command(capture_command) # Check if any packets were captured if capture_result: player = True print(f"Connection attempt detected on port {TARGET_PORT}") print("Starting server") run_command(f"docker unpause {CONTAINER_NAME}") for sec in range(30, 0, -1): print(f"Waiting for players to connect... {sec} seconds left") time.sleep(1) else: time.sleep(1) else: print("Players found. Server will continue running.") time.sleep(60) if __name__ == "__main__": while True: main() 코드를 수정한 이유는 이전 글에서 말한 itzg/rcon-cli을 사용할 때 뜨는 문구 때문에 check_players 함수는 항상 True를 리턴함. tcpdump 명령어가 요청을 대기하지 않고 그냥 넘어가버림. 이 글을 쓰는 지금은 1번의 경우 rcon을 변경해서 상관없지만, 2번은 sudo 명령어 사용 시 패스워드 입력이 필요 없어서 발생한 문제다. 패스워드 입력을 위해 echo {sudo_password} | 명령어를 사용하는데, 패스워드를 묻지 않기 때문에 표준 입력으로 넘긴 저 패스워드가 tcpdump 명령어가 받아서 그런 듯. 수정한 부분은 해당 issue에 댓글로 남겨놨다 사용법 위 코드를 파일에 저장하고 python3 file.py 명령어로 스크립트 실행. 또는 터미널을 닫아도 백그라운드로 돌아가게 하려면 nohub python3 file.py 2>&1 & 명령어 실행. 잘만 하면 컨테이너 내부에서 실행하도록 만들어서 이미지 빌드 할 때 기능으로 추가해도 될 듯한데..
  • 팰월드(Palworld)

    2024-02-08 11:09:24 요즘 매우 핫한 게임인 팰월드, 평소에 PC 게임은 1도 안 하던 내가 이건 되게 재밌어 보여서 처음으로 유료 게임을 구매해 봤다. 오랜만의 게임이라 그런지 되게 재밌고, 어쩌다 보니 게임 플레이보다 서버 여는 것에 더 열심히 하는 듯… 한동안 블로그를 까먹고 살다가 이 게임 때문에 다시 글을 작성하는 이유는.. 싱글 플레이 -> steamcmd로 서버 오픈 -> Docker로 서버 올림 -> 24시간 운영을 위해 클라우드 서버로 이전 그냥 게임만 즐기면 될 것을 서버 한번 열어보고 싶어서 거친 과정들을 기록해 보려고 한다. 공식적으로 지원하지 않는 ARM64 버전의 서버를 여는 법까지 적을 예정. List of Posts Palworld 서버 열기는 공식 사이트에서도 나와있고, 벌써 많은 글들이 올라와 있기 때문에 넘어감 Docker로 Palworld 서버 열기 Docker로 Palworld 서버 열기 (ARM64) Ver. 2 방법을 사용하자. Palworld 서버를 24시간 열어둘 경우 생기는 문제 Docker로 Palworld 서버 열기 (ARM64) Ver. 2 이렇게 만든 서버는.. 아까우니 운영해야지 뭐 자세한 내용은 모여봐요 팰들의 월드 글을 참고해 주세요!