マイクラのstorageについて備忘録

storageってなんだ?

極論を言ってしまえば、自由に作れるNBTみたいなもの。

私のざっくり感覚でまとめると。

  • スコアボードを使わなくても数字や文字を保存できる
  • ストレージの中身はチェックに使ったり、テキストチャットに表示することができる
  • データの外に保存しておける(他のワールドデータに入れれば使い回せる)

どうやって使うんだ?

storageに値を入れる方法(コマンド)は基本的にはdataコマンドになるでしょう。

data merge storage test {name:"keiduki"}

上はこういう感じになってます。

data merge storage <ストレージの名前> <NBTっぽいもの>

そうすると、testっていうストレージのnameに"keiduki"が保存されます。

使うときは例えばこういう感じ。

execute if data storage test {name:"keiduki"} run say "名前はケイヅキだった"

これが基本で、ここからさらに深めていきますよ。

データを保存してみる

保存する内容は自由に決められる

上で書いたように、適当なNBTっぽいものであれば何でも保存できます。

data merge storage test {aaa:0,bbb:"あいう",ccc:true,ddd:[1,2,3],eee:{fff:"ggg"}}

ちょっと見にくいので、見やすく整理すると、こう書いてます。

{
    aaa: 0,
    bbb: "あいう",
    ccc: true,
    ddd: [
        1,
        2,
        3
    ],
    eee: {
        fff: "ggg"
    }
}

こんなNBTはもちろん存在しません。だけれども、保存できちゃいます。

他のエンティティのNBTをそのままコピーする

dataコマンドということは、他のNBTを持ってこれます。

data modify storage test player set from entity @p

これは近くのプレイヤーの全NBTをtestの中のplayerに保存してくれます。

# gamerule sendCommandFeedback true にしておいてくださいね
data get storage test player

これで、中身を見ることができます。


全部はいらないって言う場合は一部だけ保存することもできます。

体力だけ保存しておきたい場合は、こんな感じ。

data modify storage test HP set from entity @p Health

もちろん同じ感じでブロックのNBTや他のストレージの情報もコピーできちゃいます。

こうすることで何がいいかっていうと、「その時点の」NBTデータを保存できるってことですね。プレイヤーとかブロックは、時間がたつといろんな値が変わりますからね。

スコアボードの値を保存する

ちょっと長くなりますが、executeのstoreを使って結果をストレージで保存することができます。

execute store result storage test score int 1 run scoreboard players get @p SCORE

これも「その時点の」スコアボードの値を保存できますね。

保存したデータを使ってみる

保存したデータを見る

例えば、testの中身を全部みたいなあってときはこうします。

# gamerule sendCommandFeedback true にしておいてくださいね
data get storage test

一部だけ、testのplayerだけみたいなあってときはこうします。

data get storage test player

さらに、playerのHealthだけみたいなあってときはこうします。

data get storage test player.Health

保存したデータを使う

ストレージの中身をチェックする

まずは最初の例で見せたように、シンプルなチェック。

execute if data storage test {name:"keiduki"} run say test

ただし、チェックするのは「かなり厳密に一致している必要がある」ので、地面の上で立っている判定をしたい時には、こうなります。

# ストレージの中身
{
  player: {
    OnGround: 1b
  }
}

# 失敗例 (1bにしていない)
execute if data storage test {player:{OnGround:1}} run say test

# 成功例 (1bにしている)
execute if data storage test {player:{OnGround:1b}} run say test

テキストチャットに表示する

ストレージに保存した情報はテキストチャットとして表示することもできます。

具体的にはこんな感じ。

tellraw @p {"nbt":"name","storage":"test"}

そうすると「keiduki」って表示されちゃいます。

もちろんtellrawなのでこういうふうに書けば、

tellraw @p [{"text":"名前は"},{"nbt":"name","storage":"test"},{"text":"です"}]

「私の名前はkeidukiです」って表示されます。


