前言
在上一篇文章中,我们讨论并尝试了回迁PDC的上层借口,本篇文章将详细讨论CraftBlockEntityState ,CraftEntity ,CraftMetaItem 的中PDC的实现以及NMS改造
CraftBlockEntityState
查看11400的CraftBlockEntityState,
java
public class CraftBlockEntityState<T extends TileEntity> extends CraftBlockState implements org.bukkit.block.TileState
由于其实现了TileState ,所以它实现了getPersistentDataContainer() 方法
java
@Override
public CraftPersistentDataContainer getPersistentDataContainer() {
return this.getSnapshot().persistentDataContainer;
}
查看getSnapshot()方法
java
// gets the cloned TileEntity which is used to store the captured data
protected T getSnapshot() {
return snapshot;
}
由其注释我们发现此方法返回一个NMS TileEntity实例
PDC实例
深入NMS TileEntity类,我们发现了CraftBukkit改造增加的PDC实例
java
// CraftBukkit start - data containers
private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry();
public CraftPersistentDataContainer persistentDataContainer;
// CraftBukkit end
它在TileEntity的构造函数中初始化(注:原PDC在load(NBTTagCompound nbttagcompound)函数中初始化,在后期版本的Paper中将其优化为了构造函数中初始化,本文以Paper的声明方式为主)
java
public TileEntity() {
......
this.persistentDataContainer = new DelegateCraftPersistentDataContainer(DATA_TYPE_REGISTRY); // Paper - always init
......
}
PDC如何存取数据?
查询11400中PDC的实例,我们发现
-
在TileEntity数据通过save(NBTTagCompound nbttagcompound)保存为NBT时,CraftBukkit修改了保存方法,使PDC中的数据以PublicBukkitValues -> NBTTagCompound形式的PDC数据 (键值对)保存到了Tile Entity的NBTTagCompound中
-
在TileEntity的load(NBTTagCompound nbttagcompound) 函数加载NBT数据时,CraftBukkit修改了它,使其读取PublicBukkitValues 键的数据并存入PDC实例中
于是相似的,我们可以将上述操作在11202的NMS TileEntity中实现,其中
-
load(NBTTagCompound nbttagcompound)函数的反混淆名没有变化 -
save(NBTTagCompound nbttagcompound)函数的在此时还没有反混淆名,为c(NBTTagCompound nbttagcompound)
java
public void load(NBTTagCompound nbttagcompound) {
...
// CraftBukkit start - read container
this.persistentDataContainer.clear(); // Paper - clear instead of init
NBTTagCompound persistentDataTag = nbttagcompound.getCompound("PublicBukkitValues");
if (persistentDataTag != null) {
this.persistentDataContainer.putAll(persistentDataTag);
}
// CraftBukkit end
...
}
private NBTTagCompound c(NBTTagCompound nbttagcompound) {
...
// CraftBukkit start - store container
if (this.persistentDataContainer != null && !this.persistentDataContainer.isEmpty()) {
nbttagcompound.set("PublicBukkitValues", this.persistentDataContainer.toTagCompound());
}
// CraftBukkit end
return nbttagcompound;
}
...
}
至此对CraftBlockEntityState 的PDC改造已无大碍
CraftEntity
如法炮制上方的逻辑,我们发现了CraftEntity 中的PDC实例(没错它不在NMS类中,而是在CraftBukkit的实现类里),和以下三个方法
java
@Override
public CraftPersistentDataContainer getPersistentDataContainer() {
return persistentDataContainer;
}
public void storeBukkitValues(NBTTagCompound c) {
if (!this.persistentDataContainer.isEmpty()) {
c.set("BukkitValues", this.persistentDataContainer.toTagCompound());
}
}
public void readBukkitValues(NBTTagCompound c) {
NBTTagCompound base = c.getCompound("BukkitValues");
if (base != null) {
this.persistentDataContainer.putAll(base);
}
}
下面的两个方法即为存读PDC数据的方法,它们操作的对象应为为11200 NMS Entity类的save(NBTTagCompound nbttagcompound) 和f(NBTTagCompound nbttagcompound) (即load方法)
java
public NBTTagCompound save(NBTTagCompound nbttagcompound) {
...
// CraftBukkit start - stores eventually existing bukkit values
if (this.bukkitEntity != null) {
this.getBukkitEntity().storeBukkitValues(nbttagcompound);
}
// CraftBukkit end
...
}
public void f(NBTTagCompound nbttagcompound) {
...
this.getBukkitEntity().readBukkitValues(nbttagcompound);
...
}
CraftMetaItem
!!注:这部分CraftBukkit在11400和11202的实现有很大不同,笔者在此部分不在比较11400与11200的差异,而直接给出实现思路
PDC实例
声明位于CraftMetaItem中,与CraftEntity相同
初始化
- 在复制构造函数**CraftMetaItem(CraftMetaItem meta) **中
java
this.persistentDataContainer.putAll(meta.persistentDataContainer.getTagsCloned())
这是11605Paper中的优化方法,旨在实现PDC中所有数据的完全深拷贝
- 在构造函数CraftMetaItem(NBTTagCompound tag) 中
java
...
if (tag.hasKey(BUKKIT_CUSTOM_TAG.NBT)) {
NBTTagCompound compound = tag.getCompound(BUKKIT_CUSTOM_TAG.NBT);
Set<String> keys = compound.c();
for (String key : keys) {
persistentDataContainer.put(key, compound.get(key).clone());
}
}
...
其中
java
static final ItemMetaKey BUKKIT_CUSTOM_TAG = new ItemMetaKey("PublicBukkitValues");
在构造函数**CraftMetaItem(Map<String, Object> map) **中
java
Object nbtMap = SerializableMeta.getObject(Object.class, map, BUKKIT_CUSTOM_TAG.BUKKIT, true); // We read both legacy maps and potential modern snbt strings here
if (nbtMap != null) {
this.persistentDataContainer.putAll((NBTTagCompound) CraftNBTTagConfigSerializer.deserialize(nbtMap));
}
注:序列化器**CraftNBTTagSerializer**将在下一章讲到
存取
在保存NBT方法applyToItem(NBTTagCompound itemTag) 最后
java
if (!persistentDataContainer.isEmpty()) {
NBTTagCompound bukkitCustomCompound = new NBTTagCompound();
Map<String, NBTBase> rawPublicMap = persistentDataContainer.getRaw();
for (Map.Entry<String, NBTBase> nbtBaseEntry : rawPublicMap.entrySet()) {
bukkitCustomCompound.set(nbtBaseEntry.getKey(), nbtBaseEntry.getValue());
}
itemTag.set(BUKKIT_CUSTOM_TAG.NBT, bukkitCustomCompound);
}
在序列化方法**serialize(ImmutableMap.Builder<String, Object> builder) **最后
java
if (!persistentDataContainer.isEmpty()) { // Store custom tags, wrapped in their compound
builder.put(BUKKIT_CUSTOM_TAG.BUKKIT, persistentDataContainer.serialize());
}
#其他
我们还需要将对应的逻辑添加到isEmpty() 、equalsCommon(CraftMetaItem that) 和applyHash() 方法中,它们分别用于判断ItemMeta是否为空,和比较两个ItemMeta是否相等
别忘了!
还没完!在测试中,它并不会正确的处理PDC数据的序列化,事实上,笔者发现PDC中的数据除了正常存在于PublicBukkitValues 中,还被序列化进了internal 键里,此键专门用于存储内部字段的数据,比如玩家头颅数据等。重复序列化的数据在使得ItemMeta数据变得臃肿的同时,也将影响到两个ItemMeta是否相等的比较(因为一些ItemMeta是不通过物品创建的,它们序列化出的数据中不存在internal键)
最后,笔者发现,应将
java
static final ItemMetaKey BUKKIT_CUSTOM_TAG = new ItemMetaKey("PublicBukkitValues");
添加进getHandledTags() 方法,
java
HANDLED_TAGS.addAll(Arrays.asList(
...
BUKKIT_CUSTOM_TAG.NBT,
...
));
这样在序列化时,它便不会被当作内部数据处理
最后
在本篇文章中,我们探讨了PDC在CraftBlockEntityState ,CraftEntity ,CraftMetaItem 以及NMS中的实现,在下一章中我们将分析PDC数据的序列化/反序列化,深入MC数据字段序列化的核心MojangsonParser,使其数据能够正确的被保存和读取
此外,本篇并没有详细也不可能讲解所有的方法,但可保证囊括大多数的方法,其余请读者自行探究
全部评论 (0)
暂无评论,快来抢沙发吧~