Resource [Tutorial]The Complete Guide to ItemStack NBTTags & Attributes!

Discussion in 'Spigot Plugin Development' started by kowagatte, Mar 15, 2016.

  1. Disclaimer: "This is my first tutorial/resource I have ever made. ALL comments are appreciated especially ones that can improve the tutorial or rate it. (Please don't mind my english, I know I suck at it.) Thanks for reading.
    JNBT Library: https://sourceforge.net/p/jnbt/code/HEAD/tree/src/org/jnbt/

    When dealing with NBTTags NMS is required.

    Contents:

    • What is NBT?
    • Storing your own data in NBT
    • Guide to NBTTags & Attributes
    • Adding Enchantments using NBTTags
    • Manipulating Shulker ItemStack inventories using NBTTags
    What is NBT?
    NBT (Named Binary Tag) is a tag based binary format designed to carry large amounts of binary data with smaller amounts of additional data. [e] The format is designed to store data in a tree structure made up of various tags. [e] This format is what all items in Minecraft is made up of. Every item inside of Minecraft has a NBTCompound that make up its lore, name, damage value, attack_speed, the slot it is in, the amount.

    Storing your own data in NBT.
    You may have come to a point where you wanted to store data in a Itemstack you need to access later. NBT tags are perfect for this, because this is what Mojang programmed them for. This can be done by creating an item,
    Code (Text):
    ItemStack apple = new ItemStack(Material.APPLE);
    Getting it's nmsCopy,
    Code (Text):
    net.minecraft.server.v1_12_R1.ItemStack nmsApple = CraftItemStack.asNMSCopy(apple);
    Getting the master compound and if there isn't making a new one.
    Code (Text):
    NBTTagCompound applecompound = (nmsApple.hasTag()) ? nmsApple.getTag() : new NBTTagCompound();
    Now in this compound you can set any tag, to any any value. Ex)
    Code (Text):
    applecompound.set("yourTagHere", new NBTTagString("Save your data in here."));
    When you are checking and modifying this tag it is Case-Sensitive, please be careful Using this.
    Next you can set the items tag to the Compound you created and
    modified.
    Code (Text):
    nmsApple.setTag(applecompound);
    You lastly need to turn it back to a BukkitCopy, by using this method.
    Code (Text):
    CraftItemStack.asBukkitCopy(nmsApple)
    You can retrieve stored data by getting the items compound again, and using the respective #get(); methods to retrieve the data from your tag.
    Code (Text):
    String string = applecompound.getString("yourTagHere");
    And that's pretty much it, it's fairly simple!


    The Complete Guide to Item NBTTags & Attributes!

    So to start we need to create an ItemStack to add NBTTags to, (This should be self explanatory but i'll go through it for the sake of something to do.
    Code (Java):
    ItemStack item = new ItemStack(material.DIAMOND_SWORD, 1); //Creating new item.
    When adding NBTTags to an itemstack you have to edit and set the itemmeta BEFORE you start editing the NBTTags. So we will name our item and lore before hand.
    Code (Java):
    ItemMeta itemmeta = item.getItemMeta();
    itemmeta.setDisplayName("Cool Sword");
    itemmeta.setLore(Arrays.asList("This is a cool sword." ));
    item.setItemMeta(itemmeta);
    Time to start with NMS, first we are going to create an NMS copy of the itemstack we just created.
    Code (Java):
    net.minecraft.server.v1_12_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(item);
    In this line above we crate an NMS ItemStack named nmsStack which is a copy of the itemstack we created earlier. Now that we have a NMS Itemstack we can start add and editing the Tags & Attributes of the NMS Copy! Next we need to grab the NMS items compound. And, An NMS itemstacks compound can be NULL so we need to check for it.
    Code (Java):
    NBTTagCompound compound = (nmsStack.hasTag()) ? nmsStack.getTag() : new NBTTagCompound();
    Ok, the last thing we need to add before we can start adding attributes and tags is the list of tags.
    Code (Java):
    NBTTagList modifiers = new NBTTagList();
    We will be adding all attributes to this list so we can apply them to the NMS stack.
    Finally we can now create an attribute to add to the itemstack. I will show you how to edit the DAMAGE attribute, but it is the same process for Speed, AttackSpeed and Health.
    First we will create the damage compound.
    Code (Java):
    NBTTagCompound damage = new NBTTagCompound();
    And now we can start editing it.
    Code (Java):
    damage.set("AttributeName", new NBTTagString("generic.attackDamage"));
    damage.set("Name", new NBTTagString("generic.attackDamage"));
    damage.set("Amount", new NBTTagInt(20 /*This is the Amount of the attribute aka how much damage the item will have*\));
    damage.set("Operation", new NBTTagInt(0));
    damage.set("UUIDLeast", new NBTTagInt(894654));
    damage.set("UUIDMost", new NBTTagInt(2872));
    This is the newest edition in 1.9 Not adding this next line will cause it to apply to all slots.
    Code (Java):
    damage.set("Slot", new NBTTagString("mainhand"));
    Example:
    [​IMG]
    With this line. It will only apply to main hand like so:
    [​IMG]


    UUID Range
    Items with the same UUID Range will overwrite one another, to avoid this use seperate UUID ranges based on whether it is a helmet, chestplate, legs, boots.
    This was made aware to me by this Post


    Example of a set of valid UUIDs that can be applied to the playe for armor:
    Code (Text):
    {UUIDLeast:1,UUIDMost:1}
    {UUIDLeast:1,UUIDMost:2}
    {UUIDLeast:2,UUIDMost:1}
    {UUIDLeast:2,UUIDMost:2}
    This is a list of all the slots:
    mainhand
    offhand
    feet
    legs
    chest
    head
    Next we add our damage compound to our NBTList.
    Code (Java):
    modifiers.add(damage);
    We then add our NBTList to out compound we created earlier.
    Code (Java):
    compound.set("AttributeModifiers", modifiers);
    We can then set out NMS items compound to our newly edited compound.
    Code (Java):
    nmsStack.setTag(compound);
    And last but not least we create a bukkit copy of our NMS copy so we can give it to players as a normal itemstack.
    Code (Text):
    item = CraftItemStack.asBukkitCopy(nmsStack);
    And now the item should have 20 Damage while in MainHand.


    You can also add unbreakable like this:
    Code (Java):
    compound.set("Unbreakable", new NBTTagByte((byte) 1));
    Before setting the NmsTag as our compound.

    Attributes:
    Attributes are cool in the way they are weird. Currently from my experience and information the working NBT the attributes that affect ItemStacks are
    generic.attackDamage
    generic.attackSpeed
    generic.maxHealth
    generic.movementSpeed
    generic.armor
    generic.luck

    1. maxHealth: Changes the players max health while equipped in specified slots.
    2. movementSpeed:
      [​IMG]
    3. armor: When equipped on specified slot adds armor points to player.
    4. luck:
    5. attackSpeed: affects the attackspeed of the user while swinging the item as long as it is in the specified slot.
    Spigot has a list of all the current attributes applicable to everything. They are as follows:
    GENERIC_ARMOR
    Armor bonus of an Entity.
    GENERIC_ATTACK_DAMAGE
    Attack damage of an Entity.
    GENERIC_ATTACK_SPEED
    Attack speed of an Entity.
    GENERIC_FOLLOW_RANGE
    Range at which an Entity will follow others.
    GENERIC_KNOCKBACK_RESISTANCE
    Resistance of an Entity to knockback.
    GENERIC_LUCK
    Luck bonus of an Entity.
    GENERIC_MAX_HEALTH
    Maximum health of an Entity.
    GENERIC_MOVEMENT_SPEED
    Movement speed of an Entity.
    HORSE_JUMP_STRENGTH
    Strength with which a horse will jump.
    ZOMBIE_SPAWN_REINFORCEMENTS
    Chance of a zombie to spawn reinforcements.


    NBTTags added in 1.11:
    Exactly how we edited the unbreakable tag earlier we can edit the new Tags added in 1.11.
    The new tags added in 1.11 are;

    • LocName:
    • CustomPotionColor:
    The colors for potions are handled the exact same way Leather Armor color is, Benchmark colors are: I used this site to generate the ids, http://minecraft.tools/en/armor.php
    Color codes are calculated from the Red, Green and Blue components using this formula:
    Red<<16 + Green<<8 + Blue
    1. Red: 10502464
    2. Blue: 4213152
    3. Cyan: 4235168
    4. Yellow: 16446520
    5. Pink: 16398536
    6. Purple: 9635266
    7. Green: 377379
    8. White: 16777215
    9. Black: 0
    • ColorMap:
    An example is presented above but for the sake of something to do here is another:
    Code (Text):
            NBTTagCompound speed = new NBTTagCompound();
            speed.set("AttributeName", new NBTTagString("generic.attackSpeed"));
            speed.set("Name", new NBTTagString("generic.attackSpeed"));
            speed.set("Amount", new NBTTagDouble(0.01));
            speed.set("Operation", new NBTTagInt(0));
            speed.set("UUIDLeast", new NBTTagInt(894654));
            speed.set("UUIDMost", new NBTTagInt(2872));
            speed.set("Slot", new NBTTagString("mainhand"));
    Like above you can see that not much is different than the damage attribute. UUID's do not differ between attributes. The only noticeable differences is the AttributeName, Name and Amount. Since damage is saved as an int, but attackSpeed is saved as a double we just change NBTTagInt to NBTTagDouble, and changed the attribute names. IF you where to add this new compound to the modifiers it would apply to the item.

    Adding Enchantments using NBTTags.
    To add enchantments using NBTTags we can do the same we have done before and get the ItemStack as an NMSStack and get the compound.
    Code (Text):
    net.minecraft.server.v1_11_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(ItemStack);
    NBTTagCompound compound = (nmsStack.hasTag()) ? nmsStack.getTag() : new NBTTagCompound();
    Then we can create a new NBTTagList:
    Code (Text):
    NBTTagList ench = new NBTTagList();
    This is a list of Enchantments stored inside the item.
    Next we can create our Enchantment compound to add to our list.
    Code (Text):
    NBTTagCompound enchant = new NBTTagCompound();
    enchant.set("id", new NBTTagInt(EnchantmentID));
    enchant.set("lvl", new NBTTagInt(EnchantmentLevel));
    Now that we have created our enchantment compound and all the necessary tags that are required we can add it to the list and then add the list to our items original compound.
    Code (Text):
    ench.add(enchant);
    compound.set("ench", ench);
    The TagList can be called anything but it must be set under the string "ench" since that is where minecraft looks to find the list of enchantments on an item. To finish up and see your results you can set the Items tag and save the NMSCopy as a BukkitCopy.
    Code (Text):
    nmsStack.setTag(compound);
    ItemStack  = CraftItemStack.asBukkitCopy(nmsStack);

    Now you have successfully added enchantments to an ItemStack using NBT!

    If you register your own Enchantment and add it to the item you can have the enchantment glow on an item without seeing the enchantment lore. It is pretty sweet and fun to play around with.

    Manupulating Shulker Boxes as ItemStacks:
    Shulker Box NBT Structure
    Item NBT Structure
    As requested by @Keehl254 here is my research done on how to create a Shulker_Box's Inventory.

    First we get the shulkerbox and turn it into an NMSCopy in this case I use the getItemInMainHand method and just assume the player is holding a shulkerbox, PLEASE MAKE SURE he is I have no idea what will happen. But probably bad news.
    Code (Text):
    net.minecraft.server.v1_11_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(player.getInventory().getItemInMainHand());
    NBTTagCompound compound = (nmsStack.hasTag()) ? nmsStack.getTag() : new NBTTagCompound();
    After some digging I learned that Shulker box's store their inventory in a compound called the BlockEntityTag, So we grab that compound from our original items compound and declare it.
    Code (Text):
    NBTTagCompound bet = compound.getCompound("BlockEntityTag");
    Next we can create a fresh new list of items to put into our shulkerbox.
    Code (Text):
    NBTTagList items = new NBTTagList();
    If you don't want to create an all new List of items you could get the Items list already on the Shulker box by using.
    Code (Text):
    NBTTagList items = (NBTTagList)bet.get("Items");
    We can blind cast here because if the item is a Shulker Box, the tag Items is by default an NBTTagList and shouldn't be anything else previously unless changed by you.

    Next despite either inventory we can create our item to add to the Shulker Box inventory.
    This is the creation of an item from scratch.
    Code (Text):
    NBTTagCompound item = new NBTTagCompound();
    item.set("Count", new NBTTagByte((byte)1));
    item.set("Slot", new NBTTagByte((byte)0));
    item.set("id", new NBTTagString("OBSIDIAN"));
    Next we just add our item to the NBTTagList of items.
    Code (Text):
    items.add(item);
    We can then set our Compound BlockEntityTag's "Items" tag to our new NBTTagList
    Code (Text):
    bet.set("Items", items);
    Set our BlockEntityTag to our manipulated BlockEntityTag
    Code (Text):
    compound.set("BlockEntityTag", bet);
    Set our original items compound tag to our master compound
    Code (Text):
    nmsStack.setTag(compound);
    Then just set the shulkerbox in the players hand to the new shulkerbox and the lore should change to your new contents and be placeable with your custom inventory.
    Code (Text):
    player.getInventory().setItemInMainHand(CraftItemStack.asBukkitCopy(nmsStack));
    Now you can place your updated shulker box with your custom inventory!
    Those looking for a little laugh here is my thoughts on shulker boxes.
    https://www.spigotmc.org/threads/maximum-inventory-space.202698/

    Getting ShulkerBox inventories from itemstack.
    Code (Text):

        public Inventory getBoxInventory(ItemStack item){
            net.minecraft.server.v1_12_R1.ItemStack nmsStack = nmsStack = CraftItemStack.asNMSCopy(item);
            NBTTagCompound compound = (nmsStack.hasTag()) ? nmsStack.getTag() : new NBTTagCompound();
            Inventory inv = Bukkit.createInventory(null, 27, "Shulker Box");
            NBTTagCompound bet = compound.getCompound("BlockEntityTag");
            NBTTagList items = (NBTTagList)bet.get("Items");
            try{
                for(int i = 0; i < items.size(); i++){
                    NBTTagCompound item = items.get(i);
     
                    inv.setItem(item.getInt("Slot"), CraftItemStack.asBukkitCopy(new net.minecraft.server.v1_12_R1.ItemStack(item)));
                }
                return inv;
            }catch(NullPointerException e){
                return inv;
            }
        }
     

    Hope I helped you enjoy this tutorial! FIN

    Useful Links:
    http://minecraft.tools/en/armor.php
    http://minecraft.gamepedia.com/NBT_format
    http://minecraft.gamepedia.com/Player.dat_format#Item_structure
    http://minecraft.gamepedia.com/Chunk_format

    https://sourceforge.net/p/jnbt/code/HEAD/tree/src/org/jnbt/
    http://minecraft.gamepedia.com/1.11
    http://minecraft.gamepedia.com/Tutorials/Command_NBT_tags

    http://www.minecraftforum.net/forum...488148-1-9-nbt-changes-and-additions#AllItems
    http://minecraft.gamepedia.com/Attribute
    https://minecraft.gamepedia.com/1.12
    https://minecraft.gamepedia.com/NBT_tag
     
    #1 kowagatte, Mar 15, 2016
    Last edited: Mar 3, 2019
    • Informative x 36
    • Useful x 21
    • Like x 13
    • Agree x 4
    • Winner x 3
  2. This is an amazing tutorial! Very informative!
     
    • Agree Agree x 4
    • Friendly Friendly x 2
    • Useful Useful x 1
  3. Thanks.
     
    • Like Like x 1
  4. Choco

    Moderator

    Hm. Haven't yet seen a tutorial on this. Very useful indeed :)
    Though I just want to note, when adding your lore, you're using the following:
    Code (Java):
    meta.addLore(Arrays.asList(new String[]{ "Your lore, blah" }))
    You don't need a new String array. Just list the Strings as lines in the Arrays.asList() method like so:
    Code (Java):
    meta.addLore(Arrays.asList("Line 1", "Line 2", "Line 3"))
    Other than that, I learned some new things :D Thanks
     
    • Like Like x 1
  5. Good tutorial! One thing, I notice that here:
    You really make it more complicated than it has to be, as all that can be simplified to a ternary expression. Like so:
    Code (Text):
    NBTTagCompount tag = (nmsStack.hasTag()) ? nmsStack.getTag() : new NBTTagCompound();
    Otherwise, good tutorial!
     
    • Like Like x 1
    • Agree Agree x 1
  6. Thank you guys a ton for your responses and comments, I'm learning as well which is awesome, I'll fix it up in a few moments.
     
    • Friendly Friendly x 4
  7. Haven't read it yet, but read the title and you are a beautiful person.
    All the people who will find this useful love you.
     
    • Friendly Friendly x 1
    • Optimistic Optimistic x 1
  8. You're my hero
    Please stay with me
     
    • Friendly Friendly x 1
  9. Great tutorial!

    Do you know if there's a good list for what attributes are OK to put on items and what the corresponding UUIDs would be?

    This is the best I could find, at the bottom: http://minecraft.gamepedia.com/Attribute

    But it doesn't say how to do, for instance, movementSpeed though I know that's possible.
     
  10. Currently to my knowledge all working generics on items are:
    generic.attackDamage
    generic.maxHealth
    generic.movementSpeed
    generic.armor
    generic.luck
    UUID through my research do not correspond with the attribute type. As long as two are not the same. I will write down the max values for each one after it seems useful on this tutorial. Hope I answered your questions.
    Code (Text):
            NBTTagCompound speed = new NBTTagCompound();
            speed.set("AttributeName", new NBTTagString("generic.attackSpeed"));
            speed.set("Name", new NBTTagString("generic.attackSpeed"));
            speed.set("Amount", new NBTTagDouble(0.1));
            speed.set("Operation", new NBTTagInt(0));
            speed.set("UUIDLeast", new NBTTagInt(894654));
            speed.set("UUIDMost", new NBTTagInt(2872));
            speed.set("Slot", new NBTTagString("mainhand"));
    would work if you added it to the attributes list on the item.

    NBT tags are something with little to no tutorials and information on them, I will test out everything and post all info I find here.
     
  11. Thanks! Yes that is helpful- for me the main confusion is where these UUID's come from. Looking through NMS code it's not really organized at all- they seem to be scattered about. :\
     
  12. Yea, I haven't had conflict with it, but there's not much on it. I found a lot of posts from various places saying they don't really matter as long as they are all different. :/
     
    • Informative Informative x 1
  13. Aren't UUIDs made of two longs...?
     
  14. I've always wondered why they have to have UUIDs in the first place .. seems really weird. Like why not just identify by the attribute id? :|

    Good to know they maybe don't matter, though .. I'd be a little afraid to just randomly generate one each time, since the vanilla code seems to have assigned static UUID's for the different modifiers.
     
  15. A UUID is just a 128-bit value. I have little to know Idea why Mojang made it this way. It seems like a really weird way to do things.
     
  16. Nice, now to got for ProtocolLib ;)
     
  17. Unbreakable can also be set with .spigot().setUnbreakable(true);
     
    • Agree Agree x 1
  18. Indeed, the unbreakable part is a bukkit alternative and just a showcase on how to set NBT tags as well as attributes!
     
  19. Daily, bump. Keeping this noticeable so people who need it can find it ^.^
     
    • Like Like x 1
  20. Bump, Keepin it noticeable.