Paper/patches/api/0477-Introduce-registry-entry-and-builders.patch
Jake Potrebic 8dfb85c173
Enchantment and GameEvent Registry builders (#10889)
Co-authored-by: Bjarne Koll <lynxplay101@gmail.com>
2024-06-17 00:00:12 +02:00

495 lines
22 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bjarne Koll <lynxplay101@gmail.com>
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 76daccf4ea502e1747a6f9176dc16fd20c561286..2945dde566682f977e84fde5d473a6c69be24df1 100644
--- a/src/main/java/io/papermc/paper/registry/RegistryKey.java
+++ b/src/main/java/io/papermc/paper/registry/RegistryKey.java
@@ -76,9 +76,10 @@ 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..15fcdbba6da2defb8849450ec4e19e6da301362a
--- /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<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 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 tag key.
+ */
+ @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 GameEventRegistryEntry} 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 tag key 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 tag key 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..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.
+ * <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..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}.
+ * <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 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<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 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 94587a97fcea81a43b160b01d2c81cef2b7f4413..6bc1853ada3ea38bc36cb31fbb5ce246347fe5d4 100644
--- a/src/main/java/org/bukkit/inventory/ItemType.java
+++ b/src/main/java/org/bukkit/inventory/ItemType.java
@@ -46,7 +46,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
/**