diff --git a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java index 0238eb155e..49e508c4ed 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java @@ -79,7 +79,7 @@ public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl { RegistryKey BLOCK = create("block"); /** * @apiNote use preferably only in the context of registry entries. - * @see io.papermc.paper.registry.data + * @see io.papermc.paper.registry.keys.ItemTypeKeys */ @ApiStatus.Experimental // Paper - already required for registry builders RegistryKey ITEM = create("item"); diff --git a/paper-api/src/main/java/org/bukkit/inventory/ItemStack.java b/paper-api/src/main/java/org/bukkit/inventory/ItemStack.java index dc50f83e96..aad9b078a2 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/ItemStack.java +++ b/paper-api/src/main/java/org/bukkit/inventory/ItemStack.java @@ -6,6 +6,7 @@ import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; import java.util.function.Consumer; +import java.util.function.Predicate; import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -1306,6 +1307,31 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat this.craftDelegate.resetData(type); } + /** + * Copies component values and component removals from the provided ItemStack. + *

+ * Example: + *

{@code
+     * Set types = Set.of(
+     *     DataComponentTypes.CONSUMABLE,
+     *     DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE,
+     *     DataComponentTypes.RARITY
+     * );
+     *
+     * ItemStack source = ItemStack.of(Material.ENCHANTED_GOLDEN_APPLE);
+     * ItemStack target = ItemStack.of(Material.GOLDEN_CARROT);
+     *
+     * target.copyDataFrom(source, types::contains);
+     * }
+ * + * @param source the item stack to copy from + * @param filter predicate for which components to copy + */ + @org.jetbrains.annotations.ApiStatus.Experimental + public void copyDataFrom(final @NotNull ItemStack source, final @NotNull Predicate filter) { + this.craftDelegate.copyDataFrom(source, filter); + } + /** * Checks if the data component type is overridden from the default for the * item type. diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java index a6668ae293..d3dc1f7fe2 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -7,6 +7,7 @@ import java.util.Collections; import java.util.Map; import java.util.Optional; import java.util.function.Consumer; +import java.util.function.Predicate; import net.kyori.adventure.text.Component; import net.minecraft.advancements.critereon.ItemPredicate; import net.minecraft.advancements.critereon.MinMaxBounds; @@ -15,6 +16,7 @@ import net.minecraft.core.HolderSet; import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponentPredicate; +import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; import net.minecraft.core.component.PatchedDataComponentMap; import net.minecraft.nbt.CompoundTag; @@ -54,7 +56,7 @@ public final class CraftItemStack extends ItemStack { if (bukkit instanceof final CraftItemStack craftItemStack) { return craftItemStack; } else { - return (CraftItemStack) API_ITEM_STACK_CRAFT_DELEGATE_FIELD.get(bukkit); + return (CraftItemStack) API_ITEM_STACK_CRAFT_DELEGATE_FIELD.get(bukkit); } } @@ -71,12 +73,12 @@ public final class CraftItemStack extends ItemStack { @Override public boolean equals(final Object obj) { - if (!(obj instanceof final org.bukkit.inventory.ItemStack bukkit)) return false; + if (!(obj instanceof final 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); + if (this.handle == null || craftStack.handle == null) return false; + if (this.handle.isEmpty() && craftStack.handle.isEmpty()) return true; + return net.minecraft.world.item.ItemStack.matches(this.handle, craftStack.handle); } // Paper end @@ -648,14 +650,32 @@ public final class CraftItemStack extends ItemStack { this.handle.set(nms, nmsValue); } + @Override + public void copyDataFrom(final ItemStack source, final Predicate filter) { + Preconditions.checkArgument(source != null, "source cannot be null"); + Preconditions.checkArgument(filter != null, "filter cannot be null"); + if (this.isEmpty() || source.isEmpty()) { + return; + } + + final Predicate> nmsFilter = nms -> filter.test(io.papermc.paper.datacomponent.PaperDataComponentType.minecraftToBukkit(nms)); + net.minecraft.world.item.ItemStack sourceNmsStack = getCraftStack(source).handle; + this.handle.applyComponents(sourceNmsStack.getPrototype().filter(nmsType -> { + return !sourceNmsStack.hasNonDefault(nmsType) && nmsFilter.test(nmsType); + })); + + final DataComponentPatch.SplitResult split = sourceNmsStack.getComponentsPatch().split(); + this.handle.applyComponents(split.added().filter(nmsFilter)); + split.removed().stream().filter(nmsFilter).forEach(this.handle::remove); + } + @Override public boolean isDataOverridden(final io.papermc.paper.datacomponent.DataComponentType type) { if (this.isEmpty()) { return false; } final net.minecraft.core.component.DataComponentType nms = io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type); - // maybe a more efficient way is to expose the "patch" map in PatchedDataComponentMap and just check if the type exists as a key - return !java.util.Objects.equals(this.handle.get(nms), this.handle.getPrototype().get(nms)); + return this.handle.hasNonDefault(nms); } @Override