Minecraft RCON についてのメモ
この記事はこれから Minecraft で RCON ライブラリを使いたい人、または RCON クライアントを実装したい人向けのメモです。
RCON で何ができるのか
RCON によってバニラの Minecraft サーバーとコマンドによって通信が可能になり、 これを利用することで Minecraft 内部と様々な外部アプリケーションが連携できます。
もちろんサーバー本体を書き換える Bukkit や Spigot などの方ができることが多いのですが、 RCON は公式 API である点が重要です。
私は Emacs で Minecraft Function (mcfunction) 専用のモードを作っていますが、 RCON 対応により編集中のコマンドをすぐさま Minecraft に送り、またその結果を受けとることができるようになりました。
↓↓↓ 宣伝 ↓↓↓ github.com
Source RCON と Minecraft RCON について
RCON は Source Dedicated Server (SRCDS) という Valve 社によるゲームサーバーの管理コンソールプロトコルです。
https://developer.valvesoftware.com/wiki/Source_RCON_Protocol
上記 wiki には Source RCON の仕様らしきものが書いてありますが、 あくまでも開発コミュニティの wiki であって公式仕様ではありません。
どういう経緯で Valve のサーバー機能の一部が Minecraft に採用されたのかは知りませんが、 Minecraft RCON の公開された仕様も存在しないようです。
Valve 開発コミュニティの wiki は情報が十分にあるのでまずはこちらを参考にして下さい。 Source / Minecraft RCON 両者の仕様の違いは以下に記します。
文字コード
パケット body フィールドの文字コードは Source RCON の wiki には ASCII とありますが、 2015 年に UTF-8 に対応したようです。 Minecraft RCON もシステム環境に依存せず UTF-8 固定です。
1.14.3-pre2 より前のバージョンで RCON を使うとクラッシュする。
https://bugs.mojang.com/browse/MC-72390
1.14.3-pre2 より古いバージョンの Minecraft で RCON を使用すると、サーバーは頻繁にクラッシュします。 つまり Minecraft RCON はずっとスレッドセーフではありませんでしたが修正されました。
応答テキストの改行文字が消える
https://bugs.mojang.com/browse/MC-7569
2013年1月に報告されたバグですが未だに修正されていません。
接続中はサーバーが停止できない
RCON クライアントが接続中のままサーバーを停止しようとすると、サーバーは終了できずに沢山の警告を出します。クライアントを終了させてからサーバーを停止するように注意します。
不正なパケットによる強制切断
以下のケースで RCON サーバーから強制的に切断されました。
パケットのサイズが 1460 バイトを越えると切断される
Minecraft サーバーに RCON で送れるコマンドの長さには制限があります。
パケットのヘッダサイズ等を踏まえて計算すると ASCII 文字で送れるのは 1446 文字が限界ということになり、 UTF-8 の日本語だと更に 1/3 に減るので 482 文字です。少ないですよね。
まとめて二つ以上のパケットを送信すると切断される
複数のパケットをまとめてサーバーに送ると切断されてしまいます。 ただし例外もあり、後述の分割確認用パケットは大丈夫のようです。
原因調査中。
対処方法としてはサーバの応答を待ってからコマンドを送るか、またはネットワーク接続を維持しないでコマンドを送るたびに毎回認証する方法があります。
RCON ライブラリを使用する場合の注意点まとめ
どこかの RCON ライブラリを使う場合、まず以下の二点を必ず検証してから使うようにします。
- UTF-8 が扱えるかどうか
- 4096 バイト以上のパケットを正常に受け取れるか
また RCON 自体の問題ですが暗号化通信に対応していません。 もしインターネット越しで使用する場合、Minecraft サーバーかあるいはローカルネットワーク内にRCONクライアントを配置し、SSHを通して実行することになります。
Minecraft RCON クライアント実装
RCON パケット仕様は Valve wiki と同じです。
フィールド | 型 | 説明 |
---|---|---|
size | 32-bit リトルエンディアン 符号付き整数 | RCON パケットの長さから size フィールド自身のサイズ(4 bytes)を引いた数値 |
id | 32-bit リトルエンディアン 符号付き整数 | コマンド実行時に id を指定すると、対応する応答パケットに同じ id がセットされる |
type | 32-bit リトルエンディアン 符号付き整数 | クライアント送信: 認証=3 コマンド実行=2 サーバー送信: 認証成功=2 コマンド結果=0 |
body | UTF-8文字列 (0~N バイト) | 文字列の長さは size - (id + type + null + null) == size - 10 |
null | 1 byte (0x0) | 文字列終端用(たぶん) |
null | 1 byte (0x0) | パケット終端用 |
コマンドに対する分割応答パケットの扱い方
応答パケットの分割は wiki の記述とはだいぶ異なります。
RCON サーバーは応答パケットのサイズが 4096 バイトを越える場合は分割して送る仕様になっていますが、 そもそもMinecraft RCON の応答には複数のバグがありパケットを正しい長さで計算しません。(1.14.3) それに加えて分割パケットの終端確認に対応しようとすると連結したパケットも扱うことになります。
短い応答であれば一つの通信パケットをそのまま一つの RCON 応答パケットとして扱うこともできますが、それでちゃんと扱えていると思うのは錯覚にすぎないので最初からバッファ方式で実装するようにします。
応答パケットは一旦可変長バッファに連結して溜め、先頭の size フィールドを調べてパケットサイズを計算し、サイズ分のバッファが溜まってからパケット本体を読むようにすればバグは表面化しません。
分割パケット終端の確認
分割されて送られてきたコマンド応答パケットはそのままではどれが最後のパケットなのかわかりません。
そこでコマンド実行のパケット、次に終端確認用の body が空のパケットを送ります。 確認用パケットの id は任意、type は 2 か 3 以外なら何でもよいので 0 でいいと思います。
サーバーは順序通りに応答し、分割された複数のコマンド結果のパケットと、確認に対する結果が最後のパケットに連結されて返ってきます。
SIZE ID TYPE "Unknown request TYPE(hex)" 0x0 0x0
これが終端確認用パケットに対するサーバーの応答パケットになります。
確認用パケットの ID を -2 などに指定しておけば応答するパケットの ID も -2 になるので簡単に終端が判別できます。