UbuntuでMinecraft forgeサーバー(1.18.1)を立てる

forgeサーバー(ほぼバニラ)を立てたので、忘れないうちに方法を残しておく。

## 環境
マルチプレイ人数1〜4人を想定
- レンタルサーバー (KAGOYA CLOUD VPS)
    - OS: Ubuntu (20.04 LTS)
    - CPU: 4コア
    - メモリ: 4GB
    - SSD: 30GB
- Minecraft forge (1.18.1) 導入MOD
    - [OptiFine](https://fileninja.net/download/mlO1x4Urea/application_java_archive/optifine_1_18_1_hd_u_h4_jar.html)
    - [Ore Excavation](https://www.curseforge.com/minecraft/mc-mods/ore-excavation/files)
    - [journeyMap](https://www.curseforge.com/minecraft/mc-mods/journeymap/files/3621130)

## インスタンスを作成
[カゴヤのサーバー研究室](https://www.kagoya.jp/howto/cloud/vps/vps_minecraft/)を参考にインスタンスを作成する。
この時認証キーのファイル(xxx.key)がローカルにダウンロードされるので、とっておく。

## サーバー構築
### SSH接続とパッケージアップグレード
ダウンロードした認証キーのファイルパスを指定して、サーバーのルートユーザーにSSH接続する。

```bash
ssh -i 認証キーへのパス/XXXXXX.key root@xxx.xxx.xxx.xxx
```

接続できたら、パッケージをアップグレードする。

```
sudo apt-update
sudo apt -y upgrade
```

### Javaのインストール
遊びたいMinecraftのバージョンに合わせたJavaをインストールする。
Javaのバージョンは[Minecraft Wiki 更新の必要性](https://minecraft.fandom.com/ja/wiki/%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB/Java%E3%81%AE%E6%9B%B4%E6%96%B0#.E6.9B.B4.E6.96.B0.E3.81.AE.E5.BF.85.E8.A6.81.E6.80.A7)を確認する。

```bash
sudo apt install openjdk-17-jdk
```

### ユーザーログイン
[カゴヤのサーバー研究室](https://www.kagoya.jp/howto/cloud/vps/vps_minecraft/)にならって、サーバー管理用ユーザーを作成し、スーパーユーザーでログインする。
以降の作業は全てこのユーザーで行う。

```bash
sudo adduser minecraft
su - minecraft
```

### forgeのインストール
Minecraftのバージョンや管理したいMOD構成毎にディレクトリを作成する。

```bash
mkdir forge_1.18.1
cd forge_1.18.1
```

forgeインストーラーをダウンロードする。
1. [forgeのDownloadサイト](https://files.minecraftforge.net/net/minecraftforge/forge/)にアクセスし、ダウンロードしたいインストーラーをクリックする。
1. 数秒待機すると、右上に"skip"というボタンが表示されるので、そのボタンを右クリックしてURLをコピーする。
1. `wget コピーしたURL`でjarファイルをダウンロード

インストーラーを実行する。
最後の`--installServer`オプションを必ずつけること。

```bash
java -jar forge-xxx.xxxxxx-installer.jar --installServer
```

生成されているファイルのうち、user_jvm_args.txtをサーバーのメモリに合わせて編集する。
今回は念の為メモリ上限を3800MBに設定した。

```bash
vi user_jvm_args.txt
```

```user_jvm_args.txt
# Xmx and Xms set the maximum and minimum RAM usage, respectively.
# They can take any number, followed by an M or a G.
# M means Megabyte, G means Gigabyte.
# For example, to set the maximum to 3GB: -Xmx3G
# To set the minimum to 2.5GB: -Xms2500M
-Xms1G
# A good default for a modded server is 4GB.
# Uncomment the next line to set it.
-Xmx3800M
```

[vimで方向キー、バックスペースが使えないときの対処法](https://qiita.com/blue_camel/items/e2bd59a3b33acd5edfd0)

### 初回起動
インストーラー実行によって生成されたrun.shを実行する。

```bash
./run.sh
```

すると利用規約の同意を求められるので、eula.txtの`false`を`true`に書き換える。

```
#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://account.mojang.com/documents/minecraft_eula).
eula=true
```

再度run.shを実行し、Succsessの表示が出ればサーバー起動成功。
### ポート開放
最後にポートを開放する。

```
iptables -A INPUT -p tcp --dport 25565 -j ACCEPT
iptables -A INPUT -p tcp --sport 25565 -j ACCEPT
```

## MOD導入
ダウンロードしたMODファイルをscpコマンドでサーバーにアップロードする。

:::note warn
ワイルドカードを使用する場合は、送信元ディレクトリに他のjarファイルがないことを確認する。
:::

```bash
scp *.jar minecraft@xxx.xxx.xxx.xxx:/forge_1.18.1/mods
```

modsディレクトリ下にjarファイルがコピーされていることを確認したら、run.shを実行してサーバーを起動する。

## ワールドデータ移行
他の起動構成からワールドデータを移行する時は、作業ディレクトリ(forge_1.18.1など)の下に直接worldディレクトリをコピーすれば良い。

## サーバーを常時起動
このままでは、SSH接続が切れたりユーザーがログアウトするとサーバーが停止してしまう。そのため[screen](https://kazmax.zpp.jp/cmd/s/screen.1.html)を使用してデーモン化する。
run24.shによってサーバーを起動する。

:::note info
シャットダウンは、[こちら](https://blog.huwasaku.com/147)の停止用スクリプトを使用した。
:::

```bash
apt install screen
```

```run24.sh
SCREEN_SERVER='minecraft'
screen -mdS $SCREEN_SERVER ./run.sh
```

## サーバー起動、シャットダウン時にLINEに通知を送信
せっかくなので、サーバーの起動やシャットダウン、ユーザーのログインなどのログをLINEに送信するスクリプトを組んだ(ほぼ[こちら](https://takuya-1st.hatenablog.jp/entry/2017/05/11/073000)のコピペ)。
これには[LINE Notifyでトークンを取得](https://notify-bot.line.me/ja/)する必要がある。

```bash:line_notify.sh
LINE_ACCESS_TOKEN=取得したトークン
tail -n0 -F logs/latest.log | grep --line-buffered -e joined -e left -e Successfully -e Stopping | xargs -I @ curl -X POST -H "Authorization: Bearer ${LINE_ACCESS_TOKEN}" -F "message=@" https://notify-api.line.me/api/notify
```

これを組み込むために、スクリプトを編集する。

```run24.sh
SCREEN_SERVER='minecraft'
SCREEN_NOTIFY='line_notify'

screen -mdS $SCREEN_SERVER ./run.sh
nohup ./line_notify.sh > /dev/null 2>&1 &
```

```shutdown.sh
SCREEN_SERVER='minecraft'

if [ -n "$(screen -list | grep -o "${SCREEN_SERVER}")" ]; then
    screen -S $SCREEN_SERVER -X stuff 'say 30秒後にサーバーを停止します\015'
    sleep 30s
    screen -S $SCREEN_SERVER -X stuff 'save-all\015'
    sleep 5s
    screen -S $SCREEN_SERVER -X stuff 'stop\015'
    sleep 30s
else
    echo [date '+%F %T']  'server is not runnning'
fi

pkill -f "tail -n0 -F logs/latest.log"
pkill -f "grep --line-buffered -e joined -e left -e Successfully -e Stopping"
pkill -f "xargs -I @ curl -X POST -H Authorization: Bearer LINENotifyのトークン -F message=@ https://notify-api.line.me/api/notify"
```

### サーバー起動中にコマンド入力したい
[screenにアタッチ](https://qiita.com/ryounagaoka/items/8203e9c149b542986c92)してからコマンドを入力する。

```bash
screen -r minecraft #screenにアタッチ
Ctrl+a ,d #screenからデタッチ
```

## 参考

https://www.kagoya.jp/howto/cloud/vps/vps_minecraft/

https://chiritsumo-life.com/20200821/line-notify-bash/

https://takuya-1st.hatenablog.jp/entry/2017/05/11/073000