aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJake Potrebic <[email protected]>2024-08-11 13:51:37 -0700
committerGitHub <[email protected]>2024-08-11 13:51:37 -0700
commitdd941cc40a25a4c6217f09c121b891962726f3ee (patch)
tree010ef53e6e65d68594d6a7829d79058944dd5fb6
parent1187544fb0946e1480acdd52213c2ecb3f321a74 (diff)
downloadPaper-dd941cc40a25a4c6217f09c121b891962726f3ee.tar.gz
Paper-dd941cc40a25a4c6217f09c121b891962726f3ee.zip
Re-add patches for can-place/can-destroy API (#11238)
* Re-add patches for can-place/can-destroy API This API is all deprecated for removal and has to be replaced as the structure of it has too radically changed * update patches * add link to MaterialRerouting * Deprecation --------- Co-authored-by: Bjarne Koll <[email protected]>
-rw-r--r--patches/api/0482-Add-an-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch332
-rw-r--r--patches/removed/1.20.5/0259-Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch397
-rw-r--r--patches/server/0981-Adopt-MaterialRerouting.patch6
-rw-r--r--patches/server/1044-Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch155
4 files changed, 490 insertions, 400 deletions
diff --git a/patches/api/0482-Add-an-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch b/patches/api/0482-Add-an-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch
new file mode 100644
index 0000000000..3dd2df1411
--- /dev/null
+++ b/patches/api/0482-Add-an-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch
@@ -0,0 +1,332 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mark Vainomaa <[email protected]>
+Date: Wed, 12 Sep 2018 18:53:35 +0300
+Subject: [PATCH] Add an API for CanPlaceOn and CanDestroy NBT values
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/Namespaced.java b/src/main/java/com/destroystokyo/paper/Namespaced.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e708cc48f480e91a60834897e447e4a2fc1244d4
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/Namespaced.java
+@@ -0,0 +1,41 @@
++package com.destroystokyo.paper;
++
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Represents a namespaced resource, see {@link org.bukkit.NamespacedKey} for single elements
++ * or {@link com.destroystokyo.paper.NamespacedTag} for a collection of elements
++ *
++ * Namespaces may only contain lowercase alphanumeric characters, periods,
++ * underscores, and hyphens.
++ * <p>
++ * Keys may only contain lowercase alphanumeric characters, periods,
++ * underscores, hyphens, and forward slashes.
++ * <p>
++ * You should not be implementing this interface yourself, use {@link org.bukkit.NamespacedKey}
++ * or {@link com.destroystokyo.paper.NamespacedTag} as needed instead.
++ */
++@Deprecated(forRemoval = true, since = "1.20.6")
++public interface Namespaced {
++ /**
++ * Gets the namespace this resource is a part of
++ * <p>
++ * This is contractually obligated to only contain lowercase alphanumeric characters,
++ * periods, underscores, and hyphens.
++ *
++ * @return resource namespace
++ */
++ @NotNull
++ String getNamespace();
++
++ /**
++ * Gets the key corresponding to this resource
++ * <p>
++ * This is contractually obligated to only contain lowercase alphanumeric characters,
++ * periods, underscores, hyphens, and forward slashes.
++ *
++ * @return resource key
++ */
++ @NotNull
++ String getKey();
++}
+diff --git a/src/main/java/com/destroystokyo/paper/NamespacedTag.java b/src/main/java/com/destroystokyo/paper/NamespacedTag.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c976995a0fceaf0cbddd399906a866747b12a202
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/NamespacedTag.java
+@@ -0,0 +1,143 @@
++package com.destroystokyo.paper;
++
++import com.google.common.base.Preconditions;
++import java.util.Locale;
++import java.util.UUID;
++import java.util.regex.Pattern;
++import org.bukkit.plugin.Plugin;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Represents a String based key pertaining to a tagged entry. Consists of two components - a namespace
++ * and a key.
++ * <p>
++ * Namespaces may only contain lowercase alphanumeric characters, periods,
++ * underscores, and hyphens.
++ * <p>
++ * Keys may only contain lowercase alphanumeric characters, periods,
++ * underscores, hyphens, and forward slashes.
++ *
++ */
++// Paper - entire class, based on org.bukkit.NamespacedKey
++@Deprecated(forRemoval = true, since = "1.20.6")
++public final class NamespacedTag implements com.destroystokyo.paper.Namespaced {
++
++ /**
++ * The namespace representing all inbuilt keys.
++ */
++ public static final String MINECRAFT = "minecraft";
++ /**
++ * The namespace representing all keys generated by Bukkit for backwards
++ * compatibility measures.
++ */
++ public static final String BUKKIT = "bukkit";
++ //
++ private static final Pattern VALID_NAMESPACE = Pattern.compile("[a-z0-9._-]+");
++ private static final Pattern VALID_KEY = Pattern.compile("[a-z0-9/._-]+");
++ //
++ private final String namespace;
++ private final String key;
++
++ /**
++ * Create a key in a specific namespace.
++ *
++ * @param namespace String representing a grouping of keys
++ * @param key Name for this specific key
++ * @deprecated should never be used by plugins, for internal use only!!
++ */
++ @Deprecated
++ public NamespacedTag(@NotNull String namespace, @NotNull String key) {
++ Preconditions.checkArgument(namespace != null && VALID_NAMESPACE.matcher(namespace).matches(), "Invalid namespace. Must be [a-z0-9._-]: %s", namespace);
++ Preconditions.checkArgument(key != null && VALID_KEY.matcher(key).matches(), "Invalid key. Must be [a-z0-9/._-]: %s", key);
++
++ this.namespace = namespace;
++ this.key = key;
++
++ String string = toString();
++ Preconditions.checkArgument(string.length() < 256, "NamespacedTag must be less than 256 characters", string);
++ }
++
++ /**
++ * Create a key in the plugin's namespace.
++ * <p>
++ * Namespaces may only contain lowercase alphanumeric characters, periods,
++ * underscores, and hyphens.
++ * <p>
++ * Keys may only contain lowercase alphanumeric characters, periods,
++ * underscores, hyphens, and forward slashes.
++ *
++ * @param plugin the plugin to use for the namespace
++ * @param key the key to create
++ */
++ public NamespacedTag(@NotNull Plugin plugin, @NotNull String key) {
++ Preconditions.checkArgument(plugin != null, "Plugin cannot be null");
++ Preconditions.checkArgument(key != null, "Key cannot be null");
++
++ this.namespace = plugin.getName().toLowerCase(Locale.ROOT);
++ this.key = key.toLowerCase().toLowerCase(Locale.ROOT);
++
++ // Check validity after normalization
++ Preconditions.checkArgument(VALID_NAMESPACE.matcher(this.namespace).matches(), "Invalid namespace. Must be [a-z0-9._-]: %s", this.namespace);
++ Preconditions.checkArgument(VALID_KEY.matcher(this.key).matches(), "Invalid key. Must be [a-z0-9/._-]: %s", this.key);
++
++ String string = toString();
++ Preconditions.checkArgument(string.length() < 256, "NamespacedTag must be less than 256 characters (%s)", string);
++ }
++
++ @NotNull
++ public String getNamespace() {
++ return namespace;
++ }
++
++ @NotNull
++ public String getKey() {
++ return key;
++ }
++
++ @Override
++ public int hashCode() {
++ int hash = 7;
++ hash = 47 * hash + this.namespace.hashCode();
++ hash = 47 * hash + this.key.hashCode();
++ return hash;
++ }
++
++ @Override
++ public boolean equals(Object obj) {
++ if (obj == null) {
++ return false;
++ }
++ if (getClass() != obj.getClass()) {
++ return false;
++ }
++ final NamespacedTag other = (NamespacedTag) obj;
++ return this.namespace.equals(other.namespace) && this.key.equals(other.key);
++ }
++
++ @Override
++ public String toString() {
++ return "#" + this.namespace + ":" + this.key;
++ }
++
++ /**
++ * Return a new random key in the {@link #BUKKIT} namespace.
++ *
++ * @return new key
++ * @deprecated should never be used by plugins, for internal use only!!
++ */
++ @Deprecated
++ public static NamespacedTag randomKey() {
++ return new NamespacedTag(BUKKIT, UUID.randomUUID().toString());
++ }
++
++ /**
++ * Get a key in the Minecraft namespace.
++ *
++ * @param key the key to use
++ * @return new key in the Minecraft namespace
++ */
++ @NotNull
++ public static NamespacedTag minecraft(@NotNull String key) {
++ return new NamespacedTag(MINECRAFT, key);
++ }
++}
+diff --git a/src/main/java/org/bukkit/NamespacedKey.java b/src/main/java/org/bukkit/NamespacedKey.java
+index 7ff6d60deb129e23b2a4d772aee123eb6c0b6433..52a2763773b234c581b2dcc6f0584f8d8b0fbc60 100644
+--- a/src/main/java/org/bukkit/NamespacedKey.java
++++ b/src/main/java/org/bukkit/NamespacedKey.java
+@@ -19,7 +19,7 @@ import org.jetbrains.annotations.Nullable;
+ * underscores, hyphens, and forward slashes.
+ *
+ */
+-public final class NamespacedKey implements net.kyori.adventure.key.Key { // Paper - implement Key
++public final class NamespacedKey implements net.kyori.adventure.key.Key, com.destroystokyo.paper.Namespaced { // Paper - implement Key and Namespaced
+
+ /**
+ * The namespace representing all inbuilt keys.
+@@ -121,11 +121,13 @@ public final class NamespacedKey implements net.kyori.adventure.key.Key { // Pap
+ }
+
+ @NotNull
++ @Override // Paper
+ public String getNamespace() {
+ return namespace;
+ }
+
+ @NotNull
++ @Override // Paper
+ public String getKey() {
+ return key;
+ }
+diff --git a/src/main/java/org/bukkit/inventory/meta/ItemMeta.java b/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
+index 5d5fcb2720b62e47d47f441032c4de02574b051a..f5541454ba5e508a72c83989c6feaef5406e2535 100644
+--- a/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
++++ b/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
+@@ -806,4 +806,98 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
+ @SuppressWarnings("javadoc")
+ @NotNull
+ ItemMeta clone();
++
++ // Paper start - Add an API for can-place-on/can-break adventure mode predicates
++ /**
++ * Gets set of materials what given item can destroy in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @return Set of materials
++ * @deprecated this API is unsupported and will be replaced, its usage may result in data loss related to place/destroy predicates.
++ */
++ @Deprecated(forRemoval = true, since = "1.14")
++ Set<org.bukkit.Material> getCanDestroy();
++
++ /**
++ * Sets set of materials what given item can destroy in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @param canDestroy Set of materials
++ * @deprecated this API is unsupported and will be replaced, its usage may result in data loss related to place/destroy predicates.
++ */
++ @Deprecated(forRemoval = true, since = "1.14")
++ void setCanDestroy(Set<org.bukkit.Material> canDestroy);
++
++ /**
++ * Gets set of materials where given item can be placed on in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @return Set of materials
++ * @deprecated this API is unsupported and will be replaced, its usage may result in data loss related to place/destroy predicates.
++ */
++ @Deprecated(forRemoval = true, since = "1.14")
++ Set<org.bukkit.Material> getCanPlaceOn();
++
++ /**
++ * Sets set of materials where given item can be placed on in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @param canPlaceOn Set of materials
++ * @deprecated this API is unsupported and will be replaced, its usage may result in data loss related to place/destroy predicates.
++ */
++ @Deprecated(forRemoval = true, since = "1.14")
++ void setCanPlaceOn(Set<org.bukkit.Material> canPlaceOn);
++
++ /**
++ * Gets the collection of namespaced keys that the item can destroy in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @return Set of {@link com.destroystokyo.paper.Namespaced}
++ * @deprecated this API is unsupported and will be replaced, its usage may result in data loss related to place/destroy predicates.
++ */
++ @Deprecated(forRemoval = true, since = "1.20.6")
++ @NotNull
++ Set<com.destroystokyo.paper.Namespaced> getDestroyableKeys();
++
++ /**
++ * Sets the collection of namespaced keys that the item can destroy in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @param canDestroy Collection of {@link com.destroystokyo.paper.Namespaced}
++ * @deprecated this API is unsupported and will be replaced, its usage may result in data loss related to place/destroy predicates.
++ */
++ @Deprecated(forRemoval = true, since = "1.20.6")
++ void setDestroyableKeys(@NotNull Collection<com.destroystokyo.paper.Namespaced> canDestroy);
++
++ /**
++ * Gets the collection of namespaced keys that the item can be placed on in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @return Set of {@link com.destroystokyo.paper.Namespaced}
++ * @deprecated this API is unsupported and will be replaced, its usage may result in data loss related to place/destroy predicates.
++ */
++ @NotNull
++ @Deprecated(forRemoval = true, since = "1.20.6")
++ Set<com.destroystokyo.paper.Namespaced> getPlaceableKeys();
++
++ /**
++ * Sets the set of namespaced keys that the item can be placed on in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @param canPlaceOn Collection of {@link com.destroystokyo.paper.Namespaced}
++ * @deprecated this API is unsupported and will be replaced, its usage may result in data loss related to place/destroy predicates.
++ */
++ @Deprecated(forRemoval = true, since = "1.20.6")
++ void setPlaceableKeys(@NotNull Collection<com.destroystokyo.paper.Namespaced> canPlaceOn);
++
++ /**
++ * Checks for the existence of any keys that the item can be placed on
++ *
++ * @return true if this item has placeable keys
++ * @deprecated this API is unsupported and will be replaced
++ */
++ @Deprecated(forRemoval = true, since = "1.20.6")
++ boolean hasPlaceableKeys();
++
++ /**
++ * Checks for the existence of any keys that the item can destroy
++ *
++ * @return true if this item has destroyable keys
++ * @deprecated this API is unsupported and will be replaced
++ */
++ @Deprecated(forRemoval = true, since = "1.20.6")
++ boolean hasDestroyableKeys();
++ // Paper end - Add an API for can-place-on/can-break adventure mode predicates
+ }
diff --git a/patches/removed/1.20.5/0259-Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch b/patches/removed/1.20.5/0259-Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch
deleted file mode 100644
index f179b48bd7..0000000000
--- a/patches/removed/1.20.5/0259-Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch
+++ /dev/null
@@ -1,397 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Mark Vainomaa <[email protected]>
-Date: Wed, 12 Sep 2018 18:53:55 +0300
-Subject: [PATCH] Add API for CanPlaceOn and CanDestroy NBT values
-
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-index 1920cf7ad846f57cd278cb9a72dce03f3d014fbb..7cf1153ae532a9d53ee85b05f77ed74b94cf5fbc 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-@@ -85,6 +85,12 @@ import org.bukkit.persistence.PersistentDataContainer;
- import static org.spigotmc.ValidateUtils.*;
- // Spigot end
-
-+// Paper start
-+import com.destroystokyo.paper.Namespaced;
-+import com.destroystokyo.paper.NamespacedTag;
-+import java.util.Collections;
-+// Paper end
-+
- /**
- * Children must include the following:
- *
-@@ -273,6 +279,10 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- @Specific(Specific.To.NBT)
- static final ItemMetaKey BLOCK_DATA = new ItemMetaKey("BlockStateTag");
- static final ItemMetaKey BUKKIT_CUSTOM_TAG = new ItemMetaKey("PublicBukkitValues");
-+ // Paper start - Add API for CanPlaceOn and CanDestroy NBT values
-+ static final ItemMetaKey CAN_DESTROY = new ItemMetaKey("CanDestroy");
-+ static final ItemMetaKey CAN_PLACE_ON = new ItemMetaKey("CanPlaceOn");
-+ // Paper end - Add API for CanPlaceOn and CanDestroy NBT values
-
- // We store the raw original JSON representation of all text data. See SPIGOT-5063, SPIGOT-5656, SPIGOT-5304
- private String displayName;
-@@ -286,6 +296,10 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- private int hideFlag;
- private boolean unbreakable;
- private int damage;
-+ // Paper start - Add API for CanPlaceOn and CanDestroy NBT values
-+ private Set<Namespaced> placeableKeys = Sets.newHashSet();
-+ private Set<Namespaced> destroyableKeys = Sets.newHashSet();
-+ // Paper end - Add API for CanPlaceOn and CanDestroy NBT values
-
- private static final Set<String> HANDLED_TAGS = Sets.newHashSet();
- private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry();
-@@ -323,6 +337,15 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- this.hideFlag = meta.hideFlag;
- this.unbreakable = meta.unbreakable;
- this.damage = meta.damage;
-+ // Paper start - Add API for CanPlaceOn and CanDestroy NBT values
-+ if (meta.hasPlaceableKeys()) {
-+ this.placeableKeys = new java.util.HashSet<>(meta.placeableKeys);
-+ }
-+
-+ if (meta.hasDestroyableKeys()) {
-+ this.destroyableKeys = new java.util.HashSet<>(meta.destroyableKeys);
-+ }
-+ // Paper end - Add API for CanPlaceOn and CanDestroy NBT values
- this.unhandledTags.putAll(meta.unhandledTags);
- this.persistentDataContainer.putAll(meta.persistentDataContainer.getRaw());
-
-@@ -386,6 +409,31 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- this.persistentDataContainer.put(key, compound.get(key).copy());
- }
- }
-+ // Paper start - Add API for CanPlaceOn and CanDestroy NBT values
-+ if (tag.contains(CAN_DESTROY.NBT)) {
-+ ListTag list = tag.getList(CAN_DESTROY.NBT, CraftMagicNumbers.NBT.TAG_STRING);
-+ for (int i = 0; i < list.size(); i++) {
-+ Namespaced namespaced = this.blockKeyFromString(list.getString(i));
-+ if (namespaced == null) {
-+ continue;
-+ }
-+
-+ this.destroyableKeys.add(namespaced);
-+ }
-+ }
-+
-+ if (tag.contains(CAN_PLACE_ON.NBT)) {
-+ ListTag list = tag.getList(CAN_PLACE_ON.NBT, CraftMagicNumbers.NBT.TAG_STRING);
-+ for (int i = 0; i < list.size(); i++) {
-+ Namespaced namespaced = this.blockKeyFromString(list.getString(i));
-+ if (namespaced == null) {
-+ continue;
-+ }
-+
-+ this.placeableKeys.add(namespaced);
-+ }
-+ }
-+ // Paper end - Add API for CanPlaceOn and CanDestroy NBT values
-
- Set<String> keys = tag.getAllKeys();
- for (String key : keys) {
-@@ -524,6 +572,34 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- this.setDamage(damage);
- }
-
-+ // Paper start - Add API for CanPlaceOn and CanDestroy NBT values
-+ Iterable<?> canPlaceOnSerialized = SerializableMeta.getObject(Iterable.class, map, CAN_PLACE_ON.BUKKIT, true);
-+ if (canPlaceOnSerialized != null) {
-+ for (Object canPlaceOnElement : canPlaceOnSerialized) {
-+ String canPlaceOnRaw = (String) canPlaceOnElement;
-+ Namespaced value = this.blockKeyFromString(canPlaceOnRaw);
-+ if (value == null) {
-+ continue;
-+ }
-+
-+ this.placeableKeys.add(value);
-+ }
-+ }
-+
-+ Iterable<?> canDestroySerialized = SerializableMeta.getObject(Iterable.class, map, CAN_DESTROY.BUKKIT, true);
-+ if (canDestroySerialized != null) {
-+ for (Object canDestroyElement : canDestroySerialized) {
-+ String canDestroyRaw = (String) canDestroyElement;
-+ Namespaced value = this.blockKeyFromString(canDestroyRaw);
-+ if (value == null) {
-+ continue;
-+ }
-+
-+ this.destroyableKeys.add(value);
-+ }
-+ }
-+ // Paper end - Add API for CanPlaceOn and CanDestroy NBT values
-+
- String internal = SerializableMeta.getString(map, "internal", true);
- if (internal != null) {
- ByteArrayInputStream buf = new ByteArrayInputStream(Base64.getDecoder().decode(internal));
-@@ -652,6 +728,23 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- if (this.hasDamage()) {
- itemTag.putInt(CraftMetaItem.DAMAGE.NBT, this.damage);
- }
-+ // Paper start - Add API for CanPlaceOn and CanDestroy NBT values
-+ if (hasPlaceableKeys()) {
-+ List<String> items = this.placeableKeys.stream()
-+ .map(this::serializeNamespaced)
-+ .collect(java.util.stream.Collectors.toList());
-+
-+ itemTag.put(CAN_PLACE_ON.NBT, createNonComponentStringList(items));
-+ }
-+
-+ if (hasDestroyableKeys()) {
-+ List<String> items = this.destroyableKeys.stream()
-+ .map(this::serializeNamespaced)
-+ .collect(java.util.stream.Collectors.toList());
-+
-+ itemTag.put(CAN_DESTROY.NBT, createNonComponentStringList(items));
-+ }
-+ // Paper end - Add API for CanPlaceOn and CanDestroy NBT values
-
- for (Map.Entry<String, Tag> e : this.unhandledTags.entrySet()) {
- itemTag.put(e.getKey(), e.getValue());
-@@ -668,6 +761,21 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- }
- }
-
-+ // Paper start - Add API for CanPlaceOn and CanDestroy NBT values
-+ static ListTag createNonComponentStringList(List<String> list) {
-+ if (list == null || list.isEmpty()) {
-+ return null;
-+ }
-+
-+ ListTag tagList = new ListTag();
-+ for (String value : list) {
-+ tagList.add(StringTag.valueOf(value)); // Paper - NBTTagString.of(String str)
-+ }
-+
-+ return tagList;
-+ }
-+ // Paper end - Add API for CanPlaceOn and CanDestroy NBT values
-+
- ListTag createStringList(List<String> list) {
- if (list == null) {
- return null;
-@@ -751,7 +859,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
-
- @Overridden
- boolean isEmpty() {
-- return !(this.hasDisplayName() || this.hasLocalizedName() || this.hasEnchants() || (this.lore != null) || this.hasCustomModelData() || this.hasBlockData() || this.hasRepairCost() || !this.unhandledTags.isEmpty() || !this.persistentDataContainer.isEmpty() || this.hideFlag != 0 || this.isUnbreakable() || this.hasDamage() || this.hasAttributeModifiers());
-+ return !(this.hasDisplayName() || this.hasLocalizedName() || this.hasEnchants() || (this.lore != null) || this.hasCustomModelData() || this.hasBlockData() || this.hasRepairCost() || !this.unhandledTags.isEmpty() || !this.persistentDataContainer.isEmpty() || this.hideFlag != 0 || this.isUnbreakable() || this.hasDamage() || this.hasAttributeModifiers() || this.hasPlaceableKeys() || this.hasDestroyableKeys()); // Paper - Implement an API for CanPlaceOn and CanDestroy NBT values
- }
-
- // Paper start
-@@ -1223,7 +1331,11 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- && (this.hideFlag == that.hideFlag)
- && (this.isUnbreakable() == that.isUnbreakable())
- && (this.hasDamage() ? that.hasDamage() && this.damage == that.damage : !that.hasDamage())
-- && (this.version == that.version);
-+ && (this.version == that.version)
-+ // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values
-+ && (this.hasPlaceableKeys() ? that.hasPlaceableKeys() && this.placeableKeys.equals(that.placeableKeys) : !that.hasPlaceableKeys())
-+ && (this.hasDestroyableKeys() ? that.hasDestroyableKeys() && this.destroyableKeys.equals(that.destroyableKeys) : !that.hasDestroyableKeys());
-+ // Paper end
- }
-
- /**
-@@ -1258,6 +1370,10 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- hash = 61 * hash + (this.hasDamage() ? this.damage : 0);
- hash = 61 * hash + (this.hasAttributeModifiers() ? this.attributeModifiers.hashCode() : 0);
- hash = 61 * hash + this.version;
-+ // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values
-+ hash = 61 * hash + (this.hasPlaceableKeys() ? this.placeableKeys.hashCode() : 0);
-+ hash = 61 * hash + (this.hasDestroyableKeys() ? this.destroyableKeys.hashCode() : 0);
-+ // Paper end
- return hash;
- }
-
-@@ -1282,6 +1398,14 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- clone.unbreakable = this.unbreakable;
- clone.damage = this.damage;
- clone.version = this.version;
-+ // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values
-+ if (this.placeableKeys != null) {
-+ clone.placeableKeys = Sets.newHashSet(this.placeableKeys);
-+ }
-+ if (this.destroyableKeys != null) {
-+ clone.destroyableKeys = Sets.newHashSet(this.destroyableKeys);
-+ }
-+ // Paper end
- return clone;
- } catch (CloneNotSupportedException e) {
- throw new Error(e);
-@@ -1339,6 +1463,23 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- builder.put(CraftMetaItem.DAMAGE.BUKKIT, this.damage);
- }
-
-+ // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values
-+ if (this.hasPlaceableKeys()) {
-+ List<String> cerealPlaceable = this.placeableKeys.stream()
-+ .map(this::serializeNamespaced)
-+ .collect(java.util.stream.Collectors.toList());
-+
-+ builder.put(CAN_PLACE_ON.BUKKIT, cerealPlaceable);
-+ }
-+
-+ if (this.hasDestroyableKeys()) {
-+ List<String> cerealDestroyable = this.destroyableKeys.stream()
-+ .map(this::serializeNamespaced)
-+ .collect(java.util.stream.Collectors.toList());
-+
-+ builder.put(CAN_DESTROY.BUKKIT, cerealDestroyable);
-+ }
-+ // Paper end
- final Map<String, Tag> internalTags = new HashMap<String, Tag>(this.unhandledTags);
- this.serializeInternal(internalTags);
- if (!internalTags.isEmpty()) {
-@@ -1516,6 +1657,8 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- CraftMetaArmorStand.SHOW_ARMS.NBT,
- CraftMetaArmorStand.SMALL.NBT,
- CraftMetaArmorStand.MARKER.NBT,
-+ CAN_DESTROY.NBT,
-+ CAN_PLACE_ON.NBT,
- // Paper end
- CraftMetaCompass.LODESTONE_DIMENSION.NBT,
- CraftMetaCompass.LODESTONE_POS.NBT,
-@@ -1545,4 +1688,141 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- }
- // Paper end
-
-+ // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values
-+ @Override
-+ @SuppressWarnings("deprecation")
-+ public Set<Material> getCanDestroy() {
-+ return !hasDestroyableKeys() ? Collections.emptySet() : legacyGetMatsFromKeys(this.destroyableKeys);
-+ }
-+
-+ @Override
-+ @SuppressWarnings("deprecation")
-+ public void setCanDestroy(Set<Material> canDestroy) {
-+ Preconditions.checkArgument(canDestroy != null, "Cannot replace with null set!");
-+ legacyClearAndReplaceKeys(this.destroyableKeys, canDestroy);
-+ }
-+
-+ @Override
-+ @SuppressWarnings("deprecation")
-+ public Set<Material> getCanPlaceOn() {
-+ return !hasPlaceableKeys() ? Collections.emptySet() : legacyGetMatsFromKeys(this.placeableKeys);
-+ }
-+
-+ @Override
-+ @SuppressWarnings("deprecation")
-+ public void setCanPlaceOn(Set<Material> canPlaceOn) {
-+ Preconditions.checkArgument(canPlaceOn != null, "Cannot replace with null set!");
-+ legacyClearAndReplaceKeys(this.placeableKeys, canPlaceOn);
-+ }
-+
-+ @Override
-+ public Set<Namespaced> getDestroyableKeys() {
-+ return !hasDestroyableKeys() ? Collections.emptySet() : Sets.newHashSet(this.destroyableKeys);
-+ }
-+
-+ @Override
-+ public void setDestroyableKeys(Collection<Namespaced> canDestroy) {
-+ Preconditions.checkArgument(canDestroy != null, "Cannot replace with null collection!");
-+ Preconditions.checkArgument(ofAcceptableType(canDestroy), "Can only use NamespacedKey or NamespacedTag objects!");
-+ this.destroyableKeys.clear();
-+ this.destroyableKeys.addAll(canDestroy);
-+ }
-+
-+ @Override
-+ public Set<Namespaced> getPlaceableKeys() {
-+ return !hasPlaceableKeys() ? Collections.emptySet() : Sets.newHashSet(this.placeableKeys);
-+ }
-+
-+ @Override
-+ public void setPlaceableKeys(Collection<Namespaced> canPlaceOn) {
-+ Preconditions.checkArgument(canPlaceOn != null, "Cannot replace with null collection!");
-+ Preconditions.checkArgument(ofAcceptableType(canPlaceOn), "Can only use NamespacedKey or NamespacedTag objects!");
-+ this.placeableKeys.clear();
-+ this.placeableKeys.addAll(canPlaceOn);
-+ }
-+
-+ @Override
-+ public boolean hasPlaceableKeys() {
-+ return this.placeableKeys != null && !this.placeableKeys.isEmpty();
-+ }
-+
-+ @Override
-+ public boolean hasDestroyableKeys() {
-+ return this.destroyableKeys != null && !this.destroyableKeys.isEmpty();
-+ }
-+
-+ @Deprecated
-+ private void legacyClearAndReplaceKeys(Collection<Namespaced> toUpdate, Collection<Material> beingSet) {
-+ if (beingSet.stream().anyMatch(Material::isLegacy)) {
-+ throw new IllegalArgumentException("Set must not contain any legacy materials!");
-+ }
-+
-+ toUpdate.clear();
-+ toUpdate.addAll(beingSet.stream().map(Material::getKey).collect(java.util.stream.Collectors.toSet()));
-+ }
-+
-+ @Deprecated
-+ private Set<Material> legacyGetMatsFromKeys(Collection<Namespaced> names) {
-+ Set<Material> mats = Sets.newHashSet();
-+ for (Namespaced key : names) {
-+ if (!(key instanceof org.bukkit.NamespacedKey)) {
-+ continue;
-+ }
-+
-+ Material material = Material.matchMaterial(key.toString(), false);
-+ if (material != null) {
-+ mats.add(material);
-+ }
-+ }
-+
-+ return mats;
-+ }
-+
-+ private @Nullable Namespaced blockKeyFromString(String raw) {
-+ boolean isTag = !raw.isEmpty() && raw.codePointAt(0) == '#';
-+ com.mojang.datafixers.util.Either<net.minecraft.commands.arguments.blocks.BlockStateParser.BlockResult, net.minecraft.commands.arguments.blocks.BlockStateParser.TagResult> result;
-+ try {
-+ result = net.minecraft.commands.arguments.blocks.BlockStateParser.parseForTesting(net.minecraft.core.registries.BuiltInRegistries.BLOCK.asLookup(), raw, false);
-+ } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) {
-+ return null;
-+ }
-+
-+ net.minecraft.resources.ResourceLocation key = null;
-+ if (isTag && result.right().isPresent() && result.right().get().tag() instanceof net.minecraft.core.HolderSet.Named<net.minecraft.world.level.block.Block> namedSet) {
-+ key = namedSet.key().location();
-+ } else if (result.left().isPresent()) {
-+ key = net.minecraft.core.registries.BuiltInRegistries.BLOCK.getKey(result.left().get().blockState().getBlock());
-+ }
-+
-+ if (key == null) {
-+ return null;
-+ }
-+
-+ try {
-+ if (isTag) {
-+ return new NamespacedTag(key.getNamespace(), key.getPath());
-+
-+ }
-+ return CraftNamespacedKey.fromMinecraft(key);
-+ } catch (IllegalArgumentException ignored) {
-+ return null;
-+ }
-+ }
-+
-+ private @Nonnull String serializeNamespaced(Namespaced resource) {
-+ return resource.toString();
-+ }
-+
-+ // not a fan of this
-+ private boolean ofAcceptableType(Collection<Namespaced> namespacedResources) {
-+
-+ for (Namespaced resource : namespacedResources) {
-+ if (!(resource instanceof org.bukkit.NamespacedKey || resource instanceof com.destroystokyo.paper.NamespacedTag)) {
-+ return false;
-+ }
-+ }
-+
-+ return true;
-+ }
-+ // Paper end
- }
diff --git a/patches/server/0981-Adopt-MaterialRerouting.patch b/patches/server/0981-Adopt-MaterialRerouting.patch
index acdc787b2c..e6473ef21a 100644
--- a/patches/server/0981-Adopt-MaterialRerouting.patch
+++ b/patches/server/0981-Adopt-MaterialRerouting.patch
@@ -7,7 +7,7 @@ Adopts the paper-api to the material rerouting infrastructure introduced
by upstream.
diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
-index 3ff0f0e34356cee4c510fdd60af723b1c5df156a..9c004e7cb46841d874ab997bf2e3b63ae763aec7 100644
+index 3ff0f0e34356cee4c510fdd60af723b1c5df156a..6cc9d7a9e6d4bfdc27e52fc581b2bb832616f121 100644
--- a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
+++ b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
@@ -600,4 +600,82 @@ public class MaterialRerouting {
@@ -74,12 +74,12 @@ index 3ff0f0e34356cee4c510fdd60af723b1c5df156a..9c004e7cb46841d874ab997bf2e3b63a
+ return com.destroystokyo.paper.event.player.PlayerArmorChangeEvent.SlotType.isEquipable(MaterialRerouting.transformToItemType(material));
+ }
+
-+ // Method added post 1.13, no-op (https://github.com/PaperMC/Paper/pull/1244)1
++ // Method added post 1.13, no-op (https://github.com/PaperMC/Paper/pull/1244)
+ public static Material getMaterial(final com.destroystokyo.paper.event.block.AnvilDamagedEvent.DamageState damageState) {
+ return damageState.getMaterial();
+ }
+
-+ // Method added post 1.13, no-op (https://github.com/PaperMC/Paper/pull/1244)1
++ // Method added post 1.13, no-op (https://github.com/PaperMC/Paper/pull/1244)
+ @RerouteStatic("com/destroystokyo/paper/event/block/AnvilDamagedEvent$DamageState")
+ public static com.destroystokyo.paper.event.block.AnvilDamagedEvent.DamageState getState(
+ final Material material
diff --git a/patches/server/1044-Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch b/patches/server/1044-Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch
new file mode 100644
index 0000000000..3ff8343ca9
--- /dev/null
+++ b/patches/server/1044-Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch
@@ -0,0 +1,155 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Wed, 12 Sep 2018 18:53:55 +0300
+Subject: [PATCH] Add API for CanPlaceOn and CanDestroy NBT values
+
+
+diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
+index d5c45cc09b9d60c1650507ad6fedb27246291063..5c45946f54e4b16440c15a9165ef85be43e53c84 100644
+--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
+@@ -2183,4 +2183,117 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
+ }
+ // Paper end
+
++ // Paper start - Add an API for can-place-on/can-break adventure mode predicates
++ @Override
++ public Set<Material> getCanDestroy() {
++ return !this.hasDestroyableKeys() ? Collections.emptySet() : convertToLegacyMaterial(this.canBreakPredicates);
++ }
++
++ @Override
++ public void setCanDestroy(final Set<Material> canDestroy) {
++ Preconditions.checkArgument(canDestroy != null, "Cannot replace with null set!");
++ this.canBreakPredicates = convertFromLegacyMaterial(canDestroy);
++ }
++
++ @Override
++ public Set<Material> getCanPlaceOn() {
++ return !this.hasPlaceableKeys() ? Collections.emptySet() : convertToLegacyMaterial(this.canPlaceOnPredicates);
++ }
++
++ @Override
++ public void setCanPlaceOn(final Set<Material> canPlaceOn) {
++ Preconditions.checkArgument(canPlaceOn != null, "Cannot replace with null set!");
++ this.canPlaceOnPredicates = convertFromLegacyMaterial(canPlaceOn);
++ }
++
++ private static List<net.minecraft.advancements.critereon.BlockPredicate> convertFromLegacyMaterial(final Collection<Material> materials) {
++ return materials.stream().map(m -> {
++ return net.minecraft.advancements.critereon.BlockPredicate.Builder.block().of(CraftBlockType.bukkitToMinecraft(m)).build();
++ }).toList();
++ }
++
++ private static Set<Material> convertToLegacyMaterial(final List<net.minecraft.advancements.critereon.BlockPredicate> predicates) {
++ return predicates.stream()
++ .flatMap(p -> p.blocks().map(net.minecraft.core.HolderSet::stream).orElse(java.util.stream.Stream.empty()))
++ .map(holder -> CraftBlockType.minecraftToBukkit(holder.value()))
++ .collect(java.util.stream.Collectors.toSet());
++ }
++
++ @Override
++ public Set<com.destroystokyo.paper.Namespaced> getDestroyableKeys() {
++ return !this.hasDestroyableKeys() ? Collections.emptySet() : convertToLegacyNamespaced(this.canBreakPredicates);
++ }
++
++ @Override
++ public void setDestroyableKeys(final Collection<com.destroystokyo.paper.Namespaced> canDestroy) {
++ Preconditions.checkArgument(canDestroy != null, "Cannot replace with null collection!");
++ Preconditions.checkArgument(ofAcceptableType(canDestroy), "Can only use NamespacedKey or NamespacedTag objects!");
++ this.canBreakPredicates = convertFromLegacyNamespaced(canDestroy);
++ }
++
++ @Override
++ public Set<com.destroystokyo.paper.Namespaced> getPlaceableKeys() {
++ return !this.hasPlaceableKeys() ? Collections.emptySet() : convertToLegacyNamespaced(this.canPlaceOnPredicates);
++ }
++
++ @Override
++ public void setPlaceableKeys(final Collection<com.destroystokyo.paper.Namespaced> canPlaceOn) {
++ Preconditions.checkArgument(canPlaceOn != null, "Cannot replace with null collection!");
++ Preconditions.checkArgument(ofAcceptableType(canPlaceOn), "Can only use NamespacedKey or NamespacedTag objects!");
++ this.canPlaceOnPredicates = convertFromLegacyNamespaced(canPlaceOn);
++ }
++
++ private static List<net.minecraft.advancements.critereon.BlockPredicate> convertFromLegacyNamespaced(final Collection<com.destroystokyo.paper.Namespaced> namespaceds) {
++ final List<net.minecraft.advancements.critereon.BlockPredicate> predicates = new ArrayList<>();
++ for (final com.destroystokyo.paper.Namespaced namespaced : namespaceds) {
++ if (namespaced instanceof final org.bukkit.NamespacedKey key) {
++ predicates.add(net.minecraft.advancements.critereon.BlockPredicate.Builder.block().of(CraftBlockType.bukkitToMinecraft(Objects.requireNonNull(org.bukkit.Registry.MATERIAL.get(key)))).build());
++ } else if (namespaced instanceof final com.destroystokyo.paper.NamespacedTag tag) {
++ predicates.add(net.minecraft.advancements.critereon.BlockPredicate.Builder.block().of(net.minecraft.tags.TagKey.create(Registries.BLOCK, ResourceLocation.fromNamespaceAndPath(tag.getNamespace(), tag.getKey()))).build());
++ }
++ }
++ return predicates;
++ }
++
++ private static Set<com.destroystokyo.paper.Namespaced> convertToLegacyNamespaced(final Collection<net.minecraft.advancements.critereon.BlockPredicate> predicates) {
++ final Set<com.destroystokyo.paper.Namespaced> namespaceds = Sets.newHashSet();
++ for (final net.minecraft.advancements.critereon.BlockPredicate predicate : predicates) {
++ if (predicate.blocks().isEmpty()) {
++ continue;
++ }
++ final net.minecraft.core.HolderSet<net.minecraft.world.level.block.Block> holders = predicate.blocks().get();
++ if (holders instanceof final net.minecraft.core.HolderSet.Named<net.minecraft.world.level.block.Block> named) {
++ namespaceds.add(new com.destroystokyo.paper.NamespacedTag(named.key().location().getNamespace(), named.key().location().getPath()));
++ } else {
++ holders.forEach(h -> {
++ h.unwrapKey().ifPresent(key -> {
++ namespaceds.add(new org.bukkit.NamespacedKey(key.location().getNamespace(), key.location().getPath()));
++ });
++ });
++ }
++ }
++ return namespaceds;
++ }
++
++ @Override
++ public boolean hasPlaceableKeys() {
++ return this.canPlaceOnPredicates != null;
++ }
++
++ @Override
++ public boolean hasDestroyableKeys() {
++ return this.canBreakPredicates != null;
++ }
++
++ // not a fan of this
++ private static boolean ofAcceptableType(final Collection<com.destroystokyo.paper.Namespaced> namespacedResources) {
++ for (com.destroystokyo.paper.Namespaced resource : namespacedResources) {
++ if (!(resource instanceof org.bukkit.NamespacedKey || resource instanceof com.destroystokyo.paper.NamespacedTag)) {
++ return false;
++ }
++ }
++
++ return true;
++ }
++ // Paper end - Add an API for can-place-on/can-break adventure mode predicates
+ }
+diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
+index 6930d0afb230a88aa813b02e4d55c95d3a049688..db8d8e2a07296d62c3097f02b03319e2e1ba9394 100644
+--- a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
++++ b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
+@@ -690,4 +690,22 @@ public class MaterialRerouting {
+ return ItemStack.of(material, amount);
+ }
+ // Paper end
++
++ // Paper start - methods added post 1.13, no-op (https://github.com/PaperMC/Paper/pull/1015)
++ public static Set<Material> getCanDestroy(final ItemMeta meta) {
++ return meta.getCanDestroy();
++ }
++
++ public static void setCanDestroy(final ItemMeta meta, final Set<Material> materials) {
++ meta.setCanDestroy(materials);
++ }
++
++ public static Set<Material> getCanPlaceOn(final ItemMeta meta) {
++ return meta.getCanPlaceOn();
++ }
++
++ public static void setCanPlaceOn(final ItemMeta meta, final Set<Material> materials) {
++ meta.setCanPlaceOn(materials);
++ }
++ // Paper end
+ }