Paper/patches/server/0990-Proxy-ItemStack-to-CraftItemStack.patch
2024-12-03 22:06:05 +01:00

303 lines
14 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Tue, 14 May 2024 11:57:43 -0700
Subject: [PATCH] Proxy ItemStack to CraftItemStack
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
index bb2b4528692aed8e3341428697a60c0abee13779..49d2deac8d42a505b75f2196ef895a5564b62cac 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -31,15 +31,57 @@ import org.jetbrains.annotations.ApiStatus;
@DelegateDeserialization(ItemStack.class)
public final class CraftItemStack extends ItemStack {
- // Paper start - MC Utils
- public static net.minecraft.world.item.ItemStack unwrap(ItemStack bukkit) {
- if (bukkit instanceof CraftItemStack craftItemStack) {
- return craftItemStack.handle != null ? craftItemStack.handle : net.minecraft.world.item.ItemStack.EMPTY;
+ // Paper start - delegate api-ItemStack to CraftItemStack
+ private static final java.lang.invoke.VarHandle API_ITEM_STACK_CRAFT_DELEGATE_FIELD;
+ static {
+ try {
+ API_ITEM_STACK_CRAFT_DELEGATE_FIELD = java.lang.invoke.MethodHandles.privateLookupIn(
+ ItemStack.class,
+ java.lang.invoke.MethodHandles.lookup()
+ ).findVarHandle(ItemStack.class, "craftDelegate", ItemStack.class);
+ } catch (final IllegalAccessException | NoSuchFieldException exception) {
+ throw new RuntimeException(exception);
+ }
+ }
+
+ private static CraftItemStack getCraftStack(final ItemStack bukkit) {
+ if (bukkit instanceof final CraftItemStack craftItemStack) {
+ return craftItemStack;
} else {
- return asNMSCopy(bukkit);
+ return (CraftItemStack) API_ITEM_STACK_CRAFT_DELEGATE_FIELD.get(bukkit);
}
}
+ @Override
+ public int hashCode() {
+ if (this.handle == null || this.handle.isEmpty()) {
+ return net.minecraft.world.item.ItemStack.EMPTY.hashCode();
+ } else {
+ int hash = net.minecraft.world.item.ItemStack.hashItemAndComponents(this.handle);
+ hash = hash * 31 + this.handle.getCount();
+ return hash;
+ }
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (!(obj instanceof final org.bukkit.inventory.ItemStack bukkit)) return false;
+ final CraftItemStack craftStack = getCraftStack(bukkit);
+ if (this.handle == craftStack.handle) return true;
+ else if (this.handle == null || craftStack.handle == null) return false;
+ else if (this.handle.isEmpty() && craftStack.handle.isEmpty()) return true;
+ else return net.minecraft.world.item.ItemStack.matches(this.handle, craftStack.handle);
+ }
+ // Paper end
+
+ // Paper start - MC Utils
+ public static net.minecraft.world.item.ItemStack unwrap(ItemStack bukkit) {
+ // Paper start - re-implement after delegating all api ItemStack calls to CraftItemStack
+ final CraftItemStack craftItemStack = getCraftStack(bukkit);
+ return craftItemStack.handle == null ? net.minecraft.world.item.ItemStack.EMPTY : craftItemStack.handle;
+ // Paper end - re-implement after delegating all api ItemStack calls to CraftItemStack
+ }
+
public static net.minecraft.world.item.ItemStack getOrCloneOnMutation(ItemStack old, ItemStack newInstance) {
return old == newInstance ? unwrap(old) : asNMSCopy(newInstance);
}
@@ -53,25 +95,13 @@ public final class CraftItemStack extends ItemStack {
// Paper end - override isEmpty to use vanilla's impl
public static net.minecraft.world.item.ItemStack asNMSCopy(ItemStack original) {
- if (original instanceof CraftItemStack) {
- CraftItemStack stack = (CraftItemStack) original;
- return stack.handle == null ? net.minecraft.world.item.ItemStack.EMPTY : stack.handle.copy();
- }
- if (original == null || original.isEmpty()) { // Paper - override isEmpty to use vanilla's impl; use isEmpty
+ // Paper start - re-implement after delegating all api ItemStack calls to CraftItemStack
+ if (original == null || original.isEmpty()) {
return net.minecraft.world.item.ItemStack.EMPTY;
}
-
- Item item = CraftItemType.bukkitToMinecraft(original.getType());
-
- if (item == null) {
- return net.minecraft.world.item.ItemStack.EMPTY;
- }
-
- net.minecraft.world.item.ItemStack stack = new net.minecraft.world.item.ItemStack(item, original.getAmount());
- if (original.hasItemMeta()) {
- CraftItemStack.setItemMeta(stack, original.getItemMeta());
- }
- return stack;
+ final CraftItemStack stack = getCraftStack(original);
+ return stack.handle == null ? net.minecraft.world.item.ItemStack.EMPTY : stack.handle.copy();
+ // Paper end - re-implement after delegating all api ItemStack calls to CraftItemStack
}
// Paper start
@@ -94,14 +124,10 @@ public final class CraftItemStack extends ItemStack {
* Copies the NMS stack to return as a strictly-Bukkit stack
*/
public static ItemStack asBukkitCopy(net.minecraft.world.item.ItemStack original) {
- if (original.isEmpty()) {
- return new ItemStack(Material.AIR);
- }
- ItemStack stack = new ItemStack(CraftItemType.minecraftToBukkit(original.getItem()), original.getCount());
- if (CraftItemStack.hasItemMeta(original)) {
- stack.setItemMeta(CraftItemStack.getItemMeta(original));
- }
- return stack;
+ // Paper start - no such thing as a "strictly-Bukkit stack" anymore
+ // we copy the stack since it should be a complete copy not a mirror
+ return asCraftMirror(original.copy());
+ // Paper end
}
public static CraftItemStack asCraftMirror(net.minecraft.world.item.ItemStack original) {
@@ -329,11 +355,7 @@ public final class CraftItemStack extends ItemStack {
@Override
public CraftItemStack clone() {
- CraftItemStack itemStack = (CraftItemStack) super.clone();
- if (this.handle != null) {
- itemStack.handle = this.handle.copy();
- }
- return itemStack;
+ return new org.bukkit.craftbukkit.inventory.CraftItemStack(this.handle != null ? this.handle.copy() : null); // Paper
}
@Override
@@ -436,22 +458,14 @@ public final class CraftItemStack extends ItemStack {
if (stack == this) {
return true;
}
- if (!(stack instanceof CraftItemStack)) {
- return stack.getClass() == ItemStack.class && stack.isSimilar(this);
- }
-
- CraftItemStack that = (CraftItemStack) stack;
+ final CraftItemStack that = getCraftStack(stack); // Paper - re-implement after delegating all api ItemStack calls to CraftItemStack
if (this.handle == that.handle) {
return true;
}
if (this.handle == null || that.handle == null) {
return false;
}
- Material comparisonType = CraftLegacy.fromLegacy(that.getType()); // This may be called from legacy item stacks, try to get the right material
- if (!(comparisonType == this.getType() && this.getDurability() == that.getDurability())) {
- return false;
- }
- return this.hasItemMeta() ? that.hasItemMeta() && this.handle.getComponents().equals(that.handle.getComponents()) : !that.hasItemMeta();
+ return net.minecraft.world.item.ItemStack.isSameItemSameComponents(this.handle, that.handle); // Paper - re-implement after delegating all api ItemStack calls to CraftItemStack
}
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
index f4a6ee6dfcb2d516a9a1a9c81494b50a629110e4..96dfcfa12c63c682edcdec98647ca6a94d9fb4ed 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
@@ -100,13 +100,14 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han
@NotNull
@Override
public ItemStack createItemStack(final int amount, @Nullable final Consumer<? super M> metaConfigurator) {
- final ItemStack itemStack = new ItemStack(this.asMaterial(), amount);
+ // Paper start - re-implement to return CraftItemStack
+ final net.minecraft.world.item.ItemStack stack = new net.minecraft.world.item.ItemStack(this.item, amount);
+ final CraftItemStack mirror = CraftItemStack.asCraftMirror(stack);
if (metaConfigurator != null) {
- final ItemMeta itemMeta = itemStack.getItemMeta();
- metaConfigurator.accept((M) itemMeta);
- itemStack.setItemMeta(itemMeta);
+ mirror.editMeta(this.getItemMetaClass(), metaConfigurator);
}
- return itemStack;
+ return mirror;
+ // Paper start - reimplement to return CraftItemStack
}
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
index 6cc9d7a9e6d4bfdc27e52fc581b2bb832616f121..6930d0afb230a88aa813b02e4d55c95d3a049688 100644
--- a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
+++ b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
@@ -678,4 +678,16 @@ public class MaterialRerouting {
return itemStack.withType(material);
}
// Paper end - register paper API specific material consumers in rerouting
+
+ // Paper start - methods added post 1.13, no-op
+ @RerouteStatic("org/bukkit/inventory/ItemStack")
+ public static ItemStack of(final Material material) {
+ return ItemStack.of(material);
+ }
+
+ @RerouteStatic("org/bukkit/inventory/ItemStack")
+ public static ItemStack of(final Material material, final int amount) {
+ return ItemStack.of(material, amount);
+ }
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index 0e2d9de778e9747d0c2ab897d36c623d2ffbeb95..92aeb48e565304d12a359d00f70e9bc3046419ca 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -669,6 +669,13 @@ public final class CraftMagicNumbers implements UnsafeValues {
}
// Paper end - lifecycle event API
+ // Paper start - proxy ItemStack
+ @Override
+ public org.bukkit.inventory.ItemStack createEmptyStack() {
+ return CraftItemStack.asCraftMirror(null);
+ }
+ // Paper end - proxy ItemStack
+
/**
* This helper class represents the different NBT Tags.
* <p>
diff --git a/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java b/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed45bfa577579afcbd54d655c3b5d05d6c6f3e86
--- /dev/null
+++ b/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java
@@ -0,0 +1,53 @@
+package io.papermc.paper.configuration;
+
+import org.bukkit.Material;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.support.environment.VanillaFeature;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public abstract class ConfigurationSectionTest {
+ public abstract ConfigurationSection getConfigurationSection();
+
+ @Test
+ public void testGetItemStack_String() {
+ ConfigurationSection section = getConfigurationSection();
+ String key = "exists";
+ ItemStack value = new ItemStack(Material.ACACIA_WOOD, 50);
+
+ section.set(key, value);
+
+ assertEquals(value, section.getItemStack(key));
+ assertNull(section.getString("doesntExist"));
+ }
+
+ @Test
+ public void testGetItemStack_String_ItemStack() {
+ ConfigurationSection section = getConfigurationSection();
+ String key = "exists";
+ ItemStack value = new ItemStack(Material.ACACIA_WOOD, 50);
+ ItemStack def = new ItemStack(Material.STONE, 1);
+
+ section.set(key, value);
+
+ assertEquals(value, section.getItemStack(key, def));
+ assertEquals(def, section.getItemStack("doesntExist", def));
+ }
+
+ @Test
+ public void testIsItemStack() {
+ ConfigurationSection section = getConfigurationSection();
+ String key = "exists";
+ ItemStack value = new ItemStack(Material.ACACIA_WOOD, 50);
+
+ section.set(key, value);
+
+ assertTrue(section.isItemStack(key));
+ assertFalse(section.isItemStack("doesntExist"));
+ }
+}
diff --git a/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java b/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c00085328ce8a00fc274632a556ab27660fa57ed
--- /dev/null
+++ b/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java
@@ -0,0 +1,13 @@
+package io.papermc.paper.configuration;
+
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.MemoryConfiguration;
+import org.bukkit.support.environment.Normal;
+
+@Normal
+public class MemorySectionTest extends ConfigurationSectionTest {
+ @Override
+ public ConfigurationSection getConfigurationSection() {
+ return new MemoryConfiguration().createSection("section");
+ }
+}