注意: 非常にやっかいな話ですが、tellrawやtitleなど表示する系のコマンドは「通常のJSON」で書かないといけません。「通常のJSON」とはなにかっていうお話は以下を参考にしてください。

keiduki.hatenablog.com

どこに保存されているのか?

ストレージはセーブデータの中にファイル化して、保存されます。

どこに保存されているかというと、ここになります。

.minecraft/saves/<ワールド名>/data/command_storage_<ストレージのパック名>.dat

はい、突然出てきた<ストレージのパック名>。これは何かというと、複数のストレージをさらに名前を付けて保存できるのです。

本来はストレージに保存するときはこういうふうに書きます。

data merge storage <ストレージのパック名>:<ストレージの名前> <NBTっぽいもの>

今までは、ストレージのパック名を省略していました。省略されるとパック名は何になるのか?

そうです。ド定番の「minecraft」ですね。

他のワールドで使う

ストレージはセーブデータの中に「ファイル化」されて保存されていました。

つ・ま・り、他のワールドの同じフォルダの中に入れてしまえば、使い回すことができるんですね。

結局、使い道は?

さて、ここまでで一通りざっくり使い方は説明してみましたが、具体的にどう使えばいいのか考えつかない方のために、結構頻繁に使っている私なりの使い道をご説明します。

スコアボードの数を減らしたい

スコアボードのobjectって管理するために大量にあるワールドとかってあるじゃないですか。

f:id:Keiduki:20200425104534p:plain

デバッグのときに表示すると、えーっと…どれだっけ?ってなるわけです。

そこで出た私の結論。

  • 頻繁に変更するやつはスコアボード
  • 保持しておきたいだけのやつはストレージ

チェックする内容を分かりやすく置き換えたい

今まではスコアボードで何でも管理していたわけです。

# ストーリーのアイテム入手イベントが起こった
scoreboard players set item EVENT 1
# ストーリーのアイテム2の入手イベントが起こった
scoreboard players set item2 EVENT 1

こんなのが大量にあって、いろんなファイルに書かれていると、チェックする時に非常に読みにくい。

# ストーリーのアイテム入手イベント1~3が全部達成している
execute if score item EVENT matches 1 if score item2 EVENT matches 1 if score item3 EVENT matches 1 run say "達成"

そこでストレージです。

# ストーリーのアイテム入手イベントが起こった
data modify storage event item append value "金の斧"
# ストーリーのアイテム2の入手イベントが起こった
data modify storage event item append value "銀の斧"

そして、チェックするときはこう書けばよいのです。

# ストーリーのアイテム入手イベント1~3が全部達成している
execute if data storage event {item:["金の斧","銀の斧","鉄の斧"]} run say "達成"

かなりシンプルになりました。しかも、こういうふうにも書けます。

# ストーリーのアイテム入手イベント1~3のどれかを達成していない
execute unless data storage event {item:["金の斧","銀の斧","鉄の斧"]} run say "未達成"

# ストーリーのアイテム入手イベント1を達成している(銀の斧、鉄の斧もitemに入っていてもOK)
execute if data storage event {item:["金の斧"]} run say "達成"

わかりやすく、汎用性もあがりますね。

会話の内容を一箇所にまとめたい

例えば会話の内容って、今まではこんな感じで書いていたわけです。

tellraw @p [{"text":"[村人]: "},{"text":"こんにちは!"}]
tellraw @p [{"text":"[村人]: "},{"text":"いい天気ですね!"}]
tellraw @p [{"text":"[村人]: "},{"text":"いまお時間よろしいですか!"}]
tellraw @p [{"text":"[村人]: "},{"text":"そうですか..."}]

それを会話だけを最初に登録するmcfunctionファイルを作ってしまって。

data modify storage villager name set value "村人"
# talkをまるごと消して初期化
data remove storage villager talk
data modify storage villager talk append value "こんにちは!"
data modify storage villager talk append value "いい天気ですね!"
data modify storage villager talk append value "いまお時間よろしいですか!"
data modify storage villager talk append value "そうですか..."

