ゲームサークルでのMinecraftマルチプレイ環境

大学のゲームサークルでMinecraftマルチプレイサーバの管理人をしている。この知見が誰かの役に立つかもしれないと思い、書き残しておくことにした。

# 動作環境
- WebARENA Indigo (VPS)
    - Ubuntu 22.04LTS
        - Linux 5.15
    - メモリ 4GB
    - CPU 4コア
    - SSD 80GB
    - IPv4[^1]
    - ネットワーク 500Mbps上限
    - Eclipse Temurin jdk-17
    - プラグインサーバ PaperMC

[^1]: 現在WebARENA Indigoを契約するとIPv4/IPv6両対応になるが、対応前に作成したインスタンスについてはIPv6には対応しない。

以前はConohaVPSを利用していたが、料金の関係でWebARENA Indigoに乗り換えた。

# アカウント運用
WebARENA IndigoでUbuntuを使うと、ユーザー`ubuntu`が作成され、そこにSSH鍵でログインする形になる。そのユーザーでMinecraftサーバを動かすのではなく、私個人が使用するユーザー（仮に`hoge`とする）とユーザー`minecraft`を作成し、`hoge`から`sudo su - minecraft`してユーザーを切り替え、そこでサーバを動かしている。
なぜこんなに回りくどいことをしているのかというと、私以外にも管理できる人がいた方がいいと考えているのと、その際にログインパスワードを共有したくないからである。~~私が管理人になる前は`root`のパスワードを共有してTera Termから直接ログインしていたのだから恐ろしい。~~

# JVM
Minecraft 1.18以降はJava 17が必須らしい。普通にOpenJDKを使えばいいのだが、Conohaだった頃はAdoptOpenJDKを使っていた（途中から引き継いだので理由はよく分からない）ので何となく後継のEclipse Temurinを使っている。元々入っているOpenJDKでも何も問題無いと思われる。
[公式が案内する手順](https://adoptium.net/installation/linux/#_deb_installation_on_debian_or_ubuntu)通りにやると、aptからdebパッケージでインストールできる。

# 起動スクリプト
大したことはしていない。JVM引数の解説は割愛する。というか誰か私にJVM引数教えて欲しい。


```bash:start.sh
#!/bin/bash
screen -dmS minecraft_server java -Xms2G -Xmx2G -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1NewSizePercent=20 -XX:G1ReservePercent=20 -XX:MaxGCPauseMillis=30 -XX:G1HeapRegionSize=32M -jar paper.jar nogui
exit 0
```

`screen`を使って仮想端末上で動作させると便利なのでそれを使っている。以下はオプションの解説。
- -dm ･･･生成したセッションにアタッチしない
- -S ･･･セッションに名前を付ける（ここでは`minecraft_server`）

`screen -r minecraft_server`でセッションにアタッチでき、そこで`save-all`や`stop`などができる。Ctrl+A Ctrl+Dを順に押下するとデタッチできる。デタッチしてSSH接続を切断してしまってもサーバは動き続ける。

# プラグインサーバ
プラグインサーバとして[PaperMC](https://papermc.io/)を使っている。荒らし対策として[CoreProtect](https://github.com/PlayPro/CoreProtect/)を導入する際に、どうやらSpigotよりも速いらしいと知って採用した。
ネザーとエンドのワールドデータがそれぞれ`world_neather` `world_the_end`として`world`ディレクトリから分離されるので、作ったワールドを配布する際などには注意が必要である。
チーム分けをしてミニゲームなどで遊ぶことが多い場合、`config/paper-global.yml`の`save-empty-scoreboard-teams`を`true`にしておくとよい。

使い方は単純で、jarファイルをダウンロードしてきて起動スクリプトで起動すれば良いだけ。初回起動時は公式サーバと同様にeulaへの同意を求められた気がする。起動に成功すると`plugins`ディレクトリが生成されるのでプラグインはjarファイルのままそこに入れる。

# バックアップスクリプト
[3年前の記事](https://qiita.com/zawanume/items/17bcb08cbdf2aa90dc11)に準じているが、`tar`より`afio`の方がアーカイブファイルの破損があった際にも展開できるので安全らしいと知り、`afio`を使っている。高校生の頃にやりすぎて浪人の原因を作ったこのゲームに関する知見が大学生になってからも役立っていると考えると感慨深い。

`apt show afio`を実行して表示される概要を引用する。

>ASCII フォーマットのヘッダ情報だけしか含んでいないので、afio アーカイブには移植性があります。afio は cpio フォーマットのアーカイブを作ります。afio は入力データの破壊に対してかなり柔軟に扱えます。afio は対話的な操作によるマルチボリュームなアーカイブをサポートします。afio は圧縮された tar 又はcpio アーカイブよりもより安全な圧縮アーカイブを作成できます。
afio は、バックアップスクリプトから呼び出す 'アーカイブエンジン' として使用するのが最適です。

以下は実際に使用しているワールドデータのバックアップスクリプトである。

```bash:world_backup.sh
#!/bin/bash

WAIT=60
STARTSCRIPT=$HOME/minecraft_server/start.sh
SCREEN_NAME='minecraft_server'

screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "say Backup the server after '${WAIT}' seconds.\015"'
screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "say Please wait a few minutes as we will be able to reconnect soon.\015"'

sleep $WAIT
screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "stop\015"'

while [ -n "$(screen -ls | grep -o "${SCREEN_NAME}")" ]
do
  sleep 1
done

DIR=`date '+%Y%m%d_%H%M'`
cd $HOME/minecraft_server
find ./world* | afio -oxvZ ../world_backup/$DIR.afz

$STARTSCRIPT
exit 0
```

「60秒後にサーバが停止します」「数分後に再度入れるようになります」というメッセージを表示した後、60秒待ってサーバを停止する。`screen -ls`で`minecraft_server`が無いことを確認してから、バックアップする。`find ./world*`としているのは、先述の通りネザーとエンドのワールドデータが別ディレクトリに分離されているからである。以下は`afio`コマンドのオプションである。
- -o ･･･バックアップを作成
- -x ･･･所有者情報を含める
- -v ･･･詳細を表示
- -Z ･･･圧縮

このスクリプトを`cron`を用いて毎週月曜日午前6時に実行するように設定している。`crontab -e`で設定できる。

```
SHELL=/bin/bash
HOME=/home/minecraft
PATH=/bin:/usr/bin

0 6 * * 1 $HOME/minecraft_server/world_backup.sh
```

`cron`がデフォルトで持っている環境変数は貧弱らしいので、わざわざ設定する。数字は左から順に分、時、日、月、曜日を表す。曜日は`0`から`7`にかけて順に日、月、火、水、木、金、土、日である。`*`は全ての値を表す。