[SPIGOT-3178] MetaData.contains(MetaData, boolean) Created: 11/Apr/17  Updated: 10/Jul/24  Resolved: 10/Jul/24

Status: Closed
Project: Spigot
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: New Feature Priority: Minor
Reporter: blablubbabc Assignee: Unassigned
Resolution: Duplicate Votes: 1
Labels: None

Issue Links:
Duplicate
duplicates SPIGOT-7828 MC 1.20.5+: Item component predicate ... Open

 Description   

I am currently trying to check in a plugin if an item contains specific metadata, which a player or admin can specify by providing an ItemStack, similar to minecraft's testFor command: In minecraft they parse the nbt data from the command and then check if the data of entities contains the same data. (their implementation can be found here: GameProfileSerializer.a(NBTBase paramNBTBase1, NBTBase paramNBTBase2, boolean paramBoolean))

This is different to ItemStack.isSimilar or MetaData.equals because those check for equality, while I am interested in checking if an item has certain data, specified by a player or admin. Matching items are however allowed to additionally have other data. Example: If the specified MetaData contains enchantments, then it checks if the MetaData of other items contains those enchantments, without checking if all their enchantments are the same.

 

One could write and maintain the code which checks for all the different meta data attributes (for all the different item types, etc.). I am trying to avoid doing that in my plugin. It would be nice to have that directly in craftbukkit instead, where it could be easily maintained whenever new metadata is added.

Currently I am using MetaData.serialize to get a Map representation of the ItemStack's metadata and then go through that map similar to how GameProfileSerializer.a(NBTBase paramNBTBase1, NBTBase paramNBTBase2, boolean paramBoolean) works.

This however has a few drawbacks as well:

  • It relies on the serialization only using Maps and Lists as container objects (doesn't seem to be a problem for MetaData though). However, in principal any other ConfigurationSerializable object could be used here as well in the future. ItemStack for example inserts the MetaData object directly into its serialization.
  • Serialization requires copying (and compressing) of all the data, while the actual check, if certain data is there, would not require that.
  • Serialization puts all the 'internal' (custom) item data into one compressed String. So checking if the MetaData contains the same custom data of the given MetaData is not possible. If MetaData had a dedicated 'contains' method, it could directly check in the unhandledTags map and not have this problem.

Now, I am proposing a method MetaData#contains(MetaData otherData, boolean checkListsContain) which checks if the MetaData contains the given other MetaData. With the additional boolean parameter one can decide whether to check if lists (ex. lore, ..) are completely equal, or whether it checks if the lists contain the entries which can be found in the corresponding lists of the given other MetaData. (Similar to the boolean parameter in GameProfileSerializer.a(NBTBase paramNBTBase1, NBTBase paramNBTBase2, boolean paramBoolean)).

The implementation would then check for every data in the given otherData, if the MetaData contains the data.

Example: if (otherData.hasDisplayName()) return this.hasDisplayName() && this.getDisplayName().equals(otherData.getDisplayName())

Example for lore: if (otherData.hasLore()) return this.hasLore() && checkListContains ? this.getLore().containsAll(otherData.getLore()) : this.getLore().equals(otherData.getLore())

The unhandledTags could be compared by using minecraft's GameProfileSerializer.a(NBTBase paramNBTBase1, NBTBase paramNBTBase2, boolean paramBoolean) method.

 

I would be willing to create a pull request for this if you would be willing to accept such an addition.



 Comments   
Comment by blablubbabc [ 10/Jul/24 ]

Superseded: A solution for MC 1.20.5+ should probably be based on the new DataComponentPredicate instead.

Comment by blablubbabc [ 10/Jul/24 ]

Closed in favor of https://hub.spigotmc.org/jira/browse/SPIGOT-7828  (MC 1.20.5 added a new DataComponentPredicate to handle item data matching)

Comment by blablubbabc [ 11/May/18 ]

Any chance to get this added?
I am currently finding myself in a situation again where this would be useful: Checking inside the trading inventory whether minecraft compares the items in the first and second slots to the first and second required item of the trading recipe, or the other way around. Minecraft checks both combination and uses the first one that matches. In order to determine beforehand how often the trading recipe gets applied, I need to know in which order to compare the provided with the given items.

Comment by blablubbabc [ 12/Apr/17 ]

I created pull requests for it:

https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/pull-requests/269/

https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/pull-requests/363/

 

It relies on the corresponding minecraft method this is supposed to mimic so there is actually no maintainance needed now.

Another usecase for this method (with the 'checkLists' parameter set to false) would be to check if an item matches the required item of an minecraft merchant trading recipe. Currently most shop plugins are probably comparing for full item equality with isSimilar. If they would want to mimic minecraft's trading behavior, they could now use this new method to implement the item comparison.

Comment by blablubbabc [ 11/Apr/17 ]

Also the unhandledTags cannot be checked by a plugin.

And if this 1 line of code per item-meta addition is too much of an effort to maintain, one could even use CraftItemMeta.applyTo(NBTCompound) and minecraft's own GameProfileSerializer.a(NBTBase paramNBTBase1, NBTBase paramNBTBase2, boolean paramBoolean) method to implement it.

Contra:

  • Copying the data into a new NBTCompound, while this is not really required for the check itself.

Pro:

  • Works for unhandledTags out of the box.
  • Requires no maintainance when new meta data is added.
Comment by blablubbabc [ 11/Apr/17 ]

There isn't much of additional effort in maintaining this inside the server, because you already have to update the MetaData.equals() method whenever new item meta data is added to minecraft. So when new meta data is added to minecraft and you already have to add a line of code to the equals method, you would now simply add a very similar line of code to the contains method as well.

 

Comment by md_5 [ 11/Apr/17 ]

>One could write and maintain the code which checks for all the different meta data attributes (for all the different item types, etc.). I am trying to avoid doing that in my plugin. It would be nice to have that directly in craftbukkit instead, where it could be easily maintained whenever new metadata is added.

If you want to do burden shifting on this large a scale there needs to be a compelling reason for it.
Given that you're the only one requesting such a thing and such a thing is currently 100% implementable within a plugin using only the Bukkit API, I'm hesitant to say that's a compelling reason.

It's not any easier at all to implement into the server, yet it's a non trivial piece of code that would need to be maintained by us every update for apparently just your /testfor command. It's also not unit testable which is a massive pain because it means if there is ever a lapse in it after an update, its just another bug report in a very busy time window.

Generated at Tue Apr 22 03:00:31 UTC 2025 using Jira 10.3.5#10030005-sha1:190c783f2bd6c69cd5accdb70f97e48812a78d14.