aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOwen1212055 <[email protected]>2024-10-31 13:35:26 -0400
committerOwen1212055 <[email protected]>2024-11-18 14:50:38 -0500
commiteaddf6ab8c4cb57e61b7ca150f0e2f272cfa717c (patch)
treeb3d1b6b149cf256f9a2853cbf412d2fd46895a0b
parent2bd5eee1a03fa51e5fca3dd931da84efa32eb0de (diff)
downloadPaper-eaddf6ab8c4cb57e61b7ca150f0e2f272cfa717c.tar.gz
Paper-eaddf6ab8c4cb57e61b7ca150f0e2f272cfa717c.zip
Update Data component api
-rw-r--r--patches/api/0497-WIP-DataComponent-API.patch (renamed from patches/api/0482-WIP-DataComponent-API.patch)1130
-rw-r--r--patches/server/1058-WIP-DataComponent-API.patch (renamed from patches/server/1044-WIP-DataComponent-API.patch)1276
2 files changed, 2109 insertions, 297 deletions
diff --git a/patches/api/0482-WIP-DataComponent-API.patch b/patches/api/0497-WIP-DataComponent-API.patch
index 289497f7a9..77e6b14804 100644
--- a/patches/api/0482-WIP-DataComponent-API.patch
+++ b/patches/api/0497-WIP-DataComponent-API.patch
@@ -148,18 +148,23 @@ index 0000000000000000000000000000000000000000..3a0bca9f95d6f1ad2e160620bb47f702
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java b/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java
new file mode 100644
-index 0000000000000000000000000000000000000000..4a99e023e4593dc0ccc05b9af292b44d056f534e
+index 0000000000000000000000000000000000000000..a40b95ce0de70365bf1a1d339df96a483128abe1
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java
-@@ -0,0 +1,324 @@
+@@ -0,0 +1,342 @@
+package io.papermc.paper.datacomponent;
+
+import io.papermc.paper.datacomponent.item.BannerPatternLayers;
+import io.papermc.paper.datacomponent.item.BlockItemDataProperties;
+import io.papermc.paper.datacomponent.item.BundleContents;
+import io.papermc.paper.datacomponent.item.ChargedProjectiles;
++import io.papermc.paper.datacomponent.item.Consumable;
+import io.papermc.paper.datacomponent.item.CustomModelData;
++import io.papermc.paper.datacomponent.item.DamageResistant;
++import io.papermc.paper.datacomponent.item.DeathProtection;
+import io.papermc.paper.datacomponent.item.DyedItemColor;
++import io.papermc.paper.datacomponent.item.Enchantable;
++import io.papermc.paper.datacomponent.item.Equippable;
+import io.papermc.paper.datacomponent.item.Fireworks;
+import io.papermc.paper.datacomponent.item.FoodProperties;
+import io.papermc.paper.datacomponent.item.ItemAdventurePredicate;
@@ -174,6 +179,8 @@ index 0000000000000000000000000000000000000000..4a99e023e4593dc0ccc05b9af292b44d
+import io.papermc.paper.datacomponent.item.MapDecorations;
+import io.papermc.paper.datacomponent.item.MapId;
+import io.papermc.paper.datacomponent.item.MapItemColor;
++import io.papermc.paper.datacomponent.item.UseCooldown;
++import io.papermc.paper.datacomponent.item.UseRemainder;
+import io.papermc.paper.item.MapPostProcessing;
+import io.papermc.paper.datacomponent.item.PotDecorations;
+import io.papermc.paper.datacomponent.item.PotionContents;
@@ -193,6 +200,7 @@ index 0000000000000000000000000000000000000000..4a99e023e4593dc0ccc05b9af292b44d
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+import org.bukkit.inventory.ItemRarity;
++import org.bukkit.inventory.meta.Repairable;
+import org.checkerframework.checker.index.qual.NonNegative;
+import org.checkerframework.checker.index.qual.Positive;
+import org.checkerframework.common.value.qual.IntRange;
@@ -254,6 +262,7 @@ index 0000000000000000000000000000000000000000..4a99e023e4593dc0ccc05b9af292b44d
+ * @see #CUSTOM_NAME
+ */
+ public static final DataComponentType.Valued<Component> ITEM_NAME = valued("item_name");
++ public static final DataComponentType.Valued<Key> ITEM_MODEL = valued("item_model");
+ /**
+ * Additional lines to include in an item's tooltip.
+ */
@@ -320,14 +329,23 @@ index 0000000000000000000000000000000000000000..4a99e023e4593dc0ccc05b9af292b44d
+ * When present, this item will behave as if a food (can be eaten).
+ */
+ public static final DataComponentType.Valued<FoodProperties> FOOD = valued("food");
++ public static final DataComponentType.Valued<Consumable> CONSUMABLE = valued("consumable");
++ public static final DataComponentType.Valued<UseRemainder> USE_REMAINDER = valued("use_remainder");
++ public static final DataComponentType.Valued<UseCooldown> USE_COOLDOWN = valued("use_cooldown");
+ /**
+ * If present, this item will not burn in fire.
+ */
-+ public static final DataComponentType.NonValued FIRE_RESISTANT = unvalued("fire_resistant");
++ public static final DataComponentType.Valued<DamageResistant> DAMAGE_RESISTANT = valued("damage_resistant");
+ /**
+ * Controls the behavior of the item as a tool.
+ */
+ public static final DataComponentType.Valued<Tool> TOOL = valued("tool");
++ public static final DataComponentType.Valued<Enchantable> ENCHANTABLE = valued("enchantable");
++ public static final DataComponentType.Valued<Equippable> EQUIPPABLE = valued("equippable");
++ public static final DataComponentType.Valued<Repairable> REPAIRABLE = valued("repairable");
++ public static final DataComponentType.NonValued GLIDER = unvalued("glider");
++ public static final DataComponentType.Valued<Key> TOOLTIP_STYLE = valued("tooltip_style");
++ public static final DataComponentType.Valued<DeathProtection> DEATH_PROTECTION = valued("death_protection");
+ /**
+ * Stores list of enchantments and their levels for an Enchanted Book.
+ * Unlike {@link #ENCHANTMENTS}, the effects provided by enchantments
@@ -760,6 +778,96 @@ index 0000000000000000000000000000000000000000..df663aeeb1ebe547fff956bdda4455bd
+ @NonNull Builder addAll(@NonNull List<@NonNull ItemStack> stacks);
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Consumable.java b/src/main/java/io/papermc/paper/datacomponent/item/Consumable.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..01d25b7c618926284c517e4fa67600416b8ef532
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/Consumable.java
+@@ -0,0 +1,84 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.BuildableDataComponent;
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.Collection;
++import java.util.List;
++import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect;
++import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation;
++import net.kyori.adventure.key.Key;
++import org.bukkit.inventory.ItemStack;
++import org.checkerframework.checker.index.qual.NonNegative;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Unmodifiable;
++
++/**
++ * Holds the properties for this item for when it is consumed.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#CONSUMABLE
++ */
++public interface Consumable extends BuildableDataComponent<Consumable, Consumable.Builder> {
++
++ @Contract(value = "-> new", pure = true)
++ static Consumable.@NonNull Builder consumable() {
++ return ItemComponentTypesBridge.bridge().consumable();
++ }
++
++ @Contract(pure = true)
++ @NonNegative
++ float consumeSeconds();
++
++ @Contract(pure = true)
++ ItemUseAnimation animation();
++
++ @Contract(pure = true)
++ @NonNull
++ Key sound();
++
++ @Contract(pure = true)
++ boolean hasConsumeParticles();
++
++ @Contract(pure = true)
++ @Unmodifiable
++ @NonNull
++ List<ConsumeEffect> consumeEffects();
++
++ /**
++ * Builder for {@link Consumable}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder<Consumable> {
++
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull
++ Builder consumeSeconds(@NonNegative float consumeSeconds);
++
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull
++ Builder animation(@NotNull ItemUseAnimation animation);
++
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull
++ Builder sound(@NonNull Key sound);
++
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull
++ Builder hasConsumeParticles(boolean hasConsumeParticles);
++
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull
++ Builder addEffect(@NonNull ConsumeEffect effect);
++
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull
++ Builder addEffects(@NonNull Collection<@NonNull ConsumeEffect> effects);
++ }
++
++
++}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/CustomModelData.java b/src/main/java/io/papermc/paper/datacomponent/item/CustomModelData.java
new file mode 100644
index 0000000000000000000000000000000000000000..998eb257bf88f313a812802fd7dd45320893d224
@@ -793,6 +901,106 @@ index 0000000000000000000000000000000000000000..998eb257bf88f313a812802fd7dd4532
+ @Contract(pure = true)
+ int id();
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java b/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a6fd5b513b119bbf4e998a533bb36185d5cdfa92
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java
+@@ -0,0 +1,35 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import io.papermc.paper.registry.set.RegistryKeySet;
++import io.papermc.paper.registry.tag.TagKey;
++import org.bukkit.damage.DamageType;
++import org.bukkit.inventory.ItemStack;
++import org.bukkit.potion.PotionEffectType;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++
++/**
++ * Holds the contents of damage types that the item entity containing this item is invincible to.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#DAMAGE_RESISTANT
++ */
++public interface DamageResistant {
++
++ @Contract(value = "_ -> new", pure = true)
++ static @NonNull DamageResistant damageResistant(final @NonNull TagKey<DamageType> types) {
++ return ItemComponentTypesBridge.bridge().damageResistant(types);
++ }
++
++ /**
++ * The types that this damage type is invincible tp.
++ *
++ * @return item
++ */
++ @Contract(value = "-> new", pure = true)
++ @NonNull
++ TagKey<DamageType> types();
++
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/DeathProtection.java b/src/main/java/io/papermc/paper/datacomponent/item/DeathProtection.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..2ab52d15420122f6b8c05131a4fc0ccb25d8906c
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/DeathProtection.java
+@@ -0,0 +1,53 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.Arrays;
++import java.util.Collection;
++import java.util.List;
++import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect;
++import org.bukkit.inventory.ItemStack;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++
++/**
++ * Sets whether this item should protect the entity upon death, and what effects should be played.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#DEATH_PROTECTION
++ */
++public interface DeathProtection {
++
++ @Contract(value = "_ -> new", pure = true)
++ static @NonNull DeathProtection deathProtection(final @NonNull ConsumeEffect @NonNull... deathEffects) {
++ return deathProtection(Arrays.asList(deathEffects));
++ }
++
++ @Contract(value = "_ -> new", pure = true)
++ static @NonNull DeathProtection deathProtection(final @NonNull List<@NonNull ConsumeEffect> deathEffects) {
++ return deathProtection().addEffects(deathEffects).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static DeathProtection.@NonNull Builder deathProtection() {
++ return ItemComponentTypesBridge.bridge().deathProtection();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ @NonNull @Unmodifiable List<@NonNull ConsumeEffect> deathEffects();
++
++ /**
++ * Builder for {@link DeathProtection}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder<DeathProtection> {
++
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull Builder addEffect(@NonNull ConsumeEffect effect);
++
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull Builder addEffects(@NonNull Collection<@NonNull ConsumeEffect> effects);
++ }
++}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/DyedItemColor.java b/src/main/java/io/papermc/paper/datacomponent/item/DyedItemColor.java
new file mode 100644
index 0000000000000000000000000000000000000000..78afe5d6f3796e2c1ee945c8bcac167ce847c1f4
@@ -851,6 +1059,218 @@ index 0000000000000000000000000000000000000000..78afe5d6f3796e2c1ee945c8bcac167c
+ @NonNull Builder color(@NonNull Color color);
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Enchantable.java b/src/main/java/io/papermc/paper/datacomponent/item/Enchantable.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..feced7acdac5219b6f5d6a7eab825c6e23db3899
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/Enchantable.java
+@@ -0,0 +1,28 @@
++package io.papermc.paper.datacomponent.item;
++
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++
++/**
++ * Holds if an item is enchantable, allowing for enchantments of the type to be seen in an enchanting table.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#ENCHANTABLE
++ */
++public interface Enchantable {
++
++ @Contract(value = "_ -> new", pure = true)
++ static @NonNull Enchantable enchantable(final int level) {
++ return ItemComponentTypesBridge.bridge().enchantable(level);
++ }
++
++ /**
++ * Gets the current enchantment value level allowed,
++ * a higher value allows enchantments with a higher cost to be picked.
++ *
++ * @return the value
++ */
++ @Contract(pure = true)
++ int value();
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Equippable.java b/src/main/java/io/papermc/paper/datacomponent/item/Equippable.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..ec6a408f858241cd96ef2c73db7b14914fab800e
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/Equippable.java
+@@ -0,0 +1,172 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.BuildableDataComponent;
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import io.papermc.paper.registry.set.RegistryKeySet;
++import net.kyori.adventure.key.Key;
++import org.bukkit.entity.EntityType;
++import org.bukkit.inventory.EquipmentSlot;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++
++
++/**
++ * Holds the equippable properties of an item.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#EQUIPPABLE
++ */
++public interface Equippable extends BuildableDataComponent<Equippable, Equippable.Builder> {
++
++ /**
++ * Creates a new {@link Equippable.Builder} instance.
++ * @param slot The slot for the new equppable to be equippable in.
++ *
++ * @return a new builder
++ */
++ @Contract(value = "_ -> new", pure = true)
++ static Equippable.@NonNull Builder equippable(@NonNull EquipmentSlot slot) {
++ return ItemComponentTypesBridge.bridge().equippable(slot);
++ }
++
++ /**
++ * Gets the equipment slot this item can be equipped in.
++ *
++ * @return the equipment slot
++ */
++ @Contract(pure = true)
++ @NonNull
++ EquipmentSlot slot();
++
++ /**
++ * Gets the equip sound key.
++ *
++ * @return the equip sound key
++ */
++ @Contract(pure = true)
++ @NonNull
++ Key equipSound();
++
++ /**
++ * Gets the model key if present.
++ *
++ * @return the model key or null
++ */
++ @Contract(pure = true)
++ @Nullable Key model();
++
++ /**
++ * Gets the camera overlay key if present.
++ *
++ * @return the camera overlay key or null
++ */
++ @Contract(pure = true)
++ @Nullable Key cameraOverlay();
++
++ /**
++ * Gets the set of allowed entities that can equip this item.
++ * May be null if all entities are allowed.
++ *
++ * @return the set of allowed entities
++ */
++ @Contract(pure = true)
++ @Nullable
++ RegistryKeySet<EntityType> allowedEntities();
++
++ /**
++ * Checks if the item is dispensable.
++ *
++ * @return true if dispensable, false otherwise
++ */
++ @Contract(pure = true)
++ boolean dispensable();
++
++ /**
++ * Checks if the item is swappable.
++ *
++ * @return true if swappable, false otherwise
++ */
++ @Contract(pure = true)
++ boolean swappable();
++
++ /**
++ * Checks if the item takes damage when the wearer is hurt.
++ *
++ * @return true if it damages on hurt, false otherwise
++ */
++ @Contract(pure = true)
++ boolean damageOnHurt();
++
++ /**
++ * Builder for {@link Equippable}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder<Equippable> {
++
++ /**
++ * Sets the equip sound key for this item.
++ *
++ * @param equipSound the equip sound key
++ * @return the builder for chaining
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull Builder equipSound(@NonNull Key equipSound);
++
++ /**
++ * Sets the model key for this item.
++ *
++ * @param model the model key, nullable
++ * @return the builder for chaining
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull Builder model(@Nullable Key model);
++
++ /**
++ * Sets the camera overlay key for this item.
++ *
++ * @param cameraOverlay the camera overlay key, nullable
++ * @return the builder for chaining
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull Builder cameraOverlay(@Nullable Key cameraOverlay);
++
++ /**
++ * Sets the allowed entities that can equip this item.
++ *
++ * @param allowedEntities the set of allowed entity types, or null if any
++ * @return the builder for chaining
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull Builder allowedEntities(@Nullable RegistryKeySet<EntityType> allowedEntities);
++
++ /**
++ * Sets whether the item is dispensable.
++ *
++ * @param dispensable true if dispensable
++ * @return the builder for chaining
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull Builder dispensable(boolean dispensable);
++
++ /**
++ * Sets whether the item is swappable.
++ *
++ * @param swappable true if swappable
++ * @return the builder for chaining
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull Builder swappable(boolean swappable);
++
++ /**
++ * Sets whether the item takes damage when the wearer is hurt.
++ *
++ * @param damageOnHurt true if it damages on hurt
++ * @return the builder for chaining
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull Builder damageOnHurt(boolean damageOnHurt);
++ }
++}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Fireworks.java b/src/main/java/io/papermc/paper/datacomponent/item/Fireworks.java
new file mode 100644
index 0000000000000000000000000000000000000000..127ee7a9a89783df9eee78aa94ede7390f606fdc
@@ -942,10 +1362,10 @@ index 0000000000000000000000000000000000000000..127ee7a9a89783df9eee78aa94ede739
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/FoodProperties.java b/src/main/java/io/papermc/paper/datacomponent/item/FoodProperties.java
new file mode 100644
-index 0000000000000000000000000000000000000000..b28bdb326f6579840813c4c390c0fb65fa3f0cb5
+index 0000000000000000000000000000000000000000..d8aa7a006044324763bf27a074ecfaaad93b3628
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/FoodProperties.java
-@@ -0,0 +1,161 @@
+@@ -0,0 +1,92 @@
+package io.papermc.paper.datacomponent.item;
+
+import io.papermc.paper.datacomponent.BuildableDataComponent;
@@ -999,57 +1419,6 @@ index 0000000000000000000000000000000000000000..b28bdb326f6579840813c4c390c0fb65
+ boolean canAlwaysEat();
+
+ /**
-+ * The number of seconds that it takes to eat this food item.
-+ *
-+ * @return the number seconds to eat that food
-+ */
-+ @Contract(pure = true)
-+ float eatSeconds();
-+
-+ @Contract(pure = true)
-+ @Nullable ItemStack usingConvertsTo();
-+
-+ /**
-+ * List of effects to apply when eaten.
-+ *
-+ * @return effects
-+ */
-+ @NonNull @Unmodifiable List<@NonNull PossibleEffect> effects();
-+
-+ /**
-+ * Effect to possibly be applied when eaten.
-+ */
-+ @ApiStatus.NonExtendable
-+ interface PossibleEffect {
-+
-+ /**
-+ * Create a possible effect.
-+ *
-+ * @param effect the potion effect
-+ * @param probability the probability of this effect being applied, between 0 and 1 inclusive.
-+ * @return the possible effect
-+ */
-+ @Contract(value = "_, _ -> new", pure = true)
-+ static @NonNull PossibleEffect of(final @NonNull PotionEffect effect, final float probability) {
-+ return ItemComponentTypesBridge.bridge().foodEffect(effect, probability);
-+ }
-+
-+ /**
-+ * Effect instance.
-+ *
-+ * @return effect
-+ */
-+ @NonNull PotionEffect effect();
-+
-+ /**
-+ * Float between 0 and 1, chance for the effect to be applied.
-+ *
-+ * @return chance
-+ */
-+ float probability();
-+ }
-+
-+ /**
+ * Builder for {@link FoodProperties}.
+ */
+ @ApiStatus.Experimental
@@ -1068,16 +1437,6 @@ index 0000000000000000000000000000000000000000..b28bdb326f6579840813c4c390c0fb65
+ @NonNull Builder canAlwaysEat(boolean canAlwaysEat);
+
+ /**
-+ * Set the seconds it takes to eat this food.
-+ *
-+ * @param eatSeconds the seconds, must be non-negative
-+ * @return the builder for chaining
-+ * @see #eatSeconds()
-+ */
-+ @Contract(value = "_ -> this", mutates = "this")
-+ @NonNull Builder eatSeconds(@NonNegative float eatSeconds);
-+
-+ /**
+ * Sets the saturation of the food.
+ *
+ * @param saturation the saturation
@@ -1097,14 +1456,6 @@ index 0000000000000000000000000000000000000000..b28bdb326f6579840813c4c390c0fb65
+ @Contract(value = "_ -> this", mutates = "this")
+ @NonNull Builder nutrition(@NonNegative int nutrition);
+
-+ @Contract(value = "_ -> this", mutates = "this")
-+ @NonNull Builder usingConvertsTo(@Nullable ItemStack stack);
-+
-+ @Contract(value = "_ -> this", mutates = "this")
-+ @NonNull Builder addEffect(@NonNull PossibleEffect effect);
-+
-+ @Contract(value = "_ -> this", mutates = "this")
-+ @NonNull Builder addEffects(@NonNull Collection<@NonNull PossibleEffect> effects);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemAdventurePredicate.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemAdventurePredicate.java
@@ -1320,14 +1671,15 @@ index 0000000000000000000000000000000000000000..79a7f5387142aa267655f6243a8f9291
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridge.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridge.java
new file mode 100644
-index 0000000000000000000000000000000000000000..9ecd83fdc364700ef5aaf3ac63f41818f4a91a07
+index 0000000000000000000000000000000000000000..8e2c70fca8f28996bacde186057fc6ad4eb429bb
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridge.java
-@@ -0,0 +1,92 @@
+@@ -0,0 +1,110 @@
+package io.papermc.paper.datacomponent.item;
+
+import com.destroystokyo.paper.profile.PlayerProfile;
+import io.papermc.paper.registry.set.RegistryKeySet;
++import io.papermc.paper.registry.tag.TagKey;
+import io.papermc.paper.util.Filtered;
+import java.util.Optional;
+import java.util.ServiceLoader;
@@ -1335,9 +1687,12 @@ index 0000000000000000000000000000000000000000..9ecd83fdc364700ef5aaf3ac63f41818
+import net.kyori.adventure.util.TriState;
+import org.bukkit.JukeboxSong;
+import org.bukkit.block.BlockType;
++import org.bukkit.damage.DamageType;
++import org.bukkit.inventory.EquipmentSlot;
++import org.bukkit.inventory.ItemStack;
++import org.bukkit.inventory.ItemType;
+import org.bukkit.inventory.meta.trim.ArmorTrim;
+import org.bukkit.map.MapCursor;
-+import org.bukkit.potion.PotionEffect;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.ApiStatus;
+
@@ -1364,8 +1719,6 @@ index 0000000000000000000000000000000000000000..9ecd83fdc364700ef5aaf3ac63f41818
+
+ FoodProperties.Builder food();
+
-+ FoodProperties.PossibleEffect foodEffect(PotionEffect effect, float probability);
-+
+ DyedItemColor.Builder dyedItemColor();
+
+ PotionContents.Builder potionContents();
@@ -1414,7 +1767,23 @@ index 0000000000000000000000000000000000000000..9ecd83fdc364700ef5aaf3ac63f41818
+
+ MapId mapId(int id);
+
-+ LockCode lockCode(String code);
++ UseRemainder useRemainder(ItemStack itemStack);
++
++ Consumable.Builder consumable();
++
++ UseCooldown.Builder useCooldown(final float seconds);
++
++ DamageResistant damageResistant(TagKey<DamageType> types);
++
++ Enchantable enchantable(int level);
++
++ Repairable repairable(RegistryKeySet<ItemType> types);
++
++ Equippable.Builder equippable(EquipmentSlot slot);
++
++ DeathProtection.Builder deathProtection();
++
++ OminiousBottleAmplifier ominiousBottleAmplifier(int amplifier);
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemContainerContents.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemContainerContents.java
new file mode 100644
@@ -1701,7 +2070,7 @@ index 0000000000000000000000000000000000000000..26ca1143b74d4b0c4e48865dc94128ec
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/LockCode.java b/src/main/java/io/papermc/paper/datacomponent/item/LockCode.java
new file mode 100644
-index 0000000000000000000000000000000000000000..6386d859ec06ffd23553767d8f87d85ed1e6a4df
+index 0000000000000000000000000000000000000000..e32e18010a3f3ed436547cfae86679ef00b21daa
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/LockCode.java
@@ -0,0 +1,22 @@
@@ -1719,13 +2088,13 @@ index 0000000000000000000000000000000000000000..6386d859ec06ffd23553767d8f87d85e
+public interface LockCode {
+
-+ @Contract(value = "_ -> new", pure = true)
-+ static @NonNull LockCode lockCode(final @NonNull String code) {
-+ return ItemComponentTypesBridge.bridge().lockCode(code);
-+ }
-+
-+ @Contract(pure = true)
-+ @NonNull String key();
++ // @Contract(value = "_ -> new", pure = true)
++ // static @NonNull LockCode lockCode(final @NonNull String code) {
++ // return ItemComponentTypesBridge.bridge().lockCode(code);
++ // }
++ //
++ // @Contract(pure = true)
++ // @NonNull String key();
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/LodestoneTracker.java b/src/main/java/io/papermc/paper/datacomponent/item/LodestoneTracker.java
new file mode 100644
@@ -2010,6 +2379,39 @@ index 0000000000000000000000000000000000000000..b385132b23c3f52f327f8a699fdb8cba
+ @NonNull Builder color(@NonNull Color color);
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/OminiousBottleAmplifier.java b/src/main/java/io/papermc/paper/datacomponent/item/OminiousBottleAmplifier.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..73a14a0f9bf98bed1611eeb0285d9f89b76b401f
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/OminiousBottleAmplifier.java
+@@ -0,0 +1,27 @@
++package io.papermc.paper.datacomponent.item;
++
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++
++/**
++ * Holds the custom model data.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#CUSTOM_MODEL_DATA
++ */
++public interface OminiousBottleAmplifier {
++
++ @Contract(value = "_ -> new", pure = true)
++ static @NonNull OminiousBottleAmplifier amplifier(final int amplifier) {
++ return ItemComponentTypesBridge.bridge().ominiousBottleAmplifier(amplifier);
++ }
++
++ /**
++ * Gets the bottle amplifier.
++ *
++ * @return the amplifier
++ */
++ @Contract(pure = true)
++ int amplifier();
++}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PotDecorations.java b/src/main/java/io/papermc/paper/datacomponent/item/PotDecorations.java
new file mode 100644
index 0000000000000000000000000000000000000000..35958f84bf93270c53ad8b09483bab503d85aae3
@@ -2126,10 +2528,10 @@ index 0000000000000000000000000000000000000000..35958f84bf93270c53ad8b09483bab50
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java b/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java
new file mode 100644
-index 0000000000000000000000000000000000000000..e212f5401312c64c6c25af19ba9dc8e511157409
+index 0000000000000000000000000000000000000000..8e16affd83ad0cb94b275246e0da621fbb5fb787
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java
-@@ -0,0 +1,100 @@
+@@ -0,0 +1,118 @@
+package io.papermc.paper.datacomponent.item;
+
+import io.papermc.paper.datacomponent.DataComponentBuilder;
@@ -2182,6 +2584,14 @@ index 0000000000000000000000000000000000000000..e212f5401312c64c6c25af19ba9dc8e5
+ @Contract(pure = true)
+ @NonNull @Unmodifiable List<@NonNull PotionEffect> customEffects();
+
++ /**
++ * Overrides the visual name of the potion.
++ *
++ * @return name override, or {@code null} if not present
++ * @apiNote This is used in the display of tipped arrow and potion items.
++ */
++ @Contract(pure = true) @Nullable String customName();
++
+ @ApiStatus.Experimental
+ @ApiStatus.NonExtendable
+ interface Builder extends DataComponentBuilder<PotionContents> {
@@ -2208,6 +2618,16 @@ index 0000000000000000000000000000000000000000..e212f5401312c64c6c25af19ba9dc8e5
+ @NonNull Builder customColor(@Nullable Color color);
+
+ /**
++ * Sets the name override for this builder.
++ *
++ * @param name name
++ * @return the builder for chaining
++ * @see #customName()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull Builder customName(@Nullable String name);
++
++ /**
+ * Adds a custom effect instance to this builder.
+ *
+ * @param effect effect
@@ -2230,6 +2650,45 @@ index 0000000000000000000000000000000000000000..e212f5401312c64c6c25af19ba9dc8e5
+ @NonNull Builder addCustomEffects(@NonNull List<@NonNull PotionEffect> effects);
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Repairable.java b/src/main/java/io/papermc/paper/datacomponent/item/Repairable.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..928e61eef3f33af0d76e258661a163557fdf40f8
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/Repairable.java
+@@ -0,0 +1,33 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import io.papermc.paper.registry.set.RegistryKeySet;
++import org.bukkit.damage.DamageType;
++import org.bukkit.inventory.ItemType;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++
++/**
++ * Holds if this item is repairable, and what item types it can be repaired with.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#REPAIRABLE
++ */
++public interface Repairable {
++
++ @Contract(value = "_ -> new", pure = true)
++ static @NonNull Repairable repairable(final @NonNull RegistryKeySet<ItemType> types) {
++ return ItemComponentTypesBridge.bridge().repairable(types);
++ }
++
++ /**
++ * The types that this item is repairable to.
++ *
++ * @return item
++ */
++ @Contract(value = "-> new", pure = true)
++ @NonNull
++ RegistryKeySet<ItemType> types();
++
++}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ResolvableProfile.java b/src/main/java/io/papermc/paper/datacomponent/item/ResolvableProfile.java
new file mode 100644
index 0000000000000000000000000000000000000000..85d9a14b3f8abf99793118e5d66cbf3eca66616a
@@ -2729,6 +3188,137 @@ index 0000000000000000000000000000000000000000..a8ddecde35b427f594395a5df5420a20
+ interface Builder extends ShownInTooltip.Builder<Builder>, DataComponentBuilder<Unbreakable> {
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/UseCooldown.java b/src/main/java/io/papermc/paper/datacomponent/item/UseCooldown.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..84ac174f53dfc84f9c20783b70c7fb5a679b1752
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/UseCooldown.java
+@@ -0,0 +1,71 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.List;
++import net.kyori.adventure.key.Key;
++import org.bukkit.Color;
++import org.bukkit.NamespacedKey;
++import org.bukkit.potion.PotionEffect;
++import org.bukkit.potion.PotionType;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++
++/**
++ * Holds the contents of cooldown information when an item is used.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#USE_COOLDOWN
++ */
++public interface UseCooldown {
++
++ /**
++ * Creates a new builder for use cooldown.
++ *
++ * @param seconds the duration in seconds; must be positive
++ * @return builder
++ */
++ @Contract(value = "_ -> new", pure = true)
++ static UseCooldown.@NonNull Builder useCooldown(float seconds) {
++ return ItemComponentTypesBridge.bridge().useCooldown(seconds);
++ }
++
++ /**
++ * The amount of seconds the cooldown will be active for.
++ *
++ * @return cooldown seconds
++ */
++ @Contract(pure = true)
++ float seconds();
++
++ /**
++ * The unique resource location to identify this cooldown group.
++ * <p>
++ * This allows items to share cooldowns with other items in the same cooldown group, if present.
++ *
++ * @return cooldown group, or null if not present
++ */
++ @Contract(pure = true)
++ @Nullable
++ Key cooldownGroup();
++
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder<UseCooldown> {
++
++ /**
++ * Sets a unique resource location for this cooldown group.
++ * <p>
++ * This allows items to share cooldowns with other items in the same cooldown group.
++ * </p>
++ *
++ * @param key the unique resource location; can be null
++ * @return the builder for chaining
++ * @see #cooldownGroup()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull Builder cooldownGroup(@Nullable Key key);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/UseRemainder.java b/src/main/java/io/papermc/paper/datacomponent/item/UseRemainder.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c80d5bf7ddaa24de0c7d2f812f1538b020d3ad69
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/UseRemainder.java
+@@ -0,0 +1,48 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import org.bukkit.Color;
++import org.bukkit.inventory.ItemStack;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++
++/**
++ * Holds the contents of item transformation information when an item is used.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#USE_REMAINDER
++ */
++public interface UseRemainder {
++
++ @Contract(value = "_ -> new", pure = true)
++ static @NonNull UseRemainder useRemainder(final @NonNull ItemStack itemStack) {
++ return ItemComponentTypesBridge.bridge().useRemainder(itemStack);
++ }
++
++ /**
++ * The item that the item that is consumed is transformed into.
++ *
++ * @return item
++ */
++ @Contract(value = "-> new", pure = true)
++ @NonNull ItemStack transformInto();
++
++ /**
++ * Builder for {@link UseRemainder}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends ShownInTooltip.Builder<Builder>, DataComponentBuilder<UseRemainder> {
++
++ /**
++ * Sets the transform item of this builder.
++ *
++ * @param itemStack item
++ * @return the builder for chaining
++ * @see #transformInto()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ @NonNull Builder transformInto(@NonNull ItemStack itemStack);
++ }
++}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/WritableBookContent.java b/src/main/java/io/papermc/paper/datacomponent/item/WritableBookContent.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb6042af2d5d0c63f1adc56576a4e6b80bb31463
@@ -2993,6 +3583,289 @@ index 0000000000000000000000000000000000000000..a8f86b9ec871857106903715749bbbe4
+ @NonNull Builder addFilteredPages(@NonNull Collection<@NonNull Filtered<@NonNull ? extends ComponentLike>> pages);
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/ApplyStatusEffectsConsumeEffect.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ApplyStatusEffectsConsumeEffect.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..d11d4563e20916000d2d4cb754e7c1e386386bfb
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ApplyStatusEffectsConsumeEffect.java
+@@ -0,0 +1,39 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import org.bukkit.potion.PotionEffect;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jetbrains.annotations.Contract;
++import java.util.List;
++
++/**
++ * Represents a consumable effect that applies effects based on a probability on consumption.
++ */
++public interface ApplyStatusEffectsConsumeEffect extends ConsumeEffect {
++
++ /**
++ * Creates a consume effect that gives status effects on consumption.
++ *
++ * @param effects the potion effects to apply
++ * @param probability the probability of these effects being applied, between 0 and 1 inclusive.
++ * @return the effect
++ */
++ @Contract(value = "_, _ -> new", pure = true)
++ static @NonNull ApplyStatusEffectsConsumeEffect applyStatusEffects(final @NonNull List<PotionEffect> effects, final float probability) {
++ return ConsumableTypesBridge.bridge().applyStatusEffects(effects, probability);
++ }
++
++ /**
++ * Effect instances to grant
++ *
++ * @return effect
++ */
++ @NonNull List<PotionEffect> effects();
++
++ /**
++ * Float between 0 and 1, chance for the effect to be applied.
++ *
++ * @return chance
++ */
++ float probability();
++
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/ClearAllStatusEffectsConsumeEffect.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ClearAllStatusEffectsConsumeEffect.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..97208df2e8ffbb653a96da34534fd6a08e5a141c
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ClearAllStatusEffectsConsumeEffect.java
+@@ -0,0 +1,22 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import org.bukkit.potion.PotionEffect;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jetbrains.annotations.Contract;
++import java.util.List;
++
++/**
++ * Represents a consumable effect that clears all effects on consumption.
++ */
++public interface ClearAllStatusEffectsConsumeEffect extends ConsumeEffect {
++
++ /**
++ * Creates a consume effect that clears all status effects.
++ *
++ * @return effect instance
++ */
++ @Contract(value = "-> new", pure = true)
++ static @NonNull ClearAllStatusEffectsConsumeEffect clearAllStatusEffects() {
++ return ConsumableTypesBridge.bridge().clearAllStatusEffects();
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridge.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridge.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e11b697e93126fa1b7342a4a872220130184374c
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridge.java
+@@ -0,0 +1,40 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import com.destroystokyo.paper.profile.PlayerProfile;
++import io.papermc.paper.registry.set.RegistryKeySet;
++import io.papermc.paper.registry.tag.TagKey;
++import io.papermc.paper.util.Filtered;
++import net.kyori.adventure.key.Key;
++import net.kyori.adventure.util.TriState;
++import org.bukkit.JukeboxSong;
++import org.bukkit.block.BlockType;
++import org.bukkit.inventory.meta.trim.ArmorTrim;
++import org.bukkit.map.MapCursor;
++import org.bukkit.potion.PotionEffect;
++import org.bukkit.potion.PotionEffectType;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jetbrains.annotations.ApiStatus;
++import java.util.List;
++import java.util.Optional;
++import java.util.ServiceLoader;
++
++interface ConsumableTypesBridge {
++
++ Optional<ConsumableTypesBridge> BRIDGE = ServiceLoader.load(ConsumableTypesBridge.class).findFirst();
++
++ static ConsumableTypesBridge bridge() {
++ return BRIDGE.orElseThrow();
++ }
++
++ ApplyStatusEffectsConsumeEffect applyStatusEffects(List<PotionEffect> effectList, float probability);
++
++ RemoveStatusEffectsConsumeEffect removeStatusEffects(RegistryKeySet<PotionEffectType> potionEffectTypeTagKey);
++
++ ClearAllStatusEffectsConsumeEffect clearAllStatusEffects();
++
++ PlaySoundConsumeEffect playSoundEffect(Key sound);
++
++ TeleportRandomlyConsumeEffect teleportRandomlyEffect(float diameter);
++
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumeEffect.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumeEffect.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..50b06f14d611fc189873f652d4769484ceea4703
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumeEffect.java
+@@ -0,0 +1,18 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import io.papermc.paper.datacomponent.item.Consumable;
++import org.bukkit.potion.PotionEffect;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++
++import java.util.List;
++
++/**
++ * Effect that occurs when consuming an item.
++ */
++public
++interface ConsumeEffect {
++
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/ItemUseAnimation.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ItemUseAnimation.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..8cd6dbe4ea5ee3270b9428a9c29cbd88823d9f6c
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ItemUseAnimation.java
+@@ -0,0 +1,17 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++/**
++ * Represents the hand animation that is used when a player is consuming this item.
++ */
++public enum ItemUseAnimation {
++ NONE,
++ EAT,
++ DRINK,
++ BLOCK,
++ BOW,
++ SPEAR,
++ CROSSBOW,
++ SPYGLASS,
++ TOOT_HORN,
++ BRUSH
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PlaySoundConsumeEffect.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PlaySoundConsumeEffect.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..82c232a1acc1ec93303cc03c40dc855bd8c36284
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PlaySoundConsumeEffect.java
+@@ -0,0 +1,35 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import net.kyori.adventure.key.Key;
++import org.bukkit.potion.PotionEffect;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.NotNull;
++import java.util.List;
++
++/**
++ * Represents a consumable effect that plays a sound on consumption.
++ */
++public interface PlaySoundConsumeEffect extends ConsumeEffect {
++
++ /**
++ * Creates a consume effect that plays a sound on consumption.
++ *
++ * @param key the sound effect to play
++ * @return the effect
++ */
++ @Contract(value = "_ -> new", pure = true)
++ static @NonNull PlaySoundConsumeEffect playSoundConsumeEffect(final @NotNull Key key) {
++ return ConsumableTypesBridge.bridge().playSoundEffect(key);
++ }
++
++ /**
++ * Sound effect to play in the world
++ *
++ * @return sound effect
++ */
++ @NonNull
++ Key sound();
++
++
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/RemoveStatusEffectsConsumeEffect.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/RemoveStatusEffectsConsumeEffect.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9190034759052ef643181df12fdbf94d971cc07e
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/RemoveStatusEffectsConsumeEffect.java
+@@ -0,0 +1,36 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++
++import io.papermc.paper.registry.set.RegistryKeySet;
++import net.kyori.adventure.key.Key;
++import org.bukkit.inventory.ItemType;
++import org.bukkit.potion.PotionEffectType;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Represents a consumable effect that removes status effects on consumption
++ */
++public interface RemoveStatusEffectsConsumeEffect extends ConsumeEffect {
++
++ /**
++ * Creates a consume effect that gives status effects on consumption.
++ *
++ * @param key the sound effect to play
++ * @return the effect
++ */
++ @Contract(value = "_ -> new", pure = true)
++ static @NonNull RemoveStatusEffectsConsumeEffect removeEffects(final @NotNull RegistryKeySet<PotionEffectType> key) {
++ return ConsumableTypesBridge.bridge().removeStatusEffects(key);
++ }
++
++ /**
++ * Potion effects to remove
++ *
++ * @return effects
++ */
++ @NonNull
++ RegistryKeySet<PotionEffectType> removeEffects();
++
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/TeleportRandomlyConsumeEffect.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/TeleportRandomlyConsumeEffect.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4f765f322a6ba69978411e453f772fc786b5cdf7
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/TeleportRandomlyConsumeEffect.java
+@@ -0,0 +1,28 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import io.papermc.paper.registry.set.RegistryKeySet;
++import org.bukkit.potion.PotionEffectType;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jetbrains.annotations.Contract;
++
++public interface TeleportRandomlyConsumeEffect extends ConsumeEffect {
++
++ /**
++ * Creates a consume effect that randomly teleports the entity on consumption.
++ *
++ * @param diameter diameter of random teleportation
++ * @return the effect
++ */
++ @Contract(value = "_ -> new", pure = true)
++ static @NonNull TeleportRandomlyConsumeEffect teleportRandomlyEffect(final float diameter) {
++ return ConsumableTypesBridge.bridge().teleportRandomlyEffect(diameter);
++ }
++
++ /**
++ * The max range that the entity can be teleported to.
++ *
++ * @return teleportation diameter
++ */
++ float diameter();
++
++}
diff --git a/src/main/java/io/papermc/paper/datacomponent/package-info.java b/src/main/java/io/papermc/paper/datacomponent/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ddd1f787e97c22eee3e32bb2a9660a8e300d7f9
@@ -3017,7 +3890,7 @@ index 0000000000000000000000000000000000000000..5843768d0be2ae4a0219636ed7640727
+ SCALE
+}
diff --git a/src/main/java/io/papermc/paper/registry/RegistryKey.java b/src/main/java/io/papermc/paper/registry/RegistryKey.java
-index 2945dde566682f977e84fde5d473a6c69be24df1..530cbadc8b4871113dc9ebb24bc565a95a61fdae 100644
+index d8716f855806471728c35b3ec34efb808a5146cf..cf8a28a6e41fadeeffe358e5bcdc25bf6c351aea 100644
--- a/src/main/java/io/papermc/paper/registry/RegistryKey.java
+++ b/src/main/java/io/papermc/paper/registry/RegistryKey.java
@@ -1,5 +1,6 @@
@@ -3027,13 +3900,10 @@ index 2945dde566682f977e84fde5d473a6c69be24df1..530cbadc8b4871113dc9ebb24bc565a9
import net.kyori.adventure.key.Keyed;
import org.bukkit.Art;
import org.bukkit.Fluid;
-@@ -81,6 +82,10 @@ public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
+@@ -114,6 +115,7 @@ public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
+ * @see io.papermc.paper.registry.keys.MenuTypeKeys
*/
- @ApiStatus.Experimental // Paper - already required for registry builders
- RegistryKey<ItemType> ITEM = create("item");
-+ /**
-+ * Built-in registry for data component types.
-+ */
+ RegistryKey<MenuType> MENU = create("menu");
+ RegistryKey<DataComponentType> DATA_COMPONENT_TYPE = create("data_component_type");
@@ -3075,10 +3945,10 @@ index 0000000000000000000000000000000000000000..6919f01a18bc0ab375d2e05412065243
+ @Nullable T filtered();
+}
diff --git a/src/main/java/org/bukkit/Material.java b/src/main/java/org/bukkit/Material.java
-index 54704da43cf9c429f3914f0580246dde99aa93c0..87bc4ce78350d95b9337262ba585b44c77213722 100644
+index 615eb24ffdd8f6d55ccd4f21760b809c1098bc68..1b3e120bb9b10b65eb6225af8f08caed5973007d 100644
--- a/src/main/java/org/bukkit/Material.java
+++ b/src/main/java/org/bukkit/Material.java
-@@ -130,7 +130,7 @@ import org.jetbrains.annotations.Nullable;
+@@ -137,7 +137,7 @@ import org.jetbrains.annotations.Nullable;
@SuppressWarnings({"DeprecatedIsStillUsed", "deprecation"}) // Paper
public enum Material implements Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper
//<editor-fold desc="Materials" defaultstate="collapsed">
@@ -3087,25 +3957,21 @@ index 54704da43cf9c429f3914f0580246dde99aa93c0..87bc4ce78350d95b9337262ba585b44c
STONE(22948),
GRANITE(21091),
POLISHED_GRANITE(5477),
-@@ -5599,6 +5599,7 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla
+@@ -5784,6 +5784,7 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla
*/
@ApiStatus.Internal
@Nullable
+ @org.jetbrains.annotations.Contract(pure = true) // Paper
public ItemType asItemType() {
- Material material = this;
- if (isLegacy()) {
-@@ -5615,6 +5616,7 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla
+ return itemType.get();
+ }
+@@ -5796,7 +5797,47 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla
*/
@ApiStatus.Internal
@Nullable
+ @org.jetbrains.annotations.Contract(pure = true) // Paper
public BlockType asBlockType() {
- Material material = this;
- if (isLegacy()) {
-@@ -5622,4 +5624,43 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla
- }
- return Registry.BLOCK.get(material.key);
+ return blockType.get();
}
+
+ // Paper start - data component API
@@ -3148,23 +4014,22 @@ index 54704da43cf9c429f3914f0580246dde99aa93c0..87bc4ce78350d95b9337262ba585b44c
+ // Paper end - data component API
}
diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java
-index 20015393f91af405c99db2635a471fb6ff19e4bf..7ab29c3d0471d5eb2152ff749efc15ac9d24acf8 100644
+index b4ef3133fdd9d79a3381cf8f659ff561ab2b4fad..d3c9fb2fd625ed6ae4882c3b16b86f324f236161 100644
--- a/src/main/java/org/bukkit/Registry.java
+++ b/src/main/java/org/bukkit/Registry.java
-@@ -349,6 +349,8 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
- return StreamSupport.stream(this.spliterator(), false);
- }
- };
-+
-+ Registry<io.papermc.paper.datacomponent.DataComponentType> DATA_COMPONENT_TYPE = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.DATA_COMPONENT_TYPE); // Paper
+@@ -370,6 +370,7 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
+ */
+ Registry<org.bukkit.potion.PotionEffectType> POTION_EFFECT_TYPE = EFFECT;
// Paper end - potion effect type registry
++ Registry<io.papermc.paper.datacomponent.DataComponentType> DATA_COMPONENT_TYPE = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.DATA_COMPONENT_TYPE); // Paper
/**
* Get the object by its key.
+ *
diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
-index b3abe3bde05d4a360e31e490bff8a859dc2bd4a6..794b3cf36f6ce4fb2c7957a4b259f8adf63090f3 100644
+index b59222b8c262545d100a9fd28b3bf1d2a4cf4eb0..ef254ecf202e85afec2919baf6075ad44dbcab53 100644
--- a/src/main/java/org/bukkit/inventory/ItemStack.java
+++ b/src/main/java/org/bukkit/inventory/ItemStack.java
-@@ -1,10 +1,10 @@
+@@ -1,10 +1,11 @@
package org.bukkit.inventory;
import com.google.common.base.Preconditions;
@@ -3173,10 +4038,11 @@ index b3abe3bde05d4a360e31e490bff8a859dc2bd4a6..794b3cf36f6ce4fb2c7957a4b259f8ad
import java.util.Locale;
import java.util.Map;
+import io.papermc.paper.datacomponent.DataComponentBuilder;
++import io.papermc.paper.datacomponent.DataComponentType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
-@@ -1033,4 +1033,149 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+@@ -1137,4 +1138,173 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
return Bukkit.getUnsafe().computeTooltipLines(this, tooltipContext, player);
}
// Paper end - expose itemstack tooltip lines
@@ -3248,6 +4114,30 @@ index b3abe3bde05d4a360e31e490bff8a859dc2bd4a6..794b3cf36f6ce4fb2c7957a4b259f8ad
+ }
+
+ /**
++ * Modifies the value of the specified data component type for this item stack based on the result
++ * of applying a given function to the current value.
++ *
++ * <p>If the function returns {@code null}, the data component type will be reset using
++ * {@link #unsetData(DataComponentType)}. Otherwise, the
++ * component value will be updated with the new result using {@link #setData(DataComponentType.Valued, Object)}.</p>
++ *
++ * @param <T> the type of the data component's value
++ * @param type the data component type to be modified
++ * @param consumer a function that takes the current component value (can be {@code null}) and
++ * returns the modified value (or {@code null} to unset)
++ */
++ @Utility
++ public <T> void editData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued<T> type, final @NotNull java.util.function.Function<@Nullable T, @Nullable T> consumer) {
++ T value = getData(type);
++ T newType = consumer.apply(value);
++ if (newType == null) {
++ unsetData(type);
++ } else {
++ setData(type, newType);
++ }
++ }
++
++ /**
+ * Sets the value of the data component type for this itemstack. To
+ * reset the value to the default for the {@link #getType() item type}, use
+ * {@link #resetData(io.papermc.paper.datacomponent.DataComponentType)}. To mark the data component type
@@ -3327,10 +4217,10 @@ index b3abe3bde05d4a360e31e490bff8a859dc2bd4a6..794b3cf36f6ce4fb2c7957a4b259f8ad
+ // 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 0168f0a14a3e899e84c5e36963ff79950ab580fb..6d0466a8a54531eaf51521b10047a27ef83d47b7 100644
+index 72803c00e4af576f286d2af34bf300ee554a7f3c..9769726ec1e227606a79ccab1e8e439ef9ec16c1 100644
--- a/src/main/java/org/bukkit/inventory/ItemType.java
+++ b/src/main/java/org/bukkit/inventory/ItemType.java
-@@ -2336,4 +2336,30 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
+@@ -2483,4 +2483,30 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
*/
@Nullable ItemRarity getItemRarity();
// Paper end - expand ItemRarity API
diff --git a/patches/server/1044-WIP-DataComponent-API.patch b/patches/server/1058-WIP-DataComponent-API.patch
index 99c2f3ddaa..6e42b123d4 100644
--- a/patches/server/1044-WIP-DataComponent-API.patch
+++ b/patches/server/1058-WIP-DataComponent-API.patch
@@ -8,7 +8,7 @@ public net/minecraft/world/item/component/ItemContainerContents MAX_SIZE
public net/minecraft/world/item/component/ItemContainerContents items
diff --git a/src/main/java/io/papermc/paper/adventure/PaperAdventure.java b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java
-index 22fe529890f34f66534c01248f654dc911b44c3b..6a1908d0eca04af885171cde44f419478048064e 100644
+index cfcaf215c4a901dd2938c7ce41db502c57b42bbf..e735cc98c53b62defa02d80f4b185641b5e27ae8 100644
--- a/src/main/java/io/papermc/paper/adventure/PaperAdventure.java
+++ b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java
@@ -149,6 +149,10 @@ public final class PaperAdventure {
@@ -66,10 +66,10 @@ index 0000000000000000000000000000000000000000..08c717590a34584c359408c49c69379c
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/ComponentAdapters.java b/src/main/java/io/papermc/paper/datacomponent/ComponentAdapters.java
new file mode 100644
-index 0000000000000000000000000000000000000000..04742f12afc523aa1748f94b4bad8536074fff87
+index 0000000000000000000000000000000000000000..1c861bd7773cbfce8c74403b9308c327580232cc
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/ComponentAdapters.java
-@@ -0,0 +1,172 @@
+@@ -0,0 +1,195 @@
+package io.papermc.paper.datacomponent;
+
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
@@ -78,8 +78,13 @@ index 0000000000000000000000000000000000000000..04742f12afc523aa1748f94b4bad8536
+import io.papermc.paper.datacomponent.item.PaperBlockItemDataProperties;
+import io.papermc.paper.datacomponent.item.PaperBundleContents;
+import io.papermc.paper.datacomponent.item.PaperChargedProjectiles;
++import io.papermc.paper.datacomponent.item.PaperConsumable;
+import io.papermc.paper.datacomponent.item.PaperCustomModelData;
++import io.papermc.paper.datacomponent.item.PaperDamageResistant;
++import io.papermc.paper.datacomponent.item.PaperDeathProtection;
+import io.papermc.paper.datacomponent.item.PaperDyedItemColor;
++import io.papermc.paper.datacomponent.item.PaperEnchantable;
++import io.papermc.paper.datacomponent.item.PaperEquippable;
+import io.papermc.paper.datacomponent.item.PaperFireworks;
+import io.papermc.paper.datacomponent.item.PaperFoodProperties;
+import io.papermc.paper.datacomponent.item.PaperItemAdventurePredicate;
@@ -95,23 +100,29 @@ index 0000000000000000000000000000000000000000..04742f12afc523aa1748f94b4bad8536
+import io.papermc.paper.datacomponent.item.PaperMapDecorations;
+import io.papermc.paper.datacomponent.item.PaperMapId;
+import io.papermc.paper.datacomponent.item.PaperMapItemColor;
++import io.papermc.paper.datacomponent.item.PaperOminousBottleAmplifier;
+import io.papermc.paper.datacomponent.item.PaperPotDecorations;
+import io.papermc.paper.datacomponent.item.PaperPotionContents;
++import io.papermc.paper.datacomponent.item.PaperRepairable;
+import io.papermc.paper.datacomponent.item.PaperResolvableProfile;
+import io.papermc.paper.datacomponent.item.PaperSeededContainerLoot;
+import io.papermc.paper.datacomponent.item.PaperSuspiciousStewEffects;
+import io.papermc.paper.datacomponent.item.PaperUnbreakable;
++import io.papermc.paper.datacomponent.item.PaperUseCooldown;
++import io.papermc.paper.datacomponent.item.PaperUseRemainder;
+import io.papermc.paper.datacomponent.item.PaperWritableBookContent;
+import io.papermc.paper.datacomponent.item.PaperWrittenBookContent;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
++import io.papermc.paper.registry.PaperRegistries;
+import net.minecraft.core.component.DataComponentType;
+import net.minecraft.core.component.DataComponents;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.Tag;
+import net.minecraft.resources.ResourceKey;
++import net.minecraft.resources.ResourceLocation;
+import net.minecraft.util.Unit;
+import net.minecraft.world.LockCode;
+import net.minecraft.world.item.Rarity;
@@ -124,6 +135,7 @@ index 0000000000000000000000000000000000000000..04742f12afc523aa1748f94b4bad8536
+import org.bukkit.DyeColor;
+import org.bukkit.craftbukkit.CraftMusicInstrument;
+import org.bukkit.craftbukkit.inventory.CraftMetaFirework;
++import org.bukkit.craftbukkit.util.CraftNamespacedKey;
+import org.bukkit.craftbukkit.util.Handleable;
+import org.bukkit.inventory.ItemRarity;
+import org.checkerframework.checker.nullness.qual.NonNull;
@@ -159,6 +171,8 @@ index 0000000000000000000000000000000000000000..04742f12afc523aa1748f94b4bad8536
+ register(DataComponents.UNBREAKABLE, PaperUnbreakable::new);
+ register(DataComponents.CUSTOM_NAME, PaperAdventure::asAdventure, PaperAdventure::asVanilla);
+ register(DataComponents.ITEM_NAME, PaperAdventure::asAdventure, PaperAdventure::asVanilla);
++ register(DataComponents.ITEM_MODEL, CraftNamespacedKey::fromMinecraft, CraftNamespacedKey::toMinecraft);
++
+ register(DataComponents.LORE, PaperItemLore::new);
+ register(DataComponents.RARITY, nms -> ItemRarity.valueOf(nms.name()), api -> Rarity.valueOf(api.name()));
+ register(DataComponents.ENCHANTMENTS, PaperItemEnchantments::new);
@@ -173,8 +187,17 @@ index 0000000000000000000000000000000000000000..04742f12afc523aa1748f94b4bad8536
+ registerIdentity(DataComponents.ENCHANTMENT_GLINT_OVERRIDE);
+ registerUntyped(DataComponents.INTANGIBLE_PROJECTILE);
+ register(DataComponents.FOOD, PaperFoodProperties::new);
-+ registerUntyped(DataComponents.FIRE_RESISTANT);
++ register(DataComponents.CONSUMABLE, PaperConsumable::new);
++ register(DataComponents.USE_REMAINDER, PaperUseRemainder::new);
++ register(DataComponents.USE_COOLDOWN, PaperUseCooldown::new);
++ register(DataComponents.DAMAGE_RESISTANT, PaperDamageResistant::new);
+ register(DataComponents.TOOL, PaperItemTool::new);
++ register(DataComponents.ENCHANTABLE, PaperEnchantable::new);
++ register(DataComponents.EQUIPPABLE, PaperEquippable::new);
++ register(DataComponents.REPAIRABLE, PaperRepairable::new);
++ registerUntyped(DataComponents.GLIDER);
++ register(DataComponents.TOOLTIP_STYLE, PaperAdventure::asAdventure, PaperAdventure::asVanilla);
++ register(DataComponents.DEATH_PROTECTION, PaperDeathProtection::new);
+ register(DataComponents.STORED_ENCHANTMENTS, PaperItemEnchantments::new);
+ register(DataComponents.DYED_COLOR, PaperDyedItemColor::new);
+ register(DataComponents.MAP_COLOR, PaperMapItemColor::new);
@@ -193,9 +216,9 @@ index 0000000000000000000000000000000000000000..04742f12afc523aa1748f94b4bad8536
+ // bucket entity data
+ // block entity data
+ register(DataComponents.INSTRUMENT, CraftMusicInstrument::minecraftHolderToBukkit, CraftMusicInstrument::bukkitToMinecraftHolder);
-+ registerIdentity(DataComponents.OMINOUS_BOTTLE_AMPLIFIER);
++ register(DataComponents.OMINOUS_BOTTLE_AMPLIFIER, PaperOminousBottleAmplifier::new);
+ register(DataComponents.JUKEBOX_PLAYABLE, PaperJukeboxPlayable::new);
-+ register(DataComponents.RECIPES, nms -> transform(nms, PaperAdventure::asAdventure), api -> transform(api, PaperAdventure::asVanilla));
++ register(DataComponents.RECIPES, nms -> transform(nms, PaperRegistries::fromNms), api -> transform(api, PaperRegistries::toNms));
+ register(DataComponents.LODESTONE_TRACKER, PaperLodestoneTracker::new);
+ register(DataComponents.FIREWORK_EXPLOSION, CraftMetaFirework::getEffect, CraftMetaFirework::getExplosion);
+ register(DataComponents.FIREWORKS, PaperFireworks::new);
@@ -244,10 +267,10 @@ index 0000000000000000000000000000000000000000..04742f12afc523aa1748f94b4bad8536
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/ComponentUtils.java b/src/main/java/io/papermc/paper/datacomponent/ComponentUtils.java
new file mode 100644
-index 0000000000000000000000000000000000000000..07e268941d95c315592e3464c3ea08023205813e
+index 0000000000000000000000000000000000000000..b169ea527559cfb3608037c71dedd366a14f5790
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/ComponentUtils.java
-@@ -0,0 +1,31 @@
+@@ -0,0 +1,42 @@
+package io.papermc.paper.datacomponent;
+
+import com.google.common.collect.Collections2;
@@ -256,6 +279,12 @@ index 0000000000000000000000000000000000000000..07e268941d95c315592e3464c3ea0802
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
++import io.papermc.paper.adventure.PaperAdventure;
++import net.kyori.adventure.key.Key;
++import net.minecraft.core.Holder;
++import net.minecraft.core.registries.BuiltInRegistries;
++import net.minecraft.resources.ResourceLocation;
++import net.minecraft.sounds.SoundEvent;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
@@ -265,6 +294,11 @@ index 0000000000000000000000000000000000000000..07e268941d95c315592e3464c3ea0802
+ private ComponentUtils() {
+ }
+
++ public static Holder<SoundEvent> keyToSound(Key key) {
++ ResourceLocation soundId = PaperAdventure.asVanilla(key);
++ return BuiltInRegistries.SOUND_EVENT.wrapAsHolder(BuiltInRegistries.SOUND_EVENT.getOptional(soundId).orElse(SoundEvent.createVariableRangeEvent(soundId)));
++ }
++
+ public static <A, M> List<A> transform(final List<? extends M> nms, final Function<? super M, ? extends A> converter) {
+ return Collections.unmodifiableList(Lists.transform(nms, converter::apply));
+ }
@@ -399,22 +433,32 @@ index 0000000000000000000000000000000000000000..74e883d50477b3b4dabdcb674d95e92e
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java
new file mode 100644
-index 0000000000000000000000000000000000000000..10da83e8c871ba464e124ff011497d5de37b8d8a
+index 0000000000000000000000000000000000000000..32266842261fe62ff50925782e69b2ec4f48a653
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java
-@@ -0,0 +1,184 @@
+@@ -0,0 +1,235 @@
+package io.papermc.paper.datacomponent.item;
+
+import com.destroystokyo.paper.profile.PlayerProfile;
++import io.papermc.paper.registry.PaperRegistries;
++import io.papermc.paper.registry.set.PaperRegistrySets;
+import io.papermc.paper.registry.set.RegistryKeySet;
++import io.papermc.paper.registry.tag.TagKey;
+import io.papermc.paper.util.Filtered;
+import net.kyori.adventure.key.Key;
+import net.kyori.adventure.util.TriState;
++import net.minecraft.core.registries.BuiltInRegistries;
++import net.minecraft.core.registries.Registries;
++import net.minecraft.world.item.component.OminousBottleAmplifier;
+import org.bukkit.JukeboxSong;
+import org.bukkit.block.BlockType;
++import org.bukkit.craftbukkit.inventory.CraftItemStack;
++import org.bukkit.damage.DamageType;
++import org.bukkit.inventory.EquipmentSlot;
++import org.bukkit.inventory.ItemStack;
++import org.bukkit.inventory.ItemType;
+import org.bukkit.inventory.meta.trim.ArmorTrim;
+import org.bukkit.map.MapCursor;
-+import org.bukkit.potion.PotionEffect;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
@@ -458,11 +502,6 @@ index 0000000000000000000000000000000000000000..10da83e8c871ba464e124ff011497d5d
+ }
+
+ @Override
-+ public FoodProperties.PossibleEffect foodEffect(final PotionEffect effect, final float probability) {
-+ return PaperFoodProperties.PossibleEffectImpl.toApi(effect, probability);
-+ }
-+
-+ @Override
+ public DyedItemColor.Builder dyedItemColor() {
+ return new PaperDyedItemColor.BuilderImpl();
+ }
@@ -578,14 +617,60 @@ index 0000000000000000000000000000000000000000..10da83e8c871ba464e124ff011497d5d
+ }
+
+ @Override
-+ public LockCode lockCode(final String code) {
-+ return new PaperLockCode(new net.minecraft.world.LockCode(code));
++ public UseRemainder useRemainder(final ItemStack itemStack) {
++ return new PaperUseRemainder(
++ new net.minecraft.world.item.component.UseRemainder(CraftItemStack.asNMSCopy(itemStack))
++ );
++ }
++
++ @Override
++ public Consumable.Builder consumable() {
++ return new PaperConsumable.BuilderImpl();
++ }
++
++ @Override
++ public UseCooldown.Builder useCooldown(final float seconds) {
++ return new PaperUseCooldown.BuilderImpl(seconds);
++ }
++
++ @Override
++ public DamageResistant damageResistant(final TagKey<DamageType> types) {
++ return new PaperDamageResistant(new net.minecraft.world.item.component.DamageResistant(PaperRegistries.toNms(types)));
++ }
++
++ @Override
++ public Enchantable enchantable(final int level) {
++ return new PaperEnchantable(new net.minecraft.world.item.enchantment.Enchantable(level));
++ }
++
++ @Override
++ public Repairable repairable(final RegistryKeySet<ItemType> types) {
++ return new PaperRepairable(new net.minecraft.world.item.enchantment.Repairable(
++ PaperRegistrySets.convertToNms(Registries.ITEM, BuiltInRegistries.BUILT_IN_CONVERSIONS.lookup(), types)
++ ));
++ }
++
++ @Override
++ public Equippable.Builder equippable(EquipmentSlot slot) {
++ return new PaperEquippable.BuilderImpl(slot);
++ }
++
++ @Override
++ public DeathProtection.Builder deathProtection() {
++ return new PaperDeathProtection.BuilderImpl();
+ }
+
+ @Override
+ public CustomModelData customModelData(final int id) {
+ return new PaperCustomModelData(new net.minecraft.world.item.component.CustomModelData(id));
+ }
++
++ @Override
++ public OminiousBottleAmplifier ominiousBottleAmplifier(final int amplifier) {
++ return new PaperOminousBottleAmplifier(
++ new OminousBottleAmplifier(amplifier)
++ );
++ }
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperBannerPatternLayers.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperBannerPatternLayers.java
new file mode 100644
@@ -839,6 +924,165 @@ index 0000000000000000000000000000000000000000..db00e0d68dba2b844377248c8e70b5e2
+ }
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperConsumable.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperConsumable.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..ddcd650d2f4d71ba8f602d2562ecb9b716a61fc9
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperConsumable.java
+@@ -0,0 +1,153 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.adventure.PaperAdventure;
++import io.papermc.paper.block.BlockPredicate;
++import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect;
++import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation;
++import io.papermc.paper.datacomponent.item.consumable.PaperConsumableEffects;
++import io.papermc.paper.registry.RegistryKey;
++import io.papermc.paper.registry.set.PaperRegistrySets;
++import java.util.ArrayList;
++import java.util.Collection;
++import java.util.Collections;
++import java.util.List;
++import java.util.Optional;
++import net.kyori.adventure.key.Key;
++import net.minecraft.core.Holder;
++import net.minecraft.core.registries.BuiltInRegistries;
++import net.minecraft.core.registries.Registries;
++import net.minecraft.resources.ResourceLocation;
++import net.minecraft.sounds.SoundEvent;
++import net.minecraft.sounds.SoundEvents;
++import net.minecraft.world.item.component.Consumables;
++import org.bukkit.craftbukkit.util.Handleable;
++import org.checkerframework.checker.index.qual.NonNegative;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Unmodifiable;
++
++import static io.papermc.paper.datacomponent.ComponentUtils.transform;
++
++@DefaultQualifier(NonNull.class)
++public record PaperConsumable(
++ net.minecraft.world.item.component.Consumable impl,
++ List<ConsumeEffect> consumeEffects
++) implements Consumable, Handleable<net.minecraft.world.item.component.Consumable> {
++
++ public PaperConsumable(final net.minecraft.world.item.component.Consumable impl) {
++ this(
++ impl,
++ transform(impl.onConsumeEffects(), PaperConsumableEffects::fromNms)
++ );
++ }
++
++ private static final ItemUseAnimation[] VALUES = ItemUseAnimation.values();
++
++ @Override
++ public net.minecraft.world.item.component.Consumable getHandle() {
++ return this.impl;
++ }
++
++ @Override
++ public @NonNegative float consumeSeconds() {
++ return this.impl.consumeSeconds();
++ }
++
++ @Override
++ public ItemUseAnimation animation() {
++ return VALUES[impl.animation().ordinal()];
++ }
++
++ @Override
++ public @NonNull Key sound() {
++ return PaperAdventure.asAdventure(this.impl.sound().value().location());
++ }
++
++ @Override
++ public boolean hasConsumeParticles() {
++ return this.impl.hasConsumeParticles();
++ }
++
++ @Override
++ public @Unmodifiable @NonNull List<ConsumeEffect> consumeEffects() {
++ return this.consumeEffects;
++ }
++
++ @Override
++ public Consumable.Builder toBuilder() {
++ return new BuilderImpl()
++ .consumeSeconds(this.consumeSeconds())
++ .animation(this.animation())
++ .sound(this.sound())
++ .addEffects(this.consumeEffects());
++ }
++
++ static final class BuilderImpl implements Builder {
++
++ private static final net.minecraft.world.item.ItemUseAnimation[] VALUES = net.minecraft.world.item.ItemUseAnimation.values();
++
++ private float consumeSeconds = net.minecraft.world.item.component.Consumable.DEFAULT_CONSUME_SECONDS;
++ private net.minecraft.world.item.ItemUseAnimation consumeAnimation = net.minecraft.world.item.ItemUseAnimation.EAT;
++ private Holder<SoundEvent> eatSound = SoundEvents.GENERIC_EAT;
++ private boolean hasConsumeParticles = true;
++ private final List<net.minecraft.world.item.consume_effects.ConsumeEffect> effects = new ArrayList<>();
++
++ @Override
++ public @NonNull Builder consumeSeconds(@NonNegative final float consumeSeconds) {
++ this.consumeSeconds = consumeSeconds;
++ return this;
++ }
++
++ @Override
++ public @NonNull Builder animation(@NotNull final ItemUseAnimation animation) {
++ this.consumeAnimation = VALUES[animation.ordinal()];
++ return this;
++ }
++
++ @Override
++ public @NonNull Builder sound(@NonNull final Key sound) {
++ ResourceLocation keySound = PaperAdventure.asVanilla(sound);
++ this.eatSound = BuiltInRegistries.SOUND_EVENT.wrapAsHolder(
++ BuiltInRegistries.SOUND_EVENT.getOptional(keySound).orElse(
++ SoundEvent.createVariableRangeEvent(keySound)
++ )
++ );
++
++ return this;
++ }
++
++ @Override
++ public @NonNull Builder hasConsumeParticles(final boolean hasConsumeParticles) {
++ this.hasConsumeParticles = hasConsumeParticles;
++ return this;
++ }
++
++ @Override
++ public @NonNull Builder addEffect(@NonNull final ConsumeEffect effect) {
++ this.effects.add(PaperConsumableEffects.toNms(effect));
++ return this;
++ }
++
++ @Override
++ public @NonNull Builder addEffects(@NonNull final Collection<@NonNull ConsumeEffect> effects) {
++ for (ConsumeEffect effect : effects) {
++ this.effects.add(PaperConsumableEffects.toNms(effect));
++ }
++ return this;
++ }
++
++ @Override
++ public @NonNull Consumable build() {
++ return new PaperConsumable(
++ new net.minecraft.world.item.component.Consumable(
++ this.consumeSeconds,
++ this.consumeAnimation,
++ this.eatSound,
++ this.hasConsumeParticles,
++ this.effects
++ )
++ );
++ }
++ }
++}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperCustomModelData.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperCustomModelData.java
new file mode 100644
index 0000000000000000000000000000000000000000..a68ae7a3c31094a579a8c307d275847c311e3f86
@@ -866,6 +1110,113 @@ index 0000000000000000000000000000000000000000..a68ae7a3c31094a579a8c307d275847c
+ return this.impl.value();
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperDamageResistant.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperDamageResistant.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..461461d3ad0f2c7f3d8d0267804fbe23dbefce64
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperDamageResistant.java
+@@ -0,0 +1,27 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.registry.PaperRegistries;
++import io.papermc.paper.registry.RegistryKey;
++import io.papermc.paper.registry.set.PaperRegistrySets;
++import io.papermc.paper.registry.set.RegistryKeySet;
++import io.papermc.paper.registry.tag.TagKey;
++import org.bukkit.craftbukkit.util.Handleable;
++import org.bukkit.damage.DamageType;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public record PaperDamageResistant(
++ net.minecraft.world.item.component.DamageResistant impl
++) implements DamageResistant, Handleable<net.minecraft.world.item.component.DamageResistant> {
++
++ @Override
++ public net.minecraft.world.item.component.DamageResistant getHandle() {
++ return this.impl;
++ }
++
++ @Override
++ public @NonNull TagKey<DamageType> types() {
++ return PaperRegistries.fromNms(this.impl.types());
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperDeathProtection.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperDeathProtection.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9e9ec0257c30f323ab9b10135b7b76be95a836d3
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperDeathProtection.java
+@@ -0,0 +1,68 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.adventure.PaperAdventure;
++import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect;
++import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation;
++import io.papermc.paper.datacomponent.item.consumable.PaperConsumableEffects;
++import java.util.ArrayList;
++import java.util.Collection;
++import java.util.List;
++import net.kyori.adventure.key.Key;
++import net.minecraft.core.Holder;
++import net.minecraft.core.registries.BuiltInRegistries;
++import net.minecraft.resources.ResourceLocation;
++import net.minecraft.sounds.SoundEvent;
++import net.minecraft.sounds.SoundEvents;
++import org.bukkit.craftbukkit.util.Handleable;
++import org.checkerframework.checker.index.qual.NonNegative;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Unmodifiable;
++
++import static io.papermc.paper.datacomponent.ComponentUtils.transform;
++
++@DefaultQualifier(NonNull.class)
++public record PaperDeathProtection(
++ net.minecraft.world.item.component.DeathProtection impl,
++ List<ConsumeEffect> deathEffects
++) implements DeathProtection, Handleable<net.minecraft.world.item.component.DeathProtection> {
++
++ public PaperDeathProtection(final net.minecraft.world.item.component.DeathProtection impl) {
++ this(
++ impl,
++ transform(impl.deathEffects(), PaperConsumableEffects::fromNms)
++ );
++ }
++
++ @Override
++ public net.minecraft.world.item.component.DeathProtection getHandle() {
++ return this.impl;
++ }
++
++ static final class BuilderImpl implements Builder {
++
++ private final List<net.minecraft.world.item.consume_effects.ConsumeEffect> effects = new ArrayList<>();
++
++ @Override
++ public @NonNull Builder addEffect(@NonNull final ConsumeEffect effect) {
++ this.effects.add(PaperConsumableEffects.toNms(effect));
++ return this;
++ }
++
++ @Override
++ public @NonNull Builder addEffects(@NonNull final Collection<@NonNull ConsumeEffect> effects) {
++ for (ConsumeEffect effect : effects) {
++ this.effects.add(PaperConsumableEffects.toNms(effect));
++ }
++ return this;
++ }
++
++ @Override
++ public @NonNull DeathProtection build() {
++ return new PaperDeathProtection(
++ new net.minecraft.world.item.component.DeathProtection(this.effects)
++ );
++ }
++ }
++}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperDyedItemColor.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperDyedItemColor.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff2a81366fcd554451e9b2aa438e9277fa70248b
@@ -927,6 +1278,205 @@ index 0000000000000000000000000000000000000000..ff2a81366fcd554451e9b2aa438e9277
+ }
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperEnchantable.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperEnchantable.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a9de03513e371c049375a7b87d9905371061a95f
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperEnchantable.java
+@@ -0,0 +1,22 @@
++package io.papermc.paper.datacomponent.item;
++
++import org.bukkit.craftbukkit.util.Handleable;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public record PaperEnchantable(
++ net.minecraft.world.item.enchantment.Enchantable impl
++) implements Enchantable, Handleable<net.minecraft.world.item.enchantment.Enchantable> {
++
++ @Override
++ public net.minecraft.world.item.enchantment.Enchantable getHandle() {
++ return this.impl;
++ }
++
++
++ @Override
++ public int value() {
++ return this.impl.value();
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..dfa2e7a6bd5152b225fa563347c093bc95b9c4ea
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java
+@@ -0,0 +1,165 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.adventure.PaperAdventure;
++import io.papermc.paper.datacomponent.ComponentUtils;
++import io.papermc.paper.registry.RegistryKey;
++import io.papermc.paper.registry.set.PaperRegistrySets;
++import io.papermc.paper.registry.set.RegistryKeySet;
++import net.kyori.adventure.key.Key;
++import net.minecraft.core.Holder;
++import net.minecraft.core.HolderSet;
++import net.minecraft.core.registries.BuiltInRegistries;
++import net.minecraft.core.registries.Registries;
++import net.minecraft.resources.ResourceLocation;
++import net.minecraft.sounds.SoundEvent;
++import net.minecraft.sounds.SoundEvents;
++import org.bukkit.craftbukkit.CraftEquipmentSlot;
++import org.bukkit.craftbukkit.util.Handleable;
++import org.bukkit.entity.EntityType;
++import org.bukkit.inventory.EquipmentSlot;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.checkerframework.framework.qual.DefaultQualifier;
++import java.util.Optional;
++
++@DefaultQualifier(NonNull.class)
++public record PaperEquippable(
++ net.minecraft.world.item.equipment.Equippable impl
++) implements Equippable, Handleable<net.minecraft.world.item.equipment.Equippable> {
++
++ @Override
++ public net.minecraft.world.item.equipment.Equippable getHandle() {
++ return this.impl;
++ }
++
++ @Override
++ public @NonNull EquipmentSlot slot() {
++ return CraftEquipmentSlot.getSlot(this.impl.slot());
++ }
++
++ @Override
++ public @NonNull Key equipSound() {
++ return PaperAdventure.asAdventure(this.impl.equipSound().value().location());
++ }
++
++ @Override
++ public @Nullable Key model() {
++ return this.impl.model()
++ .map(PaperAdventure::asAdventure)
++ .orElse(null);
++ }
++
++ @Override
++ public @Nullable Key cameraOverlay() {
++ return this.impl.cameraOverlay()
++ .map(PaperAdventure::asAdventure)
++ .orElse(null);
++ }
++
++ @Override
++ public @Nullable RegistryKeySet<EntityType> allowedEntities() {
++ return this.impl.allowedEntities()
++ .map((set) -> PaperRegistrySets.convertToApi(RegistryKey.ENTITY_TYPE, set))
++ .orElse(null);
++ }
++
++ @Override
++ public boolean dispensable() {
++ return this.impl.dispensable();
++ }
++
++ @Override
++ public boolean swappable() {
++ return this.impl.swappable();
++ }
++
++ @Override
++ public boolean damageOnHurt() {
++ return this.impl.damageOnHurt();
++ }
++
++ @Override
++ public Builder toBuilder() {
++ return new BuilderImpl(this.slot())
++ .equipSound(this.equipSound())
++ .model(this.model())
++ .cameraOverlay(this.cameraOverlay())
++ .allowedEntities(this.allowedEntities())
++ .dispensable(this.dispensable())
++ .swappable(this.swappable())
++ .damageOnHurt(this.damageOnHurt());
++ }
++
++
++ static final class BuilderImpl implements Builder {
++
++ private final net.minecraft.world.entity.EquipmentSlot equipmentSlot;
++ private Holder<SoundEvent> equipSound = SoundEvents.ARMOR_EQUIP_GENERIC;
++ private Optional<ResourceLocation> model = Optional.empty();
++ private Optional<ResourceLocation> cameraOverlay = Optional.empty();
++ private Optional<HolderSet<net.minecraft.world.entity.EntityType<?>>> allowedEntities = Optional.empty();
++ private boolean dispensable = true;
++ private boolean swappable = true;
++ private boolean damageOnHurt = true;
++
++ BuilderImpl(final EquipmentSlot equipmentSlot) {
++ this.equipmentSlot = CraftEquipmentSlot.getNMS(equipmentSlot);
++ }
++
++ @Override
++ public @NonNull Builder equipSound(final @NonNull Key equipSound) {
++ this.equipSound = ComponentUtils.keyToSound(equipSound);
++ return this;
++ }
++
++ @Override
++ public @NonNull Builder model(final @Nullable Key model) {
++ this.model = Optional.ofNullable(model)
++ .map(PaperAdventure::asVanilla);
++
++ return this;
++ }
++
++ @Override
++ public @NonNull Builder cameraOverlay(@Nullable final Key cameraOverlay) {
++ this.cameraOverlay = Optional.ofNullable(cameraOverlay)
++ .map(PaperAdventure::asVanilla);
++
++ return this;
++ }
++
++ @Override
++ public @NonNull Builder allowedEntities(final @Nullable RegistryKeySet<EntityType> allowedEntities) {
++ this.allowedEntities = Optional.ofNullable(allowedEntities)
++ .map((set) -> PaperRegistrySets.convertToNms(Registries.ENTITY_TYPE, BuiltInRegistries.BUILT_IN_CONVERSIONS.lookup(), set));
++ return this;
++ }
++
++ @Override
++ public @NonNull Builder dispensable(final boolean dispensable) {
++ this.dispensable = dispensable;
++ return this;
++ }
++
++ @Override
++ public @NonNull Builder swappable(final boolean swappable) {
++ this.swappable = swappable;
++ return this;
++ }
++
++ @Override
++ public @NonNull Builder damageOnHurt(final boolean damageOnHurt) {
++ this.damageOnHurt = damageOnHurt;
++ return this;
++ }
++
++ @Override
++ public @NonNull Equippable build() {
++ return new PaperEquippable(
++ new net.minecraft.world.item.equipment.Equippable(
++ this.equipmentSlot, this.equipSound, this.model, this.cameraOverlay, this.allowedEntities, this.dispensable, this.swappable, this.damageOnHurt
++ )
++ );
++ }
++ }
++}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperFireworks.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperFireworks.java
new file mode 100644
index 0000000000000000000000000000000000000000..d61720f6316b2f7dee05fdb60640dbc600db3210
@@ -1016,10 +1566,10 @@ index 0000000000000000000000000000000000000000..d61720f6316b2f7dee05fdb60640dbc6
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperFoodProperties.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperFoodProperties.java
new file mode 100644
-index 0000000000000000000000000000000000000000..3a1e6dbc09794d9bc4aad53fb05ebd7810223ec9
+index 0000000000000000000000000000000000000000..d6add9e4917f887cda2197895e2b9f045dce8bb3
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperFoodProperties.java
-@@ -0,0 +1,166 @@
+@@ -0,0 +1,89 @@
+package io.papermc.paper.datacomponent.item;
+
+import com.google.common.base.Preconditions;
@@ -1042,16 +1592,9 @@ index 0000000000000000000000000000000000000000..3a1e6dbc09794d9bc4aad53fb05ebd78
+
+@DefaultQualifier(NonNull.class)
+public record PaperFoodProperties(
-+ net.minecraft.world.food.FoodProperties impl,
-+ List<PossibleEffect> effects
++ net.minecraft.world.food.FoodProperties impl
+) implements FoodProperties, Handleable<net.minecraft.world.food.FoodProperties> {
+
-+ public PaperFoodProperties(final net.minecraft.world.food.FoodProperties impl) {
-+ this(
-+ impl,
-+ transform(impl.effects(), PossibleEffectImpl::new)
-+ );
-+ }
+
+ @Override
+ public int nutrition() {
@@ -1069,24 +1612,11 @@ index 0000000000000000000000000000000000000000..3a1e6dbc09794d9bc4aad53fb05ebd78
+ }
+
+ @Override
-+ public float eatSeconds() {
-+ return this.impl.eatSeconds();
-+ }
-+
-+ @Override
-+ public @Nullable ItemStack usingConvertsTo() {
-+ return this.impl.usingConvertsTo().map(item -> CraftItemStack.asCraftMirror(item.copy())).orElse(null);
-+ }
-+
-+ @Override
+ public FoodProperties.Builder toBuilder() {
+ return new BuilderImpl()
-+ .addEffects(this.effects())
+ .nutrition(this.nutrition())
+ .saturation(this.saturation())
-+ .canAlwaysEat(this.canAlwaysEat())
-+ .eatSeconds(this.eatSeconds())
-+ .usingConvertsTo(this.usingConvertsTo());
++ .canAlwaysEat(this.canAlwaysEat());
+ }
+
+ @Override
@@ -1094,53 +1624,18 @@ index 0000000000000000000000000000000000000000..3a1e6dbc09794d9bc4aad53fb05ebd78
+ return this.impl;
+ }
+
-+ record PossibleEffectImpl(
-+ net.minecraft.world.food.FoodProperties.PossibleEffect possibleEffect
-+ ) implements PossibleEffect, Handleable<net.minecraft.world.food.FoodProperties.PossibleEffect> {
-+
-+ public static PossibleEffect toApi(final PotionEffect effect, final float probability) {
-+ Preconditions.checkArgument(probability >= 0.0F && probability <= 1.0F, "probability must be [0.0f, 1.0f], was %s", probability);
-+ return new PossibleEffectImpl(new net.minecraft.world.food.FoodProperties.PossibleEffect(CraftPotionUtil.fromBukkit(effect), probability));
-+ }
-+
-+ @Override
-+ public PotionEffect effect() {
-+ return CraftPotionUtil.toBukkit(this.possibleEffect.effect());
-+ }
-+
-+ @Override
-+ public float probability() {
-+ return this.possibleEffect.probability();
-+ }
-+
-+ @Override
-+ public net.minecraft.world.food.FoodProperties.PossibleEffect getHandle() {
-+ return this.possibleEffect;
-+ }
-+ }
+
+ static final class BuilderImpl implements FoodProperties.Builder {
+
-+ private final List<net.minecraft.world.food.FoodProperties.PossibleEffect> possibleEffects = new ArrayList<>();
+ private boolean canAlwaysEat = false;
-+ private float eatSeconds = net.minecraft.world.food.FoodProperties.DEFAULT_EAT_SECONDS;
+ private float saturation = 0;
+ private int nutrition = 0;
-+ private @Nullable ItemStack convertedStack;
+
+ @Override
+ public FoodProperties.Builder canAlwaysEat(final boolean canAlwaysEat) {
+ this.canAlwaysEat = canAlwaysEat;
+ return this;
+ }
-+
-+ @Override
-+ public FoodProperties.Builder eatSeconds(final float eatSeconds) {
-+ Preconditions.checkArgument(eatSeconds > 0, "eatSeconds must be positive, was %s", eatSeconds);
-+ this.eatSeconds = eatSeconds;
-+ return this;
-+ }
-+
+ @Override
+ public FoodProperties.Builder saturation(final float saturation) {
+ this.saturation = saturation;
@@ -1155,43 +1650,21 @@ index 0000000000000000000000000000000000000000..3a1e6dbc09794d9bc4aad53fb05ebd78
+ }
+
+ @Override
-+ public FoodProperties.Builder usingConvertsTo(final @Nullable ItemStack stack) {
-+ Preconditions.checkArgument(stack == null || !stack.isEmpty(), "stack cannot be empty");
-+ this.convertedStack = stack;
-+ return this;
-+ }
-+
-+ @Override
-+ public FoodProperties.Builder addEffect(final PossibleEffect effect) {
-+ this.possibleEffects.add(((PossibleEffectImpl) effect).possibleEffect());
-+ return this;
-+ }
-+
-+ @Override
-+ public FoodProperties.Builder addEffects(final Collection<PossibleEffect> effects) {
-+ addAndConvert(this.possibleEffects, effects, effect -> ((PossibleEffectImpl) effect).possibleEffect());
-+ return this;
-+ }
-+
-+ @Override
+ public FoodProperties build() {
+ return new PaperFoodProperties(new net.minecraft.world.food.FoodProperties(
+ this.nutrition,
+ this.saturation,
-+ this.canAlwaysEat,
-+ this.eatSeconds,
-+ Optional.ofNullable(this.convertedStack).map(CraftItemStack::asNMSCopy),
-+ Collections.unmodifiableList(this.possibleEffects)
++ this.canAlwaysEat
+ ));
+ }
+ }
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java
new file mode 100644
-index 0000000000000000000000000000000000000000..2984b9026b3c4b5a254fe9db0e829bfb0b69d13c
+index 0000000000000000000000000000000000000000..d6e97b964070e08cb59c81a760293301a6f00030
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java
-@@ -0,0 +1,77 @@
+@@ -0,0 +1,85 @@
+package io.papermc.paper.datacomponent.item;
+
+import java.util.ArrayList;
@@ -1258,6 +1731,14 @@ index 0000000000000000000000000000000000000000..2984b9026b3c4b5a254fe9db0e829bfb
+ }
+
+ @Override
++ public @NonNull Builder addPredicates(@NonNull final List<@NonNull BlockPredicate> predicates) {
++ for (BlockPredicate predicate : predicates) {
++ this.addPredicate(predicate);
++ }
++ return this;
++ }
++
++ @Override
+ public ItemAdventurePredicate.Builder showInTooltip(final boolean showInTooltip) {
+ this.showInTooltip = showInTooltip;
+ return this;
@@ -1271,7 +1752,7 @@ index 0000000000000000000000000000000000000000..2984b9026b3c4b5a254fe9db0e829bfb
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperItemArmorTrim.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemArmorTrim.java
new file mode 100644
-index 0000000000000000000000000000000000000000..765c79f85f9a2c8009b0525a940f36bd05c0c929
+index 0000000000000000000000000000000000000000..cfcce96da5dc8fe25fd646a51cd00df6a3ed089a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemArmorTrim.java
@@ -0,0 +1,65 @@
@@ -1286,17 +1767,17 @@ index 0000000000000000000000000000000000000000..765c79f85f9a2c8009b0525a940f36bd
+
+@DefaultQualifier(NonNull.class)
+public record PaperItemArmorTrim(
-+ net.minecraft.world.item.armortrim.ArmorTrim impl
-+) implements ItemArmorTrim, Handleable<net.minecraft.world.item.armortrim.ArmorTrim> {
++ net.minecraft.world.item.equipment.trim.ArmorTrim impl
++) implements ItemArmorTrim, Handleable<net.minecraft.world.item.equipment.trim.ArmorTrim> {
+
+ @Override
-+ public net.minecraft.world.item.armortrim.ArmorTrim getHandle() {
++ public net.minecraft.world.item.equipment.trim.ArmorTrim getHandle() {
+ return this.impl;
+ }
+
+ @Override
+ public boolean showInTooltip() {
-+ return this.impl.showInTooltip;
++ return this.impl.showInTooltip();
+ }
+
+ @Override
@@ -1332,7 +1813,7 @@ index 0000000000000000000000000000000000000000..765c79f85f9a2c8009b0525a940f36bd
+
+ @Override
+ public ItemArmorTrim build() {
-+ return new PaperItemArmorTrim(new net.minecraft.world.item.armortrim.ArmorTrim(
++ return new PaperItemArmorTrim(new net.minecraft.world.item.equipment.trim.ArmorTrim(
+ CraftTrimMaterial.bukkitToMinecraftHolder(this.armorTrim.getMaterial()),
+ CraftTrimPattern.bukkitToMinecraftHolder(this.armorTrim.getPattern()),
+ this.showInTooltip
@@ -1879,10 +2360,10 @@ index 0000000000000000000000000000000000000000..1afafbc43cbf1a0ce07b43ceeefdeaf9
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperLockCode.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperLockCode.java
new file mode 100644
-index 0000000000000000000000000000000000000000..485bf8e0c7a24cc5714815f95e9818143ae226db
+index 0000000000000000000000000000000000000000..42bd5d5bd739f3dcfd8f2945c53ca3cc34cd11c9
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperLockCode.java
-@@ -0,0 +1,22 @@
+@@ -0,0 +1,17 @@
+package io.papermc.paper.datacomponent.item;
+
+import org.bukkit.craftbukkit.util.Handleable;
@@ -1899,11 +2380,6 @@ index 0000000000000000000000000000000000000000..485bf8e0c7a24cc5714815f95e981814
+ return this.impl;
+ }
+
-+ @Override
-+ public String key() {
-+ return this.impl.key();
-+ }
-+
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperLodestoneTracker.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperLodestoneTracker.java
new file mode 100644
@@ -2145,6 +2621,34 @@ index 0000000000000000000000000000000000000000..20a9652f9a1ab18df8e1581fea1ca363
+ }
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperOminousBottleAmplifier.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperOminousBottleAmplifier.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..94c43ecb85c7193baa8c78bc90fa119c5ce877c0
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperOminousBottleAmplifier.java
+@@ -0,0 +1,22 @@
++package io.papermc.paper.datacomponent.item;
++
++import net.minecraft.world.item.component.OminousBottleAmplifier;
++import org.bukkit.craftbukkit.util.Handleable;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public record PaperOminousBottleAmplifier(
++ net.minecraft.world.item.component.OminousBottleAmplifier impl
++) implements OminiousBottleAmplifier, Handleable<net.minecraft.world.item.component.OminousBottleAmplifier> {
++
++ @Override
++ public net.minecraft.world.item.component.OminousBottleAmplifier getHandle() {
++ return this.impl;
++ }
++
++ @Override
++ public int amplifier() {
++ return this.impl.value();
++ }
++}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperPotDecorations.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperPotDecorations.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb19491e0f43e075d76415cad2b8a441f292f2d3
@@ -2245,10 +2749,10 @@ index 0000000000000000000000000000000000000000..cb19491e0f43e075d76415cad2b8a441
+}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperPotionContents.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperPotionContents.java
new file mode 100644
-index 0000000000000000000000000000000000000000..81ecae8a916e07630a3fab76345542f81e62e099
+index 0000000000000000000000000000000000000000..53a57ff74c501a313f1f973eb7c60e8fc09d309c
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperPotionContents.java
-@@ -0,0 +1,95 @@
+@@ -0,0 +1,109 @@
+package io.papermc.paper.datacomponent.item;
+
+import java.util.ArrayList;
@@ -2300,11 +2804,18 @@ index 0000000000000000000000000000000000000000..81ecae8a916e07630a3fab76345542f8
+ .orElse(null);
+ }
+
++ @Override
++ public @Nullable String customName() {
++ return this.impl.customName()
++ .orElse(null);
++ }
++
+ static final class BuilderImpl implements PotionContents.Builder {
+
+ private final List<MobEffectInstance> customEffects = new ArrayList<>();
+ private @Nullable PotionType type;
+ private @Nullable Color color;
++ private @Nullable String customName;
+
+ @Override
+ public PotionContents.Builder potion(final @Nullable PotionType type) {
@@ -2319,6 +2830,12 @@ index 0000000000000000000000000000000000000000..81ecae8a916e07630a3fab76345542f8
+ }
+
+ @Override
++ public @NonNull Builder customName(@Nullable final String name) {
++ this.customName = customName;
++ return this;
++ }
++
++ @Override
+ public PotionContents.Builder addCustomEffect(final PotionEffect effect) {
+ this.customEffects.add(CraftPotionUtil.fromBukkit(effect));
+ return this;
@@ -2339,11 +2856,46 @@ index 0000000000000000000000000000000000000000..81ecae8a916e07630a3fab76345542f8
+ return new PaperPotionContents(new net.minecraft.world.item.alchemy.PotionContents(
+ Optional.ofNullable(this.type).map(CraftPotionType::bukkitToMinecraftHolder),
+ Optional.ofNullable(this.color).map(Color::asARGB),
-+ Collections.unmodifiableList(this.customEffects)
++ Collections.unmodifiableList(this.customEffects),
++ Optional.ofNullable(this.customName)
+ ));
+ }
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperRepairable.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperRepairable.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..90f524f5c203d04cfa2593c439c7d9a8e987d355
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperRepairable.java
+@@ -0,0 +1,28 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.registry.PaperRegistries;
++import io.papermc.paper.registry.RegistryKey;
++import io.papermc.paper.registry.set.PaperRegistrySets;
++import io.papermc.paper.registry.set.RegistryKeySet;
++import io.papermc.paper.registry.tag.TagKey;
++import org.bukkit.craftbukkit.util.Handleable;
++import org.bukkit.damage.DamageType;
++import org.bukkit.inventory.ItemType;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public record PaperRepairable(
++ net.minecraft.world.item.enchantment.Repairable impl
++) implements Repairable, Handleable<net.minecraft.world.item.enchantment.Repairable> {
++
++ @Override
++ public net.minecraft.world.item.enchantment.Repairable getHandle() {
++ return this.impl;
++ }
++
++ @Override
++ public @NonNull RegistryKeySet<ItemType> types() {
++ return PaperRegistrySets.convertToApi(RegistryKey.ITEM, this.impl.items());
++ }
++}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperResolvableProfile.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperResolvableProfile.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5e2f645d05c73f2a6a7902c8c3aaa92816bcca3
@@ -2644,6 +3196,102 @@ index 0000000000000000000000000000000000000000..2ff5004427766b0034595ddad04aac6b
+ }
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperUseCooldown.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperUseCooldown.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..587755c3ab532662b1cb9a6f662832e77dd7d0e2
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperUseCooldown.java
+@@ -0,0 +1,60 @@
++package io.papermc.paper.datacomponent.item;
++
++import com.google.common.base.Preconditions;
++import io.papermc.paper.adventure.PaperAdventure;
++import net.kyori.adventure.key.Key;
++import net.minecraft.resources.ResourceLocation;
++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 java.util.Optional;
++
++@DefaultQualifier(NonNull.class)
++public record PaperUseCooldown(
++ net.minecraft.world.item.component.UseCooldown impl
++) implements UseCooldown, Handleable<net.minecraft.world.item.component.UseCooldown> {
++
++ @Override
++ public net.minecraft.world.item.component.UseCooldown getHandle() {
++ return this.impl;
++ }
++
++ @Override
++ public float seconds() {
++ return this.impl.seconds();
++ }
++
++ @Override
++ public @Nullable Key cooldownGroup() {
++ return this.impl.cooldownGroup()
++ .map(PaperAdventure::asAdventure)
++ .orElse(null);
++ }
++
++
++ static final class BuilderImpl implements Builder {
++
++ private final float seconds;
++ private Optional<ResourceLocation> cooldownGroup = Optional.empty();
++
++ BuilderImpl(final float seconds) {
++ this.seconds = seconds;
++ }
++
++ @Override
++ public @NonNull Builder cooldownGroup(@Nullable final Key key) {
++ this.cooldownGroup = Optional.ofNullable(key)
++ .map(PaperAdventure::asVanilla);
++
++ return this;
++ }
++
++ @Override
++ public @NonNull UseCooldown build() {
++ return new PaperUseCooldown(
++ new net.minecraft.world.item.component.UseCooldown(this.seconds, this.cooldownGroup)
++ );
++ }
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperUseRemainder.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperUseRemainder.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..ae908c3a2edc4ed79686a2b26e775c8850ae7b86
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperUseRemainder.java
+@@ -0,0 +1,24 @@
++package io.papermc.paper.datacomponent.item;
++
++import org.bukkit.craftbukkit.inventory.CraftItemStack;
++import org.bukkit.craftbukkit.util.Handleable;
++import org.bukkit.inventory.ItemStack;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public record PaperUseRemainder(
++ net.minecraft.world.item.component.UseRemainder impl
++) implements UseRemainder, Handleable<net.minecraft.world.item.component.UseRemainder> {
++
++ @Override
++ public net.minecraft.world.item.component.UseRemainder getHandle() {
++ return this.impl;
++ }
++
++
++ @Override
++ public @NonNull ItemStack transformInto() {
++ return CraftItemStack.asBukkitCopy(this.impl.convertInto());
++ }
++}
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperWritableBookContent.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperWritableBookContent.java
new file mode 100644
index 0000000000000000000000000000000000000000..964e819110825321e06da532c9d94f8fec4eb4b0
@@ -2959,32 +3607,304 @@ index 0000000000000000000000000000000000000000..fdfc10c9b5993d098f0ad8f4772ac7e5
+ }
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridgeImpl.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridgeImpl.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..207bcdf0079163037c03e4cb5f419237d9b70e21
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridgeImpl.java
+@@ -0,0 +1,68 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import java.util.ArrayList;
++import java.util.List;
++import java.util.Optional;
++import java.util.ServiceLoader;
++import com.google.common.collect.Lists;
++import io.papermc.paper.adventure.PaperAdventure;
++import io.papermc.paper.datacomponent.ComponentUtils;
++import io.papermc.paper.registry.set.PaperRegistrySets;
++import io.papermc.paper.registry.set.RegistryKeySet;
++import net.kyori.adventure.key.Key;
++import net.minecraft.core.registries.BuiltInRegistries;
++import net.minecraft.core.registries.Registries;
++import net.minecraft.resources.ResourceLocation;
++import net.minecraft.sounds.SoundEvent;
++import org.bukkit.craftbukkit.potion.CraftPotionUtil;
++import org.bukkit.potion.PotionEffect;
++import org.bukkit.potion.PotionEffectType;
++import org.jetbrains.annotations.ApiStatus;
++
++public class ConsumableTypesBridgeImpl implements ConsumableTypesBridge {
++
++ @Override
++ public ApplyStatusEffectsConsumeEffect applyStatusEffects(final List<PotionEffect> effectList, final float probability) {
++ return new PaperApplyStatusEffectsConsumeEffect(
++ new net.minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffect(
++ new ArrayList<>(Lists.transform(effectList, CraftPotionUtil::fromBukkit)),
++ probability
++ )
++ );
++ }
++
++ @Override
++ public RemoveStatusEffectsConsumeEffect removeStatusEffects(final RegistryKeySet<PotionEffectType> potionEffectTypeTagKey) {
++ return new PaperRemoveStatusEffectsConsumeEffect(
++ new net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect(
++ PaperRegistrySets.convertToNms(Registries.MOB_EFFECT, BuiltInRegistries.BUILT_IN_CONVERSIONS.lookup(), potionEffectTypeTagKey)
++ )
++ );
++ }
++
++ @Override
++ public ClearAllStatusEffectsConsumeEffect clearAllStatusEffects() {
++ return new PaperClearAllStatusEffectsConsumeEffect(
++ new net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect()
++ );
++ }
++
++ @Override
++ public PlaySoundConsumeEffect playSoundEffect(final Key sound) {
++ return new PaperPlaySoundConsumeEffect(
++ new net.minecraft.world.item.consume_effects.PlaySoundConsumeEffect(
++ ComponentUtils.keyToSound(sound)
++ )
++ );
++ }
++
++ @Override
++ public TeleportRandomlyConsumeEffect teleportRandomlyEffect(final float diameter) {
++ return new PaperTeleportRandomlyConsumeEffect(
++ new net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect(diameter)
++ );
++ }
++
++
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperApplyStatusEffectsConsumeEffect.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperApplyStatusEffectsConsumeEffect.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..61888e17d7e66012cdb196cb26162e39b0f25588
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperApplyStatusEffectsConsumeEffect.java
+@@ -0,0 +1,30 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import java.util.List;
++import net.minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffect;
++import org.bukkit.craftbukkit.potion.CraftPotionUtil;
++import org.bukkit.craftbukkit.util.Handleable;
++import org.bukkit.potion.PotionEffect;
++import org.checkerframework.checker.nullness.qual.NonNull;
++
++import static io.papermc.paper.datacomponent.ComponentUtils.transform;
++
++public record PaperApplyStatusEffectsConsumeEffect(
++ ApplyStatusEffectsConsumeEffect impl
++) implements io.papermc.paper.datacomponent.item.consumable.ApplyStatusEffectsConsumeEffect, PaperConsumableEffectImpl<ApplyStatusEffectsConsumeEffect> {
++
++ @Override
++ public @NonNull List<PotionEffect> effects() {
++ return transform(this.impl().effects(), CraftPotionUtil::toBukkit);
++ }
++
++ @Override
++ public float probability() {
++ return this.impl.probability();
++ }
++
++ @Override
++ public ApplyStatusEffectsConsumeEffect getHandle() {
++ return this.impl;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperClearAllStatusEffectsConsumeEffect.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperClearAllStatusEffectsConsumeEffect.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..adcaa40e0b3d61ba2b7a563704fce0a5b180038e
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperClearAllStatusEffectsConsumeEffect.java
+@@ -0,0 +1,15 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import org.bukkit.craftbukkit.util.Handleable;
++
++import static io.papermc.paper.datacomponent.ComponentUtils.transform;
++
++public record PaperClearAllStatusEffectsConsumeEffect(
++ net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect impl
++) implements io.papermc.paper.datacomponent.item.consumable.ClearAllStatusEffectsConsumeEffect, PaperConsumableEffectImpl<net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect> {
++
++ @Override
++ public net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect getHandle() {
++ return this.impl;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffectImpl.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffectImpl.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..05ede1d3f5b0b5ea3a5004cb4a7a153ed7714a55
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffectImpl.java
+@@ -0,0 +1,7 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import net.minecraft.world.item.consume_effects.ConsumeEffect;
++import org.bukkit.craftbukkit.util.Handleable;
++
++public interface PaperConsumableEffectImpl<T extends ConsumeEffect> extends Handleable<T> {
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffects.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffects.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..006fc44b489e25658355da7d9303174c2a8fff19
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffects.java
+@@ -0,0 +1,32 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import io.papermc.paper.datacomponent.item.Consumable;
++import net.minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffect;
++import net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect;
++import net.minecraft.world.item.consume_effects.PlaySoundConsumeEffect;
++import net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect;
++import net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect;
++import org.bukkit.craftbukkit.util.Handleable;
++
++public class PaperConsumableEffects {
++
++
++ public static ConsumeEffect fromNms(net.minecraft.world.item.consume_effects.ConsumeEffect consumable) {
++ return switch (consumable) {
++ case ApplyStatusEffectsConsumeEffect effect -> new PaperApplyStatusEffectsConsumeEffect(effect);
++ case ClearAllStatusEffectsConsumeEffect effect -> new PaperClearAllStatusEffectsConsumeEffect(effect);
++ case PlaySoundConsumeEffect effect -> new PaperPlaySoundConsumeEffect(effect);
++ case RemoveStatusEffectsConsumeEffect effect -> new PaperRemoveStatusEffectsConsumeEffect(effect);
++ case TeleportRandomlyConsumeEffect effect -> new PaperTeleportRandomlyConsumeEffect(effect);
++ default -> throw new UnsupportedOperationException("Dont know how to convert " + consumable.getClass());
++ };
++ }
++
++ public static net.minecraft.world.item.consume_effects.ConsumeEffect toNms(ConsumeEffect effect) {
++ if (effect instanceof PaperConsumableEffectImpl<?> consumableEffect) {
++ return consumableEffect.getHandle();
++ } else {
++ throw new UnsupportedOperationException("Must implement handleable!");
++ }
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperPlaySoundConsumeEffect.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperPlaySoundConsumeEffect.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..6667a5dfd3de7f27dc6c35f95f3c3211f77a78fe
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperPlaySoundConsumeEffect.java
+@@ -0,0 +1,25 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import io.papermc.paper.adventure.PaperAdventure;
++import net.kyori.adventure.key.Key;
++import net.minecraft.world.item.consume_effects.PlaySoundConsumeEffect;
++import org.bukkit.craftbukkit.util.Handleable;
++import org.checkerframework.checker.nullness.qual.NonNull;
++
++import static io.papermc.paper.datacomponent.ComponentUtils.transform;
++
++public record PaperPlaySoundConsumeEffect(
++ PlaySoundConsumeEffect impl
++) implements io.papermc.paper.datacomponent.item.consumable.PlaySoundConsumeEffect, PaperConsumableEffectImpl<PlaySoundConsumeEffect> {
++
++ @NonNull
++ @Override
++ public Key sound() {
++ return PaperAdventure.asAdventure(this.impl.sound().value().location());
++ }
++
++ @Override
++ public PlaySoundConsumeEffect getHandle() {
++ return this.impl;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperRemoveStatusEffectsConsumeEffect.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperRemoveStatusEffectsConsumeEffect.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..3d52af596a2ded9d5e9bc7e8acaaddd511490dcf
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperRemoveStatusEffectsConsumeEffect.java
+@@ -0,0 +1,30 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import java.util.List;
++import io.papermc.paper.registry.PaperRegistries;
++import io.papermc.paper.registry.RegistryKey;
++import io.papermc.paper.registry.set.PaperRegistrySets;
++import io.papermc.paper.registry.set.RegistryKeySet;
++import net.minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffect;
++import org.bukkit.craftbukkit.potion.CraftPotionUtil;
++import org.bukkit.craftbukkit.util.Handleable;
++import org.bukkit.potion.PotionEffect;
++import org.bukkit.potion.PotionEffectType;
++import org.checkerframework.checker.nullness.qual.NonNull;
++
++import static io.papermc.paper.datacomponent.ComponentUtils.transform;
++
++public record PaperRemoveStatusEffectsConsumeEffect(
++ net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect impl
++) implements RemoveStatusEffectsConsumeEffect, PaperConsumableEffectImpl<net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect> {
++
++ @Override
++ public @NonNull RegistryKeySet<PotionEffectType> removeEffects() {
++ return PaperRegistrySets.convertToApi(RegistryKey.MOB_EFFECT, this.impl.effects());
++ }
++
++ @Override
++ public net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect getHandle() {
++ return this.impl;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperTeleportRandomlyConsumeEffect.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperTeleportRandomlyConsumeEffect.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..5985308cac8f9409eea478fb68f64e8cfd4855c9
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperTeleportRandomlyConsumeEffect.java
+@@ -0,0 +1,17 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import org.bukkit.craftbukkit.util.Handleable;
++
++public record PaperTeleportRandomlyConsumeEffect(
++ net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect impl
++) implements TeleportRandomlyConsumeEffect, PaperConsumableEffectImpl<net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect> {
++ @Override
++ public float diameter() {
++ return this.impl.diameter();
++ }
++
++ @Override
++ public net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect getHandle() {
++ return this.impl;
++ }
++}
diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
-index 6ec9d9b9acf557aa2ebf39d38a14225b0205fae1..3a10a72cf6c5f91297a2d2980929f5484b3965be 100644
+index 12220f78ffaf06433ada72fd0c7f22b97d55287d..e1c6f514f45a02d7401b5390aefd0a4946b8e4b9 100644
--- a/src/main/java/io/papermc/paper/registry/PaperRegistries.java
+++ b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
-@@ -3,6 +3,8 @@ package io.papermc.paper.registry;
+@@ -1,6 +1,8 @@
+ package io.papermc.paper.registry;
+
import io.papermc.paper.adventure.PaperAdventure;
- import io.papermc.paper.registry.data.PaperEnchantmentRegistryEntry;
- import io.papermc.paper.registry.data.PaperGameEventRegistryEntry;
+import io.papermc.paper.datacomponent.DataComponentType;
+import io.papermc.paper.datacomponent.PaperComponentType;
+ import io.papermc.paper.registry.data.PaperEnchantmentRegistryEntry;
+ import io.papermc.paper.registry.data.PaperGameEventRegistryEntry;
import io.papermc.paper.registry.entry.RegistryEntry;
- import io.papermc.paper.registry.tag.TagKey;
- import java.util.Collections;
-@@ -81,6 +83,7 @@ public final class PaperRegistries {
- entry(Registries.VILLAGER_PROFESSION, RegistryKey.VILLAGER_PROFESSION, Villager.Profession.class, CraftVillager.CraftProfession::new),
+@@ -84,6 +86,7 @@ public final class PaperRegistries {
entry(Registries.VILLAGER_TYPE, RegistryKey.VILLAGER_TYPE, Villager.Type.class, CraftVillager.CraftType::new),
entry(Registries.MAP_DECORATION_TYPE, RegistryKey.MAP_DECORATION_TYPE, MapCursor.Type.class, CraftMapCursor.CraftType::new),
+ entry(Registries.MENU, RegistryKey.MENU, MenuType.class, CraftMenuType::new),
+ entry(Registries.DATA_COMPONENT_TYPE, RegistryKey.DATA_COMPONENT_TYPE, DataComponentType.class, PaperComponentType::of),
// data-drivens
entry(Registries.STRUCTURE, RegistryKey.STRUCTURE, Structure.class, CraftStructure::new).delayed(),
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-index 40fb5b5e00f6bc82e67d318b8b3d1e7606973f52..15b2b43e718e7ea8a982e072ce9c8bc9baef119a 100644
+index 756c73a401437566258813946fa10c7caa8f2469..1222918237c3bc30a984f34cda99728499da069b 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-@@ -194,7 +194,7 @@ public final class CraftItemStack extends ItemStack {
+@@ -206,7 +206,7 @@ public final class CraftItemStack extends ItemStack {
this.adjustTagForItemMeta(oldType); // Paper
}
}
@@ -2993,7 +3913,7 @@ index 40fb5b5e00f6bc82e67d318b8b3d1e7606973f52..15b2b43e718e7ea8a982e072ce9c8bc9
}
@Override
-@@ -233,7 +233,7 @@ public final class CraftItemStack extends ItemStack {
+@@ -245,7 +245,7 @@ public final class CraftItemStack extends ItemStack {
@Override
public int getMaxStackSize() {
@@ -3002,7 +3922,7 @@ index 40fb5b5e00f6bc82e67d318b8b3d1e7606973f52..15b2b43e718e7ea8a982e072ce9c8bc9
}
// Paper start
-@@ -255,12 +255,14 @@ public final class CraftItemStack extends ItemStack {
+@@ -267,12 +267,14 @@ public final class CraftItemStack extends ItemStack {
public void addUnsafeEnchantment(Enchantment ench, int level) {
Preconditions.checkArgument(ench != null, "Enchantment cannot be null");
@@ -3022,7 +3942,7 @@ index 40fb5b5e00f6bc82e67d318b8b3d1e7606973f52..15b2b43e718e7ea8a982e072ce9c8bc9
// Paper end
}
-@@ -290,17 +292,28 @@ public final class CraftItemStack extends ItemStack {
+@@ -302,17 +304,28 @@ public final class CraftItemStack extends ItemStack {
public int removeEnchantment(Enchantment ench) {
Preconditions.checkArgument(ench != null, "Enchantment cannot be null");
@@ -3060,7 +3980,7 @@ index 40fb5b5e00f6bc82e67d318b8b3d1e7606973f52..15b2b43e718e7ea8a982e072ce9c8bc9
}
@Override
-@@ -312,7 +325,13 @@ public final class CraftItemStack extends ItemStack {
+@@ -324,7 +337,13 @@ public final class CraftItemStack extends ItemStack {
@Override
public Map<Enchantment, Integer> getEnchantments() {
@@ -3075,7 +3995,7 @@ index 40fb5b5e00f6bc82e67d318b8b3d1e7606973f52..15b2b43e718e7ea8a982e072ce9c8bc9
}
static Map<Enchantment, Integer> getEnchantments(net.minecraft.world.item.ItemStack item) {
-@@ -543,4 +562,130 @@ public final class CraftItemStack extends ItemStack {
+@@ -526,4 +545,130 @@ public final class CraftItemStack extends ItemStack {
return this.pdcView;
}
// Paper end - pdc
@@ -3207,10 +4127,10 @@ index 40fb5b5e00f6bc82e67d318b8b3d1e7606973f52..15b2b43e718e7ea8a982e072ce9c8bc9
+ // 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 450c63c31d2f5d056d989aa00452231f50c8224d..65446258661b31c4efb47d3aa7548e991adc0c56 100644
+index 1b57649d0d3db24ed32c78cf3d5ce1d9fb1353e0..ce1287edd7db00279ec8569d767ab6272c8ae3fb 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
-@@ -273,4 +273,20 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han
+@@ -270,4 +270,20 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han
return rarity == null ? null : org.bukkit.inventory.ItemRarity.valueOf(rarity.name());
}
// Paper end - expand ItemRarity API
@@ -3232,11 +4152,11 @@ index 450c63c31d2f5d056d989aa00452231f50c8224d..65446258661b31c4efb47d3aa7548e99
+ // Paper end - data component API
}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
-index 0eceacbb096481d3bd31f5f99e964c88aea2e3fb..acd80ad00caefe07232fc179cfbf4e7f56450859 100644
+index a944803771d514572f94b4e98a6d4435a009c078..82cb8cd1635c279326cec8454f1906ce35021dec 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
-@@ -78,7 +78,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- });
+@@ -91,7 +91,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
+ this.safelyAddEffects(effects, false); // Paper - limit firework effects
}
- static FireworkEffect getEffect(FireworkExplosion explosion) {
@@ -3244,7 +4164,7 @@ index 0eceacbb096481d3bd31f5f99e964c88aea2e3fb..acd80ad00caefe07232fc179cfbf4e7f
FireworkEffect.Builder effect = FireworkEffect.builder()
.flicker(explosion.hasTwinkle())
.trail(explosion.hasTrail())
-@@ -98,7 +98,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
+@@ -111,7 +111,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
return effect.build();
}
@@ -3274,31 +4194,39 @@ index 097996d3955ab5126b71f7bff1dd2c62becb5ffd..2e75620e803868ad3c254d11e6265062
public static Vec3 toVec3D(Location location) {
return new Vec3(location.getX(), location.getY(), location.getZ());
}
-diff --git a/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.ComponentTypesBridge b/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.ComponentTypesBridge
+diff --git a/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.ItemComponentTypesBridge b/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.ItemComponentTypesBridge
new file mode 100644
index 0000000000000000000000000000000000000000..0fd276c2fdbba784c1cd95105553996b4ba2460e
--- /dev/null
-+++ b/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.ComponentTypesBridge
++++ b/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.ItemComponentTypesBridge
@@ -0,0 +1 @@
+io.papermc.paper.datacomponent.item.ItemComponentTypesBridgesImpl
+diff --git a/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.consumable.ConsumableTypesBridge b/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.consumable.ConsumableTypesBridge
+new file mode 100644
+index 0000000000000000000000000000000000000000..852ab097181491735fb9ee5ee4f70e4ceeb32e6d
+--- /dev/null
++++ b/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.consumable.ConsumableTypesBridge
+@@ -0,0 +1 @@
++io.papermc.paper.datacomponent.item.consumable.ConsumableTypesBridgeImpl
diff --git a/src/test/java/io/papermc/paper/item/ItemStackDataComponentEqualsTest.java b/src/test/java/io/papermc/paper/item/ItemStackDataComponentEqualsTest.java
new file mode 100644
-index 0000000000000000000000000000000000000000..06476cdd7f8290846e86bdd3837488ca900a7ddc
+index 0000000000000000000000000000000000000000..9fc9756aff2248d7684e20f37e9202375b5ac71d
--- /dev/null
+++ b/src/test/java/io/papermc/paper/item/ItemStackDataComponentEqualsTest.java
-@@ -0,0 +1,70 @@
+@@ -0,0 +1,71 @@
+package io.papermc.paper.item;
+
+import io.papermc.paper.datacomponent.DataComponentTypes;
+import net.kyori.adventure.text.Component;
+import org.bukkit.Material;
+import org.bukkit.inventory.ItemStack;
-+import org.bukkit.support.AbstractTestingBase;
++import org.bukkit.support.environment.Normal;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import java.util.Set;
+
-+class ItemStackDataComponentEqualsTest extends AbstractTestingBase {
++@Normal
++class ItemStackDataComponentEqualsTest {
+
+ @Test
+ public void testEqual() {
@@ -3359,15 +4287,16 @@ index 0000000000000000000000000000000000000000..06476cdd7f8290846e86bdd3837488ca
+}
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..0769c462ca0defbe31c10fc7a6d3ba901b382fad
+index 0000000000000000000000000000000000000000..586f88b5bd5a7ce84d0dbd5e5bfa6a67d72089f6
--- /dev/null
+++ b/src/test/java/io/papermc/paper/item/ItemStackDataComponentTest.java
-@@ -0,0 +1,387 @@
+@@ -0,0 +1,380 @@
+package io.papermc.paper.item;
+
+import io.papermc.paper.datacomponent.DataComponentType;
+import io.papermc.paper.datacomponent.DataComponentTypes;
+import io.papermc.paper.datacomponent.item.ChargedProjectiles;
++import io.papermc.paper.datacomponent.item.Consumable;
+import io.papermc.paper.datacomponent.item.CustomModelData;
+import io.papermc.paper.datacomponent.item.DyedItemColor;
+import io.papermc.paper.datacomponent.item.Fireworks;
@@ -3382,6 +4311,9 @@ index 0000000000000000000000000000000000000000..0769c462ca0defbe31c10fc7a6d3ba90
+import io.papermc.paper.datacomponent.item.PotDecorations;
+import io.papermc.paper.datacomponent.item.Tool;
+import io.papermc.paper.datacomponent.item.Unbreakable;
++import io.papermc.paper.datacomponent.item.consumable.ApplyStatusEffectsConsumeEffect;
++import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect;
++import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation;
+import io.papermc.paper.registry.RegistryAccess;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.set.RegistrySet;
@@ -3422,7 +4354,7 @@ index 0000000000000000000000000000000000000000..0769c462ca0defbe31c10fc7a6d3ba90
+import org.bukkit.inventory.meta.trim.TrimPattern;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.potion.PotionEffectType;
-+import org.bukkit.support.AbstractTestingBase;
++import org.bukkit.support.environment.Normal;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import java.util.List;
@@ -3430,7 +4362,8 @@ index 0000000000000000000000000000000000000000..0769c462ca0defbe31c10fc7a6d3ba90
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
-+class ItemStackDataComponentTest extends AbstractTestingBase {
++@Normal
++class ItemStackDataComponentTest {
+
+ @Test
+ void testMaxStackSize() {
@@ -3550,10 +4483,8 @@ index 0000000000000000000000000000000000000000..0769c462ca0defbe31c10fc7a6d3ba90
+ void testFood() {
+ FoodProperties properties = FoodProperties.food()
+ .canAlwaysEat(true)
-+ .eatSeconds(1.3F)
++ .saturation(1.3F)
+ .nutrition(1)
-+ .addEffects(List.of(FoodProperties.PossibleEffect.possibleEffect(new PotionEffect(PotionEffectType.SLOWNESS, 5, 10), 1F)))
-+ .usingConvertsTo(new ItemStack(Material.STONE))
+ .build();
+
+ final ItemStack stack = new ItemStack(Material.CROSSBOW);
@@ -3562,22 +4493,15 @@ index 0000000000000000000000000000000000000000..0769c462ca0defbe31c10fc7a6d3ba90
+ ItemMeta meta = stack.getItemMeta();
+ FoodComponent component = meta.getFood();
+ Assertions.assertEquals(properties.canAlwaysEat(), component.canAlwaysEat());
-+ Assertions.assertEquals(properties.eatSeconds(), component.getEatSeconds());
++ Assertions.assertEquals(properties.saturation(), component.getSaturation());
+ Assertions.assertEquals(properties.nutrition(), component.getNutrition());
+
-+ int idx = 0;
-+ for (FoodComponent.FoodEffect effect : component.getEffects()) {
-+ Assertions.assertEquals(properties.effects().get(idx).effect(), effect.getEffect());
-+ Assertions.assertEquals(properties.effects().get(idx).probability(), effect.getProbability());
-+ idx++;
-+ }
-+ Assertions.assertEquals(properties.usingConvertsTo(), component.getUsingConvertsTo());
-+
+ stack.unsetData(DataComponentTypes.FOOD);
+ meta = stack.getItemMeta();
+ Assertions.assertFalse(meta.hasFood());
+ }
+
++
+ @Test
+ void testTool() {
+ Tool properties = Tool.tool()
@@ -3634,10 +4558,7 @@ index 0000000000000000000000000000000000000000..0769c462ca0defbe31c10fc7a6d3ba90
+ Assertions.assertFalse(meta.hasJukeboxPlayable());
+ }
+
-+ @Test
-+ void testFireResistant() {
-+ testWithMeta(new ItemStack(Material.STONE), DataComponentTypes.FIRE_RESISTANT, true, ItemMeta.class, ItemMeta::isFireResistant, ItemMeta::setFireResistant);
-+ }
++
+
+ @Test
+ void testDyedColor() {
@@ -3752,10 +4673,10 @@ index 0000000000000000000000000000000000000000..0769c462ca0defbe31c10fc7a6d3ba90
+}
diff --git a/src/test/java/io/papermc/paper/item/MetaComparisonTest.java b/src/test/java/io/papermc/paper/item/MetaComparisonTest.java
new file mode 100644
-index 0000000000000000000000000000000000000000..5a61ad9f3517b6cbf52e03c40b935900330da4d0
+index 0000000000000000000000000000000000000000..957b08ec2d44cb05a9eff33d4529d016fb7af2ed
--- /dev/null
+++ b/src/test/java/io/papermc/paper/item/MetaComparisonTest.java
-@@ -0,0 +1,283 @@
+@@ -0,0 +1,284 @@
+package io.papermc.paper.item;
+
+import com.destroystokyo.paper.profile.CraftPlayerProfile;
@@ -3778,7 +4699,7 @@ index 0000000000000000000000000000000000000000..5a61ad9f3517b6cbf52e03c40b935900
+import org.bukkit.inventory.meta.SkullMeta;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.potion.PotionEffectType;
-+import org.bukkit.support.AbstractTestingBase;
++import org.bukkit.support.environment.Normal;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
@@ -3787,7 +4708,8 @@ index 0000000000000000000000000000000000000000..5a61ad9f3517b6cbf52e03c40b935900
+import java.util.function.Consumer;
+
+// TODO: This should technically be used to compare legacy meta vs the newly implemented
-+public class MetaComparisonTest extends AbstractTestingBase {
++@Normal
++public class MetaComparisonTest {
+
+ private static final ItemFactory FACTORY = CraftItemFactory.instance();
+
@@ -4040,10 +4962,10 @@ index 0000000000000000000000000000000000000000..5a61ad9f3517b6cbf52e03c40b935900
+ }
+}
diff --git a/src/test/java/org/bukkit/PerMaterialTest.java b/src/test/java/org/bukkit/PerMaterialTest.java
-index 7787e399c6432120fb5a1826799cd229bbb28fe2..c06b409f3dce00bce846ae30852fe0cb33830684 100644
+index 629fccec144b5d66addc0e8258cde90e81904e1c..6961730365da9083e8963200ecc5f85dbc654f35 100644
--- a/src/test/java/org/bukkit/PerMaterialTest.java
+++ b/src/test/java/org/bukkit/PerMaterialTest.java
-@@ -100,17 +100,13 @@ public class PerMaterialTest extends AbstractTestingBase {
+@@ -101,17 +101,13 @@ public class PerMaterialTest {
final ItemStack bukkit = new ItemStack(material);
final CraftItemStack craft = CraftItemStack.asCraftCopy(bukkit);
@@ -4065,10 +4987,10 @@ index 7787e399c6432120fb5a1826799cd229bbb28fe2..c06b409f3dce00bce846ae30852fe0cb
@ParameterizedTest
diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java
-index 1a582ee78334835df79f93cc9fd3669c347d8b3a..83a11c0eeecaf6aeb65f386a777b495231be8dc4 100644
+index ba5c958f322dc34baff3c9d1b99741a4ffeee135..13614672db233c3fb96bde7c4f6c52a583e6e341 100644
--- a/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java
+++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java
-@@ -452,7 +452,7 @@ public class ItemMetaTest extends AbstractTestingBase {
+@@ -453,7 +453,7 @@ public class ItemMetaTest {
assertThat(providers, hasSize(ItemStackTest.COMPOUND_MATERIALS.length - 4/* Normal item meta, skulls, eggs and tile entities */), "Forgotten test?");
for (final StackProvider provider : providers) {
@@ -4077,7 +4999,7 @@ index 1a582ee78334835df79f93cc9fd3669c347d8b3a..83a11c0eeecaf6aeb65f386a777b4952
this.downCastTest(new CraftWrapper(provider));
}
}
-@@ -512,13 +512,6 @@ public class ItemMetaTest extends AbstractTestingBase {
+@@ -513,13 +513,6 @@ public class ItemMetaTest {
final ItemStack blank = new ItemStack(Material.STONE);
final ItemStack craftBlank = CraftItemStack.asCraftCopy(blank);
@@ -4092,13 +5014,13 @@ index 1a582ee78334835df79f93cc9fd3669c347d8b3a..83a11c0eeecaf6aeb65f386a777b4952
this.downCastTest(name, provider.stack(), craftBlank);
craftBlank.setItemMeta(craftBlank.getItemMeta());
diff --git a/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java b/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java
-index c1f886c906a9f9313d97a223f719095fa2624c57..115a12d3d33abe70388f9fa20da2c3c6fca19849 100644
+index eb3974690fb12ffe678522ed47e0f730712db016..1843b89c8616acc1fca7757f938c6b62e8b6c2a8 100644
--- a/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java
+++ b/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java
-@@ -80,6 +80,7 @@ public class RegistriesArgumentProvider implements ArgumentsProvider {
- register(RegistryKey.CAT_VARIANT, Cat.Type.class, Registries.CAT_VARIANT, CraftCat.CraftType.class, CatVariant.class);
+@@ -83,6 +83,7 @@ public class RegistriesArgumentProvider implements ArgumentsProvider {
register(RegistryKey.MAP_DECORATION_TYPE, MapCursor.Type.class, Registries.MAP_DECORATION_TYPE, CraftMapCursor.CraftType.class, MapDecorationType.class);
register(RegistryKey.BANNER_PATTERN, PatternType.class, Registries.BANNER_PATTERN, CraftPatternType.class, BannerPattern.class);
+ register(RegistryKey.MENU, MenuType.class, Registries.MENU, CraftMenuType.class, net.minecraft.world.inventory.MenuType.class);
+ register(RegistryKey.DATA_COMPONENT_TYPE, io.papermc.paper.datacomponent.DataComponentType.class, Registries.DATA_COMPONENT_TYPE, io.papermc.paper.datacomponent.PaperComponentType.class, net.minecraft.core.component.DataComponentType.class);
}