こう呼び出せばいいのです。

tellraw @p [{"nbt":"name","storage":"villager"},{"nbt":"talk[0]","storage":"villager"}]
tellraw @p [{"nbt":"name","storage":"villager"},{"nbt":"talk[1]","storage":"villager"}]
tellraw @p [{"nbt":"name","storage":"villager"},{"nbt":"talk[2]","storage":"villager"}]
tellraw @p [{"nbt":"name","storage":"villager"},{"nbt":"talk[3]","storage":"villager"}]

確かに書く量は増えました。

ですが、喋る人が増えれば増えるほど、修正する数が多ければ多いほど、1つのファイルを見るだけで済むのです。


ちなみにこういうふうに登録すると、そのまま表示されちゃいます。

data modify storage villager talk append value {"text":"こんにちは!","color":"red"}

## {text:"こんにちは!",color:"red"} が表示される

それは困るので、こう書けば、ちゃんと赤い文字で「こんにちは!」と表示されます。

# ' 'でくくってあげる
data modify storage villager talk append value '{"text":"こんにちは!","color":"red"}'
# interpretをつける
tellraw @p [{"interpret":true,"nbt":"talk[0]","storage":"villager"}]

JSONとマイクラ式JSONについて

注意:
この記事はマイクラ初心者のためのJSON講座です。
真面目にJSONを学ぶならば、参考にしないように。
また、かなりざっくりした内容です。
しっかり勉強したい方はUターンをおすすめします。

JSONってなに?

JSONは人間が読みやすくて、機械にとっても読みやすい形式のこと。 ファイルの拡張子に.jsonと書けば、それはもうJSON(言い過ぎ)。

例えばどういうもの?

こういう書き方をするのがJSON

{
    "text": "てすと",
    "color": "red",
    "bold": true,
    "clickEvent": {
        "action": "run_command",
        "value": "/say test"
    }
}

なんか書き方がいっぱいあって難しいと思われるかも知れないけど、次のやつを覚えてればOK。

種類 説明
オブジェクト { "text": "てすと" } {}でくくって、:でつなげたやつ。
かならず"文字":値の形で書かれる。右側は自由。
リスト [ 0, 1, 2, 3 ] []でくくって、いっぱい並べるやつ。
中身は自由だけども、全部同じ種類じゃないとダメ。
文字 "てすと" ""でくくっただけ。その中は文字として扱う。
数字 100 数字です。マイナスの値も小数点もOKです。
YES か NO か true YESがtrue、NOがfalseって覚えてれば大丈夫。

たったこれだけしか無いので、ルールはシンプルだね。 さっきのJSONで説明するとこんな感じ。

// {} があるってことはオブジェクト。オブジェクトの中は「"文字":値」の組み合わせをいくつでも並べられる
{
    "text": "てすと", // "文字":"文字"
    "color": "red",
    "bold": true, // "文字": YES
    "clickEvent": { // "文字": {オブジェクト}
        "action": "run_command", // "文字":"文字"
        "value": "/say test"
    }
}

イクラJSONとは?

イクラにはNBTとかstorageとかでJSONっぽい形式の書き方ができる。 例えばこういうの。

/give @p stone{display:{Name:'{"text":"石だよ"}'}}

え!?見た目ちがうじゃん!って思うよね。 でも、こういうふうに書いても、コマンドは通るんだよね。

/give @p stone{"display":{"Name":'{"text":"石だよ"}'}}

ちょっとJSONに近づいたね。 でも''でくくっているのが意味わからないね。じゃあ、こうしよう。

/give @p stone{"display":{"Name":"{\"text\":\"石だよ\"}"}}

なんか変なのが増えたけど、JSONっぽくなったね。 しかも、コマンドが通るね。 なんでだ???

普通のJSONとマイクラJSONの違いは?

ここでちょっと、さっきのコマンドを整理してみよう。

{
    "display": {
        "Name": "{\"text\":\"石だよ\"}"
    }
}

