前言
在11400加入的PDC,它的数据序列化涉及了MojangsonParser这种“难以对付的家伙”,所以值得我们新开一章单独讲解
CraftNBTTagConfigSerializer
此类为CraftBukkit提供的PDC NBT对象序列化/反序列化类,它主要含有两个方法
java
public static Object serialize(NBTBase base)
负责将NBT数据(NBTBase在11200为所有NBT类的基类,在11605它变为了一个接口,但我们此时只讨论11400和11202)序列化为Object
java
public static NBTBase deserialize(Object object)
负责将Object反序列化为NBT对象
Serialize方法
java
public static Object serialize(NBTBase base) {
if (base instanceof NBTTagCompound) {
Map<String, Object> innerMap = new HashMap<>();
for (String key : ((NBTTagCompound) base).c()) {
innerMap.put(key, serialize(((NBTTagCompound) base).get(key)));
}
return innerMap;
} else if (base instanceof NBTTagList) {
List<Object> baseList = new ArrayList<>();
for (int i = 0; i < ((NBTTagList) base).size(); i++) {
baseList.add(serialize((NBTBase) ((NBTTagList) base).get(i)));
}
return baseList;
} else if (base instanceof NBTTagString) {
return ((NBTTagString) base).c_();
} else if (base instanceof NBTTagInt) { // No need to check for doubles, those are covered by the double itself
return base.toString() + "i";
}
return base.toString();
}
此方法为适配11200以后的形式,如果直接使用11400的方法会出现无法找到NBT取对应数据方法的错误,由第一张的分析改过来即可
逻辑较为简单
-
当base为NBTTagCompound时,则将其键重新映射到递归序列化后的值后返回重新映射的Map
-
当base为NBTTagList时,则将其值全部递归序列化后返回
-
当base为NBTTagString时,直接返回其值
-
当base为NBTTagInt时,返回 值 + i (注:这个i非常重要)
-
否则调用base的toString() 方法,NBT的各种Array类型均重写了此方法,所以在序列化NBTTagLongArray等Array类型时均会调用此方法(注:NBTTagCompound和NBTTagList也重写了此方法,但Bukkit并没有采用它们)将其序列化为String
Deserialize方法
从Brigadier迁移
Mojang在11300引入了全新的命令解析系统Brigadier Mojang/brigadier
Brigadier虽名为“命令解析系统”,但在NMS内被大量用于解析各种数据字符串以提供反序列化,而11400 NMS的PDC反序列化便用到了Brigadier以及适配了Brigadier的MojangsonParser,因为Brigadier在11202还并未被引入,所以笔者改造了deserialize方法,脱去了Brigadier的依赖,使用Mojangson自己的内置正则表达式解析字符串
此方法也许并不一定正确,请读者斟酌使用
java
private static final MojangsonParser MOJANGSON_PARSER = new MojangsonParser("");
public static NBTBase deserialize(Object object) {
if (object instanceof Map) {
NBTTagCompound compound = new NBTTagCompound();
for (Map.Entry<String, Object> entry : ((Map<String, Object>) object).entrySet()) {
compound.set(entry.getKey(), deserialize(entry.getValue()));
}
return compound;
} else if (object instanceof List) {
List<Object> list = (List<Object>) object;
if (list.isEmpty()) {
return new NBTTagList(); // Default
}
NBTTagList tagList = new NBTTagList();
for (Object tag : list) {
tagList.add(deserialize(tag));
}
return tagList;
} else if (object instanceof String) {
String string = (String) object;
if (ARRAY.matcher(string).matches()) {
try {
return new MojangsonParser(string).k();
} catch (MojangsonParseException e) {
throw new RuntimeException(e);
}
} else if (INTEGER.matcher(string).matches()) { //Read integers on our own
return new NBTTagInt(Integer.parseInt(string.substring(0, string.length() - 1)));
} else if (DOUBLE.matcher(string).matches()) {
return new NBTTagDouble(Double.parseDouble(string.substring(0, string.length() - 1)));
} else {
NBTBase nbtBase = MOJANGSON_PARSER.c(string);
if (nbtBase instanceof NBTTagInt) { // If this returns an integer, it did not use our method from above
return new NBTTagString(String.valueOf(((NBTTagInt) nbtBase).e())); // It then is a string that was falsely read as an int
} else if (nbtBase instanceof NBTTagDouble) {
return new NBTTagString(String.valueOf(((NBTTagDouble) nbtBase).asDouble())); // Doubles add "d" at the end
} else {
return nbtBase;
}
}
}
throw new RuntimeException("Could not deserialize NBTBase");
}
如果你直接CV这段方法,那么会出现错误,在我们解释为什么前,先简要介绍这段代码的逻辑
-
当object为Map时,则将其键重新映射到递归反序列化后的值后返回重新映射的NBTTagCompound
-
当object为List时,则将其值全部递归反序列化后返回
-
当object为String时,会使用正则表达式检查字符串
-
当字符串符合Array toString() 方法生成的字符串形式时,使用MojangsonParser 解析字符串后调用其内部方法k() 将其反序列化为NBT对象
-
当字符串符合NBTTagInt序列化时的 数字 + i的形式时,读取数字(除最后一个字符以前的部分)并反序列化为NBTTagInt
-
当字符串符合NBTTagDouble时的 数字 + d的形式时,取数字(除最后一个字符以前的部分)并反序列化为NBTTagDouble
- 为什么会这样?因为NBTTagDouble类型的数据被有被特判,所以会调用它的toString() 方法序列化为字符段,“d”是在此时被加上的
-
当字符串不符合以上正则表达式的检测结果时,会尝试调用MojangsonParser 的内部方法NBTBase c(String s)对字符串进行解析并序列化为NBT,这个方法在11200默认不是public的,需要我们将其设为public
-
但此时我们需要特判序列化结果,因为MojangsonParser 并不会检查序列化数据在序列化前的类型,如果一个字符串中只含有整形数字,那么他将会被反序列化为NBTTagInt,如果有小数点则为NBTTagDouble,那如果我们只是碰巧在一个NBTTagString中只存储了一个数字呢?很显然,它将会被错误的反序列化NBTTagInt或NBTTagDouble
-
为了避免这个由Mojang史山代码引发的问题,CraftBukkit会特判数字并在其后加入“i”或"d",然后采用自己的反序列化逻辑,如上
-
所以此时才会出现“If this returns an integer, it did not use our method from above”这样的注释
-
注意:上述代码使用到的方法有一些在11202中为private或protected,需要设为public
为什么要单独开一章
尽管本章的内容比较少,但是因为涉及到的MojangsonParser在以后我们还会遇到,故单开一篇分析
最后
至此,我们已经初步完成了11400 PDC的全线功能复活,在下一章我们将进一步复活11605的现代化PDC功能(List、Boolean类型,支持区块和世界持久化数据)
全部评论 (0)
暂无评论,快来抢沙发吧~