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");
+ public static final DataComponentType.Valued JUKEBOX_PLAYABLE = valued("jukebox_playable");
+ /**
+ * List of recipes that should be unlocked when using the Knowledge Book item.
+ */
+ 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..785b5db96bb5a0584647f2ed41fcd882ab6b3250
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/BannerPatternLayers.java
@@ -0,0 +1,65 @@
+package io.papermc.paper.datacomponent.item;
+
+import io.papermc.paper.datacomponent.DataComponentBuilder;
+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..696a4cebb902c4afd2d2f78bb8d9261bd09d7cb1
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/BundleContents.java
@@ -0,0 +1,65 @@
+package io.papermc.paper.datacomponent.item;
+
+import io.papermc.paper.datacomponent.DataComponentBuilder;
+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(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..aac079e1d8056ec02741386a1bf2e7adf592e5bd
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/ChargedProjectiles.java
@@ -0,0 +1,65 @@
+package io.papermc.paper.datacomponent.item;
+
+import io.papermc.paper.datacomponent.DataComponentBuilder;
+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..8c88bbbeef179e6c6666d07c8b28157ee1e84a2b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/Consumable.java
@@ -0,0 +1,69 @@
+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.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..334a87c1984cbddd835f25bbe04e03579cefc88d
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/CustomModelData.java
@@ -0,0 +1,145 @@
+package io.papermc.paper.datacomponent.item;
+
+import java.util.List;
+import io.papermc.paper.datacomponent.DataComponentBuilder;
+import org.bukkit.Color;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.Unmodifiable;
+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.Builder customModelData() {
+ return ItemComponentTypesBridge.bridge().customModelData();
+ }
+
+ /**
+ * Gets the custom model data float values.
+ *
+ * @return the float values
+ */
+ @Contract(pure = true)
+ @Unmodifiable List floats();
+
+ /**
+ * Gets the custom model data boolean values.
+ *
+ * @return the boolean values
+ */
+ @Contract(pure = true)
+ @Unmodifiable List flags();
+
+ /**
+ * Gets the custom model data string values.
+ *
+ * @return the string values
+ */
+ @Contract(pure = true)
+ @Unmodifiable List strings();
+
+ /**
+ * Gets the custom model data color values.
+ *
+ * @return the color values
+ */
+ @Contract(pure = true)
+ @Unmodifiable List colors();
+
+ /**
+ * Builder for {@link CustomModelData}.
+ */
+ @ApiStatus.Experimental
+ @ApiStatus.NonExtendable
+ interface Builder extends DataComponentBuilder {
+
+ /**
+ * Adds a float to this custom model data.
+ *
+ * @param f the float
+ * @return the builder for chaining
+ * @see #floats()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ CustomModelData.Builder addFloat(float f);
+
+ /**
+ * Adds multiple floats to this custom model data.
+ *
+ * @param floats the floats
+ * @return the builder for chaining
+ * @see #floats()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ CustomModelData.Builder addFloats(List floats);
+
+ /**
+ * Adds a flag to this custom model data.
+ *
+ * @param flag the flag
+ * @return the builder for chaining
+ * @see #flags()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ CustomModelData.Builder addFlag(boolean flag);
+
+ /**
+ * Adds multiple flags to this custom model data.
+ *
+ * @param flags the flags
+ * @return the builder for chaining
+ * @see #flags()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ CustomModelData.Builder addFlags(List flags);
+
+ /**
+ * Adds a string to this custom model data.
+ *
+ * @param string the string
+ * @return the builder for chaining
+ * @see #strings()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ CustomModelData.Builder addString(String string);
+
+ /**
+ * Adds multiple strings to this custom model data.
+ *
+ * @param strings the strings
+ * @return the builder for chaining
+ * @see #strings()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ CustomModelData.Builder addStrings(List strings);
+
+ /**
+ * Adds a color to this custom model data.
+ *
+ * @param color the color
+ * @return the builder for chaining
+ * @see #colors()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ CustomModelData.Builder addColor(Color color);
+
+ /**
+ * Adds multiple colors to this custom model data.
+ *
+ * @param colors the colors
+ * @return the builder for chaining
+ * @see #colors()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ CustomModelData.Builder addColors(List colors);
+ }
+}
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..8a42550bb52d00d9b5df1a64e997e0762bffde59
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/DeathProtection.java
@@ -0,0 +1,46 @@
+package io.papermc.paper.datacomponent.item;
+
+import io.papermc.paper.datacomponent.DataComponentBuilder;
+import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect;
+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..a128348247d8845321d3fecebaa09a5175a923cc
--- /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.
+ *
+ * @return the value
+ * @see Minecraft Wiki
+ */
+ @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..d21a1cd4002a972c6e56cdf4c1e5f86414c56c12
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/Equippable.java
@@ -0,0 +1,169 @@
+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 asset id if present.
+ *
+ * @return the asset id or null
+ */
+ @Contract(pure = true)
+ @Nullable Key assetId();
+
+ /**
+ * 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 sound the equip sound key
+ * @return the builder for chaining
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ Builder equipSound(Key sound);
+
+ /**
+ * Sets the asset id for this item.
+ *
+ * @param assetId the asset id, nullable
+ * @return the builder for chaining
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ Builder assetId(@Nullable Key assetId);
+
+ /**
+ * 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..2b94017d0310d023556bdd7b9ab1eae0261669c6
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/FoodProperties.java
@@ -0,0 +1,86 @@
+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..56a3e678c6658dd617da4974d9392006875aef0c
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemAttributeModifiers.java
@@ -0,0 +1,97 @@
+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..12df050d35a8470d9a111c98d9c0e22a52b684d1
--- /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.Builder customModelData();
+
+ 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..3b00e34a1d6aeec694c712d7a3a323da938f270b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemContainerContents.java
@@ -0,0 +1,62 @@
+package io.papermc.paper.datacomponent.item;
+
+import io.papermc.paper.datacomponent.DataComponentBuilder;
+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..15154d7f9f861991134eb5a5210f7244db3216eb
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PotDecorations.java
@@ -0,0 +1,108 @@
+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;
+
+/**
+ * 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..1583d408336aec5d0c6eb7124b2cecc32c4fda56
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java
@@ -0,0 +1,118 @@
+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
+ * @apiNote alpha channel of the color is supported only for Tipped Arrow
+ * @see #customColor()
+ */
+ @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
+ * @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
+ * @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..1307221216d2b38ec5daee4d798d7fcabef7a2e2
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/ShownInTooltip.java
@@ -0,0 +1,55 @@
+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..9a740ef7da967e865a801367cf179eead0632d66
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/SuspiciousStewEffects.java
@@ -0,0 +1,66 @@
+package io.papermc.paper.datacomponent.item;
+
+import io.papermc.paper.datacomponent.DataComponentBuilder;
+import io.papermc.paper.potion.SuspiciousEffectEntry;
+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..aaecc3c954ea7c6b70c1effea65a34901e1a0924
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/UseRemainder.java
@@ -0,0 +1,29 @@
+package io.papermc.paper.datacomponent.item;
+
+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..e632221f36d0f355b4750071c7d8ccdd84b040a9
--- /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 effectTypes);
+
+ 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..87537e431e505e498f9c5f86cc3401d39ebdb2ac
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumeEffect.java
@@ -0,0 +1,147 @@
+package io.papermc.paper.datacomponent.item.consumable;
+
+import io.papermc.paper.registry.set.RegistryKeySet;
+import java.util.List;
+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;
+
+/**
+ * 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);
+ }
+
+ @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
+ */
+ @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.
+ */
+ @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.
+ */
+ @ApiStatus.Experimental
+ @ApiStatus.NonExtendable
+ interface ClearAllStatusEffects extends ConsumeEffect {
+
+ }
+
+ /**
+ * Represents a consumable effect that applies effects based on a probability on consumption.
+ */
+ @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 b28ab97ff80c9e7af85d8830f26fd0f252082541..e89edabd36a6755912694d8a8700da4ebe5c5829 100644
--- a/src/main/java/org/bukkit/Material.java
+++ b/src/main/java/org/bukkit/Material.java
@@ -138,7 +138,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),
@@ -5755,6 +5755,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();
}
@@ -5767,7 +5768,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 87907918c42b11780b285b6d82e7297628a07376..d55c33ca14257be5005520e18e465da87a58dbaf 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 10fb9624ff54f1b767d54781e4fa8af13855d93d..a340e78a4371b033d6afd2a7ccdf8292b24237b4 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 3c4e83494dfe0f5fdbbcf2d8ee12fb05d92821ed..8c9654cd19af8b28fa276a55c5060eb389e60c1c 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 5fbe84dc41a19717c73f88050c4fa476ab5aecd7..3450c635077d37ff78b0b136a991ef66770e5530 100644
--- a/src/main/java/org/bukkit/inventory/ItemType.java
+++ b/src/main/java/org/bukkit/inventory/ItemType.java
@@ -2444,4 +2444,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
}