おや?さっきよりもJSONっぽくなった。 コメントを入れてみよう。

// {} ってことはオブジェクトだ
{
    "display": { // "文字":{オブジェクト} だね
        "Name": "{\"text\":\"石だよ\"}" // "文字":"文字"
    }
}

イクラJSONJSONもちゃんと理解できるみたい。 さて、\"って何?って思い始めたことでしょう。

文字の中で"を使いたい

JSONで文字は""でくくると文字だって書きました。じゃあ、"は文字として使えないのは、不便です。ならば使えるようにしてしまおう。

そうです。\"の前に使ってあげることで、文字として認識させてあげてるわけですね。

イクラJSONの特徴

イクラJSONの文字

さて、マイクラJSONに話を戻しましょう。 さっきのJSONを1段階戻しますよ。

{
    "display": {
        "Name": '{"text":"石だよ"}'
    }
}

\""になりました! これで2つわかったことがあります。

  • イクラ式は''でくくっても文字として扱う
  • ''でくくった文字の中では、"が普通に使えるようになる

さらにもう1段階戻しましょう。

{
    display: {
        Name: `{"text":"石だよ"}`
    }
}

""でくくらなくても文字として認識されてます。 そうです。マイクラ式はどんどん省略しちゃおうぜって形なのです。

もちろん、普通のJSONではJSONとして扱っちゃくれません。

イクラJSONの数字の後の文字(b, s, L, f, d)

ここまでマイクラJSONの文字の話ばっかりしてましたけど、数字の後になんか文字がついていることってありますよね?例えばこういうの。

{
  playerGameType: 1,
  isGuiOpen: 0b,
  DeathTime: 0s,
  Health: 10.333332f,
  Pos: [-107.348d, 4.0d, 54.76943597999223d],
  UUIDMost: 486310096284565318L,
}

この数字は何かというと、数字の「種類」を表しているんですね。

種類 説明
b isGuiOpen: 0b -128から127までの整数
s DeathTime: 0s -32,768から32,767までの整数
何もなし playerGameType: 1 ものすごい範囲の整数
L UUIDMost: 486310096284565318L 何もなし のさらにものすごい範囲の整数
f Health: 10.333332f ものすごい範囲の小数点
d Pos: [-107.348d, 4.0d, 54.76943597999223d] f のさらにもっと広い範囲の小数点

ものすごい範囲とざっくりしてるのは、書いたけど誰も気にしないかってことで、簡略化した。

基本的にはゲーム側で勝手に推測して付けてくれるので、頭の片隅にでも入れておけば良いと思う。

ちなみにtruefalse1b0bで表されるよ。 上のisGuiOpenはfalse(GUIを開いていない)って意味。

ファイルとコマンド

イクラ.jsonというファイルを書くときがありますが、そのときは普通のJSONで書く必要があります。

ただし、コマンド内に出てくるJSON普通のJSONイクラJSONの両方が書けちゃうのです。

これが、ややこしくしてマイクラJSONが苦手な理由かもしれません。

(まあ、一番の理由はコマンドだと改行とかできないから読みにくいせいなのがあるんだけども…)

マイクラのpredicateについて備忘録

predicateってなんだ?

あちこちでルートテーブルみたいなものって書かれているけど、私にはそれでもピンッと来なかった。 私の中でのpredicateってこういうことだと思ってる。

  • 長ったらしい条件に名前を付けられる
  • JSONファイルだから、いろんなmcfunctionファイルから呼び出せる

どうやって使うんだ?

例えば、こういうexecuteを書いていたとしよう

execute as @a[team=red,gamemode=survival,level=1..10] run say test

上みたいなやつが下みたいにかける

execute as @a[predicate=x:yowai] run say test

こういうやつが

execute as @a[scores={A=2,B=3,C=10..100}] run say test

こうなる

execute as @a[predicate=x:super-mode] run say test

あんまり書く量が減ってないじゃないかって?

