Serialize Inventory to single string and vice versa

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

  1. Offline

    Phil2812

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    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;
        }
    }
    
    LCastr0, Anrza, ZekeMo and 25 others like this.
  2. Offline

    chaseoes Retired Staff

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Is it possible to get it back from the string? :p
  3. Offline

    Hidendra

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Code:
    public static Inventory StringToInventory (String invString)
    
  4. Offline

    Cowboys1919

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Thanks for this!
  5. Offline

    chaseoes Retired Staff

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    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..

    This post has been edited 3 times. It was last edited by chaseoes Aug 11, 2012.
  6. Offline

    Phil2812

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    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());
    
  7. Offline

    Warreo

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Wow! This is just awesome, thanks a lot! :D
  8. Offline

    krazytraynz

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Thanks a TON. I basically built an entire plugin based on this class. [diamond][diamond][diamond]
  9. Offline

    devilquak

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Does this need to be updated to save written books and soon dyed leather?
  10. Offline

    Sr4B

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    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

    This post has been edited 1 time. It was last edited by mbaxter Oct 16, 2012.
    Anrza and devilquak like this.
  11. Offline

    devilquak

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Just wondering, do you know how to save written books as well?

    This post has been edited 1 time. It was last edited by devilquak Oct 24, 2012.
  12. Offline

    devilquak

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    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

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    I LOVE YOU ! :D I had big problem how to save and load inventory :D but now.. :D
  14. Online

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    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:


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

    devilquak

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Wow, very helpful, thank you very, very much.
  16. Offline

    pizzafreak08

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    How would you save the armor contents to with the nbt method? Is there something like p.getInventory().getArmorContents().toInventory?
  17. Online

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    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]

    This post has been edited 3 times. It was last edited by Comphenix Dec 3, 2012.
  18. Offline

    pizzafreak08

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
  19. Offline

    xXSniperzzXx_SD

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    @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
  20. Online

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Try replacing it with CraftItemStack.asNMSCopy(craft).
  21. Offline

    xXSniperzzXx_SD

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    @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);

    This post has been edited 2 times. It was last edited by xXSniperzzXx_SD Dec 20, 2012.
  22. Online

    Comphenix

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    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.

    This post has been edited 1 time. It was last edited by Comphenix Jan 11, 2013.
    bobacadodl and eccentric_nz like this.
  23. Offline

    xXSniperzzXx_SD

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Thanks!
  24. Offline

    eccentric_nz

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Thank you, I was on the right track updating this, but couldn't quite get there
  25. Offline

    bobacadodl

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    The methods
    .asCraftCopy
    .asCraftMirror
    and .asNMSCopy are undefined for the type CraftItemStack when I try to use this?
  26. Offline

    xXSniperzzXx_SD

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    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
  27. Offline

    Jumla

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Same issue. A fix would be amazing.
  28. Offline

    xXSniperzzXx_SD

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    You using the right craftbukkit version?
  29. Offline

    bobacadodl

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    Just use the recommended build.
  30. Offline

    KeybordPiano459

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Minecraft account:
    MCUSERNAME
    @Phil2812
    Can you make it support custom item names?
    *cough cough* item.getItemMeta().getDisplayName();

Share This Page