ECILA RIP

思い出したように何かを書き留める

Spigot 1.14でPersistentDataを使いこなせ!

f:id:ecilavip:20190513102728p:plain Spigot 1.14はすごいぞ

皆さんお久しぶりです。えしらです。

最近Minecraftに復帰しました。現在頑張ってマルチサーバーの開発をしています、的な記事を書こうと思ってもう2週間以上経ってしまいました。キリがよくなったら改めて告知させて下さい。

今日はそんなことはどうでもよくて、Spigot API 1.14が中々に神なので紹介させて下さい。

NBTデータ

さて、皆さん「NBTデータ」をSpigotプラグインから触ったことはありますか?

NBTデータは、Minecraftにおけるデータ構造の一種で、Named Binary Tag Dataの略です。古来よりSpigotにおいては ItemStackに自由にデータをもたせたい場合に利用する ことが多かったはずです。

1.12までは、Spigot APIのみではNBTの操作は行うことが出来ず、Spigot(CraftBukkit)のいわゆる「NMS」を利用する必要がありました。 説明は省きますが、ざっくり言うとNBTの操作は「Spigot APIの範疇外」だった訳です。

1.13になって、Spigot APIが正式にItemStackのNBT操作に対応しました。JavaDocを見てみると、ItemMetaクラスに getCustomTagContainer​() というメソッドが生えており、これにより返却される CustomItemTagContainer によりNBTの操作が可能になっています。

例を上げると

//1.13のSpigot
ItemStack item = event.getPlayer().getInventory().getItemInMainHand<200b>();
ItemMeta meta = item.getItemMeta();
meta.getCustomTagContainer().setCustomTag(new NamespacedKey(MainPlugin.getInstance(), "testNBT"), ItemTagType.STRING, "NBTの中身です");
item.setItemMeta(meta);

のような形でItemStackに自由にデータを持たせる、つまりNBTデータをセットすることが出来ます。*1

この時点でSpigotは神の進化を遂げていると言えます。今まではItemStackにNBTをセットするのも、ItemStackに書かれたNBTを読むのも非常に面倒くさいステップを踏む必要がありました。NBTTagCompoundを利用する必要はもうありません。

ところが、 1.14のSpigot APIからは、この方法は非推奨になりました。

非推奨になりました。

WHAT?便利なのに????困るぜSpigotさ~~~んとお思いでしょう。僕も思いました。JavaDocを見てみます。

f:id:ecilavip:20190513104326p:plain

f:id:ecilavip:20190513104328p:plain

大非推奨祭り!!!!

残念ですがCustomTagContainerのことは忘れましょう。いいヤツでした。

代打「PersistentDataContainer」

1.14から新しく PersistentDataContainer というものが追加されました。雑に日本語化すると「永続データ入れ」みたいなものでしょうか。これはインターフェイス PersistentDataHolder により利用可能です。JavaDocを見てみる と、上で忘却の彼方に追いやったCustomTagContainerと同じ構造をしていることが分かります。

また、ItemMetaインターフェイスもPersistentDataHolderを実装*2しています。つまり、CustomTagContainerの代わりにPersistentDataContainerを使うことで、NBTデータを操作できます。

//1.14のSpigot
ItemStack item = event.getPlayer().getInventory().getItemInMainHand<200b>();
ItemMeta meta = item.getItemMeta();
meta.getPersistentDataContainer().set(new NamespacedKey(MainPlugin.getInstance(), "testNBT"), ItemTagType.STRING, "NBTの中身です");
item.setItemMeta(meta);

これで元通りです。良かった良かった。つまり 1.14からのSpigot APIでItemStackにNBTをつけたい場合は、PersistentDataContainerを使おう! ということになりますね。

それだけじゃないぞ!PersistentDataContainer

Spigotプラグインの文脈における「NBTタグ」というのは、ItemStackに用いられることがほとんどでした。というのも、NBTタグ自体はMinecraft内に様々な形で利用されている(e.g. Entity, Block)のにも関わらず、ワールド保存時に確実に保存されるのがItemStackだけだったのが背景にあります。

例えば、チェストロックの仕組みを作るために、チェストにNBTデータとして「誰がロックしたのか」というデータを持たせたいから、といってNBTタグを付与しても、ワールドを保存し、サーバーを再起動するなりしてしまうと、そのNBTデータは消えてしまう、という性質があった訳です。

しかし、PersistentDataが実装されたことにより、このNBTが 確実に保存されるようになりました。つまり、PersistentDataHolder インターフェイスを実装しているクラスであれば、何でも自由にデータをプラグインから操作できるようになったわけです。やったー。

JavaDocを見てみると、PersistentDataHolderを実装しているサブクラス及びサブインターフェイスを一覧で知ることができます。

Entityはもちろん、ChestやBannerのようなブロック系のものまで、PersistentDataHolderを利用することができます。

Spigotプラグインにおいて、置いてあるブロックにデータを持たせることができるのは革命的 なはずです。皆さん是非使ってみて下さい。

*1:最近専らKotlinでプラグインを書いているので、完全にJavaの書き方を忘却しています。間違っていたらすいません。

*2:実装だっけ?