だけども、scores={A=2,B=3,C=10..100}super-modeっていう意味なんだって分かりやすくなったことが重要なんです。しばらく時間が空いてしまって、またコマンド組もうと思ったらなんて書いてるのかさっぱりなんてざらじゃない?私だけ?

どうやってpredicateを書くんだ?

まずは、predicateファイルを用意します。下に置いてね。 predicatesフォルダはfunctionsフォルダと同じ場所に置けばOK。

datapacks/<データパック名>/data/<パック名>/predicates/super-mode.json

そしたら、さっきのscores={A=2,B=3,C=10..100}をsuper-mode.jsonに書いてみよう。

{
  "condition": "entity_scores",
  "entity": "this",
  "scores": {
    "A": 2,
    "B": 3,
    "C": {
      "min": 10,
      "max": 100
    }
  }
}

めちゃくちゃ書く量が増えたじゃないか!複雑じゃないか!って思ったあなた。 これがpredicateなんです。ただ、あちこちにscores={A=2,B=3,C=10..100}って書くのは嫌じゃないですか。

ざっくりコメント入れると下みたいな感じになってます。

{
  "condition": "entity_scores", // entity_scoresって条件で呼び出すことを宣言してる
  "entity": "this", // @aから呼び出されたら@aが、@pから呼び出されたら@pが指定される
  "scores": { // まあ、スコア一覧だよね
    "A": 2,
    "B": 3,
    "C": {
      "min": 10,
      "max": 100
    }
  }
}

あとはexecuteで呼び出せばOK。 ちなみに<パック名>をxにすると、predicate=x:super-modeだし、sugoiって<パック名>にするとpredicate=sugoi:super-modeになります。

他のpredicateはどういうふうに書けばいいんだ?

Wikiを見て!って言うんだけど、残念ながらWikiの日本語化はまだされてないみたい。 だからGoogle翻訳にかけつつ、さぐりさぐりやってみて。 Predicate – Official Minecraft Wiki

もっと書き方のサンプルがほしい

以下、よく使いそうなサンプルです。

しゃがんでいるキャラを検知する

execute as @a[predicate=x:sneak] run say "しゃがんでいる"
{
  "condition": "entity_properties",
  "entity": "this",
  "predicate": {
    "flags": {
      "is_sneaking": true
    }
  }
}

魔法の杖をメインハンドで使ったキャラを検知する

# 魔法の杖を持っていて、使った(USE)形跡がある
execute as @a[predicate=x:magic,predicate=x:use] run say "魔法を使った!"
# 魔法を使ったので使ってないことにする
scoreboard players set @a[predicate=x:use] USE 0
{
  "condition": "minecraft:entity_properties",
  "entity": "this",
  "predicate": {
    "equipment": {
      "mainhand": {
        "item": "minecraft:carrot_on_a_stick",
        "nbt": "{display:{Name:'{\"text\":\"魔法の杖\"}'}}"
      }
    }
  }
}
{
  "condition": "minecraft:entity_scores",
  "entity": "this",
  "scores": {
    "USE": {
      "min": 1,
      "max": 1000
    }
  }
}

特定のアイテムを装備しているキャラを検知する

金のヘルメット、チェストプレート、ブーツを全部装備していたときだけ反応する。

execute if entity @a[predicate=x:equip] run say "装備中"
{
  "condition": "entity_properties",
  "entity": "this",
  "predicate": {
    "equipment": {
      "head": {"item":"minecraft:golden_helmet"},
      "chest": {"item":"minecraft:golden_chestplate"},
      "feet": {"item":"minecraft:golden_boots"}
    }
  }
}

1/2の確率で成功する

changeは0~1(0%~100%)で設定。だから、サンプルは0.5(50%)。

execute if predicate x:random run say "成功"
{
  "condition": "random_chance",
  "chance": 0.5
}

晴れているかチェックする

execute if predicate x:sunny run say "晴れている"
{
  "condition": "weather_check",
  "raining": false
}

それでもやっぱりJSON書くのが面倒だよ!

こういうサイトがありました。

misode.github.io