From e53a1f5e221fe1335124643efa81b53feb7584bb Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Sun, 22 Sep 2024 21:02:06 +0200 Subject: [PATCH] Add ItemStack array serialization methods (#10387) --- ...Add-Raw-Byte-ItemStack-Serialization.patch | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/patches/api/Add-Raw-Byte-ItemStack-Serialization.patch b/patches/api/Add-Raw-Byte-ItemStack-Serialization.patch index d30beb0b77..012bca23dd 100644 --- a/patches/api/Add-Raw-Byte-ItemStack-Serialization.patch +++ b/patches/api/Add-Raw-Byte-ItemStack-Serialization.patch @@ -5,6 +5,8 @@ Subject: [PATCH] Add Raw Byte ItemStack Serialization Serializes using NBT which is safer for server data migrations than bukkits format. +Co-authored-by: Nassim Jahnke + diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/UnsafeValues.java @@ -50,7 +52,126 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + public byte[] serializeAsBytes() { + return org.bukkit.Bukkit.getUnsafe().serializeItem(this); + } ++ ++ /** ++ * The current version byte of the item array format used in {@link #serializeItemsAsBytes(java.util.Collection)} ++ * and {@link #deserializeItemsFromBytes(byte[])} respectively. ++ */ ++ private static final byte ARRAY_SERIALIZATION_VERSION = 1; ++ ++ /** ++ * Serializes a collection of items to raw bytes in NBT. Serializes empty items as null. ++ *

++ * If you need a string representation to put into a file, you can for example use {@link java.util.Base64} encoding. ++ * ++ * @param items items to serialize ++ * @return bytes representing the items in NBT ++ * @see #serializeAsBytes() ++ */ ++ public static byte @NotNull [] serializeItemsAsBytes(java.util.@NotNull Collection items) { ++ try (final java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream()) { ++ final java.io.DataOutput output = new java.io.DataOutputStream(outputStream); ++ output.writeByte(ARRAY_SERIALIZATION_VERSION); ++ output.writeInt(items.size()); ++ for (final ItemStack item : items) { ++ if (item == null || item.isEmpty()) { ++ // Ensure the correct order by including empty/null items ++ output.writeInt(0); ++ continue; ++ } ++ ++ final byte[] itemBytes = item.serializeAsBytes(); ++ output.writeInt(itemBytes.length); ++ output.write(itemBytes); ++ } ++ return outputStream.toByteArray(); ++ } catch (final java.io.IOException e) { ++ throw new RuntimeException("Error while writing itemstack", e); ++ } ++ } ++ ++ /** ++ * Serializes a collection of items to raw bytes in NBT. Serializes empty items as null. ++ *

++ * If you need a string representation to put into a file, you can for example use {@link java.util.Base64} encoding. ++ * ++ * @param items items to serialize ++ * @return bytes representing the items in NBT ++ * @see #serializeAsBytes() ++ */ ++ public static byte @NotNull [] serializeItemsAsBytes(@Nullable ItemStack @NotNull [] items) { ++ return serializeItemsAsBytes(java.util.Arrays.asList(items)); ++ } ++ ++ /** ++ * Deserializes this itemstack from raw NBT bytes. ++ *

++ * If you need a string representation to put into a file, you can for example use {@link java.util.Base64} encoding. ++ * ++ * @param bytes bytes representing an item in NBT ++ * @return ItemStack array migrated to this version of Minecraft if needed ++ * @see #deserializeBytes(byte[]) ++ */ ++ public static @NotNull ItemStack @NotNull [] deserializeItemsFromBytes(final byte @NotNull [] bytes) { ++ try (final java.io.ByteArrayInputStream inputStream = new java.io.ByteArrayInputStream(bytes)) { ++ final java.io.DataInputStream input = new java.io.DataInputStream(inputStream); ++ final byte version = input.readByte(); ++ if (version != ARRAY_SERIALIZATION_VERSION) { ++ throw new IllegalArgumentException("Unsupported version or bad data: " + version); ++ } ++ ++ final int count = input.readInt(); ++ final ItemStack[] items = new ItemStack[count]; ++ for (int i = 0; i < count; i++) { ++ final int length = input.readInt(); ++ if (length == 0) { ++ // Empty item, keep entry as empty ++ items[i] = ItemStack.empty(); ++ continue; ++ } ++ ++ final byte[] itemBytes = new byte[length]; ++ input.read(itemBytes); ++ items[i] = ItemStack.deserializeBytes(itemBytes); ++ } ++ return items; ++ } catch (final java.io.IOException e) { ++ throw new RuntimeException("Error while reading itemstack", e); ++ } ++ } + /** * Gets the Display name as seen in the Client. * Currently the server only supports the English language. To override this, +diff --git a/src/main/java/org/bukkit/util/io/BukkitObjectInputStream.java b/src/main/java/org/bukkit/util/io/BukkitObjectInputStream.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/util/io/BukkitObjectInputStream.java ++++ b/src/main/java/org/bukkit/util/io/BukkitObjectInputStream.java +@@ -0,0 +0,0 @@ import org.bukkit.configuration.serialization.ConfigurationSerialization; + *

+ * Behavior of implementations extending this class is not guaranteed across + * future versions. ++ * @deprecated Object streams on their own are not safe. For safer and more consistent serialization of items, ++ * use {@link org.bukkit.inventory.ItemStack#serializeAsBytes()} or ++ * {@link org.bukkit.inventory.ItemStack#serializeItemsAsBytes(java.util.Collection)}. + */ ++@Deprecated(since = "1.21") // Paper + public class BukkitObjectInputStream extends ObjectInputStream { + + /** +diff --git a/src/main/java/org/bukkit/util/io/BukkitObjectOutputStream.java b/src/main/java/org/bukkit/util/io/BukkitObjectOutputStream.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/util/io/BukkitObjectOutputStream.java ++++ b/src/main/java/org/bukkit/util/io/BukkitObjectOutputStream.java +@@ -0,0 +0,0 @@ import org.bukkit.configuration.serialization.ConfigurationSerializable; + *

+ * Behavior of implementations extending this class is not guaranteed across + * future versions. ++ * @deprecated Object streams on their own are not safe. For safer and more consistent serialization of items, ++ * use {@link org.bukkit.inventory.ItemStack#serializeAsBytes()} or ++ * {@link org.bukkit.inventory.ItemStack#serializeItemsAsBytes(java.util.Collection)}. + */ ++@Deprecated(since = "1.21") // Paper + public class BukkitObjectOutputStream extends ObjectOutputStream { + + /**