From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> Date: Sun, 28 Apr 2024 19:53:06 -0400 Subject: [PATCH] WIP DataComponent API 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 +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface BlockPredicate { + + static Builder predicate() { + // + record BlockPredicateImpl(@Nullable RegistryKeySet blocks) implements BlockPredicate { + } + + class BuilderImpl implements Builder { + + private @Nullable RegistryKeySet blocks; + + @Override + public Builder blocks(final @Nullable RegistryKeySet blocks) { + this.blocks = blocks; + return this; + } + + @Override + public BlockPredicate build() { + return new BlockPredicateImpl(this.blocks); + } + } + // + return new BuilderImpl(); + } + + @Nullable RegistryKeySet blocks(); + + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder { + + @Contract(value = "_ -> this", mutates = "this") + Builder blocks(@Nullable RegistryKeySet 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 +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface BuildableDataComponent, B extends DataComponentBuilder> { + + /** + * 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 built component type + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface DataComponentBuilder { + + /** + * 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 +@ApiStatus.Experimental +@ApiStatus.NonExtendable +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 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 +@ApiStatus.Experimental +public final class DataComponentTypes { + + /** + * Controls the maximum stacking size of this item. + *
+ * 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. + *
+ * 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 = valued("unbreakable"); + /** + * Custom name override for an item (as set by renaming with an Anvil). + * + * @see #ITEM_NAME + */ + public static final DataComponentType.Valued CUSTOM_NAME = valued("custom_name"); + /** + * When present, replaces default item name with contained chat component. + *

+ * Differences from {@link #CUSTOM_NAME}: + *

    + *
  • can't be changed or removed in Anvil
  • + *
  • is not styled with italics when displayed to player
  • + *
  • does not show labels where applicable + * (for example: banner markers, names in item frames)
  • + *
+ * + * @see #CUSTOM_NAME + */ + public static final DataComponentType.Valued ITEM_NAME = valued("item_name"); + public static final DataComponentType.Valued ITEM_MODEL = valued("item_model"); + /** + * Additional lines to include in an item's tooltip. + */ + public static final DataComponentType.Valued LORE = valued("lore"); + /** + * Controls the color of the item name. + */ + public static final DataComponentType.Valued RARITY = valued("rarity"); + /** + * Controls the enchantments on an item. + *
+ * If not present on a non-enchantment book, this item will not work in an anvil. + * + * @see #STORED_ENCHANTMENTS + */ + public static final DataComponentType.Valued ENCHANTMENTS = valued("enchantments"); + /** + * Controls which blocks a player in Adventure mode can place on with this item. + */ + public static final DataComponentType.Valued 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 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 ATTRIBUTE_MODIFIERS = valued("attribute_modifiers"); + /** + * Controls the minecraft:custom_model_data property in the item model. + */ + public static final DataComponentType.Valued 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 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 FOOD = valued("food"); + public static final DataComponentType.Valued CONSUMABLE = valued("consumable"); + public static final DataComponentType.Valued USE_REMAINDER = valued("use_remainder"); + public static final DataComponentType.Valued USE_COOLDOWN = valued("use_cooldown"); + /** + * If present, this item will not burn in fire. + */ + public static final DataComponentType.Valued DAMAGE_RESISTANT = valued("damage_resistant"); + /** + * Controls the behavior of the item as a tool. + */ + public static final DataComponentType.Valued TOOL = valued("tool"); + public static final DataComponentType.Valued ENCHANTABLE = valued("enchantable"); + public static final DataComponentType.Valued EQUIPPABLE = valued("equippable"); + public static final DataComponentType.Valued REPAIRABLE = valued("repairable"); + public static final DataComponentType.NonValued GLIDER = unvalued("glider"); + public static final DataComponentType.Valued TOOLTIP_STYLE = valued("tooltip_style"); + public static final DataComponentType.Valued DEATH_PROTECTION = valued("death_protection"); + /** + * Stores list of enchantments and their levels for an Enchanted Book. + * Unlike {@link #ENCHANTMENTS}, the effects provided by enchantments + * do not apply from this component. + *
+ * If not present on an Enchanted Book, it will not work in an anvil. + *

+ * Has an undefined behaviour if present on an item that is not an Enchanted Book + * (currently the presence of this component allows enchantments from {@link #ENCHANTMENTS} + * to be applied as if this item was an Enchanted Book). + * + * @see #ENCHANTMENTS + */ + public static final DataComponentType.Valued STORED_ENCHANTMENTS = valued("stored_enchantments"); + /** + * Represents a color applied to a dyeable item (in the {@link io.papermc.paper.registry.keys.tags.ItemTypeTagKeys#DYEABLE} item tag). + */ + public static final DataComponentType.Valued DYED_COLOR = valued("dyed_color"); + /** + * Represents the tint of the decorations on the {@link org.bukkit.inventory.ItemType#FILLED_MAP} item. + */ + public static final DataComponentType.Valued MAP_COLOR = valued("map_color"); + /** + * References the shared map state holding map contents and markers for a {@link org.bukkit.inventory.ItemType#FILLED_MAP}. + */ + public static final DataComponentType.Valued MAP_ID = valued("map_id"); + /** + * Holds a list of markers to be placed on a {@link org.bukkit.inventory.ItemType#FILLED_MAP} (used for Explorer Maps). + */ + public static final DataComponentType.Valued MAP_DECORATIONS = valued("map_decorations"); + /** + * Internal map item state used in the map crafting recipe. + */ + public static final DataComponentType.Valued MAP_POST_PROCESSING = valued("map_post_processing"); + /** + * Holds all projectiles that have been loaded into a Crossbow. + * If not present, the Crossbow is not charged. + */ + public static final DataComponentType.Valued CHARGED_PROJECTILES = valued("charged_projectiles"); + /** + * Holds all items stored inside a Bundle. + * If removed, items cannot be added to the Bundle. + */ + public static final DataComponentType.Valued BUNDLE_CONTENTS = valued("bundle_contents"); + /** + * Holds the contents of a potion (Potion, Splash Potion, Lingering Potion), + * or potion applied to a Tipped Arrow. + */ + public static final DataComponentType.Valued POTION_CONTENTS = valued("potion_contents"); + /** + * Holds the effects that will be applied when consuming Suspicious Stew. + */ + public static final DataComponentType.Valued SUSPICIOUS_STEW_EFFECTS = valued("suspicious_stew_effects"); + /** + * Holds the contents in a Book and Quill. + */ + public static final DataComponentType.Valued WRITABLE_BOOK_CONTENT = valued("writable_book_content"); + /** + * Holds the contents and metadata of a Written Book. + */ + public static final DataComponentType.Valued WRITTEN_BOOK_CONTENT = valued("written_book_content"); + /** + * Holds the trims applied to an item in recipes + */ + public static final DataComponentType.Valued TRIM = valued("trim"); + // debug_stick_state - Block Property API + // entity_data + // bucket_entity_data + // block_entity_data + /** + * Holds the instrument type used by a Goat Horn. + */ + public static final DataComponentType.Valued INSTRUMENT = valued("instrument"); + /** + * Controls the amplifier amount for an Ominous Bottle's Bad Omen effect. + */ + public static final DataComponentType.Valued OMINOUS_BOTTLE_AMPLIFIER = valued("ominous_bottle_amplifier"); + /** + * List of recipes that should be unlocked when using the Knowledge Book item. + */ + public static final DataComponentType.Valued JUKEBOX_PLAYABLE = valued("jukebox_playable"); + public static final DataComponentType.Valued> RECIPES = valued("recipes"); + /** + * If present, specifies that the Compass is a Lodestone Compass. + */ + public static final DataComponentType.Valued LODESTONE_TRACKER = valued("lodestone_tracker"); + /** + * Stores the explosion crafted in a Firework Star. + */ + public static final DataComponentType.Valued FIREWORK_EXPLOSION = valued("firework_explosion"); + /** + * Stores all explosions crafted into a Firework Rocket, as well as flight duration. + */ + public static final DataComponentType.Valued FIREWORKS = valued("fireworks"); + /** + * Controls the skin displayed on a Player Head. + */ + public static final DataComponentType.Valued PROFILE = valued("profile"); + /** + * Controls the sound played by a Player Head when placed on a Note Block. + */ + public static final DataComponentType.Valued NOTE_BLOCK_SOUND = valued("note_block_sound"); + /** + * Stores the additional patterns applied to a Banner or Shield. + */ + public static final DataComponentType.Valued BANNER_PATTERNS = valued("banner_patterns"); + /** + * Stores the base color for a Shield. + */ + public static final DataComponentType.Valued BASE_COLOR = valued("base_color"); + /** + * Stores the Sherds applied to each side of a Decorated Pot. + */ + public static final DataComponentType.Valued POT_DECORATIONS = valued("pot_decorations"); + /** + * Holds the contents of container blocks (Chests, Shulker Boxes) in item form. + */ + public static final DataComponentType.Valued CONTAINER = valued("container"); + /** + * Holds block state properties to apply when placing a block. + */ + public static final DataComponentType.Valued BLOCK_DATA = valued("block_state"); + // bees + // /** + // * Holds the lock state of a container-like block, + // * copied to container block when placed. + // *
+ // * An item with a custom name of the same value must be used + // * to open this container. + // */ + // public static final DataComponentType.Valued LOCK = valued("lock"); + /** + * Holds the unresolved loot table and seed of a container-like block. + */ + public static final DataComponentType.Valued CONTAINER_LOOT = valued("container_loot"); + + private static DataComponentType.NonValued unvalued(final String name) { + return (DataComponentType.NonValued) requireNonNull(Registry.DATA_COMPONENT_TYPE.get(NamespacedKey.minecraft(name)), name + " unvalued data component type couldn't be found, this is a bug."); + } + + @SuppressWarnings("unchecked") + private static DataComponentType.Valued valued(final String name) { + return (DataComponentType.Valued) requireNonNull(Registry.DATA_COMPONENT_TYPE.get(NamespacedKey.minecraft(name)), name + " valued data component type couldn't be found, this is a bug."); + } + + private DataComponentTypes() { + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/BannerPatternLayers.java b/src/main/java/io/papermc/paper/datacomponent/item/BannerPatternLayers.java new file mode 100644 index 0000000000000000000000000000000000000000..12cfae82234b8c4cb231ab91e72ad82d28b85183 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/BannerPatternLayers.java @@ -0,0 +1,66 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import java.util.Arrays; +import java.util.List; +import org.bukkit.block.banner.Pattern; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the layers of patterns on a banner. + * @see io.papermc.paper.datacomponent.DataComponentTypes#BANNER_PATTERNS + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface BannerPatternLayers { + + @Contract(value = "_ -> new", pure = true) + static BannerPatternLayers bannerPatternLayers(final List patterns) { + return bannerPatternLayers().addAll(patterns).build(); + } + + @Contract(value = "-> new", pure = true) + static BannerPatternLayers.Builder bannerPatternLayers() { + return ItemComponentTypesBridge.bridge().bannerPatternLayers(); + } + + /** + * Gets the patterns on the banner. + * + * @return the patterns + */ + @Contract(pure = true) + @Unmodifiable List patterns(); + + /** + * Builder for {@link BannerPatternLayers}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Adds a pattern to the banner. + * + * @param pattern the pattern + * @return the builder for chaining + * @see #patterns() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder add(Pattern pattern); + + /** + * Adds multiple patterns to the banner. + * + * @param patterns the patterns + * @return the builder for chaining + * @see #patterns() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addAll(List patterns); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/BlockItemDataProperties.java b/src/main/java/io/papermc/paper/datacomponent/item/BlockItemDataProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..65f1bc8d1bea0042dca9683c439561132dbeea5c --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/BlockItemDataProperties.java @@ -0,0 +1,51 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import org.bukkit.block.BlockType; +import org.bukkit.block.data.BlockData; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the {@link BlockData} properties of a block item. + * @see io.papermc.paper.datacomponent.DataComponentTypes#BLOCK_DATA + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface BlockItemDataProperties { + + @Contract(value = "-> new", pure = true) + static BlockItemDataProperties.Builder blockItemStateProperties() { + return ItemComponentTypesBridge.bridge().blockItemStateProperties(); + } + + /** + * Creates a new {@link BlockData} instance for the given {@link BlockType}. + * + * @param blockType the block type + * @return the block data + */ + @Contract(pure = true) + BlockData createBlockData(BlockType blockType); + + /** + * Applies the properties to the given {@link BlockData}. Doesn't + * mutate the parameter, but returns a new instance with the properties applied. + * + * @param blockData the block data to apply the properties to + * @return the block data with the properties applied + */ + @Contract(pure = true) + BlockData applyTo(BlockData blockData); + + /** + * Builder for {@link BlockItemDataProperties}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + // building this requires BlockProperty API, so an empty builder for now (essentially read-only) + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/BundleContents.java b/src/main/java/io/papermc/paper/datacomponent/item/BundleContents.java new file mode 100644 index 0000000000000000000000000000000000000000..c0f671aef8225c87632d2368d1b28fc8b1bce686 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/BundleContents.java @@ -0,0 +1,66 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import java.util.Arrays; +import java.util.List; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Holds all items stored inside of a Bundle. + * @see io.papermc.paper.datacomponent.DataComponentTypes#BUNDLE_CONTENTS + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface BundleContents { + + @Contract(value = "_ -> new", pure = true) + static BundleContents bundleContents(final List contents) { + return ItemComponentTypesBridge.bridge().bundleContents().addAll(contents).build(); + } + + @Contract(value = "-> new", pure = true) + static BundleContents.Builder bundleContents() { + return ItemComponentTypesBridge.bridge().bundleContents(); + } + + /** + * Lists the items that are currently stored inside of this component. + * + * @return items + */ + @Contract(value = "-> new", pure = true) + @Unmodifiable List contents(); + + /** + * Builder for {@link BundleContents}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Adds an item to this builder. + * + * @param stack item + * @return the builder for chaining + * @see #contents() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder add(ItemStack stack); + + /** + * Adds items to this builder. + * + * @param stacks items + * @return the builder for chaining + * @see #contents() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addAll(List stacks); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ChargedProjectiles.java b/src/main/java/io/papermc/paper/datacomponent/item/ChargedProjectiles.java new file mode 100644 index 0000000000000000000000000000000000000000..d0a6e7db06f540e13ac00e8da3acabd9f7838f1f --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/ChargedProjectiles.java @@ -0,0 +1,66 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import java.util.Arrays; +import java.util.List; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Holds all projectiles that have been loaded into a Crossbow. + * @see io.papermc.paper.datacomponent.DataComponentTypes#CHARGED_PROJECTILES + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface ChargedProjectiles { + + @Contract(value = "_ -> new", pure = true) + static ChargedProjectiles chargedProjectiles(final List projectiles) { + return chargedProjectiles().addAll(projectiles).build(); + } + + @Contract(value = "-> new", pure = true) + static ChargedProjectiles.Builder chargedProjectiles() { + return ItemComponentTypesBridge.bridge().chargedProjectiles(); + } + + /** + * Lists the projectiles that are currently loaded into this component. + * + * @return the loaded projectiles + */ + @Contract(value = "-> new", pure = true) + @Unmodifiable List projectiles(); + + /** + * Builder for {@link ChargedProjectiles}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Adds a projectile to be loaded in this builder. + * + * @param stack projectile + * @return the builder for chaining + * @see #projectiles() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder add(ItemStack stack); + + /** + * Adds projectiles to be loaded in this builder. + * + * @param stacks projectiles + * @return the builder for chaining + * @see #projectiles() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addAll(List stacks); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Consumable.java b/src/main/java/io/papermc/paper/datacomponent/item/Consumable.java new file mode 100644 index 0000000000000000000000000000000000000000..a448fedb63ffce18b9f6a1bd0fecfc5cd90224a6 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/Consumable.java @@ -0,0 +1,70 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.BuildableDataComponent; +import io.papermc.paper.datacomponent.DataComponentBuilder; +import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect; +import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation; +import java.util.Collection; +import java.util.List; +import net.kyori.adventure.key.Key; +import org.checkerframework.checker.index.qual.NonNegative; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the properties for this item for when it is consumed. + * @see io.papermc.paper.datacomponent.DataComponentTypes#CONSUMABLE + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface Consumable extends BuildableDataComponent { + + @Contract(value = "-> new", pure = true) + static Consumable.Builder consumable() { + return ItemComponentTypesBridge.bridge().consumable(); + } + + @Contract(pure = true) + @NonNegative float consumeSeconds(); + + @Contract(pure = true) + ItemUseAnimation animation(); + + @Contract(pure = true) + Key sound(); + + @Contract(pure = true) + boolean hasConsumeParticles(); + + @Contract(pure = true) + @Unmodifiable List consumeEffects(); + + /** + * Builder for {@link Consumable}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + @Contract(value = "_ -> this", mutates = "this") + Builder consumeSeconds(@NonNegative float consumeSeconds); + + @Contract(value = "_ -> this", mutates = "this") + Builder animation(ItemUseAnimation animation); + + @Contract(value = "_ -> this", mutates = "this") + Builder sound(Key sound); + + @Contract(value = "_ -> this", mutates = "this") + Builder hasConsumeParticles(boolean hasConsumeParticles); + + @Contract(value = "_ -> this", mutates = "this") + Builder addEffect(ConsumeEffect effect); + + @Contract(value = "_ -> this", mutates = "this") + Builder addEffects(List effects); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/CustomModelData.java b/src/main/java/io/papermc/paper/datacomponent/item/CustomModelData.java new file mode 100644 index 0000000000000000000000000000000000000000..d416c9d25b3ab88bf1e208c6faf92a8e2378c376 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/CustomModelData.java @@ -0,0 +1,28 @@ +package io.papermc.paper.datacomponent.item; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the custom model data. + * @see io.papermc.paper.datacomponent.DataComponentTypes#CUSTOM_MODEL_DATA + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface CustomModelData { + + @Contract(value = "_ -> new", pure = true) + static CustomModelData customModelData(final int id) { + return ItemComponentTypesBridge.bridge().customModelData(id); + } + + /** + * Gets the custom model data id. + * + * @return the id + */ + @Contract(pure = true) + int id(); +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java b/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java new file mode 100644 index 0000000000000000000000000000000000000000..6cbd73cb2a11f4858b44a2f57d2fe0acb1eb9fb5 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java @@ -0,0 +1,30 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.registry.tag.TagKey; +import org.bukkit.damage.DamageType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the contents of damage types that the item entity containing this item is invincible to. + * @see io.papermc.paper.datacomponent.DataComponentTypes#DAMAGE_RESISTANT + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface DamageResistant { + + @Contract(value = "_ -> new", pure = true) + static DamageResistant damageResistant(final TagKey types) { + return ItemComponentTypesBridge.bridge().damageResistant(types); + } + + /** + * The types that this damage type is invincible tp. + * + * @return item + */ + @Contract(value = "-> new", pure = true) + TagKey types(); +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/DeathProtection.java b/src/main/java/io/papermc/paper/datacomponent/item/DeathProtection.java new file mode 100644 index 0000000000000000000000000000000000000000..87c2220708af7db06348994ad5940c7cecd9f691 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/DeathProtection.java @@ -0,0 +1,48 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Sets whether this item should protect the entity upon death, and what effects should be played. + * @see io.papermc.paper.datacomponent.DataComponentTypes#DEATH_PROTECTION + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface DeathProtection { + + @Contract(value = "_ -> new", pure = true) + static DeathProtection deathProtection(final List deathEffects) { + return deathProtection().addEffects(deathEffects).build(); + } + + @Contract(value = "-> new", pure = true) + static DeathProtection.Builder deathProtection() { + return ItemComponentTypesBridge.bridge().deathProtection(); + } + + @Contract(value = "-> new", pure = true) + @Unmodifiable List deathEffects(); + + /** + * Builder for {@link DeathProtection}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + @Contract(value = "_ -> this", mutates = "this") + Builder addEffect(ConsumeEffect effect); + + @Contract(value = "_ -> this", mutates = "this") + Builder addEffects(List effects); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/DyedItemColor.java b/src/main/java/io/papermc/paper/datacomponent/item/DyedItemColor.java new file mode 100644 index 0000000000000000000000000000000000000000..d80581fc8b894cc4d4af9741244b1bb03468b263 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/DyedItemColor.java @@ -0,0 +1,53 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import org.bukkit.Color; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Represents a color applied to a dyeable item. + * @see io.papermc.paper.datacomponent.DataComponentTypes#DYED_COLOR + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface DyedItemColor extends ShownInTooltip { + + @Contract(value = "_, _ -> new", pure = true) + static DyedItemColor dyedItemColor(final Color color, final boolean showInTooltip) { + return dyedItemColor().color(color).showInTooltip(showInTooltip).build(); + } + + @Contract(value = "-> new", pure = true) + static DyedItemColor.Builder dyedItemColor() { + return ItemComponentTypesBridge.bridge().dyedItemColor(); + } + + /** + * Color of the item. + * + * @return color + */ + @Contract(value = "-> new", pure = true) + Color color(); + + /** + * Builder for {@link DyedItemColor}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends ShownInTooltip.Builder, DataComponentBuilder { + + /** + * Sets the color of this builder. + * + * @param color color + * @return the builder for chaining + * @see #color() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder color(Color color); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Enchantable.java b/src/main/java/io/papermc/paper/datacomponent/item/Enchantable.java new file mode 100644 index 0000000000000000000000000000000000000000..5169b9cd73dc0ffc8297f8d5f63d3d707a47d279 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/Enchantable.java @@ -0,0 +1,31 @@ +package io.papermc.paper.datacomponent.item; + +import org.checkerframework.checker.index.qual.Positive; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Holds if an item is enchantable, allowing for enchantments of the type to be seen in an enchanting table. + * @see io.papermc.paper.datacomponent.DataComponentTypes#ENCHANTABLE + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface Enchantable { + + @Contract(value = "_ -> new", pure = true) + static Enchantable enchantable(final @Positive int level) { + return ItemComponentTypesBridge.bridge().enchantable(level); + } + + /** + * Gets the current enchantment value level allowed, + * a higher value allows enchantments with a higher cost to be picked. + * + * @see Minecraft Wiki + * @return the value + */ + @Contract(pure = true) + @Positive int value(); +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Equippable.java b/src/main/java/io/papermc/paper/datacomponent/item/Equippable.java new file mode 100644 index 0000000000000000000000000000000000000000..7d84217814bba4ce826e33755fee0d5c3b280009 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/Equippable.java @@ -0,0 +1,170 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.BuildableDataComponent; +import io.papermc.paper.datacomponent.DataComponentBuilder; +import io.papermc.paper.registry.set.RegistryKeySet; +import net.kyori.adventure.key.Key; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.EquipmentSlot; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + + +/** + * Holds the equippable properties of an item. + * @see io.papermc.paper.datacomponent.DataComponentTypes#EQUIPPABLE + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface Equippable extends BuildableDataComponent { + + /** + * Creates a new {@link Equippable.Builder} instance. + * @param slot The slot for the new equippable to be equippable in. + * + * @return a new builder + */ + @Contract(value = "_ -> new", pure = true) + static Equippable.Builder equippable(final EquipmentSlot slot) { + return ItemComponentTypesBridge.bridge().equippable(slot); + } + + /** + * Gets the equipment slot this item can be equipped in. + * + * @return the equipment slot + */ + @Contract(pure = true) + EquipmentSlot slot(); + + /** + * Gets the equip sound key. + * + * @return the equip sound key + */ + @Contract(pure = true) + Key equipSound(); + + /** + * Gets the model key if present. + * + * @return the model key or null + */ + @Contract(pure = true) + @Nullable Key model(); + + /** + * Gets the camera overlay key if present. + * + * @return the camera overlay key or null + */ + @Contract(pure = true) + @Nullable Key cameraOverlay(); + + /** + * Gets the set of allowed entities that can equip this item. + * May be null if all entities are allowed. + * + * @return the set of allowed entities + */ + @Contract(pure = true) + @Nullable RegistryKeySet allowedEntities(); + + /** + * Checks if the item is dispensable. + * + * @return true if dispensable, false otherwise + */ + @Contract(pure = true) + boolean dispensable(); + + /** + * Checks if the item is swappable. + * + * @return true if swappable, false otherwise + */ + @Contract(pure = true) + boolean swappable(); + + /** + * Checks if the item takes damage when the wearer is hurt. + * + * @return true if it damages on hurt, false otherwise + */ + @Contract(pure = true) + boolean damageOnHurt(); + + /** + * Builder for {@link Equippable}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Sets the equip sound key for this item. + * + * @param equipSound the equip sound key + * @return the builder for chaining + */ + @Contract(value = "_ -> this", mutates = "this") + Builder equipSound(Key equipSound); + + /** + * Sets the model key for this item. + * + * @param model the model key, nullable + * @return the builder for chaining + */ + @Contract(value = "_ -> this", mutates = "this") + Builder model(@Nullable Key model); + + /** + * Sets the camera overlay key for this item. + * + * @param cameraOverlay the camera overlay key, nullable + * @return the builder for chaining + */ + @Contract(value = "_ -> this", mutates = "this") + Builder cameraOverlay(@Nullable Key cameraOverlay); + + /** + * Sets the allowed entities that can equip this item. + * + * @param allowedEntities the set of allowed entity types, or null if any + * @return the builder for chaining + */ + @Contract(value = "_ -> this", mutates = "this") + Builder allowedEntities(@Nullable RegistryKeySet allowedEntities); + + /** + * Sets whether the item is dispensable. + * + * @param dispensable true if dispensable + * @return the builder for chaining + */ + @Contract(value = "_ -> this", mutates = "this") + Builder dispensable(boolean dispensable); + + /** + * Sets whether the item is swappable. + * + * @param swappable true if swappable + * @return the builder for chaining + */ + @Contract(value = "_ -> this", mutates = "this") + Builder swappable(boolean swappable); + + /** + * Sets whether the item takes damage when the wearer is hurt. + * + * @param damageOnHurt true if it damages on hurt + * @return the builder for chaining + */ + @Contract(value = "_ -> this", mutates = "this") + Builder damageOnHurt(boolean damageOnHurt); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Fireworks.java b/src/main/java/io/papermc/paper/datacomponent/item/Fireworks.java new file mode 100644 index 0000000000000000000000000000000000000000..72aa1b4bda2693e0cd78d93449dda23bd1b74062 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/Fireworks.java @@ -0,0 +1,84 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import java.util.List; +import org.bukkit.FireworkEffect; +import org.checkerframework.common.value.qual.IntRange; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Stores all explosions crafted into a Firework Rocket, as well as flight duration. + * @see io.papermc.paper.datacomponent.DataComponentTypes#FIREWORKS + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface Fireworks { + + @Contract(value = "_, _ -> new", pure = true) + static Fireworks fireworks(final List effects, final int flightDuration) { + return fireworks().addEffects(effects).flightDuration(flightDuration).build(); + } + + @Contract(value = "-> new", pure = true) + static Fireworks.Builder fireworks() { + return ItemComponentTypesBridge.bridge().fireworks(); + } + + /** + * Lists the effects stored in this component. + * + * @return the effects + */ + @Contract(pure = true) + @Unmodifiable List effects(); + + /** + * Number of gunpowder in this component. + * + * @return the flight duration + */ + @Contract(pure = true) + @IntRange(from = 0, to = 255) int flightDuration(); + + /** + * Builder for {@link Fireworks}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Sets the number of gunpowder used in this builder. + * + * @param duration duration + * @return the builder for chaining + * @see #flightDuration() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder flightDuration(@IntRange(from = 0, to = 255) int duration); + + /** + * Adds an explosion to this builder. + * + * @param effect effect + * @return the builder for chaining + * @see #effects() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addEffect(FireworkEffect effect); + + /** + * Adds explosions to this builder. + * + * @param effects effects + * @return the builder for chaining + * @see #effects() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addEffects(List effects); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/FoodProperties.java b/src/main/java/io/papermc/paper/datacomponent/item/FoodProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..369208e15a0e7fc91a9505fef2097c4283445e4a --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/FoodProperties.java @@ -0,0 +1,87 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.BuildableDataComponent; +import io.papermc.paper.datacomponent.DataComponentBuilder; +import org.checkerframework.checker.index.qual.NonNegative; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the food properties of an item. + * @see io.papermc.paper.datacomponent.DataComponentTypes#FOOD + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface FoodProperties extends BuildableDataComponent { + + @Contract(value = "-> new", pure = true) + static FoodProperties.Builder food() { + return ItemComponentTypesBridge.bridge().food(); + } + + /** + * Number of food points to restore when eaten. + * + * @return the nutrition + */ + @Contract(pure = true) + @NonNegative int nutrition(); + + /** + * Amount of saturation to restore when eaten. + * + * @return the saturation + */ + @Contract(pure = true) + float saturation(); + + /** + * If {@code true}, this food can be eaten even if not hungry. + * + * @return can always be eaten + */ + @Contract(pure = true) + boolean canAlwaysEat(); + + /** + * Builder for {@link FoodProperties}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Set if this food can always be eaten, even if the + * player is not hungry. + * + * @param canAlwaysEat true to allow always eating + * @return the builder for chaining + * @see #canAlwaysEat() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder canAlwaysEat(boolean canAlwaysEat); + + /** + * Sets the saturation of the food. + * + * @param saturation the saturation + * @return the builder for chaining + * @see #saturation() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder saturation(float saturation); + + /** + * Sets the nutrition of the food. + * + * @param nutrition the nutrition, must be non-negative + * @return the builder for chaining + * @see #nutrition() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder nutrition(@NonNegative int nutrition); + + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemAdventurePredicate.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemAdventurePredicate.java new file mode 100644 index 0000000000000000000000000000000000000000..f5061d1f349b35e5ec57d2d1c64eafb096141404 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemAdventurePredicate.java @@ -0,0 +1,65 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.block.BlockPredicate; +import io.papermc.paper.datacomponent.DataComponentBuilder; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Controls which blocks a player in Adventure mode can do a certain action with this item. + * @see io.papermc.paper.datacomponent.DataComponentTypes#CAN_BREAK + * @see io.papermc.paper.datacomponent.DataComponentTypes#CAN_PLACE_ON + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface ItemAdventurePredicate extends ShownInTooltip { + + @Contract(value = "_ -> new", pure = true) + static ItemAdventurePredicate itemAdventurePredicate(final List predicates) { + return itemAdventurePredicate().addPredicates(predicates).build(); + } + + @Contract(value = "-> new", pure = true) + static ItemAdventurePredicate.Builder itemAdventurePredicate() { + return ItemComponentTypesBridge.bridge().itemAdventurePredicate(); + } + + /** + * List of block predicates that control if the action is allowed. + * + * @return predicates + */ + @Contract(pure = true) + @Unmodifiable List predicates(); + + /** + * Builder for {@link ItemAdventurePredicate}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends ShownInTooltip.Builder, DataComponentBuilder { + /** + * Adds a block predicate to this builder. + * + * @param predicate predicate + * @return the builder for chaining + * @see #predicates() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addPredicate(BlockPredicate predicate); + + /** + * Adds block predicates to this builder. + * + * @param predicates predicates + * @return the builder for chaining + * @see #predicates() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addPredicates(List predicates); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemArmorTrim.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemArmorTrim.java new file mode 100644 index 0000000000000000000000000000000000000000..0309ae59ab7945ddfb5410930d161e2ce3d1878a --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemArmorTrim.java @@ -0,0 +1,53 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import org.bukkit.inventory.meta.trim.ArmorTrim; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the trims applied to an item. + * @see io.papermc.paper.datacomponent.DataComponentTypes#TRIM + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface ItemArmorTrim extends ShownInTooltip { + + @Contract(value = "_, _ -> new", pure = true) + static ItemArmorTrim itemArmorTrim(final ArmorTrim armorTrim, final boolean showInTooltip) { + return itemArmorTrim(armorTrim).showInTooltip(showInTooltip).build(); + } + + @Contract(value = "_ -> new", pure = true) + static ItemArmorTrim.Builder itemArmorTrim(final ArmorTrim armorTrim) { + return ItemComponentTypesBridge.bridge().itemArmorTrim(armorTrim); + } + + /** + * Armor trim present on this item. + * + * @return trim + */ + @Contract(pure = true) + ArmorTrim armorTrim(); + + /** + * Builder for {@link ItemArmorTrim}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends ShownInTooltip.Builder, DataComponentBuilder { + + /** + * Sets the armor trim for this builder. + * + * @param armorTrim trim + * @return the builder for chaining + * @see #armorTrim() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder armorTrim(ArmorTrim armorTrim); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemAttributeModifiers.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemAttributeModifiers.java new file mode 100644 index 0000000000000000000000000000000000000000..948505d38121d54df62e6a67d4597bc7d42c356f --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemAttributeModifiers.java @@ -0,0 +1,98 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import java.util.List; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeModifier; +import org.bukkit.inventory.EquipmentSlotGroup; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Holds attribute modifiers applied to any item. + * + * @see io.papermc.paper.datacomponent.DataComponentTypes#ATTRIBUTE_MODIFIERS + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface ItemAttributeModifiers extends ShownInTooltip { + + @Contract(value = "-> new", pure = true) + static ItemAttributeModifiers.Builder itemAttributes() { + return ItemComponentTypesBridge.bridge().modifiers(); + } + + /** + * Lists the attribute modifiers that are present on this item. + * + * @return modifiers + */ + @Contract(pure = true) + @Unmodifiable List modifiers(); + + /** + * Holds an attribute entry. + */ + @ApiStatus.NonExtendable + interface Entry { + + /** + * Gets the target attribute for the paired modifier. + * + * @return the attribute + */ + @Contract(pure = true) + Attribute attribute(); + + /** + * The modifier for the paired attribute. + * + * @return the modifier + */ + @Contract(pure = true) + AttributeModifier modifier(); + + /** + * Gets the slot group for this attribute. + * + * @return the slot group + */ + default EquipmentSlotGroup getGroup() { + return this.modifier().getSlotGroup(); + } + } + + /** + * Builder for {@link ItemAttributeModifiers}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends ShownInTooltip.Builder, DataComponentBuilder { + + /** + * Adds a modifier to this builder. + * + * @param attribute attribute + * @param modifier modifier + * @return the builder for chaining + * @see #modifiers() + */ + @Contract(value = "_, _, _ -> this", mutates = "this") + Builder addModifier(Attribute attribute, AttributeModifier modifier); + + /** + * Adds a modifier to this builder. + * + * @param attribute attribute + * @param modifier modifier + * @param equipmentSlotGroup the slot group this modifier applies to (overrides any slot group in the modifier) + * @return the builder for chaining + * @see #modifiers() + */ + @Contract(value = "_, _, _ -> this", mutates = "this") + Builder addModifier(Attribute attribute, AttributeModifier modifier, EquipmentSlotGroup equipmentSlotGroup); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridge.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridge.java new file mode 100644 index 0000000000000000000000000000000000000000..1097a6e0d0921abc6714017a123176ea23652ee0 --- /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.util.Filtered; +import java.util.Optional; +import java.util.ServiceLoader; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.util.TriState; +import org.bukkit.JukeboxSong; +import org.bukkit.block.BlockType; +import org.bukkit.damage.DamageType; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.ItemType; +import org.bukkit.inventory.meta.trim.ArmorTrim; +import org.bukkit.map.MapCursor; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +@ApiStatus.Internal +interface ItemComponentTypesBridge { + + Optional BRIDGE = ServiceLoader.load(ItemComponentTypesBridge.class).findFirst(); + + static ItemComponentTypesBridge bridge() { + return BRIDGE.orElseThrow(); + } + + ChargedProjectiles.Builder chargedProjectiles(); + + PotDecorations.Builder potDecorations(); + + Unbreakable.Builder unbreakable(); + + ItemLore.Builder lore(); + + ItemEnchantments.Builder enchantments(); + + ItemAttributeModifiers.Builder modifiers(); + + FoodProperties.Builder food(); + + DyedItemColor.Builder dyedItemColor(); + + PotionContents.Builder potionContents(); + + BundleContents.Builder bundleContents(); + + SuspiciousStewEffects.Builder suspiciousStewEffects(); + + MapItemColor.Builder mapItemColor(); + + MapDecorations.Builder mapDecorations(); + + MapDecorations.DecorationEntry decorationEntry(MapCursor.Type type, double x, double z, float rotation); + + SeededContainerLoot.Builder seededContainerLoot(Key lootTableKey); + + WrittenBookContent.Builder writtenBookContent(Filtered title, String author); + + WritableBookContent.Builder writeableBookContent(); + + ItemArmorTrim.Builder itemArmorTrim(ArmorTrim armorTrim); + + LodestoneTracker.Builder lodestoneTracker(); + + Fireworks.Builder fireworks(); + + ResolvableProfile.Builder resolvableProfile(); + + ResolvableProfile resolvableProfile(PlayerProfile profile); + + BannerPatternLayers.Builder bannerPatternLayers(); + + BlockItemDataProperties.Builder blockItemStateProperties(); + + ItemContainerContents.Builder itemContainerContents(); + + JukeboxPlayable.Builder jukeboxPlayable(JukeboxSong song); + + Tool.Builder tool(); + + Tool.Rule rule(RegistryKeySet blocks, @Nullable Float speed, TriState correctForDrops); + + ItemAdventurePredicate.Builder itemAdventurePredicate(); + + CustomModelData customModelData(int id); + + MapId mapId(int id); + + UseRemainder useRemainder(ItemStack itemStack); + + Consumable.Builder consumable(); + + UseCooldown.Builder useCooldown(final float seconds); + + DamageResistant damageResistant(TagKey types); + + Enchantable enchantable(int level); + + Repairable repairable(RegistryKeySet types); + + Equippable.Builder equippable(EquipmentSlot slot); + + DeathProtection.Builder deathProtection(); + + OminousBottleAmplifier ominousBottleAmplifier(int amplifier); +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemContainerContents.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemContainerContents.java new file mode 100644 index 0000000000000000000000000000000000000000..7d1c973ba566752d7a85496327b1352d973f2218 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemContainerContents.java @@ -0,0 +1,63 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import java.util.Arrays; +import java.util.List; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the contents of an item container. + * @see io.papermc.paper.datacomponent.DataComponentTypes#CONTAINER + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface ItemContainerContents { + + @Contract(value = "_ -> new", pure = true) + static ItemContainerContents containerContents(final List contents) { + return containerContents().addAll(contents).build(); + } + + @Contract(value = "-> new", pure = true) + static ItemContainerContents.Builder containerContents() { + return ItemComponentTypesBridge.bridge().itemContainerContents(); + } + + /** + * Gets the contents of the container. + * + * @return the contents + */ + @Contract(value = "-> new", pure = true) + @Unmodifiable List contents(); + + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Adds an item stack to the container. + * + * @param stack the item stack + * @return the builder for chaining + * @see #contents() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder add(ItemStack stack); + + /** + * Adds item stacks to the container. + * + * @param stacks the item stacks + * @return the builder for chaining + * @see #contents() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addAll(List stacks); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemEnchantments.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemEnchantments.java new file mode 100644 index 0000000000000000000000000000000000000000..fca271ea198209bd48cd02f4476e89e5e3e9f396 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemEnchantments.java @@ -0,0 +1,68 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import java.util.Map; +import org.bukkit.enchantments.Enchantment; +import org.checkerframework.common.value.qual.IntRange; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Stores a list of enchantments and their levels on an item. + * @see io.papermc.paper.datacomponent.DataComponentTypes#ENCHANTMENTS + * @see io.papermc.paper.datacomponent.DataComponentTypes#STORED_ENCHANTMENTS + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface ItemEnchantments extends ShownInTooltip { + + @Contract(value = "_, _ -> new", pure = true) + static ItemEnchantments itemEnchantments(final Map enchantments, final boolean showInTooltip) { + return itemEnchantments().addAll(enchantments).showInTooltip(showInTooltip).build(); + } + + @Contract(value = "-> new", pure = true) + static ItemEnchantments.Builder itemEnchantments() { + return ItemComponentTypesBridge.bridge().enchantments(); + } + + /** + * Enchantments currently present on this item. + * + * @return enchantments + */ + @Contract(pure = true) + @Unmodifiable Map enchantments(); + + /** + * Builder for {@link ItemEnchantments}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends ShownInTooltip.Builder, DataComponentBuilder { + + /** + * Adds an enchantment with the given level to this component. + * + * @param enchantment enchantment + * @param level level + * @return the builder for chaining + * @see #enchantments() + */ + @Contract(value = "_, _ -> this", mutates = "this") + Builder add(Enchantment enchantment, @IntRange(from = 1, to = 255) int level); + + /** + * Adds enchantments with the given level to this component. + * + * @param enchantments enchantments + * @return the builder for chaining + * @see #enchantments() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addAll(Map enchantments); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemLore.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemLore.java new file mode 100644 index 0000000000000000000000000000000000000000..3be62f6005e0343c3a6ebd04e3ee824e0b969113 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemLore.java @@ -0,0 +1,84 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import java.util.List; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Additional lines to include in an item's tooltip. + * @see io.papermc.paper.datacomponent.DataComponentTypes#LORE + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface ItemLore { + + @Contract(value = "_ -> new", pure = true) + static ItemLore lore(final List lines) { + return lore().lines(lines).build(); + } + + @Contract(value = "-> new", pure = true) + static ItemLore.Builder lore() { + return ItemComponentTypesBridge.bridge().lore(); + } + + /** + * Lists the components that are added to an item's tooltip. + * + * @return component list + */ + @Contract(pure = true) + @Unmodifiable List lines(); + + /** + * Lists the styled components (example: italicized and purple) that are added to an item's tooltip. + * + * @return component list + */ + @Contract(pure = true) + @Unmodifiable List styledLines(); + + /** + * Builder for {@link ItemLore}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Sets the components of this lore. + * + * @param lines components + * @return the builder for chaining + * @see #lines() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder lines(List 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 lines); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/JukeboxPlayable.java b/src/main/java/io/papermc/paper/datacomponent/item/JukeboxPlayable.java new file mode 100644 index 0000000000000000000000000000000000000000..c59942df7101c7630eabeb247b9690b9c4c76da4 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/JukeboxPlayable.java @@ -0,0 +1,43 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import org.bukkit.JukeboxSong; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the jukebox song for an item. + * @see io.papermc.paper.datacomponent.DataComponentTypes#JUKEBOX_PLAYABLE + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface JukeboxPlayable extends ShownInTooltip { + + @Contract(value = "_ -> new", pure = true) + static JukeboxPlayable.Builder jukeboxPlayable(final JukeboxSong song) { + return ItemComponentTypesBridge.bridge().jukeboxPlayable(song); + } + + @Contract(pure = true) + JukeboxSong jukeboxSong(); + + /** + * Builder for {@link JukeboxPlayable}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends ShownInTooltip.Builder, DataComponentBuilder { + + /** + * Sets the jukebox song. + * + * @param song the song + * @return the builder for chaining + * @see #jukeboxSong() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder jukeboxSong(JukeboxSong song); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/LodestoneTracker.java b/src/main/java/io/papermc/paper/datacomponent/item/LodestoneTracker.java new file mode 100644 index 0000000000000000000000000000000000000000..b919672ceea74ae09324653847b30fde293054d8 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/LodestoneTracker.java @@ -0,0 +1,72 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import org.bukkit.Location; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * If present, specifies the target Lodestone that a Compass should point towards. + * @see io.papermc.paper.datacomponent.DataComponentTypes#LODESTONE_TRACKER + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface LodestoneTracker { + + @Contract(value = "_, _ -> new", pure = true) + static LodestoneTracker lodestoneTracker(final @Nullable Location location, final boolean tracked) { + return lodestoneTracker().location(location).tracked(tracked).build(); + } + + @Contract(value = "-> new", pure = true) + static LodestoneTracker.Builder lodestoneTracker() { + return ItemComponentTypesBridge.bridge().lodestoneTracker(); + } + + /** + * The location that the compass should point towards. + * + * @return location + */ + @Contract(value = "-> new", pure = true) + @Nullable Location location(); + + /** + * If {@code true}, when the Lodestone at the target position is removed, the component will be removed. + * + * @return tracked + */ + @Contract(pure = true) + boolean tracked(); + + /** + * Builder for {@link LodestoneTracker}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Sets the location to point towards for this builder. + * + * @param location location to point towards + * @return the builder for chaining + * @see #location() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder location(@Nullable Location location); + + /** + * Sets if this location lodestone is tracked for this builder. + * + * @param tracked is tracked + * @return the builder for chaining + * @see #tracked() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder tracked(boolean tracked); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/MapDecorations.java b/src/main/java/io/papermc/paper/datacomponent/item/MapDecorations.java new file mode 100644 index 0000000000000000000000000000000000000000..1e611f1f918c33f8d89ad23cf2fc44a127af233c --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/MapDecorations.java @@ -0,0 +1,121 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import java.util.Map; +import org.bukkit.map.MapCursor; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * Holds a list of markers to be placed on a Filled Map (used for Explorer Maps). + * @see io.papermc.paper.datacomponent.DataComponentTypes#MAP_DECORATIONS + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface MapDecorations { + + @Contract(value = "_ -> new", pure = true) + static MapDecorations mapDecorations(final Map entries) { + return mapDecorations().putAll(entries).build(); + } + + @Contract(value = "-> new", pure = true) + static MapDecorations.Builder mapDecorations() { + return ItemComponentTypesBridge.bridge().mapDecorations(); + } + + @Contract(value = "_, _, _, _ -> new", pure = true) + static DecorationEntry decorationEntry(final MapCursor.Type type, final double x, final double z, final float rotation) { + return ItemComponentTypesBridge.bridge().decorationEntry(type, x, z, rotation); + } + + /** + * Gets the decoration entry with the given id. + * + * @param id id + * @return decoration entry, or {@code null} if not present + */ + @Contract(pure = true) + @Nullable DecorationEntry decoration(String id); + + /** + * Gets the decoration entries. + * + * @return the decoration entries + */ + @Contract(pure = true) + @Unmodifiable Map decorations(); + + /** + * Decoration present on the map. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface DecorationEntry { + + /** + * Type of decoration. + * + * @return type + */ + @Contract(pure = true) + MapCursor.Type type(); + + /** + * X world coordinate of the decoration. + * + * @return x coordinate + */ + @Contract(pure = true) + double x(); + + /** + * Z world coordinate of the decoration. + * + * @return z coordinate + */ + @Contract(pure = true) + double z(); + + /** + * Clockwise rotation from north in degrees. + * + * @return rotation + */ + @Contract(pure = true) + float rotation(); + } + + /** + * Builder for {@link MapDecorations}. + */ + @ApiStatus.NonExtendable + @ApiStatus.Experimental + interface Builder extends DataComponentBuilder { + + /** + * Puts the decoration with the given id in this builder. + * + * @param id id + * @param entry decoration + * @return the builder for chaining + * @see #decorations() + */ + @Contract(value = "_, _ -> this", mutates = "this") + MapDecorations.Builder put(String id, DecorationEntry entry); + + /** + * Puts all the decoration with the given id in this builder. + * + * @param entries decorations + * @return the builder for chaining + * @see #decorations() + */ + @Contract(value = "_ -> this", mutates = "this") + MapDecorations.Builder putAll(Map entries); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/MapId.java b/src/main/java/io/papermc/paper/datacomponent/item/MapId.java new file mode 100644 index 0000000000000000000000000000000000000000..045bfe0ce5080b57a40be03a65b1a2aaf9089120 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/MapId.java @@ -0,0 +1,28 @@ +package io.papermc.paper.datacomponent.item; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * References the shared map state holding map contents and markers for a Filled Map. + * @see io.papermc.paper.datacomponent.DataComponentTypes#MAP_ID + */ +@NullMarked +@ApiStatus.NonExtendable +@ApiStatus.Experimental +public interface MapId { + + @Contract(value = "_ -> new", pure = true) + static MapId mapId(final int id) { + return ItemComponentTypesBridge.bridge().mapId(id); + } + + /** + * The map id. + * + * @return id + */ + @Contract(pure = true) + int id(); +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/MapItemColor.java b/src/main/java/io/papermc/paper/datacomponent/item/MapItemColor.java new file mode 100644 index 0000000000000000000000000000000000000000..87845d19a25ed2ae79b868fcfe40b88a2dc83f97 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/MapItemColor.java @@ -0,0 +1,43 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import org.bukkit.Color; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Represents the tint of the decorations on the Filled Map item. + * @see io.papermc.paper.datacomponent.DataComponentTypes#MAP_COLOR + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface MapItemColor { + + @Contract(value = "-> new", pure = true) + static MapItemColor.Builder mapItemColor() { + return ItemComponentTypesBridge.bridge().mapItemColor(); + } + + /** + * The tint to apply. + * + * @return color + */ + Color color(); + + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Sets the tint color of this map. + * + * @param color tint color + * @return the builder for chaining + * @see #color() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder color(Color color); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/OminousBottleAmplifier.java b/src/main/java/io/papermc/paper/datacomponent/item/OminousBottleAmplifier.java new file mode 100644 index 0000000000000000000000000000000000000000..4f16e08f04c2cea24f3cb132ff21f4bdd6b70582 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/OminousBottleAmplifier.java @@ -0,0 +1,29 @@ +package io.papermc.paper.datacomponent.item; + +import org.checkerframework.common.value.qual.IntRange; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the ominous bottle amplifier. + * @see io.papermc.paper.datacomponent.DataComponentTypes#OMINOUS_BOTTLE_AMPLIFIER + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface OminousBottleAmplifier { + + @Contract(value = "_ -> new", pure = true) + static OminousBottleAmplifier amplifier(final @IntRange(from = 0, to = 4) int amplifier) { + return ItemComponentTypesBridge.bridge().ominousBottleAmplifier(amplifier); + } + + /** + * Gets the bottle amplifier. + * + * @return the amplifier + */ + @Contract(pure = true) + @IntRange(from = 0, to = 4) int amplifier(); +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PotDecorations.java b/src/main/java/io/papermc/paper/datacomponent/item/PotDecorations.java new file mode 100644 index 0000000000000000000000000000000000000000..6da78b8735a6cadd1282fa2fafd8b0f74f087fb4 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/PotDecorations.java @@ -0,0 +1,109 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import org.bukkit.inventory.ItemType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +// CONTRIBUTORS: LEAVE THIS AS ITEM TYPE!!! +/** + * Holds the item types for the decorations on a flower pot. + * @see io.papermc.paper.datacomponent.DataComponentTypes#POT_DECORATIONS + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface PotDecorations { + + @Contract(value = "_, _, _, _ -> new", pure = true) + static PotDecorations potDecorations(final @Nullable ItemType back, final @Nullable ItemType left, final @Nullable ItemType right, final @Nullable ItemType front) { + return potDecorations().back(back).left(left).right(right).front(front).build(); + } + + @Contract(value = "-> new", pure = true) + static PotDecorations.Builder potDecorations() { + return ItemComponentTypesBridge.bridge().potDecorations(); + } + + /** + * Get the item type for the back. + * + * @return the back item type. + */ + @Contract(pure = true) + @Nullable ItemType back(); + + /** + * Get the item type for the left. + * + * @return the left item type. + */ + @Contract(pure = true) + @Nullable ItemType left(); + + /** + * Get the item type for the right. + * + * @return the right item type. + */ + @Contract(pure = true) + @Nullable ItemType right(); + + /** + * Get the item type for the front. + * + * @return the front item type. + */ + @Contract(pure = true) + @Nullable ItemType front(); + + /** + * Builder for {@link PotDecorations}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Set the {@link ItemType} for the back. + * + * @param back item for the back + * @return the builder for chaining + * @see #back() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder back(@Nullable ItemType back); + + /** + * Set the {@link ItemType} for the left. + * + * @param left item for the left + * @return the builder for chaining + * @see #left() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder left(@Nullable ItemType left); + + /** + * Set the {@link ItemType} for the right. + * + * @param right item for the right + * @return the builder for chaining + * @see #right() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder right(@Nullable ItemType right); + + /** + * Set the {@link ItemType} for the front. + * + * @param front item for the front + * @return the builder for chaining + * @see #front() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder front(@Nullable ItemType front); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java b/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java new file mode 100644 index 0000000000000000000000000000000000000000..7cf05b382319064d45433a7e2678f65c25d11b14 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/PotionContents.java @@ -0,0 +1,120 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import java.util.List; +import org.bukkit.Color; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * Holds the contents of a potion (Potion, Splash Potion, Lingering Potion), or potion applied to a Tipped Arrow. + * @see io.papermc.paper.datacomponent.DataComponentTypes#POTION_CONTENTS + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface PotionContents { + + @Contract(value = "-> new", pure = true) + static PotionContents.Builder potionContents() { + return ItemComponentTypesBridge.bridge().potionContents(); + } + + /** + * The potion type in this item: the item will inherit all effects from this. + * + * @return potion type, or {@code null} if not present + */ + @Contract(pure = true) + @Nullable PotionType potion(); + + /** + * Overrides the visual color of the potion. + * + * @return color override, or {@code null} if not present + * @apiNote alpha channel of the color is only relevant + * for Tipped Arrow + */ + @Contract(pure = true) + @Nullable Color customColor(); + + /** + * Additional list of effect instances that this item should apply. + * + * @return effects + */ + @Contract(pure = true) + @Unmodifiable List customEffects(); + + /** + * Suffix to the translation key of the potion item. + * + * @return translation key suffix, or {@code null} if not present + * @apiNote This is used in the display of tipped arrow and potion items. + */ + @Contract(pure = true) + @Nullable String customName(); + + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Sets the potion type for this builder. + * + * @param type builder + * @return the builder for chaining + * @see #potion() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder potion(@Nullable PotionType type); + + /** + * Sets the color override for this builder. + * + * @param color color + * @return the builder for chaining + * @see #customColor() + * @apiNote alpha channel of the color is supported only for Tipped Arrow + */ + @Contract(value = "_ -> this", mutates = "this") + Builder customColor(@Nullable Color color); + + /** + * Sets the suffix to the translation key of the potion item. + * + * @param name name + * @return the builder for chaining + * @see #customName() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder customName(@Nullable String name); + + /** + * Adds a custom effect instance to this builder. + * + * @param effect effect + * @see #customEffects() + * @return the builder for chaining + * @see #customEffects() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addCustomEffect(PotionEffect effect); + + /** + * Adds custom effect instances to this builder. + * + * @param effects effects + * @see #customEffects() + * @return the builder for chaining + * @see #customEffects() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addCustomEffects(List effects); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Repairable.java b/src/main/java/io/papermc/paper/datacomponent/item/Repairable.java new file mode 100644 index 0000000000000000000000000000000000000000..ff84d9123aab0ad2f93b397e20a37f21894547a3 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/Repairable.java @@ -0,0 +1,30 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.registry.set.RegistryKeySet; +import org.bukkit.inventory.ItemType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Holds if this item is repairable, and what item types it can be repaired with. + * @see io.papermc.paper.datacomponent.DataComponentTypes#REPAIRABLE + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface Repairable { + + @Contract(value = "_ -> new", pure = true) + static Repairable repairable(final RegistryKeySet types) { + return ItemComponentTypesBridge.bridge().repairable(types); + } + + /** + * The types that this item is repairable to. + * + * @return item + */ + @Contract(value = "-> new", pure = true) + RegistryKeySet types(); +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ResolvableProfile.java b/src/main/java/io/papermc/paper/datacomponent/item/ResolvableProfile.java new file mode 100644 index 0000000000000000000000000000000000000000..dc6cd191553e7ca5b6c5768f594924e4c39fcbbe --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/ResolvableProfile.java @@ -0,0 +1,123 @@ +package io.papermc.paper.datacomponent.item; + +import com.destroystokyo.paper.profile.PlayerProfile; +import com.destroystokyo.paper.profile.ProfileProperty; +import io.papermc.paper.datacomponent.DataComponentBuilder; +import java.util.Collection; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import org.intellij.lang.annotations.Pattern; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * Holds player profile data that can be resolved to a {@link PlayerProfile}. + * @see io.papermc.paper.datacomponent.DataComponentTypes#PROFILE + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface ResolvableProfile { + + @Contract(value = "_ -> new", pure = true) + static ResolvableProfile resolvableProfile(final PlayerProfile profile) { + return ItemComponentTypesBridge.bridge().resolvableProfile(profile); + } + + @Contract(value = "-> new", pure = true) + static ResolvableProfile.Builder resolvableProfile() { + return ItemComponentTypesBridge.bridge().resolvableProfile(); + } + + @Contract(pure = true) + @Nullable UUID uuid(); + + @Contract(pure = true) + @Nullable String name(); + + @Contract(pure = true) + @Unmodifiable Collection properties(); + + /** + * Produces an updated player profile based on this. + *

+ * This tries to produce a completed profile by filling in missing + * properties (name, unique id, textures, etc.), and updates existing + * properties (e.g. name, textures, etc.) to their official and up-to-date + * values. This operation does not alter the current profile, but produces a + * new updated {@link PlayerProfile}. + *

+ * If no player exists for the unique id or name of this profile, this + * operation yields a profile that is equal to the current profile, which + * might not be complete. + *

+ * This is an asynchronous operation: Updating the profile can result in an + * outgoing connection in another thread in order to fetch the latest + * profile properties. The returned {@link CompletableFuture} will be + * completed once the updated profile is available. In order to not block + * the server's main thread, you should not wait for the result of the + * returned CompletableFuture on the server's main thread. Instead, if you + * want to do something with the updated player profile on the server's main + * thread once it is available, you could do something like this: + *

+     * profile.resolve().thenAcceptAsync(updatedProfile -> {
+     *     // Do something with the updated profile:
+     *     // ...
+     * }, runnable -> Bukkit.getScheduler().runTask(plugin, runnable));
+     * 
+ */ + @Contract(pure = true) + CompletableFuture resolve(); + + /** + * Builder for {@link ResolvableProfile}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Sets the name for this profile. Must be 16-or-less + * characters and not contain invalid characters. + * + * @param name the name + * @return the builder for chaining + * @see #name() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder name(@Pattern("^[!-~]{0,16}$") @Nullable String name); + + /** + * Sets the UUID for this profile. + * + * @param uuid the UUID + * @return the builder for chaining + * @see #uuid() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder uuid(@Nullable UUID uuid); + + /** + * Adds a property to this profile. + * + * @param property the property + * @return the builder for chaining + * @see #properties() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addProperty(ProfileProperty property); + + /** + * Adds properties to this profile. + * + * @param properties the properties + * @return the builder for chaining + * @see #properties() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addProperties(Collection properties); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/SeededContainerLoot.java b/src/main/java/io/papermc/paper/datacomponent/item/SeededContainerLoot.java new file mode 100644 index 0000000000000000000000000000000000000000..f79af65e8f3f8ffbb9be1cf1c6b537cd1e2b1031 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/SeededContainerLoot.java @@ -0,0 +1,71 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import net.kyori.adventure.key.Key; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the loot table and seed for a container. + * @see io.papermc.paper.datacomponent.DataComponentTypes#CONTAINER_LOOT + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface SeededContainerLoot { + + @Contract(value = "_, _ -> new", pure = true) + static SeededContainerLoot seededContainerLoot(final Key lootTableKey, final long seed) { + return SeededContainerLoot.seededContainerLoot(lootTableKey).seed(seed).build(); + } + + @Contract(value = "_ -> new", pure = true) + static SeededContainerLoot.Builder seededContainerLoot(final Key lootTableKey) { + return ItemComponentTypesBridge.bridge().seededContainerLoot(lootTableKey); + } + + /** + * Gets the loot table key. + * + * @return the loot table key + */ + @Contract(pure = true) + Key lootTable(); + + /** + * Gets the loot table seed. + * + * @return the seed + */ + @Contract(pure = true) + long seed(); + + /** + * Builder for {@link SeededContainerLoot}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Sets the loot table key. + * + * @param key the loot table key + * @return the builder for chaining + * @see #lootTable() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder lootTable(Key key); + + /** + * Sets the loot table seed. + * + * @param seed the seed + * @return the builder for chaining + * @see #seed() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder seed(long seed); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ShownInTooltip.java b/src/main/java/io/papermc/paper/datacomponent/item/ShownInTooltip.java new file mode 100644 index 0000000000000000000000000000000000000000..7e058aebcbd768517f6db51540598721cdae4425 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/ShownInTooltip.java @@ -0,0 +1,52 @@ +package io.papermc.paper.datacomponent.item; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the state of whether a data component should be shown + * in an item's tooltip. + * @param the data component type + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface ShownInTooltip { + + /** + * Gets if the data component should be shown in the item's tooltip. + * + * @return {@code true} to show in the tooltip + */ + @Contract(pure = true) + boolean showInTooltip(); + + /** + * Returns a copy of this data component with the specified + * show-in-tooltip state. + * @param showInTooltip {@code true} to show in the tooltip + * @return the new data component + */ + @Contract(value = "_ -> new", pure = true) + T showInTooltip(boolean showInTooltip); + + /** + * A builder for creating a {@link ShownInTooltip} data component. + * @param builder type + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder { + + /** + * Sets if the data component should be shown in the item's tooltip. + * + * @param showInTooltip {@code true} to show in the tooltip + * @return the builder for chaining + * @see #showInTooltip() + */ + @Contract(value = "_ -> this", mutates = "this") + B showInTooltip(boolean showInTooltip); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/SuspiciousStewEffects.java b/src/main/java/io/papermc/paper/datacomponent/item/SuspiciousStewEffects.java new file mode 100644 index 0000000000000000000000000000000000000000..422bb5ccc42b94b60fba6714e9e6fb8aced6eb0c --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/SuspiciousStewEffects.java @@ -0,0 +1,67 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import io.papermc.paper.potion.SuspiciousEffectEntry; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the effects that will be applied when consuming Suspicious Stew. + * @see io.papermc.paper.datacomponent.DataComponentTypes#SUSPICIOUS_STEW_EFFECTS + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface SuspiciousStewEffects { + + @Contract(value = "_ -> new", pure = true) + static SuspiciousStewEffects suspiciousStewEffects(final Collection effects) { + return suspiciousStewEffects().addAll(effects).build(); + } + + @Contract(value = "-> new", pure = true) + static SuspiciousStewEffects.Builder suspiciousStewEffects() { + return ItemComponentTypesBridge.bridge().suspiciousStewEffects(); + } + + /** + * Effects that will be applied when consuming Suspicious Stew. + * + * @return effects + */ + @Contract(pure = true) + @Unmodifiable List effects(); + + /** + * Builder for {@link SuspiciousStewEffects}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Adds an effect applied to this builder. + * + * @param entry effect + * @return the builder for chaining + * @see #effects() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder add(SuspiciousEffectEntry entry); + + /** + * Adds effects applied to this builder. + * + * @param entries effect + * @return the builder for chaining + * @see #effects() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addAll(Collection entries); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Tool.java b/src/main/java/io/papermc/paper/datacomponent/item/Tool.java new file mode 100644 index 0000000000000000000000000000000000000000..4e87feb83204266e1fefdafe7b7e5ac53da3160e --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/Tool.java @@ -0,0 +1,149 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import io.papermc.paper.registry.set.RegistryKeySet; +import java.util.Collection; +import java.util.List; +import net.kyori.adventure.util.TriState; +import org.bukkit.block.BlockType; +import org.checkerframework.checker.index.qual.NonNegative; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * Controls the behavior of the item as a tool. + * @see io.papermc.paper.datacomponent.DataComponentTypes#TOOL + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface Tool { + + @Contract(value = "-> new", pure = true) + static Tool.Builder tool() { + return ItemComponentTypesBridge.bridge().tool(); + } + + /** + * Creates a mining rule that specifies how an item interacts with certain block types. + * + *

This method allows you to define a rule for a set of block types, optionally setting a custom mining speed + * and determining whether the item should correct for drops when mining these blocks.

+ * + * @param blocks The set of block types this rule applies to. + * @param speed The custom mining speed multiplier for these blocks. If {@code null}, the default speed is used. + * @param correctForDrops A {@link TriState} indicating how to handle item drops: + *
    + *
  • {@link TriState#TRUE} - Items will be dropped.
  • + *
  • {@link TriState#FALSE} - Items will not be dropped.
  • + *
  • {@link TriState#NOT_SET} - The default drop behavior is used.
  • + *
+ * @return A new {@link Rule} instance representing the mining rule. + */ + static Rule rule(final RegistryKeySet blocks, final @Nullable Float speed, final TriState correctForDrops) { + return ItemComponentTypesBridge.bridge().rule(blocks, speed, correctForDrops); + } + + /** + * Mining speed to use if no rules match and don't override mining speed. + * + * @return default mining speed + */ + @Contract(pure = true) + float defaultMiningSpeed(); + + /** + * Amount of durability to remove each time a block is mined with this tool. + * + * @return durability + */ + @Contract(pure = true) + @NonNegative int damagePerBlock(); + + /** + * List of rule entries. + * + * @return rules + */ + @Contract(pure = true) + @Unmodifiable List rules(); + + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Rule { + + /** + * Blocks to match. + * + * @return blocks + */ + RegistryKeySet blocks(); + + /** + * Overrides the mining speed if present and matched. + *

+ * {@code true} will cause the block to mine at its most efficient speed, and drop items if the targeted block requires that. + * + * @return speed override + */ + @Nullable Float speed(); + + /** + * Overrides whether this tool is considered 'correct' if present and matched. + * + * @return a tri-state + */ + TriState correctForDrops(); + } + + /** + * Builder for {@link Tool}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Controls the amount of durability to remove each time a block is mined with this tool. + * + * @param damage durability to remove + * @return the builder for chaining + * @see #damagePerBlock() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder damagePerBlock(@NonNegative int damage); + + /** + * Controls mining speed to use if no rules match and don't override mining speed. + * + * @param miningSpeed mining speed + * @return the builder for chaining + * @see #defaultMiningSpeed() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder defaultMiningSpeed(float miningSpeed); + + /** + * Adds a rule to the tool that controls the breaking speed / damage per block if matched. + * + * @param rule rule + * @return the builder for chaining + * @see #rules() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addRule(Rule rule); + + /** + * Adds rules to the tool that control the breaking speed / damage per block if matched. + * + * @param rules rules + * @return the builder for chaining + * @see #rules() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addRules(Collection rules); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/Unbreakable.java b/src/main/java/io/papermc/paper/datacomponent/item/Unbreakable.java new file mode 100644 index 0000000000000000000000000000000000000000..498eb479dce406d2b0b470b327eac8279a0d98bc --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/Unbreakable.java @@ -0,0 +1,34 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * If set, the item will not lose any durability when used. + * @see io.papermc.paper.datacomponent.DataComponentTypes#UNBREAKABLE + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface Unbreakable extends ShownInTooltip { + + @Contract(value = "_ -> new", pure = true) + static Unbreakable unbreakable(final boolean showInTooltip) { + return unbreakable().showInTooltip(showInTooltip).build(); + } + + @Contract(value = "-> new", pure = true) + static Unbreakable.Builder unbreakable() { + return ItemComponentTypesBridge.bridge().unbreakable(); + } + + /** + * Builder for {@link Unbreakable}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends ShownInTooltip.Builder, DataComponentBuilder { + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/UseCooldown.java b/src/main/java/io/papermc/paper/datacomponent/item/UseCooldown.java new file mode 100644 index 0000000000000000000000000000000000000000..57fc55ad1def2bb14fc0a95ee3c0c157b0ac53fb --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/UseCooldown.java @@ -0,0 +1,65 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import net.kyori.adventure.key.Key; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * Holds the contents of cooldown information when an item is used. + * @see io.papermc.paper.datacomponent.DataComponentTypes#USE_COOLDOWN + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface UseCooldown { + + /** + * Creates a new builder for use cooldown. + * + * @param seconds the duration in seconds; must be positive + * @return builder + */ + @Contract(value = "_ -> new", pure = true) + static UseCooldown.Builder useCooldown(final float seconds) { + return ItemComponentTypesBridge.bridge().useCooldown(seconds); + } + + /** + * The amount of seconds the cooldown will be active for. + * + * @return cooldown seconds + */ + @Contract(pure = true) + float seconds(); + + /** + * The unique resource location to identify this cooldown group. + *

+ * This allows items to share cooldowns with other items in the same cooldown group, if present. + * + * @return cooldown group, or null if not present + */ + @Contract(pure = true) + @Nullable Key cooldownGroup(); + + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Sets a unique resource location for this cooldown group. + *

+ * This allows items to share cooldowns with other items in the same cooldown group. + *

+ * + * @param key the unique resource location; can be null + * @return the builder for chaining + * @see #cooldownGroup() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder cooldownGroup(@Nullable Key key); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/UseRemainder.java b/src/main/java/io/papermc/paper/datacomponent/item/UseRemainder.java new file mode 100644 index 0000000000000000000000000000000000000000..50e42e073311332142980828d0beec1828570512 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/UseRemainder.java @@ -0,0 +1,31 @@ +package io.papermc.paper.datacomponent.item; + +import io.papermc.paper.datacomponent.DataComponentBuilder; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the contents of item transformation information when an item is used. + * @see io.papermc.paper.datacomponent.DataComponentTypes#USE_REMAINDER + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface UseRemainder { + + @Contract(value = "_ -> new", pure = true) + static UseRemainder useRemainder(final ItemStack itemStack) { + return ItemComponentTypesBridge.bridge().useRemainder(itemStack); + } + + /** + * The item that the item that is consumed is transformed into. + * + * @return item + */ + @Contract(value = "-> new", pure = true) + ItemStack transformInto(); + +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/WritableBookContent.java b/src/main/java/io/papermc/paper/datacomponent/item/WritableBookContent.java new file mode 100644 index 0000000000000000000000000000000000000000..e1218114932d6965b073102920d7c4773496ec99 --- /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.util.Filtered; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the pages for a writable book. + * @see io.papermc.paper.datacomponent.DataComponentTypes#WRITABLE_BOOK_CONTENT + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface WritableBookContent { + + @Contract(value = "-> new", pure = true) + static WritableBookContent.Builder writeableBookContent() { + return ItemComponentTypesBridge.bridge().writeableBookContent(); + } + + /** + * Holds the pages that can be written to for this component. + * + * @return pages, as filtered objects + */ + @Contract(pure = true) + @Unmodifiable List> pages(); + + /** + * Builder for {@link WritableBookContent}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Adds a page that can be written to for this builder. + * + * @param page page + * @return the builder for chaining + * @see #pages() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addPage(String page); + + /** + * Adds pages that can be written to for this builder. + * + * @param pages pages + * @return the builder for chaining + * @see #pages() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addPages(List pages); + + /** + * Adds a filterable page that can be written to for this builder. + * + * @param page page + * @return the builder for chaining + * @see #pages() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addFilteredPage(Filtered page); + + /** + * Adds filterable pages that can be written to for this builder. + * + * @param pages pages + * @return the builder for chaining + * @see #pages() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addFilteredPages(List> pages); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/WrittenBookContent.java b/src/main/java/io/papermc/paper/datacomponent/item/WrittenBookContent.java new file mode 100644 index 0000000000000000000000000000000000000000..173ec331286635942145e55956ccf6bcc64535e4 --- /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.util.Filtered; +import java.util.List; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import org.checkerframework.common.value.qual.IntRange; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Holds the contents and metadata of a Written Book. + * @see io.papermc.paper.datacomponent.DataComponentTypes#WRITTEN_BOOK_CONTENT + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface WrittenBookContent { + + @Contract(value = "_, _ -> new", pure = true) + static WrittenBookContent.Builder writtenBookContent(final String title, final String author) { + return writtenBookContent(Filtered.of(title, null), author); + } + + @Contract(value = "_, _ -> new", pure = true) + static WrittenBookContent.Builder writtenBookContent(final Filtered title, final String author) { + return ItemComponentTypesBridge.bridge().writtenBookContent(title, author); + } + + /** + * Title of this book. + * + * @return title + */ + @Contract(pure = true) + Filtered title(); + + /** + * Player name of the author of this book. + * + * @return author + */ + @Contract(pure = true) + String author(); + + /** + * The number of times this book has been copied (0 = original). + * + * @return generation + */ + @Contract(pure = true) + @IntRange(from = 0, to = 3) int generation(); + + /** + * Gets the pages of this book. + * + * @return pages + */ + @Contract(pure = true) + @Unmodifiable List> pages(); + + /** + * If the chat components in this book have already been resolved (entity selectors, scores substituted). + * If {@code false}, will be resolved when opened by a player. + * + * @return resolved + */ + @Contract(pure = true) + boolean resolved(); + + /** + * Builder for {@link WrittenBookContent}. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + /** + * Sets the title of this book. + * + * @param title the title + * @return the builder for chaining + * @see #title() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder title(String title); + + /** + * Sets the filterable title of this book. + * + * @param title the title + * @return the builder for chaining + * @see #title() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder filteredTitle(Filtered title); + + /** + * Sets the author of this book. + * + * @param author the author + * @return the builder for chaining + * @see #author() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder author(String author); + + /** + * Sets the generation of this book. + * + * @param generation the generation, [0-3] + * @return the builder for chaining + * @see #generation() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder generation(@IntRange(from = 0, to = 3) int generation); + + /** + * Sets if the chat components in this book have already been resolved (entity selectors, scores substituted). + * If {@code false}, will be resolved when opened by a player. + * + * @param resolved resolved + * @return the builder for chaining + * @see #resolved() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder resolved(boolean resolved); + + /** + * Adds a page to this book. + * + * @param page the page + * @return the builder for chaining + * @see #pages() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addPage(ComponentLike page); + + /** + * Adds pages to this book. + * + * @param page the pages + * @return the builder for chaining + * @see #pages() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addPages(List 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 page); + + /** + * Adds filterable pages to this book. + * + * @param pages the pages + * @return the builder for chaining + * @see #pages() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addFilteredPages(List> pages); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridge.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridge.java new file mode 100644 index 0000000000000000000000000000000000000000..a845ccfc21f101f0632249745bbd8b334f85e72c --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridge.java @@ -0,0 +1,32 @@ +package io.papermc.paper.datacomponent.item.consumable; + +import io.papermc.paper.registry.set.RegistryKeySet; +import java.util.List; +import java.util.Optional; +import java.util.ServiceLoader; +import net.kyori.adventure.key.Key; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +@NullMarked +@ApiStatus.Internal +interface ConsumableTypesBridge { + + Optional BRIDGE = ServiceLoader.load(ConsumableTypesBridge.class).findFirst(); + + static ConsumableTypesBridge bridge() { + return BRIDGE.orElseThrow(); + } + + ConsumeEffect.ApplyStatusEffects applyStatusEffects(List effectList, float probability); + + ConsumeEffect.RemoveStatusEffects removeStatusEffects(RegistryKeySet potionEffectTypeTagKey); + + ConsumeEffect.ClearAllStatusEffects clearAllStatusEffects(); + + ConsumeEffect.PlaySound playSoundEffect(Key sound); + + ConsumeEffect.TeleportRandomly teleportRandomlyEffect(float diameter); +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumeEffect.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumeEffect.java new file mode 100644 index 0000000000000000000000000000000000000000..45d674b68ab0e532c2f87bc528c1b095a50b5068 --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumeEffect.java @@ -0,0 +1,149 @@ +package io.papermc.paper.datacomponent.item.consumable; + +import io.papermc.paper.registry.set.RegistryKeySet; +import net.kyori.adventure.key.Key; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; +import java.util.List; + +/** + * Effect that occurs when consuming an item. + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface ConsumeEffect { + + /** + * Creates a consume effect that randomly teleports the entity on consumption. + * + * @param diameter diameter of random teleportation + * @return the effect + */ + @Contract(value = "_ -> new", pure = true) + static TeleportRandomly teleportRandomlyEffect(final float diameter) { + return ConsumableTypesBridge.bridge().teleportRandomlyEffect(diameter); + } + + /** + * Creates a consume effect that gives status effects on consumption. + * + * @param key the sound effect to play + * @return the effect + */ + @Contract(value = "_ -> new", pure = true) + static RemoveStatusEffects removeEffects(final RegistryKeySet key) { + return ConsumableTypesBridge.bridge().removeStatusEffects(key); + } + + /** + * Creates a consume effect that plays a sound on consumption. + * + * @param key the sound effect to play + * @return the effect + */ + @Contract(value = "_ -> new", pure = true) + static PlaySound playSoundConsumeEffect(final Key key) { + return ConsumableTypesBridge.bridge().playSoundEffect(key); + } + + /** + * Creates a consume effect that clears all status effects. + * + * @return effect instance + */ + @Contract(value = "-> new", pure = true) + static ClearAllStatusEffects clearAllStatusEffects() { + return ConsumableTypesBridge.bridge().clearAllStatusEffects(); + } + + /** + * Creates a consume effect that gives status effects on consumption. + * + * @param effects the potion effects to apply + * @param probability the probability of these effects being applied, between 0 and 1 inclusive. + * @return the effect + */ + @Contract(value = "_, _ -> new", pure = true) + static ApplyStatusEffects applyStatusEffects(final List effects, final float probability) { + return ConsumableTypesBridge.bridge().applyStatusEffects(effects, probability); + } + + @NullMarked + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface TeleportRandomly extends ConsumeEffect { + + /** + * The max range that the entity can be teleported to. + * + * @return teleportation diameter + */ + float diameter(); + } + /** + * Represents a consumable effect that removes status effects on consumption + */ + @NullMarked + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface RemoveStatusEffects extends ConsumeEffect { + + /** + * Potion effects to remove + * + * @return effects + */ + RegistryKeySet removeEffects(); + } + + /** + * Represents a consumable effect that plays a sound on consumption. + */ + @NullMarked + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface PlaySound extends ConsumeEffect { + + /** + * Sound effect to play in the world + * + * @return sound effect + */ + Key sound(); + } + + /** + * Represents a consumable effect that clears all effects on consumption. + */ + @NullMarked + interface ClearAllStatusEffects extends ConsumeEffect { + + } + + /** + * Represents a consumable effect that applies effects based on a probability on consumption. + */ + @NullMarked + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface ApplyStatusEffects extends ConsumeEffect { + + /** + * Effect instances to grant + * + * @return effect + */ + List effects(); + + /** + * Float between 0 and 1, chance for the effect to be applied. + * + * @return chance + */ + float probability(); + } +} diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/ItemUseAnimation.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ItemUseAnimation.java new file mode 100644 index 0000000000000000000000000000000000000000..8cd6dbe4ea5ee3270b9428a9c29cbd88823d9f6c --- /dev/null +++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ItemUseAnimation.java @@ -0,0 +1,17 @@ +package io.papermc.paper.datacomponent.item.consumable; + +/** + * Represents the hand animation that is used when a player is consuming this item. + */ +public enum ItemUseAnimation { + NONE, + EAT, + DRINK, + BLOCK, + BOW, + SPEAR, + CROSSBOW, + SPYGLASS, + TOOT_HORN, + BRUSH +} diff --git a/src/main/java/io/papermc/paper/item/MapPostProcessing.java b/src/main/java/io/papermc/paper/item/MapPostProcessing.java new file mode 100644 index 0000000000000000000000000000000000000000..5843768d0be2ae4a0219636ed7640727808da567 --- /dev/null +++ b/src/main/java/io/papermc/paper/item/MapPostProcessing.java @@ -0,0 +1,6 @@ +package io.papermc.paper.item; + +public enum MapPostProcessing { + LOCK, + SCALE +} diff --git a/src/main/java/io/papermc/paper/registry/RegistryKey.java b/src/main/java/io/papermc/paper/registry/RegistryKey.java index 9b39e33514b15a9d07104e2ad826d0da11f569d6..116857b4479565b602b94d227ee32dc29ebd6e5f 100644 --- a/src/main/java/io/papermc/paper/registry/RegistryKey.java +++ b/src/main/java/io/papermc/paper/registry/RegistryKey.java @@ -1,5 +1,6 @@ package io.papermc.paper.registry; +import io.papermc.paper.datacomponent.DataComponentType; import net.kyori.adventure.key.Keyed; import org.bukkit.Art; import org.bukkit.Fluid; @@ -124,6 +125,11 @@ public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl { * @see io.papermc.paper.registry.keys.SoundEventKeys */ RegistryKey SOUND_EVENT = create("sound_event"); + /** + * Built-in registry for data component types. + * @see io.papermc.paper.registry.keys.DataComponentTypeKeys + */ + RegistryKey DATA_COMPONENT_TYPE = create("data_component_type"); diff --git a/src/main/java/io/papermc/paper/util/Filtered.java b/src/main/java/io/papermc/paper/util/Filtered.java new file mode 100644 index 0000000000000000000000000000000000000000..54e1a383c4eeb595417ea6e9e4e9527a8ffd9a99 --- /dev/null +++ b/src/main/java/io/papermc/paper/util/Filtered.java @@ -0,0 +1,32 @@ +package io.papermc.paper.util; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * Denotes that this type is filterable by the client, and may be shown differently + * depending on the player's set configuration. + * + * @param type of value + */ +@ApiStatus.Experimental +@NullMarked +public interface Filtered { + + @Contract(value = "_, _ -> new", pure = true) + static Filtered of(final T raw, final @Nullable T filtered) { + @ApiStatus.Internal + record Instance(T raw, @Nullable T filtered) implements Filtered {} + + return new Instance<>(raw, filtered); + } + + @Contract(pure = true) + T raw(); + + @Contract(pure = true) + @Nullable + T filtered(); +} diff --git a/src/main/java/org/bukkit/Material.java b/src/main/java/org/bukkit/Material.java index 615eb24ffdd8f6d55ccd4f21760b809c1098bc68..c7ce8fa1ff9feda66d5a4e497112a24ff51c9d2b 100644 --- a/src/main/java/org/bukkit/Material.java +++ b/src/main/java/org/bukkit/Material.java @@ -137,7 +137,7 @@ import org.jetbrains.annotations.Nullable; @SuppressWarnings({"DeprecatedIsStillUsed", "deprecation"}) // Paper public enum Material implements Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper // - AIR(9648, 0), + AIR(9648, 64), // Paper - air stacks to 64 STONE(22948), GRANITE(21091), POLISHED_GRANITE(5477), @@ -5784,6 +5784,7 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla */ @ApiStatus.Internal @Nullable + @org.jetbrains.annotations.Contract(pure = true) // Paper public ItemType asItemType() { return itemType.get(); } @@ -5796,7 +5797,47 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla */ @ApiStatus.Internal @Nullable + @org.jetbrains.annotations.Contract(pure = true) // Paper public BlockType asBlockType() { return blockType.get(); } + + // Paper start - data component API + /** + * Gets the default value of the data component type for this item type. + * + * @param type the data component type + * @param the value type + * @return the default value or {@code null} if there is none + * @see #hasDefaultData(io.papermc.paper.datacomponent.DataComponentType) for DataComponentType.NonValued + * @throws IllegalArgumentException if {@link #isItem()} is {@code false} + */ + public @Nullable T getDefaultData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued type) { + Preconditions.checkArgument(this.asItemType() != null); + return this.asItemType().getDefaultData(type); + } + + /** + * Checks if the data component type has a default value for this item type. + * + * @param type the data component type + * @return {@code true} if there is a default value + * @throws IllegalArgumentException if {@link #isItem()} is {@code false} + */ + public boolean hasDefaultData(final io.papermc.paper.datacomponent.@NotNull DataComponentType type) { + Preconditions.checkArgument(this.asItemType() != null); + return this.asItemType().hasDefaultData(type); + } + + /** + * Gets the default data component types for this item type. + * + * @return an immutable set of data component types + * @throws IllegalArgumentException if {@link #isItem()} is {@code false} + */ + public java.util.@org.jetbrains.annotations.Unmodifiable @NotNull Set getDefaultDataTypes() { + Preconditions.checkArgument(this.asItemType() != null); + return this.asItemType().getDefaultDataTypes(); + } + // Paper end - data component API } diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java index 7cf7c6d05aa6cbf3f0c8612831404552c6a7b84a..c60e31425efd7b863941f5538faef6c0552290ae 100644 --- a/src/main/java/org/bukkit/Registry.java +++ b/src/main/java/org/bukkit/Registry.java @@ -376,6 +376,7 @@ public interface Registry extends Iterable { */ Registry POTION_EFFECT_TYPE = EFFECT; // Paper end - potion effect type registry + Registry DATA_COMPONENT_TYPE = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.DATA_COMPONENT_TYPE); // Paper /** * Get the object by its key. * diff --git a/src/main/java/org/bukkit/block/BlockType.java b/src/main/java/org/bukkit/block/BlockType.java index ed534fe4983873a2d5f623f0d9d5e3ce254615eb..f019d490794b49d21057820bab047e2f909934a1 100644 --- a/src/main/java/org/bukkit/block/BlockType.java +++ b/src/main/java/org/bukkit/block/BlockType.java @@ -128,7 +128,7 @@ import org.jetbrains.annotations.Nullable; * official replacement for the aforementioned enum. Entirely incompatible * changes may occur. Do not use this API in plugins. */ -@ApiStatus.Internal +@org.jetbrains.annotations.ApiStatus.Experimental // Paper - data component API - already required for data component API public interface BlockType extends Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - add translatable & feature flag API /** diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java index b59222b8c262545d100a9fd28b3bf1d2a4cf4eb0..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 the value type + * @return the value for the data component type, or {@code null} if not set or marked as removed + * @see #hasData(io.papermc.paper.datacomponent.DataComponentType) for DataComponentType.NonValued + */ + @org.jetbrains.annotations.Contract(pure = true) + @org.jetbrains.annotations.ApiStatus.Experimental + public @Nullable T getData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued type) { + return this.craftDelegate.getData(type); + } + + /** + * Gets the value for the data component type on this stack with + * a fallback value. + * + * @param type the data component type + * @param fallback the fallback value if the value isn't present + * @param the value type + * @return the value for the data component type or the fallback value + */ + @Utility + @org.jetbrains.annotations.Contract(value = "_, !null -> !null", pure = true) + @org.jetbrains.annotations.ApiStatus.Experimental + public @Nullable T getDataOrDefault(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued type, final @Nullable T fallback) { + final T object = this.getData(type); + return object != null ? object : fallback; + } + + /** + * Checks if the data component type is set on the itemstack. + * + * @param type the data component type + * @return {@code true} if set, {@code false} otherwise + */ + @org.jetbrains.annotations.Contract(pure = true) + @org.jetbrains.annotations.ApiStatus.Experimental + public boolean hasData(final io.papermc.paper.datacomponent.@NotNull DataComponentType type) { + return this.craftDelegate.hasData(type); + } + + /** + * Gets all the data component types set on this stack. + * + * @return an immutable set of data component types + */ + @org.jetbrains.annotations.Contract("-> new") + @org.jetbrains.annotations.ApiStatus.Experimental + public java.util.@org.jetbrains.annotations.Unmodifiable Set getDataTypes() { + return this.craftDelegate.getDataTypes(); + } + + /** + * Sets the value of the data component type for this itemstack. To + * reset the value to the default for the {@link #getType() item type}, use + * {@link #resetData(io.papermc.paper.datacomponent.DataComponentType)}. To mark the data component type + * as removed, use {@link #unsetData(io.papermc.paper.datacomponent.DataComponentType)}. + * + * @param type the data component type + * @param valueBuilder value builder + * @param value type + */ + @Utility + @org.jetbrains.annotations.ApiStatus.Experimental + public void setData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued type, final @NotNull io.papermc.paper.datacomponent.DataComponentBuilder valueBuilder) { + this.setData(type, valueBuilder.build()); + } + + // /** + // * Modifies the value of the specified data component type for this item stack based on the result + // * of applying a given function to the current value. + // * + // *

If the function returns {@code null}, the data component type will be reset using + // * {@link #unsetData(DataComponentType)}. Otherwise, the + // * component value will be updated with the new result using {@link #setData(DataComponentType.Valued, Object)}.

+ // * + // * @param the type of the data component's value + // * @param type the data component type to be modified + // * @param consumer a function that takes the current component value (can be {@code null}) and + // * returns the modified value (or {@code null} to unset) + // */ + // @Utility + // public void editData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued type, final @NotNull java.util.function.Function<@Nullable T, @Nullable T> consumer) { + // T value = getData(type); + // T newType = consumer.apply(value); + // if (newType == null) { + // unsetData(type); + // } else { + // setData(type, newType); + // } + // } + + /** + * Sets the value of the data component type for this itemstack. To + * reset the value to the default for the {@link #getType() item type}, use + * {@link #resetData(io.papermc.paper.datacomponent.DataComponentType)}. To mark the data component type + * as removed, use {@link #unsetData(io.papermc.paper.datacomponent.DataComponentType)}. + * + * @param type the data component type + * @param value value to set + * @param value type + */ + @org.jetbrains.annotations.ApiStatus.Experimental + public void setData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued type, final @NotNull T value) { + this.craftDelegate.setData(type, value); + } + + /** + * Marks this non-valued data component type as present in this itemstack. + * + * @param type the data component type + */ + @org.jetbrains.annotations.ApiStatus.Experimental + public void setData(final io.papermc.paper.datacomponent.DataComponentType.@NotNull NonValued type) { + this.craftDelegate.setData(type); + } + + /** + * Marks this data component as removed for this itemstack. + * + * @param type the data component type + */ + @org.jetbrains.annotations.ApiStatus.Experimental + public void unsetData(final io.papermc.paper.datacomponent.@NotNull DataComponentType type) { + this.craftDelegate.unsetData(type); + } + + /** + * Resets the value of this component to be the default + * value for the item type from {@link Material#getDefaultData(io.papermc.paper.datacomponent.DataComponentType.Valued)}. + * + * @param type the data component type + */ + @org.jetbrains.annotations.ApiStatus.Experimental + public void resetData(final io.papermc.paper.datacomponent.@NotNull DataComponentType type) { + this.craftDelegate.resetData(type); + } + + /** + * Checks if the data component type is overridden from the default for the + * item type. + * + * @param type the data component type + * @return {@code true} if the data type is overridden + */ + @org.jetbrains.annotations.ApiStatus.Experimental + public boolean isDataOverridden(final io.papermc.paper.datacomponent.@NotNull DataComponentType type) { + return this.craftDelegate.isDataOverridden(type); + } + + /** + * Checks if this itemstack matches another given itemstack excluding the provided components. + * This is useful if you are wanting to ignore certain properties of itemstacks, such as durability. + * + * @param item the item to compare + * @param excludeTypes the data component types to ignore + * @return {@code true} if the provided item is equal, ignoring the provided components + */ + @org.jetbrains.annotations.ApiStatus.Experimental + public boolean matchesWithoutData(final @NotNull ItemStack item, final @NotNull java.util.Set excludeTypes) { + return this.matchesWithoutData(item, excludeTypes, false); + } + + /** + * Checks if this itemstack matches another given itemstack excluding the provided components. + * This is useful if you are wanting to ignore certain properties of itemstacks, such as durability. + * + * @param item the item to compare + * @param excludeTypes the data component types to ignore + * @param ignoreCount ignore the count of the item + * @return {@code true} if the provided item is equal, ignoring the provided components + */ + @org.jetbrains.annotations.ApiStatus.Experimental + public boolean matchesWithoutData(final @NotNull ItemStack item, final @NotNull java.util.Set excludeTypes, final boolean ignoreCount) { + return this.craftDelegate.matchesWithoutData(item, excludeTypes, ignoreCount); + } + // Paper end - data component API } diff --git a/src/main/java/org/bukkit/inventory/ItemType.java b/src/main/java/org/bukkit/inventory/ItemType.java index 72803c00e4af576f286d2af34bf300ee554a7f3c..2a3c4f055d0e4ef009caed95152570660ab100d5 100644 --- a/src/main/java/org/bukkit/inventory/ItemType.java +++ b/src/main/java/org/bukkit/inventory/ItemType.java @@ -2483,4 +2483,33 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans */ @Nullable ItemRarity getItemRarity(); // Paper end - expand ItemRarity API + // Paper start - data component API + /** + * Gets the default value of the data component type for this item type. + * + * @param type the data component type + * @param the value type + * @return the default value or {@code null} if there is none + * @see #hasDefaultData(io.papermc.paper.datacomponent.DataComponentType) for DataComponentType.NonValued + */ + @org.jetbrains.annotations.ApiStatus.Experimental + @Nullable T getDefaultData(io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued type); + + /** + * Checks if the data component type has a default value for this item type. + * + * @param type the data component type + * @return {@code true} if there is a default value + */ + @org.jetbrains.annotations.ApiStatus.Experimental + boolean hasDefaultData(io.papermc.paper.datacomponent.@NotNull DataComponentType type); + + /** + * Gets the default data component types for this item type. + * + * @return an immutable set of data component types + */ + @org.jetbrains.annotations.ApiStatus.Experimental + java.util.@org.jetbrains.annotations.Unmodifiable @NotNull Set getDefaultDataTypes(); + // Paper end - data component API }