Serialize Inventory to single string and vice versa

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

  1. 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 (Text):
    1.  
    2. import java.util.Map;
    3. import java.util.Map.Entry;
    4.  
    5. import org.bukkit.Bukkit;
    6. import org.bukkit.Material;
    7. import org.bukkit.enchantments.Enchantment;
    8. import org.bukkit.inventory.Inventory;
    9. import org.bukkit.inventory.ItemStack;
    10.  
    11. public class InventoryStringDeSerializer {
    12.     public static String InventoryToString (Inventory invInventory)
    13.     {
    14.         String serialization = invInventory.getSize() + ";";
    15.         for (int i = 0; i < invInventory.getSize(); i++)
    16.         {
    17.             ItemStack is = invInventory.getItem(i);
    18.             if (is != null)
    19.             {
    20.                 String serializedItemStack = new String();
    21.                
    22.                 String isType = String.valueOf(is.getType().getId());
    23.                 serializedItemStack += "t@" + isType;
    24.                
    25.                 if (is.getDurability() != 0)
    26.                 {
    27.                     String isDurability = String.valueOf(is.getDurability());
    28.                     serializedItemStack += ":d@" + isDurability;
    29.                 }
    30.                
    31.                 if (is.getAmount() != 1)
    32.                 {
    33.                     String isAmount = String.valueOf(is.getAmount());
    34.                     serializedItemStack += ":a@" + isAmount;
    35.                 }
    36.                
    37.                 Map<Enchantment,Integer> isEnch = is.getEnchantments();
    38.                 if (isEnch.size() > 0)
    39.                 {
    40.                     for (Entry<Enchantment,Integer> ench : isEnch.entrySet())
    41.                     {
    42.                         serializedItemStack += ":e@" + ench.getKey().getId() + "@" + ench.getValue();
    43.                     }
    44.                 }
    45.                
    46.                 serialization += i + "#" + serializedItemStack + ";";
    47.             }
    48.         }
    49.         return serialization;
    50.     }
    51.    
    52.     public static Inventory StringToInventory (String invString)
    53.     {
    54.         String[] serializedBlocks = invString.split(";");
    55.         String invInfo = serializedBlocks[0];
    56.         Inventory deserializedInventory = Bukkit.getServer().createInventory(null, Integer.valueOf(invInfo));
    57.        
    58.         for (int i = 1; i < serializedBlocks.length; i++)
    59.         {
    60.             String[] serializedBlock = serializedBlocks[i].split("#");
    61.             int stackPosition = Integer.valueOf(serializedBlock[0]);
    62.            
    63.             if (stackPosition >= deserializedInventory.getSize())
    64.             {
    65.                 continue;
    66.             }
    67.            
    68.             ItemStack is = null;
    69.             Boolean createdItemStack = false;
    70.            
    71.             String[] serializedItemStack = serializedBlock[1].split(":");
    72.             for (String itemInfo : serializedItemStack)
    73.             {
    74.                 String[] itemAttribute = itemInfo.split("@");
    75.                 if (itemAttribute[0].equals("t"))
    76.                 {
    77.                     is = new ItemStack(Material.getMaterial(Integer.valueOf(itemAttribute[1])));
    78.                     createdItemStack = true;
    79.                 }
    80.                 else if (itemAttribute[0].equals("d") && createdItemStack)
    81.                 {
    82.                     is.setDurability(Short.valueOf(itemAttribute[1]));
    83.                 }
    84.                 else if (itemAttribute[0].equals("a") && createdItemStack)
    85.                 {
    86.                     is.setAmount(Integer.valueOf(itemAttribute[1]));
    87.                 }
    88.                 else if (itemAttribute[0].equals("e") && createdItemStack)
    89.                 {
    90.                     is.addEnchantment(Enchantment.getById(Integer.valueOf(itemAttribute[1])), Integer.valueOf(itemAttribute[2]));
    91.                 }
    92.             }
    93.             deserializedInventory.setItem(stackPosition, is);
    94.         }
    95.        
    96.         return deserializedInventory;
    97.     }
    98. }
    99.  
     
  2. Is it possible to get it back from the string? :p
     
  3. Code (Text):
    1.  
    2. public static Inventory StringToInventory (String invString)
    3.  
     
  4. Thanks for this!
     
  5. But how to actually set it as their inventory? There's no .setInventory...

    Edit: Got it:
    Code (Text):
    1.                     Inventory i = StringToInventory(inv);
    2.                     for (ItemStack is : i) {
    3.                         player.getInventory().addItem(is);
    4.                     }
    It works correctly and fine, however the 3rd line there somehow throws a NPE..
     
  6. The problem here is that the is is null when there is no item at a slot.
    You should do
    Code (Text):
    1.  
    2. if (is != null)
    3. {
    4.    player.getInventory().addItems(is);
    5. }
    6.  
    instead or just do it like this:
    Code (Text):
    1.  
    2. Inventory i = StringToInventory(inv);
    3. player.getInventory().setContents(i.getContents());
    4.  
     
  7. Wow! This is just awesome, thanks a lot! :D
     
  8. Thanks a TON. I basically built an entire plugin based on this class. [diamond][diamond][diamond]
     
  9. 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 (Text):
    1.     public static String InventoryToString (Inventory invInventory, Player player)
    2.     {
    3.        
    4.         invInventory.addItem(player.getInventory().getArmorContents());
    5.         String serialization = invInventory.getSize() + ";";
    6.         for (int i = 0; i < invInventory.getSize(); i++)
    7.         {
    8.             ItemStack is = invInventory.getItem(i);
    9.             if (is != null)
    10.             {
    11.                 String serializedItemStack = new String();
    12.              
    13.                 String isType = String.valueOf(is.getType().getId());
    14.                 serializedItemStack += "t@" + isType;
    15.              
    16.                 if (is.getDurability() != 0)
    17.                 {
    18.                     String isDurability = String.valueOf(is.getDurability());
    19.                     serializedItemStack += ":d@" + isDurability;
    20.                 }
    21.              
    22.                 if (is.getAmount() != 1)
    23.                 {
    24.                     String isAmount = String.valueOf(is.getAmount());
    25.                     serializedItemStack += ":a@" + isAmount;
    26.                 }
    27.              
    28.                 Map<Enchantment,Integer> isEnch = is.getEnchantments();
    29.                 if (isEnch.size() > 0)
    30.                 {
    31.                     for (Entry<Enchantment,Integer> ench : isEnch.entrySet())
    32.                     {
    33.                         serializedItemStack += ":e@" + ench.getKey().getId() + "@" + ench.getValue();
    34.                     }
    35.                 }
    36.              
    37.                 serialization += i + "#" + serializedItemStack + ";";
    38.             }
    39.         }
    40.         return serialization;
    41.     }
    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. Just wondering, do you know how to save written books as well?
     
  12. 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. 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 (Text):
    1. String data = ItemSerialization.toBase64(player.getInventory());
    2. Inventory copy = ItemSerialization.fromBase64(data);
    3.  
     
    MyzelYam, Axe2760, Milkywayz and 4 others like this.
  15. Wow, very helpful, thank you very, very much.
     
  16. Offline

    pizzafreak08

    How would you save the armor contents to with the nbt method? Is there something like p.getInventory().getArmorContents().toInventory?
     
  17. 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

    pizzafreak08

  19. 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. Try replacing it with CraftItemStack.asNMSCopy(craft).
     
  21. 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);
     
  22. 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.
  23. Thanks!
     
  24. Thank you, I was on the right track updating this, but couldn't quite get there
     
  25. The methods
    .asCraftCopy
    .asCraftMirror
    and .asNMSCopy are undefined for the type CraftItemStack when I try to use this?
     
  26. 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

    Same issue. A fix would be amazing.
     
  28. You using the right craftbukkit version?
     
  29. Just use the recommended build.
     
  30. Phil2812
    Can you make it support custom item names?
    *cough cough* item.getItemMeta().getDisplayName();
     

Share This Page