Minecraftのバニラサーバーに対しても動くようなDiscordのbotを作成した

時折、身内で遊ぶためにMinecraftのサーバーを立ち上げることがある。その際、運用のための各種コマンド入力が問題となる。

例えば、直接ttyから入力→実行しても良いのだが、インターフェイスが貧弱で結構怖かったりする。有名なMinecraft用コンテナイメージの作者itzgさんが用意しているrcon-cliなどを使い、RCON経由で直接コマンドを実行しても良いのだが、これも手順を考えると煩わしい。また、サーバーにspigotやpaperを使い、DiscordSRVのようなプラグインを入れることも考えられるが、そもそもバニラでないと実現できない挙動もいくつかあるためサードパーティ製のサーバーは利用できない。

そこで、Minecraftサーバー専用のDiscordサーバーを用意し、botを介することで問題を解決することを考え、実装した。それを紹介する。

github.com

minecraft-command-bot

コマンド一覧

cmd

コマンドを実行する。\cmd kill @e のように使える。なんでもできる強力なコマンドなので、サーバー上に cmd というロールがあり、それを付与されたユーザーのみが実行できるようにしている。

say

DiscordチャンネルからMinecraftサーバーのチャットにチャットをDiscordの名前を付与してから送信する。この後説明する Minecraftサーバーのチャット to Discordのチャンネルの逆を行うために用意した。

mcuuid

Minecraft上のnameとUUIDを相互変換する。

- `\mcuuid naarisan` will returns => `05140bb4-f432-43fe-a5e4-069da2d4fc46`
- `\mcuuid 05140bb4-f432-43fe-a5e4-069da2d4fc46` will returns => `naarisan`
- `\mcuuid 05140bb4f43243fea5e4069da2d4fc46` will also returns => `naarisan`

ログ転送

MinecraftのチャットあたりのログをDiscordのサーバーに転送する。そのまま転送しているわけではなく、正規表現で雑にルールを列挙してマッチできたものだけ転送する。

:heart: リアクションでのホワイトリスト追加

プレイヤー数表示機能

実装について

Rustで実装した。理由としては、

  • Rustを使いたかったから
  • リッチな言語のご加護を受けたかったから
  • マルチアーキテクチャのため

最後の「マルチアーキテクチャのため」について。まず、Minecraftのサーバーアプリは大変にステートフルな実装で、スケールアウトできない上にそれなりのスペックを要求する。最近は「arm64であれば超格安で強力なインスタンスを提供するよ」という例をよく見るのだが、これは前述の要求にとってベターな選択肢となりうる。今回、自分もその選択をした。OCIは無料でかなり強力なarm64のインスタンスを提供してくれるのだ。この環境上でbotを動かすことを考えると、言語の機能や文化としてクロスコンパイルが行えるRustを選択するのが良さそうだった、という感じ。詳しくはこちらでどうぞ→Rust + GitHub Actionsでマルチアーキテクチャ対応のイメージをなるべく早く作る - 名有りさんの日記

Minecraftに対してのコマンド実行はRCONとして接続することで成り立っている。Discordはインターフェイスとしても大変優秀で、これをRCONのクライアントとして使えたのは大変良いことだった。

ログをチャンネルに転送する機能は、Minecraftが標準出力と同時にログを出力するファイルがあるのだが、それをbotが見ることで実現している。linemuxでかんたんに実現できた。

itzgのMinecraft用イメージと組み合わせることでとても簡単にbotを起動することができる。↓こんなかんじ

version: "3.8"

services:
  mc:
    ports:
      - "25565:25565"
      - "25575:25575"
    volumes:
      - "mc:/data"
    environment:
      EULA: "TRUE"
    image: itzg/minecraft-server
    restart: always
  bot:
    image: ghcr.io/naari3/minecraft-command-bot:latest
    environment:
      RCON_HOST: mc
      RCON_PASSWORD: minecraft
      DISCORD_BOT_TOKEN: your_token
      DISCORD_BOT_PREFIX: \
      MINECRAFT_LOG_PATH: /data/logs/latest.log
      MINECRAFT_LOG_CHANNEL_ID: your_channel_id
    volumes:
      - "mc:/data"

volumes:
  mc:

/data にすべてのデータやログなどを吐くため、ログなどの情報が欲しい場合はこれをマウントすることで取得できるようになる。また、ボリュームを別出ししているのでコマンドひとつでバックアップが可能。本当に素晴らしい。

docker run --rm -v minecraft-command-bot_mc:/data -v $(pwd)/backup:/backup alpine tar cvf /backup/backup-$(TZ=JST+15 date '+%Y%m%d%H%M').tar /data

今後

個人的にはこのbotは大変便利で恐らく今後もこのbotは育て続けると思う。いずれ最強のMinecraftBotとなっていただきたいですね。