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 7763ea28a00c25ffd8ba8941cff29bae5ae117a2..ccbe3fa2e01a80abb801d14891dce34ed179b5ee 100644 --- a/src/main/java/io/papermc/paper/registry/RegistryKey.java +++ b/src/main/java/io/papermc/paper/registry/RegistryKey.java @@ -77,9 +77,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"); 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..6c7d8b98103909428bb4dcf14825fe188db6d12f --- /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.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Range; +import org.jetbrains.annotations.Unmodifiable; + +/** + * A data-centric version-specific registry entry for the {@link Enchantment} type. + */ +@ApiStatus.Experimental +@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. + */ + @NonNull Component description(); + + /** + * Provides the registry key set referencing the items this enchantment is supported on. + * + * @return the registry key set. + */ + @NonNull 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. + */ + @NonNull 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. + */ + @NonNull 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() + */ + @NonNull @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. + */ + @NonNull 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") + @NonNull Builder description(@NonNull 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") + @NonNull Builder supportedItems(@NonNull 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") + @NonNull 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") + @NonNull 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") + @NonNull 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") + @NonNull Builder minimumCost(@NotNull 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") + @NonNull Builder maximumCost(@NotNull 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") + @NonNull 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 @NonNull Builder activeSlots(final @NonNull EquipmentSlotGroup @NonNull... 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") + @NonNull Builder activeSlots(@NonNull Iterable<@NonNull EquipmentSlotGroup> 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") + @NonNull Builder exclusiveWith(@NonNull 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..27b4da08168c7b341b776635011ff4022a12d361 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/data/GameEventRegistryEntry.java @@ -0,0 +1,43 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.registry.RegistryBuilder; +import org.bukkit.GameEvent; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Range; + +/** + * A data-centric version-specific registry entry for the {@link GameEvent} type. + */ +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)}
  • + *
+ */ + 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") + @NonNull 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 1f89945be2ed68f52a544f41f7a151b8fdfe113e..b32ae215e976bcfcdd86b03037de61b3d896f57c 100644 --- a/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java +++ b/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java @@ -1,7 +1,14 @@ 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 static io.papermc.paper.registry.event.RegistryEventProviderImpl.create; + /** * Holds providers for {@link RegistryEntryAddEvent} and {@link RegistryFreezeEvent} * handlers for each applicable registry. @@ -9,6 +16,9 @@ import org.jetbrains.annotations.ApiStatus; @ApiStatus.Experimental 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 6c9689baca1763e2ef79495d38618d587e792434..4583092c2d1ffe95be2831c5d5f0e904283ab762 100644 --- a/src/main/java/org/bukkit/GameEvent.java +++ b/src/main/java/org/bukkit/GameEvent.java @@ -147,4 +147,22 @@ public abstract class GameEvent implements Keyed { return gameEvent; } + // 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 01ec84248a681180088fb1d7d22b80f8572b0305..0168f0a14a3e899e84c5e36963ff79950ab580fb 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 /**