Minecraftサーバー(Spigot)をUbuntuで動かすまとめ　起動・停止・バックアップスクリプト付き

仕事をやめてのんびりフリーターやってます。
ひょんなことからMinecraftのサーバーを頻繁に弄る生活になったので、忘備録を兼ねて書きます。

## サーバーを動かしている環境
現状、最大同時接続50人くらいに耐えることは確認済み。

* OS : Ubuntu 20.04 LTS
* マシンスペック
    * CPU : intel core i7 4790k
    * Mem : DDR3 32GB 1866MHz
    * Network : auひかり 100mbps
    * 他、かつてのゲーム用自作PCを引退させて転用
* JavaはOS標準リポジトリから入れた1.8。多分openJDKだった気がする。
<br>


## サーバーに導入しているもの
タイミングを見て適宜アップデート、詳細なバージョン番号は割愛。

* Spigot 1.15.2
* プラグインたち
    * [ByeByeWither](https://e-craft.io/bukkit/plugin/byebyewither/)
    * [ChestSafe](https://e-craft.io/bukkit/plugin/chestsafe/)
    * [CoreProtect](https://dev.bukkit.org/projects/coreprotect)
    * [ImmutableSpawner](https://e-craft.io/bukkit/plugin/immutablespawner/)
    * [LuckPerms](https://www.spigotmc.org/resources/luckperms.28140/)
    * [LunaChat](https://github.com/ucchyocean/LunaChat)
    * [Multiverse-Core](https://dev.bukkit.org/projects/multiverse-core)
    * [Multiverse-Portals](https://dev.bukkit.org/projects/multiverse-portals/)
<br>

## Spigotのビルド手順
いちいち調べてヒットしたのが自分にとって分かりづらい記事だとかったるいため。
[公式](https://www.spigotmc.org/wiki/buildtools/)も読もう。

#####1. [BuildTools.jar](https://hub.spigotmc.org/jenkins/job/BuildTools)のダウンロード
```
curl -L -O https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar
```
#####2. 実行
```
java -jar BuildTools.jar --rev 1.15.2
```
これでspigot-1.15.2.jarが同ディレクトリに生成されていれば完了。
<br>

## サーバー起動、停止とバックアップに使用しているスクリプト
<>内は適宜環境に合わせて書き換えて使用すること。
バックグラウンドプロセス化するためにscreenコマンドを使用している。
~~マークダウンの制御文字とスクリプトの修飾が衝突するせいでうまく折りたたみできなかった勘弁~~
###起動

```startServer.sh
#!/bin/bash

# 変数定義
#===============================================================
# サーバーjar
SERVICE='spigot-1.15.2.jar'
# サーバー起動ユーザー
USERNAME='<spigotを起動するOSユーザー>'
# サーバーセッション名
SCNAME='minecraft'
# サーバー格納パス
MC_PATH='<spigotのjarが保存されているディレクトリのフルパス>'

# メモリ割り当て最大
XMX="24G"
# メモリ割り当て最小
XMS="24G"
#===============================================================

# 処理
#===============================================================
cd $MC_PATH

ME=`whoami`

# ユーザー判定
if [ $ME == $USERNAME ] ; then
  # 起動確認
  if pgrep -u $USERNAME -f $SERVICE > /dev/null
    then
      echo "$SERVICE is already runnning."
    else
      # サーバー起動
      echo "Starting $SERVICE..."
      screen -UAmdS $SCNAME java -server -XX:+UseG1GC -XX:+UseTLAB -XX:+UseFastAccessorMethods -XX:+AggressiveOpts -XX:+UseBiasedLocking -Xmx$XMX -Xms$XMS -Xmn18G -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=512M -XX:CompressedClassSpaceSize=512M -Xss1G -XX:ParallelGCThreads=8 -XX:ConcGCThreads=2 -XX:+DisableExplicitGC -XX:+UseCompressedOops -XX:+OptimizeStringConcat -XX:-UseGCOverheadLimit -jar $SERVICE nogui
  fi
else
  echo "Please run the $USERNAME user."
fi
#===============================================================
```

###停止
```startServer.sh
#!/bin/bash

# 変数定義
#===============================================================
# サーバーjar
SERVICE='spigot-1.15.2.jar'
# サーバー起動ユーザー
USERNAME='<spigotを起動するOSユーザー>'
# サーバーセッション名
SCNAME='minecraft'
# サーバー格納パス
MC_PATH='<spigotのjarが保存されているディレクトリのフルパス>'
#===============================================================

# 処理
#===============================================================
cd $MC_PATH

ME=`whoami`

# ユーザー判定
if [ $ME == $USERNAME ] ; then
  # 起動確認
  if pgrep -u $USERNAME -f $SERVICE > /dev/null
    then
      # 停止開始
      echo 'server stop script start'
      # サーバー内にアナウンス
      screen -p 0 -S $SCNAME -X eval 'stuff "say 30秒後にサーバーを停止します"\015'
      sleep 30s
      # セーブコマンド発行
      screen -p 0 -S $SCNAME -X eval 'stuff "save-all"\015'
      sleep 5s
      # 停止コマンド発行
      screen -p 0 -S $SCNAME -X eval 'stuff "stop"\015'
      #停止実行待機
      sleep 10s
    else
      echo "$SERVICE is not runnning."
  fi
else
  echo "Please run the $USERNAME user."
fi
#===============================================================
```
<br>
###バックアップ2種
無停止は毎時30分に、フルバックアップは毎日0時と12時にそれぞれcronに登録して実行。

```bash:bash
crontab -e

0  12 * * * /home/mcsrv/mc-spigot/backupFull.sh
0  0  * * * /home/mcsrv/mc-spigot/backupFull.sh
30 *  * * * /home/mcsrv/mc-spigot/backupOnTime.sh
```

####無停止、ワールドデータのみのバックアップ
```backupOnTime.sh
#!/bin/bash

# 変数定義
#===============================================================
# サーバーjar
SERVICE='spigot-1.15.2.jar'
# サーバー起動ユーザー
USERNAME='<spigotを起動するOSユーザー>'
# サーバーセッション名
SCNAME='minecraft'
# サーバー格納パス
MC_PATH='<spigotのjarが保存されているディレクトリのフルパス>'
# バックアップ格納パス
BK_PATH='<バックアップを保存するディレクトリ>/on_time'

# バックアップ取得時刻
BK_TIME=`date +%Y%m%d-%H%M%S`
# バックアップ保存世代数
BK_GEN="3"
TAIL_GEN="+4"
# バックアップファイル名
BK_NAME="${BK_PATH}/mc_backup_on_time_${BK_TIME}.tar"
# バックアップ対象ファイル
BK_FILE="$MC_PATH/<バックアップ対象のディレクトリ、ファイル> \
        $MC_PATH/<必要な分だけ追記> \
        $MC_PATH/<終行にはバックスラッシュ不要>"
#===============================================================

# 処理
#===============================================================
# 処理開始時刻を取得
IS_TIME="date '+%Y-%m-%d %H:%M:%S'"
echo "starting full backup..."
eval $IS_TIME

cd $MC_PATH

ME=`whoami`

# ユーザー判定
if [ $ME == $USERNAME ] ; then
  # 起動確認
  if pgrep -u $USERNAME -f $SERVICE > /dev/null
    then
      echo "Backup start minecraft data..."
      # サーバー内にアナウンス
      screen -p 0 -S $SCNAME -X eval 'stuff "say ワールドバックアップを取得します"\015'
      # オートセーブ停止
      screen -p 0 -S $SCNAME -X eval 'stuff "save-off"\015'
      sleep 10s
      # 手動セーブ
      screen -p 0 -S $SCNAME -X eval 'stuff "save-all"\015'
      sleep 10s
      # 対象ファイルのアーカイブ
      tar cfv $BK_NAME $BK_FILE
      sleep 20s
      # オートセーブ再開
      screen -p 0 -S $SCNAME -X eval 'stuff "save-on"\015'
      # サーバー内にアナウンス
      screen -p 0 -S $SCNAME -X eval 'stuff "say ワールドバックアップの取得を完了しました"\015'
      echo "minecraft_server backup compleate!"
      # 圧縮
      gzip -f $BK_NAME
      # 過去世代の削除
      ## ディレクトリの存在チェック
      if [ -d $BK_PATH ]; then
        # バックアップディレクトリが存在している場合、
        # 格納されているファイル一覧を表示
        echo "These files are in the backup directory now"
        ls -lrth $BK_PATH
      else
        echo "The backup directory (${BK_PATH}) doesn't exist !"
        eval $IS_TIME
      fi

      # バックアップファイルをローカルのバックアップフォルダで世代管理
      TGT_FILE="${BK_PATH}/mc_backup_on_time_????????-??????.tar.gz"
      CHK_GEN=$(ls -1 ${TGT_FILE}|wc -l)

      # 削除対象ファイルリスト
      DEL_LIST=$(ls -1 -t ${TGT_FILE}|tail -n ${TAIL_GEN})

      ## ファイル削除要否判定
      if [[ ${CHK_GEN} -gt $BK_GEN ]]; then
          # バックアップファイル数が保管世代数より多い場合、
          # 逆順ソートして、最新3世代を残して削除
          echo "Saved backup gen : ${CHK_GEN}"
          echo "To delete : ${DEL_LIST}"
          echo "Delete old backup files..."
          rm -f ${BK_PATH}/${DEL_LIST}
          eval $IS_TIME
      else
          echo "There is no old backup files to delete"
          eval $IS_TIME
      fi
    else
      echo "$SERVICE was not runnning."
  fi
else
  echo "Please run the echo $USERNAME user."
```

####サーバー停止、ディレクトリ全体のバックアップ
```backupFull.sh
#!/bin/bash

# 変数定義
#===============================================================
# サーバーjar
SERVICE='spigot-1.15.2.jar'
# サーバー起動ユーザー
USERNAME='<spigotを起動するOSユーザー>'
# サーバーセッション名
SCNAME='minecraft'
# サーバー格納パス
MC_PATH='<spigotのjarが保存されているディレクトリのフルパス>'
# バックアップ格納パス
BK_PATH='<バックアップを保存するディレクトリ>/full'

# バックアップ取得時刻
BK_TIME=`date +%Y%m%d-%H%M%S`
# バックアップファイル名
BK_NAME="${BK_PATH}/mc_backup_full_${BK_TIME}.tar.gz"
# バックアップ保存世代数
BK_GEN="3"
TAIL_GEN="+4"

# メモリ割り当て最大
XMX="24G"
# メモリ割り当て最小
XMS="24G"
#===============================================================

# 処理
#===============================================================
# 処理開始時刻を取得
IS_TIME="date '+%Y-%m-%d %H:%M:%S'"
echo "starting full backup..."
eval $IS_TIME

cd $MC_PATH

ME=`whoami`

# ユーザー判定
if [ $ME == $USERNAME ] ; then
  # 起動確認
  if pgrep -u $USERNAME -f $SERVICE > /dev/null
    then
      echo "Full backup start minecraft data..."
      # サーバー内にアナウンス
      screen -p 0 -S $SCNAME -X eval 'stuff "say 30秒後にサーバーを停止し、バックアップを取得します"\015'
      screen -p 0 -S $SCNAME -X eval 'stuff "say 完了次第、再起動します"\015'
      sleep 30s
      # セーブコマンド発行
      screen -p 0 -S $SCNAME -X eval 'stuff "save-all"\015'
      sleep 5s
      # 停止コマンド発行
      screen -p 0 -S $SCNAME -X eval 'stuff "stop"\015'
      #停止実行待機
      sleep 10s
      echo "Stopped minecraft_server"
      echo "Full Backup start ..."
      # アーカイブ・圧縮
      tar cfvz $BK_NAME $MC_PATH
      sleep 30s
      echo "Full Backup compleate!"
      # 過去世代の削除
      ## ディレクトリの存在チェック
      if [ -d $BK_PATH ]; then
        # バックアップディレクトリが存在している場合、
        # 格納されているファイル一覧を表示
        echo "These files are in the backup directory now"
        ls -lrth $BK_PATH
      else
        echo "The backup directory (${BK_PATH}) doesn't exist !"
        eval $IS_TIME
      fi

      # バックアップファイルをローカルのバックアップフォルダで世代管理
      TGT_FILE="${BK_PATH}/mc_backup_full_????????-??????.tar.gz"
      CHK_GEN=$(ls -1 ${TGT_FILE}|wc -l)

      # 削除対象ファイルリスト
      DEL_LIST=$(ls -1 -t ${TGT_FILE}|tail -n ${TAIL_GEN})

      ## ファイル削除要否判定
      if [[ ${CHK_GEN} -gt $BK_GEN ]]; then
          # バックアップファイル数が保管世代数より多い場合、
          # 逆順ソートして、最新3世代を残して削除
          echo "Saved backup gen : ${CHK_GEN}"
          echo "To delete : ${DEL_LIST}"
          echo "Delete old backup files..."
          rm -f ${BK_PATH}/${DEL_LIST}
          eval $IS_TIME
      else
          echo "There is no old backup files to delete"
          eval $IS_TIME
      fi

      # サーバー起動
      echo "Starting $SERVICE..."
      screen -UAmdS $SCNAME java -server -XX:+UseG1GC -XX:+UseTLAB -XX:+UseFastAccessorMethods -XX:+AggressiveOpts -XX:+UseBiasedLocking -Xmx$XMX -Xms$XMS -Xmn18G -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=512M -XX:CompressedClassSpaceSize=512M -Xss1G -XX:ParallelGCThreads=8 -XX:ConcGCThreads=2 -XX:+DisableExplicitGC -XX:+UseCompressedOops -XX:+OptimizeStringConcat -XX:-UseGCOverheadLimit -jar $SERVICE nogui
    else
      echo "$SERVICE is not runnning."
  fi
else
  echo "Please run the $USERNAME user."
fi
#===============================================================
```

今の所書きたいことは以上！