结冰的离季
本帖最后由 结冰的离季 于 2021-8-19 00:51 编辑

bukkit在1.14.1开始增加了几个读取nbt数据相关的类
PersistentDataAdapterContext
PersistentDataContainer
PersistentDataHolder
PersistentDataType

可能很多人都不知道这个东西,站内也搜不到类似的介绍文章,我是别人告诉我才知道的。
使用场景很广泛,可以添加自己的数据到物品/实体里,我作为rpg数据存储用。
本篇文章 基于Spigot-API 1.17-R0.1-SNAPSHOT API 中文文档) 将简单介绍一下这几个类以及使用方法。

PersistentDataHolder
这个接口提供了一个方法  PersistentDataContainer getPersistentDataContainer()
  1. Returns a custom tag container capable of storing tags on the object. Note that the tags stored on this container are all stored under their own custom namespace therefore modifying default tags using this PersistentDataHolder is impossible.
  2. 返回一个能够在该对象上存储标签的自定义标签容器。请注意,这个容器上的标签都存储在自己的自定义命名空间下,因此使用这个PersistentDataHolder修改默认标签是不可能的。
复制代码

继承并实现他的类有很多,具体点击 PersistentDataHolder 查看所有子接口,比如实体、物品、方块等
大概就是获取这个对象对应的标签容器 返回类型PersistentDataContainer类似于一个Map,具体看下面

PersistentDataContainer
  1. //Returns the metadata value that is stored on the PersistentDataHolder instance.
  2. //返回指定键指定数据类型的值
  3. <T,Z> Z get(NamespacedKey key, PersistentDataType<T,Z> type)
  4.      
  5. //Returns the adapter context this tag container uses.
  6. //返回这个标签容器的适配器(类似模板一样的东西,比如实体与物品就是不一样的模板)
  7. PersistentDataAdapterContext    getAdapterContext()

  8. //Get a set of keys present on this PersistentDataContainer instance.
  9. //获取所有键
  10. java.util.Set<NamespacedKey>    getKeys()
  11.   
  12.   //Returns the metadata value that is stored on the PersistentDataHolder instance.
  13.   //在get()的基础上指定默认值
  14. <T,Z> Z getOrDefault(NamespacedKey key, PersistentDataType<T,Z> type, Z defaultValue)
  15.    
  16. //Returns if the persistent metadata provider has metadata registered matching the provided parameters.
  17. //检查是否含有指定键值和类型的键
  18. <T,Z> boolean   has(NamespacedKey key, PersistentDataType<T,Z> type)
  19.    
  20. //Returns if the container instance is empty, therefore has no entries inside it.
  21. //判空
  22. boolean isEmpty()
  23.    
  24. //Removes a custom key from the PersistentDataHolder instance.
  25. //删除一对键
  26. void    remove(NamespacedKey key)
  27.      
  28. //Stores a metadata value on the PersistentDataHolder instance.
  29. //设置指定键值
  30. <T,Z> void  set(NamespacedKey key, PersistentDataType<T,Z> type, Z value)
复制代码


很像一个简化版的Map,但是注意是没有getValues()方法的(数据类型太混杂),具体请看文档PersistentDataContainer

PersistentDataType<T,Z>
此类其实是枚举,默认提供了BYTE、BYTE_ARRAY、DOUBLE、FLOAT、INTEGER、INTEGER_ARRAY、LONG、LONG_ARRAY、SHORT、STRING、TAG_CONTAINER、TAG_CONTAINER_ARRAY 这些类型,你也可以自定义类型
具体说明在文档内,使用的时候直接静态调用 ,如整形的 PersistentDataType.INTEGER

PersistentDataAdapterContext
一个模板接口,主要用于创建空白容器(用于嵌套啥的)
只有一个方法
  1. //Creates a new and empty meta container instance.
  2. //返回一个空的容器
  3. PersistentDataContainer newPersistentDataContainer()
复制代码


实例

添加并读取物品自定义标签
  1. //注:getPlugin() 方法返回的是继承JavaPlugin的主类
  2. NamespacedKey MY_DATA = new NamespacedKey(getPlugin(), "my_data"); //注册键值,实际使用推荐用static存到类成员去
  3. ItemStack item = new ItemStack(Material.DIAMOND_SWORD); //实验物品

  4. //添加
  5. ItemMeta itemMeta = item.getItemMeta();  //获取数据的复制
  6. PersistentDataContainer dataContainer = itemMeta.getPersistentDataContainer(); //获取数据容器
  7. String data = "Hello World!";
  8. dataContainer.set(MY_DATA, PersistentDataType.STRING, data);//添加自定义标签
  9. item.setItemMeta(itemMeta); //别忘了只是复制而已,需要设置回去

  10. //读取
  11. ItemMeta itemMeta = item.getItemMeta(); //重新获取新的具有自定义标签的meta
  12. String readData = itemMeta.getPersistentDataContainer().get(MY_DATA, PersistentDataType.STRING); //读取数据 PersistentDataType是什么类型就返回什么类型的数据
复制代码


一个键值存入多对数据
  1. //注:getPlugin() 方法返回的是继承JavaPlugin的主类
  2. NamespacedKey MAIN_DATA = new NamespacedKey(getPlugin(), "main_data"); //注册主键值
  3. NamespacedKey MY_DATA1 = new NamespacedKey(getPlugin(), "my_data1"); //注册键值2
  4. NamespacedKey MY_DATA2 = new NamespacedKey(getPlugin(), "my_data2"); //注册键值3
  5. ItemStack item = new ItemStack(Material.DIAMOND_SWORD); //实验物品

  6. //添加
  7. ItemMeta itemMeta = item.getItemMeta();  //获取数据的复制
  8. PersistentDataContainer dataContainer = itemMeta.getPersistentDataContainer(); //获取数据容器
  9. PersistentDataContainer newDataContainer = dataContainer.getAdapterContext().newPersistentDataContainer(); //生成一个新的容器

  10. newDataContainer.set(MY_DATA1, PersistentDataType.STRING, "这是子键1"); //添加自定义子标签 String
  11. newDataContainer.set(MY_DATA2, PersistentDataType.INTEGER, 666);       //添加自定义子标签 Integer

  12. dataContainer.set(MAIN_DATA, PersistentDataType.TAG_CONTAINER, newDataContainer); //把子键容器添加为主键
  13. item.setItemMeta(itemMeta); //保存数据

  14. //读取
  15. //与上面类似
  16. //使用 get(MAIN_DATA, PersistentDataType.TAG_CONTAINER); 然后当做一个容器来获取数据即可
  17. //可以使用PersistentDataContainer 里的getKeys()方法返回键,然后遍历键get数据 ......
复制代码


暂时就这些,对于基本的使用也就这样了,此类可以一定程度上代替NBTAPI 这类库(很大,很不爽),唯一的缺点是不能修改原版的东西(文档里说不能)
还有有人说实体的修改之后重启服务端就没了(我没试验过),但是ItemStack我自己实验是不会丢失的。

如有错误和建议请不吝指出




Rothes
补充一下,在 1.13.2 中也有这个功能(新版服务端已弃用,但仍存在)
https://helpch.at/docs/1.13.2/or ... ackage-summary.html

xy2285111308
想让我回复不可能必须评分

baichajun
虽然看不懂,但觉得很厉害

白色的小熊
键值是干嘛的?