# MCEEキャンプ 開発者用ドキュメント >作成:2022/07/19 >作成者:めいぷる(oishi_kaede@ca-techkids.com) [TOC] :::info **略語の定義** - MCEE:Minecraft Education Edition - WS:WebSocket - GAS:Google Application Script ::: MCEEキャンプでは、エージェントと呼ばれるエンティティをプログラムによって操作し、進めていきます! メンターと生徒のワールドをつなぐために、WSサーバーを建ててMCEEのワールドと外部をつなげています。ホストPCがWindowsPCであればCode Connectionを用いて外部からワールドにアクセスすることができるのですが、MacOS版のCode Connectionがなかったことと、MCEEと外部の**双方向通信**を可能にするために、WSサーバを作成しました。 例えば、スプレッドシートとMCEEのワールドを接続し、生徒の進捗を管理したりすることができます。 なにか不明な点等あれば、めいぷるまで! ## WebSocketサーバー ### 接続手順 グループホストPCで以下の手順を踏む。 #### 1. MCEEを起動する :::warning 開かない場合、[トラブルシューティング](#MCEEが起動しない)を参照。 ::: #### 2. (初回接続のみ)設定>プロフィール>暗号化されたWebsocketの要求→OFF WSサーバーとの接続のため、初回のみWSの設定を変更。 #### 3. デスクトップの`CAMP.sh`を実行 or `npm start`をし、サーバーを起動する。 :::warning 起動しない場合、[トラブルシューティング](#CAMP.shでエラーが出る)を参照。 ::: #### 4. ワールドを開く 初回は、配布される`EECamp.mcworld`をインポートする。 #### 5. コマンドで`/connect <グループホストPCのIPアドレス>:9999`と入力 IPアドレスは、スプレッドシートで確認。 #### 6. 接続完了 ### 仕様 MCEEクライアント内で何か変更が起きた場合や、メンターがワールドを操作する際、MCEEコマンド(ブロック)を用いて、WSサーバーにJSONデータを送信する。 :::info 厳密には、情報をサーバーに送信したいときに情報を含んだJSONを`/tellraw`コマンドがMCサーバー内に送信し、それをWSサーバーが検知する。 ::: :::warning JSON以外の内容がチャットに送られた場合は、先頭に「?」が付いていれば、その内容をコマンドとしてWSサーバーが実行する。(WSサーバーにしか実行できないコマンドがあるため[[参考](https://minecraft.fandom.com/ja/wiki/%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89#%E9%9A%A0%E3%81%97%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89)]) 例:「?querytarget *ID*」→「/querytarget *ID*」として実行 ::: 送信されるデータの種類には様々なものがある。WSサーバーがこの後どのような処理をするか識別するために、すべてのデータには、共通のフォーマットのヘッダーが付与されている。 ```json! { "header": { "sender": "送信者", "type": "送信情報の種類", } } ``` `type`の値によって、WSサーバーで行う処理がswitchされる。 #### 初期化 スプレッドシートに設定した情報をもとに、MCEE内の変数(`scoreboard`)に生徒の名前等を設定する。ワールドに生徒を入室させた状態で行う必要がある。 #### 1. 初期化ボタン(←要検討) トリガーにより、`/tellraw`コマンドを実行する。 ```json { "header": { "sender": "oishic", "type": "init", }, "body": { "group": "A", } } ``` #### 2. チャット検知 トリガーに伴い実行された`/tellraw`コマンドをWSサーバーが検知する。チャットに送信されたグループレターをもとにGASAPIに**GET**リクエストを送る。 レスポンスの可視化のため、ヘッダーに`"Accept-Encoding": "identity"`を指定する。 :::warning GASAPIはセキュリティ保護の観点から、`script.google.com`ではなく、`script.googleusercontent.com`にリダイレクトしてからレスポンスを返す。そのため、GASAPIを叩く際はリダイレクトの遡求を許可しなければならない([参考](https://developers.google.com/apps-script/guides/content#redirects))。 <details open><summary>JavaScript(WSサーバー)</summary> ```javascript fetch(URL, { redirect: "follow" }) ``` </details> <details><summary>cURL</summary> ```shell curl -L URL ``` </details> ::: #### 3. データ送信 GETリクエストに対するレスポンスとして、 - 名前 - アカウント名 - Days - 原点座標 それぞれをリストとしてJSON形式で受け取り、WSサーバー側でオブジェクトのリストに整形する。 - 整形後のリスト ```json [ { "name": "オオイシカエデ", //String "account": "oishic", //String "days": "4days", //String "coord": "0,6,0", //String }, { //同上 }, ... ] ``` ####  4.初期化実行 WSサーバー内で整形されたデータを元に、WSサーバーがMCEEにコマンドリクエストを送る。 送信するコマンドは以下の通り。 - スコアボードのリセット(必要?) ``` /scoreboard objectives remove days /scoreboard objectives add days dummy ``` - IDとDaysの紐付け(必要?) ``` /scoreboard players set {ID} days {日数} ``` 既存のキャンプ(特にオンライン)では、生徒のDaysに応じてコースの内容が変わるため、開始前に参加生徒に応じたワールドの設定をする必要があった。 EEキャンプも同様に、生徒のDaysに応じてコースの内容が変わるが、開始前のセットアップの手間を省くため、スプシにまとめられているデータを元にコースを自動生成する。 そのために、MCEE内でプレーヤーとDaysの情報を紐づける必要がある。これを実現するために、`/scoreboard`コマンドを使用している。 - ワールドの生成 ``` ??? ``` 生徒のDaysに応じてコースを生成する。Daysの情報をスプシから取得し、WSサーバー上で`/structure`コマンドを生成する。 この処理はプレーヤー全員のコースに対して一括で行うが、MCEE内では階層的(ワールド→プレーヤー→コース)に処理を行うため、個別で処理を実行することもできる。 行う処理は以下の通り。 1. コースの生成 - 原点の取得 - `/structure`する座標の生成 - 生徒がプレイするステージの割り振りは今後決めていく。 - Daysに応じて? - レベル(学年)に応じて? - ひとつひとつのステージの区画の大きさは全て等しいため、ステージの原点からの相対座標も単純計算によって導出できる。 - `/clone`コマンドの生成 3. プレーヤーのセット 4. エージェントのスポーン #### エージェントスポーン 基本操作チュートリアルをクリアすると、エージェントをスポーンさせる。 エージェントを制御するコマンド`/agent`はWSサーバーからのリクエストのみ受け付けられる。 そのため、MCEEクライアント内でチュートリアルのクリアを検知すると同時に、WSサーバーにプレイヤー情報をチャットに送信する。 ```json { "header": { "type": "intro_agent" }, "body": { "player": "oishic" } } ``` ``` tellraw @a[tag=admin] { "rawtext": [ { "text": "{\"header\":{\"type\":\"intro_agent\"},\"body\":{\"player\":\""},{ "selector": "@p[tag=players]" },{ "text": "\"}}" } ] } ``` #### コースクリア時 #### 1. クリア検知 コース毎に設置されたコマンドブロックが、クリアを検知し`/tellraw`コマンドでクリア情報をチャットに送信する。 ```json { "header": { "sender": "oishic", "type": "clear", }, "body": { "player": "テストタロウ", "course": 1, } } ``` #### 2. チャット検知 クリアに伴い実行された`/tellraw`コマンドをWSサーバーが検知する。チャットに送信された内容をもとにGASAPIに**POST**リクエストを送る。 + `server.js` ```javascript=85 case "clear": commitClear(msgJson.body.player, msgJson.body.course); break; ``` ```javascript=124 function commitClear(player, course) { var SendDATA = { player: player, course: course, }; var postparam = { method: "POST", mode: "no-cors", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: JSON.stringify(SendDATA), }; fetch(GAS_API_URL, postparam) .then((response) => console.log("Success:", response)) .catch((error) => console.error("Error:", error)); } ``` #### 3. 進捗管理表の変更 GASスクリプトがPOSTリクエストを受け取り、スプレッドシートを変更する。 + `APIServer.gs` ```javascript= function doPost(e) { var rawData = JSON.parse(e.postData.getDataAsString()); const app = SpreadsheetApp.openById(SPREAD_SHEET_ID); //シートをシート名で取得 const sheet = app.getSheetByName(SHEET_NAME); const lastRow = sheet.getLastRow(); var _changeRaw = sheet.getRange(2, 2, lastRow).getValues().flat(); var changeRaw = _changeRaw.indexOf(rawData.player) + HEADER_RAW; var changeColumn = parseInt(rawData.course) + HEADER_COLUMN; sheet.getRange(3, 16).setValue(rawData.course); sheet.getRange(3, 17).setValue(rawData); sheet.getRange(changeRaw, changeColumn).setValue(true); } ``` ## ビヘイビアーパック MCEEでは、ワールド内で使用されるさまざまなリソースを管理するために、ビヘイビアーパックが用意されている。 ビヘイビアーパックはワールドデータの中に`develop_behaivior_pack`フォルダをルートディレクトリとして保存されており、最上位に`manifest.json`というマニフェストファイルが置かれている。 ルートディレクトリの中にリソースの種類に応じて、サブフォルダを作成。 このワールドでは、 + `~/structures/` + `~/scenes/` + `~/functions/` の二つを主に用いている。 <details><summary>マニフェストファイル</summary> ```json { } ``` </details> :::warning マニフェストファイルの中では、UUIDによってリソースを管理しています。 同じビヘイビアーパックの中でUUIDが衝突するとMCEEがクラッシュしたので注意。([参考](https://docs.microsoft.com/ja-jp/minecraft/creator/documents/behaviorpack)) ::: ### structures `structures`フォルダには、MCEEワールド内で繰り返し使用することのできる構造物が`.mcstructure`形式で保存されている。 ### scenes このワールドでは、NPCによってチュートリアル等が進められている。NPCが発する台詞等の管理を`scenes`フォルダ内の`scene.json`で管理している。 ## トラブルシューティング ### MCEEが起動しない ### ○CAMP.commandでエラーが出る WebSocketサーバーが起動しない場合、以下の理由が考えられます。 「ターミナル」で`node -v`を実行し、Node.js環境が構築されているかを確認してください。 #### Node.js環境が構築されていない `SETUP.command`をダブルクリックして実行してください。 Node.js環境が構築されます。 #### 起動に必要なライブラリがインストールされていない `LIBRARY.command`をダブルクリックして実行してください。 サーバーに必要なNode.jsライブラリがインストールされます。 # 開発メモ + `/minecraftpe/games/com.mojang/minecraftpe/options.txt`にオートジャンプの設定がある→変更可能 + `sed -i -e "s/ctrl_autojump_mouse:1/ctrl_autojump_mouse:0/g" ./options.txt` + 2~4daysに応じて知識の数を変え、`/structure`によりコース全体を生成する + 縦に積み上げるとtickingarea問題が楽になる + ![](https://i.imgur.com/qsL9w1o.png) + ~~agentのスポーンにはWebsocketサーバーが必要~~ + node-packageで環境構築 + チュートリアルエディタ + ghostスニペット + コードブロック内のプログラムを構成するブロックをツールボックスに追加する + templateスニペット + コードブロック内のプログラムをエディタ内に表示する + blocksスニペット + コードブロック内のプログラムをヒントとして表示する + MCEE内のコードビルダーの制御 + `/codebuilder`(WSサーバー限定)でチュートリアルをセットする + ```codebuilder navigate @s false https://minecraft.makecode.com/#tutorial:65527-90621-86679-67168``` + `/code`でエディタを開く + `/codebuilder reset @s`でリセット出来なさそう、、? 1. `/codebuilder https://minecraft.makecode.com/#tutorial:xxx/yyy/zzz` 2. `/codebuilder https://minecraft.makecode.com/#editor` 3. `/codebuilder https://minecraft.makecode.com/` 4. 新しいプロジェクトを作らせる 5. `/codebuolder https://minecraft.makecode.com/#editor` ```md ### @hideIteration true # TechKidsCamp ## コマンドを実行する 「start」とチャットに入力してエージェントに命令してみよう! エージェントがスタートポータルを開いてくれるよ! ```template player.onChat("start", function () { agent.turn(LEFT_TURN) agent.turn(LEFT_TURN) for (let index = 0; index < 10; index++) { agent.move(FORWARD, 1) } for (let index = 0; index < 4; index++) { agent.move(UP, 1) agent.move(FORWARD, 1) } for (let index = 0; index < 3; index++) { agent.move(FORWARD, 1) } agent.destroy(DOWN) agent.move(DOWN, 1) agent.destroy(RIGHT) agent.destroy(LEFT) for (let index = 0; index < 2; index++) { agent.destroy(FORWARD) agent.move(FORWARD, 1) agent.destroy(RIGHT) agent.destroy(LEFT) } }) ```// ``` + https://education.minecraft.net/wp-content/uploads/Minecraft-Education-Edition-Multiplayer-Guide-1.pdf + リソースパックのentityフォルダにあるnpcのファイルのgeometryの部分をプレイヤーと同じにする(https://discord.com/channels/834393520898965504/899161647321600060/1023563988145029180) + チャット画面>設定>すべてのチャットをミュートで`tellraw`の出力を非表示にできる + `/structure load`では、ロードされているチャンクでないと即座にloadされないので、予め`/tickingarea`でロードさせておく + ```/tickingarea add 14 0 -196 78 150 -132 dungeon true``` + 参加コードは、絵柄を数字に直し、base64でエンコードすると、URLに含むことができる。 + `/wssocket`はコマンドブロックでは動作せず、`.mcfunction`ファイルで動作する。(非同期だから?) + `.mctemplate`で配布の手間が減る! + makecodeのひらがな化 + https://note.com/topnyan/n/n6f9b2ecfbd0d + http://ict.puziro.com/2020/05/22/local_makecode/ + `C:\Program Files (x86)\Microsoft Studios\Minecraft Education Edition\data\behavior_packs\agent_gametest` + `C:\Program Files (x86)\Microsoft Studios\Minecraft Education Edition\data\resource_packs\education\splashes.json`編集したらスプラッシュいじれた + ![](https://i.imgur.com/Qo5yR6U.jpg) + ![](https://i.imgur.com/jAIolHf.png) + https://notebooks.minecrafteduservices.com/prod/index.html?gameVersion=1.18.42&inGame=1&ipc=1&lang=ja-JP&protocolVersion=16908288 + ![](https://i.imgur.com/zZLmFbN.png) + ![](https://i.imgur.com/C4BlDMH.png) + ![](https://i.imgur.com/AKRuuny.png) + ターゲットセレクタに関して + x,y,zは指定しないとコマンドブロックの座標が継承される + ![](https://i.imgur.com/Y4V5F9S.png) + https://yogurr19-minecraft.hatenablog.com/entry/2020/11/15/081935#can_destroy + `git clone https://~~`でだれでもclone可能 + https://www.reddit.com/r/MCPE/comments/5qf4ah/websockets_huge_potential_currently_unusable/ # 設計メモ