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

    eccentric_nz

    Early days yet, but any chance of an update for 1.7.x?
     
  2. Offline

    Diederikmc

    There's a good chance, I'm working on it.
    But I need a little help,
    (NBTTagList) NBTBase.a(new DataInputStream(inputStream))
    is in 1.7.2 broken, and I think
    (NBTTagList) (NBTCompressedStreamTools.a(new DataInputStream(inputStream)))
    is the new way, but eclipse won't let me do that, it says:
    The method a(InputStream) is ambiguous for the type
    above the a.
    There is a public method a and a private method a in NBTCompressedStreamTools btw, I just want to call the public one that returns a NBTBase casted to a NBTTagCompound which calls the private method that returns a NBTBase. And the NBTTagCompound will be casted to a NBTBase back and that will be casted to a NBTTagList.
    I'm just a simple plugin developer, and not a java expert, please help me fix the eclipse problem to update the code.
     
  3. Offline

    eccentric_nz

    Comphenix

    Can we call upon your prodigious java skills to help us out here?
     
  4. Offline

    DenialMC

    Comphenix

    Could you please update to 1.7.2? It would be highly appreciated! :)

    Daniel
     
  5. Offline

    DPOH-VAR

    NBTCompressedStreamTools makes compressed(gzip) data with NBTTagCompound
    it works fine, but you can't compress NBTTagList with it.

    if you want to save any nbt tag to DataOutputStream:
    1) write one byte, type of nbt tag (1-11)
    2) if you want to have compatibility with earlier format, output.writeUTF("");
    3) call method "write" of nbt tag

    load data from DataInputStream:
    1) read one byte from stream
    2) readUTF(); (If you previously wrote it)
    3) call static method of class NBTBase "create from byte"
    4) call method read(stream,0)
    5) catch exceptions. just in case
     
  6. Offline

    chris752w

    Is it possible to save the players armor contents in the save method as the InventoryToString? If so, how?
     
  7. Offline

    Johnzeh

    As Sr4B had said on the first page, you're going to need to add a Player parameter to the method and then get and add the given players armor contents to the invInventory.

    edit: goodnight chris my sweet little angel
     
  8. Offline

    chris752w

    Yeah, but whenever I load the inventory back it sets the first 4 items saved to their armor.
     
  9. Offline

    Ch4t4r

    DenialMC

    I got it working. However you have to rewrite the Class NBTTagList and replace it in your Bukkit build. If you want to do so, have a look at this (Some way down is an method I haven't tested yet)

    NBTTagList to be replaced in bukkit
    Code:java
    1. package net.minecraft.server.v1_7_R1;
    2.  
    3. import java.io.DataInput;
    4. import java.io.DataOutput;
    5. import java.io.IOException;
    6. import java.util.ArrayList;
    7. import java.util.Iterator;
    8. import java.util.List;
    9.  
    10. public class NBTTagList extends NBTBase {
    11.  
    12. private List list = new ArrayList();
    13. public static NBTTagList instance; //My Addition
    14. private byte type = 0;
    15.  
    16. public NBTTagList() {instance = this;} //My Addition
    17.  
    18. void write(DataOutput dataoutput) {
    19. if (!this.list.isEmpty()) {
    20. this.type = ((NBTBase) this.list.get(0)).getTypeId();
    21. } else {
    22. this.type = 0;
    23. }
    24.  
    25.  
    26. try {
    27. dataoutput.writeByte(this.type);
    28. dataoutput.writeInt(this.list.size());
    29. } catch (IOException e) {
    30. // TODO Auto-generated catch block
    31. e.printStackTrace();
    32. }
    33.  
    34. for (int i = 0; i < this.list.size(); ++i) {
    35. ((NBTBase) this.list.get(i)).write(dataoutput);
    36. }
    37. }
    38.  
    39. public static void write2(DataOutput dataoutput) { //My Addition
    40. if (!instance.list.isEmpty()) {
    41. instance.type = ((NBTBase) instance.list.get(0)).getTypeId();
    42. } else {
    43. instance.type = 0;
    44. }
    45.  
    46.  
    47. try {
    48. dataoutput.writeByte(instance.type);
    49. dataoutput.writeInt(instance.list.size());
    50. } catch (IOException e) {
    51. // TODO Auto-generated catch block
    52. e.printStackTrace();
    53. }
    54.  
    55. for (int i = 0; i < instance.list.size(); ++i) {
    56. ((NBTBase) instance.list.get(i)).write(dataoutput);
    57. }
    58. }
    59.  
    60. void load(DataInput datainput, int i) {
    61. if (i > 512) {
    62. throw new RuntimeException("Tried to read NBT tag with too high complexity, depth > 512");
    63. } else {
    64. int j = 0;
    65. try {
    66. this.type = datainput.readByte();
    67. j = datainput.readInt();
    68. } catch (IOException e) {
    69. // TODO Auto-generated catch block
    70. e.printStackTrace();
    71. }
    72.  
    73. this.list = new ArrayList();
    74.  
    75. for (int k = 0; k < j; ++k) {
    76. NBTBase nbtbase = NBTBase.createTag(this.type);
    77.  
    78. nbtbase.load(datainput, i + 1);
    79. this.list.add(nbtbase);
    80. }
    81. }
    82. }
    83.  
    84. public static void load2(DataInput datainput, int i) { //My Addition
    85. if (i > 512) {
    86. throw new RuntimeException("Tried to read NBT tag with too high complexity, depth > 512");
    87. } else {
    88. int j = 0;
    89. try {
    90. instance.type = datainput.readByte();
    91. j = datainput.readInt();
    92. } catch (IOException e) {
    93. // TODO Auto-generated catch block
    94. e.printStackTrace();
    95. }
    96.  
    97. instance.list = new ArrayList();
    98.  
    99. for (int k = 0; k < j; ++k) {
    100. NBTBase nbtbase = NBTBase.createTag(instance.type);
    101.  
    102. nbtbase.load(datainput, i + 1);
    103. instance.list.add(nbtbase);
    104. }
    105. }
    106. }
    107.  
    108. public byte getTypeId() {
    109. return (byte) 9;
    110. }
    111.  
    112. public String toString() {
    113. String s = "[";
    114. int i = 0;
    115.  
    116. for (Iterator iterator = this.list.iterator(); iterator.hasNext(); ++i) {
    117. NBTBase nbtbase = (NBTBase) iterator.next();
    118.  
    119. s = s + "" + i + ':' + nbtbase + ',';
    120. }
    121.  
    122. return s + "]";
    123. }
    124.  
    125. public void add(NBTBase nbtbase) {
    126. if (this.type == 0) {
    127. this.type = nbtbase.getTypeId();
    128. } else if (this.type != nbtbase.getTypeId()) {
    129. System.err.println("WARNING: Adding mismatching tag types to tag list");
    130. return;
    131. }
    132.  
    133. this.list.add(nbtbase);
    134. }
    135.  
    136. public NBTTagCompound get(int i) {
    137. if (i >= 0 && i < this.list.size()) {
    138. NBTBase nbtbase = (NBTBase) this.list.get(i);
    139.  
    140. return nbtbase.getTypeId() == 10 ? (NBTTagCompound) nbtbase : new NBTTagCompound();
    141. } else {
    142. return new NBTTagCompound();
    143. }
    144. }
    145.  
    146. public int[] c(int i) {
    147. if (i >= 0 && i < this.list.size()) {
    148. NBTBase nbtbase = (NBTBase) this.list.get(i);
    149.  
    150. return nbtbase.getTypeId() == 11 ? ((NBTTagIntArray) nbtbase).c() : new int[0];
    151. } else {
    152. return new int[0];
    153. }
    154. }
    155.  
    156. public double d(int i) {
    157. if (i >= 0 && i < this.list.size()) {
    158. NBTBase nbtbase = (NBTBase) this.list.get(i);
    159.  
    160. return nbtbase.getTypeId() == 6 ? ((NBTTagDouble) nbtbase).g() : 0.0D;
    161. } else {
    162. return 0.0D;
    163. }
    164. }
    165.  
    166. public float e(int i) {
    167. if (i >= 0 && i < this.list.size()) {
    168. NBTBase nbtbase = (NBTBase) this.list.get(i);
    169.  
    170. return nbtbase.getTypeId() == 5 ? ((NBTTagFloat) nbtbase).h() : 0.0F;
    171. } else {
    172. return 0.0F;
    173. }
    174. }
    175.  
    176. public String f(int i) {
    177. if (i >= 0 && i < this.list.size()) {
    178. NBTBase nbtbase = (NBTBase) this.list.get(i);
    179.  
    180. return nbtbase.getTypeId() == 8 ? nbtbase.a_() : nbtbase.toString();
    181. } else {
    182. return "";
    183. }
    184. }
    185.  
    186. public int size() {
    187. return this.list.size();
    188. }
    189.  
    190. public NBTBase clone() {
    191. NBTTagList nbttaglist = new NBTTagList();
    192.  
    193. nbttaglist.type = this.type;
    194. Iterator iterator = this.list.iterator();
    195.  
    196. while (iterator.hasNext()) {
    197. NBTBase nbtbase = (NBTBase) iterator.next();
    198. NBTBase nbtbase1 = nbtbase.clone();
    199.  
    200. nbttaglist.list.add(nbtbase1);
    201. }
    202.  
    203. return nbttaglist;
    204. }
    205.  
    206. public boolean equals(Object object) {
    207. if (super.equals(object)) {
    208. NBTTagList nbttaglist = (NBTTagList) object;
    209.  
    210. if (this.type == nbttaglist.type) {
    211. return this.list.equals(nbttaglist.list);
    212. }
    213. }
    214.  
    215. return false;
    216. }
    217.  
    218. public int hashCode() {
    219. return super.hashCode() ^ this.list.hashCode();
    220. }
    221.  
    222. public int d() {
    223. return this.type;
    224. }
    225. }


    My InventoryHandler class (Most of the code is from Comphenix)
    Code:
    package me.Ch4t4r.ECHEST;
     
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.DataInput;
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.math.BigInteger;
     
    import net.minecraft.server.v1_7_R1.NBTTagCompound;
    import net.minecraft.server.v1_7_R1.NBTTagList;
     
    import org.bukkit.craftbukkit.v1_7_R1.inventory.CraftInventoryCustom;
    import org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemStack;
    import org.bukkit.inventory.Inventory;
    import org.bukkit.inventory.PlayerInventory;
     
    public class InventoryHandler
    {
      public static Inventory getContentInventory(Inventory inventory)
      {
        org.bukkit.inventory.ItemStack[] content = inventory.getContents();
        CraftInventoryCustom storage = new CraftInventoryCustom(null, content.length);
     
        for (int i = 0; i < content.length; i++) {
          storage.setItem(i, content[i]);
        }
        return storage;
      }
     
      public static Inventory getContentInventory(PlayerInventory inventory)
      {
        org.bukkit.inventory.ItemStack[] content = inventory.getContents();
        CraftInventoryCustom storage = new CraftInventoryCustom(null, content.length);
     
        for (int i = 0; i < content.length; i++) {
          storage.setItem(i, content[i]);
        }
        return storage;
      }
     
      public static String toBase64(Inventory inventory)
      {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        DataOutputStream dataOutput = new DataOutputStream(outputStream);
        NBTTagList itemList = new NBTTagList();
        for (int i = 0; i < inventory.getSize(); i++)
        {
          NBTTagCompound outputObject = new NBTTagCompound();
          net.minecraft.server.v1_7_R1.ItemStack craft = getCraftVersion(inventory.getItem(i));
     
          if (craft != null){
              craft.save(outputObject);
              itemList.add(outputObject);
          }
        }
        itemList.write2(dataOutput);
        //NBTTagListWRITE(itemList,dataOutput);
        return new BigInteger(1, outputStream.toByteArray()).toString(32);
      }
     
      public static Inventory fromBase64(String data){
        ByteArrayInputStream inputStream = new ByteArrayInputStream(new BigInteger(data, 32).toByteArray());
        DataInputStream input= new DataInputStream(inputStream);
        NBTTagList itemList = new NBTTagList();
        itemList.load2(input, 0);
        //NBTTagListLOAD(tagList,input);
     
        Inventory inventory = new CraftInventoryCustom(null, itemList.size());
     
        for (int i = 0; i < itemList.size(); i++)
        {
          NBTTagCompound inputObject = (NBTTagCompound)itemList.get(i);
     
          if (!inputObject.isEmpty())
          {
            inventory.setItem(i, CraftItemStack.asBukkitCopy(net.minecraft.server.v1_7_R1.ItemStack.createStack(inputObject)));
          }
        }
        try {
            input.close();
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return inventory;
      }
     
      private static net.minecraft.server.v1_7_R1.ItemStack getCraftVersion(org.bukkit.inventory.ItemStack stack)
      {
        if (stack != null) {
          return CraftItemStack.asNMSCopy(stack);
        }
        return null;
      }
     
      public static void NBTTagListWRITE(NBTTagList tagList,DataOutputStream dataoutput){
          Class c = tagList.getClass();
          for(Method m: c.getMethods()){
              if(m.getName().equalsIgnoreCase("write")){
                  try {
                    m.invoke(tagList,dataoutput);
                  } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                    e.printStackTrace();
                  }
              }
          }
      }
     
      public static void NBTTagListLOAD(NBTTagList tagList,DataInputStream datainput){
          Class c = tagList.getClass();
          for(Method m: c.getMethods()){
              if(m.getName().equalsIgnoreCase("load")){
                  try{
                    m.invoke(tagList,datainput,0);
                  } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                    e.printStackTrace();
                  }
              }
          }
      }
    }
    These last two voids are a second method which I haven't tried out yet but they should work. With those you don't have to replace NBTTagList.
     
  10. Offline

    Pulsior

    Ch4t4r

    When I've got to replace NBTTaglist in my Bukkit build fort this, does that also mean the compiled plugin will not work with an unmodified CraftBukkit build?
     
  11. Offline

    Ch4t4r

    Pulsior Yeah thats the case, but NBTTagListLOAD and NBTTagListWRITE are yet untested voids of me wich may work without modifying your build.
     
  12. Offline

    Pulsior

    Maybe I'm a newb for asking, but it seems that I need another library for net.minecraft.server. Where can I find it?
     
  13. Offline

    maciekmm

    You must use craftbukkit not bukkit :)
     
    Pulsior likes this.
  14. Offline

    Pulsior

    There's an error in toBase64(), the string it returns is always "0". Not sure about a fix though, I'm not really familliar with NMS myself. Does anyone know? I'm using the untested methods.
     
  15. Offline

    Ch4t4r

    I'm gonna test and maybe update it later.
     
  16. Offline

    Comphenix

    You don't even need CraftBukkit - you can do this entirely in the standard Bukkit API:
    https://gist.github.com/aadnk/8138186

    Of course, that's not backwards compatible with older versions of this resource, so in that case you will still be forced to use CraftBukkit. I've updated the previous version to use a similar format, though I'm not 100% positive it's backwards compatible. I believe it should work though:
    https://gist.github.com/aadnk/8138345
     
  17. Offline

    Bradley Hilton

    Before I go and copy this into a plugin I'm working on, can I do so and provide you credit for it?
     
  18. Offline

    Comphenix

    Sure, no problem at all.

    I suppose I can release my two previous examples under the Creative Commons Attribution license. :)
     
    Bradley Hilton likes this.
  19. Offline

    Bradley Hilton

    Thanks! :)
     
  20. Offline

    viper_monster

    Comphenix How could we save the armor with your method? Or does it already save it?
     
  21. Offline

    Bradley Hilton

    No the armor doesn't save when you pass in the player's inventory. Since I needed this as well, I've went ahead and created a couple methods based off Comphenix's methods. The method to serialize a player's inventory returns an array of strings which the first item will be the content and the second item will be the armor. Here's a gist of it: https://gist.github.com/graywolf336/8153678
     
  22. Offline

    mattrick

    Phil2812
    Nice! But it doesn't store lore right? I know its an easy fix to do that, but I just thought I'd bring that to your attention.
     
  23. Offline

    DenialMC


    Comphenix

    #ComphenixForBukkitStaff

    Thank you so much, very helpful! <3

    Daniel
     
  24. Offline

    desht

    I've been playing around with this in conjunction with https://forums.bukkit.org/threads/s...tently-on-item-itemstack.174080/#post-1875531 in the hope of setting a custom attribute on an ItemStack and then later serialising the ItemStack. Would I be correct in supposing that the BukkitObjectInputStream & BukkitObjectOutputStream don't work well with such custom attribute names? (My efforts so far suggest that this won't work well...)

    Update: yeah, hacked around that for now by explicitly writing the attribute data that I've added (of course this means that items with custom attributes from other plugins won't serialise properly yet so I need to look at that...)

    Are BukkitObjectInputStream/BukkitObjectOutputStream aware of attributes at all, even vanilla ones?
     
  25. Offline

    Comphenix

    Ah, of course - Bukkit only supports attributes when ItemMeta has been loaded from a CraftItemStack, and not a Bukkit ItemStack (the deserialized type), so attributes will be removed during serialization.

    It's unfortunate, but there is no simple way around this with pure Bukkit API (beside Bukkit fixing the issue itself). So, I recommend just falling back to my NMS solution.
     
  26. Offline

    desht

    Yeah, I'd come to that conclusion myself :)

    I have had some success using your AttributeStorage, Attributes & NbtFactory classes with inserting/serializing/deserializing/extracting custom attributes. My code is quite ugly right now, but it seems to work well (no public code as yet, but I will be uploading to github sometime soon). Your Attributes.Attribute class could benefit from implementing ConfigurationSerializable, so it can be handled via BukkitObject{Input|Output}Stream - at the moment I'm doing some explicit serialization of the attributes to a string and writing that to the output stream (and vice versa).
     
  27. Offline

    elementalgodz11

    Is there anyway to get the itemmeta using this?
     
  28. Offline

    Toni


    Sorry for the bump and bother,

    I'm interested in using this (to hopefully save a lot of space for inventories saved), however I've noticed that your NMS class uses a normal Inventory object class, not PlayerInventory. I may have missed this, but is there a reason it's not PlayerInventory? I'd like to be able to include armor contents in there as well if possible. Would you have a possible fork of your original NMS class that includes armor?
     
  29. Offline

    Gater12

    Toni
    PlayerInventory inherits Inventory, so it's just like an Inventory but it's an object for the player's inventory (Hope this sounds clear enough)
     
  30. Offline

    Toni


    Ah thank you. So theoretically I should be able to change the objects within the NMS class to PlayerInventory, and add a little bit more in order to also save/load armor content?

    An edit:
    https://gist.github.com/ToniTang/97b240dc4dafbca89181

    So I've been trying to mess with it a little bit, however I don't think I'm making as much progress as I'd like to see for myself. I receive an NPE when executing the toBase64 method on line 44 (https://gist.github.com/ToniTang/97b240dc4dafbca89181#file-itemserialization-java-L44).

    The inventory I'm trying to save looks like this: http://i.imgur.com/LoaZlV8.png

    As you can see, helmets, leggings, and boots are empty. So I could see why it'd return an NPE, however I thought because of the null check this would be avoided.
     
Thread Status:
Not open for further replies.

Share This Page