Solved How to Serialize ItemStack/Inventory with AttributeStorage?

Discussion in 'Spigot Plugin Development' started by MrPowerGamerBR, Jun 4, 2016.

  1. Howdy!

    So, I was storing some info on a ItemStack with AttributeStorage ( https://github.com/aadnk/AttributeStorage ) I was happy that it persists between restarts, however...

    Code (Text):

         /**
         * Serialize a Inventory
         * @param p
         * @return
         */
        public static String toBase64(Inventory inv, int i) {
            try {
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream);

                // Inventory Type
                dataOutput.writeInt(i);
                dataOutput.writeInt(inv.getSize());

                if (i == WITH_NAME) {
                    dataOutput.writeUTF(inv.getName());
                }

                dataOutput.writeObject(inv.getContents());

                // Serialize that array
                dataOutput.close();
                return Base64Coder.encodeLines(outputStream.toByteArray());

            } catch (Exception e) {
                throw new IllegalStateException("Unable to save item stacks.", e);
            }
        }

        /**
         * Deserialize a Inventory
         * @param p
         * @param s
         */
        public static Inventory fromBase64(String s) {
            try {
                ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(s));
                BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);

                int type = dataInput.readInt();

                Inventory inv = null;
                if (type == DEFAULT) {
                    inv = Bukkit.createInventory(null, dataInput.readInt());
                } else {
                    inv = Bukkit.createInventory(null, dataInput.readInt(), dataInput.readUTF());
                }

                inv.setContents((ItemStack[]) dataInput.readObject());

                dataInput.close();
                return inv;
            }
            catch (Exception e) {
                throw new IllegalStateException("Unable to save item stacks.", e);
            }
        }
    And here's the issue: If the inventory is serialized and then unserialized, all attributes are removed.

    I'm now unsure of what should I do, so... how can I serialize the AttributeStorage? I also accept a better serialization way that keeps the item's attributes, thanks!

    EDIT: Also, I need to serialize the inventory to store on a database, so I can't use "serialize to YAML" methods.
     
    #1 MrPowerGamerBR, Jun 4, 2016
    Last edited: Jun 4, 2016
  2. Already found a solution:

    Code (Text):
        /**
         * Item to Base 64
         * @param item
         * @return
         */
        public static String toBase64(ItemStack item) {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            DataOutputStream dataOutput = new DataOutputStream(outputStream);

            NBTTagList nbtTagListItems = new NBTTagList();
            NBTTagCompound nbtTagCompoundItem = new NBTTagCompound();

            net.minecraft.server.v1_7_R4.ItemStack nmsItem = CraftItemStack.asNMSCopy(item);

            nmsItem.save(nbtTagCompoundItem);

            nbtTagListItems.add(nbtTagCompoundItem);

            NBTCompressedStreamTools.a(nbtTagCompoundItem, (DataOutput) dataOutput);

            return new BigInteger(1, outputStream.toByteArray()).toString(32);
        }

        /**
         * Item from Base64
         * @param data
         * @return
         */
        public static ItemStack fromBase64(String data) {
            ByteArrayInputStream inputStream = new ByteArrayInputStream(new BigInteger(data, 32).toByteArray());

            NBTTagCompound nbtTagCompoundRoot = NBTCompressedStreamTools.a(new DataInputStream(inputStream));

            net.minecraft.server.v1_7_R4.ItemStack nmsItem = net.minecraft.server.v1_7_R4.ItemStack.createStack(nbtTagCompoundRoot);
            ItemStack item = (ItemStack) CraftItemStack.asBukkitCopy(nmsItem);

            return item;
        }
    Code (Text):
        /**
         * ItemStack List to Base64
         */
        public static String toBase64List(ItemStack[] items) {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            BukkitObjectOutputStream dataOutput;
            try {
                dataOutput = new BukkitObjectOutputStream(outputStream);

                // Content Size
                // Contents
                dataOutput.writeInt(items.length);

                int index = 0;
                for (ItemStack is : items) {
                    if (is != null && is.getType() != Material.AIR) {
                        dataOutput.writeObject(ItemUtils.toBase64(is));
                    } else {
                        dataOutput.writeObject(null);
                    }
                    dataOutput.writeInt(index);
                    index++;
                }
                dataOutput.close();
                return Base64Coder.encodeLines(outputStream.toByteArray());
            }
            catch (Exception e) {
                throw new IllegalStateException("Unable to save item stacks.", e);
            }
        }
       
        /**
         * ItemStack List from Base64
         */
        public static ItemStack[] fromBase64List(String items) {
            try {
                ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(items));
                BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);

                int size = dataInput.readInt();

                ItemStack[] list = new ItemStack[size];
                // Read the serialized inventory
                for (int i = 0; i < size; i++) {
                    Object utf = dataInput.readObject();
                    int slot = dataInput.readInt();
                    if (utf == null) { // yey┬▓?

                    } else {
                        list[slot] = ItemUtils.fromBase64((String) utf);
                    }
                }

                dataInput.close();
                return list;
            }
            catch (Exception e) {
                throw new IllegalStateException("Unable to load item stacks.", e);
            }
        }
     
  3. I am also trying to save item data. I am trying to store NBT data of items into a database field ( a string perhaps )...is this essentially what this code does?
     
  4. Way too late response, but yes! That code serializes a inventory into a base64 string.
     
  5. code adapted using reflection

    Code (Java):
    package your.package

    import
    java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.DataInputStream;
    import java.io.DataOutput;
    import java.io.DataOutputStream;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.math.BigInteger;

    import org.bukkit.Bukkit;
    import org.bukkit.inventory.ItemStack;

    public class Conversor {

        public static ItemStack deserializeItemStack(String data){
            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 nbtTagCompound = getNMSClass("NBTCompressedStreamTools").getMethod("a", DataInputStream.class).invoke(null, inputStream);
                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;
        }
       
        public static String serializeItemStack(ItemStack item) {
            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)dataOutput);

            } catch (SecurityException | NoSuchMethodException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                e.printStackTrace();
            }
         
            //return BaseEncoding.base64().encode(outputStream.toByteArray());
            return new BigInteger(1, outputStream.toByteArray()).toString(32);
        }
       
           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 var3) {
                   var3.printStackTrace();
                   return null;
               }
           }
         
           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 var3) {
                   var3.printStackTrace();
                   return null;
               }
           }
         
    }
     
     
  6. What is it with this post and replying a year late?!
     
  7. His post did actually improve upon my code (by adding reflection, so it can work in multiple versions) so it isn't against the forum rules.

    And by the way, I still use the same code I shared in this thread on my projects (with some differences due to NMS updates), so... yay!
     
  8. Choco

    Moderator

    You should be caching values obtained from reflection. No reason to get Class, Method, Constructor or Field instances more than once. Also, as of Java 7, ReflectiveOperationException was added to encapsulate all of those nasty exceptions that you have to catch when performing reflective operations. Catch that instead of the 6 you're catching currently.
     
    • Funny Funny x 1
    • Informative Informative x 1