書き始め:2023-11-13 19:11:47
Minecraftでリアルタイムを取得した話
目次
前口上を多少入れております。本編が気になる方は時間を取得するまで飛ばしてお読みください。
事前情報
先にどんなものを作ったのか見たい人はこちら。
https://github.com/Lit-to/realtime (現実時間を取得するデータパック1)
この記事は2023年アドベントカレンダー・MinecraftCommandに向けて寄稿するものです。対戦よろしくお願いします。本記事は
- データパックの存在を知っている
- コマンドブロックを触ったことがある
- データパックを作ったことがある
くらいの前提知識を必要とする可能性があります。わからなくても結構細かめに説明しているのでどうぞ気軽にお読みください。
本編
アドベントカレンダーって何
アドベントカレンダーというのは、12/1からクリスマスの12/25まで毎日一日ずつめくっていってカウントダウンするカレンダーのことです。それになぞらえてQiitaアドベントカレンダーは毎日コミュニティのみんなで1記事ずつ投稿していって、カウントダウンしつつ技術を共有しよう!というイベントです(もう19日目でわざわざ書くものでもありませんが・・)。
→http://ja.wikipedia.org/w/index.php?curid=79238
タイマーは作れても出来ないこと
アドベントカレンダーは日めくりでカウントダウンしていくもの。ということで今回はマイクラで現在の時間をもとにカウントダウンタイマーを実装することを考えてみます。
マイクラにはスコアボードというシステムがあるので、カウントダウンタイマーみたいなのは結構簡単に作れそう。
雑にgoogleやyoutubeで検索しただけでもカウントダウンタイマーの作り方サイトはいっぱい出て来ますね。
- https://codewars.jp/howtomake-timer-minecraft/
- https://qiita.com/KizahashiLuca/items/9694f4f1432fd8849380
ざっくり要約すればMinecraft内の1秒が20tickで進むので、スコアボードのどこかに時間を入力し20tickに一回1減らしていけば、あとはスコアボードを何かしらの手段で表示するだけでタイマーが作れるというもの。この方法なら特定の瞬間に開始して、カウントダウンし続けることは難しくなさそうではあるけれど、一度サーバーを再起動してしまうと今が何時何分かを知る方法がないといちいち入力しなければならない・・どうにかして現在時刻を取得出来るようにしたいですね。ただマイクラ内を見渡してもシステム時間(サーバーの時計)が取得可能な場所はぱっと見思いつかない・・。実際数値を直接取得する方法は筆者が探す限り見つかりませんでした。
とてもヘンな海外勢の動画
現在時刻を取得する方法がないかと探し回っていたらこちらを作っている人がいました。
とても面白い(奇抜な)方法で実装していたので、今回はこれを参考に同じようなものを作ってみたいと思います。
時間を取得する
コマンドブロックのエラーを逆手に取る
コマンドブロックでスペルミスをやらかしちゃったことはありませんか。例えばaaaaというコマンドをコマンドブロックで実行してみます。当然/aaaaなんてコマンドはありません。
すると前回の実行結果欄に"[15:43:00] aaaa← [問題箇所]"と書かれていますね。本来コマンド成功時は実行結果が返されるところに失敗時は最終実行時間と失敗理由が書かれるので、コマンドに失敗すれば実行時間を教えてくれます。この欄はNBTデータとしてコマンドブロックに保存されるので/data get block <座標> LastOutput
と実行すれば外から中身を確認することが出来るだけでなく、storageにしまったりアマスタにコピーしたり出来ます。
文字列の扱い
しかし、取得できたとてこの方法で取得出来るものは文字列です。文字列は数値として扱えないため、足し算引き算のみならず大小比較などの計算することができません。文字列を使って出来ることはせいぜい「文字列Aと文字列Bが全く同じものかどうかを確かめる」、「文字列を分割2」、「文字数を数える」くらい。そのうえ、ここで手に入る文字列はrawJSONと呼ばれる入れ子構造の文字列JSON形式なため少々扱いづらい・・・・3。ただ表示するだけならこれでもいいけど、スコアボードに表示出来ることなどを考えると数値であってほしいですよね。
ゴリ押し数値変換
文字列から計算しやすい数値に変換します。コマンドブロックから文字列を取り出すと"[00:00:00] aaaa← [問題箇所]"となり前述のとおり「同じ文字列かどうか」は調べることが可能なので、取り出した文字列が"[00:00:00]..."だったら0
時0
分0
秒という数値を出力する関数を作成します。印刷された紙の書類を渡されてExcelにデータ入力するようなものですね。
同じ変換をする関数を全てのパターンで行う必要があるため、"[00:00:00]..."から"[24:59:59]..."までの86400パターン想定する必要があります。脳筋すぎる。
コマンドの上限の問題
さて、ここで86400パターンを想定するとある問題にぶつかります。86000もの数のコマンドブロックを並べるわけにはいかないので、データパックの関数機能にコマンドを並べるとしましょう。一番少なくコマンドを書いたとしても86400回のコマンドが実行されることになります4。デフォルトのサーバー設定であれば65565回を超えてコマンドを繋げると途中で止まってしまいます。/gamerule maxCommandChainLength
を大きくすれば増やせなくもないけれど・・処理の数が多すぎるというのも考えもの。
LootTableの機能
これを回避するためにLootTableのCondition機能を使います。LootTableは簡単に言うと事前に特殊な装飾したアイテムをJSONファイルに記述することでコマンド1つで生み出せるようにするデータパックの機能のこと。データパックでエンチャント付きのピッケルを渡したり、名前を付けた紙を準備したいときに便利に使うことが出来ます。Condition機能とは、そのアイテムをエンティティに渡すときにどんなエンティティに渡すのかに関する条件を決めることが出来る機能ですルートテーブル。この機能を使ってゴリ押し数値変換をしていきます。具体的には次の手順を踏みます。
- LootTableに「アイテムのタグのあるパスの内容が[00:00:00]ならアイテムタグに
hour:0,minute:0,second:0
を追加する」という内容を記述する(86400パターンぶんを1つのファイルに記述する)。 - 常に読み込まれているチャンクに透明なアマスタを召喚する。(
/summon armor_stand ~ ~ ~ {Tags:["r_time"]}
) - アマスタにマグマクリーム(アイテムならなんでもいい)を渡す(
/item replace entity @e[type=armor_stand,tag=r_time] weapon.mainhand with magma_cream
)。 - アイテムのtag内にコマンドブロックのエラー文をコピーする(
/data modify entity @e[type=armor_stand,tag=r_time,limit=1] HandItems[0].tag.Time set from block 0 -64 0 LastOutput
)。 - Loottableを用いてアイテムタグに変更を適用する(
/loot replace entity @e[type=armor_stand,tag=r_time,limit=1] weapon.mainhand loot xx:xx
)。 - アマスタからデータを取り出す(
/data get entity @e[type=armor_stand,tag=r_time,limit=1] HandItems[0].tag.Now[0]
)
これでアイテムタグから時間、分、秒の情報をhour、minute、secondから数値として取り出すことが出来るようになりました。86400パターンなんて想定するLootTableの条件定義ファイルは脳筋すぎてとても手では作れない。ということで数値以外は同じものを書く単純作業を手では作らずPythonスクリプトに作成させました。単純作業はPythonにやらせると手書きより速い。
さいごに
がんばれば文字列を数値に変換させることは出来ます!!元ネタの海外コマンド勢よくこれ思いついたな、というのが正直な感想です。ではまた。
執筆終了日:2023-11-30 18:05:13
参考資料
- https://github.com/Lit-to/realtime (現実時間を取得するデータパック1)
- http://ja.wikipedia.org/w/index.php?curid=79238 (アドベントカレンダーwiki)
- https://codewars.jp/howtomake-timer-minecraft/ (スコアボードからのタイマーの作り方)
- https://qiita.com/KizahashiLuca/items/9694f4f1432fd8849380 (スコアボードからのタイマーの作り方)
- https://youtu.be/rTCVKCxp84A?si=jUSnPBQIaKlmINYi (今回のパロディ元)
- https://ai-akaishi.hatenablog.jp/entry/ar857358 (赤石愛氏のrawJSON解説ページ)
- https://x.com/Cres333/status/1256008459851403264 (ケイヅキ氏の関数実行数に関するツイート)
- https://minecraft.fandom.com/ja/wiki/ルートテーブル (LootTableのwiki)
-
制作時点ではこの操作できることを知らず、執筆時点でのリサーチで気づいた。したがって本記事では文字列の分割をしない縛りがある ↩
-
厳密なJSON形式の書き方:https://ai-akaishi.hatenablog.jp/entry/ar857358 ↩
-
コマンド数の数え方は諸説ありそうなので割愛(https://x.com/Cres333/status/1256008459851403264) ↩