トップ画像
Minecraftサーバ リモート制御サイトの制作

執筆者: いけちぃ

最終更新: 4/23/2021

プログラミングを始めた頃に構築した、Minecraftサーバのリモート制御ツールの開発体験記です。(※この記事はブログからの転用です。)

Minecraft っていいよね

Minecraft って、複数人でプレイすると沼ですよね。
今や大陸内には高速道路が広がり、複数の大陸間には水上橋が建築されています。(やばすぎ)

ある日、Minecraft のサーバ管理者になった

最初はローカルでサーバを建ててプレイしていましたが、
プレイヤーがどんどん増えてきたため、Google Cloud Platform(GCP)でサーバを立てることにしました。

これが、「情クラ!」という Minecraft サーバの誕生のきっかけです。

いつどこでも Minecraft サーバで遊べる環境が完成しましたが、しかし1つ大きな問題が発生しました。

Google Cloud Platform(GCP)が、まだ365日無料トライアルだった頃の話です。
当時は n1-standard-1 プラン(仮想 CPU 数: 1, メモリ: 3.75 GB)を使用していたため、大人数が長時間プレイしていると動作がもっさりしてくるのです。

サーバ管理者だった私は、毎度 SSH 接続してサーバを再起動していました。
ですがここは、ぜひ技術の力で課題解決をしよう!ということで、Minecraftサーバを誰でも簡単に制御できる、Webアプリケーションを作成することになりました。

まずは完成品をどうぞ


最初に実装したのは、「ホーム」のお知らせ機能と「サーバ状況」の機能です。
モバイルファーストのデザインですが、レスポンシブ対応させています。

  • ホーム: プレイヤーが自由に建築報告を投稿することができます
  • サーバ状況: サーバの起動・停止状況やオンラインメンバーを確認できます

ここで、少し技術的な話をしましょう

一応そういうブログ( ?)なので、今回 Minecraft サーバと連携したノウハウについて記述しておきます。
Minecraft サーバでは、ある特定のパケットを受け取ると、現在のサーバのステータスを返す機能が搭載されています。
これは、Minecraft のマルチサーバ 一覧画面などで使用されています。
まず、クライアント側が Handshake パケットを送信します。(以下 wiki の情報)

さらに続けてリクエストパケットを送信します。その応答パケットとして、サーバが以下のようなJSONを返します。

{
    "version": {
        "name": "1.16.1",
        "protocol": 47
    },
    "players": {
        "max": 12,
        "online": 5,
        "sample": [
            {
                "name": "minecraft_name",
                "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"
            }
        ]
    },
    "description": {
        "text": "Hello world"
    },
    "favicon": "data:image/png;base64,<data>"
}

また、このパケットを送信するためのライブラリとして、今回は以下のPHPライブラリを使用しました。

PHP-Minecraft-Query https://github.com/xPaw/PHP-Minecraft-Query

実装した機能

あまり技術的な話をすると長くなっちゃいそうなので、次に行きます。

他に実装した機能も紹介します。

  • ピクチャ: プレイヤーが自由に画像を投稿することができます
  • オンラインユーザ: ホワイトリストに登録されたプレイヤーが表示され、サーバに入っているかどうかがわかります
  • 起動・停止: サーバをしばらく利用しない場合は、GCP課金対策のためにサーバを切ることができます
  • 再起動: Minecraft サーバを再起動させることができます
  • バックアップ実行: Minecraft サーバのバックアップを作成します(毎日5:00に定期実行されます)
  • バックアップ履歴: バックアップ履歴の一覧を表示します
  • 情クラマップ: 情クラワールドの建築物紹介が載っています
  • 情クラ!公式アプリ: 公式アプリへのリンクです

みんなログインしよう

今回サーバのコントロール機能が Web に公開されるので、ログイン機能を実装する必要があります。全員 Google アカウントを持っていたので、Firebase Authentication を使って Google ログインを実装します。

以下の公式ドキュメントを読むとよくわかりますよ(雑)

Firebase Authentication(Google) https://firebase.google.com/docs/auth/web/start?hl=ja

マイクラサーバのバックエンドはどうなってるの?

Minecraft サーバでは、Node.js + TypeScript でAPIを構築しています。
以下は Minecraft を起動するルーティングの一部抜粋です。

// --- Start Server ------------------------------------------------------------
router.post('/api/run/start', (req: express.Request, res: express.Response) => {
    const schema = Joi.object({
        user: Joi.string().required(),
    })

    const validation = schema.validate(req.body)
    if (validation.error) {
        post("不正なリクエストを拒否しました", "ユーザ固有IDが設定されていないリクエストが送信されました", 3)
        res.status(400).send('Bad request')
        return
    }

    statusAsync(req.body.user)
        .then(() => {
            startAsync(req.body.user)
                .then(() => {
                    res.send('Success')
                })
                .catch((err) => {
                    if (err == 'failed due to run interval')
                        post("起動コマンドを拒否しました", "前回の処理の実行から" + process.env.WAIT_SECONDS_FROM_LAST_PROCESS + "秒経過していないため、コマンドを拒否しました。", 2)
                    else
                        post("起動コマンドを拒否しました", "サーバが既に起動しているため、起動コマンドを拒否しました。サーバとの同期ができていない恐れがあります。[Err: startAsync()]", 2)
                    res.status(400).send('Bad request')
                })
        })
        .catch(() => {
            post("起動コマンドを拒否しました", "既に起動しているため、起動コマンドを拒否しました。サーバとの同期ができていない恐れがあります。[Err: statusAsync()]", 2)
            res.status(400).send('Bad request')
        })
})
// -----------------------------------------------------------------------------

軽く説明していきます。

まず、Joi という npm パッケージを利用して、送信されたクエリパラメータなどのバリデーションを行います。
そして、post( ) という関数がありますが、これはサーバのエラーなどを Discord のサーバに通知するための関数として用意してあります。

server.jar は screen 上で走らせているのですが、screen の二重起動にならないように初めに起動してもよいかのチェックも行っています。

また、実行系はシェルにまとめてあります。

#!/bin/bash

JARFILE=/home/jokura_server/minecraft/server.jar

MEM=15000M

cd `dirname $0`
screen -UAmdS minecraft java -server -Xms${MEM} -Xmx${MEM} -jar ${JARFILE} nogui

上記のファイルは、起動用のシェルです。

他にも再起動用やバックアップ用などのシェルも用意してあります。(下記は 再起動用)

#!/bin/bash

WAIT=30

STARTSCRIPT=/home/jokura_server/minecraft/start.sh

SCREEN_NAME='minecraft'


screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "say '${WAIT}'秒後にサーバを再起動します\015"'
screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "say すぐに再接続可能になるので、しばらくお待ち下さい\015"'

sleep $WAIT
screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "stop\015"'

while [ -n "$(screen -list | grep -o "${SCREEN_NAME}")" ]
do
   sleep 1
done
   $STARTSCRIPT

今現在の 情クラ!

今は残念ながら、Webサービスを終了しています。
(Minecraft の活動自体はしています!)

2021年4月から、Minecraft サーバを別のメンバーが自宅サーバで建ててくれることになりました。
やはり GCP などの従量課金制のサーバで運用するには、かなり気を使ってしまうので正直解放されました。

かといって、情クラ!での活動はまだまだ続けていく予定なので、ぜひこのブログでも活動を共有していけたらなと思います!

取得に失敗しました

2019年度 入部

GitHub