Custom Item Data (NBT) API

Discussion in 'Spigot Plugin Development' started by NathanWolf, Jan 12, 2015.

  1. NathanWolf



    I am extremely interested in submitting a PR for custom item data. The main goals of this patch would be, in order of importance:

    1. Ensure that any custom data attached to an ItemStack is maintained
    2. Serialize and deserialize custom Item data when using the Bukkit serialization API
    3. Provide API access to read and write all Item NBT data

    The goal here is to allow storage of arbitrary custom data- this is not really meant to be a way to edit the "raw" builtin (vanilla) NBT data, as there are already ways to do that via the API.

    I had previously submitted a PR to CraftBukkit and Bukkit for this- the was API built on the Metadata API.

    I'm not sure if this is the best fit for Spigot, and I am interested to hear what people would like. If you are interested in being able to reliably store data on an ItemStack, please leave a message here with any ideas or suggestions you have. I will take this into account as I put a PR together.

    Thank you!
    • Like Like x 7
    • Agree Agree x 4
    • Optimistic Optimistic x 1
    • Creative Creative x 1
  2. clip


    Great idea, hope others agree :)
    • Like Like x 1
    • Friendly Friendly x 1
  3. I think this is a great idea. I expected something like this to be in Bukkit in the first place.
    • Agree Agree x 1
    • Optimistic Optimistic x 1
  4. Do you mean something like:
    Code (Text):
    //Give some player this item
    ItemStack item = new ItemStack(Material.COMPASS, 1);
    item.addCustomMeta("someTrackingField", "someValue");

    if (item.getCustomMeta("someTrackingField") != null && item.getCustomMeta("someTrackingField").equals("someValue")) {
        //We have a boogie
    *Not 100% sure if thats is (or similar) but that would be cool regardless*
    • Agree Agree x 1
  5. NathanWolf


    Yes, exactly! And that data stays on the item no matter where it goes- stored in a chest, dropped on ground, serialized to config, etc.

    Here is a concrete example of using the API in the PR I submitted for Bukkit/CraftBukkit:

    It worked off of ItemMeta, and used the Metadata interface. So, for example:

    Code (Text):
            ItemMeta meta = heldItem.getItemMeta();
            if (meta.hasMetadata("test_fly")) {
                sendError(player, "That item is already a super awesome flying item");
            Map<String, Object> flyData = new HashMap<String, Object>();
            Date now = new Date();
            flyData.put("created", now.toString());
            flyData.put("uses", 0);
            flyData.put("speed", 2.0);
            meta.setMetadata("test_fly", new PersistentMetadataValue(this, flyData));
    This shows how to check for and set data. You can store anything serializable in a "PersistentMetadataValue", but you can't (for instance) put Entities or arbitrary objects on here like you can with Block or Entity Metadata. This is one way I feel this implementation is a little confusing.

    I originally submitted a very similar API, but using the Configuration interface instead of Metadata:

    The code is very similar, but you'd use ConfigurationSection instead of Map:

    Code (Text):
     ItemMeta meta = heldItem.getItemMeta();
            ConfigurationSection data = meta.getCustomData();
            if (data.contains("test_fly")) {
                sendError(player, "That item is already a super awesome flying item");
            ConfigurationSection flyData = data.createSection("test_fly");
            Date now = new Date();
            flyData.set("created", now.toString());
            flyData.set("uses", 0);
            flyData.set("speed", 2.0);
    In both cases, it's still easy to store simple primitive data in a key/value kind of way.

    The main reason I've made this thread is to try and feel out what kind of API people would want. The Bukkit team made me jump through some hoops with this PR and so I had to change it several times.

    I kind of feel like the original implementation was the cleanest and easiest to use, though I can see (and agree) that re-using the Configuration API like that was a little weird. Metadata wasn't a great fit, either- but it seems like overkill to make a whole new "store arbitrary types of structured data" API. I'd love some opinions now that this may have new life in Spigot!

    Thanks for your interest!
    • Like Like x 3
  6. I'd personally prefer the ConfigurationSection API (simple because I don't like Metadata-API).

    Anyway, it's a shame, something like this hasn't been implemented long time ago.
    • Agree Agree x 1
  7. YES. Please, so much can be done!
    • Like Like x 2
    • Agree Agree x 1
  8. That would allow to create a simple way to track Items or create custom ones. I luv it :) Make it a thing :D
  9. I use custom NBT data in one of my plugins to save an ID number on an item and then store a TON of variables in a save file. Working with NBT has always been finicky and getting the data silently erased is a problem i personally have made a ton of work arounds for.

    An API for this would be VERY useful!
    • Like Like x 1
  10. NathanWolf


    Nice to see someone else actually using NBT! I, too, am sick of having to work around creative mode, item dragging, etc. :)

    One of my main goals with the API is to also transparently fix this problem for devs like you (and I) who already use NBT data directly. The data will stay put, you can use creative mode without fear- you can even use a variety of other plugins (Shopkeepers, StarterKit) and find they now magically work with your custom item data (since they save and load items via the Bukkit serialization API).
  11. I currently store some data in lore, including other items on occasion. For this, I convert to hex, then prepend each hex char with a section sign to make it largely invisible to the client. I've noticed, however, that item lore has a max length of 1024 characters per line. Will a similar restriction apply, or will I be able to jam as much information as I need into a single tag?
  12. I don't think the client will be able to see these fields on the item due to it being server side.
    And you should be able to store an infinite amount of data because there are no client limitations.
  13. NathanWolf


    There is a practical limit on how much data you can (or should) stick in an NBT tag- I think this is one of the reasons Bukkit was hesitant to add an API for it :)

    In particular, I have run into an issue where an item with "too much" data in it would crash clients if it appeared in chat (in JSON form). I'm not sure how best to handle this, but your mileage may vary.

    This data does get sent to the client - at least as far as I know.
  14. NathanWolf


    It has come to my attention that there is already such an API in MultiCube, and a PR into Spigot to port the API over.

    Personally I want an API that supports structured data (MultiCube is just <string, string>) and, in particular, I want the data to serialize and deserialize, something MultiCube doesn't do.

    I'm working on getting my changes re-worked to play nicely with ThinkofDeath's changes to the internal ItemMeta class (to handle unknown data). I'm hoping to have something working once I get over various build and merge hurdles.
    • Like Like x 1
  15. NathanWolf


    Well, my PR is submitted- wish me luck! If you have dev access, please go take a look, comment, vote, etc.
  16. Whatever happened to this movement? Or is your PR still in cold storage?
  17. NathanWolf


    I closed it... the API side was not terribly well-received, NBT API's have always been a polarizing issue.

    I decided that I didn't need it anymore, the main draw for me was serialization of custom data, and ThinkOfDeath has added that as a Spigot feature.

    In other words, you can now get/set custom data via NMS access to NBT and it is more-or-less fully supported in Spigot, and that's good enough for me :)
    • Winner Winner x 1
  18. Could you provide me with a link to the commit (or point me to the class file / line). Would be much appreciated :p (and would save me the time it takes to go through the whole source)
  19. Would that be the item.serialize() Map? Or would I have to do actual NMS stuff with getting and saving the Tags? And thank you.
  20. NathanWolf


    Here's my PR, assuming you have Stash access:

    If not, let me know :)

    You'd have to do use NMS method to access the Tag data, yeah. There's no way to stick data into the "unhandled" data blob via the API, it just stores whatever has been put there by vanilla commands or plugins that do raw NBT access via NMS.
    • Useful Useful x 1