1~8/91
  • Docker로 Longvinter 서버 열기 (AMD64, ARM64)

    2024-04-04 16:04:53 먼저 AMD64 아키텍처에서는 Longvinter에서 공식적으로 사설 서버를 도커로 돌릴 수 있도록 소스 코드를 올려두긴 했다. 하지만 이를 제대로 사용하려면 소스 코드를 수정해야 한다. 그렇지 않으면 단순히 서버를 실행하기만 하는 용도로만 사용할 수 있다. 여기서 사용할 이미지(GitHub)는 Palworld 서버를 열 때 사용한 이미지의 소스 코드를 롱빈터용으로 수정한 것이다. 리눅스에 도커가 설치되어 있다는 가정 하에 진행 이미지 다운로드 이 과정은 생략해도 어차피 나중에 자동으로 다운로드가 진행된다. docker pull kimzuni/longvinter-docker-server:latest 서버 실행 준비 docker-compose.yml 파일 작성 mkdir -p ~/docker/longvinter cd ~/docker/longvinter vi docker-compose.yml 원래 제일 첫 번째 줄에 적던 version: "x.x" 구문은 이제 의미가 없어졌다고 작성하지 않아도 된다고 한다. services: longvinter-server: container_name: longvinter-server image: kimzuni/longvinter-docker-server:latest restart: unless-stopped stop_grace_period: 30s logging: driver: json-file options: max-size: "10m" max-file: "3" ports: - "7777:7777/tcp" - "7777:7777/udp" - "27016:27016/tcp" - "27016:27016/udp" environment: TZ: "UTC" PUID: 1000 PGID: 1000 PORT: 7777 QUERY_PORT: 27016 CFG_SERVER_NAME: "Unnamed Island" CFG_MAX_PLAYERS: 32 CFG_SERVER_MOTD: "Welcome to Longvinter Island!" CFG_PASSWORD: "" CFG_COMMUNITY_WEBSITE: "www.longvinter.com" CFG_COOP_PLAY: false CFG_COOP_SPAWN: 0 CFG_SERVER_TAG: "none" CFG_ADMIN_STEAM_ID: "" CFG_ENABLE_PVP: true CFG_TENT_DECAY: true CFG_MAX_TENTS: 2 volumes: - ./data:/data networks: games: ipv4_address: 172.16.11.12 networks: games: name: games external: true 서버 실행 docker compose up -d # 실행 docker logs -f longvinter-server # 컨테이너 로그 확인 필터링을 위한 비속어가 포함된 굉장히 긴 로그가 나타나고 잠시 후 아래와 같은 로그가 뜨면 서버에 접속할 수 있다. ****Starting Server**** /data/LongvinterServer.sh -Port=7777 -QueryPort=27016 time="2024-04-04T08:50:08Z" level=info msg="read crontab: /home/steam/server/crontab" ... [2024.04.04-09.04.21:243][ 74]LogEOS: Verbose: CreateSession: Successfully created session 'Unnamed Island' with ID 'fb347698360146d5971fe63b8ad04bfe' [2024.04.04-09.04.21:573][ 84]LogNet: ReplicationDriverClass is null! Not using ReplicationDriver. [2024.04.04-09.04.21:573][ 84]LogNetCore: DDoS detection status: detection enabled: 0 analytics enabled: 0 [2024.04.04-09.04.21:573][ 84]LogInit: BSD IPv4/6: Socket queue. Rx: 262144 (config 131072) Tx: 262144 (config 131072) [2024.04.04-09.04.21:573][ 84]LogNet: Created socket for bind address: 0.0.0.0 on port 7787 그 외 docker compose 명령어는 docker-compose.yml 파일이 있는 위치 또는 그 하위에서 실행해야 햔다. docker restart longvinter-server # 재시작 docker compose down # 종료 docker pause longvinter-server # 일시정지 docker unpause longvinter-server # 일시정지 해제 Failed to find object ARM64 버전에선 아래와 같은 경고 메시지가 굉장히 많이 뜨긴 하지만, 서버를 들어가보면 모든 오브젝트들이 아무런 문제 없이 표시되며, 정상적으로 플레이가 가능하다. [2024.04.03-12.41.47:651][ 0]LogUObjectGlobals: Warning: Failed to find object 'Object None.None'
  • 롱빈터(Longvinter)

    2024-04-04 11:38:11 AMD64, ARM64 아키텍처에서 서버 여는 법은 아래 글 참고 바람 Docker로 Longvinter 서버 열기 약 두 달 전.. Palworld 서버 여는 방법의 글들을 올리고 난 후 Longvinter라는 게임을 발견하고 되게 재밌어 보이길래 시작했다. 역시 단순히 게임만 플레이할 거면 이 글을 안 올렸을 텐데.. 롱빈터도 개인이 사설 서버를 열 수 있다길래 또 한 번 열어보겠다고 시작했다가 ARM64 버전에서 돌릴 수 있는 도커 이미지가 없어서 직접 Longvinter 공식 도커 이미지 소스 코드를 참고하여 Palworld때 쓰던 이미지 소스 코드를 롱빈터용으로 싹 바꿔버렸다. 그 결과 정상적으로 작동하는 것을 확인했고, GitHub, Docker Hub에 소스 코드와 이미지를 올렸는데 2주가 지난 지금까지 전혀 구글 검색에 노출이 안되고 있다. (GitHub Topic에 올라간 내 저장소는 뜨는데 저장소 자체는 검색이 되지 않는다.) 그래서 결론은 이 글이라도 구글 검색 노출돼서 유입되라고 적는 글이다.
  • 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 이렇게 만든 서버는.. 아까우니 운영해야지 뭐 자세한 내용은 모여봐요 팰들의 월드 글을 참고해 주세요!
  • 리눅스에서 카카오톡 사용하기

    2023-08-09 19:42:22 이 글에서는 우분투에 설치했지만 Flatpak을 사용하기 때문에 리눅스 배포판 종류와 상관없이 설치할 수 있다. 다만 카카오톡이 열리는데 시간이 약간 오래 걸린다. 원래 wine도 약간 느리긴 하지만 이건 아무래도 bottles을 통해 실행해야 하니 더 느리다. 실행이 되고 나면 속도는 문제없다. 설치 CentOS를 사용하던 시절부터 카카오톡을 설치해 보려고 wine, PlayOnLinux 등을 사용해 봤지만, 잘되지 않아서 bottles를 사용했다. 그래서 wine으로 카카오톡이 잘 돌아가는 지금도 bottles를 사용 중이다.. 종류 및 버전 관리도 편하고, 프로그램들이 컨테이너 방식으로 돌아가기 때문에 관리도 편하다. Flatpak 설치 먼저 Flatpak을 설치해야 한다. Debian/Ubuntu # sudo apt -y install flatpak gnome-software-plugin-flatpak RHEL/CentOS 7 # sudo yum -y install flatpak RHEL/CentOS 8 & Fedora & Rocky Linux # sudo dnf -y install flatpak 그리고 Flatpak의 Repository 추가 # flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo Bottles 설치 Flatpak으로 Bottles 설치 후 재부팅 # flatpak install flathub com.usebottles.bottles # sudo reboot KakaoTalk 설치 준비 터미널에서 flatpak run com.usebottles.bottles를 입력하거나 우분투 메뉴에서 Bottles 선택해서 실행 후 계속 진행 오른쪽 상단 삼선 메뉴 - Preferences - Runners에서 원하는 것으로 다운로드 왼쪽 상단 + 누른 후 Bottle 생성 폰트 설치 폰트를 설치하지 않으면 설치 과정에서 영어를 사용해야 하며, 다른 언어는 모두 깨져서 보인다. 물론 이 과정을 거치지 않고 영어로 설치하거나 깨진 언어로 설치한 후에 폰트를 직접 설치하고 카카오톡 설정에서 폰트를 변경해도 된다. Bottle에서 제공하는 폰트(cjkfonts)를 설치할 수 있다. cjkfonts 설치 시 굴림, 돋움, 맑은 고딕, 바탕 등의 폰트를 포함한 중국어(c), 일본어(j), 한국어(k)의 여러 폰트가 설치된다. KakaoTalk 설치 카카오톡 사이트에서 Windows 설치 파일 다운로드 및 “Run Executable…“에서 카카오톡 설치 파일 선택. 2023.05.01 - caffe-7.20 에러 2023년 5월 1일 현재 caffe(v7.20)로 진행 시 에러가 발생한다. 아무런 업데이트가 없었는데 갑자기 되던 게 안 되니 내가 문제인지 caffe가 문제인지는 모르겠다. Bottle을 제거 후 다른 Runner로 새로 생성하거나 Bottle의 Settings에 들어가서 Runner를 변경하면 문제없이 설치가 된다. 설정 카카오톡 실행 지금 이상태로는 카카오톡을 실행하려면 Bottles을 열고 카카오톡을 실행하고… 귀찮다. Apps 목록에서 카카오톡이 뜨게 .desktop 파일을 만들거나, 터미널에서 실행할 수 있게 실행 파일을 생성하면 훨씬 편하다. 기본적으로 flatpak 명령어를 통해 실행하며 xdg-open 명령어를 사용할 수도 있다. Applications 목록에 카카오톡 생성 /usr/share/applications/, /usr/local/share/applications/ 등 디렉토리에 .desktop 확장자 파일들 두면 Apps 목록에 아이콘이 생성된다. 직접 생성 카카오톡은 현재 유저만 사용할 수 있으므로 ~/.local/share/applications/ 디렉토리를 사용. 아래에서 실행 파일을 생성했다면 Exec= 뒤에 해당 파일의 경로를 적어줘도 된다. 이 방법의 단점은 KakaoTalk의 기본 아이콘이 어디에도 없다는 것. 아이콘을 직접 다운로드 하거나 exe 파일에서 아이콘 추출 후 해당 경로를 Icon= 뒤에 적으면 된다. 카카오톡 아이콘 추출 # sudo apt -y install icoutils # wrestool -x -t3 -n1 --raw KakaoTalk_Setup.exe --output=/path/to/KakaoTalk.png .desktop 파일 생성 [Desktop Entry] Encoding=UTF-8 Name=KakaoTalk Comment=KakaoTalk Exec=xdg-open bottles:run/KakaoTalk/KakaoTalk Terminal=false Type=Application Icon=/path/to/KakaoTalk.png Bottles 기능 사용 Bottles의 기능을 사용하여 Apps 목록에 아이콘을 생성할 수 있다. 먼저 Bottles에 권한을 줘야한다. Bottles 종료 후 아래 명령어 실행 # flatpak override com.usebottles.bottles --user --filesystem=xdg-data/applications 이제 Bottles에서 아래 버튼을 클릭하면 .desktop 파일이 생성되는 것을 확인할 수 있다. 실행 파일 생성 kakaotalk이라는 텍스트 파일 생성 후 실행 권한을 주고 ~/bin 디렉토리에 두면 터미널에서 언제 어디서든 kakaotalk을 입력하여 카카오톡을 실행할 수 있다. 첫 번째 KakaoTalk은 생성한 Bottle 이름, 두 번째 kakaoTalk은 Bottle 내 실행시킬 Programs의 이름이다. #!/usr/bin/env bash if [ `ps -ef | grep -i bottles.*kakaotalk | wc -l` != 1 ]; then echo "KakaoTalk is already running." exit 1 fi # nohup flatpak run --command=bottles-cli com.usebottles.bottles run -b KakaoTalk -p KakaoTalk 2>&1 > /dev/null & xdg-open bottles:run/KakaoTalk/KakaoTalk 폰트 직접 설치 원래 이 내용은 cjkfonts를 설치하지 않았을 때 한글 깨짐 현상을 해결하기 위해 적은 내용이지만, 폰트를 직접 추가하려면 필요한 내용이므로 삭제하지 않았다. 네이버에서 나눔 폰트를 다운로드한 후 unzip으로 압축을 푼다. 그리고 KakaoTalk이 설치된 곳에 폰트를 설치복사하면 된다. bottles와 drive_c 사이에 오는 KakaoTalk은 생성한 Bottle의 이름이다. # unzip nanum-all.zip # find 나눔\ 글꼴/ -type f -exec cp {} ~/.var/app/com.usebottles.bottles/data/bottles/bottles/KakaoTalk/drive_c/windows/Fonts/ \; 한글은 이제 안 깨지고 보이는데.. 하단 광고와 입력란의 한글은 여전히 깨진다. 이건 카카오톡 설정에서 폰트를 변경하면 된다. 카카오톡에서 파일 업로드 및 다운로드 업로드 Z 드라이브에 우분투의 루트 디렉토리가 마운트 되어있기 때문에 파일 업로드 시 Z 드라이브에 들어가서 파일을 선택하면 되..ㄹ 것 같지만.. 이유는 모르겠지만 홈 디렉토리에는 Downloads 폴더밖에 보이지 않는다. 그래서 업로드할 파일을 Downloads 디렉토리에 복사한 후 전송하는 중.. 어차피 아래 과정을 거치면 그 경로가 업로드 창을 열었을 때 뜨는 기본 경로기 때문에 파일이 바로 떠서 편하긴 하다. 업로드 (2023.08.09 추가) 어제 익명의 어느 분께서 메일을 남겨주셨다. 그 내용은.. flatpak override --user --filesystem=host com.usebottles.bottles 위 명령어를 사용하면 모든 디렉토리에 접근이 가능하다고 한다. 다만, 시스템의 모든 파일에 접근이 가능하기 때문에 주의가 필요할 듯. 권한을 제거하려면 --filesystem을 --nofilesystem으로 바꿔주기만 하면 된다. flatpak override --user --nofilesystem=host com.usebottles.bottles 도움을 주신 익명님 감사합니다! 다운로드 카카오톡 내에서 파일을 다운로드할 때 파일을 홈 디렉토리의 Downloads 디렉토리에 다운로드되게 하려고 한다. 카카오톡에서 다운로드한 파일들은 “카카오톡 받은 파일”(영어로 설치했다면 “KakaoTalk Downloads”) 폴더에 저장된다. 카카오톡 설정에서 이 경로만 변경하면 된다. Documents 폴더에서 “카카오톡 받은 파일”을 제외한 나머지 폴더가 모두 리눅스의 홈 디렉토리에 심볼릭 링크가 걸려있다. 즉, 아무거나 선택 후 Downloads 폴더를 선택하면 파일 다운로드 시 홈 디렉토리에 저장된다. 이게 귀찮다면 그냥 터미널에서 카카오톡 받은 파일 폴더를 직접 홈 디렉토리의 Downloads에 심볼릭 링크를 걸어도 된다. # cd ~/.var/app/com.usebottles.bottles/data/bottles/bottles/KakaoTalk/drive_c/users/jh1950/Documents # rmdir ./카카오톡\ 받은\ 파일/ # ln -s ~/Downloads/ ./카카오톡\ 받은\ 파일