From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bjarne Koll <git@lynxplay.dev>
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/io/papermc/paper/registry/RegistryKey.java
+++ b/src/main/java/io/papermc/paper/registry/RegistryKey.java
@@ -0,0 +0,0 @@ public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
     @ApiStatus.Internal
     RegistryKey<BlockType> 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<ItemType> 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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/data/EnchantmentRegistryEntry.java
@@ -0,0 +0,0 @@
+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<ItemType> supportedItems();
+
+    /**
+     * Provides the registry key set referencing the item types this enchantment can be applied to when
+     * enchanting in an enchantment table.
+     * <p>
+     * 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<ItemType> primaryItems();
+
+    /**
+     * Provides the weight of this enchantment used by the weighted random when selecting enchantments.
+     *
+     * @return the weight value.
+     * @see <a href="https://minecraft.wiki/w/Enchanting">https://minecraft.wiki/w/Enchanting</a> 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.
+     * <p>
+     * Note that a cost is not directly related to the consumed xp.
+     *
+     * @return the enchantment cost.
+     * @see <a href="https://minecraft.wiki/w/Enchanting/Levels">https://minecraft.wiki/w/Enchanting/Levels</a> for
+     * examplary costs.
+     */
+    @NonNull EnchantmentCost minimumCost();
+
+    /**
+     * Provides the maximum cost allowed to enchant an item with this enchantment.
+     * <p>
+     * Note that a cost is not directly related to the consumed xp.
+     *
+     * @return the enchantment cost.
+     * @see <a href="https://minecraft.wiki/w/Enchanting/Levels">https://minecraft.wiki/w/Enchanting/Levels</a> for
+     * examplary costs.
+     */
+    @NonNull EnchantmentCost maximumCost();
+
+    /**
+     * Provides the cost of applying this enchantment using an anvil.
+     * <p>
+     * Note that this is halved when using an enchantment book, and is multiplied by the level of the enchantment.
+     * See <a href="https://minecraft.wiki/w/Anvil_mechanics">https://minecraft.wiki/w/Anvil_mechanics</a> for more
+     * information.
+     * </p>
+     *
+     * @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.
+     * <p>
+     * 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<EquipmentSlotGroup> activeSlots();
+
+    /**
+     * Provides the registry key set of enchantments that this enchantment is exclusive with.
+     * <p>
+     * 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<Enchantment> exclusiveWith();
+
+    /**
+     * A mutable builder for the {@link EnchantmentRegistryEntry} plugins may change in applicable registry events.
+     * <p>
+     * The following values are required for each builder:
+     * <ul>
+     *     <li>{@link #description(Component)}</li>
+     *     <li>{@link #supportedItems(RegistryKeySet)}</li>
+     *     <li>{@link #weight(int)}</li>
+     *     <li>{@link #maxLevel(int)}</li>
+     *     <li>{@link #minimumCost(EnchantmentCost)}</li>
+     *     <li>{@link #maximumCost(EnchantmentCost)}</li>
+     *     <li>{@link #anvilCost(int)}</li>
+     *     <li>{@link #activeSlots(Iterable)}</li>
+     * </ul>
+     */
+    @ApiStatus.Experimental
+    @ApiStatus.NonExtendable
+    interface Builder extends EnchantmentRegistryEntry, RegistryBuilder<Enchantment> {
+
+        /**
+         * 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<ItemType> 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}.
+         * <p>
+         * 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<ItemType> primaryItems);
+
+        /**
+         * Configures the weight of this enchantment used by the weighted random when selecting enchantments.
+         *
+         * @param weight the weight value.
+         * @return this builder.
+         * @see <a href="https://minecraft.wiki/w/Enchanting">https://minecraft.wiki/w/Enchanting</a> 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.
+         * <p>
+         * Note that a cost is not directly related to the consumed xp.
+         *
+         * @param minimumCost the enchantment cost.
+         * @return this builder.
+         * @see <a href="https://minecraft.wiki/w/Enchanting/Levels">https://minecraft.wiki/w/Enchanting/Levels</a> 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.
+         * <p>
+         * Note that a cost is not directly related to the consumed xp.
+         *
+         * @param maximumCost the enchantment cost.
+         * @return this builder.
+         * @see <a href="https://minecraft.wiki/w/Enchanting/Levels">https://minecraft.wiki/w/Enchanting/Levels</a> for
+         * examplary costs.
+         */
+        @Contract(value = "_ -> this", mutates = "this")
+        @NonNull Builder maximumCost(@NotNull EnchantmentCost maximumCost);
+
+        /**
+         * Configures the cost of applying this enchantment using an anvil.
+         * <p>
+         * Note that this is halved when using an enchantment book, and is multiplied by the level of the enchantment.
+         * See <a href="https://minecraft.wiki/w/Anvil_mechanics">https://minecraft.wiki/w/Anvil_mechanics</a> for more information.
+         * </p>
+         *
+         * @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.
+         * <p>
+         * 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.
+         * <p>
+         * 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.
+         * <p>
+         * Exclusive enchantments prohibit the application of this enchantment to an item if they are already present on
+         * said item.
+         * <p>
+         * 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<Enchantment> 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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/data/GameEventRegistryEntry.java
@@ -0,0 +0,0 @@
+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.
+     * <p>
+     * The following values are required for each builder:
+     * <ul>
+     *     <li>{@link #range(int)}</li>
+     * </ul>
+     */
+    interface Builder extends GameEventRegistryEntry, RegistryBuilder<GameEvent> {
+
+        /**
+         * 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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/data/package-info.java
@@ -0,0 +0,0 @@
+/**
+ * Collection of registry entry types that may be created or modified via the
+ * {@link io.papermc.paper.registry.event.RegistryEvent}.
+ * <p>
+ * 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java
@@ -0,0 +0,0 @@
 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.
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.ApiStatus;
 @ApiStatus.Experimental
 public final class RegistryEvents {
 
+    public static final RegistryEventProvider<GameEvent, GameEventRegistryEntry.Builder> GAME_EVENT = create(RegistryKey.GAME_EVENT);
+    public static final RegistryEventProvider<Enchantment, EnchantmentRegistryEntry.Builder> 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/GameEvent.java
+++ b/src/main/java/org/bukkit/GameEvent.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/inventory/ItemType.java
+++ b/src/main/java/org/bukkit/inventory/ItemType.java
@@ -0,0 +0,0 @@ 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
 
     /**
