ほんじゃらねっと

ダイエット中プログラマのブログ

マインクラフトBE (Win10版)をNode.jsで自動操作する

子どもたちがマインクラフト(iOS版)にはまっており、一緒に遊んだりしている。

クリエイティブモードで一緒にわーわー言いながら街や建物を作るのはなかなか楽しい。

楽しいのだけど、

ちまちまブロックを置いていくのはそろそろ飽きてきたので、

ビャーっとブロックを配置したり、

建物を自動生成したり、

自動化してもっと面白いことができないものかと調べてみた。

調べたところ、

自動化できる仕組みがいくつか用意されていた。

functionコマンド

最初に見つけたのが、「function」コマンド。

これはファイルに書いたコマンドのリストを実行するもので、

まさに求めていたものだったのだけど、

残念ながらJava版のみでWindows版は非対応だった。

napoan.com

iOS版の Pocket Edition (PE) と一緒に遊ぶには、

Windows版でないといけないらしい。

MakeCode for Minecraft

次に見つけたのが「MakeCode for Minecraft」。

minecraft.makecode.com

Microsoft製で、

Scratchのように処理を組み合わせてコマンドを作ったり、

Javascriptでコマンドを作ったりできる。

子供にプログラミングに触れてもらうこともできて最高なツール。

MakeCodeを起動して、マイクラ側からWebSocketでconnectして、

MakeCode側でイベントや発言に対して実行する処理を定義したら、

そのイベントをマイクラ側で実行するだけで処理を実行できる。

Windows10版でも使えるし、大抵の環境はこれで自動化できるはず...

なのだけど

うちのPCが非力すぎるのか、Mac上の仮想Windows環境だからなのか、

やたら負荷がかかって、コマンドを実行するとすぐ落ちる。

残念。これは使いたかった。PCを買い替えたら再挑戦したい。

自前WebSocketサーバーを立てる

それで諦めかけていたのだけど、

MakeCodeとの接続に使用しているconnectコマンドは

WebSocketサーバに接続するためのコマンドのようなので、

MakeCodeの代わりに自前でWebSocketサーバーを立ててマイクラとやりとりしたら

コマンドも流せるんじゃない?ということで試してみた。

WebSocketは基本的にサーバ側とクライアント側でイベント名を決めておき、

そのイベント名を使ってデータのやりとりをしているはずなので、

まずはMakeCodeにWebSocketクライアントとして接続してみたり、

WebSocketサーバにマイクラから接続してみたり、

同じようにマイクラとWebSocket通信してる事例がないかググってみたりした。

どうやらMakeCodeはJSONデータをマイクラとやりとりしており、

あらかじめ受け取りたいイベントを登録(subscribe)するリクエストを

マインクラフトに投げることでマイクラがそのイベントが発生した時に

メッセージイベントを投げてくれるらしい。

また、マイクラに対してコマンドリクエスト(commandRequst)を投げることで

マイクラ側でコマンドを実行してくれるらしい。

で、マイクラ側からはJSONデータでコマンドの成否や結果情報がレスポンスとして返される。

下記のページが参考になった。

https://www.reddit.com/r/MCPE/comments/5ta719/mcpewin10_global_chat_using_websockets/

EventSubscribe.js · GitHub

MCPE/Win10 WebSocket JSON Messages · GitHub

これで、

  1. イベント発生時のサーバへの送信設定(subscribe)
  2. コマンドの実行(commandRequest)
  3. レスポンスの取得

がJSON形式でやり取りできることが分かった。

受け取れるイベントの種類は上記の参考ページに記載されている

「MCPE & W10 Event Names」で確認できる。

コマンドは、マイクラで実行できるコマンドをそのまま書けばいいみたい。

手順としては、WebSocketサーバを立てておき、

マインクラフト側で「/connect」コマンドで対象サーバに接続する。

接続後、受け取りたいイベントをsubscribeしておき、

イベント処理としてイベント発生時に実行したいコマンドを定義しておく。

試しに、Node.jsで

「build」と発言したら自分のいる位置にブロックを配置する

WebSocketサーバを書いてみた。

const WebSocket = require('ws');
const app = require('express')();
const server = require('http').Server(app);
const uuid = require('uuid/v4');

// setblockコマンドリクエスト用JSON文字列を生成する関数
function setBlockCommand(x, y, z, blockType) {
    return JSON.stringify({
        "body": {
            "origin": {
                "type": "player"
            },
            "commandLine": util.format("setblock %s %s %s %s", x, y, z, blockType),
            "version": 1
        },
        "header": {
            "requestId": uuid(),
            "messagePurpose": "commandRequest",
            "version": 1,
            "messageType": "commandRequest"
        }
    });
}

// ユーザー発言時のイベント登録用JSON文字列を生成する関数
function subscribePlayerChatEventCommand() {
    return JSON.stringify({
        "body": {
            "eventName": "PlayerMessage"
            //"eventName": "PlayerChat"
        },
        "header": {
            "requestId": uuid(), // UUID
            "messagePurpose": "subscribe",
            "version": 1,
            "messageType": "commandRequest"
        }
    });
}

const wss = new WebSocket.Server({server});

// マイクラ側からの接続時に呼び出される関数
wss.on('connection', socket => {
    console.log('user connected');

    // ユーザー発言時のイベントをsubscribe
    socket.send(subscribePlayerChatEventCommand());

    // 各種イベント発生時に呼ばれる関数
    socket.on('message', packet => {
        console.log('Message Event');
        console.log(packet);
        const res = JSON.parse(packet);

        // ユーザーが「build」と発言した場合だけ実行
        if (res.header.messagePurpose === 'event' && res.body.properties.Sender !== '外部') {
            if (res.body.eventName === 'PlayerMessage' && res.body.properties.Message.startsWith('build')) {
                console.log('start build');

                // 石ブロックを配置するリクエストを送信
                socket.send(setBlockCommand('~0', '~0', '~0', 'stonebrick'));
            }
        }
    });

});

server.listen(3000, () => {
    console.log('listening on *:3000');
});

マイクラ側で

/connect <WebSocketサーバが動いているPCのIPアドレス>:3000

と実行するとサーバ側で「user connected」と表示されるはず。

その後、マイクラ側で「build」と発言したらその位置にブロックが配置されるはず。

ここまでできたら、

様々なイベントをsubscribeして移動した後ろにブロックを配置していったり、

ブロック生成処理をループ処理して建物を作る、みたいなこともできる。

おわり

これまでWebSocketを使う機会がなかったので、良い勉強になった。

次は元々の目的だった、建物自動生成に挑戦したい。

Minecraft (PC/Mac 版)

Minecraft (PC/Mac 版)

Minecraft (マインクラフト) - Switch

Minecraft (マインクラフト) - Switch