diff options
author | Bjarne Koll <[email protected]> | 2024-11-18 20:19:15 +0100 |
---|---|---|
committer | Owen1212055 <[email protected]> | 2024-11-18 14:50:39 -0500 |
commit | d67588da1ef7ccf5630b668dbff6ca7ec61db010 (patch) | |
tree | 925feeb19b8164b2347ff82b528968c45e308b12 /patches/api/0495-DataComponent-API.patch | |
parent | 1ff855ba8bd3ecaee79457aab9407c7619880195 (diff) | |
download | Paper-d67588da1ef7ccf5630b668dbff6ca7ec61db010.tar.gz Paper-d67588da1ef7ccf5630b668dbff6ca7ec61db010.zip |
Rename patches
Diffstat (limited to 'patches/api/0495-DataComponent-API.patch')
-rw-r--r-- | patches/api/0495-DataComponent-API.patch | 4194 |
1 files changed, 4194 insertions, 0 deletions
diff --git a/patches/api/0495-DataComponent-API.patch b/patches/api/0495-DataComponent-API.patch new file mode 100644 index 0000000000..347715d17a --- /dev/null +++ b/patches/api/0495-DataComponent-API.patch @@ -0,0 +1,4194 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <[email protected]> +Date: Sun, 28 Apr 2024 19:53:06 -0400 +Subject: [PATCH] DataComponent API + +Exposes the data component logic used by vanilla ItemStack to API +consumers as a version-specific API. +The types and methods introduced by this patch do not follow the general +API deprecation contracts and will be adapted to each new minecraft +release without backwards compatibility measures. + +diff --git a/src/main/java/io/papermc/paper/block/BlockPredicate.java b/src/main/java/io/papermc/paper/block/BlockPredicate.java +new file mode 100644 +index 0000000000000000000000000000000000000000..92ea82ee95c449916955631297a059f1b9198c9b +--- /dev/null ++++ b/src/main/java/io/papermc/paper/block/BlockPredicate.java +@@ -0,0 +1,50 @@ ++package io.papermc.paper.block; ++ ++import io.papermc.paper.registry.set.RegistryKeySet; ++import org.bukkit.block.BlockType; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jspecify.annotations.NullMarked; ++import org.jspecify.annotations.Nullable; ++ ++@NullMarked ++public interface BlockPredicate { ++ ++ static Builder predicate() { ++ //<editor-fold desc="implementations" defaultstate="collapsed"> ++ record BlockPredicateImpl(@Nullable RegistryKeySet<BlockType> blocks) implements BlockPredicate { ++ } ++ ++ class BuilderImpl implements Builder { ++ ++ private @Nullable RegistryKeySet<BlockType> blocks; ++ ++ @Override ++ public Builder blocks(final @Nullable RegistryKeySet<BlockType> blocks) { ++ this.blocks = blocks; ++ return this; ++ } ++ ++ @Override ++ public BlockPredicate build() { ++ return new BlockPredicateImpl(this.blocks); ++ } ++ } ++ //</editor-fold> ++ return new BuilderImpl(); ++ } ++ ++ @Nullable RegistryKeySet<BlockType> blocks(); ++ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder { ++ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder blocks(@Nullable RegistryKeySet<BlockType> blocks); ++ ++ BlockPredicate build(); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/BuildableDataComponent.java b/src/main/java/io/papermc/paper/datacomponent/BuildableDataComponent.java +new file mode 100644 +index 0000000000000000000000000000000000000000..4d2ee71b82ff4a66c7f84e73c028f146e0f851ad +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/BuildableDataComponent.java +@@ -0,0 +1,19 @@ ++package io.papermc.paper.datacomponent; ++ ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jspecify.annotations.NullMarked; ++ ++@NullMarked ++public interface BuildableDataComponent<C extends BuildableDataComponent<C, B>, B extends DataComponentBuilder<C>> { ++ ++ /** ++ * Creates a new builder from this data component. ++ * ++ * @return a new builder ++ */ ++ @Contract(value = "-> new", pure = true) ++ B toBuilder(); ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/DataComponentBuilder.java b/src/main/java/io/papermc/paper/datacomponent/DataComponentBuilder.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9365e57499c8e337a40835b2ec9a92ebe4391bfc +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/DataComponentBuilder.java +@@ -0,0 +1,24 @@ ++package io.papermc.paper.datacomponent; ++ ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Base builder type for all component builders. ++ * ++ * @param <C> built component type ++ */ ++@NullMarked ++public interface DataComponentBuilder<C> { ++ ++ /** ++ * Builds the immutable component value. ++ * ++ * @return a new component value ++ */ ++ @Contract(value = "-> new", pure = true) ++ C build(); ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/DataComponentType.java b/src/main/java/io/papermc/paper/datacomponent/DataComponentType.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e2266d86a4dd1bf20346e48c428f8baf8a84b76b +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/DataComponentType.java +@@ -0,0 +1,30 @@ ++package io.papermc.paper.datacomponent; ++ ++import org.bukkit.Keyed; ++import org.jetbrains.annotations.ApiStatus; ++import org.jspecify.annotations.NullMarked; ++ ++@NullMarked ++public interface DataComponentType extends Keyed { ++ ++ /** ++ * Checks if this data component type is persistent, or ++ * that it will be saved with any itemstack it's attached to. ++ * ++ * @return {@code true} if persistent, {@code false} otherwise ++ */ ++ boolean isPersistent(); ++ ++ @SuppressWarnings("unused") ++ @ApiStatus.NonExtendable ++ interface Valued<T> extends DataComponentType { ++ ++ } ++ ++ @ApiStatus.NonExtendable ++ interface NonValued extends DataComponentType { ++ ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java b/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e79737ae012179fc7c89b14af8801b8b09fa042b +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java +@@ -0,0 +1,344 @@ ++package io.papermc.paper.datacomponent; ++ ++import io.papermc.paper.datacomponent.item.BannerPatternLayers; ++import io.papermc.paper.datacomponent.item.BlockItemDataProperties; ++import io.papermc.paper.datacomponent.item.BundleContents; ++import io.papermc.paper.datacomponent.item.ChargedProjectiles; ++import io.papermc.paper.datacomponent.item.Consumable; ++import io.papermc.paper.datacomponent.item.CustomModelData; ++import io.papermc.paper.datacomponent.item.DamageResistant; ++import io.papermc.paper.datacomponent.item.DeathProtection; ++import io.papermc.paper.datacomponent.item.DyedItemColor; ++import io.papermc.paper.datacomponent.item.Enchantable; ++import io.papermc.paper.datacomponent.item.Equippable; ++import io.papermc.paper.datacomponent.item.Fireworks; ++import io.papermc.paper.datacomponent.item.FoodProperties; ++import io.papermc.paper.datacomponent.item.ItemAdventurePredicate; ++import io.papermc.paper.datacomponent.item.ItemArmorTrim; ++import io.papermc.paper.datacomponent.item.ItemAttributeModifiers; ++import io.papermc.paper.datacomponent.item.ItemContainerContents; ++import io.papermc.paper.datacomponent.item.ItemEnchantments; ++import io.papermc.paper.datacomponent.item.ItemLore; ++import io.papermc.paper.datacomponent.item.JukeboxPlayable; ++import io.papermc.paper.datacomponent.item.LodestoneTracker; ++import io.papermc.paper.datacomponent.item.MapDecorations; ++import io.papermc.paper.datacomponent.item.MapId; ++import io.papermc.paper.datacomponent.item.MapItemColor; ++import io.papermc.paper.datacomponent.item.OminousBottleAmplifier; ++import io.papermc.paper.datacomponent.item.PotDecorations; ++import io.papermc.paper.datacomponent.item.PotionContents; ++import io.papermc.paper.datacomponent.item.Repairable; ++import io.papermc.paper.datacomponent.item.ResolvableProfile; ++import io.papermc.paper.datacomponent.item.SeededContainerLoot; ++import io.papermc.paper.datacomponent.item.SuspiciousStewEffects; ++import io.papermc.paper.datacomponent.item.Tool; ++import io.papermc.paper.datacomponent.item.Unbreakable; ++import io.papermc.paper.datacomponent.item.UseCooldown; ++import io.papermc.paper.datacomponent.item.UseRemainder; ++import io.papermc.paper.datacomponent.item.WritableBookContent; ++import io.papermc.paper.datacomponent.item.WrittenBookContent; ++import io.papermc.paper.item.MapPostProcessing; ++import java.util.List; ++import net.kyori.adventure.key.Key; ++import net.kyori.adventure.text.Component; ++import org.bukkit.DyeColor; ++import org.bukkit.FireworkEffect; ++import org.bukkit.MusicInstrument; ++import org.bukkit.NamespacedKey; ++import org.bukkit.Registry; ++import org.bukkit.inventory.ItemRarity; ++import org.checkerframework.checker.index.qual.NonNegative; ++import org.checkerframework.checker.index.qual.Positive; ++import org.checkerframework.common.value.qual.IntRange; ++import org.jetbrains.annotations.ApiStatus; ++import org.jspecify.annotations.NullMarked; ++ ++import static java.util.Objects.requireNonNull; ++ ++/** ++ * All the different types of data that {@link org.bukkit.inventory.ItemStack ItemStacks} ++ * and {@link org.bukkit.inventory.ItemType ItemTypes} can have. ++ */ ++@NullMarked ++public final class DataComponentTypes { ++ ++ /** ++ * Controls the maximum stacking size of this item. ++ * <br> ++ * Values greater than 1 are mutually exclusive with the {@link #MAX_DAMAGE} component. ++ */ ++ public static final DataComponentType.Valued<@IntRange(from = 1, to = 99) Integer> MAX_STACK_SIZE = valued("max_stack_size"); ++ /** ++ * Controls the maximum amount of damage than an item can take, ++ * if not present, the item cannot be damaged. ++ * <br> ++ * Mutually exclusive with the {@link #MAX_STACK_SIZE} component greater than 1. ++ * ++ * @see #DAMAGE ++ */ ++ public static final DataComponentType.Valued<@Positive Integer> MAX_DAMAGE = valued("max_damage"); ++ /** ++ * The amount of durability removed from an item, ++ * for damageable items (with the {@link #MAX_DAMAGE} component), has an implicit default value of: {@code 0}. ++ * ++ * @see #MAX_DAMAGE ++ */ ++ public static final DataComponentType.Valued<@NonNegative Integer> DAMAGE = valued("damage"); ++ /** ++ * If set, the item will not lose any durability when used. ++ */ ++ public static final DataComponentType.Valued<Unbreakable> UNBREAKABLE = valued("unbreakable"); ++ /** ++ * Custom name override for an item (as set by renaming with an Anvil). ++ * ++ * @see #ITEM_NAME ++ */ ++ public static final DataComponentType.Valued<Component> CUSTOM_NAME = valued("custom_name"); ++ /** ++ * When present, replaces default item name with contained chat component. ++ * <p> ++ * Differences from {@link #CUSTOM_NAME}: ++ * <ul> ++ * <li>can't be changed or removed in Anvil</li> ++ * <li>is not styled with italics when displayed to player</li> ++ * <li>does not show labels where applicable ++ * (for example: banner markers, names in item frames)</li> ++ * </ul> ++ * ++ * @see #CUSTOM_NAME ++ */ ++ public static final DataComponentType.Valued<Component> ITEM_NAME = valued("item_name"); ++ public static final DataComponentType.Valued<Key> ITEM_MODEL = valued("item_model"); ++ /** ++ * Additional lines to include in an item's tooltip. ++ */ ++ public static final DataComponentType.Valued<ItemLore> LORE = valued("lore"); ++ /** ++ * Controls the color of the item name. ++ */ ++ public static final DataComponentType.Valued<ItemRarity> RARITY = valued("rarity"); ++ /** ++ * Controls the enchantments on an item. ++ * <br> ++ * If not present on a non-enchantment book, this item will not work in an anvil. ++ * ++ * @see #STORED_ENCHANTMENTS ++ */ ++ public static final DataComponentType.Valued<ItemEnchantments> ENCHANTMENTS = valued("enchantments"); ++ /** ++ * Controls which blocks a player in Adventure mode can place on with this item. ++ */ ++ public static final DataComponentType.Valued<ItemAdventurePredicate> CAN_PLACE_ON = valued("can_place_on"); ++ /** ++ * Controls which blocks a player in Adventure mode can break with this item. ++ */ ++ public static final DataComponentType.Valued<ItemAdventurePredicate> CAN_BREAK = valued("can_break"); ++ /** ++ * Holds attribute modifiers applied to any item, ++ * if not set, has an implicit default value based on the item type's ++ * default attributes (e.g. attack damage for weapons). ++ */ ++ public static final DataComponentType.Valued<ItemAttributeModifiers> ATTRIBUTE_MODIFIERS = valued("attribute_modifiers"); ++ /** ++ * Controls the minecraft:custom_model_data property in the item model. ++ */ ++ public static final DataComponentType.Valued<CustomModelData> CUSTOM_MODEL_DATA = valued("custom_model_data"); ++ /** ++ * If set, disables 'additional' tooltip part which comes from the item type ++ * (e.g. content of a shulker). ++ */ ++ public static final DataComponentType.NonValued HIDE_ADDITIONAL_TOOLTIP = unvalued("hide_additional_tooltip"); ++ /** ++ * If set, it will completely hide whole item tooltip (that includes item name). ++ */ ++ public static final DataComponentType.NonValued HIDE_TOOLTIP = unvalued("hide_tooltip"); ++ /** ++ * The additional experience cost required to modify an item in an Anvil. ++ * If not present, has an implicit default value of: {@code 0}. ++ */ ++ public static final DataComponentType.Valued<@NonNegative Integer> REPAIR_COST = valued("repair_cost"); ++ /** ++ * Causes an item to not be pickable in the creative menu, currently not very useful. ++ */ ++ // public static final DataComponentType.NonValued CREATIVE_SLOT_LOCK = unvalued("creative_slot_lock"); ++ /** ++ * Overrides the enchantment glint effect on an item. ++ * If not present, default behaviour is used. ++ */ ++ public static final DataComponentType.Valued<Boolean> ENCHANTMENT_GLINT_OVERRIDE = valued("enchantment_glint_override"); ++ /** ++ * Marks that a projectile item would be intangible when fired ++ * (i.e. can only be picked up by a creative mode player). ++ */ ++ public static final DataComponentType.NonValued INTANGIBLE_PROJECTILE = unvalued("intangible_projectile"); ++ /** ++ * Controls potential food benefits gained when consuming the item the component is applied on. ++ * Requires the {@link #CONSUMABLE} component to allow consumption in the first place. ++ */ ++ public static final DataComponentType.Valued<FoodProperties> FOOD = valued("food"); ++ public static final DataComponentType.Valued<Consumable> CONSUMABLE = valued("consumable"); ++ public static final DataComponentType.Valued<UseRemainder> USE_REMAINDER = valued("use_remainder"); ++ public static final DataComponentType.Valued<UseCooldown> USE_COOLDOWN = valued("use_cooldown"); ++ /** ++ * If present, this item will not burn in fire. ++ */ ++ public static final DataComponentType.Valued<DamageResistant> DAMAGE_RESISTANT = valued("damage_resistant"); ++ /** ++ * Controls the behavior of the item as a tool. ++ */ ++ public static final DataComponentType.Valued<Tool> TOOL = valued("tool"); ++ public static final DataComponentType.Valued<Enchantable> ENCHANTABLE = valued("enchantable"); ++ public static final DataComponentType.Valued<Equippable> EQUIPPABLE = valued("equippable"); ++ public static final DataComponentType.Valued<Repairable> REPAIRABLE = valued("repairable"); ++ public static final DataComponentType.NonValued GLIDER = unvalued("glider"); ++ public static final DataComponentType.Valued<Key> TOOLTIP_STYLE = valued("tooltip_style"); ++ public static final DataComponentType.Valued<DeathProtection> DEATH_PROTECTION = valued("death_protection"); ++ /** ++ * Stores list of enchantments and their levels for an Enchanted Book. ++ * Unlike {@link #ENCHANTMENTS}, the effects provided by enchantments ++ * do not apply from this component. ++ * <br> ++ * If not present on an Enchanted Book, it will not work in an anvil. ++ * <p> ++ * 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<ItemEnchantments> 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<DyedItemColor> 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<MapItemColor> 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<MapId> 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<MapDecorations> MAP_DECORATIONS = valued("map_decorations"); ++ /** ++ * Internal map item state used in the map crafting recipe. ++ */ ++ public static final DataComponentType.Valued<MapPostProcessing> 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<ChargedProjectiles> 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<BundleContents> 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<PotionContents> POTION_CONTENTS = valued("potion_contents"); ++ /** ++ * Holds the effects that will be applied when consuming Suspicious Stew. ++ */ ++ public static final DataComponentType.Valued<SuspiciousStewEffects> SUSPICIOUS_STEW_EFFECTS = valued("suspicious_stew_effects"); ++ /** ++ * Holds the contents in a Book and Quill. ++ */ ++ public static final DataComponentType.Valued<WritableBookContent> WRITABLE_BOOK_CONTENT = valued("writable_book_content"); ++ /** ++ * Holds the contents and metadata of a Written Book. ++ */ ++ public static final DataComponentType.Valued<WrittenBookContent> WRITTEN_BOOK_CONTENT = valued("written_book_content"); ++ /** ++ * Holds the trims applied to an item in recipes ++ */ ++ public static final DataComponentType.Valued<ItemArmorTrim> 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<MusicInstrument> INSTRUMENT = valued("instrument"); ++ /** ++ * Controls the amplifier amount for an Ominous Bottle's Bad Omen effect. ++ */ ++ public static final DataComponentType.Valued<OminousBottleAmplifier> OMINOUS_BOTTLE_AMPLIFIER = valued("ominous_bottle_amplifier"); ++ /** ++ * List of recipes that should be unlocked when using the Knowledge Book item. ++ */ ++ public static final DataComponentType.Valued<JukeboxPlayable> JUKEBOX_PLAYABLE = valued("jukebox_playable"); ++ public static final DataComponentType.Valued<List<Key>> RECIPES = valued("recipes"); ++ /** ++ * If present, specifies that the Compass is a Lodestone Compass. ++ */ ++ public static final DataComponentType.Valued<LodestoneTracker> LODESTONE_TRACKER = valued("lodestone_tracker"); ++ /** ++ * Stores the explosion crafted in a Firework Star. ++ */ ++ public static final DataComponentType.Valued<FireworkEffect> FIREWORK_EXPLOSION = valued("firework_explosion"); ++ /** ++ * Stores all explosions crafted into a Firework Rocket, as well as flight duration. ++ */ ++ public static final DataComponentType.Valued<Fireworks> FIREWORKS = valued("fireworks"); ++ /** ++ * Controls the skin displayed on a Player Head. ++ */ ++ public static final DataComponentType.Valued<ResolvableProfile> PROFILE = valued("profile"); ++ /** ++ * Controls the sound played by a Player Head when placed on a Note Block. ++ */ ++ public static final DataComponentType.Valued<Key> NOTE_BLOCK_SOUND = valued("note_block_sound"); ++ /** ++ * Stores the additional patterns applied to a Banner or Shield. ++ */ ++ public static final DataComponentType.Valued<BannerPatternLayers> BANNER_PATTERNS = valued("banner_patterns"); ++ /** ++ * Stores the base color for a Shield. ++ */ ++ public static final DataComponentType.Valued<DyeColor> BASE_COLOR = valued("base_color"); ++ /** ++ * Stores the Sherds applied to each side of a Decorated Pot. ++ */ ++ public static final DataComponentType.Valued<PotDecorations> POT_DECORATIONS = valued("pot_decorations"); ++ /** ++ * Holds the contents of container blocks (Chests, Shulker Boxes) in item form. ++ */ ++ public static final DataComponentType.Valued<ItemContainerContents> CONTAINER = valued("container"); ++ /** ++ * Holds block state properties to apply when placing a block. ++ */ ++ public static final DataComponentType.Valued<BlockItemDataProperties> BLOCK_DATA = valued("block_state"); ++ // bees ++ // /** ++ // * Holds the lock state of a container-like block, ++ // * copied to container block when placed. ++ // * <br> ++ // * An item with a custom name of the same value must be used ++ // * to open this container. ++ // */ ++ // public static final DataComponentType.Valued<LockCode> LOCK = valued("lock"); ++ /** ++ * Holds the unresolved loot table and seed of a container-like block. ++ */ ++ public static final DataComponentType.Valued<SeededContainerLoot> 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 <T> DataComponentType.Valued<T> valued(final String name) { ++ return (DataComponentType.Valued<T>) requireNonNull(Registry.DATA_COMPONENT_TYPE.get(NamespacedKey.minecraft(name)), name + " valued data component type couldn't be found, this is a bug."); ++ } ++ ++ private DataComponentTypes() { ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/BannerPatternLayers.java b/src/main/java/io/papermc/paper/datacomponent/item/BannerPatternLayers.java +new file mode 100644 +index 0000000000000000000000000000000000000000..12cfae82234b8c4cb231ab91e72ad82d28b85183 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/BannerPatternLayers.java +@@ -0,0 +1,66 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.datacomponent.DataComponentBuilder; ++import java.util.Arrays; ++import java.util.List; ++import org.bukkit.block.banner.Pattern; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jetbrains.annotations.Unmodifiable; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Holds the layers of patterns on a banner. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#BANNER_PATTERNS ++ */ ++@NullMarked ++public interface BannerPatternLayers { ++ ++ @Contract(value = "_ -> new", pure = true) ++ static BannerPatternLayers bannerPatternLayers(final List<Pattern> 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<Pattern> patterns(); ++ ++ /** ++ * Builder for {@link BannerPatternLayers}. ++ */ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends DataComponentBuilder<BannerPatternLayers> { ++ ++ /** ++ * 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<Pattern> 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 ++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<BlockItemDataProperties> { ++ // building this requires BlockProperty API, so an empty builder for now (essentially read-only) ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/BundleContents.java b/src/main/java/io/papermc/paper/datacomponent/item/BundleContents.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c0f671aef8225c87632d2368d1b28fc8b1bce686 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/BundleContents.java +@@ -0,0 +1,66 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.datacomponent.DataComponentBuilder; ++import java.util.Arrays; ++import java.util.List; ++import org.bukkit.inventory.ItemStack; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jetbrains.annotations.Unmodifiable; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Holds all items stored inside of a Bundle. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#BUNDLE_CONTENTS ++ */ ++@NullMarked ++public interface BundleContents { ++ ++ @Contract(value = "_ -> new", pure = true) ++ static BundleContents bundleContents(final List<ItemStack> contents) { ++ return ItemComponentTypesBridge.bridge().bundleContents().addAll(contents).build(); ++ } ++ ++ @Contract(value = "-> new", pure = true) ++ static BundleContents.Builder bundleContents() { ++ return ItemComponentTypesBridge.bridge().bundleContents(); ++ } ++ ++ /** ++ * Lists the items that are currently stored inside of this component. ++ * ++ * @return items ++ */ ++ @Contract(value = "-> new", pure = true) ++ @Unmodifiable List<ItemStack> contents(); ++ ++ /** ++ * Builder for {@link BundleContents}. ++ */ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends DataComponentBuilder<BundleContents> { ++ ++ /** ++ * 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<ItemStack> stacks); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ChargedProjectiles.java b/src/main/java/io/papermc/paper/datacomponent/item/ChargedProjectiles.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d0a6e7db06f540e13ac00e8da3acabd9f7838f1f +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/ChargedProjectiles.java +@@ -0,0 +1,66 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.datacomponent.DataComponentBuilder; ++import java.util.Arrays; ++import java.util.List; ++import org.bukkit.inventory.ItemStack; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jetbrains.annotations.Unmodifiable; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Holds all projectiles that have been loaded into a Crossbow. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#CHARGED_PROJECTILES ++ */ ++@NullMarked ++public interface ChargedProjectiles { ++ ++ @Contract(value = "_ -> new", pure = true) ++ static ChargedProjectiles chargedProjectiles(final List<ItemStack> 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<ItemStack> projectiles(); ++ ++ /** ++ * Builder for {@link ChargedProjectiles}. ++ */ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends DataComponentBuilder<ChargedProjectiles> { ++ ++ /** ++ * 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<ItemStack> stacks); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Consumable.java b/src/main/java/io/papermc/paper/datacomponent/item/Consumable.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a448fedb63ffce18b9f6a1bd0fecfc5cd90224a6 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/Consumable.java +@@ -0,0 +1,70 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.datacomponent.BuildableDataComponent; ++import io.papermc.paper.datacomponent.DataComponentBuilder; ++import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect; ++import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation; ++import java.util.Collection; ++import java.util.List; ++import net.kyori.adventure.key.Key; ++import org.checkerframework.checker.index.qual.NonNegative; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jetbrains.annotations.Unmodifiable; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Holds the properties for this item for when it is consumed. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#CONSUMABLE ++ */ ++@NullMarked ++public interface Consumable extends BuildableDataComponent<Consumable, Consumable.Builder> { ++ ++ @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<ConsumeEffect> consumeEffects(); ++ ++ /** ++ * Builder for {@link Consumable}. ++ */ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends DataComponentBuilder<Consumable> { ++ ++ @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<ConsumeEffect> effects); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/CustomModelData.java b/src/main/java/io/papermc/paper/datacomponent/item/CustomModelData.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d416c9d25b3ab88bf1e208c6faf92a8e2378c376 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/CustomModelData.java +@@ -0,0 +1,28 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Holds the custom model data. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#CUSTOM_MODEL_DATA ++ */ ++@NullMarked ++public interface CustomModelData { ++ ++ @Contract(value = "_ -> new", pure = true) ++ static CustomModelData customModelData(final int id) { ++ return ItemComponentTypesBridge.bridge().customModelData(id); ++ } ++ ++ /** ++ * Gets the custom model data id. ++ * ++ * @return the id ++ */ ++ @Contract(pure = true) ++ int id(); ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java b/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6cbd73cb2a11f4858b44a2f57d2fe0acb1eb9fb5 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java +@@ -0,0 +1,30 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.registry.tag.TagKey; ++import org.bukkit.damage.DamageType; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Holds the contents of damage types that the item entity containing this item is invincible to. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#DAMAGE_RESISTANT ++ */ ++@NullMarked ++public interface DamageResistant { ++ ++ @Contract(value = "_ -> new", pure = true) ++ static DamageResistant damageResistant(final TagKey<DamageType> types) { ++ return ItemComponentTypesBridge.bridge().damageResistant(types); ++ } ++ ++ /** ++ * The types that this damage type is invincible tp. ++ * ++ * @return item ++ */ ++ @Contract(value = "-> new", pure = true) ++ TagKey<DamageType> types(); ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/DeathProtection.java b/src/main/java/io/papermc/paper/datacomponent/item/DeathProtection.java +new file mode 100644 +index 0000000000000000000000000000000000000000..87c2220708af7db06348994ad5940c7cecd9f691 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/DeathProtection.java +@@ -0,0 +1,48 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.datacomponent.DataComponentBuilder; ++import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect; ++import java.util.Arrays; ++import java.util.Collection; ++import java.util.List; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jetbrains.annotations.Unmodifiable; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Sets whether this item should protect the entity upon death, and what effects should be played. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#DEATH_PROTECTION ++ */ ++@NullMarked ++public interface DeathProtection { ++ ++ @Contract(value = "_ -> new", pure = true) ++ static DeathProtection deathProtection(final List<ConsumeEffect> 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<ConsumeEffect> deathEffects(); ++ ++ /** ++ * Builder for {@link DeathProtection}. ++ */ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends DataComponentBuilder<DeathProtection> { ++ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder addEffect(ConsumeEffect effect); ++ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder addEffects(List<ConsumeEffect> effects); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/DyedItemColor.java b/src/main/java/io/papermc/paper/datacomponent/item/DyedItemColor.java +new file mode 100644 +index 0000000000000000000000000000000000000000..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 ++public interface DyedItemColor extends ShownInTooltip<DyedItemColor> { ++ ++ @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<Builder>, DataComponentBuilder<DyedItemColor> { ++ ++ /** ++ * Sets the color of this builder. ++ * ++ * @param color color ++ * @return the builder for chaining ++ * @see #color() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder color(Color color); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Enchantable.java b/src/main/java/io/papermc/paper/datacomponent/item/Enchantable.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5169b9cd73dc0ffc8297f8d5f63d3d707a47d279 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/Enchantable.java +@@ -0,0 +1,31 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import org.checkerframework.checker.index.qual.Positive; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Holds if an item is enchantable, allowing for enchantments of the type to be seen in an enchanting table. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#ENCHANTABLE ++ */ ++@NullMarked ++public interface Enchantable { ++ ++ @Contract(value = "_ -> new", pure = true) ++ static Enchantable enchantable(final @Positive int level) { ++ return ItemComponentTypesBridge.bridge().enchantable(level); ++ } ++ ++ /** ++ * Gets the current enchantment value level allowed, ++ * a higher value allows enchantments with a higher cost to be picked. ++ * ++ * @see <a href="https://minecraft.wiki/w/Enchanting_mechanics#Java_Edition_2">Minecraft Wiki</a> ++ * @return the value ++ */ ++ @Contract(pure = true) ++ @Positive int value(); ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Equippable.java b/src/main/java/io/papermc/paper/datacomponent/item/Equippable.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7d84217814bba4ce826e33755fee0d5c3b280009 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/Equippable.java +@@ -0,0 +1,170 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.datacomponent.BuildableDataComponent; ++import io.papermc.paper.datacomponent.DataComponentBuilder; ++import io.papermc.paper.registry.set.RegistryKeySet; ++import net.kyori.adventure.key.Key; ++import org.bukkit.entity.EntityType; ++import org.bukkit.inventory.EquipmentSlot; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jspecify.annotations.NullMarked; ++import org.jspecify.annotations.Nullable; ++ ++ ++/** ++ * Holds the equippable properties of an item. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#EQUIPPABLE ++ */ ++@NullMarked ++public interface Equippable extends BuildableDataComponent<Equippable, Equippable.Builder> { ++ ++ /** ++ * Creates a new {@link Equippable.Builder} instance. ++ * @param slot The slot for the new equippable to be equippable in. ++ * ++ * @return a new builder ++ */ ++ @Contract(value = "_ -> new", pure = true) ++ static Equippable.Builder equippable(final EquipmentSlot slot) { ++ return ItemComponentTypesBridge.bridge().equippable(slot); ++ } ++ ++ /** ++ * Gets the equipment slot this item can be equipped in. ++ * ++ * @return the equipment slot ++ */ ++ @Contract(pure = true) ++ EquipmentSlot slot(); ++ ++ /** ++ * Gets the equip sound key. ++ * ++ * @return the equip sound key ++ */ ++ @Contract(pure = true) ++ Key equipSound(); ++ ++ /** ++ * Gets the model key if present. ++ * ++ * @return the model key or null ++ */ ++ @Contract(pure = true) ++ @Nullable Key model(); ++ ++ /** ++ * Gets the camera overlay key if present. ++ * ++ * @return the camera overlay key or null ++ */ ++ @Contract(pure = true) ++ @Nullable Key cameraOverlay(); ++ ++ /** ++ * Gets the set of allowed entities that can equip this item. ++ * May be null if all entities are allowed. ++ * ++ * @return the set of allowed entities ++ */ ++ @Contract(pure = true) ++ @Nullable RegistryKeySet<EntityType> allowedEntities(); ++ ++ /** ++ * Checks if the item is dispensable. ++ * ++ * @return true if dispensable, false otherwise ++ */ ++ @Contract(pure = true) ++ boolean dispensable(); ++ ++ /** ++ * Checks if the item is swappable. ++ * ++ * @return true if swappable, false otherwise ++ */ ++ @Contract(pure = true) ++ boolean swappable(); ++ ++ /** ++ * Checks if the item takes damage when the wearer is hurt. ++ * ++ * @return true if it damages on hurt, false otherwise ++ */ ++ @Contract(pure = true) ++ boolean damageOnHurt(); ++ ++ /** ++ * Builder for {@link Equippable}. ++ */ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends DataComponentBuilder<Equippable> { ++ ++ /** ++ * Sets the equip sound key for this item. ++ * ++ * @param equipSound the equip sound key ++ * @return the builder for chaining ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder equipSound(Key equipSound); ++ ++ /** ++ * Sets the model key for this item. ++ * ++ * @param model the model key, nullable ++ * @return the builder for chaining ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder model(@Nullable Key model); ++ ++ /** ++ * Sets the camera overlay key for this item. ++ * ++ * @param cameraOverlay the camera overlay key, nullable ++ * @return the builder for chaining ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder cameraOverlay(@Nullable Key cameraOverlay); ++ ++ /** ++ * Sets the allowed entities that can equip this item. ++ * ++ * @param allowedEntities the set of allowed entity types, or null if any ++ * @return the builder for chaining ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder allowedEntities(@Nullable RegistryKeySet<EntityType> 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 ++public interface Fireworks { ++ ++ @Contract(value = "_, _ -> new", pure = true) ++ static Fireworks fireworks(final List<FireworkEffect> 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<FireworkEffect> 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<Fireworks> { ++ ++ /** ++ * 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<FireworkEffect> effects); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/FoodProperties.java b/src/main/java/io/papermc/paper/datacomponent/item/FoodProperties.java +new file mode 100644 +index 0000000000000000000000000000000000000000..369208e15a0e7fc91a9505fef2097c4283445e4a +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/FoodProperties.java +@@ -0,0 +1,87 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.datacomponent.BuildableDataComponent; ++import io.papermc.paper.datacomponent.DataComponentBuilder; ++import org.checkerframework.checker.index.qual.NonNegative; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Holds the food properties of an item. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#FOOD ++ */ ++@NullMarked ++public interface FoodProperties extends BuildableDataComponent<FoodProperties, FoodProperties.Builder> { ++ ++ @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<FoodProperties> { ++ ++ /** ++ * 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 ++public interface ItemAdventurePredicate extends ShownInTooltip<ItemAdventurePredicate> { ++ ++ @Contract(value = "_ -> new", pure = true) ++ static ItemAdventurePredicate itemAdventurePredicate(final List<BlockPredicate> 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<BlockPredicate> predicates(); ++ ++ /** ++ * Builder for {@link ItemAdventurePredicate}. ++ */ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends ShownInTooltip.Builder<Builder>, DataComponentBuilder<ItemAdventurePredicate> { ++ /** ++ * 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<BlockPredicate> 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 ++public interface ItemArmorTrim extends ShownInTooltip<ItemArmorTrim> { ++ ++ @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<Builder>, DataComponentBuilder<ItemArmorTrim> { ++ ++ /** ++ * Sets the armor trim for this builder. ++ * ++ * @param armorTrim trim ++ * @return the builder for chaining ++ * @see #armorTrim() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder armorTrim(ArmorTrim armorTrim); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemAttributeModifiers.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemAttributeModifiers.java +new file mode 100644 +index 0000000000000000000000000000000000000000..948505d38121d54df62e6a67d4597bc7d42c356f +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemAttributeModifiers.java +@@ -0,0 +1,98 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.datacomponent.DataComponentBuilder; ++import java.util.List; ++import org.bukkit.attribute.Attribute; ++import org.bukkit.attribute.AttributeModifier; ++import org.bukkit.inventory.EquipmentSlotGroup; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jetbrains.annotations.Unmodifiable; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Holds attribute modifiers applied to any item. ++ * ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#ATTRIBUTE_MODIFIERS ++ */ ++@NullMarked ++public interface ItemAttributeModifiers extends ShownInTooltip<ItemAttributeModifiers> { ++ ++ @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<Entry> 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<Builder>, DataComponentBuilder<ItemAttributeModifiers> { ++ ++ /** ++ * Adds a modifier to this builder. ++ * ++ * @param attribute attribute ++ * @param modifier modifier ++ * @return the builder for chaining ++ * @see #modifiers() ++ */ ++ @Contract(value = "_, _, _ -> this", mutates = "this") ++ Builder addModifier(Attribute attribute, AttributeModifier modifier); ++ ++ /** ++ * Adds a modifier to this builder. ++ * ++ * @param attribute attribute ++ * @param modifier modifier ++ * @param equipmentSlotGroup the slot group this modifier applies to (overrides any slot group in the modifier) ++ * @return the builder for chaining ++ * @see #modifiers() ++ */ ++ @Contract(value = "_, _, _ -> this", mutates = "this") ++ Builder addModifier(Attribute attribute, AttributeModifier modifier, EquipmentSlotGroup equipmentSlotGroup); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridge.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridge.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1ce34642371a65590ce1ac74b402ccfc301671d7 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridge.java +@@ -0,0 +1,112 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import com.destroystokyo.paper.profile.PlayerProfile; ++import io.papermc.paper.registry.set.RegistryKeySet; ++import io.papermc.paper.registry.tag.TagKey; ++import io.papermc.paper.text.Filtered; ++import java.util.Optional; ++import java.util.ServiceLoader; ++import net.kyori.adventure.key.Key; ++import net.kyori.adventure.util.TriState; ++import org.bukkit.JukeboxSong; ++import org.bukkit.block.BlockType; ++import org.bukkit.damage.DamageType; ++import org.bukkit.inventory.EquipmentSlot; ++import org.bukkit.inventory.ItemStack; ++import org.bukkit.inventory.ItemType; ++import org.bukkit.inventory.meta.trim.ArmorTrim; ++import org.bukkit.map.MapCursor; ++import org.jetbrains.annotations.ApiStatus; ++import org.jspecify.annotations.NullMarked; ++import org.jspecify.annotations.Nullable; ++ ++@NullMarked ++interface ItemComponentTypesBridge { ++ ++ Optional<ItemComponentTypesBridge> 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<String> 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<BlockType> blocks, @Nullable Float speed, TriState correctForDrops); ++ ++ ItemAdventurePredicate.Builder itemAdventurePredicate(); ++ ++ CustomModelData customModelData(int id); ++ ++ MapId mapId(int id); ++ ++ UseRemainder useRemainder(ItemStack itemStack); ++ ++ Consumable.Builder consumable(); ++ ++ UseCooldown.Builder useCooldown(final float seconds); ++ ++ DamageResistant damageResistant(TagKey<DamageType> types); ++ ++ Enchantable enchantable(int level); ++ ++ Repairable repairable(RegistryKeySet<ItemType> types); ++ ++ Equippable.Builder equippable(EquipmentSlot slot); ++ ++ DeathProtection.Builder deathProtection(); ++ ++ OminousBottleAmplifier ominousBottleAmplifier(int amplifier); ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemContainerContents.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemContainerContents.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7d1c973ba566752d7a85496327b1352d973f2218 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemContainerContents.java +@@ -0,0 +1,63 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.datacomponent.DataComponentBuilder; ++import java.util.Arrays; ++import java.util.List; ++import org.bukkit.inventory.ItemStack; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jetbrains.annotations.Unmodifiable; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Holds the contents of an item container. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#CONTAINER ++ */ ++@NullMarked ++public interface ItemContainerContents { ++ ++ @Contract(value = "_ -> new", pure = true) ++ static ItemContainerContents containerContents(final List<ItemStack> 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<ItemStack> contents(); ++ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends DataComponentBuilder<ItemContainerContents> { ++ ++ /** ++ * 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<ItemStack> 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 ++public interface ItemEnchantments extends ShownInTooltip<ItemEnchantments> { ++ ++ @Contract(value = "_, _ -> new", pure = true) ++ static ItemEnchantments itemEnchantments(final Map<Enchantment, @IntRange(from = 1, to = 255) Integer> 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<Enchantment, @IntRange(from = 1, to = 255) Integer> enchantments(); ++ ++ /** ++ * Builder for {@link ItemEnchantments}. ++ */ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends ShownInTooltip.Builder<Builder>, DataComponentBuilder<ItemEnchantments> { ++ ++ /** ++ * 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<Enchantment, @IntRange(from = 1, to = 255) Integer> 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 ++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<Component> 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<Component> styledLines(); ++ ++ /** ++ * Builder for {@link ItemLore}. ++ */ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends DataComponentBuilder<ItemLore> { ++ ++ /** ++ * 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 ++public interface JukeboxPlayable extends ShownInTooltip<JukeboxPlayable> { ++ ++ @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<JukeboxPlayable.Builder>, DataComponentBuilder<JukeboxPlayable> { ++ ++ /** ++ * 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 ++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<LodestoneTracker> { ++ ++ /** ++ * 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 ++public interface MapDecorations { ++ ++ @Contract(value = "_ -> new", pure = true) ++ static MapDecorations mapDecorations(final Map<String, DecorationEntry> 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<String, DecorationEntry> 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<MapDecorations> { ++ ++ /** ++ * 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<String, DecorationEntry> 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 ++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 ++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<MapItemColor> { ++ ++ /** ++ * 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 ++public interface OminousBottleAmplifier { ++ ++ @Contract(value = "_ -> new", pure = true) ++ static OminousBottleAmplifier amplifier(final @IntRange(from = 0, to = 4) int amplifier) { ++ return ItemComponentTypesBridge.bridge().ominousBottleAmplifier(amplifier); ++ } ++ ++ /** ++ * Gets the bottle amplifier. ++ * ++ * @return the amplifier ++ */ ++ @Contract(pure = true) ++ @IntRange(from = 0, to = 4) int amplifier(); ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PotDecorations.java b/src/main/java/io/papermc/paper/datacomponent/item/PotDecorations.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6da78b8735a6cadd1282fa2fafd8b0f74f087fb4 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/PotDecorations.java +@@ -0,0 +1,109 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.datacomponent.DataComponentBuilder; ++import org.bukkit.inventory.ItemType; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jspecify.annotations.NullMarked; ++import org.jspecify.annotations.Nullable; ++ ++// CONTRIBUTORS: LEAVE THIS AS ITEM TYPE!!! ++/** ++ * Holds the item types for the decorations on a flower pot. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#POT_DECORATIONS ++ */ ++@NullMarked ++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<PotDecorations> { ++ ++ /** ++ * Set the {@link ItemType} for the back. ++ * ++ * @param back item for the back ++ * @return the builder for chaining ++ * @see #back() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder back(@Nullable ItemType back); ++ ++ /** ++ * Set the {@link ItemType} for the left. ++ * ++ * @param left item for the left ++ * @return the builder for chaining ++ * @see #left() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder left(@Nullable ItemType left); ++ ++ /** ++ * Set the {@link ItemType} for the right. ++ * ++ * @param right item for the right ++ * @return the builder for chaining ++ * @see #right() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder right(@Nullable ItemType right); ++ ++ /** ++ * Set the {@link ItemType} for the front. ++ * ++ * @param front item for the front ++ * @return the builder for chaining ++ * @see #front() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder front(@Nullable ItemType front); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java b/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7cf05b382319064d45433a7e2678f65c25d11b14 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java +@@ -0,0 +1,120 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.datacomponent.DataComponentBuilder; ++import java.util.List; ++import org.bukkit.Color; ++import org.bukkit.potion.PotionEffect; ++import org.bukkit.potion.PotionType; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jetbrains.annotations.Unmodifiable; ++import org.jspecify.annotations.NullMarked; ++import org.jspecify.annotations.Nullable; ++ ++/** ++ * Holds the contents of a potion (Potion, Splash Potion, Lingering Potion), or potion applied to a Tipped Arrow. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#POTION_CONTENTS ++ */ ++@NullMarked ++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<PotionEffect> 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<PotionContents> { ++ ++ /** ++ * Sets the potion type for this builder. ++ * ++ * @param type builder ++ * @return the builder for chaining ++ * @see #potion() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder potion(@Nullable PotionType type); ++ ++ /** ++ * Sets the color override for this builder. ++ * ++ * @param color color ++ * @return the builder for chaining ++ * @see #customColor() ++ * @apiNote alpha channel of the color is supported only for Tipped Arrow ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder customColor(@Nullable Color color); ++ ++ /** ++ * Sets the suffix to the translation key of the potion item. ++ * ++ * @param name name ++ * @return the builder for chaining ++ * @see #customName() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder customName(@Nullable String name); ++ ++ /** ++ * Adds a custom effect instance to this builder. ++ * ++ * @param effect effect ++ * @see #customEffects() ++ * @return the builder for chaining ++ * @see #customEffects() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder addCustomEffect(PotionEffect effect); ++ ++ /** ++ * Adds custom effect instances to this builder. ++ * ++ * @param effects effects ++ * @see #customEffects() ++ * @return the builder for chaining ++ * @see #customEffects() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder addCustomEffects(List<PotionEffect> effects); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Repairable.java b/src/main/java/io/papermc/paper/datacomponent/item/Repairable.java +new file mode 100644 +index 0000000000000000000000000000000000000000..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 ++public interface Repairable { ++ ++ @Contract(value = "_ -> new", pure = true) ++ static Repairable repairable(final RegistryKeySet<ItemType> types) { ++ return ItemComponentTypesBridge.bridge().repairable(types); ++ } ++ ++ /** ++ * The types that this item is repairable to. ++ * ++ * @return item ++ */ ++ @Contract(value = "-> new", pure = true) ++ RegistryKeySet<ItemType> types(); ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ResolvableProfile.java b/src/main/java/io/papermc/paper/datacomponent/item/ResolvableProfile.java +new file mode 100644 +index 0000000000000000000000000000000000000000..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 ++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<ProfileProperty> properties(); ++ ++ /** ++ * Produces an updated player profile based on this. ++ * <p> ++ * 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}. ++ * <p> ++ * 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. ++ * <p> ++ * 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: ++ * <pre> ++ * profile.resolve().thenAcceptAsync(updatedProfile -> { ++ * // Do something with the updated profile: ++ * // ... ++ * }, runnable -> Bukkit.getScheduler().runTask(plugin, runnable)); ++ * </pre> ++ */ ++ @Contract(pure = true) ++ CompletableFuture<PlayerProfile> resolve(); ++ ++ /** ++ * Builder for {@link ResolvableProfile}. ++ */ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends DataComponentBuilder<ResolvableProfile> { ++ ++ /** ++ * 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<ProfileProperty> 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 ++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<SeededContainerLoot> { ++ ++ /** ++ * Sets the loot table key. ++ * ++ * @param key the loot table key ++ * @return the builder for chaining ++ * @see #lootTable() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder lootTable(Key key); ++ ++ /** ++ * Sets the loot table seed. ++ * ++ * @param seed the seed ++ * @return the builder for chaining ++ * @see #seed() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder seed(long seed); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ShownInTooltip.java b/src/main/java/io/papermc/paper/datacomponent/item/ShownInTooltip.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7e058aebcbd768517f6db51540598721cdae4425 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/ShownInTooltip.java +@@ -0,0 +1,52 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Holds the state of whether a data component should be shown ++ * in an item's tooltip. ++ * @param <T> the data component type ++ */ ++@NullMarked ++public interface ShownInTooltip<T> { ++ ++ /** ++ * 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 <B> builder type ++ */ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder<B> { ++ ++ /** ++ * Sets if the data component should be shown in the item's tooltip. ++ * ++ * @param showInTooltip {@code true} to show in the tooltip ++ * @return the builder for chaining ++ * @see #showInTooltip() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ B showInTooltip(boolean showInTooltip); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/SuspiciousStewEffects.java b/src/main/java/io/papermc/paper/datacomponent/item/SuspiciousStewEffects.java +new file mode 100644 +index 0000000000000000000000000000000000000000..422bb5ccc42b94b60fba6714e9e6fb8aced6eb0c +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/SuspiciousStewEffects.java +@@ -0,0 +1,67 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.datacomponent.DataComponentBuilder; ++import io.papermc.paper.potion.SuspiciousEffectEntry; ++import java.util.Arrays; ++import java.util.Collection; ++import java.util.List; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jetbrains.annotations.Unmodifiable; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Holds the effects that will be applied when consuming Suspicious Stew. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#SUSPICIOUS_STEW_EFFECTS ++ */ ++@NullMarked ++public interface SuspiciousStewEffects { ++ ++ @Contract(value = "_ -> new", pure = true) ++ static SuspiciousStewEffects suspiciousStewEffects(final Collection<SuspiciousEffectEntry> 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<SuspiciousEffectEntry> effects(); ++ ++ /** ++ * Builder for {@link SuspiciousStewEffects}. ++ */ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends DataComponentBuilder<SuspiciousStewEffects> { ++ ++ /** ++ * 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<SuspiciousEffectEntry> 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 ++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. ++ * ++ * <p>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.</p> ++ * ++ * @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: ++ * <ul> ++ * <li>{@link TriState#TRUE} - Items will be dropped.</li> ++ * <li>{@link TriState#FALSE} - Items will not be dropped.</li> ++ * <li>{@link TriState#NOT_SET} - The default drop behavior is used.</li> ++ * </ul> ++ * @return A new {@link Rule} instance representing the mining rule. ++ */ ++ static Rule rule(final RegistryKeySet<BlockType> 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<Tool.Rule> rules(); ++ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Rule { ++ ++ /** ++ * Blocks to match. ++ * ++ * @return blocks ++ */ ++ RegistryKeySet<BlockType> blocks(); ++ ++ /** ++ * Overrides the mining speed if present and matched. ++ * <p> ++ * {@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<Tool> { ++ ++ /** ++ * 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<Rule> 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 ++public interface Unbreakable extends ShownInTooltip<Unbreakable> { ++ ++ @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<Builder>, DataComponentBuilder<Unbreakable> { ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/UseCooldown.java b/src/main/java/io/papermc/paper/datacomponent/item/UseCooldown.java +new file mode 100644 +index 0000000000000000000000000000000000000000..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 ++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. ++ * <p> ++ * This allows items to share cooldowns with other items in the same cooldown group, if present. ++ * ++ * @return cooldown group, or null if not present ++ */ ++ @Contract(pure = true) ++ @Nullable Key cooldownGroup(); ++ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends DataComponentBuilder<UseCooldown> { ++ ++ /** ++ * Sets a unique resource location for this cooldown group. ++ * <p> ++ * This allows items to share cooldowns with other items in the same cooldown group. ++ * </p> ++ * ++ * @param key the unique resource location; can be null ++ * @return the builder for chaining ++ * @see #cooldownGroup() ++ */ ++ @Contract(value = "_ -> this", mutates = "this") ++ Builder cooldownGroup(@Nullable Key key); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/UseRemainder.java b/src/main/java/io/papermc/paper/datacomponent/item/UseRemainder.java +new file mode 100644 +index 0000000000000000000000000000000000000000..50e42e073311332142980828d0beec1828570512 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/UseRemainder.java +@@ -0,0 +1,31 @@ ++package io.papermc.paper.datacomponent.item; ++ ++import io.papermc.paper.datacomponent.DataComponentBuilder; ++import org.bukkit.inventory.ItemStack; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jspecify.annotations.NullMarked; ++ ++/** ++ * Holds the contents of item transformation information when an item is used. ++ * @see io.papermc.paper.datacomponent.DataComponentTypes#USE_REMAINDER ++ */ ++@NullMarked ++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 ++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<Filtered<String>> pages(); ++ ++ /** ++ * Builder for {@link WritableBookContent}. ++ */ ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface Builder extends DataComponentBuilder<WritableBookContent> { ++ ++ /** ++ * 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<String> 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<String> 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<Filtered<String>> 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 ++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<String> title, final String author) { ++ return ItemComponentTypesBridge.bridge().writtenBookContent(title, author); ++ } ++ ++ /** ++ * Title of this book. ++ * ++ * @return title ++ */ ++ @Contract(pure = true) ++ Filtered<String> 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<Filtered<Component>> 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<WrittenBookContent> { ++ ++ /** ++ * 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<String> 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<Filtered<? extends ComponentLike>> pages); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridge.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridge.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a845ccfc21f101f0632249745bbd8b334f85e72c +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridge.java +@@ -0,0 +1,32 @@ ++package io.papermc.paper.datacomponent.item.consumable; ++ ++import io.papermc.paper.registry.set.RegistryKeySet; ++import java.util.List; ++import java.util.Optional; ++import java.util.ServiceLoader; ++import net.kyori.adventure.key.Key; ++import org.bukkit.potion.PotionEffect; ++import org.bukkit.potion.PotionEffectType; ++import org.jetbrains.annotations.ApiStatus; ++import org.jspecify.annotations.NullMarked; ++ ++@NullMarked ++interface ConsumableTypesBridge { ++ ++ Optional<ConsumableTypesBridge> BRIDGE = ServiceLoader.load(ConsumableTypesBridge.class).findFirst(); ++ ++ static ConsumableTypesBridge bridge() { ++ return BRIDGE.orElseThrow(); ++ } ++ ++ ConsumeEffect.ApplyStatusEffects applyStatusEffects(List<PotionEffect> effectList, float probability); ++ ++ ConsumeEffect.RemoveStatusEffects removeStatusEffects(RegistryKeySet<PotionEffectType> potionEffectTypeTagKey); ++ ++ ConsumeEffect.ClearAllStatusEffects clearAllStatusEffects(); ++ ++ ConsumeEffect.PlaySound playSoundEffect(Key sound); ++ ++ ConsumeEffect.TeleportRandomly teleportRandomlyEffect(float diameter); ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumeEffect.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumeEffect.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ff1a14e19c21dd22f249503a0b738f190a75aca0 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumeEffect.java +@@ -0,0 +1,150 @@ ++package io.papermc.paper.datacomponent.item.consumable; ++ ++import io.papermc.paper.registry.set.RegistryKeySet; ++import net.kyori.adventure.key.Key; ++import org.bukkit.potion.PotionEffect; ++import org.bukkit.potion.PotionEffectType; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jspecify.annotations.NullMarked; ++import java.util.List; ++ ++/** ++ * Effect that occurs when consuming an item. ++ */ ++@NullMarked ++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<PotionEffectType> 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<PotionEffect> effects, final float probability) { ++ return ConsumableTypesBridge.bridge().applyStatusEffects(effects, probability); ++ } ++ ++ @NullMarked ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface TeleportRandomly extends ConsumeEffect { ++ ++ /** ++ * The max range that the entity can be teleported to. ++ * ++ * @return teleportation diameter ++ */ ++ float diameter(); ++ } ++ ++ /** ++ * Represents a consumable effect that removes status effects on consumption ++ */ ++ @NullMarked ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface RemoveStatusEffects extends ConsumeEffect { ++ ++ /** ++ * Potion effects to remove ++ * ++ * @return effects ++ */ ++ RegistryKeySet<PotionEffectType> removeEffects(); ++ } ++ ++ /** ++ * Represents a consumable effect that plays a sound on consumption. ++ */ ++ @NullMarked ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface PlaySound extends ConsumeEffect { ++ ++ /** ++ * Sound effect to play in the world ++ * ++ * @return sound effect ++ */ ++ Key sound(); ++ } ++ ++ /** ++ * Represents a consumable effect that clears all effects on consumption. ++ */ ++ @NullMarked ++ interface ClearAllStatusEffects extends ConsumeEffect { ++ ++ } ++ ++ /** ++ * Represents a consumable effect that applies effects based on a probability on consumption. ++ */ ++ @NullMarked ++ @ApiStatus.Experimental ++ @ApiStatus.NonExtendable ++ interface ApplyStatusEffects extends ConsumeEffect { ++ ++ /** ++ * Effect instances to grant ++ * ++ * @return effect ++ */ ++ List<PotionEffect> effects(); ++ ++ /** ++ * Float between 0 and 1, chance for the effect to be applied. ++ * ++ * @return chance ++ */ ++ float probability(); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/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<T> extends Keyed permits RegistryKeyImpl { + * @see io.papermc.paper.registry.keys.SoundEventKeys + */ + RegistryKey<Sound> SOUND_EVENT = create("sound_event"); ++ /** ++ * Built-in registry for data component types. ++ * <!-- @see io.papermc.paper.registry.keys.DataComponentTypeKeys --> ++ */ ++ RegistryKey<DataComponentType> 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 <T> type of value ++ */ ++@NullMarked ++public interface Filtered<T> { ++ ++ @Contract(value = "_, _ -> new", pure = true) ++ static <T> Filtered<T> of(final T raw, final @Nullable T filtered) { ++ @ApiStatus.Internal ++ record Instance<T>(T raw, @Nullable T filtered) implements Filtered<T> {} ++ ++ return new Instance<>(raw, filtered); ++ } ++ ++ @Contract(pure = true) ++ T raw(); ++ ++ @Contract(pure = true) ++ @Nullable ++ T filtered(); ++} +diff --git a/src/main/java/org/bukkit/Material.java b/src/main/java/org/bukkit/Material.java +index 615eb24ffdd8f6d55ccd4f21760b809c1098bc68..c7ce8fa1ff9feda66d5a4e497112a24ff51c9d2b 100644 +--- a/src/main/java/org/bukkit/Material.java ++++ b/src/main/java/org/bukkit/Material.java +@@ -137,7 +137,7 @@ import org.jetbrains.annotations.Nullable; + @SuppressWarnings({"DeprecatedIsStillUsed", "deprecation"}) // Paper + public enum Material implements Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper + //<editor-fold desc="Materials" defaultstate="collapsed"> +- AIR(9648, 0), ++ AIR(9648, 64), // Paper - air stacks to 64 + STONE(22948), + GRANITE(21091), + POLISHED_GRANITE(5477), +@@ -5784,6 +5784,7 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla + */ + @ApiStatus.Internal + @Nullable ++ @org.jetbrains.annotations.Contract(pure = true) // Paper + public ItemType asItemType() { + return itemType.get(); + } +@@ -5796,7 +5797,47 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla + */ + @ApiStatus.Internal + @Nullable ++ @org.jetbrains.annotations.Contract(pure = true) // Paper + public BlockType asBlockType() { + return blockType.get(); + } ++ ++ // Paper start - data component API ++ /** ++ * Gets the default value of the data component type for this item type. ++ * ++ * @param type the data component type ++ * @param <T> 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> T getDefaultData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued<T> 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 [email protected] @NotNull Set<io.papermc.paper.datacomponent.DataComponentType> getDefaultDataTypes() { ++ Preconditions.checkArgument(this.asItemType() != null); ++ return this.asItemType().getDefaultDataTypes(); ++ } ++ // Paper end - data component API + } +diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java +index 7cf7c6d05aa6cbf3f0c8612831404552c6a7b84a..c60e31425efd7b863941f5538faef6c0552290ae 100644 +--- a/src/main/java/org/bukkit/Registry.java ++++ b/src/main/java/org/bukkit/Registry.java +@@ -376,6 +376,7 @@ public interface Registry<T extends Keyed> extends Iterable<T> { + */ + Registry<org.bukkit.potion.PotionEffectType> POTION_EFFECT_TYPE = EFFECT; + // Paper end - potion effect type registry ++ Registry<io.papermc.paper.datacomponent.DataComponentType> DATA_COMPONENT_TYPE = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.DATA_COMPONENT_TYPE); // Paper + /** + * Get the object by its key. + * +diff --git a/src/main/java/org/bukkit/block/BlockType.java b/src/main/java/org/bukkit/block/BlockType.java +index ed534fe4983873a2d5f623f0d9d5e3ce254615eb..f019d490794b49d21057820bab047e2f909934a1 100644 +--- a/src/main/java/org/bukkit/block/BlockType.java ++++ b/src/main/java/org/bukkit/block/BlockType.java +@@ -128,7 +128,7 @@ import org.jetbrains.annotations.Nullable; + * official replacement for the aforementioned enum. Entirely incompatible + * changes may occur. Do not use this API in plugins. + */ [email protected] // Paper - data component API - already required for data component API + public interface BlockType extends Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - add translatable & feature flag API + + /** +diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java +index b59222b8c262545d100a9fd28b3bf1d2a4cf4eb0..9c5f61ea845237e78cc3da61c06506e14351f228 100644 +--- a/src/main/java/org/bukkit/inventory/ItemStack.java ++++ b/src/main/java/org/bukkit/inventory/ItemStack.java +@@ -1,7 +1,6 @@ + package org.bukkit.inventory; + + import com.google.common.base.Preconditions; +-import com.google.common.collect.ImmutableMap; + import java.util.LinkedHashMap; + import java.util.Locale; + import java.util.Map; +@@ -1137,4 +1136,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 <T> 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 <T> @Nullable T getData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued<T> 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 <T> 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 <T> @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 [email protected] Set<io.papermc.paper.datacomponent.@NotNull DataComponentType> 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 <T> value type ++ */ ++ @Utility ++ @org.jetbrains.annotations.ApiStatus.Experimental ++ public <T> void setData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued<T> type, final @NotNull io.papermc.paper.datacomponent.DataComponentBuilder<T> 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. ++ // * ++ // * <p>If the function returns {@code null}, the data component type will be reset using ++ // * {@link #unsetData(DataComponentType)}. Otherwise, the ++ // * component value will be updated with the new result using {@link #setData(DataComponentType.Valued, Object)}.</p> ++ // * ++ // * @param <T> the type of the data component's value ++ // * @param type the data component type to be modified ++ // * @param consumer a function that takes the current component value (can be {@code null}) and ++ // * returns the modified value (or {@code null} to unset) ++ // */ ++ // @Utility ++ // public <T> void editData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued<T> type, final @NotNull java.util.function.Function<@Nullable T, @Nullable T> consumer) { ++ // T value = getData(type); ++ // T newType = consumer.apply(value); ++ // if (newType == null) { ++ // unsetData(type); ++ // } else { ++ // setData(type, newType); ++ // } ++ // } ++ ++ /** ++ * Sets the value of the data component type for this itemstack. To ++ * reset the value to the default for the {@link #getType() item type}, use ++ * {@link #resetData(io.papermc.paper.datacomponent.DataComponentType)}. To mark the data component type ++ * as removed, use {@link #unsetData(io.papermc.paper.datacomponent.DataComponentType)}. ++ * ++ * @param type the data component type ++ * @param value value to set ++ * @param <T> value type ++ */ ++ @org.jetbrains.annotations.ApiStatus.Experimental ++ public <T> void setData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued<T> 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<io.papermc.paper.datacomponent.@NotNull DataComponentType> 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<io.papermc.paper.datacomponent.@NotNull DataComponentType> excludeTypes, final boolean ignoreCount) { ++ return this.craftDelegate.matchesWithoutData(item, excludeTypes, ignoreCount); ++ } ++ // Paper end - data component API + } +diff --git a/src/main/java/org/bukkit/inventory/ItemType.java b/src/main/java/org/bukkit/inventory/ItemType.java +index 72803c00e4af576f286d2af34bf300ee554a7f3c..2a3c4f055d0e4ef009caed95152570660ab100d5 100644 +--- a/src/main/java/org/bukkit/inventory/ItemType.java ++++ b/src/main/java/org/bukkit/inventory/ItemType.java +@@ -2483,4 +2483,33 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans + */ + @Nullable ItemRarity getItemRarity(); + // Paper end - expand ItemRarity API ++ // Paper start - data component API ++ /** ++ * Gets the default value of the data component type for this item type. ++ * ++ * @param type the data component type ++ * @param <T> 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> T getDefaultData(io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued<T> 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 ++ [email protected] @NotNull Set<io.papermc.paper.datacomponent.DataComponentType> getDefaultDataTypes(); ++ // Paper end - data component API + } |