ようこそぷっこ村へ、ししゃもです。
(・∀・)ノ
何とかマイクラリハビリ中なので、折角だから前から実験してみようと思ってたことをやってみようと思います。マイクラから現実世界に干渉できるよなーってずっと思ってたので、この機にチャレンジしておきましょう。
以前最後の秘境と称してJSON UIを攻略したんですけど、マイクラにはさらに最奥のダンジョンがあります。最後とは…?
/wsserver という最大の謎存在
その最奥ダンジョンが 「/wsserver」 というコマンド。
ウェブソケットを利用して外部のURLにアクセスできるコマンドなのですが、一切の情報がないと言っても過言ではないのだ、なレベルの謎コマンドなのです。公式も「やる気?ないよ?」みたいな雰囲気で、なぜか存在だけは許されてる、いつ消されてもおかしくない、文字通り謎の存在。
しかし、賢いししゃもは見逃しませんでした。ウェブソケット…、URL…、スクリプトAPI…、サーバー接続とラズベリーパイ…、コレ!マイクラから現実世界の操作できるやん!?とな。賢い、あまりにも賢い(棒)
実は前から思ってはいたんだけど、如何せんめんどくさくて完全に放置…、リハビリにはちょうどいいでしょう。
まずは準備
やろうとしてることがなかなか壮大なので、まずは準備から始めなければなりません。というか、果たして外部と双方向通信みたいなことができるのか?から検証していかないといけません。どうやら、API関連のイベントなら監視が可能っぽく、何かの動作をきっかけにウェブサーバー側のスクリプトも実行が可能…らしい。(情報少なすぎ)
と、いうわけで、
- スクリプトAPIでイベントをサブスクするアドオン
- イベントを監視するウェブサーバー
を用意して実験してみます。
サーバーはどうするか?
はい、もうNode.jsでええんちゃう?ワイあんまりこの辺の細かいことは分からん。ただ、npmでSocketBE(後述)も配布されてるし、ラズパイのCUIでやるのがメインになるだろうから、Node.jsでいいでしょ。他の選択肢わからん。
スクリプトAPI
では続いてアドオン。まずクッソ適当に、ボタンを押したときに発火するイベントと、/scriptevent 用にカスタムイベントを1つ置いておきます。
import { world, system } from "@minecraft/server";
const BRIDGE_PREFIX = "__WS_BRIDGE_DATA__";
world.afterEvents.buttonPush.subscribe((event) => {
const data = JSON.stringify({
type: "button_push",
blockType: event.block.typeId,
location: event.block.location,
});
event.source.runCommand(`say ${BRIDGE_PREFIX}${data}`);
});
system.afterEvents.scriptEventReceive.subscribe((event) => {
if (event.id !== "bridge:server") {
return;
}
world.sendMessage(
BRIDGE_PREFIX +
JSON.stringify({
type: "server_command_received",
message: event.message,
}),
);
});こんな感じで、ボタンを押したときに/say発動。/scripteventでbridge:serverが飛んで来たら受けるってだけです。後者はサーバーから指示を受け取るために用意してます。実験なのでホンマに雑でいいです。
ウェブソケットサーバー
続いてサーバー側、マイクラ内の色々を受け取って、サーバー内処理をします。こっちもテストなのでクッソ適当。
import { Server, ServerEvent } from "socket-be";
const BRIDGE_PREFIX = "__WS_BRIDGE_DATA__";
const server = new Server({ port: 8765 });
server.on(ServerEvent.Open, () => {
console.log("WebSocket server: localhost:8765");
console.log("connect command: /wsserver localhost:8765");
});
server.on(ServerEvent.PlayerMessage, async (event) => {
const prefixIndex = event.message.indexOf(BRIDGE_PREFIX);
const { message, world } = event;
console.log(message);
console.log(message.startsWith(BRIDGE_PREFIX));
console.log(prefixIndex);
if (prefixIndex === -1) {
return;
}
const data = event.message.slice(prefixIndex + BRIDGE_PREFIX.length);
console.log("Addon -> Server:", data);
if (message === "bridge-test") {
await world.runCommand("scriptevent bridge:server hello-from-node-server");
console.log("Server -> Addon: hello-from-node-server");
}
});
socket-beが最大の疑問だと思いますけど、今はいいです。
肝心の内容ですが、プレイヤーがなんか発言したら拾って/scripteventを返すだけです。つまり、さっきのスクリプトAPIと合わせ技で、
マイクラ内でボタンを押す
↓
/say ${BRIDGE_PREFIX}${data} が発動して発言
↓
サーバーのイベントで拾う
↓
文字列照合して /scriptevent bridge:server hello-from-node-server の実行要求を返す
↓
スクリプトAPIでworld.sendMessage()が実行されてメッセージが表示されるっていう流れです。そう!つまり双方向通信が確立したのだよ!えぐくね!?
socket-beとはなんぞ?
ここで疑問だった socket-be について解決しておきましょう。有志の方が作ってくださった、大変ありがたいSocketBEというライブラリです。
すごーくざっくり言うと、マイクラとサーバーの間に立って、翻訳と通信を担ってくれる、便利なライブラリです。
リファ読んだ感じだと、チャット系とスコアボード系のAPIがメインなので、単発の指示なら前者、ラジコンを動かしたいみたいな継続して指示が必要な場合は後者でポーリングになるのかなぁ?って感じの見立てです。
一応ローカルで確立するようになったので、次回はラズパイで実行してみようと思います。




コメント
BEってPythonでの制御も行けた気がする
直接的にという意味ならパイソン制御ができるのはEEですかね。
MakeCodeでJavaScriptとPythonが使るので、これのことじゃないかと。
あとブロックエディタ(?)でコマンド組んだりとかですね。
別にサーバー側の言語は何でもいいのですが、ScriptAPIの仕様がTS(JS)なので。
TypeScriptはいいゾ。