- 資源エンドのリセットを、毎回手でやるのが面倒
- でも定期で回すと、再生成の途中で誰かが入ってきそう
- 失敗したときにサーバーが変な状態で残るのも怖い
資源用のエンドは、遊ばれるほど掘られて、資源が減っていきます。
僕のサーバーにもリセット用のスクリプトはあったものの、流すのは毎回手動。
これを毎週日曜の朝4時に自動で回すことにしました。そのとき引っかかったのが、再生成の途中で入ってくる人をどう防ぐか、という点です。
選んだのは、リセット中だけマイクラ標準のホワイトリストを有効にして、全員を締め出すやり方でした。
この記事で扱うのは次の3つです。
- 再生成のあいだ全員を締め出す組み方
- 途中で失敗しても締め出しを解除する仕組み
- systemdでバックアップのあとに続けて動かす設定
自分のサーバーで定期リセットを組むとき、どこで事故が起きるかを判断しやすくなるでしょう。
目次
- 前提の環境
- 手動のリセットでやっていたこと
- 定期にすると、途中で入ってくる人を止められない
- リセット中だけホワイトリストを有効にする
- 失敗しても戻し忘れない
- systemdで、バックアップに成功した日だけリセットする
- 締め出しても残る穴
- まとめ:定期リセットは「止める」と「戻す」をセットで組む
前提の環境
| 項目 | 内容 |
|---|---|
| サーバー | Paper 26.2(Dockerのitzg/minecraft-serverで動かしている) |
| 資源エンド | Multiverse-Core 5.8.1のワールドresource_the_end |
| 入場の判定 | 自作のDiscord認証プラグインMCAuth。標準のホワイトリストは使っていない |
| 定期実行 | ホストのUbuntuのsystemdタイマー |
| 操作 | シェルスクリプトからRCON(外部からサーバーコマンドを送る仕組み)で実行 |
資源エンドへは、ロビーの感圧板から入れるようにしています。
手動のリセットでやっていたこと
もともとのreset-end.shは、次の順で動いていました。
- サーバーを止めてバックアップを取り、再開する
- 接続者がいれば、30秒前にチャットで告知する
- 資源エンドにいる人を、ロビーのスポーン地点へテレポートする
mv regen resource_the_end --seedで新しいシードに作り直し、mv confirmで確定する- ワールドのUIDが変わったことで、作り直しが終わったと判断する
- メイン島の地表を調べて、
mv setspawnでスポーン地点を決める
3でロビーへ移しているのは、mv regenの--remove-playersを使わないためです。このオプションは、再生成のあとにプレイヤーを同じワールドのスポーンへ戻します。ところが作り直した直後のスポーンは出口ポータルの上で、そこへ戻されると困るわけです。
手動なら、流す人がその場で様子を見られます。困るのは、誰も見ていない時間に自動で回すとき。
定期にすると、途中で入ってくる人を止められない
自動で回すと、3でエンドを空にしたあと、4の再生成が終わるまでのあいだに、誰かがロビーの感圧板を踏むかもしれません。
再生成中のワールドにテレポートされると、何が起きるか分かりません。少なくとも、出口ポータルの上に立たされるのは避けたいところです。
止め方の候補は2つ。
| 方法 | 良いところ | 気になるところ |
|---|---|---|
| 資源エンドへの移動だけを止める | ほかのワールドで遊んでいる人に影響しない | 感圧板、コマンド、ポータルなど、入口を全部ふさぐ必要がある |
| サーバー全体を締め出す | 入口がどこにあっても関係ない | 朝4時の数分間、全員が入れなくなる |
朝4時の数分間なので、サーバー全体を締め出すほうにしました。入口を全部ふさげているかを考えなくて済むのが大きいです。
リセット中だけホワイトリストを有効にする

