From 6f196fe701a4f452a2f357760dbd3f380ace1222 Mon Sep 17 00:00:00 2001 From: Mariell Hoversholm Date: Thu, 30 Apr 2020 16:59:24 +0200 Subject: [PATCH] Add Raw Byte ItemStack Serialization Serializes using NBT which is safer for server data migrations than bukkits format. --- ...Add-Raw-Byte-ItemStack-Serialization.patch | 55 ++++++++++ ...Add-Raw-Byte-ItemStack-Serialization.patch | 102 ++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 Spigot-API-Patches/0208-Add-Raw-Byte-ItemStack-Serialization.patch create mode 100644 Spigot-Server-Patches/0529-Add-Raw-Byte-ItemStack-Serialization.patch diff --git a/Spigot-API-Patches/0208-Add-Raw-Byte-ItemStack-Serialization.patch b/Spigot-API-Patches/0208-Add-Raw-Byte-ItemStack-Serialization.patch new file mode 100644 index 000000000..e595bcf9d --- /dev/null +++ b/Spigot-API-Patches/0208-Add-Raw-Byte-ItemStack-Serialization.patch @@ -0,0 +1,55 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Mariell Hoversholm +Date: Thu, 30 Apr 2020 16:56:31 +0200 +Subject: [PATCH] Add Raw Byte ItemStack Serialization + +Serializes using NBT which is safer for server data migrations than bukkits format. + +diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java +index 821064264405cde147e944df279b44a20a4868ad..4fa00140cf17e3ea7d602b6337b3fe4f2901202e 100644 +--- a/src/main/java/org/bukkit/UnsafeValues.java ++++ b/src/main/java/org/bukkit/UnsafeValues.java +@@ -89,5 +89,9 @@ public interface UnsafeValues { + static boolean isLegacyPlugin(org.bukkit.plugin.Plugin plugin) { + return !Bukkit.getUnsafe().isSupportedApiVersion(plugin.getDescription().getAPIVersion()); + } ++ ++ byte[] serializeItem(ItemStack item); ++ ++ ItemStack deserializeItem(byte[] data); + // Paper end + } +diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java +index d7d6a3e83dfd88359708749f5c12be02815c3580..ccd81fca25233c2a9c2a8c3f4dda3053d7b2e723 100644 +--- a/src/main/java/org/bukkit/inventory/ItemStack.java ++++ b/src/main/java/org/bukkit/inventory/ItemStack.java +@@ -613,6 +613,29 @@ public class ItemStack implements Cloneable, ConfigurationSerializable { + return Bukkit.getServer().getItemFactory().ensureServerConversions(this); + } + ++ /** ++ * Deserializes this itemstack from raw NBT bytes. NBT is safer for data migrations as it will ++ * use the built in data converter instead of bukkits dangerous serialization system. ++ * ++ * This expects that the DataVersion was stored on the root of the Compound, as saved from ++ * the {@link #serializeAsBytes()} API returned. ++ * @return ItemStack migrated to this version of Minecraft if needed. ++ */ ++ @NotNull ++ public static ItemStack deserializeBytes(@NotNull byte[] bytes) { ++ return org.bukkit.Bukkit.getUnsafe().deserializeItem(bytes); ++ } ++ ++ /** ++ * Serializes this itemstack to raw bytes in NBT. NBT is safer for data migrations as it will ++ * use the built in data converter instead of bukkits dangerous serialization system. ++ * @return bytes representing this item in NBT. ++ */ ++ @NotNull ++ public byte[] serializeAsBytes() { ++ return org.bukkit.Bukkit.getUnsafe().serializeItem(this); ++ } ++ + /** + * Gets the Display name as seen in the Client. + * Currently the server only supports the English language. To override this, diff --git a/Spigot-Server-Patches/0529-Add-Raw-Byte-ItemStack-Serialization.patch b/Spigot-Server-Patches/0529-Add-Raw-Byte-ItemStack-Serialization.patch new file mode 100644 index 000000000..967757f9b --- /dev/null +++ b/Spigot-Server-Patches/0529-Add-Raw-Byte-ItemStack-Serialization.patch @@ -0,0 +1,102 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Mariell Hoversholm +Date: Thu, 30 Apr 2020 16:56:54 +0200 +Subject: [PATCH] Add Raw Byte ItemStack Serialization + +Serializes using NBT which is safer for server data migrations than bukkits format. + +diff --git a/src/main/java/net/minecraft/server/DataConverterRegistry.java b/src/main/java/net/minecraft/server/DataConverterRegistry.java +index 77c77973bb335cc89dae31bef2bbbcd4adc1d14b..d957d5fc2dc724cdda553e3a67ffe65e1e41c304 100644 +--- a/src/main/java/net/minecraft/server/DataConverterRegistry.java ++++ b/src/main/java/net/minecraft/server/DataConverterRegistry.java +@@ -22,6 +22,7 @@ public class DataConverterRegistry { + return datafixerbuilder.build(SystemUtils.e()); + } + ++ public static DataFixer getDataFixer() { return a(); } // Paper - OBFHELPER + public static DataFixer a() { + return DataConverterRegistry.c; + } +diff --git a/src/main/java/net/minecraft/server/ItemStack.java b/src/main/java/net/minecraft/server/ItemStack.java +index d6e43313bf0c678cf78fe77de2f8f4b6f819e3f4..ea60880c6f15b1a39579896d78cf641563621aa4 100644 +--- a/src/main/java/net/minecraft/server/ItemStack.java ++++ b/src/main/java/net/minecraft/server/ItemStack.java +@@ -140,6 +140,7 @@ public final class ItemStack { + this.checkEmpty(); + } + ++ public static ItemStack fromCompound(NBTTagCompound nbttagcompound) { return a(nbttagcompound); } // Paper - OBFHELPER + public static ItemStack a(NBTTagCompound nbttagcompound) { + try { + return new ItemStack(nbttagcompound); +diff --git a/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java b/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java +index 2322c0c8c5aacebb6317eab8ce4245554f6d9d55..3e6f878cdbbf5ebfa7fb390745ead135c1c4dee8 100644 +--- a/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java ++++ b/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java +@@ -14,6 +14,7 @@ import java.util.zip.GZIPOutputStream; + + public class NBTCompressedStreamTools { + ++ public static NBTTagCompound readNBT(InputStream inputstream) throws IOException { return a(inputstream); } // Paper - OBFHELPER + public static NBTTagCompound a(InputStream inputstream) throws IOException { + DataInputStream datainputstream = new DataInputStream(new BufferedInputStream(new GZIPInputStream(inputstream))); + Throwable throwable = null; +@@ -43,6 +44,7 @@ public class NBTCompressedStreamTools { + return nbttagcompound; + } + ++ public static void writeNBT(NBTTagCompound nbttagcompound, OutputStream outputstream) throws IOException { a(nbttagcompound, outputstream); } // Paper - OBFHELPER + public static void a(NBTTagCompound nbttagcompound, OutputStream outputstream) throws IOException { + DataOutputStream dataoutputstream = new DataOutputStream(new BufferedOutputStream(new GZIPOutputStream(outputstream))); + Throwable throwable = null; +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +index 770375ed4207920e71d2d0799c611c4b3cdbe6f7..aa8f135250338452c6143d41cca9aaea886ba31f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -313,6 +313,46 @@ public final class CraftMagicNumbers implements UnsafeValues { + public boolean isSupportedApiVersion(String apiVersion) { + return apiVersion != null && SUPPORTED_API.contains(apiVersion); + } ++ ++ @Override ++ public byte[] serializeItem(ItemStack item) { ++ Preconditions.checkNotNull(item, "null cannot be serialized"); ++ Preconditions.checkArgument(item.getType() != Material.AIR, "air cannot be serialized"); ++ ++ java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream(); ++ NBTTagCompound compound = (item instanceof CraftItemStack ? ((CraftItemStack) item).getHandle() : CraftItemStack.asNMSCopy(item)).save(new NBTTagCompound()); ++ compound.setInt("DataVersion", getDataVersion()); ++ try { ++ net.minecraft.server.NBTCompressedStreamTools.writeNBT( ++ compound, ++ outputStream ++ ); ++ } catch (IOException ex) { ++ throw new RuntimeException(ex); ++ } ++ ++ return outputStream.toByteArray(); ++ } ++ ++ @Override ++ public ItemStack deserializeItem(byte[] data) { ++ Preconditions.checkNotNull(data, "null cannot be deserialized"); ++ Preconditions.checkArgument(data.length > 0, "cannot deserialize nothing"); ++ ++ try { ++ NBTTagCompound compound = net.minecraft.server.NBTCompressedStreamTools.readNBT( ++ new java.io.ByteArrayInputStream(data) ++ ); ++ int dataVersion = compound.getInt("DataVersion"); ++ ++ Preconditions.checkArgument(dataVersion < getDataVersion(), "Newer version! Server downgrades are not supported!"); ++ Dynamic converted = DataConverterRegistry.getDataFixer().update(DataConverterTypes.ITEM_STACK, new Dynamic(DynamicOpsNBT.a, compound), -1, getDataVersion()); ++ return CraftItemStack.asCraftMirror(net.minecraft.server.ItemStack.fromCompound((NBTTagCompound) converted.getValue())); ++ } catch (IOException ex) { ++ com.destroystokyo.paper.util.SneakyThrow.sneaky(ex); ++ throw new RuntimeException(); ++ } ++ } + // Paper end + + /**