パルワールドサーバーの構築とデーモン化

## 概要

本記事は「steamdmd を初めて使うよ」という方に向けて送るメモです

| | |
| --- | --- |
| os | Ubuntu 22.04.2 LTS |
| arch | x86_64 |
| palserv-version | 1.3.0 |
| install-dir | /home/steam/dedicated-server/palworld |
| unit-file | /etc/systemd/system/palworld.service |

<details><summary>補足</summary><div>

``` log
Ubuntu 22.04.2 LTS で
パルワールドサーバー を構築します
 => Ubuntu 20.04.2 LTS でも同じ手順で構築できました

インストール先は
/home/steam/dedicated-server/palworld とします

インストール後は
デーモン化(palworld.service)して 起動 + 停止 できるようにします
```
</div></details>

### 注意

:::note warn
> For security reasons, it is recommended that you create a new Steam account just for your dedicated servers.
:::

**steamcmd** を **root以外のユーザ** で実行する必要があります

なので steamcmd 自体のインストール は **rootユーザ** で行い、
パルワールドサーバー インストール + 実行 は **steamユーザ** で実行します

| 作業 | ユーザ |
| --- | --- |
| steamcmd 自体のインストール | root |
| steamcmd の実行 | steam |
| PalServer.sh の実行 | steam |
| palworld.service の実行 | root + steam |

↑↑↑前置きはここまでです↑↑↑

## steamcmd のインストール 

まず **steamcmd** をインストールします

``` log
# add architecture
add-apt-repository multiverse
dpkg --add-architecture i386
dpkg --print-foreign-architectures
apt update -y

# install steamcmd
echo steam steam/question select "I AGREE" | debconf-set-selections
echo steam steam/license note '' | debconf-set-selections
debconf-show steam
apt install steamcmd
```

