or: Why you should NEVER use NBT tags again!
Spigot 1.14.1 added the biggest improvement that (in my opinion) ever made it into the Bukkit API: The Persistent Data Container (PDC). It can be used to store custom data on Entities, TileEntities, and ItemStacks. Using a bit of math, it can also be used to store custom data inside blocks.
Advantages over NBT
- No need to use dirty NMS classes – PDC is part of the API (and has been for years)!
- You can store any type of data inside PDCs
- The data is actually persistent, unlike for example NBT tags on TileEntities
Where can PDC be used?
All Entities, TileEntities and ItemMetas implement the PersistentDataHolder interface, meaning they provide a PersistentDataContainer using the getPersistentDataContainer()
method. That means you can store ANY kind and amount of data inside, for example
- ItemMeta
- Mobs
- Players
- Vehicles like Minecarts and Boats
- Other Entities like ArmorStands, Arrows, ShulkerBullets, AreaEffectClouds, …
- TileEntities like Chests, ShulkerBoxes, Beacons, Beehives, …
- Chunks (in 1.16.3 and later)
- and even every Block (with a bit of Math by using the Chunk instead)
Introduction to NamespacedKeys
Using PDCs is pretty straightforward. Data in a PDC is stored similar to a HashMap, with the Key being a NamespacedKey. They are used so that two plugins that store a value of the same name are not conflicting with each other. The best way to create a NamespacedKey is by providing an instance of your plugin, and a name for your key, like this:
NamespacedKey myKey = new NamespacedKey(myPlugin, "myKey");
It’s also possible to create keys using a name instead of the plugin instance, but you should always use the plugin instance when you can.
Since you probably need the same NamespacedKey more than once, you should store them somewhere – e.g. as public static final
fields in your main class. That also avoids you having to debug disgusting errors that resulted by typos in the “myKey” String above, as you always only have to write out every key name once.
How to use PDC
Let’s imagine you have a Player object, and you want to store a simple String inside, like their real life surname. First, we have to get their PDC:
PersistentDataContainer pdc = player.getPersistentDataContainer();
To store data inside, we have to tell the API what kind of PersistentDataType it is. The SpigotAPI already provides PersistentDataType implementations for most primitives (byte, short, integer, float, double, long), some primitive arrays (byte array, integer array, long array), Strings, nested PersistentDataContainers, and arrays of nested PersistentDataContainers.
Obviously, we’re going to use the String datatype:
String surname = "Alex"; PersistentDataContainer pdc = player.getPersistentDataContainer(); NamespacedKey surnameKey = new NamespacedKey(myPlugin, "surname"); pdc.set(surnameKey, PersistentDataType.STRING, surname);
We can now easily retrieve the value again:
String surname = pdc.get(surnameKey, PersistentDataType.STRING);
But how can I use it on ItemStacks?
ItemStack’s do NOT have a PersistentDataContainer – but ItemMeta does. So, simply get the ItemStack’s ItemMeta, store it, do your stuff, and then set the ItemMeta back to the ItemStack. Like this:
ItemMeta meta = myItemStack.getItemMeta(); meta.getPersistentDataContainer().set(someNamespacedKey, PersistentDataType.INTEGER, 1337); myItemStack.setItemMeta(meta);
How is this better than NBT tags in any way?
It’s better than NBT tags in every single way, except for the one.
- The PDC is part of the Spigot API.
- That avoids the usage of dirty NMS code
- It also means your plugin doesn’t break when NMS code changes on every update
- It also avoids the usage of any NBTAPI plugins that might be already abandoned on the next NMS release
- Two plugins don’t have conflicts when both use an identical called key name
- But you can still explicitly access other plugin’s PDC key/values
- It allows you to easily create your own DataTypes, see below
- PersistentDataContainer values are actually persistent, unlike for example custom NBT tags on TileEntities
The only disadvantage I that indeed exists, is that it does NOT allow you to access vanilla NBT tags. HOWEVER, in modern Minecraft versions, there basically is no need to do so because everything that can be done with NBT tags can be done through the API (when I’m not mistaken).
Custom DataTypes
Imagine you want to save an ItemStack inside a Player object instead of just a String. No problem, you can easily create your own PersistentDataType. I have already made a tiny library for that, you can find it here on SpigotMC and here on GitHub.
But how can I save data inside Blocks?
If your block’s BlockState is an instance of TileEntity, you can simply use the TileEntitiy’s PersistentDataContainer. For all other blocks, I made a tiny library for this purpose, too. You can find it here on SpigotMC and here on GitHub.
Join my Discord Server for feedback or support. Just check out the channel #programming-help
🙂