在 Minecraft 1.14 的时候,Bukkit 终于添加了持久化数据存储相关的 API。
简单的看了一下,一共添加了 4 个接口,其中 PersistentDataHolder
接口标记了对应的实现可以存储数据。
实现该接口的主要有三类比较重要:
- 一类是所有的实体,也就是说我们可以在任何实体(比如玩家)身上存储永久的数据,比如玩家的属性、职业啥的;
- 一类是所有附带 TileEntity 的 BlockState,对应的接口命名为
TileState
,就是说可以往部分方块里存数据; - 一类是
ItemMeta
,也就是我们可以正大光明的往物品里存数据了。
简介
接口
PersistentDataHolder
:getPersistentDataContainer
用于获取持久化数据的存储容器,所有数据的读写都在这里接口
PersistentDataContainer
:
有 get
has
set
等一系列方法,类似于一个 Map,键为 NamespacedKey
,用于区分不用插件的不同数据。
- 接口
PersistentDataType<T, Z>
:
表示存储的数据类型,其中接口内部的字段定义了常用的一些数据类型,比如 Integer
, String
等等。
可以通过实现该接口实现自定义数据的序列化和反序列化。
接口的两个泛型参数中,
T
表示存储的原生类型,目测必须为那几个内置的字段的类型之一Z
表示你的自定义类型
简单样例
拿我上一篇文做例子
static class PlayerData {
int hp = 20;
double strength = 0;
boolean haveJob;
Job job;
}
static class Job {
String name;
int level;
int exp;
String prefix;
}
仍然是这两个类,这次我们不用保存在别的文件里了。
利用新的 Bukkit API,可以直接存到 Player 里去,因为 Player
继承了 PersistentDataHolder
。
首先要把我们的类转换成原生数据类型,按照上一篇,我们用 byte[]
存数据。
首先,实现一个 PersistentDataType<byte[], Job>
,按照上一篇教程,大概长这样:
(不重复展示怎么读写字符串的方法)
static class JobDataType implements PersistentDataType<byte[], Job> {
static final JobDataType INSTANCE = new JobDataType();
@Override
public Class<byte[]> getPrimitiveType() {
return byte[].class;
}
@Override
public Class<Job> getComplexType() {
return Job.class;
}
@Override
public byte[] toPrimitive(Job complex, PersistentDataAdapterContext context) {
ByteBuf buffer = Unpooled.buffer();
writeString(buffer, complex.name);
buffer.writeInt(complex.level);
buffer.writeInt(complex.exp);
writeString(buffer, complex.prefix);
return buffer.array();
}
@Override
public Job fromPrimitive(byte[] primitive, PersistentDataAdapterContext context) {
Job job = new Job();
ByteBuf buffer = Unpooled.wrappedBuffer(primitive);
job.name = readString(buffer);
job.level = buffer.readInt();
job.exp = buffer.readInt();
job.prefix = readString(buffer);
return job;
}
}
接着,把数据存到 Player 里:
Plugin plugin = /*...*/;
Player player = /*...*/;
PersistentDataContainer container = player.getPersistentDataContainer();
// 存
Job job = /*...*/;
container.set(new NamespacedKey(plugin, "playerJob"), JobDataType.INSTANCE, job);
// 取
Job get = container.get(new NamespacedKey(plugin, "playerJob"), JobDataType.INSTANCE);
十分的简单。
把 Player
换成 ItemMeta
,就是向物品中存储数据。
使用 Bukkit API 而不是操作 byte 数组
注意到 PersistentDataType
里有一个名为 CONTAINER
的字段,可以合理猜测内部原生类型支持 PersistentDataContainer
,因此不难写出这样的代码:
static class JobContainerType implements PersistentDataType<PersistentDataContainer, Job> {
static JobContainerType INSTANCE = new JobContainerType();
@Override
public Class<PersistentDataContainer> getPrimitiveType() {
return PersistentDataContainer.class;
}
@Override
public Class<Job> getComplexType() {
return Job.class;
}
@Override
public PersistentDataContainer toPrimitive(Job complex, PersistentDataAdapterContext context) {
PersistentDataContainer container = context.newPersistentDataContainer();
container.set(new NamespacedKey(plugin, "name"), PersistentDataType.STRING, complex.name);
container.set(new NamespacedKey(plugin, "level"), PersistentDataType.INTEGER, complex.level);
container.set(new NamespacedKey(plugin, "exp"), PersistentDataType.INTEGER, complex.exp);
container.set(new NamespacedKey(plugin, "prefix"), PersistentDataType.STRING, complex.prefix);
return container;
}
@Override
public Job fromPrimitive(PersistentDataContainer primitive, PersistentDataAdapterContext context) {
Job job = new Job();
job.name = primitive.get(new NamespacedKey(plugin, "name"), PersistentDataType.STRING);
job.level = primitive.get(new NamespacedKey(plugin, "level"), PersistentDataType.INTEGER);
job.exp = primitive.get(new NamespacedKey(plugin, "exp"), PersistentDataType.INTEGER);
job.prefix = primitive.get(new NamespacedKey(plugin, "primitive"), PersistentDataType.STRING);
return job;
}
}
看起来也还行。