Delegate ItemStack (#10852)

This commit is contained in:
Jake Potrebic
2024-06-17 12:12:42 -07:00
parent 27a9efdca5
commit 70f3730d25
7 changed files with 1599 additions and 8 deletions

View File

@@ -846,6 +846,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
<T> Applicator put(ItemMetaKeyType<T> key, T value) {
this.builder.set(key.TYPE, value);
@@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
private CraftFoodComponent food;
private CraftToolComponent tool;
private CraftJukeboxComponent jukebox;
- private int damage;
+ private Integer damage; // Paper - may not be set
private Integer maxDamage;
private static final Set<DataComponentType> HANDLED_TAGS = Sets.newHashSet();
@@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
this.enchantments = new EnchantmentMap(meta.enchantments); // Paper
}
@@ -876,6 +885,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}
for (Object obj : mods.keySet()) {
@@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
itemTag.put(CraftMetaItem.JUKEBOX_PLAYABLE, this.jukebox.getHandle());
}
- if (this.hasDamage()) {
+ if (this.hasDamageValue()) { // Paper - preserve empty/0 damage
itemTag.put(CraftMetaItem.DAMAGE, this.damage);
}
@@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
}
@@ -889,6 +907,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
return;
}
@@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
@Overridden
boolean isEmpty() {
- return !(this.hasDisplayName() || this.hasItemName() || this.hasLocalizedName() || this.hasEnchants() || (this.lore != null) || this.hasCustomModelData() || this.hasBlockData() || this.hasRepairCost() || !this.unhandledTags.build().isEmpty() || !this.removedTags.isEmpty() || !this.persistentDataContainer.isEmpty() || this.hideFlag != 0 || this.isHideTooltip() || this.isUnbreakable() || this.hasEnchantmentGlintOverride() || this.isFireResistant() || this.hasMaxStackSize() || this.hasRarity() || this.hasFood() || this.hasTool() || this.hasDamage() || this.hasMaxDamage() || this.hasAttributeModifiers() || this.customTag != null || this.canPlaceOnPredicates != null || this.canBreakPredicates != null); // Paper
+ return !(this.hasDisplayName() || this.hasItemName() || this.hasLocalizedName() || this.hasEnchants() || (this.lore != null) || this.hasCustomModelData() || this.hasBlockData() || this.hasRepairCost() || !this.unhandledTags.build().isEmpty() || !this.removedTags.isEmpty() || !this.persistentDataContainer.isEmpty() || this.hideFlag != 0 || this.isHideTooltip() || this.isUnbreakable() || this.hasEnchantmentGlintOverride() || this.isFireResistant() || this.hasMaxStackSize() || this.hasRarity() || this.hasFood() || this.hasTool() || this.hasDamageValue() || this.hasMaxDamage() || this.hasAttributeModifiers() || this.customTag != null || this.canPlaceOnPredicates != null || this.canBreakPredicates != null); // Paper
}
// Paper start
@@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
@Override
@@ -1037,6 +1064,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
return false;
@@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
@Override
public boolean hasDamage() {
- return this.damage > 0;
+ return this.damage != null && this.damage > 0; // Paper - null check
}
@Override
public int getDamage() {
- return this.damage;
+ return this.damage == null ? 0 : this.damage; // Paper - null check
}
@Override
public void setDamage(int damage) {
+ Preconditions.checkArgument(damage >= 0, "Damage cannot be negative"); // Paper
@@ -1044,6 +1083,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
this.damage = damage;
}
+ // Paper start - preserve empty/0 damage
+ @Override
+ public boolean hasDamageValue() {
+ return this.damage != null;
+ }
+
+ @Override
+ public void resetDamage() {
+ this.damage = null;
+ }
+ // Paper end - preserve empty/0 damage
+
@Override
public boolean hasMaxDamage() {
return this.maxDamage != null;
@@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
@Override
@@ -1062,11 +1116,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
&& (this.removedTags.equals(that.removedTags))
&& (Objects.equals(this.customTag, that.customTag))
@@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
&& (this.hasFood() ? that.hasFood() && this.food.equals(that.food) : !that.hasFood())
&& (this.hasTool() ? that.hasTool() && this.tool.equals(that.tool) : !that.hasTool())
&& (this.hasJukeboxPlayable() ? that.hasJukeboxPlayable() && this.jukebox.equals(that.jukebox) : !that.hasJukeboxPlayable())
- && (this.hasDamage() ? that.hasDamage() && this.damage == that.damage : !that.hasDamage())
+ && (Objects.equals(this.damage, that.damage)) // Paper - preserve empty/0 damage
&& (this.hasMaxDamage() ? that.hasMaxDamage() && this.maxDamage.equals(that.maxDamage) : !that.hasMaxDamage())
&& (this.canPlaceOnPredicates != null ? that.canPlaceOnPredicates != null && this.canPlaceOnPredicates.equals(that.canPlaceOnPredicates) : that.canPlaceOnPredicates == null) // Paper
&& (this.canBreakPredicates != null ? that.canBreakPredicates != null && this.canBreakPredicates.equals(that.canBreakPredicates) : that.canBreakPredicates == null) // Paper
@@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
hash = 61 * hash + (this.hasFood() ? this.food.hashCode() : 0);
hash = 61 * hash + (this.hasTool() ? this.tool.hashCode() : 0);
hash = 61 * hash + (this.hasJukeboxPlayable() ? this.jukebox.hashCode() : 0);
hash = 61 * hash + (this.hasDamage() ? this.damage : 0);
- hash = 61 * hash + (this.hasDamage() ? this.damage : 0);
- hash = 61 * hash + (this.hasMaxDamage() ? 1231 : 1237);
- hash = 61 * hash + (this.hasAttributeModifiers() ? this.attributeModifiers.hashCode() : 0);
+ hash = 61 * hash + (this.hasDamageValue() ? this.damage : -1); // Paper - preserve empty/0 damage
+ hash = 61 * hash + (this.hasMaxDamage() ? this.maxDamage.hashCode() : 0); // Paper - max damage is not a boolean
+ hash = 61 * hash + (this.attributeModifiers != null ? this.attributeModifiers.hashCode() : 0); // Paper - track only null attributes
hash = 61 * hash + (this.canPlaceOnPredicates != null ? this.canPlaceOnPredicates.hashCode() : 0); // Paper
@@ -1081,6 +1146,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
clone.attributeModifiers = LinkedHashMultimap.create(this.attributeModifiers);
}
if (this.customTag != null) {
@@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
builder.put(CraftMetaItem.JUKEBOX_PLAYABLE.BUKKIT, this.jukebox);
}
- if (this.hasDamage()) {
+ if (this.hasDamageValue()) { // Paper - preserve empty/0 damage
builder.put(CraftMetaItem.DAMAGE.BUKKIT, this.damage);
}
@@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
}