:::note info
[ここ](https://developer.valvesoftware.com/wiki/SteamCMD#Linux) で ディストリビューションごとに
steamcmd をインストールする方法がまとまっています

<details><summary>debconf-set-selectionsについて</summary>
対話形式で「ライセンスに同意しますか？」って聞かれるので、
debconf-set-selections 非対話で、事前に同意しています
(一度読んでみてください)
</details>
:::

## パルワールドサーバー のインストール 

先に **steamユーザを作成** した後に、
**/home/steam/dedicated-server/palworld** にインストールします

``` log
useradd -m steam -s /bin/bash
su - steam

mkdir -p ~/dedicated-server/palworld
steamcmd +login anonymous \
  +force_install_dir ~/dedicated-server/palworld/ \
  +app_update 2394010 validate +quit

cd /home/steam/dedicated-server/palworld/
ls -al
```

:::note info
インストール先は **force_install_dir** で指定できます
無指定だと **/home/steam/Steam/steamapps/common/PalServer** になります
:::

## パルワールドサーバー の起動 

パルワールドサーバー を一度起動してみます
(起動するのが確認できたら Ctrl+C で終了しても大丈夫です)

``` log
lsof -i:8211
./PalServer.sh
...
^C
```

問題なくインストールできてたら、
この時点で `<サーバIP>:8821` にログインできます

:::note warn
何やらエラーが出るかもしれませんが、
`steamclient.so: cannot open shared object file`
って書いてたら、問題ないので、一旦無視します

<details><summary>起動ログとエラーはこんな感じです</summary><div>

``` log
[steam@game-server 00:55:25] palworld $ ./PalServer.sh
Shutdown handler: initalize.
Increasing per-process limit of core file size to infinity.
dlopen failed trying to load:
steamclient.so
with error:
steamclient.so: cannot open shared object file: No such file or directory
dlopen failed trying to load:
/home/steam/.steam/sdk64/steamclient.so
with error:
/home/steam/.steam/sdk64/steamclient.so: cannot open shared object file: No such file or directory
[S_API] SteamAPI_Init(): Sys_LoadModule failed to load: /home/steam/.steam/sdk64/steamclient.so
dlopen failed trying to load:
steamclient.so
with error:
steamclient.so: cannot open shared object file: No such file or directory
dlopen failed trying to load:
/home/steam/.steam/sdk64/steamclient.so  # <= こんなのが出ます
...

^C
FUnixPlatformMisc::RequestExitWithStatus
FUnixPlatformMisc::RequestExit
Exiting abnormally (error code: 130)
Shutdown handler: cleanup.
```
</div></details>
:::

### エラーの対処

現在(2024/01/31)、起動時に上記エラーがでます

放っておいても問題なさそうですが、
毎度エラーが出力されるのも気持ち悪いので対処しておきます
(steamcmd で 64bit の steamclient.so だけ入手します)

``` log
mkdir -p ~/dedicated-server/tools/sdk64
steamcmd +login anonymous \
  +force_install_dir ~/dedicated-server/tools/sdk64/ \
  +app_update 1007 validate +quit

mkdir -p ~/.steam/sdk64/
cp ~/dedicated-server/tools/sdk64/steamclient.so ~/.steam/sdk64/

lsof -i:8211
./PalServer.sh
...
^C
```

:::note info
x86_64 の場合 /home/steam/dedicated-server/tools/sdk64/steamclient.so でなく
**/home/steam/dedicated-server/tools/sdk64/linux64/steamclient.so** をコピーします
:::

<details><summary>エラーの原因?</summary><div>

~/dedicated-server/palworld/linux64/steamclient.so を
~/.steam/sdk64/steamclient.so にコピーしても同じエラーが出ましたので、
 => **steamcmd で steamclient.so を入手する** のがよさそうです

``` log
# arch が 64bit の場合
[steam@game-server 00:58:03] ~ $ arch
x86_64

# 64bit 用の steamclient.so を使う
[steam@game-server 00:58:05] ~ $ ls -l ~/dedicated-server/tools/sdk64/{linux64/,}steamclient.so
-rwxrwxr-x 1 steam steam 36116272 Jan 31 00:45 /home/steam/dedicated-server/tools/sdk64/linux64/steamclient.so  # <= 64bit 用 ★
-rwxrwxr-x 1 steam steam 37665020 Jan 31 00:45 /home/steam/dedicated-server/tools/sdk64/steamclient.so  # <= 32bit 用

# ===

[memo]
# 1007 で取得した steamclient.so
[steam@game-server 01:03:42] ~ $ ls -al ~/dedicated-server/tools/sdk64/{linux64/,}steamclient.so
-rwxrwxr-x 1 steam steam 36116272 Jan 31 00:45 /home/steam/dedicated-server/tools/sdk64/linux64/steamclient.so
-rwxrwxr-x 1 steam steam 37665020 Jan 31 00:45 /home/steam/dedicated-server/tools/sdk64/steamclient.so
[steam@game-server 01:03:48] ~ $ sha1sum ~/dedicated-server/tools/sdk64/{linux64/,}steamclient.so
d86a5dc086a154728ac47be63bd537784b016ade  /home/steam/dedicated-server/tools/sdk64/linux64/steamclient.so
8247a65ba2e8552216ef1b0b1287739c04032761  /home/steam/dedicated-server/tools/sdk64/steamclient.so

# 2394010 にある steamclient.so
[steam@game-server 01:03:52] ~ $ ls -al ~/dedicated-server/palworld/{linux64/,}steamclient.so
-rwxrwxr-x 1 steam steam 37665020 Jan 31 00:27 /home/steam/dedicated-server/palworld/linux64/steamclient.so
-rwxrwxr-x 1 steam steam 37665020 Jan 31 00:19 /home/steam/dedicated-server/palworld/steamclient.so
[steam@game-server 01:03:53] ~ $ sha1sum ~/dedicated-server/palworld/{linux64/,}steamclient.so
8247a65ba2e8552216ef1b0b1287739c04032761  /home/steam/dedicated-server/palworld/linux64/steamclient.so  # <= コイツが怪しい
8247a65ba2e8552216ef1b0b1287739c04032761  /home/steam/dedicated-server/palworld/steamclient.so
```
</div></details>

<details><summary>エラー対処後のログ</summary><div>

``` log
[steam@game-server 01:19:18] palworld $ ./PalServer.sh
Shutdown handler: initalize.
Increasing per-process limit of core file size to infinity.
dlopen failed trying to load:
steamclient.so
with error:
steamclient.so: cannot open shared object file: No such file or directory
[S_API] SteamAPI_Init(): Loaded '/home/steam/.steam/sdk64/steamclient.so' OK.  (First tried local 'steamclient.so')
CAppInfoCacheReadFromDiskThread took 2 milliseconds to initialize
dlmopen steamservice.so failed: steamservice.so: cannot open shared object file: No such file or directory
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.

^C
FUnixPlatformMisc::RequestExitWithStatus
FUnixPlatformMisc::RequestExit
Exiting abnormally (error code: 130)
Shutdown handler: cleanup.
```
</div></details>

## パルワールドサーバー のデーモン化

rootユーザで /etc/systemd/system/palworld.service を作成して、
パルワールドサーバを systemctl で起動できるようにします

``` log
# rootユーザ で作成
cat << 'EOF' > /etc/systemd/system/palworld.service
[Unit]
Description=Palworld Dedicated Server
After=network-online.target

[Service]
Type=simple
# ExecStart=/home/steam/dedicated-server/palworld/PalServer.sh port=8211 players=32 -useperfthreads -NoAsyncLoadingThread -UseMultithreadForDS
ExecStart=/home/steam/dedicated-server/palworld/PalServer.sh port=8211 players=32 -useperfthreads -NoAsyncLoadingThread -UseMultithreadForDS -RCONPort=25575
Restart=always
RestartSec=10
User=steam
LimitNOFILE=100000

[Install]
WantedBy=multi-user.target
EOF

# rootユーザ で起動
systemctl start palworld
systemctl status palworld
```

(02/28 追記)
v0.1.5.0 から デフォルトのポートを使う場合でも、
-RCONPort=25575 を明示的に指定する必要があるっぽいです

### steam ユーザでもデーモンを操作できるようにする 

steam ユーザで palworld.service を操作できるようにします

``` log
# rootユーザ で作成
tmp=$(mktemp)
cat << 'EOF' > $tmp
steam ALL=(root) NOPASSWD: /bin/systemctl status palworld, /bin/systemctl start palworld, /bin/systemctl stop palworld, /bin/systemctl restart palworld
steam ALL=(root) NOPASSWD: /bin/systemctl status palworld.service, /bin/systemctl start palworld.service, /bin/systemctl stop palworld.service, /bin/systemctl restart palworld.service
EOF

# 書式チェック
visudo -cf $tmp && mv $tmp /etc/sudoers.d/steam

# steamユーザ で停止,起動,再起動
sudo systemctl stop palworld
sudo systemctl start palworld
sudo systemctl restart palworld
```

<details><summary>補足</summary><div>

``` log
/etc/sudoers.d/steam を作成し、
palworld.service の 起動,停止,再起動 ができるようにしています

~/.config/systemd/user/ に ユニットファイルを作成して、
一般ユーザで起動する方法もあるっぽいですが、
root で デーモンを操作することが多そうなので、この方式にしてみました
```
</div></details>

## その他の設定

以下のケースについては、別の記事にまとめました

> ゲームバランスを調整ってどうするの？
> 新しいバージョンが出たけど、アップデートってどうするの？

- 「パルワールドサーバーのアップデートとゲームバランスの調整方法について」に記載
  - [https://qiita.com/teityura/items/b7c839bace1056bcca5b](https://qiita.com/teityura/items/b7c839bace1056bcca5b)

> バックアップってどうするの？
> チャットボットでコマンド送信してみたい..

- 「パルワールドサーバー の Discord Bot を作ってみた」に記載
  - [https://qiita.com/teityura/items/d9743ec733d9255bb150](https://qiita.com/teityura/items/d9743ec733d9255bb150)

それでは、よいパルワールドライフを！
https://store.steampowered.com/app/1623730/Palworld/?l=japanese

## 参考

- パルワールド テックガイド
  - https://tech.palworldgame.com/dedicated-server-guide
- Steam Application IDs
  - https://developer.valvesoftware.com/wiki/Steam_Application_IDs
- SteamCMD > Linux
  - https://developer.valvesoftware.com/wiki/SteamCMD#Linux

- ファイルディスクリプタ数の上限変更とlimits.confの罠
  - https://www.yumemi.co.jp/apache_limits_conf
    - /etc/pam.d/, session, pam_limits.so
- ファイルディスクリプタ数の上限を変更
  - https://christina04.hatenablog.com/entry/how-to-change-file-descriptor-limit
    - LimitNOFILE, DefaultLimitNOFILE
- systemd で ulimit 上げるときの推奨の方法
  - https://blog.kteru.net/systemd-ulimit-tips/
    - /etc/systemd/system/mariadb.service.d/limits.conf
- systemd のユニットファイルの作り方
  - https://tex2e.github.io/blog/linux/create-my-systemd-service
- systemd - ArchWiki
  - https://wiki.archlinux.jp/index.php/Systemd