学后余生
最新的主题!owo
这里可以看到最新发布的主题呢QAQ
Java获取数组中的所有子集,并返回List
2022 / October 16
[Asp.net]注册控件CreateUserWizard移除外部表table
2022 / June 7
[Bukkit]利用反射序列化/反序列化ItemStack
2022 / June 1
ForgeGradle 配置离线的MDK构建环境
2020 / August 26
划水
2020 / August 26
这是测试文章
2020 / August 25
favorite_border
主题分类
所有文章的分类都在这啦
arrow_forward
日常 ( 2 个主题 )
arrow_forward
Bukkit 插件 ( 1 个主题 )
arrow_forward
Forge模组 ( 1 个主题 )
arrow_forward
杂物柜 ( 1 个主题 )
arrow_forward
日常分享 ( 1 个主题 )
标签云☁
会有些什么标签呢?
测试
七夕
难受
文档
hhhhh
看见
文章
实际上
构建
mdk
forge
环境
bukkit
反射
NMS
OBC
序列化
ItemStack
反序列化
控件
table
渲染
login
模版
list
集合
子集
int
array
支持一下,来发点电呗~
owo
爱发电
上一篇 :
ForgeGradle 配置离线的MDK构建环境
下一篇 :
[Asp.net]注册控件CreateUserWizard移除外部表table
[Bukkit]利用反射序列化/反序列化ItemStack
Bukkit 插件
3
face
starorbb
·
date_range
2022-06-01
·
people
浏览: 2869
loyalty
标签:
bukkit
反射
NMS
OBC
序列化
ItemStack
反序列化
*~~今天是儿童节,而我在写代码~~* ### 写这东西的原因 本来写一个简简单单的插件以为会很快,结果写到将ItemStack序列化并保存为文件这里的时候,我才发现原来Bukkit中的ItemStack这东西并没有实现Serializable这个接口。看一下源代码,是这么写的: ```java public class ItemStack implements Cloneable, ConfigurationSerializable ``` 完蛋,它实现的是ConfigurationSerializable接口,也就是说如果要序列化ItemStack的时候,只能是序列化成YAML的一组元素,这就变得阴间起来了,且不说它序列化之后原本物品中包含的NBT标签(包括非原版的NBT或者是自定义的标签)能不能被Bukkit正常识别,而且操作也很阴间。 ### 第一步 我现在想要把包含ItemStack的一个类叫UIPart的东西实例化(这里我需要用UIPart存储三个ItemStack,然后在别的地方渲染UI的时候用),首先得让这东西实现Java的序列化接口: ```java public class UIPart implements Serializable ``` 因为是内部类,所以完整代码如下: ```java public static class UIPart implements Serializable { private final String smelt; private final String fuel; private final String result; public UIPart(String smelt, String fuel, String result) { this.smelt = smelt; this.fuel = fuel; this.result = result; } public ItemStack getSmelt() { return NBTUtil.deserializeItemStack(smelt); } public ItemStack getFuel() { return NBTUtil.deserializeItemStack(fuel); } public ItemStack getResult() { return NBTUtil.deserializeItemStack(result); } } ``` 其中,NBTUtil.deserializeItemStack(String)方法会在后文中提到。 ### 第二步 如果要将ItemStack序列化,需要用到NMS包与OBC包,为什么呢?只因为Bukkit提供的API并没有给你操作它NBT标签数据的方式,因此只能使用原版类也就是NMS的方式来操作。而Bukkit与NMS交互的中间人就是OBC(是包名org.bukkit.craftbukkit的简称),所以要通过NMS来操作Bukkit的ItemStack类,需要用到OBC来将其转换成CraftItemStack进而转换为NMS的ItemStack类。1.17/1.18以后的版本NMS不再以上述方式获取,因为新版本用了mojang官方的映射表,因此1.18以后如需调用NMS则需要额外的步骤,反射:使用新的包名,如net.minecraft.item,普通方式:需要重映射原版端后才能调用,不然看到的类就是混淆后的东西比如a.a.B。 那么,我们现在需要通过反射的方式来获取NMS与OBC包,因为在不同版本中,这些包被单独存放在了不同的位置,如net.minecraft.server.v1_7_R4以及org.bukkit.craftbukkit.v1_7_R4,v1_7_R4是版本号,对应1.7.10版本。 获取NMS类: ```java private static Class> getNMSClass(String name) { String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; try { return Class.forName("net.minecraft.server." + version + "." + name); } catch (ClassNotFoundException ex) { ex.printStackTrace(); return null; } } ``` 获取OBC类: ```java private static Class> getOBClass(String name) { String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; try { return Class.forName("org.bukkit.craftbukkit." + version + "." + name); } catch (ClassNotFoundException ex) { ex.printStackTrace(); return null; } } ``` ### 第三步 在以上步骤完成以后,我们就能将ItemStack进行序列化以及反序列化了。 序列化ItemStack: ```java public static String serializeItemStack(ItemStack item) { if (item == null) { return null; } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); DataOutputStream dataOutput = new DataOutputStream(outputStream); try { Class> nbtTagCompoundClass = getNMSClass("NBTTagCompound"); Constructor> nbtTagCompoundConstructor = nbtTagCompoundClass.getConstructor(); Object nbtTagCompound = nbtTagCompoundConstructor.newInstance(); Object nmsItemStack = getOBClass("inventory.CraftItemStack").getMethod("asNMSCopy", ItemStack.class).invoke(null, item); getNMSClass("ItemStack").getMethod("save", nbtTagCompoundClass).invoke(nmsItemStack, nbtTagCompound); getNMSClass("NBTCompressedStreamTools").getMethod("a", nbtTagCompoundClass, DataOutput.class).invoke(null, nbtTagCompound, dataOutput); } catch (SecurityException | NoSuchMethodException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); } return new BigInteger(1, outputStream.toByteArray()).toString(32); } ``` 反序列化ItemStack: ```java public static ItemStack deserializeItemStack(String data){ if (data == null) { return null; } ByteArrayInputStream inputStream = new ByteArrayInputStream(new BigInteger(data, 32).toByteArray()); DataInputStream dataInputStream = new DataInputStream(inputStream); ItemStack itemStack = null; try { Class> nbtTagCompoundClass = getNMSClass("NBTTagCompound"); Class> nmsItemStackClass = getNMSClass("ItemStack"); Object nbtTagCompound = getNMSClass("NBTCompressedStreamTools").getMethod("a", DataInputStream.class).invoke(null, dataInputStream); Object craftItemStack = nmsItemStackClass.getMethod("createStack", nbtTagCompoundClass).invoke(null, nbtTagCompound); itemStack = (ItemStack) getOBClass("inventory.CraftItemStack").getMethod("asBukkitCopy", nmsItemStackClass).invoke(null, craftItemStack); } catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { e.printStackTrace(); } return itemStack; } ``` ### 第四步 将UIPart类序列化并保存为文件,以便下次加载。 UIPart类内的ItemStack会序列化成String存储,下次反序列化UIPart获取ItemStack时再反序列化字符串。 文件名称,约定俗成为ser后缀。 其中smelt、fuel、result均为ItemStack。 ```java UIPart part = new UIPart(NBTUtil.serializeItemStack(smelt), NBTUtil.serializeItemStack(fuel), NBTUtil.serializeItemStack(result)); FileOutputStream output = new FileOutputStream("pair.ser"); ObjectOutputStream objOutput = new ObjectOutputStream(output); objOutput.writeObject(part); objOutput.close(); output.close(); ``` 将UIPart反序列化: ```java FileInputStream input = new FileInputStream("pair.ser"); ObjectInputStream objInput = new ObjectInputStream(input); UIPart part = (UIPart) objInput.readObject(); objInput.close(); input.close(); ```
explore
">
add
关于
所有主题
登陆