View File

@@ -69,11 +69,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+
+ assertFalse(diamond.canRepair(new ItemStack(Material.OAK_BUTTON)), "diamond can repair oak button");
+ }
+
+ @Test
+ public void testInvalidItem() {
+ ItemStack badItemStack = new ItemStack(Material.ACACIA_WALL_SIGN);
+
+ assertFalse(badItemStack.isRepairableBy(new ItemStack(Material.DIAMOND)), "acacia wall sign is repairable by diamond");
+ }
+}

View File

@@ -0,0 +1,266 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Wed, 12 Jun 2024 10:29:40 -0700
Subject: [PATCH] Make a PDC view accessible directly from ItemStack
diff --git a/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java b/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.persistence;
+
+import com.google.common.base.Preconditions;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.Tag;
+import org.bukkit.NamespacedKey;
+import org.bukkit.craftbukkit.persistence.CraftPersistentDataAdapterContext;
+import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry;
+import org.bukkit.persistence.PersistentDataAdapterContext;
+import org.bukkit.persistence.PersistentDataType;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public abstract class PaperPersistentDataContainerView implements PersistentDataContainerView {
+
+ protected final CraftPersistentDataTypeRegistry registry;
+ protected final CraftPersistentDataAdapterContext adapterContext;
+
+ public PaperPersistentDataContainerView(final CraftPersistentDataTypeRegistry registry) {
+ this.registry = registry;
+ this.adapterContext = new CraftPersistentDataAdapterContext(this.registry);
+ }
+
+ public abstract @Nullable Tag getTag(final String key);
+
+ public abstract CompoundTag toTagCompound();
+
+ @Override
+ public <P, C> boolean has(final NamespacedKey key, final PersistentDataType<P, C> type) {
+ Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
+ Preconditions.checkArgument(type != null, "The provided type cannot be null");
+
+ final @Nullable Tag value = this.getTag(key.toString());
+ if (value == null) {
+ return false;
+ }
+
+ return this.registry.isInstanceOf(type, value);
+ }
+
+ @Override
+ public boolean has(final NamespacedKey key) {
+ Preconditions.checkArgument(key != null, "The provided key for the custom value was null"); // Paper
+ return this.getTag(key.toString()) != null;
+ }
+
+ @Override
+ public <P, C> @Nullable C get(final NamespacedKey key, final PersistentDataType<P, C> type) {
+ Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
+ Preconditions.checkArgument(type != null, "The provided type cannot be null");
+
+ final @Nullable Tag value = this.getTag(key.toString());
+ if (value == null) {
+ return null;
+ }
+
+ return type.fromPrimitive(this.registry.extract(type, value), this.adapterContext);
+ }
+
+ @Override
+ public <P, C> C getOrDefault(final NamespacedKey key, final PersistentDataType<P, C> type, final C defaultValue) {
+ final C c = this.get(key, type);
+ return c != null ? c : defaultValue;
+ }
+
+ @Override
+ public PersistentDataAdapterContext getAdapterContext() {
+ return this.adapterContext;
+ }
+
+ @Override
+ public byte[] serializeToBytes() throws IOException {
+ final net.minecraft.nbt.CompoundTag root = this.toTagCompound();
+ final ByteArrayOutputStream byteArrayOutput = new ByteArrayOutputStream();
+ try (final DataOutputStream dataOutput = new DataOutputStream(byteArrayOutput)) {
+ NbtIo.write(root, dataOutput);
+ return byteArrayOutput.toByteArray();
+ }
+ }
+}
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 @@ public final class CraftItemStack extends ItemStack {
return mirrored;
}
// Paper end
+
+ // Paper start - pdc
+ private net.minecraft.nbt.CompoundTag getPdcTag() {
+ if (this.handle == null) {
+ return new net.minecraft.nbt.CompoundTag();
+ }
+ final net.minecraft.world.item.component.CustomData customData = this.handle.getOrDefault(DataComponents.CUSTOM_DATA, net.minecraft.world.item.component.CustomData.EMPTY);
+ // getUnsafe is OK here because we are only ever *reading* the data so immutability is preserved
+ //noinspection deprecation
+ return customData.getUnsafe().getCompound("PublicBukkitValues");
+ }
+
+ private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
+ private final io.papermc.paper.persistence.PaperPersistentDataContainerView pdcView = new io.papermc.paper.persistence.PaperPersistentDataContainerView(REGISTRY) {
+
+ @Override
+ public net.minecraft.nbt.CompoundTag toTagCompound() {
+ return CraftItemStack.this.getPdcTag();
+ }
+
+ @Override
+ public net.minecraft.nbt.Tag getTag(final String key) {
+ return CraftItemStack.this.getPdcTag().get(key);
+ }
+
+ @Override
+ public java.util.Set<org.bukkit.NamespacedKey> getKeys() {
+ java.util.Set<org.bukkit.NamespacedKey> keys = new java.util.HashSet<>();
+ CraftItemStack.this.getPdcTag().getAllKeys().forEach(key -> {
+ final String[] keyData = key.split(":", 2);
+ if (keyData.length == 2) {
+ keys.add(new org.bukkit.NamespacedKey(keyData[0], keyData[1]));
+ }
+ });
+ return java.util.Collections.unmodifiableSet(keys);
+ };
+
+ @Override
+ public boolean isEmpty() {
+ return CraftItemStack.this.getPdcTag().isEmpty();
+ }
+
+ @Override
+ public void copyTo(final org.bukkit.persistence.PersistentDataContainer other, final boolean replace) {
+ Preconditions.checkArgument(other != null, "The target container cannot be null");
+ final org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer target = (org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer) other;
+ final net.minecraft.nbt.CompoundTag pdcTag = org.bukkit.craftbukkit.inventory.CraftItemStack.this.getPdcTag();
+ for (final String key : pdcTag.getAllKeys()) {
+ if (replace || !target.getRaw().containsKey(key)) {
+ target.getRaw().put(key, pdcTag.get(key).copy());
+ }
+ }
+ }
+ };
+ @Override
+ public io.papermc.paper.persistence.PersistentDataContainerView getPersistentDataContainer() {
+ return this.pdcView;
+ }
+ // Paper end - pdc
}
diff --git a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
+++ b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
@@ -0,0 +0,0 @@ import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
-public class CraftPersistentDataContainer implements PersistentDataContainer {
+public class CraftPersistentDataContainer extends io.papermc.paper.persistence.PaperPersistentDataContainerView implements PersistentDataContainer { // Paper - split up view and mutable
private final Map<String, Tag> customDataTags = new HashMap<>();
- private final CraftPersistentDataTypeRegistry registry;
- private final CraftPersistentDataAdapterContext adapterContext;
+ // Paper - move to PersistentDataContainerView
public CraftPersistentDataContainer(Map<String, Tag> customTags, CraftPersistentDataTypeRegistry registry) {
this(registry);
@@ -0,0 +0,0 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
}
public CraftPersistentDataContainer(CraftPersistentDataTypeRegistry registry) {
- this.registry = registry;
- this.adapterContext = new CraftPersistentDataAdapterContext(this.registry);
+ super(registry); // Paper - move to PersistentDataContainerView
}
+ // Paper start
+ @Override
+ public Tag getTag(final String key) {
+ return this.customDataTags.get(key);
+ }
+ // Paper end
@Override
public <T, Z> void set(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type, @NotNull Z value) {
@@ -0,0 +0,0 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
this.customDataTags.put(key.toString(), this.registry.wrap(type, type.toPrimitive(value, this.adapterContext)));
}
- @Override
- public <T, Z> boolean has(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type) {
- Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
- Preconditions.checkArgument(type != null, "The provided type cannot be null");
-
- Tag value = this.customDataTags.get(key.toString());
- if (value == null) {
- return false;
- }
-
- return this.registry.isInstanceOf(type, value);
- }
-
- @Override
- public boolean has(NamespacedKey key) {
- Preconditions.checkArgument(key != null, "The provided key for the custom value was null"); // Paper
- return this.customDataTags.get(key.toString()) != null;
- }
-
- @Override
- public <T, Z> Z get(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type) {
- Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
- Preconditions.checkArgument(type != null, "The provided type cannot be null");
-
- Tag value = this.customDataTags.get(key.toString());
- if (value == null) {
- return null;
- }
-
- return type.fromPrimitive(this.registry.extract(type, value), this.adapterContext);
- }
-
- @NotNull
- @Override
- public <T, Z> Z getOrDefault(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type, @NotNull Z defaultValue) {
- Z z = this.get(key, type);
- return z != null ? z : defaultValue;
- }
+ // Paper - move to PersistentDataContainerView
@NotNull
@Override
@@ -0,0 +0,0 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
// Paper end
// Paper start - byte array serialization
- @Override
- public byte[] serializeToBytes() throws java.io.IOException {
- final net.minecraft.nbt.CompoundTag root = this.toTagCompound();
- final java.io.ByteArrayOutputStream byteArrayOutput = new java.io.ByteArrayOutputStream();
- try (final java.io.DataOutputStream dataOutput = new java.io.DataOutputStream(byteArrayOutput)) {
- net.minecraft.nbt.NbtIo.write(root, dataOutput);
- return byteArrayOutput.toByteArray();
- }
- }
-
+ // Paper - move to PersistentDataContainerView
@Override
public void readFromBytes(final byte[] bytes, final boolean clear) throws java.io.IOException {
if (clear) {

View File

@@ -0,0 +1,300 @@
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 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.bukkit.material.MaterialData;
@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<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 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.
* <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..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.AbstractTestingBase;
+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 extends AbstractTestingBase {
+ 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;
+
+public class MemorySectionTest extends ConfigurationSectionTest {
+ @Override
+ public ConfigurationSection getConfigurationSection() {
+ return new MemoryConfiguration().createSection("section");
+ }
+}