締め出しには、マイクラ標準のホワイトリストを使いました。
僕のサーバーは、入場の判定を自作のMCAuthだけで行っています。標準のホワイトリストはComposeのENABLE_WHITELIST: "FALSE"で切ってあり、whitelist.jsonも空のまま。
この状態でRCONからwhitelist onにすると、whitelist.jsonに誰もいないので、OP以外は誰も入れなくなります。リスト作りも後片付けもいりません。
MCAuthとぶつからないかも確かめました。MCAuthは、ログイン直前のイベントで未認証の人を拒否するだけで、許可を上書きする処理は持っていません。そのため、MCAuthを通った人も、そのあとのホワイトリストの判定で止まる仕組みです。
告知のあとの流れは、次のとおりです。
rcon "say 30秒後に資源エンドをリセットします。リセット中の数分間はサーバーに入れません。"
sleep 30
whitelist_enabled=1
[[ "$(rcon "minecraft:whitelist on")" == *"turned on"* ]] || fail "ホワイトリストを有効にできませんでした。"
# 資源エンドにいる人は、先にロビーへ移してから切断する
rcon "minecraft:execute in minecraft:resource_the_end as @a[distance=0..] in minecraft:overworld run minecraft:tp @s $lobby_spawn"
rcon "minecraft:kick @a 資源エンドをリセットしています。数分後に入り直してください。"
切断の前にロビーへ移すのは、マイクラが切断した時点の位置を保存するからです。エンドにいたまま切断すると、入り直したときに、作り直したエンドの同じ座標に出てしまいます。
切断したあとは、listで接続者が0人になったのを確かめてから再生成へ進みます。0人にならなければ、リセットは中止。
RCONのwhitelistやkickには、頭にminecraft:を付けました。プラグインが同じ名前のコマンドを上書きしていても、バニラのコマンドを確実に呼べます。
成功したかどうかは、RCONの返事の文言で見ています。有効にするとWhitelist is now turned on、すでに有効ならWhitelist is already turned onが返るので、turned onを含むかで判定すれば十分でしょう。
失敗しても戻し忘れない
この方法でいちばん怖いのは、途中で失敗して、ホワイトリストが有効なまま残ることです。そうなると、朝起きたら誰も入れないサーバーができあがっています。
対策は2段にしました。
1つ目は、スクリプトの終了時に必ず戻す処理です。bashのtrap(終了時に決まった処理を走らせる仕組み)で、成功でも失敗でもwhitelist offを送ります。
disable_whitelist() {
if [[ "$whitelist_enabled" -eq 1 ]]; then
if [[ "$(rcon "minecraft:whitelist off")" == *"turned off"* ]]; then
whitelist_enabled=0
else
log "ERROR: ホワイトリストを無効に戻せませんでした。"
return 1
fi
fi
}
cleanup() {
local exit_code=$?
trap - EXIT
disable_whitelist || exit_code=1
exit "$exit_code"
}
trap cleanup EXIT
2つ目は、コンテナごと落ちた場合。このときはtrapも動きません。
ここは手元のサーバーで試しました。whitelist onにしたままコンテナを再起動したところ、起動後のserver.propertiesはwhite-list=falseに戻っていました。起動時に、ComposeのENABLE_WHITELIST: "FALSE"がserver.propertiesへ反映されるからです。
スクリプトが失敗しても、コンテナが落ちても、ホワイトリストは無効に戻る。これで朝の締め出し事故はかなり起きにくくなりました。
systemdで、バックアップに成功した日だけリセットする
リセットは、日曜の朝4時のバックアップのすぐあとに動かすことにしました。バックアップのためにサーバーは一度止まるので、ついでにリセットも済ませれば、止まる回数が1回で済みます。
リセット用のタイマーを別に作り、バックアップのタイマーと同じ4時に起動する形もあり得ます。ただ、2つのタイマーが同時に発火すると、どちらが先に動くかがはっきりしません。
そこで、日曜だけは1つのサービスで、バックアップとリセットを順番に動かすことにしました。
[Service]
Type=oneshot
# 毎日のバックアップと同じ処理を行い、成功した場合だけ資源エンドをリセットする。
ExecStart=/usr/bin/env bash /home/ubuntu/spsmc-infra/scripts/stop-backup.sh --update-geyser
ExecStart=/usr/bin/env bash /home/ubuntu/spsmc-infra/scripts/reset-end.sh --skip-backup
Type=oneshotのサービスにExecStartを2行書くと、上から順に実行されます。1行目が失敗したら、2行目は動きません。バックアップが取れなかった日にリセットだけ進む、という事故はこれで防げるでしょう。
リセット側はバックアップを済ませたあとなので、--skip-backupを付けて二重に取らないようにしました。
タイマーの分け方は次のとおりです。
| タイマー | 起動する日時 | 動かすもの |
|---|---|---|
| 毎日のバックアップ | 月曜から土曜の4時 | バックアップだけ |
| 毎週のリセット | 日曜の4時 | バックアップ、続けてリセット |
# 毎日のバックアップ
OnCalendar=Mon..Sat *-*-* 04:00:00 Asia/Tokyo
# 毎週のリセット
OnCalendar=Sun *-*-* 04:00:00 Asia/Tokyo
日曜のバックアップは毎週のリセットのほうで取るので、毎日のバックアップは日曜に動かしません。
締め出しても残る穴
ホワイトリストには例外が1つあります。OPは、whitelist.jsonにいなくても入れてしまう点です。
リセット中にOPが入ってくると、締め出しは素通り。今のスクリプトでは、ここは止めていません。
もう1つ、入り直そうとした人に表示されるのは、標準の「You are not whitelisted on this server!」です。プレイヤーには少し分かりにくい文言ですが、今は標準のままにしています。
プレイヤー向けには、毎週日曜の4時にリセットすることと、そのあいだは入れないことを、公式サイトのニュースで案内します。初回は10月11日の朝4時。
まとめ:定期リセットは「止める」と「戻す」をセットで組む
定期リセットで気をつけるのは、再生成そのものより、その前後の締め出しと後片付けでした。
自分のサーバーで組むなら、次の点を見ておくと事故を防げます。
- 再生成のあいだ、誰も入れない状態になっているか
- スクリプトが失敗しても、コンテナが落ちても、締め出しが解除されるか
- バックアップに失敗した日に、リセットだけが進まないか
全部を一度に作り込む必要はありません。まずは手元のサーバーでwhitelist onにしたままコンテナを再起動して、起動後に無効へ戻るかを確かめてみてください。戻るなら、最後の安全網はもうできています。
入場の判定を自作プラグインに任せている場合は、プラグインが壊れたときの振る舞いも見ておくと安心です。
関連記事マイクラのDiscord認証プラグインで「誰でも入れる」穴を塞いだ話。フェイルクローズの直し方自作のDiscord認証プラグインを監査したら、例外や起動失敗で誰でも入れる穴がありました。AsyncPlayerPreLoginEventの例外処理、連携の上書き、退出判定、起動失敗時のロックダウンの直し方と、本番で全員に再認証してもらった手順です。
