From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Bjarne Koll Date: Thu, 13 Jun 2024 22:35:05 +0200 Subject: [PATCH] Introduce registry entry and builders diff --git a/src/main/java/io/papermc/paper/registry/RegistryKey.java b/src/main/java/io/papermc/paper/registry/RegistryKey.java index 647f6a1ec1f9d3c203b41f90a99bfd415bf67366..9b39e33514b15a9d07104e2ad826d0da11f569d6 100644 --- a/src/main/java/io/papermc/paper/registry/RegistryKey.java +++ b/src/main/java/io/papermc/paper/registry/RegistryKey.java @@ -74,9 +74,10 @@ public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl { @ApiStatus.Internal RegistryKey BLOCK = create("block"); /** - * @apiNote DO NOT USE + * @apiNote use preferably only in the context of registry entries. + * @see io.papermc.paper.registry.data */ - @ApiStatus.Internal + @ApiStatus.Experimental // Paper - already required for registry builders RegistryKey ITEM = create("item"); /** * Built-in registry for cat variants. diff --git a/src/main/java/io/papermc/paper/registry/data/EnchantmentRegistryEntry.java b/src/main/java/io/papermc/paper/registry/data/EnchantmentRegistryEntry.java new file mode 100644 index 0000000000000000000000000000000000000000..d0d19a4bb2ba0e92710861b106777a428bdc5ad9 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/data/EnchantmentRegistryEntry.java @@ -0,0 +1,332 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.registry.RegistryBuilder; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.registry.set.RegistryKeySet; +import io.papermc.paper.registry.set.RegistrySet; +import io.papermc.paper.registry.tag.TagKey; +import java.util.List; +import net.kyori.adventure.text.Component; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.EquipmentSlotGroup; +import org.bukkit.inventory.ItemType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Range; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * A data-centric version-specific registry entry for the {@link Enchantment} type. + */ +@ApiStatus.Experimental +@NullMarked +@ApiStatus.NonExtendable +public interface EnchantmentRegistryEntry { + + /** + * Provides the description of this enchantment entry as displayed to the client, e.g. "Sharpness" for the sharpness + * enchantment. + * + * @return the description component. + */ + Component description(); + + /** + * Provides the registry key set referencing the items this enchantment is supported on. + * + * @return the registry key set. + */ + RegistryKeySet supportedItems(); + + /** + * Provides the registry key set referencing the item types this enchantment can be applied to when + * enchanting in an enchantment table. + *

+ * If this value is {@code null}, {@link #supportedItems()} will be sourced instead in the context of an enchantment table. + * Additionally, the tag {@link io.papermc.paper.registry.keys.tags.EnchantmentTagKeys#IN_ENCHANTING_TABLE} defines + * which enchantments can even show up in an enchantment table. + * + * @return the registry key set. + */ + @Nullable RegistryKeySet primaryItems(); + + /** + * Provides the weight of this enchantment used by the weighted random when selecting enchantments. + * + * @return the weight value. + * @see https://minecraft.wiki/w/Enchanting for examplary weights. + */ + @Range(from = 1, to = 1024) int weight(); + + /** + * Provides the maximum level this enchantment can have when applied. + * + * @return the maximum level. + */ + @Range(from = 1, to = 255) int maxLevel(); + + /** + * Provides the minimum cost needed to enchant an item with this enchantment. + *

+ * Note that a cost is not directly related to the consumed xp. + * + * @return the enchantment cost. + * @see https://minecraft.wiki/w/Enchanting/Levels for + * examplary costs. + */ + EnchantmentCost minimumCost(); + + /** + * Provides the maximum cost allowed to enchant an item with this enchantment. + *

+ * Note that a cost is not directly related to the consumed xp. + * + * @return the enchantment cost. + * @see https://minecraft.wiki/w/Enchanting/Levels for + * examplary costs. + */ + EnchantmentCost maximumCost(); + + /** + * Provides the cost of applying this enchantment using an anvil. + *

+ * Note that this is halved when using an enchantment book, and is multiplied by the level of the enchantment. + * See https://minecraft.wiki/w/Anvil_mechanics for more + * information. + *

+ * + * @return the anvil cost of this enchantment + */ + @Range(from = 0, to = Integer.MAX_VALUE) int anvilCost(); + + /** + * Provides a list of slot groups this enchantment may be active in. + *

+ * If the item enchanted with this enchantment is equipped in a slot not covered by the returned list and its + * groups, the enchantment's effects, like attribute modifiers, will not activate. + * + * @return a list of equipment slot groups. + * @see Enchantment#getActiveSlotGroups() + */ + @Unmodifiable List activeSlots(); + + /** + * Provides the registry key set of enchantments that this enchantment is exclusive with. + *

+ * Exclusive enchantments prohibit the application of this enchantment to an item if they are already present on + * said item. + * + * @return a registry set of enchantments exclusive to this one. + */ + RegistryKeySet exclusiveWith(); + + /** + * A mutable builder for the {@link EnchantmentRegistryEntry} plugins may change in applicable registry events. + *

+ * The following values are required for each builder: + *

    + *
  • {@link #description(Component)}
  • + *
  • {@link #supportedItems(RegistryKeySet)}
  • + *
  • {@link #weight(int)}
  • + *
  • {@link #maxLevel(int)}
  • + *
  • {@link #minimumCost(EnchantmentCost)}
  • + *
  • {@link #maximumCost(EnchantmentCost)}
  • + *
  • {@link #anvilCost(int)}
  • + *
  • {@link #activeSlots(Iterable)}
  • + *
+ */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends EnchantmentRegistryEntry, RegistryBuilder { + + /** + * Configures the description of this enchantment entry as displayed to the client, e.g. "Sharpness" for the + * sharpness enchantment. + * + * @param description the description component. + * @return this builder. + */ + @Contract(value = "_ -> this", mutates = "this") + Builder description(Component description); + + /** + * Configures the set of supported items this enchantment can be applied on. This + * can be a {@link RegistryKeySet} created via {@link RegistrySet#keySet(io.papermc.paper.registry.RegistryKey, Iterable)} or + * a tag obtained via {@link io.papermc.paper.registry.event.RegistryFreezeEvent#getOrCreateTag(TagKey)} with + * tag keys found in {@link io.papermc.paper.registry.keys.tags.ItemTypeTagKeys} such as + * {@link io.papermc.paper.registry.keys.tags.ItemTypeTagKeys#ENCHANTABLE_ARMOR} and + * {@link io.papermc.paper.registry.keys.tags.ItemTypeTagKeys#ENCHANTABLE_SWORD}. + * + * @param supportedItems the registry key set representing the supported items. + * @return this builder. + * @see RegistrySet#keySet(RegistryKey, TypedKey[]) + * @see io.papermc.paper.registry.event.RegistryFreezeEvent#getOrCreateTag(TagKey) + */ + @Contract(value = "_ -> this", mutates = "this") + Builder supportedItems(RegistryKeySet supportedItems); + + /** + * Configures a set of item types this enchantment can naturally be applied to, when enchanting in an + * enchantment table.This can be a {@link RegistryKeySet} created via + * {@link RegistrySet#keySet(io.papermc.paper.registry.RegistryKey, Iterable)} or a tag obtained via + * {@link io.papermc.paper.registry.event.RegistryFreezeEvent#getOrCreateTag(TagKey)} with + * tag keys found in {@link io.papermc.paper.registry.keys.tags.ItemTypeTagKeys} such as + * {@link io.papermc.paper.registry.keys.tags.ItemTypeTagKeys#ENCHANTABLE_ARMOR} and + * {@link io.papermc.paper.registry.keys.tags.ItemTypeTagKeys#ENCHANTABLE_SWORD}. + *

+ * Defaults to {@code null} which means all {@link #supportedItems()} are considered primary items. + * Additionally, the tag {@link io.papermc.paper.registry.keys.tags.EnchantmentTagKeys#IN_ENCHANTING_TABLE} defines + * which enchantments can even show up in an enchantment table. + * + * @param primaryItems the registry key set representing the primary items. + * @return this builder. + * @see RegistrySet#keySet(RegistryKey, TypedKey[]) + * @see io.papermc.paper.registry.event.RegistryFreezeEvent#getOrCreateTag(TagKey) + */ + @Contract(value = "_ -> this", mutates = "this") + Builder primaryItems(@Nullable RegistryKeySet primaryItems); + + /** + * Configures the weight of this enchantment used by the weighted random when selecting enchantments. + * + * @param weight the weight value. + * @return this builder. + * @see https://minecraft.wiki/w/Enchanting for examplary weights. + */ + @Contract(value = "_ -> this", mutates = "this") + Builder weight(@Range(from = 1, to = 1024) int weight); + + /** + * Configures the maximum level this enchantment can have when applied. + * + * @param maxLevel the maximum level. + * @return this builder. + */ + @Contract(value = "_ -> this", mutates = "this") + Builder maxLevel(@Range(from = 1, to = 255) int maxLevel); + + /** + * Configures the minimum cost needed to enchant an item with this enchantment. + *

+ * Note that a cost is not directly related to the consumed xp. + * + * @param minimumCost the enchantment cost. + * @return this builder. + * @see https://minecraft.wiki/w/Enchanting/Levels for + * examplary costs. + */ + @Contract(value = "_ -> this", mutates = "this") + Builder minimumCost(EnchantmentCost minimumCost); + + /** + * Configures the maximum cost to enchant an item with this enchantment. + *

+ * Note that a cost is not directly related to the consumed xp. + * + * @param maximumCost the enchantment cost. + * @return this builder. + * @see https://minecraft.wiki/w/Enchanting/Levels for + * examplary costs. + */ + @Contract(value = "_ -> this", mutates = "this") + Builder maximumCost(EnchantmentCost maximumCost); + + /** + * Configures the cost of applying this enchantment using an anvil. + *

+ * Note that this is halved when using an enchantment book, and is multiplied by the level of the enchantment. + * See https://minecraft.wiki/w/Anvil_mechanics for more information. + *

+ * + * @param anvilCost the anvil cost of this enchantment + * @return this builder. + * @see Enchantment#getAnvilCost() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder anvilCost(@Range(from = 0, to = Integer.MAX_VALUE) int anvilCost); + + /** + * Configures the list of slot groups this enchantment may be active in. + *

+ * If the item enchanted with this enchantment is equipped in a slot not covered by the returned list and its + * groups, the enchantment's effects, like attribute modifiers, will not activate. + * + * @param activeSlots a list of equipment slot groups. + * @return this builder. + * @see Enchantment#getActiveSlotGroups() + */ + @Contract(value = "_ -> this", mutates = "this") + default Builder activeSlots(final EquipmentSlotGroup... activeSlots) { + return this.activeSlots(List.of(activeSlots)); + } + + /** + * Configures the list of slot groups this enchantment may be active in. + *

+ * If the item enchanted with this enchantment is equipped in a slot not covered by the returned list and its + * groups, the enchantment's effects, like attribute modifiers, will not activate. + * + * @param activeSlots a list of equipment slot groups. + * @return this builder. + * @see Enchantment#getActiveSlotGroups() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder activeSlots(Iterable activeSlots); + + /** + * Configures the registry key set of enchantments that this enchantment is exclusive with. + *

+ * Exclusive enchantments prohibit the application of this enchantment to an item if they are already present on + * said item. + *

+ * Defaults to an empty set allowing this enchantment to be applied regardless of other enchantments. + * + * @param exclusiveWith a registry set of enchantments exclusive to this one. + * @return this builder. + * @see RegistrySet#keySet(RegistryKey, TypedKey[]) + * @see io.papermc.paper.registry.event.RegistryFreezeEvent#getOrCreateTag(TagKey) + */ + @Contract(value = "_ -> this", mutates = "this") + Builder exclusiveWith(RegistryKeySet exclusiveWith); + } + + /** + * The enchantment cost interface represents the cost of applying an enchantment, split up into its different components. + */ + interface EnchantmentCost { + + /** + * Returns the base cost of this enchantment cost, no matter what level the enchantment has. + * + * @return the cost in levels. + */ + int baseCost(); + + /** + * Returns the additional cost added per level of the enchantment to be applied. + * This cost is applied per level above the first. + * + * @return the cost added to the {@link #baseCost()} for each level above the first. + */ + int additionalPerLevelCost(); + + /** + * Creates a new enchantment cost instance based on the passed values. + * + * @param baseCost the base cost of the enchantment cost as returned by {@link #baseCost()} + * @param additionalPerLevelCost the additional cost per level, as returned by {@link #additionalPerLevelCost()} + * @return the created instance. + */ + @Contract(value = "_,_ -> new", pure = true) + static EnchantmentCost of(final int baseCost, final int additionalPerLevelCost) { + record Impl(int baseCost, int additionalPerLevelCost) implements EnchantmentCost { + } + + return new Impl(baseCost, additionalPerLevelCost); + } + } + +} diff --git a/src/main/java/io/papermc/paper/registry/data/GameEventRegistryEntry.java b/src/main/java/io/papermc/paper/registry/data/GameEventRegistryEntry.java new file mode 100644 index 0000000000000000000000000000000000000000..980fe12b75258b51cc2498590cadb9de80805b1f --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/data/GameEventRegistryEntry.java @@ -0,0 +1,49 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.registry.RegistryBuilder; +import org.bukkit.GameEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Range; +import org.jspecify.annotations.NullMarked; + +/** + * A data-centric version-specific registry entry for the {@link GameEvent} type. + */ +@ApiStatus.Experimental +@NullMarked +@ApiStatus.NonExtendable +public interface GameEventRegistryEntry { + + /** + * Provides the range in which this game event will notify its listeners. + * + * @return the range of blocks, represented as an int. + * @see GameEvent#getRange() + */ + @Range(from = 0, to = Integer.MAX_VALUE) int range(); + + /** + * A mutable builder for the {@link GameEventRegistryEntry} plugins may change in applicable registry events. + *

+ * The following values are required for each builder: + *

    + *
  • {@link #range(int)}
  • + *
+ */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends GameEventRegistryEntry, RegistryBuilder { + + /** + * Sets the range in which this game event should notify its listeners. + * + * @param range the range of blocks. + * @return this builder instance. + * @see GameEventRegistryEntry#range() + * @see GameEvent#getRange() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder range(@Range(from = 0, to = Integer.MAX_VALUE) int range); + } +} diff --git a/src/main/java/io/papermc/paper/registry/data/package-info.java b/src/main/java/io/papermc/paper/registry/data/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..4f8f536f437c5f483ac7bce393e664fd7bc38477 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/data/package-info.java @@ -0,0 +1,9 @@ +/** + * Collection of registry entry types that may be created or modified via the + * {@link io.papermc.paper.registry.event.RegistryEvent}. + *

+ * A registry entry represents its runtime API counterpart as a version-specific data-focused type. + * Registry entries are not expected to be used during plugin runtime interactions with the API but are mostly + * exposed during registry creation/modification. + */ +package io.papermc.paper.registry.data; diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java b/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java index 91ae9c0d3ec55ce417d4b447bf3d1b0d0c174b5e..1c8e77c7243cfedef6c4d1491cf98e6ec8f1690f 100644 --- a/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java +++ b/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java @@ -1,8 +1,15 @@ package io.papermc.paper.registry.event; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.data.EnchantmentRegistryEntry; +import io.papermc.paper.registry.data.GameEventRegistryEntry; +import org.bukkit.GameEvent; +import org.bukkit.enchantments.Enchantment; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; +import static io.papermc.paper.registry.event.RegistryEventProviderImpl.create; + /** * Holds providers for {@link RegistryEntryAddEvent} and {@link RegistryFreezeEvent} * handlers for each applicable registry. @@ -11,6 +18,9 @@ import org.jspecify.annotations.NullMarked; @NullMarked public final class RegistryEvents { + public static final RegistryEventProvider GAME_EVENT = create(RegistryKey.GAME_EVENT); + public static final RegistryEventProvider ENCHANTMENT = create(RegistryKey.ENCHANTMENT); + private RegistryEvents() { } } diff --git a/src/main/java/org/bukkit/GameEvent.java b/src/main/java/org/bukkit/GameEvent.java index cb5f7dfcdbbb548d93ad21c215ba35a9e142a7b2..e2c632afdf555418dd1dc6ad6c5d197670e2211a 100644 --- a/src/main/java/org/bukkit/GameEvent.java +++ b/src/main/java/org/bukkit/GameEvent.java @@ -141,4 +141,22 @@ public abstract class GameEvent implements Keyed { private static GameEvent getEvent(@NotNull String key) { return Registry.GAME_EVENT.getOrThrow(NamespacedKey.minecraft(key)); } + // Paper start + /** + * Gets the range of the event which is used to + * notify listeners of the event. + * + * @return the range + */ + public abstract int getRange(); + + /** + * Gets the vibration level of the game event for vibration listeners. + * Not all events have vibration levels, and a level of 0 means + * it won't cause any vibrations. + * + * @return the vibration level + */ + public abstract int getVibrationLevel(); + // Paper end } diff --git a/src/main/java/org/bukkit/inventory/ItemType.java b/src/main/java/org/bukkit/inventory/ItemType.java index 7a1a0aebbfdaac6b6af41236d4a00512244b58fa..ef3a30d5cca29c7a7c546791be3c333e63e425f4 100644 --- a/src/main/java/org/bukkit/inventory/ItemType.java +++ b/src/main/java/org/bukkit/inventory/ItemType.java @@ -47,7 +47,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 +@ApiStatus.Experimental // Paper - already required for registry builders public interface ItemType extends Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper - add Translatable /**