Skip to content

Instantly share code, notes, and snippets.

@graywolf336
Last active April 30, 2024 02:00
Show Gist options
  • Save graywolf336/8153678 to your computer and use it in GitHub Desktop.
Save graywolf336/8153678 to your computer and use it in GitHub Desktop.
Serialize and deserialize the player's inventory, including armor and content.
/**
* Converts the player inventory to a String array of Base64 strings. First string is the content and second string is the armor.
*
* @param playerInventory to turn into an array of strings.
* @return Array of strings: [ main content, armor content ]
* @throws IllegalStateException
*/
public static String[] playerInventoryToBase64(PlayerInventory playerInventory) throws IllegalStateException {
//get the main content part, this doesn't return the armor
String content = toBase64(playerInventory);
String armor = itemStackArrayToBase64(playerInventory.getArmorContents());
return new String[] { content, armor };
}
/**
*
* A method to serialize an {@link ItemStack} array to Base64 String.
*
* <p />
*
* Based off of {@link #toBase64(Inventory)}.
*
* @param items to turn into a Base64 String.
* @return Base64 string of the items.
* @throws IllegalStateException
*/
public static String itemStackArrayToBase64(ItemStack[] items) throws IllegalStateException {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream);
// Write the size of the inventory
dataOutput.writeInt(items.length);
// Save every element in the list
for (int i = 0; i < items.length; i++) {
dataOutput.writeObject(items[i]);
}
// Serialize that array
dataOutput.close();
return Base64Coder.encodeLines(outputStream.toByteArray());
} catch (Exception e) {
throw new IllegalStateException("Unable to save item stacks.", e);
}
}
/**
* A method to serialize an inventory to Base64 string.
*
* <p />
*
* Special thanks to Comphenix in the Bukkit forums or also known
* as aadnk on GitHub.
*
* <a href="https://gist.github.com/aadnk/8138186">Original Source</a>
*
* @param inventory to serialize
* @return Base64 string of the provided inventory
* @throws IllegalStateException
*/
public static String toBase64(Inventory inventory) throws IllegalStateException {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream);
// Write the size of the inventory
dataOutput.writeInt(inventory.getSize());
// Save every element in the list
for (int i = 0; i < inventory.getSize(); i++) {
dataOutput.writeObject(inventory.getItem(i));
}
// Serialize that array
dataOutput.close();
return Base64Coder.encodeLines(outputStream.toByteArray());
} catch (Exception e) {
throw new IllegalStateException("Unable to save item stacks.", e);
}
}
/**
*
* A method to get an {@link Inventory} from an encoded, Base64, string.
*
* <p />
*
* Special thanks to Comphenix in the Bukkit forums or also known
* as aadnk on GitHub.
*
* <a href="https://gist.github.com/aadnk/8138186">Original Source</a>
*
* @param data Base64 string of data containing an inventory.
* @return Inventory created from the Base64 string.
* @throws IOException
*/
public static Inventory fromBase64(String data) throws IOException {
try {
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data));
BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
Inventory inventory = Bukkit.getServer().createInventory(null, dataInput.readInt());
// Read the serialized inventory
for (int i = 0; i < inventory.getSize(); i++) {
inventory.setItem(i, (ItemStack) dataInput.readObject());
}
dataInput.close();
return inventory;
} catch (ClassNotFoundException e) {
throw new IOException("Unable to decode class type.", e);
}
}
/**
* Gets an array of ItemStacks from Base64 string.
*
* <p />
*
* Base off of {@link #fromBase64(String)}.
*
* @param data Base64 string to convert to ItemStack array.
* @return ItemStack array created from the Base64 string.
* @throws IOException
*/
public static ItemStack[] itemStackArrayFromBase64(String data) throws IOException {
try {
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data));
BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
ItemStack[] items = new ItemStack[dataInput.readInt()];
// Read the serialized inventory
for (int i = 0; i < items.length; i++) {
items[i] = (ItemStack) dataInput.readObject();
}
dataInput.close();
return items;
} catch (ClassNotFoundException e) {
throw new IOException("Unable to decode class type.", e);
}
}
@MySoulcutting
Copy link

Thank you

@kennytv
Copy link

kennytv commented Apr 4, 2024

Using ObjectInputStream is highly discouraged and should be avoided whenever possible due to major security risks. This will also have (continuously) worse(ning) performance compared to https://jd.papermc.io/paper/1.20/org/bukkit/inventory/ItemStack.html#serializeAsBytes() and https://jd.papermc.io/paper/1.20/org/bukkit/inventory/ItemStack.html#deserializeBytes(byte[]), e.g.:

    public static String serialize(ItemStack[] items) {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            DataOutput output = new DataOutputStream(outputStream);
            output.writeInt(items.length);

            for (ItemStack item : items) {
                if (item == null) {
                    // Ensure the correct order by including empty/null items
                    // Simply remove the write line if you don't want this
                    output.writeInt(0);
                    continue;
                }

                byte[] bytes = item.serializeAsBytes();
                output.writeInt(bytes.length);
                output.write(bytes);
            }
            return Base64Coder.encodeLines(outputStream.toByteArray()); // Base64 encoding is not strictly needed
        } catch (IOException e) {
            throw new RuntimeException("Error while writing itemstack", e);
        }
    }

    public static ItemStack[] deserialize(String encodedItems) {
        byte[] bytes = Base64Coder.decodeLines(encodedItems); // Base64 encoding is not strictly needed
        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
            DataInputStream input = new DataInputStream(inputStream);
            int count = input.readInt();
            ItemStack[] items = new ItemStack[count];
            for (int i = 0; i < count; i++) {
                int length = input.readInt();
                if (length == 0) {
                    // Empty item, keep entry as null
                    continue;
                }

                byte[] itemBytes = new byte[length];
                input.read(itemBytes);
                items[i] = ItemStack.deserializeBytes(itemBytes);
            }
            return items;
        } catch (IOException e) {
            throw new RuntimeException("Error while reading itemstack", e);
        }
    }

Or if you for some reason really need to support those few % of lost souls still using Spigot on even semi recent version, using internals is still better than the object streams and Spigot item serialization (though the linked example will still perform worse than Paper API)

@anjoismysign
Copy link

License?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment