From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jake Potrebic 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -0,0 +0,0 @@ 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); } @@ -0,0 +0,0 @@ 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 @@ -0,0 +0,0 @@ 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) { @@ -0,0 +0,0 @@ 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 @@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java @@ -0,0 +0,0 @@ public class CraftItemType implements ItemType.Typed, Han @NotNull @Override public ItemStack createItemStack(final int amount, @Nullable final Consumer 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java +++ b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java @@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java @@ -0,0 +0,0 @@ public final class CraftMagicNumbers implements UnsafeValues { } // Paper end - hack to get tags for non server-backed registries + // 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. *

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..0000000000000000000000000000000000000000 --- /dev/null +++ b/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java @@ -0,0 +0,0 @@ +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..0000000000000000000000000000000000000000 --- /dev/null +++ b/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java @@ -0,0 +0,0 @@ +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"); + } +}