Skip to content

Commit c467df9

Browse files
Add ItemStack#copyDataFrom (#12224)
1 parent 5a6ab97 commit c467df9

File tree

3 files changed

+54
-8
lines changed

3 files changed

+54
-8
lines changed

paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
7979
RegistryKey<BlockType> BLOCK = create("block");
8080
/**
8181
* @apiNote use preferably only in the context of registry entries.
82-
* @see io.papermc.paper.registry.data
82+
* @see io.papermc.paper.registry.keys.ItemTypeKeys
8383
*/
8484
@ApiStatus.Experimental // Paper - already required for registry builders
8585
RegistryKey<ItemType> ITEM = create("item");

paper-api/src/main/java/org/bukkit/inventory/ItemStack.java

+26
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.Locale;
77
import java.util.Map;
88
import java.util.function.Consumer;
9+
import java.util.function.Predicate;
910
import net.kyori.adventure.text.Component;
1011
import org.bukkit.Bukkit;
1112
import org.bukkit.Material;
@@ -1306,6 +1307,31 @@ public void resetData(final io.papermc.paper.datacomponent.@NotNull DataComponen
13061307
this.craftDelegate.resetData(type);
13071308
}
13081309

1310+
/**
1311+
* Copies component values and component removals from the provided ItemStack.
1312+
* <p>
1313+
* Example:
1314+
* <pre>{@code
1315+
* Set<DataComponentType> types = Set.of(
1316+
* DataComponentTypes.CONSUMABLE,
1317+
* DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE,
1318+
* DataComponentTypes.RARITY
1319+
* );
1320+
*
1321+
* ItemStack source = ItemStack.of(Material.ENCHANTED_GOLDEN_APPLE);
1322+
* ItemStack target = ItemStack.of(Material.GOLDEN_CARROT);
1323+
*
1324+
* target.copyDataFrom(source, types::contains);
1325+
* }</pre>
1326+
*
1327+
* @param source the item stack to copy from
1328+
* @param filter predicate for which components to copy
1329+
*/
1330+
@org.jetbrains.annotations.ApiStatus.Experimental
1331+
public void copyDataFrom(final @NotNull ItemStack source, final @NotNull Predicate<io.papermc.paper.datacomponent.@NotNull DataComponentType> filter) {
1332+
this.craftDelegate.copyDataFrom(source, filter);
1333+
}
1334+
13091335
/**
13101336
* Checks if the data component type is overridden from the default for the
13111337
* item type.

paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java

+27-7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.Map;
88
import java.util.Optional;
99
import java.util.function.Consumer;
10+
import java.util.function.Predicate;
1011
import net.kyori.adventure.text.Component;
1112
import net.minecraft.advancements.critereon.ItemPredicate;
1213
import net.minecraft.advancements.critereon.MinMaxBounds;
@@ -15,6 +16,7 @@
1516
import net.minecraft.core.component.DataComponentMap;
1617
import net.minecraft.core.component.DataComponentPatch;
1718
import net.minecraft.core.component.DataComponentPredicate;
19+
import net.minecraft.core.component.DataComponentType;
1820
import net.minecraft.core.component.DataComponents;
1921
import net.minecraft.core.component.PatchedDataComponentMap;
2022
import net.minecraft.nbt.CompoundTag;
@@ -54,7 +56,7 @@ private static CraftItemStack getCraftStack(final ItemStack bukkit) {
5456
if (bukkit instanceof final CraftItemStack craftItemStack) {
5557
return craftItemStack;
5658
} else {
57-
return (CraftItemStack) API_ITEM_STACK_CRAFT_DELEGATE_FIELD.get(bukkit);
59+
return (CraftItemStack) API_ITEM_STACK_CRAFT_DELEGATE_FIELD.get(bukkit);
5860
}
5961
}
6062

@@ -71,12 +73,12 @@ public int hashCode() {
7173

7274
@Override
7375
public boolean equals(final Object obj) {
74-
if (!(obj instanceof final org.bukkit.inventory.ItemStack bukkit)) return false;
76+
if (!(obj instanceof final ItemStack bukkit)) return false;
7577
final CraftItemStack craftStack = getCraftStack(bukkit);
7678
if (this.handle == craftStack.handle) return true;
77-
else if (this.handle == null || craftStack.handle == null) return false;
78-
else if (this.handle.isEmpty() && craftStack.handle.isEmpty()) return true;
79-
else return net.minecraft.world.item.ItemStack.matches(this.handle, craftStack.handle);
79+
if (this.handle == null || craftStack.handle == null) return false;
80+
if (this.handle.isEmpty() && craftStack.handle.isEmpty()) return true;
81+
return net.minecraft.world.item.ItemStack.matches(this.handle, craftStack.handle);
8082
}
8183
// Paper end
8284

@@ -648,14 +650,32 @@ private <M> void resetData(final io.papermc.paper.datacomponent.PaperDataCompone
648650
this.handle.set(nms, nmsValue);
649651
}
650652

653+
@Override
654+
public void copyDataFrom(final ItemStack source, final Predicate<io.papermc.paper.datacomponent.DataComponentType> filter) {
655+
Preconditions.checkArgument(source != null, "source cannot be null");
656+
Preconditions.checkArgument(filter != null, "filter cannot be null");
657+
if (this.isEmpty() || source.isEmpty()) {
658+
return;
659+
}
660+
661+
final Predicate<DataComponentType<?>> nmsFilter = nms -> filter.test(io.papermc.paper.datacomponent.PaperDataComponentType.minecraftToBukkit(nms));
662+
net.minecraft.world.item.ItemStack sourceNmsStack = getCraftStack(source).handle;
663+
this.handle.applyComponents(sourceNmsStack.getPrototype().filter(nmsType -> {
664+
return !sourceNmsStack.hasNonDefault(nmsType) && nmsFilter.test(nmsType);
665+
}));
666+
667+
final DataComponentPatch.SplitResult split = sourceNmsStack.getComponentsPatch().split();
668+
this.handle.applyComponents(split.added().filter(nmsFilter));
669+
split.removed().stream().filter(nmsFilter).forEach(this.handle::remove);
670+
}
671+
651672
@Override
652673
public boolean isDataOverridden(final io.papermc.paper.datacomponent.DataComponentType type) {
653674
if (this.isEmpty()) {
654675
return false;
655676
}
656677
final net.minecraft.core.component.DataComponentType<?> nms = io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type);
657-
// maybe a more efficient way is to expose the "patch" map in PatchedDataComponentMap and just check if the type exists as a key
658-
return !java.util.Objects.equals(this.handle.get(nms), this.handle.getPrototype().get(nms));
678+
return this.handle.hasNonDefault(nms);
659679
}
660680

661681
@Override

0 commit comments

Comments
 (0)