DEATH_PROTECTION = valued("death_protection");
++ /**
++ * Stores list of enchantments and their levels for an Enchanted Book.
++ * Unlike {@link #ENCHANTMENTS}, the effects provided by enchantments
++ * do not apply from this component.
++ *
++ * If not present on an Enchanted Book, it will not work in an anvil.
++ *
++ * Has an undefined behaviour if present on an item that is not an Enchanted Book
++ * (currently the presence of this component allows enchantments from {@link #ENCHANTMENTS}
++ * to be applied as if this item was an Enchanted Book).
++ *
++ * @see #ENCHANTMENTS
++ */
++ public static final DataComponentType.Valued STORED_ENCHANTMENTS = valued("stored_enchantments");
++ /**
++ * Represents a color applied to a dyeable item (in the {@link io.papermc.paper.registry.keys.tags.ItemTypeTagKeys#DYEABLE} item tag).
++ */
++ public static final DataComponentType.Valued DYED_COLOR = valued("dyed_color");
++ /**
++ * Represents the tint of the decorations on the {@link org.bukkit.inventory.ItemType#FILLED_MAP} item.
++ */
++ public static final DataComponentType.Valued MAP_COLOR = valued("map_color");
++ /**
++ * References the shared map state holding map contents and markers for a {@link org.bukkit.inventory.ItemType#FILLED_MAP}.
++ */
++ public static final DataComponentType.Valued MAP_ID = valued("map_id");
++ /**
++ * Holds a list of markers to be placed on a {@link org.bukkit.inventory.ItemType#FILLED_MAP} (used for Explorer Maps).
++ */
++ public static final DataComponentType.Valued MAP_DECORATIONS = valued("map_decorations");
++ /**
++ * Internal map item state used in the map crafting recipe.
++ */
++ public static final DataComponentType.Valued MAP_POST_PROCESSING = valued("map_post_processing");
++ /**
++ * Holds all projectiles that have been loaded into a Crossbow.
++ * If not present, the Crossbow is not charged.
++ */
++ public static final DataComponentType.Valued CHARGED_PROJECTILES = valued("charged_projectiles");
++ /**
++ * Holds all items stored inside a Bundle.
++ * If removed, items cannot be added to the Bundle.
++ */
++ public static final DataComponentType.Valued BUNDLE_CONTENTS = valued("bundle_contents");
++ /**
++ * Holds the contents of a potion (Potion, Splash Potion, Lingering Potion),
++ * or potion applied to a Tipped Arrow.
++ */
++ public static final DataComponentType.Valued POTION_CONTENTS = valued("potion_contents");
++ /**
++ * Holds the effects that will be applied when consuming Suspicious Stew.
++ */
++ public static final DataComponentType.Valued SUSPICIOUS_STEW_EFFECTS = valued("suspicious_stew_effects");
++ /**
++ * Holds the contents in a Book and Quill.
++ */
++ public static final DataComponentType.Valued WRITABLE_BOOK_CONTENT = valued("writable_book_content");
++ /**
++ * Holds the contents and metadata of a Written Book.
++ */
++ public static final DataComponentType.Valued WRITTEN_BOOK_CONTENT = valued("written_book_content");
++ /**
++ * Holds the trims applied to an item in recipes
++ */
++ public static final DataComponentType.Valued TRIM = valued("trim");
++ // debug_stick_state - Block Property API
++ // entity_data
++ // bucket_entity_data
++ // block_entity_data
++ /**
++ * Holds the instrument type used by a Goat Horn.
++ */
++ public static final DataComponentType.Valued INSTRUMENT = valued("instrument");
++ /**
++ * Controls the amplifier amount for an Ominous Bottle's Bad Omen effect.
++ */
++ public static final DataComponentType.Valued OMINOUS_BOTTLE_AMPLIFIER = valued("ominous_bottle_amplifier");
++ /**
++ * List of recipes that should be unlocked when using the Knowledge Book item.
++ */
++ public static final DataComponentType.Valued JUKEBOX_PLAYABLE = valued("jukebox_playable");
++ public static final DataComponentType.Valued> RECIPES = valued("recipes");
++ /**
++ * If present, specifies that the Compass is a Lodestone Compass.
++ */
++ public static final DataComponentType.Valued LODESTONE_TRACKER = valued("lodestone_tracker");
++ /**
++ * Stores the explosion crafted in a Firework Star.
++ */
++ public static final DataComponentType.Valued FIREWORK_EXPLOSION = valued("firework_explosion");
++ /**
++ * Stores all explosions crafted into a Firework Rocket, as well as flight duration.
++ */
++ public static final DataComponentType.Valued FIREWORKS = valued("fireworks");
++ /**
++ * Controls the skin displayed on a Player Head.
++ */
++ public static final DataComponentType.Valued PROFILE = valued("profile");
++ /**
++ * Controls the sound played by a Player Head when placed on a Note Block.
++ */
++ public static final DataComponentType.Valued NOTE_BLOCK_SOUND = valued("note_block_sound");
++ /**
++ * Stores the additional patterns applied to a Banner or Shield.
++ */
++ public static final DataComponentType.Valued BANNER_PATTERNS = valued("banner_patterns");
++ /**
++ * Stores the base color for a Shield.
++ */
++ public static final DataComponentType.Valued BASE_COLOR = valued("base_color");
++ /**
++ * Stores the Sherds applied to each side of a Decorated Pot.
++ */
++ public static final DataComponentType.Valued POT_DECORATIONS = valued("pot_decorations");
++ /**
++ * Holds the contents of container blocks (Chests, Shulker Boxes) in item form.
++ */
++ public static final DataComponentType.Valued CONTAINER = valued("container");
++ /**
++ * Holds block state properties to apply when placing a block.
++ */
++ public static final DataComponentType.Valued BLOCK_DATA = valued("block_state");
++ // bees
++ // /**
++ // * Holds the lock state of a container-like block,
++ // * copied to container block when placed.
++ // *
++ // * An item with a custom name of the same value must be used
++ // * to open this container.
++ // */
++ // public static final DataComponentType.Valued LOCK = valued("lock");
++ /**
++ * Holds the unresolved loot table and seed of a container-like block.
++ */
++ public static final DataComponentType.Valued CONTAINER_LOOT = valued("container_loot");
++
++ private static DataComponentType.NonValued unvalued(final String name) {
++ return (DataComponentType.NonValued) requireNonNull(Registry.DATA_COMPONENT_TYPE.get(NamespacedKey.minecraft(name)), name + " unvalued data component type couldn't be found, this is a bug.");
++ }
++
++ @SuppressWarnings("unchecked")
++ private static DataComponentType.Valued valued(final String name) {
++ return (DataComponentType.Valued) requireNonNull(Registry.DATA_COMPONENT_TYPE.get(NamespacedKey.minecraft(name)), name + " valued data component type couldn't be found, this is a bug.");
++ }
++
++ private DataComponentTypes() {
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/BannerPatternLayers.java b/src/main/java/io/papermc/paper/datacomponent/item/BannerPatternLayers.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..12cfae82234b8c4cb231ab91e72ad82d28b85183
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/BannerPatternLayers.java
+@@ -0,0 +1,66 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.Arrays;
++import java.util.List;
++import org.bukkit.block.banner.Pattern;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the layers of patterns on a banner.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#BANNER_PATTERNS
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface BannerPatternLayers {
++
++ @Contract(value = "_ -> new", pure = true)
++ static BannerPatternLayers bannerPatternLayers(final List patterns) {
++ return bannerPatternLayers().addAll(patterns).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static BannerPatternLayers.Builder bannerPatternLayers() {
++ return ItemComponentTypesBridge.bridge().bannerPatternLayers();
++ }
++
++ /**
++ * Gets the patterns on the banner.
++ *
++ * @return the patterns
++ */
++ @Contract(pure = true)
++ @Unmodifiable List patterns();
++
++ /**
++ * Builder for {@link BannerPatternLayers}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Adds a pattern to the banner.
++ *
++ * @param pattern the pattern
++ * @return the builder for chaining
++ * @see #patterns()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder add(Pattern pattern);
++
++ /**
++ * Adds multiple patterns to the banner.
++ *
++ * @param patterns the patterns
++ * @return the builder for chaining
++ * @see #patterns()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addAll(List patterns);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/BlockItemDataProperties.java b/src/main/java/io/papermc/paper/datacomponent/item/BlockItemDataProperties.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..65f1bc8d1bea0042dca9683c439561132dbeea5c
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/BlockItemDataProperties.java
+@@ -0,0 +1,51 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import org.bukkit.block.BlockType;
++import org.bukkit.block.data.BlockData;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the {@link BlockData} properties of a block item.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#BLOCK_DATA
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface BlockItemDataProperties {
++
++ @Contract(value = "-> new", pure = true)
++ static BlockItemDataProperties.Builder blockItemStateProperties() {
++ return ItemComponentTypesBridge.bridge().blockItemStateProperties();
++ }
++
++ /**
++ * Creates a new {@link BlockData} instance for the given {@link BlockType}.
++ *
++ * @param blockType the block type
++ * @return the block data
++ */
++ @Contract(pure = true)
++ BlockData createBlockData(BlockType blockType);
++
++ /**
++ * Applies the properties to the given {@link BlockData}. Doesn't
++ * mutate the parameter, but returns a new instance with the properties applied.
++ *
++ * @param blockData the block data to apply the properties to
++ * @return the block data with the properties applied
++ */
++ @Contract(pure = true)
++ BlockData applyTo(BlockData blockData);
++
++ /**
++ * Builder for {@link BlockItemDataProperties}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++ // building this requires BlockProperty API, so an empty builder for now (essentially read-only)
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/BundleContents.java b/src/main/java/io/papermc/paper/datacomponent/item/BundleContents.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c0f671aef8225c87632d2368d1b28fc8b1bce686
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/BundleContents.java
+@@ -0,0 +1,66 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.Arrays;
++import java.util.List;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds all items stored inside of a Bundle.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#BUNDLE_CONTENTS
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface BundleContents {
++
++ @Contract(value = "_ -> new", pure = true)
++ static BundleContents bundleContents(final List contents) {
++ return ItemComponentTypesBridge.bridge().bundleContents().addAll(contents).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static BundleContents.Builder bundleContents() {
++ return ItemComponentTypesBridge.bridge().bundleContents();
++ }
++
++ /**
++ * Lists the items that are currently stored inside of this component.
++ *
++ * @return items
++ */
++ @Contract(value = "-> new", pure = true)
++ @Unmodifiable List contents();
++
++ /**
++ * Builder for {@link BundleContents}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Adds an item to this builder.
++ *
++ * @param stack item
++ * @return the builder for chaining
++ * @see #contents()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder add(ItemStack stack);
++
++ /**
++ * Adds items to this builder.
++ *
++ * @param stacks items
++ * @return the builder for chaining
++ * @see #contents()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addAll(List stacks);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ChargedProjectiles.java b/src/main/java/io/papermc/paper/datacomponent/item/ChargedProjectiles.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..d0a6e7db06f540e13ac00e8da3acabd9f7838f1f
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/ChargedProjectiles.java
+@@ -0,0 +1,66 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.Arrays;
++import java.util.List;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds all projectiles that have been loaded into a Crossbow.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#CHARGED_PROJECTILES
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface ChargedProjectiles {
++
++ @Contract(value = "_ -> new", pure = true)
++ static ChargedProjectiles chargedProjectiles(final List projectiles) {
++ return chargedProjectiles().addAll(projectiles).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static ChargedProjectiles.Builder chargedProjectiles() {
++ return ItemComponentTypesBridge.bridge().chargedProjectiles();
++ }
++
++ /**
++ * Lists the projectiles that are currently loaded into this component.
++ *
++ * @return the loaded projectiles
++ */
++ @Contract(value = "-> new", pure = true)
++ @Unmodifiable List projectiles();
++
++ /**
++ * Builder for {@link ChargedProjectiles}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Adds a projectile to be loaded in this builder.
++ *
++ * @param stack projectile
++ * @return the builder for chaining
++ * @see #projectiles()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder add(ItemStack stack);
++
++ /**
++ * Adds projectiles to be loaded in this builder.
++ *
++ * @param stacks projectiles
++ * @return the builder for chaining
++ * @see #projectiles()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addAll(List 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..a448fedb63ffce18b9f6a1bd0fecfc5cd90224a6
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/Consumable.java
+@@ -0,0 +1,70 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.BuildableDataComponent;
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect;
++import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation;
++import java.util.Collection;
++import java.util.List;
++import net.kyori.adventure.key.Key;
++import org.checkerframework.checker.index.qual.NonNegative;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the properties for this item for when it is consumed.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#CONSUMABLE
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface Consumable extends BuildableDataComponent {
++
++ @Contract(value = "-> new", pure = true)
++ static Consumable.Builder consumable() {
++ return ItemComponentTypesBridge.bridge().consumable();
++ }
++
++ @Contract(pure = true)
++ @NonNegative float consumeSeconds();
++
++ @Contract(pure = true)
++ ItemUseAnimation animation();
++
++ @Contract(pure = true)
++ Key sound();
++
++ @Contract(pure = true)
++ boolean hasConsumeParticles();
++
++ @Contract(pure = true)
++ @Unmodifiable List consumeEffects();
++
++ /**
++ * Builder for {@link Consumable}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder consumeSeconds(@NonNegative float consumeSeconds);
++
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder animation(ItemUseAnimation animation);
++
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder sound(Key sound);
++
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder hasConsumeParticles(boolean hasConsumeParticles);
++
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addEffect(ConsumeEffect effect);
++
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addEffects(List 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..d416c9d25b3ab88bf1e208c6faf92a8e2378c376
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/CustomModelData.java
+@@ -0,0 +1,28 @@
++package io.papermc.paper.datacomponent.item;
++
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the custom model data.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#CUSTOM_MODEL_DATA
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface CustomModelData {
++
++ @Contract(value = "_ -> new", pure = true)
++ static CustomModelData customModelData(final int id) {
++ return ItemComponentTypesBridge.bridge().customModelData(id);
++ }
++
++ /**
++ * Gets the custom model data id.
++ *
++ * @return the id
++ */
++ @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..6cbd73cb2a11f4858b44a2f57d2fe0acb1eb9fb5
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java
+@@ -0,0 +1,30 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.registry.tag.TagKey;
++import org.bukkit.damage.DamageType;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the contents of damage types that the item entity containing this item is invincible to.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#DAMAGE_RESISTANT
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface DamageResistant {
++
++ @Contract(value = "_ -> new", pure = true)
++ static DamageResistant damageResistant(final TagKey types) {
++ return ItemComponentTypesBridge.bridge().damageResistant(types);
++ }
++
++ /**
++ * The types that this damage type is invincible tp.
++ *
++ * @return item
++ */
++ @Contract(value = "-> new", pure = true)
++ TagKey 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..87c2220708af7db06348994ad5940c7cecd9f691
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/DeathProtection.java
+@@ -0,0 +1,48 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect;
++import java.util.Arrays;
++import java.util.Collection;
++import java.util.List;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Sets whether this item should protect the entity upon death, and what effects should be played.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#DEATH_PROTECTION
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface DeathProtection {
++
++ @Contract(value = "_ -> new", pure = true)
++ static DeathProtection deathProtection(final List deathEffects) {
++ return deathProtection().addEffects(deathEffects).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static DeathProtection.Builder deathProtection() {
++ return ItemComponentTypesBridge.bridge().deathProtection();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ @Unmodifiable List deathEffects();
++
++ /**
++ * Builder for {@link DeathProtection}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addEffect(ConsumeEffect effect);
++
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addEffects(List 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..d80581fc8b894cc4d4af9741244b1bb03468b263
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/DyedItemColor.java
+@@ -0,0 +1,53 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import org.bukkit.Color;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Represents a color applied to a dyeable item.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#DYED_COLOR
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface DyedItemColor extends ShownInTooltip {
++
++ @Contract(value = "_, _ -> new", pure = true)
++ static DyedItemColor dyedItemColor(final Color color, final boolean showInTooltip) {
++ return dyedItemColor().color(color).showInTooltip(showInTooltip).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static DyedItemColor.Builder dyedItemColor() {
++ return ItemComponentTypesBridge.bridge().dyedItemColor();
++ }
++
++ /**
++ * Color of the item.
++ *
++ * @return color
++ */
++ @Contract(value = "-> new", pure = true)
++ Color color();
++
++ /**
++ * Builder for {@link DyedItemColor}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends ShownInTooltip.Builder, DataComponentBuilder {
++
++ /**
++ * Sets the color of this builder.
++ *
++ * @param color color
++ * @return the builder for chaining
++ * @see #color()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder color(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..5169b9cd73dc0ffc8297f8d5f63d3d707a47d279
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/Enchantable.java
+@@ -0,0 +1,31 @@
++package io.papermc.paper.datacomponent.item;
++
++import org.checkerframework.checker.index.qual.Positive;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * 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
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface Enchantable {
++
++ @Contract(value = "_ -> new", pure = true)
++ static Enchantable enchantable(final @Positive 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.
++ *
++ * @see Minecraft Wiki
++ * @return the value
++ */
++ @Contract(pure = true)
++ @Positive 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..7d84217814bba4ce826e33755fee0d5c3b280009
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/Equippable.java
+@@ -0,0 +1,170 @@
++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.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++import org.jspecify.annotations.Nullable;
++
++
++/**
++ * Holds the equippable properties of an item.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#EQUIPPABLE
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface Equippable extends BuildableDataComponent {
++
++ /**
++ * Creates a new {@link Equippable.Builder} instance.
++ * @param slot The slot for the new equippable to be equippable in.
++ *
++ * @return a new builder
++ */
++ @Contract(value = "_ -> new", pure = true)
++ static Equippable.Builder equippable(final EquipmentSlot slot) {
++ return ItemComponentTypesBridge.bridge().equippable(slot);
++ }
++
++ /**
++ * Gets the equipment slot this item can be equipped in.
++ *
++ * @return the equipment slot
++ */
++ @Contract(pure = true)
++ EquipmentSlot slot();
++
++ /**
++ * Gets the equip sound key.
++ *
++ * @return the equip sound key
++ */
++ @Contract(pure = true)
++ 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 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 {
++
++ /**
++ * Sets the equip sound key for this item.
++ *
++ * @param equipSound the equip sound key
++ * @return the builder for chaining
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder equipSound(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")
++ 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")
++ 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")
++ Builder allowedEntities(@Nullable RegistryKeySet allowedEntities);
++
++ /**
++ * Sets whether the item is dispensable.
++ *
++ * @param dispensable true if dispensable
++ * @return the builder for chaining
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ 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")
++ 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")
++ 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..72aa1b4bda2693e0cd78d93449dda23bd1b74062
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/Fireworks.java
+@@ -0,0 +1,84 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.List;
++import org.bukkit.FireworkEffect;
++import org.checkerframework.common.value.qual.IntRange;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Stores all explosions crafted into a Firework Rocket, as well as flight duration.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#FIREWORKS
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface Fireworks {
++
++ @Contract(value = "_, _ -> new", pure = true)
++ static Fireworks fireworks(final List effects, final int flightDuration) {
++ return fireworks().addEffects(effects).flightDuration(flightDuration).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static Fireworks.Builder fireworks() {
++ return ItemComponentTypesBridge.bridge().fireworks();
++ }
++
++ /**
++ * Lists the effects stored in this component.
++ *
++ * @return the effects
++ */
++ @Contract(pure = true)
++ @Unmodifiable List effects();
++
++ /**
++ * Number of gunpowder in this component.
++ *
++ * @return the flight duration
++ */
++ @Contract(pure = true)
++ @IntRange(from = 0, to = 255) int flightDuration();
++
++ /**
++ * Builder for {@link Fireworks}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Sets the number of gunpowder used in this builder.
++ *
++ * @param duration duration
++ * @return the builder for chaining
++ * @see #flightDuration()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder flightDuration(@IntRange(from = 0, to = 255) int duration);
++
++ /**
++ * Adds an explosion to this builder.
++ *
++ * @param effect effect
++ * @return the builder for chaining
++ * @see #effects()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addEffect(FireworkEffect effect);
++
++ /**
++ * Adds explosions to this builder.
++ *
++ * @param effects effects
++ * @return the builder for chaining
++ * @see #effects()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addEffects(List effects);
++ }
++}
+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..369208e15a0e7fc91a9505fef2097c4283445e4a
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/FoodProperties.java
+@@ -0,0 +1,87 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.BuildableDataComponent;
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import org.checkerframework.checker.index.qual.NonNegative;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the food properties of an item.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#FOOD
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface FoodProperties extends BuildableDataComponent {
++
++ @Contract(value = "-> new", pure = true)
++ static FoodProperties.Builder food() {
++ return ItemComponentTypesBridge.bridge().food();
++ }
++
++ /**
++ * Number of food points to restore when eaten.
++ *
++ * @return the nutrition
++ */
++ @Contract(pure = true)
++ @NonNegative int nutrition();
++
++ /**
++ * Amount of saturation to restore when eaten.
++ *
++ * @return the saturation
++ */
++ @Contract(pure = true)
++ float saturation();
++
++ /**
++ * If {@code true}, this food can be eaten even if not hungry.
++ *
++ * @return can always be eaten
++ */
++ @Contract(pure = true)
++ boolean canAlwaysEat();
++
++ /**
++ * Builder for {@link FoodProperties}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Set if this food can always be eaten, even if the
++ * player is not hungry.
++ *
++ * @param canAlwaysEat true to allow always eating
++ * @return the builder for chaining
++ * @see #canAlwaysEat()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder canAlwaysEat(boolean canAlwaysEat);
++
++ /**
++ * Sets the saturation of the food.
++ *
++ * @param saturation the saturation
++ * @return the builder for chaining
++ * @see #saturation()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder saturation(float saturation);
++
++ /**
++ * Sets the nutrition of the food.
++ *
++ * @param nutrition the nutrition, must be non-negative
++ * @return the builder for chaining
++ * @see #nutrition()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder nutrition(@NonNegative int nutrition);
++
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemAdventurePredicate.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemAdventurePredicate.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f5061d1f349b35e5ec57d2d1c64eafb096141404
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemAdventurePredicate.java
+@@ -0,0 +1,65 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.block.BlockPredicate;
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.List;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Controls which blocks a player in Adventure mode can do a certain action with this item.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#CAN_BREAK
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#CAN_PLACE_ON
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface ItemAdventurePredicate extends ShownInTooltip {
++
++ @Contract(value = "_ -> new", pure = true)
++ static ItemAdventurePredicate itemAdventurePredicate(final List predicates) {
++ return itemAdventurePredicate().addPredicates(predicates).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static ItemAdventurePredicate.Builder itemAdventurePredicate() {
++ return ItemComponentTypesBridge.bridge().itemAdventurePredicate();
++ }
++
++ /**
++ * List of block predicates that control if the action is allowed.
++ *
++ * @return predicates
++ */
++ @Contract(pure = true)
++ @Unmodifiable List predicates();
++
++ /**
++ * Builder for {@link ItemAdventurePredicate}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends ShownInTooltip.Builder, DataComponentBuilder {
++ /**
++ * Adds a block predicate to this builder.
++ *
++ * @param predicate predicate
++ * @return the builder for chaining
++ * @see #predicates()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addPredicate(BlockPredicate predicate);
++
++ /**
++ * Adds block predicates to this builder.
++ *
++ * @param predicates predicates
++ * @return the builder for chaining
++ * @see #predicates()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addPredicates(List predicates);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemArmorTrim.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemArmorTrim.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..0309ae59ab7945ddfb5410930d161e2ce3d1878a
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemArmorTrim.java
+@@ -0,0 +1,53 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import org.bukkit.inventory.meta.trim.ArmorTrim;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the trims applied to an item.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#TRIM
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface ItemArmorTrim extends ShownInTooltip {
++
++ @Contract(value = "_, _ -> new", pure = true)
++ static ItemArmorTrim itemArmorTrim(final ArmorTrim armorTrim, final boolean showInTooltip) {
++ return itemArmorTrim(armorTrim).showInTooltip(showInTooltip).build();
++ }
++
++ @Contract(value = "_ -> new", pure = true)
++ static ItemArmorTrim.Builder itemArmorTrim(final ArmorTrim armorTrim) {
++ return ItemComponentTypesBridge.bridge().itemArmorTrim(armorTrim);
++ }
++
++ /**
++ * Armor trim present on this item.
++ *
++ * @return trim
++ */
++ @Contract(pure = true)
++ ArmorTrim armorTrim();
++
++ /**
++ * Builder for {@link ItemArmorTrim}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends ShownInTooltip.Builder, DataComponentBuilder {
++
++ /**
++ * Sets the armor trim for this builder.
++ *
++ * @param armorTrim trim
++ * @return the builder for chaining
++ * @see #armorTrim()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder armorTrim(ArmorTrim armorTrim);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemAttributeModifiers.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemAttributeModifiers.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..948505d38121d54df62e6a67d4597bc7d42c356f
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemAttributeModifiers.java
+@@ -0,0 +1,98 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.List;
++import org.bukkit.attribute.Attribute;
++import org.bukkit.attribute.AttributeModifier;
++import org.bukkit.inventory.EquipmentSlotGroup;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds attribute modifiers applied to any item.
++ *
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#ATTRIBUTE_MODIFIERS
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface ItemAttributeModifiers extends ShownInTooltip {
++
++ @Contract(value = "-> new", pure = true)
++ static ItemAttributeModifiers.Builder itemAttributes() {
++ return ItemComponentTypesBridge.bridge().modifiers();
++ }
++
++ /**
++ * Lists the attribute modifiers that are present on this item.
++ *
++ * @return modifiers
++ */
++ @Contract(pure = true)
++ @Unmodifiable List modifiers();
++
++ /**
++ * Holds an attribute entry.
++ */
++ @ApiStatus.NonExtendable
++ interface Entry {
++
++ /**
++ * Gets the target attribute for the paired modifier.
++ *
++ * @return the attribute
++ */
++ @Contract(pure = true)
++ Attribute attribute();
++
++ /**
++ * The modifier for the paired attribute.
++ *
++ * @return the modifier
++ */
++ @Contract(pure = true)
++ AttributeModifier modifier();
++
++ /**
++ * Gets the slot group for this attribute.
++ *
++ * @return the slot group
++ */
++ default EquipmentSlotGroup getGroup() {
++ return this.modifier().getSlotGroup();
++ }
++ }
++
++ /**
++ * Builder for {@link ItemAttributeModifiers}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends ShownInTooltip.Builder, DataComponentBuilder {
++
++ /**
++ * Adds a modifier to this builder.
++ *
++ * @param attribute attribute
++ * @param modifier modifier
++ * @return the builder for chaining
++ * @see #modifiers()
++ */
++ @Contract(value = "_, _, _ -> this", mutates = "this")
++ Builder addModifier(Attribute attribute, AttributeModifier modifier);
++
++ /**
++ * Adds a modifier to this builder.
++ *
++ * @param attribute attribute
++ * @param modifier modifier
++ * @param equipmentSlotGroup the slot group this modifier applies to (overrides any slot group in the modifier)
++ * @return the builder for chaining
++ * @see #modifiers()
++ */
++ @Contract(value = "_, _, _ -> this", mutates = "this")
++ Builder addModifier(Attribute attribute, AttributeModifier modifier, EquipmentSlotGroup equipmentSlotGroup);
++ }
++}
+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..1ce34642371a65590ce1ac74b402ccfc301671d7
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridge.java
+@@ -0,0 +1,112 @@
++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.text.Filtered;
++import java.util.Optional;
++import java.util.ServiceLoader;
++import net.kyori.adventure.key.Key;
++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.jetbrains.annotations.ApiStatus;
++import org.jspecify.annotations.NullMarked;
++import org.jspecify.annotations.Nullable;
++
++@NullMarked
++@ApiStatus.Internal
++interface ItemComponentTypesBridge {
++
++ Optional BRIDGE = ServiceLoader.load(ItemComponentTypesBridge.class).findFirst();
++
++ static ItemComponentTypesBridge bridge() {
++ return BRIDGE.orElseThrow();
++ }
++
++ ChargedProjectiles.Builder chargedProjectiles();
++
++ PotDecorations.Builder potDecorations();
++
++ Unbreakable.Builder unbreakable();
++
++ ItemLore.Builder lore();
++
++ ItemEnchantments.Builder enchantments();
++
++ ItemAttributeModifiers.Builder modifiers();
++
++ FoodProperties.Builder food();
++
++ DyedItemColor.Builder dyedItemColor();
++
++ PotionContents.Builder potionContents();
++
++ BundleContents.Builder bundleContents();
++
++ SuspiciousStewEffects.Builder suspiciousStewEffects();
++
++ MapItemColor.Builder mapItemColor();
++
++ MapDecorations.Builder mapDecorations();
++
++ MapDecorations.DecorationEntry decorationEntry(MapCursor.Type type, double x, double z, float rotation);
++
++ SeededContainerLoot.Builder seededContainerLoot(Key lootTableKey);
++
++ WrittenBookContent.Builder writtenBookContent(Filtered title, String author);
++
++ WritableBookContent.Builder writeableBookContent();
++
++ ItemArmorTrim.Builder itemArmorTrim(ArmorTrim armorTrim);
++
++ LodestoneTracker.Builder lodestoneTracker();
++
++ Fireworks.Builder fireworks();
++
++ ResolvableProfile.Builder resolvableProfile();
++
++ ResolvableProfile resolvableProfile(PlayerProfile profile);
++
++ BannerPatternLayers.Builder bannerPatternLayers();
++
++ BlockItemDataProperties.Builder blockItemStateProperties();
++
++ ItemContainerContents.Builder itemContainerContents();
++
++ JukeboxPlayable.Builder jukeboxPlayable(JukeboxSong song);
++
++ Tool.Builder tool();
++
++ Tool.Rule rule(RegistryKeySet blocks, @Nullable Float speed, TriState correctForDrops);
++
++ ItemAdventurePredicate.Builder itemAdventurePredicate();
++
++ CustomModelData customModelData(int id);
++
++ MapId mapId(int id);
++
++ UseRemainder useRemainder(ItemStack itemStack);
++
++ Consumable.Builder consumable();
++
++ UseCooldown.Builder useCooldown(final float seconds);
++
++ DamageResistant damageResistant(TagKey types);
++
++ Enchantable enchantable(int level);
++
++ Repairable repairable(RegistryKeySet types);
++
++ Equippable.Builder equippable(EquipmentSlot slot);
++
++ DeathProtection.Builder deathProtection();
++
++ OminousBottleAmplifier ominousBottleAmplifier(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
+index 0000000000000000000000000000000000000000..7d1c973ba566752d7a85496327b1352d973f2218
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemContainerContents.java
+@@ -0,0 +1,63 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.Arrays;
++import java.util.List;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the contents of an item container.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#CONTAINER
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface ItemContainerContents {
++
++ @Contract(value = "_ -> new", pure = true)
++ static ItemContainerContents containerContents(final List contents) {
++ return containerContents().addAll(contents).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static ItemContainerContents.Builder containerContents() {
++ return ItemComponentTypesBridge.bridge().itemContainerContents();
++ }
++
++ /**
++ * Gets the contents of the container.
++ *
++ * @return the contents
++ */
++ @Contract(value = "-> new", pure = true)
++ @Unmodifiable List contents();
++
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Adds an item stack to the container.
++ *
++ * @param stack the item stack
++ * @return the builder for chaining
++ * @see #contents()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder add(ItemStack stack);
++
++ /**
++ * Adds item stacks to the container.
++ *
++ * @param stacks the item stacks
++ * @return the builder for chaining
++ * @see #contents()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addAll(List stacks);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemEnchantments.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemEnchantments.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..fca271ea198209bd48cd02f4476e89e5e3e9f396
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemEnchantments.java
+@@ -0,0 +1,68 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.Map;
++import org.bukkit.enchantments.Enchantment;
++import org.checkerframework.common.value.qual.IntRange;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Stores a list of enchantments and their levels on an item.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#ENCHANTMENTS
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#STORED_ENCHANTMENTS
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface ItemEnchantments extends ShownInTooltip {
++
++ @Contract(value = "_, _ -> new", pure = true)
++ static ItemEnchantments itemEnchantments(final Map enchantments, final boolean showInTooltip) {
++ return itemEnchantments().addAll(enchantments).showInTooltip(showInTooltip).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static ItemEnchantments.Builder itemEnchantments() {
++ return ItemComponentTypesBridge.bridge().enchantments();
++ }
++
++ /**
++ * Enchantments currently present on this item.
++ *
++ * @return enchantments
++ */
++ @Contract(pure = true)
++ @Unmodifiable Map enchantments();
++
++ /**
++ * Builder for {@link ItemEnchantments}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends ShownInTooltip.Builder, DataComponentBuilder {
++
++ /**
++ * Adds an enchantment with the given level to this component.
++ *
++ * @param enchantment enchantment
++ * @param level level
++ * @return the builder for chaining
++ * @see #enchantments()
++ */
++ @Contract(value = "_, _ -> this", mutates = "this")
++ Builder add(Enchantment enchantment, @IntRange(from = 1, to = 255) int level);
++
++ /**
++ * Adds enchantments with the given level to this component.
++ *
++ * @param enchantments enchantments
++ * @return the builder for chaining
++ * @see #enchantments()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addAll(Map enchantments);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemLore.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemLore.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..3be62f6005e0343c3a6ebd04e3ee824e0b969113
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemLore.java
+@@ -0,0 +1,84 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.List;
++import net.kyori.adventure.text.Component;
++import net.kyori.adventure.text.ComponentLike;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Additional lines to include in an item's tooltip.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#LORE
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface ItemLore {
++
++ @Contract(value = "_ -> new", pure = true)
++ static ItemLore lore(final List extends ComponentLike> lines) {
++ return lore().lines(lines).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static ItemLore.Builder lore() {
++ return ItemComponentTypesBridge.bridge().lore();
++ }
++
++ /**
++ * Lists the components that are added to an item's tooltip.
++ *
++ * @return component list
++ */
++ @Contract(pure = true)
++ @Unmodifiable List lines();
++
++ /**
++ * Lists the styled components (example: italicized and purple) that are added to an item's tooltip.
++ *
++ * @return component list
++ */
++ @Contract(pure = true)
++ @Unmodifiable List styledLines();
++
++ /**
++ * Builder for {@link ItemLore}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Sets the components of this lore.
++ *
++ * @param lines components
++ * @return the builder for chaining
++ * @see #lines()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder lines(List extends ComponentLike> lines);
++
++ /**
++ * Adds a component to the lore.
++ *
++ * @param line component
++ * @return the builder for chaining
++ * @see #lines()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addLine(ComponentLike line);
++
++ /**
++ * Adds components to the lore.
++ *
++ * @param lines components
++ * @return the builder for chaining
++ * @see #lines()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addLines(List extends ComponentLike> lines);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/JukeboxPlayable.java b/src/main/java/io/papermc/paper/datacomponent/item/JukeboxPlayable.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c59942df7101c7630eabeb247b9690b9c4c76da4
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/JukeboxPlayable.java
+@@ -0,0 +1,43 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import org.bukkit.JukeboxSong;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the jukebox song for an item.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#JUKEBOX_PLAYABLE
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface JukeboxPlayable extends ShownInTooltip {
++
++ @Contract(value = "_ -> new", pure = true)
++ static JukeboxPlayable.Builder jukeboxPlayable(final JukeboxSong song) {
++ return ItemComponentTypesBridge.bridge().jukeboxPlayable(song);
++ }
++
++ @Contract(pure = true)
++ JukeboxSong jukeboxSong();
++
++ /**
++ * Builder for {@link JukeboxPlayable}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends ShownInTooltip.Builder, DataComponentBuilder {
++
++ /**
++ * Sets the jukebox song.
++ *
++ * @param song the song
++ * @return the builder for chaining
++ * @see #jukeboxSong()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder jukeboxSong(JukeboxSong song);
++ }
++}
+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
+index 0000000000000000000000000000000000000000..b919672ceea74ae09324653847b30fde293054d8
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/LodestoneTracker.java
+@@ -0,0 +1,72 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import org.bukkit.Location;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++import org.jspecify.annotations.Nullable;
++
++/**
++ * If present, specifies the target Lodestone that a Compass should point towards.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#LODESTONE_TRACKER
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface LodestoneTracker {
++
++ @Contract(value = "_, _ -> new", pure = true)
++ static LodestoneTracker lodestoneTracker(final @Nullable Location location, final boolean tracked) {
++ return lodestoneTracker().location(location).tracked(tracked).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static LodestoneTracker.Builder lodestoneTracker() {
++ return ItemComponentTypesBridge.bridge().lodestoneTracker();
++ }
++
++ /**
++ * The location that the compass should point towards.
++ *
++ * @return location
++ */
++ @Contract(value = "-> new", pure = true)
++ @Nullable Location location();
++
++ /**
++ * If {@code true}, when the Lodestone at the target position is removed, the component will be removed.
++ *
++ * @return tracked
++ */
++ @Contract(pure = true)
++ boolean tracked();
++
++ /**
++ * Builder for {@link LodestoneTracker}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Sets the location to point towards for this builder.
++ *
++ * @param location location to point towards
++ * @return the builder for chaining
++ * @see #location()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder location(@Nullable Location location);
++
++ /**
++ * Sets if this location lodestone is tracked for this builder.
++ *
++ * @param tracked is tracked
++ * @return the builder for chaining
++ * @see #tracked()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder tracked(boolean tracked);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/MapDecorations.java b/src/main/java/io/papermc/paper/datacomponent/item/MapDecorations.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..1e611f1f918c33f8d89ad23cf2fc44a127af233c
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/MapDecorations.java
+@@ -0,0 +1,121 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.Map;
++import org.bukkit.map.MapCursor;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++import org.jspecify.annotations.Nullable;
++
++/**
++ * Holds a list of markers to be placed on a Filled Map (used for Explorer Maps).
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#MAP_DECORATIONS
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface MapDecorations {
++
++ @Contract(value = "_ -> new", pure = true)
++ static MapDecorations mapDecorations(final Map entries) {
++ return mapDecorations().putAll(entries).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static MapDecorations.Builder mapDecorations() {
++ return ItemComponentTypesBridge.bridge().mapDecorations();
++ }
++
++ @Contract(value = "_, _, _, _ -> new", pure = true)
++ static DecorationEntry decorationEntry(final MapCursor.Type type, final double x, final double z, final float rotation) {
++ return ItemComponentTypesBridge.bridge().decorationEntry(type, x, z, rotation);
++ }
++
++ /**
++ * Gets the decoration entry with the given id.
++ *
++ * @param id id
++ * @return decoration entry, or {@code null} if not present
++ */
++ @Contract(pure = true)
++ @Nullable DecorationEntry decoration(String id);
++
++ /**
++ * Gets the decoration entries.
++ *
++ * @return the decoration entries
++ */
++ @Contract(pure = true)
++ @Unmodifiable Map decorations();
++
++ /**
++ * Decoration present on the map.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface DecorationEntry {
++
++ /**
++ * Type of decoration.
++ *
++ * @return type
++ */
++ @Contract(pure = true)
++ MapCursor.Type type();
++
++ /**
++ * X world coordinate of the decoration.
++ *
++ * @return x coordinate
++ */
++ @Contract(pure = true)
++ double x();
++
++ /**
++ * Z world coordinate of the decoration.
++ *
++ * @return z coordinate
++ */
++ @Contract(pure = true)
++ double z();
++
++ /**
++ * Clockwise rotation from north in degrees.
++ *
++ * @return rotation
++ */
++ @Contract(pure = true)
++ float rotation();
++ }
++
++ /**
++ * Builder for {@link MapDecorations}.
++ */
++ @ApiStatus.NonExtendable
++ @ApiStatus.Experimental
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Puts the decoration with the given id in this builder.
++ *
++ * @param id id
++ * @param entry decoration
++ * @return the builder for chaining
++ * @see #decorations()
++ */
++ @Contract(value = "_, _ -> this", mutates = "this")
++ MapDecorations.Builder put(String id, DecorationEntry entry);
++
++ /**
++ * Puts all the decoration with the given id in this builder.
++ *
++ * @param entries decorations
++ * @return the builder for chaining
++ * @see #decorations()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ MapDecorations.Builder putAll(Map entries);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/MapId.java b/src/main/java/io/papermc/paper/datacomponent/item/MapId.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..045bfe0ce5080b57a40be03a65b1a2aaf9089120
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/MapId.java
+@@ -0,0 +1,28 @@
++package io.papermc.paper.datacomponent.item;
++
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * References the shared map state holding map contents and markers for a Filled Map.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#MAP_ID
++ */
++@NullMarked
++@ApiStatus.NonExtendable
++@ApiStatus.Experimental
++public interface MapId {
++
++ @Contract(value = "_ -> new", pure = true)
++ static MapId mapId(final int id) {
++ return ItemComponentTypesBridge.bridge().mapId(id);
++ }
++
++ /**
++ * The map id.
++ *
++ * @return id
++ */
++ @Contract(pure = true)
++ int id();
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/MapItemColor.java b/src/main/java/io/papermc/paper/datacomponent/item/MapItemColor.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..87845d19a25ed2ae79b868fcfe40b88a2dc83f97
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/MapItemColor.java
+@@ -0,0 +1,43 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import org.bukkit.Color;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Represents the tint of the decorations on the Filled Map item.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#MAP_COLOR
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface MapItemColor {
++
++ @Contract(value = "-> new", pure = true)
++ static MapItemColor.Builder mapItemColor() {
++ return ItemComponentTypesBridge.bridge().mapItemColor();
++ }
++
++ /**
++ * The tint to apply.
++ *
++ * @return color
++ */
++ Color color();
++
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Sets the tint color of this map.
++ *
++ * @param color tint color
++ * @return the builder for chaining
++ * @see #color()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder color(Color color);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/OminousBottleAmplifier.java b/src/main/java/io/papermc/paper/datacomponent/item/OminousBottleAmplifier.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4f16e08f04c2cea24f3cb132ff21f4bdd6b70582
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/OminousBottleAmplifier.java
+@@ -0,0 +1,29 @@
++package io.papermc.paper.datacomponent.item;
++
++import org.checkerframework.common.value.qual.IntRange;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the ominous bottle amplifier.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#OMINOUS_BOTTLE_AMPLIFIER
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface OminousBottleAmplifier {
++
++ @Contract(value = "_ -> new", pure = true)
++ static OminousBottleAmplifier amplifier(final @IntRange(from = 0, to = 4) int amplifier) {
++ return ItemComponentTypesBridge.bridge().ominousBottleAmplifier(amplifier);
++ }
++
++ /**
++ * Gets the bottle amplifier.
++ *
++ * @return the amplifier
++ */
++ @Contract(pure = true)
++ @IntRange(from = 0, to = 4) 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..6da78b8735a6cadd1282fa2fafd8b0f74f087fb4
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/PotDecorations.java
+@@ -0,0 +1,109 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import org.bukkit.inventory.ItemType;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++import org.jspecify.annotations.Nullable;
++
++// CONTRIBUTORS: LEAVE THIS AS ITEM TYPE!!!
++/**
++ * Holds the item types for the decorations on a flower pot.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#POT_DECORATIONS
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface PotDecorations {
++
++ @Contract(value = "_, _, _, _ -> new", pure = true)
++ static PotDecorations potDecorations(final @Nullable ItemType back, final @Nullable ItemType left, final @Nullable ItemType right, final @Nullable ItemType front) {
++ return potDecorations().back(back).left(left).right(right).front(front).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static PotDecorations.Builder potDecorations() {
++ return ItemComponentTypesBridge.bridge().potDecorations();
++ }
++
++ /**
++ * Get the item type for the back.
++ *
++ * @return the back item type.
++ */
++ @Contract(pure = true)
++ @Nullable ItemType back();
++
++ /**
++ * Get the item type for the left.
++ *
++ * @return the left item type.
++ */
++ @Contract(pure = true)
++ @Nullable ItemType left();
++
++ /**
++ * Get the item type for the right.
++ *
++ * @return the right item type.
++ */
++ @Contract(pure = true)
++ @Nullable ItemType right();
++
++ /**
++ * Get the item type for the front.
++ *
++ * @return the front item type.
++ */
++ @Contract(pure = true)
++ @Nullable ItemType front();
++
++ /**
++ * Builder for {@link PotDecorations}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Set the {@link ItemType} for the back.
++ *
++ * @param back item for the back
++ * @return the builder for chaining
++ * @see #back()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder back(@Nullable ItemType back);
++
++ /**
++ * Set the {@link ItemType} for the left.
++ *
++ * @param left item for the left
++ * @return the builder for chaining
++ * @see #left()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder left(@Nullable ItemType left);
++
++ /**
++ * Set the {@link ItemType} for the right.
++ *
++ * @param right item for the right
++ * @return the builder for chaining
++ * @see #right()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder right(@Nullable ItemType right);
++
++ /**
++ * Set the {@link ItemType} for the front.
++ *
++ * @param front item for the front
++ * @return the builder for chaining
++ * @see #front()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder front(@Nullable ItemType front);
++ }
++}
+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..7cf05b382319064d45433a7e2678f65c25d11b14
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java
+@@ -0,0 +1,120 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.List;
++import org.bukkit.Color;
++import org.bukkit.potion.PotionEffect;
++import org.bukkit.potion.PotionType;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++import org.jspecify.annotations.Nullable;
++
++/**
++ * Holds the contents of a potion (Potion, Splash Potion, Lingering Potion), or potion applied to a Tipped Arrow.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#POTION_CONTENTS
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface PotionContents {
++
++ @Contract(value = "-> new", pure = true)
++ static PotionContents.Builder potionContents() {
++ return ItemComponentTypesBridge.bridge().potionContents();
++ }
++
++ /**
++ * The potion type in this item: the item will inherit all effects from this.
++ *
++ * @return potion type, or {@code null} if not present
++ */
++ @Contract(pure = true)
++ @Nullable PotionType potion();
++
++ /**
++ * Overrides the visual color of the potion.
++ *
++ * @return color override, or {@code null} if not present
++ * @apiNote alpha channel of the color is only relevant
++ * for Tipped Arrow
++ */
++ @Contract(pure = true)
++ @Nullable Color customColor();
++
++ /**
++ * Additional list of effect instances that this item should apply.
++ *
++ * @return effects
++ */
++ @Contract(pure = true)
++ @Unmodifiable List customEffects();
++
++ /**
++ * Suffix to the translation key of the potion item.
++ *
++ * @return translation key suffix, 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 {
++
++ /**
++ * Sets the potion type for this builder.
++ *
++ * @param type builder
++ * @return the builder for chaining
++ * @see #potion()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder potion(@Nullable PotionType type);
++
++ /**
++ * Sets the color override for this builder.
++ *
++ * @param color color
++ * @return the builder for chaining
++ * @see #customColor()
++ * @apiNote alpha channel of the color is supported only for Tipped Arrow
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder customColor(@Nullable Color color);
++
++ /**
++ * Sets the suffix to the translation key of the potion item.
++ *
++ * @param name name
++ * @return the builder for chaining
++ * @see #customName()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder customName(@Nullable String name);
++
++ /**
++ * Adds a custom effect instance to this builder.
++ *
++ * @param effect effect
++ * @see #customEffects()
++ * @return the builder for chaining
++ * @see #customEffects()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addCustomEffect(PotionEffect effect);
++
++ /**
++ * Adds custom effect instances to this builder.
++ *
++ * @param effects effects
++ * @see #customEffects()
++ * @return the builder for chaining
++ * @see #customEffects()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addCustomEffects(List 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..ff84d9123aab0ad2f93b397e20a37f21894547a3
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/Repairable.java
+@@ -0,0 +1,30 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.registry.set.RegistryKeySet;
++import org.bukkit.inventory.ItemType;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds if this item is repairable, and what item types it can be repaired with.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#REPAIRABLE
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface Repairable {
++
++ @Contract(value = "_ -> new", pure = true)
++ static Repairable repairable(final RegistryKeySet types) {
++ return ItemComponentTypesBridge.bridge().repairable(types);
++ }
++
++ /**
++ * The types that this item is repairable to.
++ *
++ * @return item
++ */
++ @Contract(value = "-> new", pure = true)
++ RegistryKeySet 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..dc6cd191553e7ca5b6c5768f594924e4c39fcbbe
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/ResolvableProfile.java
+@@ -0,0 +1,123 @@
++package io.papermc.paper.datacomponent.item;
++
++import com.destroystokyo.paper.profile.PlayerProfile;
++import com.destroystokyo.paper.profile.ProfileProperty;
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import java.util.Collection;
++import java.util.UUID;
++import java.util.concurrent.CompletableFuture;
++import org.intellij.lang.annotations.Pattern;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++import org.jspecify.annotations.Nullable;
++
++/**
++ * Holds player profile data that can be resolved to a {@link PlayerProfile}.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#PROFILE
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface ResolvableProfile {
++
++ @Contract(value = "_ -> new", pure = true)
++ static ResolvableProfile resolvableProfile(final PlayerProfile profile) {
++ return ItemComponentTypesBridge.bridge().resolvableProfile(profile);
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static ResolvableProfile.Builder resolvableProfile() {
++ return ItemComponentTypesBridge.bridge().resolvableProfile();
++ }
++
++ @Contract(pure = true)
++ @Nullable UUID uuid();
++
++ @Contract(pure = true)
++ @Nullable String name();
++
++ @Contract(pure = true)
++ @Unmodifiable Collection properties();
++
++ /**
++ * Produces an updated player profile based on this.
++ *
++ * This tries to produce a completed profile by filling in missing
++ * properties (name, unique id, textures, etc.), and updates existing
++ * properties (e.g. name, textures, etc.) to their official and up-to-date
++ * values. This operation does not alter the current profile, but produces a
++ * new updated {@link PlayerProfile}.
++ *
++ * If no player exists for the unique id or name of this profile, this
++ * operation yields a profile that is equal to the current profile, which
++ * might not be complete.
++ *
++ * This is an asynchronous operation: Updating the profile can result in an
++ * outgoing connection in another thread in order to fetch the latest
++ * profile properties. The returned {@link CompletableFuture} will be
++ * completed once the updated profile is available. In order to not block
++ * the server's main thread, you should not wait for the result of the
++ * returned CompletableFuture on the server's main thread. Instead, if you
++ * want to do something with the updated player profile on the server's main
++ * thread once it is available, you could do something like this:
++ *
++ * profile.resolve().thenAcceptAsync(updatedProfile -> {
++ * // Do something with the updated profile:
++ * // ...
++ * }, runnable -> Bukkit.getScheduler().runTask(plugin, runnable));
++ *
++ */
++ @Contract(pure = true)
++ CompletableFuture resolve();
++
++ /**
++ * Builder for {@link ResolvableProfile}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Sets the name for this profile. Must be 16-or-less
++ * characters and not contain invalid characters.
++ *
++ * @param name the name
++ * @return the builder for chaining
++ * @see #name()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder name(@Pattern("^[!-~]{0,16}$") @Nullable String name);
++
++ /**
++ * Sets the UUID for this profile.
++ *
++ * @param uuid the UUID
++ * @return the builder for chaining
++ * @see #uuid()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder uuid(@Nullable UUID uuid);
++
++ /**
++ * Adds a property to this profile.
++ *
++ * @param property the property
++ * @return the builder for chaining
++ * @see #properties()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addProperty(ProfileProperty property);
++
++ /**
++ * Adds properties to this profile.
++ *
++ * @param properties the properties
++ * @return the builder for chaining
++ * @see #properties()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addProperties(Collection properties);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/SeededContainerLoot.java b/src/main/java/io/papermc/paper/datacomponent/item/SeededContainerLoot.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f79af65e8f3f8ffbb9be1cf1c6b537cd1e2b1031
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/SeededContainerLoot.java
+@@ -0,0 +1,71 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import net.kyori.adventure.key.Key;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the loot table and seed for a container.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#CONTAINER_LOOT
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface SeededContainerLoot {
++
++ @Contract(value = "_, _ -> new", pure = true)
++ static SeededContainerLoot seededContainerLoot(final Key lootTableKey, final long seed) {
++ return SeededContainerLoot.seededContainerLoot(lootTableKey).seed(seed).build();
++ }
++
++ @Contract(value = "_ -> new", pure = true)
++ static SeededContainerLoot.Builder seededContainerLoot(final Key lootTableKey) {
++ return ItemComponentTypesBridge.bridge().seededContainerLoot(lootTableKey);
++ }
++
++ /**
++ * Gets the loot table key.
++ *
++ * @return the loot table key
++ */
++ @Contract(pure = true)
++ Key lootTable();
++
++ /**
++ * Gets the loot table seed.
++ *
++ * @return the seed
++ */
++ @Contract(pure = true)
++ long seed();
++
++ /**
++ * Builder for {@link SeededContainerLoot}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Sets the loot table key.
++ *
++ * @param key the loot table key
++ * @return the builder for chaining
++ * @see #lootTable()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder lootTable(Key key);
++
++ /**
++ * Sets the loot table seed.
++ *
++ * @param seed the seed
++ * @return the builder for chaining
++ * @see #seed()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder seed(long seed);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ShownInTooltip.java b/src/main/java/io/papermc/paper/datacomponent/item/ShownInTooltip.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..7e058aebcbd768517f6db51540598721cdae4425
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/ShownInTooltip.java
+@@ -0,0 +1,52 @@
++package io.papermc.paper.datacomponent.item;
++
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the state of whether a data component should be shown
++ * in an item's tooltip.
++ * @param the data component type
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface ShownInTooltip {
++
++ /**
++ * Gets if the data component should be shown in the item's tooltip.
++ *
++ * @return {@code true} to show in the tooltip
++ */
++ @Contract(pure = true)
++ boolean showInTooltip();
++
++ /**
++ * Returns a copy of this data component with the specified
++ * show-in-tooltip state.
++ * @param showInTooltip {@code true} to show in the tooltip
++ * @return the new data component
++ */
++ @Contract(value = "_ -> new", pure = true)
++ T showInTooltip(boolean showInTooltip);
++
++ /**
++ * A builder for creating a {@link ShownInTooltip} data component.
++ * @param builder type
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder {
++
++ /**
++ * Sets if the data component should be shown in the item's tooltip.
++ *
++ * @param showInTooltip {@code true} to show in the tooltip
++ * @return the builder for chaining
++ * @see #showInTooltip()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ B showInTooltip(boolean showInTooltip);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/SuspiciousStewEffects.java b/src/main/java/io/papermc/paper/datacomponent/item/SuspiciousStewEffects.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..422bb5ccc42b94b60fba6714e9e6fb8aced6eb0c
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/SuspiciousStewEffects.java
+@@ -0,0 +1,67 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import io.papermc.paper.potion.SuspiciousEffectEntry;
++import java.util.Arrays;
++import java.util.Collection;
++import java.util.List;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the effects that will be applied when consuming Suspicious Stew.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#SUSPICIOUS_STEW_EFFECTS
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface SuspiciousStewEffects {
++
++ @Contract(value = "_ -> new", pure = true)
++ static SuspiciousStewEffects suspiciousStewEffects(final Collection effects) {
++ return suspiciousStewEffects().addAll(effects).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static SuspiciousStewEffects.Builder suspiciousStewEffects() {
++ return ItemComponentTypesBridge.bridge().suspiciousStewEffects();
++ }
++
++ /**
++ * Effects that will be applied when consuming Suspicious Stew.
++ *
++ * @return effects
++ */
++ @Contract(pure = true)
++ @Unmodifiable List effects();
++
++ /**
++ * Builder for {@link SuspiciousStewEffects}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Adds an effect applied to this builder.
++ *
++ * @param entry effect
++ * @return the builder for chaining
++ * @see #effects()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder add(SuspiciousEffectEntry entry);
++
++ /**
++ * Adds effects applied to this builder.
++ *
++ * @param entries effect
++ * @return the builder for chaining
++ * @see #effects()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addAll(Collection entries);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Tool.java b/src/main/java/io/papermc/paper/datacomponent/item/Tool.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4e87feb83204266e1fefdafe7b7e5ac53da3160e
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/Tool.java
+@@ -0,0 +1,149 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import io.papermc.paper.registry.set.RegistryKeySet;
++import java.util.Collection;
++import java.util.List;
++import net.kyori.adventure.util.TriState;
++import org.bukkit.block.BlockType;
++import org.checkerframework.checker.index.qual.NonNegative;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++import org.jspecify.annotations.Nullable;
++
++/**
++ * Controls the behavior of the item as a tool.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#TOOL
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface Tool {
++
++ @Contract(value = "-> new", pure = true)
++ static Tool.Builder tool() {
++ return ItemComponentTypesBridge.bridge().tool();
++ }
++
++ /**
++ * Creates a mining rule that specifies how an item interacts with certain block types.
++ *
++ * This method allows you to define a rule for a set of block types, optionally setting a custom mining speed
++ * and determining whether the item should correct for drops when mining these blocks.
++ *
++ * @param blocks The set of block types this rule applies to.
++ * @param speed The custom mining speed multiplier for these blocks. If {@code null}, the default speed is used.
++ * @param correctForDrops A {@link TriState} indicating how to handle item drops:
++ *
++ * - {@link TriState#TRUE} - Items will be dropped.
++ * - {@link TriState#FALSE} - Items will not be dropped.
++ * - {@link TriState#NOT_SET} - The default drop behavior is used.
++ *
++ * @return A new {@link Rule} instance representing the mining rule.
++ */
++ static Rule rule(final RegistryKeySet blocks, final @Nullable Float speed, final TriState correctForDrops) {
++ return ItemComponentTypesBridge.bridge().rule(blocks, speed, correctForDrops);
++ }
++
++ /**
++ * Mining speed to use if no rules match and don't override mining speed.
++ *
++ * @return default mining speed
++ */
++ @Contract(pure = true)
++ float defaultMiningSpeed();
++
++ /**
++ * Amount of durability to remove each time a block is mined with this tool.
++ *
++ * @return durability
++ */
++ @Contract(pure = true)
++ @NonNegative int damagePerBlock();
++
++ /**
++ * List of rule entries.
++ *
++ * @return rules
++ */
++ @Contract(pure = true)
++ @Unmodifiable List rules();
++
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Rule {
++
++ /**
++ * Blocks to match.
++ *
++ * @return blocks
++ */
++ RegistryKeySet blocks();
++
++ /**
++ * Overrides the mining speed if present and matched.
++ *
++ * {@code true} will cause the block to mine at its most efficient speed, and drop items if the targeted block requires that.
++ *
++ * @return speed override
++ */
++ @Nullable Float speed();
++
++ /**
++ * Overrides whether this tool is considered 'correct' if present and matched.
++ *
++ * @return a tri-state
++ */
++ TriState correctForDrops();
++ }
++
++ /**
++ * Builder for {@link Tool}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Controls the amount of durability to remove each time a block is mined with this tool.
++ *
++ * @param damage durability to remove
++ * @return the builder for chaining
++ * @see #damagePerBlock()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder damagePerBlock(@NonNegative int damage);
++
++ /**
++ * Controls mining speed to use if no rules match and don't override mining speed.
++ *
++ * @param miningSpeed mining speed
++ * @return the builder for chaining
++ * @see #defaultMiningSpeed()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder defaultMiningSpeed(float miningSpeed);
++
++ /**
++ * Adds a rule to the tool that controls the breaking speed / damage per block if matched.
++ *
++ * @param rule rule
++ * @return the builder for chaining
++ * @see #rules()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addRule(Rule rule);
++
++ /**
++ * Adds rules to the tool that control the breaking speed / damage per block if matched.
++ *
++ * @param rules rules
++ * @return the builder for chaining
++ * @see #rules()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addRules(Collection rules);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Unbreakable.java b/src/main/java/io/papermc/paper/datacomponent/item/Unbreakable.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..498eb479dce406d2b0b470b327eac8279a0d98bc
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/Unbreakable.java
+@@ -0,0 +1,34 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * If set, the item will not lose any durability when used.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#UNBREAKABLE
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface Unbreakable extends ShownInTooltip {
++
++ @Contract(value = "_ -> new", pure = true)
++ static Unbreakable unbreakable(final boolean showInTooltip) {
++ return unbreakable().showInTooltip(showInTooltip).build();
++ }
++
++ @Contract(value = "-> new", pure = true)
++ static Unbreakable.Builder unbreakable() {
++ return ItemComponentTypesBridge.bridge().unbreakable();
++ }
++
++ /**
++ * Builder for {@link Unbreakable}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends ShownInTooltip.Builder, DataComponentBuilder {
++ }
++}
+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..57fc55ad1def2bb14fc0a95ee3c0c157b0ac53fb
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/UseCooldown.java
+@@ -0,0 +1,65 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import net.kyori.adventure.key.Key;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++import org.jspecify.annotations.Nullable;
++
++/**
++ * Holds the contents of cooldown information when an item is used.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#USE_COOLDOWN
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++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.Builder useCooldown(final 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.
++ *
++ * 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 {
++
++ /**
++ * Sets a unique resource location for this cooldown group.
++ *
++ * This allows items to share cooldowns with other items in the same cooldown group.
++ *
++ *
++ * @param key the unique resource location; can be null
++ * @return the builder for chaining
++ * @see #cooldownGroup()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ 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..50e42e073311332142980828d0beec1828570512
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/UseRemainder.java
+@@ -0,0 +1,31 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the contents of item transformation information when an item is used.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#USE_REMAINDER
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface UseRemainder {
++
++ @Contract(value = "_ -> new", pure = true)
++ static UseRemainder useRemainder(final 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)
++ ItemStack transformInto();
++
++}
+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..828d3bb1c763e0f3c89a73d6b70d1f006258644f
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/WritableBookContent.java
+@@ -0,0 +1,80 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import io.papermc.paper.text.Filtered;
++import java.util.List;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the pages for a writable book.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#WRITABLE_BOOK_CONTENT
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface WritableBookContent {
++
++ @Contract(value = "-> new", pure = true)
++ static WritableBookContent.Builder writeableBookContent() {
++ return ItemComponentTypesBridge.bridge().writeableBookContent();
++ }
++
++ /**
++ * Holds the pages that can be written to for this component.
++ *
++ * @return pages, as filtered objects
++ */
++ @Contract(pure = true)
++ @Unmodifiable List> pages();
++
++ /**
++ * Builder for {@link WritableBookContent}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Adds a page that can be written to for this builder.
++ *
++ * @param page page
++ * @return the builder for chaining
++ * @see #pages()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addPage(String page);
++
++ /**
++ * Adds pages that can be written to for this builder.
++ *
++ * @param pages pages
++ * @return the builder for chaining
++ * @see #pages()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addPages(List pages);
++
++ /**
++ * Adds a filterable page that can be written to for this builder.
++ *
++ * @param page page
++ * @return the builder for chaining
++ * @see #pages()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addFilteredPage(Filtered page);
++
++ /**
++ * Adds filterable pages that can be written to for this builder.
++ *
++ * @param pages pages
++ * @return the builder for chaining
++ * @see #pages()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addFilteredPages(List> pages);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/datacomponent/item/WrittenBookContent.java b/src/main/java/io/papermc/paper/datacomponent/item/WrittenBookContent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..979bc05009b84b6fcdb59938cceace351e61c78b
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/WrittenBookContent.java
+@@ -0,0 +1,172 @@
++package io.papermc.paper.datacomponent.item;
++
++import io.papermc.paper.datacomponent.DataComponentBuilder;
++import io.papermc.paper.text.Filtered;
++import java.util.List;
++import net.kyori.adventure.text.Component;
++import net.kyori.adventure.text.ComponentLike;
++import org.checkerframework.common.value.qual.IntRange;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.Unmodifiable;
++import org.jspecify.annotations.NullMarked;
++
++/**
++ * Holds the contents and metadata of a Written Book.
++ * @see io.papermc.paper.datacomponent.DataComponentTypes#WRITTEN_BOOK_CONTENT
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface WrittenBookContent {
++
++ @Contract(value = "_, _ -> new", pure = true)
++ static WrittenBookContent.Builder writtenBookContent(final String title, final String author) {
++ return writtenBookContent(Filtered.of(title, null), author);
++ }
++
++ @Contract(value = "_, _ -> new", pure = true)
++ static WrittenBookContent.Builder writtenBookContent(final Filtered title, final String author) {
++ return ItemComponentTypesBridge.bridge().writtenBookContent(title, author);
++ }
++
++ /**
++ * Title of this book.
++ *
++ * @return title
++ */
++ @Contract(pure = true)
++ Filtered title();
++
++ /**
++ * Player name of the author of this book.
++ *
++ * @return author
++ */
++ @Contract(pure = true)
++ String author();
++
++ /**
++ * The number of times this book has been copied (0 = original).
++ *
++ * @return generation
++ */
++ @Contract(pure = true)
++ @IntRange(from = 0, to = 3) int generation();
++
++ /**
++ * Gets the pages of this book.
++ *
++ * @return pages
++ */
++ @Contract(pure = true)
++ @Unmodifiable List> pages();
++
++ /**
++ * If the chat components in this book have already been resolved (entity selectors, scores substituted).
++ * If {@code false}, will be resolved when opened by a player.
++ *
++ * @return resolved
++ */
++ @Contract(pure = true)
++ boolean resolved();
++
++ /**
++ * Builder for {@link WrittenBookContent}.
++ */
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface Builder extends DataComponentBuilder {
++
++ /**
++ * Sets the title of this book.
++ *
++ * @param title the title
++ * @return the builder for chaining
++ * @see #title()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder title(String title);
++
++ /**
++ * Sets the filterable title of this book.
++ *
++ * @param title the title
++ * @return the builder for chaining
++ * @see #title()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder filteredTitle(Filtered title);
++
++ /**
++ * Sets the author of this book.
++ *
++ * @param author the author
++ * @return the builder for chaining
++ * @see #author()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder author(String author);
++
++ /**
++ * Sets the generation of this book.
++ *
++ * @param generation the generation, [0-3]
++ * @return the builder for chaining
++ * @see #generation()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder generation(@IntRange(from = 0, to = 3) int generation);
++
++ /**
++ * Sets if the chat components in this book have already been resolved (entity selectors, scores substituted).
++ * If {@code false}, will be resolved when opened by a player.
++ *
++ * @param resolved resolved
++ * @return the builder for chaining
++ * @see #resolved()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder resolved(boolean resolved);
++
++ /**
++ * Adds a page to this book.
++ *
++ * @param page the page
++ * @return the builder for chaining
++ * @see #pages()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addPage(ComponentLike page);
++
++ /**
++ * Adds pages to this book.
++ *
++ * @param page the pages
++ * @return the builder for chaining
++ * @see #pages()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addPages(List extends ComponentLike> page);
++
++ /**
++ * Adds a filterable page to this book.
++ *
++ * @param page the page
++ * @return the builder for chaining
++ * @see #pages()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addFilteredPage(Filtered extends ComponentLike> page);
++
++ /**
++ * Adds filterable pages to this book.
++ *
++ * @param pages the pages
++ * @return the builder for chaining
++ * @see #pages()
++ */
++ @Contract(value = "_ -> this", mutates = "this")
++ Builder addFilteredPages(List> pages);
++ }
++}
+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..a845ccfc21f101f0632249745bbd8b334f85e72c
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridge.java
+@@ -0,0 +1,32 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import io.papermc.paper.registry.set.RegistryKeySet;
++import java.util.List;
++import java.util.Optional;
++import java.util.ServiceLoader;
++import net.kyori.adventure.key.Key;
++import org.bukkit.potion.PotionEffect;
++import org.bukkit.potion.PotionEffectType;
++import org.jetbrains.annotations.ApiStatus;
++import org.jspecify.annotations.NullMarked;
++
++@NullMarked
++@ApiStatus.Internal
++interface ConsumableTypesBridge {
++
++ Optional BRIDGE = ServiceLoader.load(ConsumableTypesBridge.class).findFirst();
++
++ static ConsumableTypesBridge bridge() {
++ return BRIDGE.orElseThrow();
++ }
++
++ ConsumeEffect.ApplyStatusEffects applyStatusEffects(List effectList, float probability);
++
++ ConsumeEffect.RemoveStatusEffects removeStatusEffects(RegistryKeySet potionEffectTypeTagKey);
++
++ ConsumeEffect.ClearAllStatusEffects clearAllStatusEffects();
++
++ ConsumeEffect.PlaySound playSoundEffect(Key sound);
++
++ ConsumeEffect.TeleportRandomly 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..ff1a14e19c21dd22f249503a0b738f190a75aca0
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumeEffect.java
+@@ -0,0 +1,150 @@
++package io.papermc.paper.datacomponent.item.consumable;
++
++import io.papermc.paper.registry.set.RegistryKeySet;
++import net.kyori.adventure.key.Key;
++import org.bukkit.potion.PotionEffect;
++import org.bukkit.potion.PotionEffectType;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++import java.util.List;
++
++/**
++ * Effect that occurs when consuming an item.
++ */
++@NullMarked
++@ApiStatus.Experimental
++@ApiStatus.NonExtendable
++public interface 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 TeleportRandomly teleportRandomlyEffect(final float diameter) {
++ return ConsumableTypesBridge.bridge().teleportRandomlyEffect(diameter);
++ }
++
++ /**
++ * 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 RemoveStatusEffects removeEffects(final RegistryKeySet key) {
++ return ConsumableTypesBridge.bridge().removeStatusEffects(key);
++ }
++
++ /**
++ * 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 PlaySound playSoundConsumeEffect(final Key key) {
++ return ConsumableTypesBridge.bridge().playSoundEffect(key);
++ }
++
++ /**
++ * Creates a consume effect that clears all status effects.
++ *
++ * @return effect instance
++ */
++ @Contract(value = "-> new", pure = true)
++ static ClearAllStatusEffects clearAllStatusEffects() {
++ return ConsumableTypesBridge.bridge().clearAllStatusEffects();
++ }
++
++ /**
++ * 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 ApplyStatusEffects applyStatusEffects(final List effects, final float probability) {
++ return ConsumableTypesBridge.bridge().applyStatusEffects(effects, probability);
++ }
++
++ @NullMarked
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface TeleportRandomly extends ConsumeEffect {
++
++ /**
++ * The max range that the entity can be teleported to.
++ *
++ * @return teleportation diameter
++ */
++ float diameter();
++ }
++
++ /**
++ * Represents a consumable effect that removes status effects on consumption
++ */
++ @NullMarked
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface RemoveStatusEffects extends ConsumeEffect {
++
++ /**
++ * Potion effects to remove
++ *
++ * @return effects
++ */
++ RegistryKeySet removeEffects();
++ }
++
++ /**
++ * Represents a consumable effect that plays a sound on consumption.
++ */
++ @NullMarked
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface PlaySound extends ConsumeEffect {
++
++ /**
++ * Sound effect to play in the world
++ *
++ * @return sound effect
++ */
++ Key sound();
++ }
++
++ /**
++ * Represents a consumable effect that clears all effects on consumption.
++ */
++ @NullMarked
++ interface ClearAllStatusEffects extends ConsumeEffect {
++
++ }
++
++ /**
++ * Represents a consumable effect that applies effects based on a probability on consumption.
++ */
++ @NullMarked
++ @ApiStatus.Experimental
++ @ApiStatus.NonExtendable
++ interface ApplyStatusEffects extends ConsumeEffect {
++
++ /**
++ * Effect instances to grant
++ *
++ * @return effect
++ */
++ List 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/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/item/MapPostProcessing.java b/src/main/java/io/papermc/paper/item/MapPostProcessing.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..5843768d0be2ae4a0219636ed7640727808da567
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/item/MapPostProcessing.java
+@@ -0,0 +1,6 @@
++package io.papermc.paper.item;
++
++public enum MapPostProcessing {
++ LOCK,
++ SCALE
++}
+diff --git a/src/main/java/io/papermc/paper/registry/RegistryKey.java b/src/main/java/io/papermc/paper/registry/RegistryKey.java
+index 9b39e33514b15a9d07104e2ad826d0da11f569d6..e5319bdb9f75358b8bb0ac35373125a7d94edfa6 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 @@
+ package io.papermc.paper.registry;
+
++import io.papermc.paper.datacomponent.DataComponentType;
+ import net.kyori.adventure.key.Keyed;
+ import org.bukkit.Art;
+ import org.bukkit.Fluid;
+@@ -124,6 +125,11 @@ public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl {
+ * @see io.papermc.paper.registry.keys.SoundEventKeys
+ */
+ RegistryKey SOUND_EVENT = create("sound_event");
++ /**
++ * Built-in registry for data component types.
++ *
++ */
++ RegistryKey DATA_COMPONENT_TYPE = create("data_component_type");
+
+
+
+diff --git a/src/main/java/io/papermc/paper/text/Filtered.java b/src/main/java/io/papermc/paper/text/Filtered.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9e892621354c784632204559f9fdf0827b3bc4f1
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/text/Filtered.java
+@@ -0,0 +1,32 @@
++package io.papermc.paper.text;
++
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jspecify.annotations.NullMarked;
++import org.jspecify.annotations.Nullable;
++
++/**
++ * Denotes that this type is filterable by the client, and may be shown differently
++ * depending on the player's set configuration.
++ *
++ * @param type of value
++ */
++@ApiStatus.Experimental
++@NullMarked
++public interface Filtered {
++
++ @Contract(value = "_, _ -> new", pure = true)
++ static Filtered of(final T raw, final @Nullable T filtered) {
++ @ApiStatus.Internal
++ record Instance(T raw, @Nullable T filtered) implements Filtered {}
++
++ return new Instance<>(raw, filtered);
++ }
++
++ @Contract(pure = true)
++ T raw();
++
++ @Contract(pure = true)
++ @Nullable
++ T filtered();
++}
+diff --git a/src/main/java/org/bukkit/Material.java b/src/main/java/org/bukkit/Material.java
+index 615eb24ffdd8f6d55ccd4f21760b809c1098bc68..c7ce8fa1ff9feda66d5a4e497112a24ff51c9d2b 100644
+--- a/src/main/java/org/bukkit/Material.java
++++ b/src/main/java/org/bukkit/Material.java
+@@ -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
+ //
+- AIR(9648, 0),
++ AIR(9648, 64), // Paper - air stacks to 64
+ STONE(22948),
+ GRANITE(21091),
+ POLISHED_GRANITE(5477),
+@@ -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() {
+ 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() {
+ return blockType.get();
+ }
++
++ // Paper start - data component API
++ /**
++ * Gets the default value of the data component type for this item type.
++ *
++ * @param type the data component type
++ * @param the value type
++ * @return the default value or {@code null} if there is none
++ * @see #hasDefaultData(io.papermc.paper.datacomponent.DataComponentType) for DataComponentType.NonValued
++ * @throws IllegalArgumentException if {@link #isItem()} is {@code false}
++ */
++ public @Nullable T getDefaultData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued type) {
++ Preconditions.checkArgument(this.asItemType() != null);
++ return this.asItemType().getDefaultData(type);
++ }
++
++ /**
++ * Checks if the data component type has a default value for this item type.
++ *
++ * @param type the data component type
++ * @return {@code true} if there is a default value
++ * @throws IllegalArgumentException if {@link #isItem()} is {@code false}
++ */
++ public boolean hasDefaultData(final io.papermc.paper.datacomponent.@NotNull DataComponentType type) {
++ Preconditions.checkArgument(this.asItemType() != null);
++ return this.asItemType().hasDefaultData(type);
++ }
++
++ /**
++ * Gets the default data component types for this item type.
++ *
++ * @return an immutable set of data component types
++ * @throws IllegalArgumentException if {@link #isItem()} is {@code false}
++ */
++ public java.util.@org.jetbrains.annotations.Unmodifiable @NotNull Set getDefaultDataTypes() {
++ Preconditions.checkArgument(this.asItemType() != null);
++ return this.asItemType().getDefaultDataTypes();
++ }
++ // Paper end - data component API
+ }
+diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java
+index 7cf7c6d05aa6cbf3f0c8612831404552c6a7b84a..c60e31425efd7b863941f5538faef6c0552290ae 100644
+--- a/src/main/java/org/bukkit/Registry.java
++++ b/src/main/java/org/bukkit/Registry.java
+@@ -376,6 +376,7 @@ public interface Registry extends Iterable {
+ */
+ Registry POTION_EFFECT_TYPE = EFFECT;
+ // Paper end - potion effect type registry
++ Registry 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/block/BlockType.java b/src/main/java/org/bukkit/block/BlockType.java
+index ed534fe4983873a2d5f623f0d9d5e3ce254615eb..f019d490794b49d21057820bab047e2f909934a1 100644
+--- a/src/main/java/org/bukkit/block/BlockType.java
++++ b/src/main/java/org/bukkit/block/BlockType.java
+@@ -128,7 +128,7 @@ import org.jetbrains.annotations.Nullable;
+ * official replacement for the aforementioned enum. Entirely incompatible
+ * changes may occur. Do not use this API in plugins.
+ */
+-@ApiStatus.Internal
++@org.jetbrains.annotations.ApiStatus.Experimental // Paper - data component API - already required for data component API
+ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - add translatable & feature flag API
+
+ /**
+diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
+index b59222b8c262545d100a9fd28b3bf1d2a4cf4eb0..6e4ca7d95953a25c0aaafd35e54ef9254a1b5f0b 100644
+--- a/src/main/java/org/bukkit/inventory/ItemStack.java
++++ b/src/main/java/org/bukkit/inventory/ItemStack.java
+@@ -1137,4 +1137,185 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
+ return Bukkit.getUnsafe().computeTooltipLines(this, tooltipContext, player);
+ }
+ // Paper end - expose itemstack tooltip lines
++
++ // Paper start - data component API
++ /**
++ * Gets the value for the data component type on this stack.
++ *
++ * @param type the data component type
++ * @param the value type
++ * @return the value for the data component type, or {@code null} if not set or marked as removed
++ * @see #hasData(io.papermc.paper.datacomponent.DataComponentType) for DataComponentType.NonValued
++ */
++ @org.jetbrains.annotations.Contract(pure = true)
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ public @Nullable T getData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued type) {
++ return this.craftDelegate.getData(type);
++ }
++
++ /**
++ * Gets the value for the data component type on this stack with
++ * a fallback value.
++ *
++ * @param type the data component type
++ * @param fallback the fallback value if the value isn't present
++ * @param the value type
++ * @return the value for the data component type or the fallback value
++ */
++ @Utility
++ @org.jetbrains.annotations.Contract(value = "_, !null -> !null", pure = true)
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ public @Nullable T getDataOrDefault(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued extends T> type, final @Nullable T fallback) {
++ final T object = this.getData(type);
++ return object != null ? object : fallback;
++ }
++
++ /**
++ * Checks if the data component type is set on the itemstack.
++ *
++ * @param type the data component type
++ * @return {@code true} if set, {@code false} otherwise
++ */
++ @org.jetbrains.annotations.Contract(pure = true)
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ public boolean hasData(final io.papermc.paper.datacomponent.@NotNull DataComponentType type) {
++ return this.craftDelegate.hasData(type);
++ }
++
++ /**
++ * Gets all the data component types set on this stack.
++ *
++ * @return an immutable set of data component types
++ */
++ @org.jetbrains.annotations.Contract("-> new")
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ public java.util.@org.jetbrains.annotations.Unmodifiable Set getDataTypes() {
++ return this.craftDelegate.getDataTypes();
++ }
++
++ /**
++ * 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
++ * as removed, use {@link #unsetData(io.papermc.paper.datacomponent.DataComponentType)}.
++ *
++ * @param type the data component type
++ * @param valueBuilder value builder
++ * @param value type
++ */
++ @Utility
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ public void setData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued type, final @NotNull io.papermc.paper.datacomponent.DataComponentBuilder valueBuilder) {
++ this.setData(type, valueBuilder.build());
++ }
++
++ // /**
++ // * 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.
++ // *
++ // * 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)}.
++ // *
++ // * @param 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 void editData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued 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
++ * as removed, use {@link #unsetData(io.papermc.paper.datacomponent.DataComponentType)}.
++ *
++ * @param type the data component type
++ * @param value value to set
++ * @param value type
++ */
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ public void setData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued type, final @NotNull T value) {
++ this.craftDelegate.setData(type, value);
++ }
++
++ /**
++ * Marks this non-valued data component type as present in this itemstack.
++ *
++ * @param type the data component type
++ */
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ public void setData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull NonValued type) {
++ this.craftDelegate.setData(type);
++ }
++
++ /**
++ * Marks this data component as removed for this itemstack.
++ *
++ * @param type the data component type
++ */
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ public void unsetData(final io.papermc.paper.datacomponent.@NotNull DataComponentType type) {
++ this.craftDelegate.unsetData(type);
++ }
++
++ /**
++ * Resets the value of this component to be the default
++ * value for the item type from {@link Material#getDefaultData(io.papermc.paper.datacomponent.DataComponentType.Valued)}.
++ *
++ * @param type the data component type
++ */
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ public void resetData(final io.papermc.paper.datacomponent.@NotNull DataComponentType type) {
++ this.craftDelegate.resetData(type);
++ }
++
++ /**
++ * Checks if the data component type is overridden from the default for the
++ * item type.
++ *
++ * @param type the data component type
++ * @return {@code true} if the data type is overridden
++ */
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ public boolean isDataOverridden(final io.papermc.paper.datacomponent.@NotNull DataComponentType type) {
++ return this.craftDelegate.isDataOverridden(type);
++ }
++
++ /**
++ * Checks if this itemstack matches another given itemstack excluding the provided components.
++ * This is useful if you are wanting to ignore certain properties of itemstacks, such as durability.
++ *
++ * @param item the item to compare
++ * @param excludeTypes the data component types to ignore
++ * @return {@code true} if the provided item is equal, ignoring the provided components
++ */
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ public boolean matchesWithoutData(final @NotNull ItemStack item, final @NotNull java.util.Set excludeTypes) {
++ return this.matchesWithoutData(item, excludeTypes, false);
++ }
++
++ /**
++ * Checks if this itemstack matches another given itemstack excluding the provided components.
++ * This is useful if you are wanting to ignore certain properties of itemstacks, such as durability.
++ *
++ * @param item the item to compare
++ * @param excludeTypes the data component types to ignore
++ * @param ignoreCount ignore the count of the item
++ * @return {@code true} if the provided item is equal, ignoring the provided components
++ */
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ public boolean matchesWithoutData(final @NotNull ItemStack item, final @NotNull java.util.Set excludeTypes, final boolean ignoreCount) {
++ return this.craftDelegate.matchesWithoutData(item, excludeTypes, ignoreCount);
++ }
++ // 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 72803c00e4af576f286d2af34bf300ee554a7f3c..2a3c4f055d0e4ef009caed95152570660ab100d5 100644
+--- a/src/main/java/org/bukkit/inventory/ItemType.java
++++ b/src/main/java/org/bukkit/inventory/ItemType.java
+@@ -2483,4 +2483,33 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
+ */
+ @Nullable ItemRarity getItemRarity();
+ // Paper end - expand ItemRarity API
++ // Paper start - data component API
++ /**
++ * Gets the default value of the data component type for this item type.
++ *
++ * @param type the data component type
++ * @param the value type
++ * @return the default value or {@code null} if there is none
++ * @see #hasDefaultData(io.papermc.paper.datacomponent.DataComponentType) for DataComponentType.NonValued
++ */
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ @Nullable T getDefaultData(io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued type);
++
++ /**
++ * Checks if the data component type has a default value for this item type.
++ *
++ * @param type the data component type
++ * @return {@code true} if there is a default value
++ */
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ boolean hasDefaultData(io.papermc.paper.datacomponent.@NotNull DataComponentType type);
++
++ /**
++ * Gets the default data component types for this item type.
++ *
++ * @return an immutable set of data component types
++ */
++ @org.jetbrains.annotations.ApiStatus.Experimental
++ java.util.@org.jetbrains.annotations.Unmodifiable @NotNull Set getDefaultDataTypes();
++ // Paper end - data component API
+ }
diff --git a/patches/api/0495-Optimise-color-distance-check-in-MapPalette-by-remov.patch b/patches/api/0496-Optimise-color-distance-check-in-MapPalette-by-remov.patch
similarity index 100%
rename from patches/api/0495-Optimise-color-distance-check-in-MapPalette-by-remov.patch
rename to patches/api/0496-Optimise-color-distance-check-in-MapPalette-by-remov.patch
diff --git a/patches/server/0009-MC-Utils.patch b/patches/server/0009-MC-Utils.patch
index 4f57aa7015..108f5992a4 100644
--- a/patches/server/0009-MC-Utils.patch
+++ b/patches/server/0009-MC-Utils.patch
@@ -4691,16 +4691,21 @@ index 0000000000000000000000000000000000000000..197224e31175252d8438a8df585bbb65
+}
diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java
new file mode 100644
-index 0000000000000000000000000000000000000000..422bc104e5bdd4ae786b14d97eb779dc76bfad69
+index 0000000000000000000000000000000000000000..e85e544506b4c762503a1cb490e6c0f5b1d563f4
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/MCUtil.java
-@@ -0,0 +1,190 @@
+@@ -0,0 +1,220 @@
+package io.papermc.paper.util;
+
++import com.google.common.collect.Collections2;
++import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
++import io.papermc.paper.adventure.PaperAdventure;
+import io.papermc.paper.math.BlockPosition;
+import io.papermc.paper.math.FinePosition;
+import io.papermc.paper.math.Position;
++import java.util.Collection;
++import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
@@ -4709,11 +4714,17 @@ index 0000000000000000000000000000000000000000..422bc104e5bdd4ae786b14d97eb779dc
+import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
++import java.util.function.Function;
+import java.util.function.Supplier;
++import net.kyori.adventure.key.Key;
+import net.minecraft.core.BlockPos;
++import net.minecraft.core.Holder;
+import net.minecraft.core.Vec3i;
++import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.resources.ResourceKey;
++import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.MinecraftServer;
++import net.minecraft.sounds.SoundEvent;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.Vec3;
@@ -4884,6 +4895,25 @@ index 0000000000000000000000000000000000000000..422bc104e5bdd4ae786b14d97eb779dc
+ public static NamespacedKey fromResourceKey(final ResourceKey> key) {
+ return CraftNamespacedKey.fromMinecraft(key.location());
+ }
++
++ public static Holder keyToSound(Key key) {
++ ResourceLocation soundId = PaperAdventure.asVanilla(key);
++ return BuiltInRegistries.SOUND_EVENT.wrapAsHolder(BuiltInRegistries.SOUND_EVENT.getOptional(soundId).orElse(SoundEvent.createVariableRangeEvent(soundId)));
++ }
++
++ public static List transformUnmodifiable(final List extends M> nms, final Function super M, ? extends A> converter) {
++ return Collections.unmodifiableList(Lists.transform(nms, converter::apply));
++ }
++
++ public static Collection transformUnmodifiable(final Collection extends M> nms, final Function super M, ? extends A> converter) {
++ return Collections.unmodifiableCollection(Collections2.transform(nms, converter::apply));
++ }
++
++ public static > void addAndConvert(final C target, final Collection toAdd, final Function super A, ? extends M> converter) {
++ for (final A value : toAdd) {
++ target.add(converter.apply(value));
++ }
++ }
+}
diff --git a/src/main/java/io/papermc/paper/util/StackWalkerUtil.java b/src/main/java/io/papermc/paper/util/StackWalkerUtil.java
new file mode 100644
@@ -6309,6 +6339,28 @@ index f6e6f0ddef6693c58f28b89cf3df005a8d47e08d..101eea3452c9e387e770b716543c3a4f
public static net.minecraft.world.item.ItemStack asNMSCopy(ItemStack original) {
if (original instanceof CraftItemStack) {
CraftItemStack stack = (CraftItemStack) original;
+diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftLocation.java b/src/main/java/org/bukkit/craftbukkit/util/CraftLocation.java
+index 097996d3955ab5126b71f7bff1dd2c62becb5ffd..a8b46ea5e4b6260c2728c67e8651b74fe6356605 100644
+--- a/src/main/java/org/bukkit/craftbukkit/util/CraftLocation.java
++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftLocation.java
+@@ -40,6 +40,17 @@ public final class CraftLocation {
+ return new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
+ }
+
++ // Paper start
++ public static net.minecraft.core.GlobalPos toGlobalPos(Location location) {
++ return net.minecraft.core.GlobalPos.of(((org.bukkit.craftbukkit.CraftWorld) location.getWorld()).getHandle().dimension(), toBlockPosition(location));
++ }
++
++ public static Location fromGlobalPos(net.minecraft.core.GlobalPos globalPos) {
++ BlockPos pos = globalPos.pos();
++ return new org.bukkit.Location(net.minecraft.server.MinecraftServer.getServer().getLevel(globalPos.dimension()).getWorld(), pos.getX(), pos.getY(), pos.getZ());
++ }
++ // Paper end
++
+ public static Vec3 toVec3D(Location location) {
+ return new Vec3(location.getX(), location.getY(), location.getZ());
+ }
diff --git a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
index b8a865305cc61954aeebff4a7cd1d1973c5f46ab..e444662ee4d9405eeea7caa41b9cd6b36586d840 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
diff --git a/patches/server/0010-Adventure.patch b/patches/server/0010-Adventure.patch
index b22e85c55b..f7659f6916 100644
--- a/patches/server/0010-Adventure.patch
+++ b/patches/server/0010-Adventure.patch
@@ -1161,10 +1161,10 @@ index 0000000000000000000000000000000000000000..2fd6c3e65354071af71c7d8ebb97b559
+}
diff --git a/src/main/java/io/papermc/paper/adventure/PaperAdventure.java b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java
new file mode 100644
-index 0000000000000000000000000000000000000000..161bc8c577643094d824ea96fb6974c76e5e77f0
+index 0000000000000000000000000000000000000000..610003a668c4a7fe53e3477accc6bafb8479b936
--- /dev/null
+++ b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java
-@@ -0,0 +1,479 @@
+@@ -0,0 +1,483 @@
+package io.papermc.paper.adventure;
+
+import com.mojang.brigadier.StringReader;
@@ -1314,6 +1314,10 @@ index 0000000000000000000000000000000000000000..161bc8c577643094d824ea96fb6974c7
+
+ // Key
+
++ public static Key asAdventure(final ResourceLocation key) {
++ return Key.key(key.getNamespace(), key.getPath());
++ }
++
+ public static ResourceLocation asVanilla(final Key key) {
+ return ResourceLocation.fromNamespaceAndPath(key.namespace(), key.value());
+ }
@@ -2704,7 +2708,7 @@ index 709330ca9caa82a6de71767b3d5c32f97ea1d68b..daf20aa9a83a2583c0c61a4123cc2e52
public boolean logIPs() {
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 32e7c6e6f09e53fe8b5ade22dad8142cd09e0163..4303bde198050cd037f006234d269af406606eff 100644
+index 785c7e11f92610be58b624d252d1858658496af7..99607edee9fba8df87f0525bf0eadee865ab38a4 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -190,6 +190,7 @@ import net.minecraft.world.item.trading.MerchantOffers;
@@ -3639,7 +3643,7 @@ index 8b58884d6cb1088a2fffb36a99bfe4dc568326d1..9a79b948264150d0f7a843a8ddd2ea92
// Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
-index 94004204b6cdbbbf35263faae56e3e06cb6b650c..2e33acc428dbfd3e123dfd6ef90bc020b8a08daf 100644
+index 39126769594a535ebd4bf7052f42e9eda9d3e10a..41ceea1093edbf777f9ebe252114be7f75438420 100644
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
@@ -20,6 +20,12 @@ public class Main {
@@ -5202,7 +5206,7 @@ index c71a4971f127fdfc753306019313ce1a31201120..fd3b12477c30d1eabdbe57ea77902793
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-index 5dea83c1a0de4e5123430bf2f902a78969021167..2e524520308dda7ce7df98d91c89a9cfe7542862 100644
+index 2d68086ef09b5f2d6b3d3e6200e621c23747b25a..f3789ea732b17a311d8203a58a97d11370ec7863 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
@@ -1097,6 +1097,18 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
diff --git a/patches/server/1036-DataComponent-API.patch b/patches/server/1036-DataComponent-API.patch
new file mode 100644
index 0000000000..744404bca8
--- /dev/null
+++ b/patches/server/1036-DataComponent-API.patch
@@ -0,0 +1,4804 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
+Date: Sun, 28 Apr 2024 19:53:01 -0400
+Subject: [PATCH] DataComponent API
+
+Exposes the data component logic used by vanilla ItemStack to API
+consumers as a version-specific API.
+The types and methods introduced by this patch do not follow the general
+API deprecation contracts and will be adapted to each new minecraft
+release without backwards compatibility measures.
+
+== AT ==
+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/datacomponent/ComponentAdapter.java b/src/main/java/io/papermc/paper/datacomponent/ComponentAdapter.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4a49f65cae1354afbcd4afda07581790e06094be
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/ComponentAdapter.java
+@@ -0,0 +1,36 @@
++package io.papermc.paper.datacomponent;
++
++import java.util.function.Function;
++import net.minecraft.core.component.DataComponentType;
++import net.minecraft.core.registries.BuiltInRegistries;
++import net.minecraft.util.NullOps;
++import net.minecraft.util.Unit;
++import org.bukkit.craftbukkit.CraftRegistry;
++
++public record ComponentAdapter(
++ DataComponentType type,
++ Function apiToVanilla,
++ Function vanillaToApi,
++ boolean codecValidation
++) {
++ static final Function API_TO_UNIT_CONVERTER = $ -> Unit.INSTANCE;
++
++ public boolean isValued() {
++ return this.apiToVanilla != API_TO_UNIT_CONVERTER;
++ }
++
++ public NMS toVanilla(final API value) {
++ final NMS nms = this.apiToVanilla.apply(value);
++ if (this.codecValidation) {
++ this.type.codecOrThrow().encodeStart(CraftRegistry.getMinecraftRegistry().createSerializationContext(NullOps.INSTANCE), nms).ifError(error -> {
++ throw new IllegalArgumentException("Failed to encode data component %s (%s)".formatted(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(this.type), error.message()));
++ });
++ }
++
++ return nms;
++ }
++
++ public API fromVanilla(final NMS value) {
++ return this.vanillaToApi.apply(value);
++ }
++}
+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..c11de1f077c51483c61af6492f998781df866b88
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/ComponentAdapters.java
+@@ -0,0 +1,168 @@
++package io.papermc.paper.datacomponent;
++
++import io.papermc.paper.adventure.PaperAdventure;
++import io.papermc.paper.datacomponent.item.PaperBannerPatternLayers;
++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;
++import io.papermc.paper.datacomponent.item.PaperItemArmorTrim;
++import io.papermc.paper.datacomponent.item.PaperItemAttributeModifiers;
++import io.papermc.paper.datacomponent.item.PaperItemContainerContents;
++import io.papermc.paper.datacomponent.item.PaperItemEnchantments;
++import io.papermc.paper.datacomponent.item.PaperItemLore;
++import io.papermc.paper.datacomponent.item.PaperItemTool;
++import io.papermc.paper.datacomponent.item.PaperJukeboxPlayable;
++import io.papermc.paper.datacomponent.item.PaperLodestoneTracker;
++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 io.papermc.paper.registry.PaperRegistries;
++import java.util.HashMap;
++import java.util.Map;
++import java.util.function.Function;
++import net.minecraft.core.component.DataComponentType;
++import net.minecraft.core.component.DataComponents;
++import net.minecraft.core.registries.BuiltInRegistries;
++import net.minecraft.resources.ResourceKey;
++import net.minecraft.util.Unit;
++import net.minecraft.world.item.Rarity;
++import net.minecraft.world.item.component.MapPostProcessing;
++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 static io.papermc.paper.datacomponent.ComponentUtils.transform;
++
++public final class ComponentAdapters {
++
++ static final Function UNIT_TO_API_CONVERTER = $ -> {
++ throw new UnsupportedOperationException("Cannot convert the Unit type to an API value");
++ };
++
++ static final Map>, ComponentAdapter, ?>> ADAPTERS = new HashMap<>();
++
++ public static void bootstrap() {
++ registerIdentity(DataComponents.MAX_STACK_SIZE);
++ registerIdentity(DataComponents.MAX_DAMAGE);
++ registerIdentity(DataComponents.DAMAGE);
++ 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);
++ register(DataComponents.CAN_PLACE_ON, PaperItemAdventurePredicate::new);
++ register(DataComponents.CAN_BREAK, PaperItemAdventurePredicate::new);
++ register(DataComponents.ATTRIBUTE_MODIFIERS, PaperItemAttributeModifiers::new);
++ register(DataComponents.CUSTOM_MODEL_DATA, PaperCustomModelData::new);
++ registerUntyped(DataComponents.HIDE_ADDITIONAL_TOOLTIP);
++ registerUntyped(DataComponents.HIDE_TOOLTIP);
++ registerIdentity(DataComponents.REPAIR_COST);
++ // registerUntyped(DataComponents.CREATIVE_SLOT_LOCK);
++ registerIdentity(DataComponents.ENCHANTMENT_GLINT_OVERRIDE);
++ registerUntyped(DataComponents.INTANGIBLE_PROJECTILE);
++ register(DataComponents.FOOD, PaperFoodProperties::new);
++ 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);
++ register(DataComponents.MAP_ID, PaperMapId::new);
++ register(DataComponents.MAP_DECORATIONS, PaperMapDecorations::new);
++ register(DataComponents.MAP_POST_PROCESSING, nms -> io.papermc.paper.item.MapPostProcessing.valueOf(nms.name()), api -> MapPostProcessing.valueOf(api.name()));
++ register(DataComponents.CHARGED_PROJECTILES, PaperChargedProjectiles::new);
++ register(DataComponents.BUNDLE_CONTENTS, PaperBundleContents::new);
++ register(DataComponents.POTION_CONTENTS, PaperPotionContents::new);
++ register(DataComponents.SUSPICIOUS_STEW_EFFECTS, PaperSuspiciousStewEffects::new);
++ register(DataComponents.WRITTEN_BOOK_CONTENT, PaperWrittenBookContent::new);
++ register(DataComponents.WRITABLE_BOOK_CONTENT, PaperWritableBookContent::new);
++ register(DataComponents.TRIM, PaperItemArmorTrim::new);
++ // debug stick state
++ // entity data
++ // bucket entity data
++ // block entity data
++ register(DataComponents.INSTRUMENT, CraftMusicInstrument::minecraftHolderToBukkit, CraftMusicInstrument::bukkitToMinecraftHolder);
++ register(DataComponents.OMINOUS_BOTTLE_AMPLIFIER, PaperOminousBottleAmplifier::new);
++ register(DataComponents.JUKEBOX_PLAYABLE, PaperJukeboxPlayable::new);
++ 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);
++ register(DataComponents.PROFILE, PaperResolvableProfile::new);
++ register(DataComponents.NOTE_BLOCK_SOUND, PaperAdventure::asAdventure, PaperAdventure::asVanilla);
++ register(DataComponents.BANNER_PATTERNS, PaperBannerPatternLayers::new);
++ register(DataComponents.BASE_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData()));
++ register(DataComponents.POT_DECORATIONS, PaperPotDecorations::new);
++ register(DataComponents.CONTAINER, PaperItemContainerContents::new);
++ register(DataComponents.BLOCK_STATE, PaperBlockItemDataProperties::new);
++ // bees
++ // register(DataComponents.LOCK, PaperLockCode::new);
++ register(DataComponents.CONTAINER_LOOT, PaperSeededContainerLoot::new);
++
++ // TODO: REMOVE THIS... we want to build the PR... so lets just make things UNTYPED!
++ for (final Map.Entry>, DataComponentType>> componentType : BuiltInRegistries.DATA_COMPONENT_TYPE.entrySet()) {
++ if (!ADAPTERS.containsKey(componentType.getKey())) {
++ registerUntyped((DataComponentType) componentType.getValue());
++ }
++ }
++ }
++
++ public static void registerUntyped(final DataComponentType type) {
++ registerInternal(type, UNIT_TO_API_CONVERTER, ComponentAdapter.API_TO_UNIT_CONVERTER, false);
++ }
++
++ private static void registerIdentity(final DataComponentType type) {
++ registerInternal(type, Function.identity(), Function.identity(), true);
++ }
++
++ private static > void register(final DataComponentType type, final Function vanillaToApi) {
++ registerInternal(type, vanillaToApi, Handleable::getHandle, false);
++ }
++
++ private static void register(final DataComponentType type, final Function vanillaToApi, final Function apiToVanilla) {
++ registerInternal(type, vanillaToApi, apiToVanilla, false);
++ }
++
++ private static void registerInternal(final DataComponentType type, final Function vanillaToApi, final Function apiToVanilla, final boolean codecValidation) {
++ final ResourceKey> key = BuiltInRegistries.DATA_COMPONENT_TYPE.getResourceKey(type).orElseThrow();
++ if (ADAPTERS.containsKey(key)) {
++ throw new IllegalStateException("Duplicate adapter registration for " + key);
++ }
++ ADAPTERS.put(key, new ComponentAdapter<>(type, apiToVanilla, vanillaToApi, codecValidation && !type.isTransient()));
++ }
++}
+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..c95ffef54d7149cd8bb220533dddade515e48c8c
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/datacomponent/ComponentUtils.java
+@@ -0,0 +1,39 @@
++package io.papermc.paper.datacomponent;
++
++import com.google.common.collect.Collections2;
++import com.google.common.collect.Lists;
++import io.papermc.paper.adventure.PaperAdventure;
++import java.util.Collection;
++import java.util.Collections;
++import java.util.List;
++import java.util.function.Function;
++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;
++
++public final class ComponentUtils {
++
++ private ComponentUtils() {
++ }
++
++ public static Holder keyToSound(Key key) {
++ ResourceLocation soundId = PaperAdventure.asVanilla(key);
++ return BuiltInRegistries.SOUND_EVENT.wrapAsHolder(BuiltInRegistries.SOUND_EVENT.getOptional(soundId).orElse(SoundEvent.createVariableRangeEvent(soundId)));
++ }
++
++ public static