diff options
author | Jake Potrebic <[email protected]> | 2024-06-03 12:00:45 -0700 |
---|---|---|
committer | Owen1212055 <[email protected]> | 2024-11-18 14:50:37 -0500 |
commit | a9a35ffe103cb3a428806e47522e10e12833fc3b (patch) | |
tree | c593a8e82d9c30fe68f4877976a0e2d32afcffc1 | |
parent | e5de01bcf034af1902f9f8cdfa6e14813d81daba (diff) | |
download | Paper-a9a35ffe103cb3a428806e47522e10e12833fc3b.tar.gz Paper-a9a35ffe103cb3a428806e47522e10e12833fc3b.zip |
remove map types
-rw-r--r-- | patches/api/0485-WIP-DataComponent-API.patch (renamed from patches/api/0484-WIP-DataComponent-API.patch) | 505 | ||||
-rw-r--r-- | patches/server/1054-WIP-DataComponent-API.patch (renamed from patches/server/1053-WIP-DataComponent-API.patch) | 414 |
2 files changed, 237 insertions, 682 deletions
diff --git a/patches/api/0484-WIP-DataComponent-API.patch b/patches/api/0485-WIP-DataComponent-API.patch index 365da8d9ac..11143445af 100644 --- a/patches/api/0484-WIP-DataComponent-API.patch +++ b/patches/api/0485-WIP-DataComponent-API.patch @@ -914,176 +914,6 @@ index 0000000000000000000000000000000000000000..36e48ef697c001fff1697542eae6f79b + + } +} -diff --git a/src/main/java/io/papermc/paper/component/map/DataComponentMap.java b/src/main/java/io/papermc/paper/component/map/DataComponentMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..98d3c3b32557b54c1836a8649fa4312a93f49fc0 ---- /dev/null -+++ b/src/main/java/io/papermc/paper/component/map/DataComponentMap.java -@@ -0,0 +1,31 @@ -+package io.papermc.paper.component.map; -+ -+import io.papermc.paper.component.DataComponentType; -+import org.checkerframework.checker.nullness.qual.NonNull; -+import org.checkerframework.checker.nullness.qual.Nullable; -+import org.jetbrains.annotations.Contract; -+import org.jetbrains.annotations.Unmodifiable; -+import java.util.Set; -+ -+public interface DataComponentMap { -+ -+ @Contract(value = "-> new", pure = true) -+ static @NonNull DataComponentMap empty() { -+ return DataComponentPatchMapBridge.Holder.bridge().empty(); -+ } -+ -+ @Contract(pure = true) -+ <T> @Nullable T get(DataComponentType.@NonNull Valued<T> type); -+ -+ @Contract(value = "_, !null -> !null", pure = true) -+ default <T> @Nullable T getOrDefault(final DataComponentType.@NonNull Valued<? extends T> type, final @Nullable T fallback) { -+ final T object = this.get(type); -+ return object != null ? object : fallback; -+ } -+ -+ @Contract(pure = true) -+ boolean has(@NonNull DataComponentType type); -+ -+ @NonNull @Unmodifiable Set<DataComponentType> keySet(); -+ -+} -diff --git a/src/main/java/io/papermc/paper/component/map/DataComponentPatchMapBridge.java b/src/main/java/io/papermc/paper/component/map/DataComponentPatchMapBridge.java -new file mode 100644 -index 0000000000000000000000000000000000000000..c4b4f7d5d3c02d94733f78b3d15a7d8703db4d88 ---- /dev/null -+++ b/src/main/java/io/papermc/paper/component/map/DataComponentPatchMapBridge.java -@@ -0,0 +1,30 @@ -+package io.papermc.paper.component.map; -+ -+import java.util.Optional; -+import net.kyori.adventure.util.Services; -+import org.bukkit.Material; -+import org.bukkit.inventory.meta.ItemMeta; -+import org.jetbrains.annotations.ApiStatus; -+ -+public interface DataComponentPatchMapBridge { -+ -+ PatchedDataComponentMap of(DataComponentMap map); -+ -+ DataComponentMap empty(); -+ -+ DataComponentMap fromItem(Material material); -+ -+ PatchedDataComponentMap fromItemAndMeta(Material material, ItemMeta meta); -+ -+ ItemMeta toItemMeta(Material material, PatchedDataComponentMap map); -+ -+ @ApiStatus.Internal -+ final class Holder { -+ private static final Optional<DataComponentPatchMapBridge> BRIDGE = Services.service(DataComponentPatchMapBridge.class); -+ -+ public static DataComponentPatchMapBridge bridge() { -+ return BRIDGE.orElseThrow(); -+ } -+ } -+} -diff --git a/src/main/java/io/papermc/paper/component/map/PatchedDataComponentMap.java b/src/main/java/io/papermc/paper/component/map/PatchedDataComponentMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d284fbb11beea9d18e297605b01e30796e60e3b1 ---- /dev/null -+++ b/src/main/java/io/papermc/paper/component/map/PatchedDataComponentMap.java -@@ -0,0 +1,51 @@ -+package io.papermc.paper.component.map; -+ -+import io.papermc.paper.component.DataComponentType; -+import org.checkerframework.checker.nullness.qual.NonNull; -+import org.jetbrains.annotations.Nullable; -+ -+public interface PatchedDataComponentMap extends DataComponentMap { -+ -+ static @NonNull PatchedDataComponentMap empty() { -+ return DataComponentPatchMapBridge.Holder.bridge().of(DataComponentPatchMapBridge.Holder.bridge().empty()); -+ } -+ -+ static @NonNull PatchedDataComponentMap of(final @NonNull DataComponentMap map) { -+ return DataComponentPatchMapBridge.Holder.bridge().of(map); -+ } -+ -+ /** -+ * Sets this data component type to be present with a value in this map. -+ * <p> -+ * Note: supplying null will act similarly to {@link PatchedDataComponentMap#unset(DataComponentType)} -+ * -+ * @param type component type -+ * @param value set value -+ * @param <T> type -+ */ -+ <T> void set(DataComponentType.@NonNull Valued<T> type, @Nullable T value); -+ -+ /** -+ * Sets this data component type to be present in this map. -+ * -+ * @param type type -+ */ -+ void set(DataComponentType.@NonNull NonValued type); -+ -+ /** -+ * Unsets the value from this map. -+ * -+ * @param type data component type -+ */ -+ void unset(@NonNull DataComponentType type); -+ -+ /** -+ * Resets the value of this component to be the default value as -+ * supplied in the base map of this patched map. -+ * -+ * @param type data component type to reset -+ */ -+ void reset(@NonNull DataComponentType type); -+ -+ @NonNull PatchedDataComponentMap copy(); -+} -diff --git a/src/main/java/io/papermc/paper/component/map/PatchedDataComponentMapHolder.java b/src/main/java/io/papermc/paper/component/map/PatchedDataComponentMapHolder.java -new file mode 100644 -index 0000000000000000000000000000000000000000..956825207da17b8dc6c1216ff37cb9861c7481bc ---- /dev/null -+++ b/src/main/java/io/papermc/paper/component/map/PatchedDataComponentMapHolder.java -@@ -0,0 +1,34 @@ -+package io.papermc.paper.component.map; -+ -+import io.papermc.paper.component.DataComponentType; -+import org.checkerframework.checker.nullness.qual.NonNull; -+import org.checkerframework.checker.nullness.qual.Nullable; -+import org.jetbrains.annotations.ApiStatus; -+ -+public interface PatchedDataComponentMapHolder { -+ -+ @NonNull PatchedDataComponentMap components(); -+ -+ default boolean hasData(final @NonNull DataComponentType type) { -+ return this.components().has(type); -+ } -+ -+ default @Nullable <T> T getData(final DataComponentType.@NonNull Valued<T> type) { -+ return this.components().get(type); -+ } -+ -+ default <T> void setData(final DataComponentType.@NonNull Valued<T> type, final @Nullable T value) { -+ this.components().set(type, value); -+ } -+ -+ default void setData(final DataComponentType.@NonNull NonValued type) { -+ this.components().set(type); -+ } -+ -+ default <T> void removeData(final @NonNull DataComponentType type) { -+ this.components().unset(type); -+ } -+ -+} diff --git a/src/main/java/io/papermc/paper/component/package-info.java b/src/main/java/io/papermc/paper/component/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..37c9e2b084fe4c82242ae64569bb76beb6a88c09 @@ -1151,235 +981,120 @@ index e20f64828548c647a29dad5a475f4596cad88cd8..80c10ab30ca6ea6e2a80a916d8a5831a /** diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java -index c64413a6740b604282984dea2a8430a6e7478d68..628aabd52c9686e00918adb8aee5bd9d97baa900 100644 +index 15a59a27f0854ff6f4038349d3a0d00347130140..44951ecb5ae4713cce515e2952c8c7e8c8d4eac4 100644 --- a/src/main/java/org/bukkit/inventory/ItemStack.java +++ b/src/main/java/org/bukkit/inventory/ItemStack.java -@@ -1,7 +1,6 @@ - package org.bukkit.inventory; - - import com.google.common.base.Preconditions; --import com.google.common.collect.ImmutableMap; - import java.util.LinkedHashMap; - import java.util.Locale; - import java.util.Map; -@@ -27,11 +26,11 @@ import org.jetbrains.annotations.Nullable; - * use this class to encapsulate Materials for which {@link Material#isItem()} - * returns false.</b> - */ --public class ItemStack implements Cloneable, ConfigurationSerializable, Translatable, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowItem>, net.kyori.adventure.translation.Translatable { // Paper -+public class ItemStack implements Cloneable, ConfigurationSerializable, Translatable, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowItem>, net.kyori.adventure.translation.Translatable, io.papermc.paper.component.map.PatchedDataComponentMapHolder { // Paper - private Material type = Material.AIR; - private int amount = 0; - private MaterialData data = null; -- private ItemMeta meta; -+ private io.papermc.paper.component.map.PatchedDataComponentMap dataKeyMap; // Paper - - @Utility - protected ItemStack() {} -@@ -88,6 +87,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - Preconditions.checkArgument(type != null, "Material cannot be null"); - this.type = type; - this.amount = amount; -+ this.dataKeyMap = io.papermc.paper.component.map.DataComponentPatchMapBridge.Holder.bridge().of(io.papermc.paper.component.map.DataComponentPatchMapBridge.Holder.bridge().fromItem(type)); // Paper - if (damage != 0) { - setDurability(damage); - } -@@ -107,6 +107,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - Preconditions.checkArgument(stack != null, "Cannot copy null stack"); - this.type = stack.getType(); - this.amount = stack.getAmount(); -+ this.dataKeyMap = io.papermc.paper.component.map.DataComponentPatchMapBridge.Holder.bridge().of(io.papermc.paper.component.map.DataComponentPatchMapBridge.Holder.bridge().fromItem(type)); // Paper - if (this.type.isLegacy()) { - this.data = stack.getData(); - } -@@ -150,10 +151,8 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - @Deprecated // Paper - public void setType(@NotNull Material type) { - Preconditions.checkArgument(type != null, "Material cannot be null"); -+ this.dataKeyMap = io.papermc.paper.component.map.DataComponentPatchMapBridge.Holder.bridge().fromItemAndMeta(type, Bukkit.getItemFactory().asMetaFor(this.getItemMeta(), type)); // Paper - update type first too, we need the old context to resolve the meta from the type - this.type = type; -- if (this.meta != null) { -- this.meta = Bukkit.getItemFactory().asMetaFor(meta, type); -- } - if (type.isLegacy()) { - createData((byte) 0); - } else { -@@ -277,6 +276,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - */ - @Utility - public int getMaxStackSize() { -+ ItemMeta meta = getItemMeta(); - if (meta != null && meta.hasMaxStackSize()) { - return meta.getMaxStackSize(); - } -@@ -337,9 +337,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - try { - ItemStack itemStack = (ItemStack) super.clone(); - -- if (this.meta != null) { -- itemStack.meta = this.meta.clone(); -- } -+ itemStack.dataKeyMap = this.components().copy(); // Paper - - if (this.data != null) { - itemStack.data = this.data.clone(); -@@ -359,7 +357,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - hash = hash * 31 + getType().hashCode(); - hash = hash * 31 + getAmount(); - hash = hash * 31 + (getDurability() & 0xffff); -- hash = hash * 31 + (hasItemMeta() ? (meta == null ? getItemMeta().hashCode() : meta.hashCode()) : 0); -+ hash = hash * 31 + dataKeyMap.hashCode(); // Paper - - return hash; - } -@@ -371,7 +369,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - * @return True if this has the given enchantment - */ - public boolean containsEnchantment(@NotNull Enchantment ench) { -- return meta == null ? false : meta.hasEnchant(ench); -+ return this.getItemMeta().hasEnchant(ench); // Paper - } - - /** -@@ -381,7 +379,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - * @return Level of the enchantment, or 0 - */ - public int getEnchantmentLevel(@NotNull Enchantment ench) { -- return meta == null ? 0 : meta.getEnchantLevel(ench); -+ return this.getItemMeta().getEnchantLevel(ench); // Paper - } - - /** -@@ -391,7 +389,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - */ - @NotNull - public Map<Enchantment, Integer> getEnchantments() { -- return meta == null ? ImmutableMap.<Enchantment, Integer>of() : meta.getEnchants(); -+ return this.getItemMeta().getEnchants(); // Paper - } - - /** -@@ -467,10 +465,11 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - * @param level Level of the enchantment - */ - public void addUnsafeEnchantment(@NotNull Enchantment ench, int level) { -- ItemMeta itemMeta = (meta == null ? meta = Bukkit.getItemFactory().getItemMeta(type) : meta); -+ editMeta((itemMeta) -> { // Paper - if (itemMeta != null) { - itemMeta.addEnchant(ench, level, true); - } -+ }); // Paper - } - - /** -@@ -482,10 +481,10 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - */ - public int removeEnchantment(@NotNull Enchantment ench) { - int level = getEnchantmentLevel(ench); -- if (level == 0 || meta == null) { -+ if (level == 0) { // Paper - return level; - } -- meta.removeEnchant(ench); -+ editMeta((itemMeta) -> itemMeta.removeEnchant(ench)); // Paper - return level; - } - -@@ -493,11 +492,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - * Removes all enchantments on this ItemStack. - */ - public void removeEnchantments() { -- if (meta == null) { -- return; -- } -- -- meta.removeEnchantments(); -+ editMeta(ItemMeta::removeEnchantments); // Paper - } - - @Override -@@ -653,7 +648,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - */ - @UndefinedNullability // Paper - public ItemMeta getItemMeta() { -- return this.meta == null ? Bukkit.getItemFactory().getItemMeta(this.type) : this.meta.clone(); -+ return io.papermc.paper.component.map.DataComponentPatchMapBridge.Holder.bridge().toItemMeta(this.type, this.components()); - } - - /** -@@ -662,7 +657,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - * @return Returns true if some meta data has been set for this item - */ - public boolean hasItemMeta() { -- return !Bukkit.getItemFactory().equals(meta, null); -+ return !Bukkit.getItemFactory().equals(this.getItemMeta(), null); // Paper - } - - /** -@@ -683,22 +678,13 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - */ - private boolean setItemMeta0(@Nullable ItemMeta itemMeta, @NotNull Material material) { - if (itemMeta == null) { -- this.meta = null; -+ this.dataKeyMap = io.papermc.paper.component.map.PatchedDataComponentMap.of(io.papermc.paper.component.map.DataComponentPatchMapBridge.Holder.bridge().fromItem(material)); - return true; - } - if (!Bukkit.getItemFactory().isApplicable(itemMeta, material)) { - return false; - } -- this.meta = Bukkit.getItemFactory().asMetaFor(itemMeta, material); -- -- Material newType = Bukkit.getItemFactory().updateMaterial(meta, material); -- if (this.type != newType) { -- this.type = newType; -- } -- -- if (this.meta == itemMeta) { -- this.meta = itemMeta.clone(); -- } -+ this.dataKeyMap = io.papermc.paper.component.map.DataComponentPatchMapBridge.Holder.bridge().fromItemAndMeta(material, itemMeta); - - return true; - } -@@ -1079,4 +1065,10 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat +@@ -1029,4 +1029,96 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat return Bukkit.getUnsafe().computeTooltipLines(this, tooltipContext, player); } // Paper end - expose itemstack tooltip lines -+ // Paper start -+ @Override -+ public io.papermc.paper.component.map.@NotNull PatchedDataComponentMap components() { -+ return this.dataKeyMap; ++ ++ // Paper start - data component API ++ /** ++ * Gets the value for the data component type on this stack. ++ * ++ * @param type the data component type ++ * @return the value for the data component type, or null if not set or marked as removed ++ * @param <T> the value type ++ * @see #hasData(io.papermc.paper.component.DataComponentType) for non-valued data component types ++ */ ++ @org.jetbrains.annotations.Contract(pure = true) ++ public <T> @Nullable T getData(final io.papermc.paper.component.DataComponentType.@NotNull Valued<T> type) { ++ return this.craftDelegate.getData(type); + } -+ // Paper end ++ ++ /** ++ * Gets the value for the data component type on this stack with ++ * a fallback value. ++ * ++ * @param type the data component type ++ * @param fallback the fallback value if the value isn't present ++ * @return the value for the data component type or the fallback value ++ * @param <T> the value type ++ */ ++ @Utility ++ @org.jetbrains.annotations.Contract(value = "_, !null -> !null", pure = true) ++ public <T> @Nullable T getDataOrDefault(final io.papermc.paper.component.DataComponentType.@NotNull Valued<? extends T> type, final @Nullable T fallback) { ++ final T object = this.getData(type); ++ return object != null ? object : fallback; ++ } ++ ++ /** ++ * Checks if the data component type is set on the itemstack. ++ * ++ * @param type the data component type ++ * @return true if set, false otherwise ++ */ ++ @org.jetbrains.annotations.Contract(pure = true) ++ public boolean hasData(final io.papermc.paper.component.@NotNull DataComponentType type) { ++ return this.craftDelegate.hasData(type); ++ } ++ ++ /** ++ * Gets all the data component types set on this stack. ++ * ++ * @return an immutable set of data component types ++ */ ++ @org.jetbrains.annotations.Contract("-> new") ++ public [email protected] Set<io.papermc.paper.component.@NotNull DataComponentType> getDataComponents() { ++ return this.craftDelegate.getDataComponents(); ++ } ++ ++ /** ++ * Sets the value of this data component type for this itemstack. ++ * <p> ++ * Note: supplying null will act similarly to {@link ItemStack#unsetData(io.papermc.paper.component.DataComponentType)}. ++ * ++ * @param type component type ++ * @param value set value ++ * @param <T> type ++ */ ++ public <T> void setData(final io.papermc.paper.component.DataComponentType.@NotNull Valued<T> type, final @Nullable T value) { ++ this.craftDelegate.setData(type, value); ++ } ++ ++ /** ++ * Marks this non-valued data component type as present in this itemstack. ++ * ++ * @param type type ++ */ ++ public void setData(final io.papermc.paper.component.DataComponentType.@NotNull NonValued type) { ++ this.craftDelegate.setData(type); ++ } ++ ++ /** ++ * Marks this data component as removed for this itemstack. ++ * ++ * @param type data component type ++ */ ++ public void unsetData(final io.papermc.paper.component.@NotNull DataComponentType type) { ++ this.craftDelegate.unsetData(type); ++ } ++ ++ /** ++ * Resets the value of this component to be the default ++ * value for the item type from {@link ItemType#getDefaultDataComponent(io.papermc.paper.component.DataComponentType.Valued)} ++ * @param type data component type to reset ++ */ ++ public void resetData(final io.papermc.paper.component.@NotNull DataComponentType type) { ++ this.craftDelegate.resetData(type); ++ } ++ // Paper end - data component API + } +diff --git a/src/main/java/org/bukkit/inventory/ItemType.java b/src/main/java/org/bukkit/inventory/ItemType.java +index 077bd4d4e50169780f27e8502104e9e4b2ecdae6..173fdeac69729e3a4bc75d2dae1b1db588bb6c5b 100644 +--- a/src/main/java/org/bukkit/inventory/ItemType.java ++++ b/src/main/java/org/bukkit/inventory/ItemType.java +@@ -2443,4 +2443,12 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans + @Override + @NotNull String getTranslationKey(); + // Paper end - add Translatable ++ ++ // Paper start - data component API ++ @Nullable <T> T getDefaultDataComponent(io.papermc.paper.component.DataComponentType.@NotNull Valued<T> dataComponentType); ++ ++ boolean hasDefaultDataComponent(io.papermc.paper.component.@NotNull DataComponentType dataComponentType); ++ ++ [email protected] @NotNull Set<io.papermc.paper.component.DataComponentType> getDefaultDataComponentTypes(); ++ // Paper end - data component API } -diff --git a/src/test/java/org/bukkit/configuration/ConfigurationSectionTest.java b/src/test/java/org/bukkit/configuration/ConfigurationSectionTest.java -index ee5bc86f47cf5599e4c5c34e3a9084f86d74bdb7..d892f3b82f8e85143497d7dfd6790526fd455b90 100644 ---- a/src/test/java/org/bukkit/configuration/ConfigurationSectionTest.java -+++ b/src/test/java/org/bukkit/configuration/ConfigurationSectionTest.java -@@ -546,7 +546,7 @@ public abstract class ConfigurationSectionTest { - assertFalse(section.isVector("doesntExist")); - } - -- @Test -+ @org.junit.jupiter.api.Disabled // Paper - public void testGetItemStack_String() { - ConfigurationSection section = getConfigurationSection(); - String key = "exists"; -@@ -558,7 +558,7 @@ public abstract class ConfigurationSectionTest { - assertNull(section.getString("doesntExist")); - } - -- @Test -+ @org.junit.jupiter.api.Disabled // Paper - public void testGetItemStack_String_ItemStack() { - ConfigurationSection section = getConfigurationSection(); - String key = "exists"; -@@ -571,7 +571,7 @@ public abstract class ConfigurationSectionTest { - assertEquals(def, section.getItemStack("doesntExist", def)); - } - -- @Test -+ @org.junit.jupiter.api.Disabled // Paper - public void testIsItemStack() { - ConfigurationSection section = getConfigurationSection(); - String key = "exists"; diff --git a/patches/server/1053-WIP-DataComponent-API.patch b/patches/server/1054-WIP-DataComponent-API.patch index a1cc160338..6867fa7d85 100644 --- a/patches/server/1053-WIP-DataComponent-API.patch +++ b/patches/server/1054-WIP-DataComponent-API.patch @@ -174,13 +174,16 @@ index 0000000000000000000000000000000000000000..ca621dd53bee70a2f383517a4f1f4c15 +} diff --git a/src/main/java/io/papermc/paper/component/PaperComponentType.java b/src/main/java/io/papermc/paper/component/PaperComponentType.java new file mode 100644 -index 0000000000000000000000000000000000000000..93d18f6fc171b777e81a2546c47dadf6c2e30e9c +index 0000000000000000000000000000000000000000..4f2dc5add01a3998b4d46af0a663a6fe539f6b0c --- /dev/null +++ b/src/main/java/io/papermc/paper/component/PaperComponentType.java -@@ -0,0 +1,93 @@ +@@ -0,0 +1,113 @@ +package io.papermc.paper.component; + ++import java.util.Collections; ++import java.util.Set; +import net.kyori.adventure.key.Key; ++import net.minecraft.core.component.DataComponentMap; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import org.bukkit.NamespacedKey; @@ -189,8 +192,8 @@ index 0000000000000000000000000000000000000000..93d18f6fc171b777e81a2546c47dadf6 +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.craftbukkit.util.Handleable; +import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.DefaultQualifier; -+import org.jetbrains.annotations.NotNull; + +@DefaultQualifier(NonNull.class) +public abstract class PaperComponentType<T, NMS> implements DataComponentType, Handleable<net.minecraft.core.component.DataComponentType<NMS>> { @@ -207,6 +210,23 @@ index 0000000000000000000000000000000000000000..93d18f6fc171b777e81a2546c47dadf6 + return CraftRegistry.minecraftToBukkit(type, Registries.DATA_COMPONENT_TYPE, Registry.DATA_COMPONENT_TYPE); + } + ++ public static Set<DataComponentType> minecraftToBukkit(final Set<net.minecraft.core.component.DataComponentType<?>> nmsTypes) { ++ final Set<DataComponentType> types = new java.util.HashSet<>(); ++ for (final net.minecraft.core.component.DataComponentType<?> nmsType : nmsTypes) { ++ types.add(PaperComponentType.minecraftToBukkit(nmsType)); ++ } ++ return Collections.unmodifiableSet(types); ++ } ++ ++ public static <B, M> @Nullable B convertDataComponentValue(final DataComponentMap map, final PaperComponentType.ValuedImpl<B, M> type) { ++ final net.minecraft.core.component.DataComponentType<M> nms = bukkitToMinecraft(type); ++ final M nmsValue = map.get(nms); ++ if (nmsValue == null) { ++ return null; ++ } ++ return type.getAdapter().fromVanilla(nmsValue); ++ } ++ + private final NamespacedKey key; + private final net.minecraft.core.component.DataComponentType<NMS> type; + private final ComponentAdapter<NMS, T> adapter; @@ -218,7 +238,7 @@ index 0000000000000000000000000000000000000000..93d18f6fc171b777e81a2546c47dadf6 + } + + @Override -+ public @NotNull NamespacedKey getKey() { ++ public NamespacedKey getKey() { + return this.key; + } + @@ -249,7 +269,7 @@ index 0000000000000000000000000000000000000000..93d18f6fc171b777e81a2546c47dadf6 + } + } + -+ static final class NonValuedImpl<T, NMS> extends PaperComponentType<T, NMS> implements NonValued { ++ public static final class NonValuedImpl<T, NMS> extends PaperComponentType<T, NMS> implements NonValued { + + NonValuedImpl( + final NamespacedKey key, @@ -260,7 +280,7 @@ index 0000000000000000000000000000000000000000..93d18f6fc171b777e81a2546c47dadf6 + } + } + -+ static final class ValuedImpl<T, NMS> extends PaperComponentType<T, NMS> implements Valued<T> { ++ public static final class ValuedImpl<T, NMS> extends PaperComponentType<T, NMS> implements Valued<T> { + + ValuedImpl( + final NamespacedKey key, @@ -1519,191 +1539,6 @@ index 0000000000000000000000000000000000000000..8be3187a6c624d4ba74d2a58bc64b1b0 + } + } +} -diff --git a/src/main/java/io/papermc/paper/component/map/DataComponentPatchBridgeImpl.java b/src/main/java/io/papermc/paper/component/map/DataComponentPatchBridgeImpl.java -new file mode 100644 -index 0000000000000000000000000000000000000000..39d941a9b910676735c38dd7eae1b3fe7a30d0ea ---- /dev/null -+++ b/src/main/java/io/papermc/paper/component/map/DataComponentPatchBridgeImpl.java -@@ -0,0 +1,59 @@ -+package io.papermc.paper.component.map; -+ -+import com.mojang.authlib.GameProfile; -+import net.minecraft.core.component.DataComponents; -+import net.minecraft.world.item.Item; -+import net.minecraft.world.item.ItemStack; -+import net.minecraft.world.item.Items; -+import org.bukkit.Material; -+import org.bukkit.craftbukkit.inventory.CraftItemStack; -+import org.bukkit.craftbukkit.inventory.CraftItemType; -+import org.bukkit.craftbukkit.inventory.CraftMetaItem; -+import org.bukkit.inventory.meta.ItemMeta; -+import org.jetbrains.annotations.Nullable; -+ -+public class DataComponentPatchBridgeImpl implements io.papermc.paper.component.map.DataComponentPatchMapBridge { -+ @Override -+ public PatchedDataComponentMap of(final DataComponentMap map) { -+ return new PaperPatchedDataComponentMap(new net.minecraft.core.component.PatchedDataComponentMap(((PaperDataComponentMap) map).map)); -+ } -+ -+ @Override -+ public DataComponentMap empty() { -+ return new PaperDataComponentMap(net.minecraft.core.component.DataComponentMap.EMPTY); -+ } -+ -+ @Override -+ public DataComponentMap fromItem(final Material material) { -+ final @Nullable Item item = CraftItemType.bukkitToMinecraft(material); -+ if (item == null || item == Items.AIR) { -+ // Because people can make non-item itemstacks still.. -+ return this.empty(); -+ } -+ return new PaperDataComponentMap(item.components()); -+ } -+ -+ @Override -+ public PatchedDataComponentMap fromItemAndMeta(final Material material, final ItemMeta meta) { -+ final CraftMetaItem.Applicator tag = new CraftMetaItem.Applicator() { -+ @Override -+ public void skullCallback(final GameProfile gameProfile) { -+ // TODO this isn't correct, this is called after resolving the profile, the builder is usually built when this is called -+ // I'm not even sure if we can do anything about this. -+ this.builder.set(DataComponents.PROFILE, new net.minecraft.world.item.component.ResolvableProfile(gameProfile)); -+ } -+ }; -+ ((CraftMetaItem) meta).applyToItemPublic(tag); -+ -+ final net.minecraft.core.component.PatchedDataComponentMap map = new net.minecraft.core.component.PatchedDataComponentMap(CraftItemType.bukkitToMinecraft(material).components()); -+ map.applyPatch(tag.builder.build()); -+ return new PaperPatchedDataComponentMap(map); -+ } -+ -+ @Override -+ public ItemMeta toItemMeta(final Material material, final PatchedDataComponentMap map) { -+ final ItemStack stack = new ItemStack(CraftItemType.bukkitToMinecraft(material)); -+ stack.restorePatch(((PaperPatchedDataComponentMap) map).getHandle().asPatch()); -+ return CraftItemStack.getItemMeta(stack, material); -+ } -+} -diff --git a/src/main/java/io/papermc/paper/component/map/PaperDataComponentMap.java b/src/main/java/io/papermc/paper/component/map/PaperDataComponentMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..4432c3e5cd6d3da8fd9d657e09e6833f3e9ddde2 ---- /dev/null -+++ b/src/main/java/io/papermc/paper/component/map/PaperDataComponentMap.java -@@ -0,0 +1,46 @@ -+package io.papermc.paper.component.map; -+ -+import io.papermc.paper.component.ComponentAdapter; -+import io.papermc.paper.component.DataComponentType; -+import io.papermc.paper.component.PaperComponentType; -+import java.util.Collections; -+import java.util.HashSet; -+import java.util.Set; -+import org.checkerframework.checker.nullness.qual.Nullable; -+ -+public class PaperDataComponentMap implements DataComponentMap { -+ -+ protected final net.minecraft.core.component.DataComponentMap map; -+ -+ public PaperDataComponentMap(final net.minecraft.core.component.DataComponentMap map) { -+ this.map = map; -+ } -+ -+ @Override -+ public <T> @Nullable T get(final DataComponentType.Valued<T> type) { -+ @SuppressWarnings("unchecked") -+ final PaperComponentType<T, Object> typeAsImpl = (PaperComponentType<T, Object>) type; -+ final ComponentAdapter<Object, T> adapter = typeAsImpl.getAdapter(); -+ final @Nullable Object value = this.map.get(typeAsImpl.getHandle()); -+ -+ return value == null ? null : adapter.fromVanilla(value); -+ } -+ -+ @Override -+ public Set<DataComponentType> keySet() { -+ final Set<net.minecraft.core.component.DataComponentType<?>> nmsKeys = this.map.keySet(); -+ final Set<DataComponentType> keys = new HashSet<>(nmsKeys.size()); -+ for (final net.minecraft.core.component.DataComponentType<?> nmsKey : nmsKeys) { -+ keys.add(PaperComponentType.minecraftToBukkit(nmsKey)); -+ } -+ -+ return Collections.unmodifiableSet(keys); -+ } -+ -+ @Override -+ public boolean has(final DataComponentType type) { -+ @SuppressWarnings("unchecked") -+ final PaperComponentType<?, Object> typeAsImpl = (PaperComponentType<?, Object>) type; -+ return this.map.has(typeAsImpl.getHandle()); -+ } -+} -diff --git a/src/main/java/io/papermc/paper/component/map/PaperPatchedDataComponentMap.java b/src/main/java/io/papermc/paper/component/map/PaperPatchedDataComponentMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3d9559a326d0aabcdeabac5d862b7b6244d7e973 ---- /dev/null -+++ b/src/main/java/io/papermc/paper/component/map/PaperPatchedDataComponentMap.java -@@ -0,0 +1,62 @@ -+package io.papermc.paper.component.map; -+ -+import io.papermc.paper.component.ComponentAdapter; -+import io.papermc.paper.component.DataComponentType; -+import io.papermc.paper.component.PaperComponentType; -+import org.checkerframework.checker.nullness.qual.Nullable; -+ -+public class PaperPatchedDataComponentMap extends PaperDataComponentMap implements PatchedDataComponentMap { -+ -+ public PaperPatchedDataComponentMap(final net.minecraft.core.component.PatchedDataComponentMap map) { -+ super(map); -+ } -+ -+ @Override -+ public <T> void set(final DataComponentType.Valued<T> type, final @Nullable T value) { -+ @SuppressWarnings("unchecked") -+ final PaperComponentType<T, Object> typeAsImpl = ((PaperComponentType<T, Object>) type); -+ this.setInternal(typeAsImpl, value); -+ } -+ -+ @Override -+ public void set(final DataComponentType.NonValued type) { -+ @SuppressWarnings("unchecked") -+ final PaperComponentType<?, Object> typeAsImpl = ((PaperComponentType<?, Object>) type); -+ this.setInternal(typeAsImpl, null); -+ } -+ -+ private <A, V> void setInternal(final PaperComponentType<A, V> type, final @Nullable A value) { -+ final ComponentAdapter<V, A> adapter = type.getAdapter(); -+ -+ if (adapter.isValued()) { -+ this.getHandle().set(type.getHandle(), value == null ? null : adapter.toVanilla(value)); -+ } else { -+ this.getHandle().set(type.getHandle(), adapter.toVanilla(value)); -+ } -+ } -+ -+ @Override -+ public void unset(final DataComponentType type) { -+ @SuppressWarnings("unchecked") -+ final PaperComponentType<?, Object> typeAsImpl = ((PaperComponentType<?, Object>) type); -+ this.getHandle().remove(typeAsImpl.getHandle()); -+ } -+ -+ @Override -+ public void reset(final DataComponentType type) { -+ @SuppressWarnings("unchecked") -+ final PaperComponentType<?, Object> typeAsImpl = (PaperComponentType<?, Object>) type; -+ -+ final net.minecraft.core.component.PatchedDataComponentMap map = this.getHandle(); -+ map.applyPatch(map.asPatch().forget((forgetType) -> forgetType == typeAsImpl.getHandle())); // Apply patch with type removed -+ } -+ -+ @Override -+ public PatchedDataComponentMap copy() { -+ return new PaperPatchedDataComponentMap(this.getHandle().copy()); -+ } -+ -+ public net.minecraft.core.component.PatchedDataComponentMap getHandle() { -+ return ((net.minecraft.core.component.PatchedDataComponentMap) this.map); -+ } -+} diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java index 51979b3c3f1f3a3c63e0559c70bed9193fd35dbb..df2af45f32af3a1ddef25c5e7cca3973481806e2 100644 --- a/src/main/java/io/papermc/paper/registry/PaperRegistries.java @@ -1738,10 +1573,10 @@ index af18de11dd55938b6091f5ab183bd3fe4e8df152..dad6cb4bbb52f4ce7e8f40131ee0bd37 ItemEnchantments(Object2IntAVLTreeMap<Holder<Enchantment>> enchantments, boolean showInTooltip) { // Paper diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java -index f1e1953f2dc65dc615b7b7b648c37b195d3b4c25..5941ea2cdb9ef6cd54505239d6e7b3d382db2420 100644 +index 3496b98ff0b984dbfec4f0983459a273dc0e3471..d14d0b4a0edbff19d6b5ce0674aa70ba7af902d6 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java -@@ -163,7 +163,7 @@ public final class CraftItemStack extends ItemStack { +@@ -167,7 +167,7 @@ public final class CraftItemStack extends ItemStack { this.adjustTagForItemMeta(oldType); // Paper } } @@ -1750,7 +1585,7 @@ index f1e1953f2dc65dc615b7b7b648c37b195d3b4c25..5941ea2cdb9ef6cd54505239d6e7b3d3 } @Override -@@ -202,7 +202,7 @@ public final class CraftItemStack extends ItemStack { +@@ -206,7 +206,7 @@ public final class CraftItemStack extends ItemStack { @Override public int getMaxStackSize() { @@ -1777,20 +1612,111 @@ index f1e1953f2dc65dc615b7b7b648c37b195d3b4c25..5941ea2cdb9ef6cd54505239d6e7b3d3 item.set(DataComponents.PROFILE, new net.minecraft.world.item.component.ResolvableProfile(gameProfile)); } }; -@@ -767,5 +767,14 @@ public final class CraftItemStack extends ItemStack { - mirrored.setItemMeta(mirrored.getItemMeta()); +@@ -768,4 +768,79 @@ public final class CraftItemStack extends ItemStack { return mirrored; } + // Paper end + ++ // Paper start - data component API + @Override -+ public io.papermc.paper.component.map.@org.checkerframework.checker.nullness.qual.NonNull PaperPatchedDataComponentMap components() { -+ if (this.handle == null) { -+ return new io.papermc.paper.component.map.PaperPatchedDataComponentMap(new net.minecraft.core.component.PatchedDataComponentMap(net.minecraft.core.component.DataComponentMap.EMPTY)); // Paper ++ public <T> T getData(final io.papermc.paper.component.DataComponentType.Valued<T> type) { ++ if (this.isEmpty()) { ++ return null; + } ++ return io.papermc.paper.component.PaperComponentType.convertDataComponentValue(this.handle.getComponents(), (io.papermc.paper.component.PaperComponentType.ValuedImpl<T, ?>) type); ++ } + -+ return new io.papermc.paper.component.map.PaperPatchedDataComponentMap((net.minecraft.core.component.PatchedDataComponentMap) this.handle.getComponents()); // Paper ++ @Override ++ public boolean hasData(final io.papermc.paper.component.DataComponentType type) { ++ if (this.isEmpty()) { ++ return false; ++ } ++ return this.handle.has(io.papermc.paper.component.PaperComponentType.bukkitToMinecraft(type)); + } - // Paper end ++ ++ @Override ++ public java.util.Set<io.papermc.paper.component.DataComponentType> getDataComponents() { ++ if (this.isEmpty()) { ++ return java.util.Collections.emptySet(); ++ } ++ return io.papermc.paper.component.PaperComponentType.minecraftToBukkit(this.handle.getComponents().keySet()); ++ } ++ ++ @Override ++ public <T> void setData(final io.papermc.paper.component.DataComponentType.Valued<T> type, final T value) { ++ if (this.isEmpty()) { ++ return; ++ } ++ this.setDataInternal((io.papermc.paper.component.PaperComponentType.ValuedImpl<T, ?>) type, value); ++ } ++ ++ @Override ++ public void setData(final io.papermc.paper.component.DataComponentType.NonValued type) { ++ if (this.isEmpty()) { ++ return; ++ } ++ this.setDataInternal((io.papermc.paper.component.PaperComponentType.NonValuedImpl<?, ?>) type, null); ++ } ++ ++ private <A, V> void setDataInternal(final io.papermc.paper.component.PaperComponentType<A, V> type, final A value) { ++ final io.papermc.paper.component.ComponentAdapter<V, A> adapter = type.getAdapter(); ++ if (adapter.isValued()) { ++ this.handle.set(type.getHandle(), value == null ? null : adapter.toVanilla(value)); ++ } else { ++ this.handle.set(type.getHandle(), adapter.toVanilla(value)); ++ } ++ } ++ ++ @Override ++ public void unsetData(final io.papermc.paper.component.DataComponentType type) { ++ if (this.isEmpty()) { ++ return; ++ } ++ this.handle.remove(io.papermc.paper.component.PaperComponentType.bukkitToMinecraft(type)); ++ } ++ ++ @Override ++ public void resetData(final io.papermc.paper.component.DataComponentType type) { ++ if (this.isEmpty()) { ++ return; ++ } ++ this.resetData((io.papermc.paper.component.PaperComponentType<?, ?>) type); ++ } ++ ++ private <M> void resetData(final io.papermc.paper.component.PaperComponentType<?, M> type) { ++ final net.minecraft.core.component.DataComponentType<M> nms = io.papermc.paper.component.PaperComponentType.bukkitToMinecraft(type); ++ final M nmsValue = this.handle.getItem().components().get(nms); ++ // if nmsValue is null, it will clear any set patch ++ // if nmsValue is not null, it will still clear any set patch because it will equal the default value ++ this.handle.set(nms, nmsValue); ++ } ++ // Paper end - data component API + } +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java +index b54be1122af2b303c0f063ff6b61bf8e2478b0df..f0c394741c56b23df47c1b3e6790d4d41b9d851b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java +@@ -253,4 +253,21 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han + return this.item.getDescriptionId(); + } + // Paper end - add Translatable ++ ++ // Paper start - data component API ++ @Override ++ public <T> T getDefaultDataComponent(final io.papermc.paper.component.DataComponentType.Valued<T> dataComponentType) { ++ return io.papermc.paper.component.PaperComponentType.convertDataComponentValue(this.item.components(), ((io.papermc.paper.component.PaperComponentType.ValuedImpl<T, ?>) dataComponentType)); ++ } ++ ++ @Override ++ public boolean hasDefaultDataComponent(final io.papermc.paper.component.DataComponentType dataComponentType) { ++ return this.item.components().has(io.papermc.paper.component.PaperComponentType.bukkitToMinecraft(dataComponentType)); ++ } ++ ++ @Override ++ public java.util.Set<io.papermc.paper.component.DataComponentType> getDefaultDataComponentTypes() { ++ return io.papermc.paper.component.PaperComponentType.minecraftToBukkit(this.item.components().keySet()); ++ } ++ // Paper end - data component API } diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java index d5789326d70bb8b029c5448270bbaa6faf52e6e1..02cdd38a55741a56ed9de428d9145e6103b71f65 100644 @@ -1841,92 +1767,6 @@ index 0000000000000000000000000000000000000000..a2c02206254a18e089cb2b40eab5c59e +++ b/src/main/resources/META-INF/services/io.papermc.paper.component.item.ComponentTypesBridge @@ -0,0 +1 @@ +io.papermc.paper.component.item.ComponentTypesBridgesImpl -diff --git a/src/main/resources/META-INF/services/io.papermc.paper.component.map.DataComponentPatchMapBridge b/src/main/resources/META-INF/services/io.papermc.paper.component.map.DataComponentPatchMapBridge -new file mode 100644 -index 0000000000000000000000000000000000000000..4630bf3ffa719c218791d4a4f2aea8f872b25baa ---- /dev/null -+++ b/src/main/resources/META-INF/services/io.papermc.paper.component.map.DataComponentPatchMapBridge -@@ -0,0 +1 @@ -+io.papermc.paper.component.map.DataComponentPatchBridgeImpl -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..0aad5d896a6adb691a7efaee3baebed4da7c607e ---- /dev/null -+++ b/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java -@@ -0,0 +1,56 @@ -+package io.papermc.paper.configuration; -+ -+import static org.junit.jupiter.api.Assertions.*; -+import java.util.Arrays; -+import java.util.Collections; -+import java.util.HashMap; -+import java.util.LinkedHashMap; -+import java.util.List; -+import java.util.Map; -+import org.bukkit.Material; -+import org.bukkit.configuration.ConfigurationSection; -+import org.bukkit.configuration.serialization.ConfigurationSerializable; -+import org.bukkit.inventory.ItemStack; -+import org.bukkit.util.Vector; -+import org.junit.jupiter.api.Test; -+ -+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..def33c36f207a4c5306b5a895336aa70335c1678 ---- /dev/null -+++ b/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java -@@ -0,0 +1,11 @@ -+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"); -+ } -+} diff --git a/src/test/java/io/papermc/paper/item/ItemStackDataComponentTest.java b/src/test/java/io/papermc/paper/item/ItemStackDataComponentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..40c77423134931083913e871b80dae8e6e5f2194 |