ほんじゃら堂

めんどくさい仕事をラクにする作業自動化レシピ集

マインクラフト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

Node.jsでSQLServerにWindows認証接続する

苦労したのでメモ。

Node.jsでのデータベース操作にはいつもknexを使用していて、

Knex.js - A SQL Query Builder for Javascript

knexはSQLServerにも対応しているのだけど、

そこで使用されているmssqlパッケージがWindows認証には対応していないのか、

うまく接続できなかった。

www.npmjs.com

このmssqlパッケージで使用されるドライバには

デフォルトで使用される「Tedious」と

Node.jsの0.12以上で使用可能な「msnodesqlv8」の

2種類があるようで、「Tedious」だとうまく接続できないっぽい。

knexでどうドライバを切り替えるのかが分からなかったのだけど、

単体で「msnodesqlv8」を使用するとうまく接続できた。

www.npmjs.com

続きを読む

作成した Node.js スクリプトをサーバーレスな AWS Lambda で手軽に定期実行する

以前作成した映画情報取得用スクリプトのように

blog.honjala.net

定期的に実行したいスクリプトを作成した場合、

そのスクリプトをどこで動かすか、というのが悩みどころになる。

いくつか方法はあるのだけど、

ローカルのPCでタスクスケジューラなりcronなりで動かす方法は

無料だし手軽にできるけど、実行したい時間にPCを起動しておく必要がある。

さくらインターネット等のVPSを借りてcronで動かす方法は

費用はそんなに高くないけど、ファイアウォールの設定等

安心して使える環境を作るまでのハードルが結構高い。

一昔前はそこで頑張ってVPSを立てるか、諦めるかしかなかったのだけど、

今はそんな時のためのサーバーレス(=サーバ管理のいらない)なサービスが使える。

今回は AWS の AWS Lambda を使って、

作成したスクリプトを定期実行する環境を作ってみたい。

続きを読む

Node.jsでテレビの映画放映情報をWebスクレイピングしてSlackに通知する

最近素晴らしいことに家族内でSlackを使ってやりとりするようになった。

せっかくなので何かボット的なものを作ろう、ということで

昔Clojureで作った、

「映画情報をスクレイピングして通知する」スクリプトのNode.js版を作って、

定期的にSlackに通知するようにしてみたい。

blog.honjala.net

続きを読む

Windowsでタスクスケジューラを使用せずにnode.jsスクリプトを定期実行する

会社の自分のPC(Windows)で何かを定期的に実行したいときは

だいたいPythonなりnode.jsなりでスクリプトを書いて

タスクスケジューラに登録しておく。

のだけど、

タスクスケジューラは未ログイン時に実行しようとすると

ログインアカウントを登録しておく必要があったり、

パスワードを変更した時に更新を忘れて実行に失敗しまくってたり、

そのログイン失敗のせいで社内ネットワークから閉め出されちゃったり、

色々と困ることがある。

Linuxのcrondと同じような仕組みができないか、と探してみたら

「pm2」と「node-cron」の組み合わせで実現できそうだったので、試してみた。

続きを読む

Vue.js + SVG でブロック崩しゲーム(自動版)を作る

数当てゲーム、マインスイーパーに続き、Vue.jsでこども向けゲームを作る。

もう少し動きのあるゲームが作ってみたいので、

今回はボールが跳ね回るブロック崩しゲームに挑戦する。

これまでに作ったゲームは初期処理で何か答えを生成しておいて、

あとはユーザーのイベントに応じて処理を実行するだけだったのだけど、

ブロック崩しの場合はゲームがスタートしたら終始ボールが

動いている必要があるので、この仕組みから考えてみよう。

続きを読む