GetNBTData for Blocks

Discussion in 'Plugin Development' started by Weasel_Squeezer, May 26, 2013.

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

    Weasel_Squeezer

    I am trying to edit the WorldEdit schematic save function so that it could be used mostly outside of the WorldEdit API mainly because I cannot figure out how to create the objects the WorldEdit save method has as parameters.
    The original WorldEdit save method takes the following parameters:
    save(CuboidClipboard clipboard, File file)
    Saves the data from the specified CuboidClipboard to the given file, overwriting any existing data in the file

    For the life of me, I cannot create a CuboidClipboard object with two Locations (max and min), so instead, I am trying to edit the WorldEdit schematic save method to take the following parameters:

    Code:java
    1. public void save(int width, int height, int length, Location origin, File file) throws IOException, DataException {}

    The CuboidClipboard object was able to retrieve the width, height, length, and origin with WorldEdits vectors, but I have all of those values on-hand easily.
    I am able to convert the method for the most part except for retrieving the blocks NBTId and NBTData for the TileEntityBlocks.
    WorldEdit has its own constructor classes for Minecraft blocks which has a method for getting the NBT values, but there are only interfaces of those methods in the source. I cannot find it.
    So I am using Bukkits Block class and need to retrieve the NBT values.

    Below is what I am working with. I have commented all the original WorldEdit code which has been changed and show what I changed it too.

    Code:java
    1. public void save(int width, int height, int length, Location origin, File file) throws IOException, DataException {
    2. // Do not need to initialize the dimension values as they are now the parameters
    3. //int width = clipboard.getWidth();
    4. //int height = clipboard.getHeight();
    5. //int length = clipboard.getLength();
    6.  
    7. if (width > (Short.MAX_VALUE - Short.MIN_VALUE)) {
    8. throw new DataException("Width of region too large for a .schematic");
    9. }
    10. if (height > (Short.MAX_VALUE - Short.MIN_VALUE)) {
    11. throw new DataException("Height of region too large for a .schematic");
    12. }
    13. if (length > (Short.MAX_VALUE - Short.MIN_VALUE)) {
    14. throw new DataException("Length of region too large for a .schematic");
    15. }
    16.  
    17. HashMap<String, Tag> schematic = new HashMap<String, Tag>();
    18. schematic.put("Width", new ShortTag("Width", (short) width));
    19. schematic.put("Length", new ShortTag("Length", (short) length));
    20. schematic.put("Height", new ShortTag("Height", (short) height));
    21. schematic.put("Materials", new StringTag("Materials", "Alpha"));
    22.  
    23. //I changed this to get the int x, y, and z values from the Bukkit Location
    24. schematic.put("WEOriginX", new IntTag("WEOriginX", origin.getBlockX())); //clipboard.getOrigin().getBlockX()));
    25. schematic.put("WEOriginY", new IntTag("WEOriginY", origin.getBlockY())); //clipboard.getOrigin().getBlockY()));
    26. schematic.put("WEOriginZ", new IntTag("WEOriginZ", origin.getBlockZ())); //clipboard.getOrigin().getBlockZ()));
    27. //I do not need an offset so I just set this to 0
    28. schematic.put("WEOffsetX", new IntTag("WEOffsetX", 0)); //clipboard.getOffset().getBlockX()));
    29. schematic.put("WEOffsetY", new IntTag("WEOffsetY", 0)); //clipboard.getOffset().getBlockY()));
    30. schematic.put("WEOffsetZ", new IntTag("WEOffsetZ", 0)); //clipboard.getOffset().getBlockZ()));
    31.  
    32. // Copy
    33. byte[] blocks = new byte[width * height * length];
    34. byte[] addBlocks = null;
    35. byte[] blockData = new byte[width * height * length];
    36. ArrayList<Tag> tileEntities = new ArrayList<Tag>();
    37.  
    38. for (int x = 0; x < width; ++x) {
    39. for (int y = 0; y < height; ++y) {
    40. for (int z = 0; z < length; ++z) {
    41. int index = y * width * length + z * width + x;
    42.  
    43. // I cannot use clipboard, and I cannot convert Bukkit Block to WorldEdit BaseBlock
    44. // But from the looks of it, this line just gets the block at the x,y,z coordinates of the Cuboid Area. Not the Minecraft world
    45. //BaseBlock block = clipboard.getPoint(new BlockVector(x, y, z));
    46.  
    47. //So I get the block at the current point in the Cuboid region by adding the x, y, and z values to the origin coordinates
    48. Location currentLoc = new Location(origin.getWorld(), origin.getBlockX() + x, origin.getBlockY() + y, origin.getBlockZ() + z);
    49. Block block = currentLoc.getBlock();
    50.  
    51.  
    52. // Save 4096 IDs in an AddBlocks section
    53. // I change the getId() methods for Bukkits Block class
    54. //if (block.getType() > 255) {
    55. if (block.getTypeId() > 255) {
    56. if (addBlocks == null) { // Lazily create section
    57. addBlocks = new byte[(blocks.length >> 1) + 1];
    58. }
    59.  
    60. //I changed from getType() to getTypeId() again for Bukkits Block class
    61. addBlocks[index >> 1] = (byte) (((index & 1) == 0) ?
    62. addBlocks[index >> 1] & 0xF0 | (block.getTypeId() >> 8) & 0xF
    63. : addBlocks[index >> 1] & 0xF | ((block.getTypeId() >> 8) & 0xF) << 4);
    64. }
    65.  
    66. // change the same getId() method again...
    67. blocks[index] = (byte) block.getTypeId();
    68. blockData[index] = (byte) block.getData();
    69.  
    70. // Store TileEntity data
    71.  
    72. /*
    73.   * HERE IS WHERE THE PROBLEM IS
    74.   * Here the WorldEdit code creates a TileEntityBlock from the BaseBlock class so that
    75.   * it can get the NbtId and NbtData of the TileEntityBlock. There is no method in the Bukkit
    76.   * Block class that I know about, so I need to find a different way to get these NBT values
    77.   */
    78.  
    79. if (block instanceof TileEntityBlock) {
    80. TileEntityBlock tileEntityBlock = block;
    81.  
    82. // Get the list of key/values from the block
    83. CompoundTag rawTag = tileEntityBlock.getNbtData();
    84. if (rawTag != null) {
    85. Map<String, Tag> values = new HashMap<String, Tag>();
    86. for (Entry<String, Tag> entry : rawTag.getValue().entrySet()) {
    87. values.put(entry.getKey(), entry.getValue());
    88. }
    89.  
    90. values.put("id", new StringTag("id", tileEntityBlock.getNbtId()));
    91. values.put("x", new IntTag("x", x));
    92. values.put("y", new IntTag("y", y));
    93. values.put("z", new IntTag("z", z));
    94.  
    95. CompoundTag tileEntityTag = new CompoundTag("TileEntity", values);
    96. tileEntities.add(tileEntityTag);
    97. }
    98. }
    99. }
    100. }
    101. }
    102.  
    103. // The rest should be fine
    104.  
    105. schematic.put("Blocks", new ByteArrayTag("Blocks", blocks));
    106. schematic.put("Data", new ByteArrayTag("Data", blockData));
    107. schematic.put("Entities", new ListTag("Entities", CompoundTag.class, new ArrayList<Tag>()));
    108. schematic.put("TileEntities", new ListTag("TileEntities", CompoundTag.class, tileEntities));
    109. if (addBlocks != null) {
    110. schematic.put("AddBlocks", new ByteArrayTag("AddBlocks", addBlocks));
    111. }
    112.  
    113. // Build and output
    114. CompoundTag schematicTag = new CompoundTag("Schematic", schematic);
    115. NBTOutputStream stream = new NBTOutputStream(new FileOutputStream(file));
    116. stream.writeTag(schematicTag);
    117. stream.close();
    118. }


    I really need help on this if anyone has any idea. I have been stuck on being able to save schematics for the longest time...
     
  2. Offline

    Jogy34

  3. Offline

    Weasel_Squeezer

    Why would Bukkit Hide classes like these?? >.>
    It has been really hard to find good NBT libraries and documentation.
    thanks btw!
     
  4. Offline

    Jogy34

    I'm not sure why they wanted to hide it. Before they were hidden I was making a plugin that allowed you to directly edit the NBTTags of all TileEntities but then they stopped allowing us to be able to easily access them and that pissed me off and so I stopped making that. A little bit ago I needed to be able to change the title of a chest inventory but in order to do that I had to get the TileEntityChest from the NMS code however the CraftBukkit code doesn't allow for that so I spent a few hours trying to figure out a work around for it and I finally figured it out with the use of reflection, which is kind of sketchy but it works. I realized I could just do that for all of the TileEntity objects and then thought I probably wouldn't be the only person that wanted access to the TileEntities so I decided to make a library for it. I also added in infinite config generation to that because I knew people that wanted to do that also.

    Anyways thanks, and feel free to either link into the plugin or just copy the files you want directly over from the source as long as you give me credit for it.
     
  5. Offline

    Weasel_Squeezer

    Would there be a way to get the NBT values of the TileEntity without checking for every kind of TileEntity?

    I will most certainly give you credit if I use your code :p
    Awesome work! its ridiculous that you had to use reflection though!

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 1, 2016
  6. Offline

    Jogy34

    I was about to say no to that but then I realized that everything that is a TileEntity is a BlockState in the bukkit API so I'll just write up a method real quick to do that. Give my like 10 minutes.
     
  7. Offline

    Weasel_Squeezer

    Oh you're right! haha well you are very helpful! thanks!
     
  8. Offline

    Jogy34


    Wow, I don't know why I wanted to use BlockStates when I could have just compared the bock materials but whatever. It's updated and now has a getGeneralTileEntity() method which just returns a base TileEntity object. It also doesn't throw the exception and it just returns null if the block at the sent in location isn't a TileEntity.
     
  9. Offline

    Weasel_Squeezer

    Awesome! Can you update your bitbukket repository with it too? :p
     
  10. Offline

    Jogy34

    Sorry, I did but I accidentally had the link go to the previous version. It should be fixed now.
     
  11. Offline

    Weasel_Squeezer

    Wait.. I am stuck.
    ok so I have this worldEdit code that I need to modify so that instead of using WorldEdit BaseBlock class, I use Bukkits Block class. Also, I have to use WorldEdits CompoundTag class because all of the NBT tag handling is done with world edit and I hate messing with NBT and bytes.
    Here is the section of the Save method that I need to change. this is the original WorldEdit code:

    Code:java
    1. BaseBlock block = clipboard.getPoint(new BlockVector(x, y, z));
    2.  
    3. if (block instanceof TileEntityBlock) {
    4. TileEntityBlock tileEntityBlock = block;
    5.  
    6. // Get the list of key/values from the block
    7. CompoundTag rawTag = tileEntityBlock.getNbtData();
    8. if (rawTag != null) {
    9. Map<String, Tag> values = new HashMap<String, Tag>();
    10. for (Entry<String, Tag> entry : rawTag.getValue().entrySet()) {
    11. values.put(entry.getKey(), entry.getValue());
    12. }
    13.  
    14. values.put("id", new StringTag("id", tileEntityBlock.getNbtId()));
    15. values.put("x", new IntTag("x", x));
    16. values.put("y", new IntTag("y", y));
    17. values.put("z", new IntTag("z", z));
    18.  
    19. CompoundTag tileEntityTag = new CompoundTag("TileEntity", values);
    20. tileEntities.add(tileEntityTag);
    21. }
    22. }


    I need to change this code to use the Bukkit block variable as you see below:
    Code:java
    1. Location currentLoc = new Location(origin.getWorld(), origin.getBlockX() + x, origin.getBlockY() + y, origin.getBlockZ() + z);
    2. Block block = currentLoc.getBlock();
    3.  
    4. if (block instanceof TileEntityBlock) {
    5. TileEntityBlock tileEntityBlock = block;
    6.  
    7. // Get the list of key/values from the block
    8. CompoundTag rawTag = tileEntityBlock.getNbtData();
    9. if (rawTag != null) {
    10. Map<String, Tag> values = new HashMap<String, Tag>();
    11. for (Entry<String, Tag> entry : rawTag.getValue().entrySet()) {
    12. values.put(entry.getKey(), entry.getValue());
    13. }
    14.  
    15. values.put("id", new StringTag("id", tileEntityBlock.getNbtId()));
    16. values.put("x", new IntTag("x", x));
    17. values.put("y", new IntTag("y", y));
    18. values.put("z", new IntTag("z", z));
    19.  
    20. CompoundTag tileEntityTag = new CompoundTag("TileEntity", values);
    21. tileEntities.add(tileEntityTag);
    22. }
    23. }


    So using your class, how could I get the NBTTagCompound? I have:
    TileEntities.getGeneralTileEntity(currentLoc).b(nbttagcompound);
    and how would I use this to replace "CompoundTag rawTag = tileEntityBlock.getNbtData();" in the worldEdit code? CompoundTag is a HashMap<String, Tag> I would need to convert it to that.
    This hurts my head >.>
     
  12. Offline

    Jogy34

    I'm not exactly sure what you are trying to do but to get the NBTTags for a TileEntity it would be something like this:
    Code:
    NBTTagCompound tag = new NBTTagCompound(); //Creates a blank tag compound
    TileEntity te = TileEntities.getGeneralTileEntity(currentLoc); //Get's the TileEntity
    if(te != null) te.b(tag); //Sets the tag's fields to that of the TileEntity
    
    from there you can just call getType[eg.String, Int, Long, etc...](String name); to get the values in there. Depending on the TileEntity it has different NBTTags. You can check those out here.

    NBTTags aren't actually as hard as you think. It's basically just a list of Objects.
     
  13. Offline

    Weasel_Squeezer

    How would I know what value to get? like when calling: .b(compound).get[getwhat??]
    I am iterating through thousands of blocks and I dont know if one will be a TileEntity or not.
    For example, if I want to get the NBT values for a chest which look something like:
    [​IMG]

    how would I be able to get all of those lists?
     
  14. Offline

    Jogy34

  15. Offline

    Weasel_Squeezer

    I know what a collection is haha. I am just really tired of trying to figure this out. thanks for your help though! I will get figure it out some time. At least I am now able to save a schematic with all the proper values except for TileEntities
     
  16. Offline

    Jogy34

    Well I don't actually know many people that really use Collections, I've never actually used them before any ways. And for saving the NBTTags you would just go through the collection and get the value that corresponds to that and you would want to make sure that you don't get another NBTTagCompound or an NBTTagList. From there you would save it's value.
     
  17. Offline

    Weasel_Squeezer

    WOW. I found the problem! now I don't feel bad because the problem is WorldEdits problem. WorldEdit doesn't properly load the schematics TileEntities! I tried using worldEdit's load command and My own which is just the modified WorldEdit load method. I got the save method working and I checked the schematic files and the TileEntity entries are exactly how they should be with my modified WorldEdit Save method. Stupid WorldEdit >.>

    anyway, Thank you so much for your help!
     
  18. Offline

    Jogy34

    No problem
     
Thread Status:
Not open for further replies.

Share This Page