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; } }
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..
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());
Thanks ! it needs to support armor too 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 Thanks again, Phil
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?
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 public class ItemSerialization { public static String toBase64(Inventory inventory) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); DataOutputStream dataOutput = new DataOutputStream(outputStream); NBTTagList itemList = new NBTTagList(); // Save every element in the list for (int i = 0; i < inventory.getSize(); i++) { NBTTagCompound outputObject = new NBTTagCompound(); CraftItemStack craft = getCraftVersion(inventory.getItem(i)); // Convert the item stack to a NBT compound if (craft != null) craft.getHandle().save(outputObject); itemList.add(outputObject); } // Now save the list NBTBase.a(itemList, dataOutput); // Serialize that array return new BigInteger(1, outputStream.toByteArray()).toString(32); } public static Inventory fromBase64(String data) { ByteArrayInputStream inputStream = new ByteArrayInputStream(new BigInteger(data, 32).toByteArray()); NBTTagList itemList = (NBTTagList) NBTBase.b(new DataInputStream(inputStream)); Inventory inventory = new CraftInventoryCustom(null, itemList.size()); for (int i = 0; i < itemList.size(); i++) { NBTTagCompound inputObject = (NBTTagCompound) itemList.get(i); // IsEmpty if (!inputObject.d()) { inventory.setItem(i, new CraftItemStack(net.minecraft.server.ItemStack.a(inputObject))); } } // Serialize that array return inventory; } private static CraftItemStack getCraftVersion(ItemStack stack) { if (stack instanceof CraftItemStack) return (CraftItemStack) stack; else if (stack != null) return new CraftItemStack(stack); else return null; }} 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);
How would you save the armor contents to with the nbt method? Is there something like p.getInventory().getArmorContents().toInventory?
You can easily make one yourself: Code:java import org.bukkit.craftbukkit.inventory.CraftInventoryCustom;import org.bukkit.inventory.Inventory;import org.bukkit.inventory.ItemStack;import org.bukkit.inventory.PlayerInventory; public class ItemSerialization { public Inventory getArmorInventory(PlayerInventory inventory) { ItemStack[] armor = inventory.getArmorContents(); CraftInventoryCustom storage = new CraftInventoryCustom(null, armor.length); for (int i = 0; i < armor.length; i++) storage.setItem(i, armor[i]); return storage; }// ect.}[I][/I][/i]
@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
@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);
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.
The methods .asCraftCopy .asCraftMirror and .asNMSCopy are undefined for the type CraftItemStack when I try to use this?
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
@Phil2812 Can you make it support custom item names? *cough cough* item.getItemMeta().getDisplayName();