Serialize Inventory to single string and vice versa

Discussion in 'Resources' started by Phil2812, Aug 9, 2012.

Thread Status:
Not open for further replies.
  1. Offline

    Phil2812

    I used this in a recent project where I needed to store the content of a chest in a database and this seemed like the easiest way to do so.

    It might look a little bit dirty but it works like a charm.
    It saves the id, amount, damage, enchantments and position of each item (stack).

    Feel free to use this code in your projects (you don't need to credit me, but a thanks in this thread would be appreciated if you use it ;) ).

    A full double chest (54 slots) would look something like this:
    Code:
    import java.util.Map;
    import java.util.Map.Entry;
     
    import org.bukkit.Bukkit;
    import org.bukkit.Material;
    import org.bukkit.enchantments.Enchantment;
    import org.bukkit.inventory.Inventory;
    import org.bukkit.inventory.ItemStack;
     
    public class InventoryStringDeSerializer {
        public static String InventoryToString (Inventory invInventory)
        {
            String serialization = invInventory.getSize() + ";";
            for (int i = 0; i < invInventory.getSize(); i++)
            {
                ItemStack is = invInventory.getItem(i);
                if (is != null)
                {
                    String serializedItemStack = new String();
                   
                    String isType = String.valueOf(is.getType().getId());
                    serializedItemStack += "t@" + isType;
                   
                    if (is.getDurability() != 0)
                    {
                        String isDurability = String.valueOf(is.getDurability());
                        serializedItemStack += ":d@" + isDurability;
                    }
                   
                    if (is.getAmount() != 1)
                    {
                        String isAmount = String.valueOf(is.getAmount());
                        serializedItemStack += ":a@" + isAmount;
                    }
                   
                    Map<Enchantment,Integer> isEnch = is.getEnchantments();
                    if (isEnch.size() > 0)
                    {
                        for (Entry<Enchantment,Integer> ench : isEnch.entrySet())
                        {
                            serializedItemStack += ":e@" + ench.getKey().getId() + "@" + ench.getValue();
                        }
                    }
                   
                    serialization += i + "#" + serializedItemStack + ";";
                }
            }
            return serialization;
        }
       
        public static Inventory StringToInventory (String invString)
        {
            String[] serializedBlocks = invString.split(";");
            String invInfo = serializedBlocks[0];
            Inventory deserializedInventory = Bukkit.getServer().createInventory(null, Integer.valueOf(invInfo));
           
            for (int i = 1; i < serializedBlocks.length; i++)
            {
                String[] serializedBlock = serializedBlocks[i].split("#");
                int stackPosition = Integer.valueOf(serializedBlock[0]);
               
                if (stackPosition >= deserializedInventory.getSize())
                {
                    continue;
                }
               
                ItemStack is = null;
                Boolean createdItemStack = false;
               
                String[] serializedItemStack = serializedBlock[1].split(":");
                for (String itemInfo : serializedItemStack)
                {
                    String[] itemAttribute = itemInfo.split("@");
                    if (itemAttribute[0].equals("t"))
                    {
                        is = new ItemStack(Material.getMaterial(Integer.valueOf(itemAttribute[1])));
                        createdItemStack = true;
                    }
                    else if (itemAttribute[0].equals("d") && createdItemStack)
                    {
                        is.setDurability(Short.valueOf(itemAttribute[1]));
                    }
                    else if (itemAttribute[0].equals("a") && createdItemStack)
                    {
                        is.setAmount(Integer.valueOf(itemAttribute[1]));
                    }
                    else if (itemAttribute[0].equals("e") && createdItemStack)
                    {
                        is.addEnchantment(Enchantment.getById(Integer.valueOf(itemAttribute[1])), Integer.valueOf(itemAttribute[2]));
                    }
                }
                deserializedInventory.setItem(stackPosition, is);
            }
           
            return deserializedInventory;
        }
    }
    
     
  2. Offline

    chaseoes

    Is it possible to get it back from the string? :p
     
  3. Offline

    Hidendra

    Code:
    public static Inventory StringToInventory (String invString)
    
     
  4. Offline

    Cowboys1919

    Thanks for this!
     
  5. Offline

    chaseoes

    But how to actually set it as their inventory? There's no .setInventory...

    Edit: Got it:
    Code:
                        Inventory i = StringToInventory(inv);
                        for (ItemStack is : i) {
                            player.getInventory().addItem(is);
                        }
    It works correctly and fine, however the 3rd line there somehow throws a NPE..
     
  6. Offline

    Phil2812

    The problem here is that the is is null when there is no item at a slot.
    You should do
    Code:
    if (is != null)
    {
       player.getInventory().addItems(is);
    }
    
    instead or just do it like this:
    Code:
    Inventory i = StringToInventory(inv);
    player.getInventory().setContents(i.getContents());
    
     
    Photon156 likes this.
  7. Offline

    Warreo

    Wow! This is just awesome, thanks a lot! :D
     
  8. Offline

    krazytraynz

    Thanks a TON. I basically built an entire plugin based on this class. [diamond][diamond][diamond]
     
  9. Offline

    devilquak

    Does this need to be updated to save written books and soon dyed leather?
     
  10. Offline

    Sr4B

    Thanks ! :D

    it needs to support armor too :D it's simple to do , just edit the first method to be like this one :

    Code:
        public static String InventoryToString (Inventory invInventory, Player player)
        {
           
            invInventory.addItem(player.getInventory().getArmorContents());
            String serialization = invInventory.getSize() + ";";
            for (int i = 0; i < invInventory.getSize(); i++)
            {
                ItemStack is = invInventory.getItem(i);
                if (is != null)
                {
                    String serializedItemStack = new String();
                 
                    String isType = String.valueOf(is.getType().getId());
                    serializedItemStack += "t@" + isType;
                 
                    if (is.getDurability() != 0)
                    {
                        String isDurability = String.valueOf(is.getDurability());
                        serializedItemStack += ":d@" + isDurability;
                    }
                 
                    if (is.getAmount() != 1)
                    {
                        String isAmount = String.valueOf(is.getAmount());
                        serializedItemStack += ":a@" + isAmount;
                    }
                 
                    Map<Enchantment,Integer> isEnch = is.getEnchantments();
                    if (isEnch.size() > 0)
                    {
                        for (Entry<Enchantment,Integer> ench : isEnch.entrySet())
                        {
                            serializedItemStack += ":e@" + ench.getKey().getId() + "@" + ench.getValue();
                        }
                    }
                 
                    serialization += i + "#" + serializedItemStack + ";";
                }
            }
            return serialization;
        }
    Add a new parameter : Player player
    let it add the armor to the Virtual Inventory: invInventory.addItem(player.getInventory().getArmorContents());

    That's it :D
    Thanks again, Phil
     
    Anrza and devilquak like this.
  11. Offline

    devilquak

    Just wondering, do you know how to save written books as well?
     
  12. Offline

    devilquak

    Actually, just thought I'd let you know that your code only saves the armor as part of the inventory. When you re-apply the deserialized inventory, it restores the armor to the first open inventory slots available. I haven't tested it with a full inventory, but I don't really need to.

    Also, does anyone know about the books? With the 1.4 update, the NBT tags won't be saved in this either, so I'm wondering if anyone's modified this or added anything to support books and tags yet? Maybe Phil2812?
     
  13. Offline

    Hester

    I LOVE YOU ! :D I had big problem how to save and load inventory :D but now.. :D
     
  14. Offline

    Comphenix

    You should serialize the NBT content instead. That's actually pretty simple, provided that you reference CraftBukkit and use its built-in NBT classes:
    Code:java
    1. public class ItemSerialization {
    2. public static String toBase64(Inventory inventory) {
    3. DataOutputStream dataOutput = new DataOutputStream(outputStream);
    4. NBTTagList itemList = new NBTTagList();
    5.  
    6. // Save every element in the list
    7. for (int i = 0; i < inventory.getSize(); i++) {
    8. NBTTagCompound outputObject = new NBTTagCompound();
    9. CraftItemStack craft = getCraftVersion(inventory.getItem(i));
    10.  
    11. // Convert the item stack to a NBT compound
    12. if (craft != null)
    13. craft.getHandle().save(outputObject);
    14. itemList.add(outputObject);
    15. }
    16.  
    17. // Now save the list
    18. NBTBase.a(itemList, dataOutput);
    19.  
    20. // Serialize that array
    21. return new BigInteger(1, outputStream.toByteArray()).toString(32);
    22. }
    23.  
    24. public static Inventory fromBase64(String data) {
    25. ByteArrayInputStream inputStream = new ByteArrayInputStream(new BigInteger(data, 32).toByteArray());
    26. NBTTagList itemList = (NBTTagList) NBTBase.b(new DataInputStream(inputStream));
    27. Inventory inventory = new CraftInventoryCustom(null, itemList.size());
    28.  
    29. for (int i = 0; i < itemList.size(); i++) {
    30. NBTTagCompound inputObject = (NBTTagCompound) itemList.get(i);
    31.  
    32. // IsEmpty
    33. if (!inputObject.d()) {
    34. inventory.setItem(i, new CraftItemStack(net.minecraft.server.ItemStack.a(inputObject)));
    35. }
    36. }
    37.  
    38. // Serialize that array
    39. return inventory;
    40. }
    41.  
    42. private static CraftItemStack getCraftVersion(ItemStack stack) {
    43. if (stack instanceof CraftItemStack)
    44. return (CraftItemStack) stack;
    45. else if (stack != null)
    46. return new CraftItemStack(stack);
    47. else
    48. return null;
    49. }
    50. }


    You can download the above class here.

    I also have a version that uses Base64 instead of Base32, to minimize the string a little:
    https://gist.github.com/4102419

    Here's an example output comparing the two versions:

    And base 64:
    This represents the following inventory:
    Show Spoiler


    And finally, this is how you use the class:
    Code:
    String data = ItemSerialization.toBase64(player.getInventory());
    Inventory copy = ItemSerialization.fromBase64(data);
    
     
  15. Offline

    devilquak

    Wow, very helpful, thank you very, very much.
     
  16. How would you save the armor contents to with the nbt method? Is there something like p.getInventory().getArmorContents().toInventory?
     
  17. Offline

    Comphenix

    You can easily make one yourself:
    Code:java
    1. import org.bukkit.craftbukkit.inventory.CraftInventoryCustom;
    2. import org.bukkit.inventory.Inventory;
    3. import org.bukkit.inventory.ItemStack;
    4. import org.bukkit.inventory.PlayerInventory;
    5.  
    6. public class ItemSerialization {
    7. public Inventory getArmorInventory(PlayerInventory inventory) {
    8. ItemStack[] armor = inventory.getArmorContents();
    9. CraftInventoryCustom storage = new CraftInventoryCustom(null, armor.length);
    10.  
    11. for (int i = 0; i < armor.length; i++)
    12. storage.setItem(i, armor[i]);
    13. return storage;
    14. }
    15. // ect.
    16. }
    17. [I][/I][/i]
     
  18. Offline

    xXSniperzzXx_SD

    Comphenix

    And how would you do it with CB 1.4.5-R1.0, it's buggin out at craft.getHandle() and 2 other places, but i've also been told you can just serialize an itemstack, how would i just use that method but still have it save to the string... I'm really having a brain clog atm... can't think straight lol
     
  19. Offline

    Comphenix

    Try replacing it with CraftItemStack.asNMSCopy(craft).
     
  20. Offline

    xXSniperzzXx_SD

    Comphenix
    Well i managed to replace one of the errors with
    CraftItemStack.asCraftCopy(stack)
    but now i'm stuck trying to fix
    inventory.setItem(i, new CraftItemStack(net.minecraft.server.v1_4_5.ItemStack.a(inputObject)));
    and at
    craft.getHandle().save(outputObject);
     
  21. Offline

    Comphenix

    I had some time to play around with it, and it seems to be working. You can get the new version here.

    EDIT: Updated to Minecraft 1.4.6.
     
    bobacadodl and eccentric_nz like this.
  22. Offline

    xXSniperzzXx_SD

    Thanks!
     
  23. Offline

    eccentric_nz

    Thank you, I was on the right track updating this, but couldn't quite get there
     
  24. Offline

    bobacadodl

    The methods
    .asCraftCopy
    .asCraftMirror
    and .asNMSCopy are undefined for the type CraftItemStack when I try to use this?
     
  25. Offline

    xXSniperzzXx_SD

    It works for me, try downloading the class, then replacing the current one, that way u get all the proper imports etc, and btw this is for CB 1.4.5-R1.0 That version won't work for anything older
     
  26. Offline

    Jumla

    Same issue. A fix would be amazing.
     
  27. Offline

    xXSniperzzXx_SD

    You using the right craftbukkit version?
     
  28. Offline

    bobacadodl

    Just use the recommended build.
     
  29. Offline

    KeybordPiano459

    Phil2812
    Can you make it support custom item names?
    *cough cough* item.getItemMeta().getDisplayName();
     
Thread Status:
Not open for further replies.

Share This Page