Resource [1.8-1.14] Single Class NBT Editor for items, skulls, mobs, and tile entities!

Discussion in 'Spigot Plugin Development' started by BananaPuncher714, Aug 30, 2017.


  1. Intro

    I read this post here while I was trying to figure out how to detect whether an item was custom and set/get values from it. Some code was provided but it was all in NMS. I have successfully made a reflection version which you can implement very easily into your plugins. Now the need for hidden lore is gone!! This single class works with 1.8-1.13 and can be used to edit item, entity, and block tags!

    What are NBT tags?

    NBT stands for "Named Binary Tag" which is used by Minecraft to store information about various things. Here are two pictures of an item, one with an nbt tag, and one without.
    No NBT:
    [​IMG]
    Yes NBT:
    [​IMG]

    NBT is used for specifying various aspects of mobs, and states of blocks. They are also used in items, such as the enchantment, lore, and various other custom data. You can find a list of all tags here:
    For a more detailed explanation on what they are and how it's done with NMS, check out the tutorial by @kowagatte for his excellent tutorial on how to edit them!

    What object types are supported by NBT?
    • Strings
    • Ints
    • Longs
    • Floats
    • Shorts
    • Bytes - Use this for boolean values
    • Byte arrays - This means object serialization!!!
    • Int arrays
    How to use NBTEditor?
    Let's first start with editing an ItemStack. You can add as many custom tags onto ItemStacks as you want.

    First we set an owner tag so we can easily identify it later on:
    Code (Java):
    item = NBTEditor.set( item, "BananaPuncher714", "any custom string key", "item", "owner" );

    We can also edit normal tags, like the Unbreakable tag
    Code (Java):
    item = NBTEditor.setItemTag( item, ( byte ) 1, "Unbreakable" );
    If we wanted to get the owner tag, we'd first have to check if it contains the tag, then we can use the NBTEditor.getString() method to fetch it like so:
    Code (Java):
    if ( NBTEditor.contains( item, "any custom string key", "item", "owner" ) ) {
        String owner = NBTEditor.getString( item, "any custom string key", "item", "owner" );
    }

    Here is an example to edit the attribute modifiers of an item:
    Code (Java):
    item = NBTEditor.set( item, "generic.attackDamage", "AttributeModifiers", null, "AttributeName" );
    item = NBTEditor.set( item, "generic.attackDamage", "AttributeModifiers", 0, "Name" );
    item = NBTEditor.set( item, "mainhand", "AttributeModifiers", 0, "Slot" );
    item = NBTEditor.set( item, ( double ) 20, "AttributeModifiers", 0, "Amount" );
    item = NBTEditor.set( item, 0, "AttributeModifiers", 0, "Operation" );
    item = NBTEditor.set( item, ( long ) 894654, "AttributeModifiers", 0, "UUIDLeast" );
    item = NBTEditor.set( item, ( long ) 2872, "AttributeModifiers", 0, "UUIDMost" );
    First we create a new index in the AttributeModifiers list, with the null key. Then we specify the element we just created with the 0 index. This will make the item do 20 more damage.

    You can also remove elements by simply specifying null as the value.
    Code (Java):
    item = NBTEditor.set( item, null, "AttributeModifiers", 0 );


    ItemStacks can also be converted into an NBTCompound and back like so:
    Code (Java):
    NBTCompound compound = NBTEditor.getItemNBTTag( item );
    ItemStack item = NBTEditor.getItemFromTag( compound );
    This will become more useful on later since we can use this to insert ItemStacks directly into other items/entities/blocks.

    Now, what happens if you try to get a tag and it is not a primitive? Then in that case, it will return a recursive map or list of the compound you are trying to get. If you want to get an NBTCompound instead, then you'd have to specifically use the NBTEditor.getItemNBTTag() method with the proper keys.
    Let's edit an entity or block. Note that for these, you cannot add custom tags! If you want to add custom data to entities, then you'd have to use the Tags tag, which is a list of custom string data.

    If we wanted to set the mob to have no ai, we can set the Minecraft tag NoAI to 1b like so:
    Code (Java):
    NBTEditor.set( entity, ( byte ) 1, "NoAI" );
    Note that you must cast the value to the proper type as required by the entity tags or it may not work!

    We can edit blocks similarly, but note that these only work for tile entities. Here is how we'd set the lock on a chest or other inventory holder:
    Code (Java):
    NBTEditor.set( block, "A custom lock", "Lock" );

    Now this is where the previous NBTCompound from an item comes into use. With the NBTCompound obtained from an item, we can insert it into an entity or a chest like so:
    Code (Java):
    NBTEditor.set( chest, nbtCompoundCreatedFromItemStack, "Items", 0 );

    NBTEditor.set( zombie, nbtCompoundCreatedFromItemStack, "HandItems", 0 );
    This will set the first slot in a chest to the item we got the compound from, and make a zombie hold the item in their main hand.

    Likewise, we can also do the opposite and get items from the chest and zombie:
    Code (Java):
    ItemStack chestItem = NBTEditor.getItemFromTag( NBTEditor.getBlockNBTTag( chest, "Items", 0 ) );

    ItemStack heldItem = NBTEditor.getItemFromTag( NBTEditor.getEntityNBTTag( zombie, "HandItems", 0 ) );
    There are many Bukkit methods that should be used to get and set items from blocks/entities, but this provides you a way to support methods that may not be present in old versions, and can also let you do interesting things like manipulate a villager's trade in ways not allowed with Bukkit.
    A nice feature of NBTEditor that isn't quite directly part of NBT is the skull texture feature. This resource lets you set skull textures, get skull textures from items, and set the texture of skull blocks

    Note that our textures MUST be in URL format, not encoded, such as http://textures.minecraft.net/texture/894497788b3002c9da34b299116acff6b7b51395fc70aebd1a0a71486df25c87. Most skull websites should provide this URL, or a base64 string that looks something like this: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODk0NDk3Nzg4YjMwMDJjOWRhMzRiMjk5MTE2YWNmZjZiN2I1MTM5NWZjNzBhZWJkMWEwYTcxNDg2ZGYyNWM4NyJ9fX0=. If they give you the latter, then put it through a base64 decoder and find the URL that is stored inside.

    Once we have our URL, we can get a skull ItemStack like so:
    Code (Java):
    ItemStack skullItem = NBTEditor.getHead( url );
    where url is the URLmentioned above.

    But let's say you have a cool looking skull and you want to get the URL of that texture. Well, you can use this method to get it:
    Code (Java):
    String url = NBTEditor.getTexture( skullItem );
    The item must be a player's head.

    So, now that we know how to get and set the texture of a skull, how about a block? Skull blocks are slightly different but thankfully there is a method to set it.
    Code (Java):
    NBTEditor.setSkullTexture( skullBlock, url );
    The skullBlock must be a player's head too. As for getting the url of a skull block, I have not added that yet, but may do that in the future when I have more time and motivation. :coffee:
    Using NBT is extremely useful for marking items as custom, as well as giving individual items unique tags and attributes, and is made powerful by the fact that NBT format does not change much over versions, so tags on items from 1.8 will still be there in 1.13. Another great feature of using NBT is that the tags persist across server restarts.

    There are definitely instances in which using NBT may be less efficient and slightly more complex than using the Bukkit implementation, but there are also times when Bukkit doesn't cover what you want to do, particularly with editing items that are marked with NBT+ data, such as when you ctrl+middle click a chest to obtain a custom chest block. You can use NBTEditor to get and set items from within those items, and from shulker boxes as well.

    More information
    Be sure to check out some of my other open-source projects:
    • Locksmith(Github) - Directly uses NBTEditor to create a nice vanilla tile entity locking system
    • MDChat - Better formatting to turn regular strings into clickable/hoverable TextComponents
    • RadioBoard(Github) - Map displays done right to support GIFs and intricate interactive menus
    • Public Crafters(Github) - Taps directly into crafting tables to make them public with an awesome display
    • BrickBoard - Epic non-flicker and interactive chat GUI
    You can find the repo here: https://github.com/BananaPuncher714/NBTEditor

    Feel free to message me if you need any help understanding how to use this resource :).
    If you encounter any errors or bugs, tell me so I can fix it as quickly as possible! If you would also like additional features added, tell me too so I can add them!;)

    NOTE: In 1.13.2+ there are new methods to manipulate NBT without using NMS or reflection. Here is a nice tutorial on how they work and how to use them: https://www.spigotmc.org/threads/tutorial-storing-custom-data-on-itemstacks-in-1-13-2.354283/. These tags are accessible with under the "Bukkit" tag.
     
    #1 BananaPuncher714, Aug 30, 2017
    Last edited: Apr 2, 2019
    • Useful Useful x 9
    • Like Like x 4
    • Winner Winner x 3
  2. Nice! I'll have to try it out some time.
     
  3. Is this something that get's retained upon server restarts? Or do I have to make sure the plugin re-adds these tags every time? (Don't know too much about the tags).
     
  4. This is restart persistent:)
     
  5. Sounds nifty. Will try it out. :)
     
  6. Oh really? Since when does MC store custom NBT data?
     
  7. Reflection is expensive, you should cache all the fields and stuff, here is an example.
     
    • Agree Agree x 1
    • Informative Informative x 1
  8. Turn that short into an integer and for arrays, use Gson?
     
  9. MC does store custom NBT data in itemstack, it does not work on entities and block states
     
    • Informative Informative x 1
  10. You can use a NBTList if i'm not wrong.
     
  11. I would probably do something like that, if you want you can try adding the NBTTagList and such:). I just use this for specifying what items are custom and stuff like that.
    Wow I had no idea you already had such an excellent util! I did cache the classes in a HashMap, unless you meant something else?

    EDIT 1:
    Ok, I see what you mean now. I'll try to shove everything into one class.
     
    #11 BananaPuncher714, Aug 30, 2017
    Last edited: Aug 30, 2017
  12. I have merged the two classes together and cached the methods and fields thanks to @MaTaMoR_ 's advice. I have also added support for shorts and floats, and this one really works on almost any version.
     
  13. Since Minecraft Beta 1.3, it has always been the default way minecraft handles and saves item data ex; Enchantments on items.
    @BananaPuncher714
    My resource has been floating around for a long time on Item NBT and how to use it. Although this is very useful for multiple versions.
    https://www.spigotmc.org/threads/tu...guide-to-itemstack-nbttags-attributes.131458/
    I think it is a requirement for this resource to provide support for NBTTagList's if it doesn't already since they are required to change the attributes of an item (Attackspeed and damage).
     
    • Like Like x 1
  14. Thanks for your awesome tutorial! I have added all the features that should allow better NBT data manipulation:). I have also added support for Entity editing.
     
    #15 BananaPuncher714, Aug 31, 2017
    Last edited: Aug 31, 2017
  15. Does this work to edit NBTTagLists?
     
  16. Code (Java):

        public static Class<?> getPrimitiveClass( Class<?> clazz ) {
            if ( clazz.getSimpleName().equals( "Byte" ) )
                return byte.class;
            if ( clazz.getSimpleName().equals( "Short" ) )
                return short.class;
            if ( clazz.getSimpleName().equals( "Integer" ) )
                return int.class;
            if ( clazz.getSimpleName().equals( "Long" ) )
                return long.class;
            if ( clazz.getSimpleName().equals( "Character" ) )
                return char.class;
            if ( clazz.getSimpleName().equals( "Float" ) )
                return float.class;
            if ( clazz.getSimpleName().equals( "Double" ) )
                return double.class;
            if ( clazz.getSimpleName().equals( "Boolean" ) )
                return boolean.class;
            if ( clazz.getSimpleName().equals( "Void" ) )
                return void.class;
            return clazz;
        }
     
    That can be smaller

    Code (Java):

        public static Class<?> getPrimitiveClass( Class<?> clazz ) {
            return Primitives.unwrap(clazz);
        }
     
     
    • Like Like x 1
  17. Yes.:) You would provide null or an int as a key to create/edit a list.
     
  18. @BananaPuncher714 , such code:
    Code (Text):
    ItemStack item = new ItemStack( Material.WOOD_HOE, 1 );
            item = NBTEditor.setItemTag(item,"TestValue","TestKey");
            Object val = NBTEditor.getItemTag(item, "TestKey");
           
            System.out.println("test "+val);
    returns exactly
    am i doing something wrong?:eek::eek:
     
  19. Hmm, I'm not quite sure. Did this always occur and are you using the latest version?

    EDIT 1: Wait, I dun goofed and I will fix it unless you want to try yourself. Inside the setItemTag method, after assigning the tag, I forgot to call the "setTag" method. I will fix it asap

    EDIT 2: Fixed:)
     
    #20 BananaPuncher714, Sep 8, 2017
    Last edited: Sep 8, 2017
    • Like Like x 1