aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/api/0474-Proxy-ItemStack-to-CraftItemStack.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/api/0474-Proxy-ItemStack-to-CraftItemStack.patch')
-rw-r--r--patches/api/0474-Proxy-ItemStack-to-CraftItemStack.patch474
1 files changed, 474 insertions, 0 deletions
diff --git a/patches/api/0474-Proxy-ItemStack-to-CraftItemStack.patch b/patches/api/0474-Proxy-ItemStack-to-CraftItemStack.patch
new file mode 100644
index 0000000000..246fa425d8
--- /dev/null
+++ b/patches/api/0474-Proxy-ItemStack-to-CraftItemStack.patch
@@ -0,0 +1,474 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Tue, 14 May 2024 11:57:51 -0700
+Subject: [PATCH] Proxy ItemStack to CraftItemStack
+
+
+diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
+index 9bdba60fa96edbc4be5dcf54a815579db887048b..330e3013eda204aa9b33d5e1c3104e0b595abdbc 100644
+--- a/src/main/java/org/bukkit/UnsafeValues.java
++++ b/src/main/java/org/bukkit/UnsafeValues.java
+@@ -285,4 +285,6 @@ public interface UnsafeValues {
+ @NotNull java.util.List<net.kyori.adventure.text.Component> computeTooltipLines(@NotNull ItemStack itemStack, @NotNull io.papermc.paper.inventory.tooltip.TooltipContext tooltipContext, @Nullable org.bukkit.entity.Player player); // Paper - expose itemstack tooltip lines
+
+ <A extends Keyed, M> io.papermc.paper.registry.tag.@Nullable Tag<A> getTag(io.papermc.paper.registry.tag.@NotNull TagKey<A> tagKey); // Paper - hack to get tags for non-server backed registries
++
++ ItemStack createEmptyStack(); // Paper - proxy ItemStack
+ }
+diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
+index 77edc2e1c5c865db7e101aaa186657ac85edfed9..6fc8bddfcabca8c363e0d9f958f0ce7bc39099f1 100644
+--- a/src/main/java/org/bukkit/inventory/ItemStack.java
++++ b/src/main/java/org/bukkit/inventory/ItemStack.java
+@@ -28,10 +28,38 @@ import org.jetbrains.annotations.Nullable;
+ * returns false.</b>
+ */
+ public class ItemStack implements Cloneable, ConfigurationSerializable, Translatable, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowItem>, net.kyori.adventure.translation.Translatable { // Paper
+- private Material type = Material.AIR;
+- private int amount = 0;
++ private ItemStack craftDelegate; // Paper - always delegate to server-backed stack
+ private MaterialData data = null;
+- private ItemMeta meta;
++
++ // Paper start - add static factory methods
++ /**
++ * Creates an itemstack with the specified item type and a count of 1.
++ *
++ * @param type the item type to use
++ * @return a new itemstack
++ * @throws IllegalArgumentException if the Material provided is not an item ({@link Material#isItem()})
++ */
++ @org.jetbrains.annotations.Contract(value = "_ -> new", pure = true)
++ public static @NotNull ItemStack of(final @NotNull Material type) {
++ return of(type, 1);
++ }
++
++ /**
++ * Creates an itemstack with the specified item type and count.
++ *
++ * @param type the item type to use
++ * @param amount the count of items in the stack
++ * @return a new itemstack
++ * @throws IllegalArgumentException if the Material provided is not an item ({@link Material#isItem()})
++ * @throws IllegalArgumentException if the amount is less than 1
++ */
++ @org.jetbrains.annotations.Contract(value = "_, _ -> new", pure = true)
++ public static @NotNull ItemStack of(final @NotNull Material type, final int amount) {
++ Preconditions.checkArgument(type.asItemType() != null, type + " isn't an item");
++ Preconditions.checkArgument(amount > 0, "amount must be greater than 0");
++ return java.util.Objects.requireNonNull(type.asItemType(), type + " is not an item").createItemStack(amount); // Paper - delegate
++ }
++ // Paper end
+
+ @Utility
+ protected ItemStack() {}
+@@ -44,7 +72,10 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ * {@link Material#isItem()} returns false.</b>
+ *
+ * @param type item material
++ * @apiNote use {@link #of(Material)}
++ * @see #of(Material)
+ */
++ @org.jetbrains.annotations.ApiStatus.Obsolete(since = "1.21") // Paper
+ public ItemStack(@NotNull final Material type) {
+ this(type, 1);
+ }
+@@ -58,7 +89,10 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ *
+ * @param type item material
+ * @param amount stack size
++ * @apiNote Use {@link #of(Material, int)}
++ * @see #of(Material, int)
+ */
++ @org.jetbrains.annotations.ApiStatus.Obsolete(since = "1.21") // Paper
+ public ItemStack(@NotNull final Material type, final int amount) {
+ this(type, amount, (short) 0);
+ }
+@@ -93,8 +127,9 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ type = Bukkit.getUnsafe().fromLegacy(new MaterialData(type, data == null ? (byte) damage : data), true);
+ }
+ }
+- this.type = type;
+- this.amount = amount;
++ // Paper start - create delegate
++ this.craftDelegate = type == Material.AIR ? ItemStack.empty() : ItemStack.of(type, amount);
++ // Paper end - create delegate
+ if (damage != 0) {
+ setDurability(damage);
+ }
+@@ -109,17 +144,16 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ * @param stack the stack to copy
+ * @throws IllegalArgumentException if the specified stack is null or
+ * returns an item meta not created by the item factory
++ * @apiNote Use {@link #clone()}
++ * @see #clone()
+ */
++ @org.jetbrains.annotations.ApiStatus.Obsolete(since = "1.21") // Paper
+ public ItemStack(@NotNull final ItemStack stack) throws IllegalArgumentException {
+ Preconditions.checkArgument(stack != null, "Cannot copy null stack");
+- this.type = stack.getType();
+- this.amount = stack.getAmount();
+- if (this.type.isLegacy()) {
++ this.craftDelegate = stack.clone(); // Paper - delegate
++ if (stack.getType().isLegacy()) {
+ this.data = stack.getData();
+ }
+- if (stack.hasItemMeta()) {
+- setItemMeta0(stack.getItemMeta(), type);
+- }
+ }
+
+ /**
+@@ -127,10 +161,9 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ *
+ * @return Type of the items in this stack
+ */
+- @Utility
+ @NotNull
+ public Material getType() {
+- return type;
++ return this.craftDelegate.getType(); // Paper - delegate
+ }
+
+ /**
+@@ -153,19 +186,10 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ * Using this method in ItemStacks passed in events will result in undefined behavior.
+ * @see ItemStack#withType(Material)
+ */
+- @Utility
+ @Deprecated // Paper
+ public void setType(@NotNull Material type) {
+ Preconditions.checkArgument(type != null, "Material cannot be null");
+- this.type = type;
+- if (this.meta != null) {
+- this.meta = Bukkit.getItemFactory().asMetaFor(meta, type);
+- }
+- if (type.isLegacy()) {
+- createData((byte) 0);
+- } else {
+- this.data = null;
+- }
++ this.craftDelegate.setType(type); // Paper - delegate
+ }
+ // Paper start
+ /**
+@@ -177,12 +201,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ @NotNull
+ @org.jetbrains.annotations.Contract(value = "_ -> new", pure = true)
+ public ItemStack withType(@NotNull Material type) {
+- ItemStack itemStack = new ItemStack(type, this.amount);
+- if (this.hasItemMeta()) {
+- itemStack.setItemMeta(this.getItemMeta());
+- }
+-
+- return itemStack;
++ return this.craftDelegate.withType(type); // Paper - delegate
+ }
+ // Paper end
+
+@@ -192,7 +211,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ * @return Amount of items in this stack
+ */
+ public int getAmount() {
+- return amount;
++ return this.craftDelegate.getAmount(); // Paper - delegate
+ }
+
+ /**
+@@ -201,7 +220,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ * @param amount New amount of items in this stack
+ */
+ public void setAmount(int amount) {
+- this.amount = amount;
++ this.craftDelegate.setAmount(amount); // Paper - delegate
+ }
+
+ /**
+@@ -254,11 +273,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ */
+ @Deprecated
+ public void setDurability(final short durability) {
+- ItemMeta meta = getItemMeta();
+- if (meta != null) {
+- ((Damageable) meta).setDamage(durability);
+- setItemMeta(meta);
+- }
++ this.craftDelegate.setDurability(durability); // Paper - delegate
+ }
+
+ /**
+@@ -269,8 +284,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ */
+ @Deprecated
+ public short getDurability() {
+- ItemMeta meta = getItemMeta();
+- return (meta == null) ? 0 : (short) ((Damageable) meta).getDamage();
++ return this.craftDelegate.getDurability(); // Paper - delegate
+ }
+
+ /**
+@@ -282,17 +296,12 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ *
+ * @return The maximum you can stack this item to.
+ */
+- @Utility
+ public int getMaxStackSize() {
+- if (meta != null && meta.hasMaxStackSize()) {
+- return meta.getMaxStackSize();
+- }
+-
+- return getType().getMaxStackSize();
++ return this.craftDelegate.getMaxStackSize(); // Paper - delegate
+ }
+
+ private void createData(final byte data) {
+- this.data = type.getNewData(data);
++ this.data = this.craftDelegate.getType().getNewData(data); // Paper
+ }
+
+ @Override
+@@ -306,17 +315,8 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ }
+
+ @Override
+- @Utility
+ public boolean equals(Object obj) {
+- if (this == obj) {
+- return true;
+- }
+- if (!(obj instanceof ItemStack)) {
+- return false;
+- }
+-
+- ItemStack stack = (ItemStack) obj;
+- return getAmount() == stack.getAmount() && isSimilar(stack);
++ return this.craftDelegate.equals(obj); // Paper - delegate
+ }
+
+ /**
+@@ -326,49 +326,19 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ * @param stack the item stack to compare to
+ * @return true if the two stacks are equal, ignoring the amount
+ */
+- @Utility
+ public boolean isSimilar(@Nullable ItemStack stack) {
+- if (stack == null) {
+- return false;
+- }
+- if (stack == this) {
+- return true;
+- }
+- Material comparisonType = (this.type.isLegacy()) ? Bukkit.getUnsafe().fromLegacy(this.getData(), true) : this.type; // This may be called from legacy item stacks, try to get the right material
+- return comparisonType == stack.getType() && /* getDurability() == stack.getDurability() && */hasItemMeta() == stack.hasItemMeta() && (hasItemMeta() ? Bukkit.getItemFactory().equals(getItemMeta(), stack.getItemMeta()) : true); // Paper - remove redundant item durability check
++ return this.craftDelegate.isSimilar(stack); // Paper - delegate
+ }
+
+ @NotNull
+ @Override
+ public ItemStack clone() {
+- try {
+- ItemStack itemStack = (ItemStack) super.clone();
+-
+- if (this.meta != null) {
+- itemStack.meta = this.meta.clone();
+- }
+-
+- if (this.data != null) {
+- itemStack.data = this.data.clone();
+- }
+-
+- return itemStack;
+- } catch (CloneNotSupportedException e) {
+- throw new Error(e);
+- }
++ return this.craftDelegate.clone(); // Paper - delegate
+ }
+
+ @Override
+- @Utility
+ public int hashCode() {
+- int hash = 1;
+-
+- hash = hash * 31 + getType().hashCode();
+- hash = hash * 31 + getAmount();
+- hash = hash * 31 + (getDurability() & 0xffff);
+- hash = hash * 31 + (hasItemMeta() ? (meta == null ? getItemMeta().hashCode() : meta.hashCode()) : 0);
+-
+- return hash;
++ return this.craftDelegate.hashCode(); // Paper - delegate
+ }
+
+ /**
+@@ -378,7 +348,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ * @return True if this has the given enchantment
+ */
+ public boolean containsEnchantment(@NotNull Enchantment ench) {
+- return meta == null ? false : meta.hasEnchant(ench);
++ return this.craftDelegate.containsEnchantment(ench); // Paper - delegate
+ }
+
+ /**
+@@ -388,7 +358,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ * @return Level of the enchantment, or 0
+ */
+ public int getEnchantmentLevel(@NotNull Enchantment ench) {
+- return meta == null ? 0 : meta.getEnchantLevel(ench);
++ return this.craftDelegate.getEnchantmentLevel(ench); // Paper - delegate
+ }
+
+ /**
+@@ -398,7 +368,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ */
+ @NotNull
+ public Map<Enchantment, Integer> getEnchantments() {
+- return meta == null ? ImmutableMap.<Enchantment, Integer>of() : meta.getEnchants();
++ return this.craftDelegate.getEnchantments(); // Paper - delegate
+ }
+
+ /**
+@@ -474,10 +444,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ * @param level Level of the enchantment
+ */
+ public void addUnsafeEnchantment(@NotNull Enchantment ench, int level) {
+- ItemMeta itemMeta = (meta == null ? meta = Bukkit.getItemFactory().getItemMeta(type) : meta);
+- if (itemMeta != null) {
+- itemMeta.addEnchant(ench, level, true);
+- }
++ this.craftDelegate.addUnsafeEnchantment(ench, level); // Paper - delegate
+ }
+
+ /**
+@@ -488,23 +455,14 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ * @return Previous level, or 0
+ */
+ public int removeEnchantment(@NotNull Enchantment ench) {
+- int level = getEnchantmentLevel(ench);
+- if (level == 0 || meta == null) {
+- return level;
+- }
+- meta.removeEnchant(ench);
+- return level;
++ return this.craftDelegate.removeEnchantment(ench); // Paper - delegate
+ }
+
+ /**
+ * Removes all enchantments on this ItemStack.
+ */
+ public void removeEnchantments() {
+- if (meta == null) {
+- return;
+- }
+-
+- meta.removeEnchantments();
++ this.craftDelegate.removeEnchantments(); // Paper - delegate
+ }
+
+ @Override
+@@ -660,7 +618,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ */
+ @UndefinedNullability // Paper
+ public ItemMeta getItemMeta() {
+- return this.meta == null ? Bukkit.getItemFactory().getItemMeta(this.type) : this.meta.clone();
++ return this.craftDelegate.getItemMeta(); // Paper - delegate
+ }
+
+ /**
+@@ -669,7 +627,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ * @return Returns true if some meta data has been set for this item
+ */
+ public boolean hasItemMeta() {
+- return !Bukkit.getItemFactory().equals(meta, null);
++ return this.craftDelegate.hasItemMeta(); // Paper - delegate
+ }
+
+ /**
+@@ -682,28 +640,10 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ * the {@link ItemFactory}
+ */
+ public boolean setItemMeta(@Nullable ItemMeta itemMeta) {
+- return setItemMeta0(itemMeta, type);
++ return this.craftDelegate.setItemMeta(itemMeta); // Paper - delegate
+ }
+
+- /*
+- * Cannot be overridden, so it's safe for constructor call
+- */
+- private boolean setItemMeta0(@Nullable ItemMeta itemMeta, @NotNull Material material) {
+- if (itemMeta == null) {
+- this.meta = null;
+- return true;
+- }
+- if (!Bukkit.getItemFactory().isApplicable(itemMeta, material)) {
+- return false;
+- }
+- this.meta = Bukkit.getItemFactory().asMetaFor(itemMeta, material);
+-
+- if (this.meta == itemMeta) {
+- this.meta = itemMeta.clone();
+- }
+-
+- return true;
+- }
++ // Paper - delegate
+
+ @Override
+ @NotNull
+@@ -894,11 +834,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ }
+
+ public int getMaxItemUseDuration(@NotNull final org.bukkit.entity.LivingEntity entity) {
+- if (type == null || type == Material.AIR || !type.isItem()) {
+- return 0;
+- }
+- // Requires access to NMS
+- return ensureServerConversions().getMaxItemUseDuration(entity);
++ return this.craftDelegate.getMaxItemUseDuration(entity); // Paper - delegate
+ }
+
+ /**
+@@ -1148,7 +1084,8 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ */
+ @NotNull
+ public static ItemStack empty() {
+- return new ItemStack();
++ //noinspection deprecation
++ return Bukkit.getUnsafe().createEmptyStack(); // Paper - proxy ItemStack
+ }
+
+ /**
+@@ -1156,7 +1093,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ * it is either air or the stack has a size of 0.
+ */
+ public boolean isEmpty() {
+- return type.isAir() || amount <= 0;
++ return this.craftDelegate.isEmpty(); // Paper - delegate
+ }
+ // Paper end
+ // Paper start - expose itemstack tooltip lines
+diff --git a/src/test/java/org/bukkit/configuration/ConfigurationSectionTest.java b/src/test/java/org/bukkit/configuration/ConfigurationSectionTest.java
+index ee5bc86f47cf5599e4c5c34e3a9084f86d74bdb7..c6b49c7e0a3357566fc859d4f0c76e77fefbd371 100644
+--- a/src/test/java/org/bukkit/configuration/ConfigurationSectionTest.java
++++ b/src/test/java/org/bukkit/configuration/ConfigurationSectionTest.java
+@@ -547,6 +547,7 @@ public abstract class ConfigurationSectionTest {
+ }
+
+ @Test
++ @org.junit.jupiter.api.Disabled("ItemStack can't exist without the Server, test moved to server")
+ public void testGetItemStack_String() {
+ ConfigurationSection section = getConfigurationSection();
+ String key = "exists";
+@@ -559,6 +560,7 @@ public abstract class ConfigurationSectionTest {
+ }
+
+ @Test
++ @org.junit.jupiter.api.Disabled("ItemStack can't exist without the Server, test moved to server")
+ public void testGetItemStack_String_ItemStack() {
+ ConfigurationSection section = getConfigurationSection();
+ String key = "exists";
+@@ -572,6 +574,7 @@ public abstract class ConfigurationSectionTest {
+ }
+
+ @Test
++ @org.junit.jupiter.api.Disabled("ItemStack can't exist without the Server, test moved to server")
+ public void testIsItemStack() {
+ ConfigurationSection section = getConfigurationSection();
+ String key = "exists";