From 8dfb85c173bff095b4ea983dad8344fdcccfabbe Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Sun, 16 Jun 2024 15:00:12 -0700 Subject: [PATCH] Enchantment and GameEvent Registry builders (#10889) Co-authored-by: Bjarne Koll --- ...ntroduce-registry-entry-and-builders.patch | 494 ++++++++++++++++++ .../1021-Registry-Modification-API.patch | 36 +- ...1022-Add-registry-entry-and-builders.patch | 483 +++++++++++++++++ 3 files changed, 992 insertions(+), 21 deletions(-) create mode 100644 patches/api/0477-Introduce-registry-entry-and-builders.patch create mode 100644 patches/server/1022-Add-registry-entry-and-builders.patch diff --git a/patches/api/0477-Introduce-registry-entry-and-builders.patch b/patches/api/0477-Introduce-registry-entry-and-builders.patch new file mode 100644 index 0000000000..19801e599e --- /dev/null +++ b/patches/api/0477-Introduce-registry-entry-and-builders.patch @@ -0,0 +1,494 @@ +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 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 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..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 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 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 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 GameEventRegistryEntry} 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 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 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 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 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 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 + + /** diff --git a/patches/server/1021-Registry-Modification-API.patch b/patches/server/1021-Registry-Modification-API.patch index d8e13337bd..c45b119038 100644 --- a/patches/server/1021-Registry-Modification-API.patch +++ b/patches/server/1021-Registry-Modification-API.patch @@ -65,7 +65,7 @@ index d591e3a2e19d5358a0d25a5a681368943622d231..f05ebf453406a924da3de6fb250f4793 return delayedRegistry.delegate(); diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistryBuilder.java b/src/main/java/io/papermc/paper/registry/PaperRegistryBuilder.java new file mode 100644 -index 0000000000000000000000000000000000000000..6932ffef54f90cc486f517561f73c2e4daf3983b +index 0000000000000000000000000000000000000000..528c6ee1739d92f766f3904acd7fc5734c93388a --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/PaperRegistryBuilder.java @@ -0,0 +1,26 @@ @@ -82,7 +82,7 @@ index 0000000000000000000000000000000000000000..6932ffef54f90cc486f517561f73c2e4 + @FunctionalInterface + interface Filler> { + -+ B fill(@Nullable Conversions conversions, TypedKey key, @Nullable M nms); ++ B fill(Conversions conversions, TypedKey key, @Nullable M nms); + + default Factory asFactory() { + return (lookup, key) -> this.fill(lookup, key, null); @@ -92,7 +92,7 @@ index 0000000000000000000000000000000000000000..6932ffef54f90cc486f517561f73c2e4 + @FunctionalInterface + interface Factory> { + -+ B create(@Nullable Conversions conversions, TypedKey key); ++ B create(Conversions conversions, TypedKey key); + } +} diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java b/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java @@ -426,10 +426,10 @@ index 0000000000000000000000000000000000000000..eda5cc7d45ef59ccc1c9c7e027c1f044 +} diff --git a/src/main/java/io/papermc/paper/registry/entry/AddableRegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/AddableRegistryEntry.java new file mode 100644 -index 0000000000000000000000000000000000000000..a93ee8a52c6ec8b8e9712e8449a9c0e6c3fd4046 +index 0000000000000000000000000000000000000000..aeec9b3ae2911f041d000b3db72f37974020ba60 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/entry/AddableRegistryEntry.java -@@ -0,0 +1,46 @@ +@@ -0,0 +1,44 @@ +package io.papermc.paper.registry.entry; + +import io.papermc.paper.registry.PaperRegistryBuilder; @@ -441,11 +441,9 @@ index 0000000000000000000000000000000000000000..a93ee8a52c6ec8b8e9712e8449a9c0e6 +import java.util.function.BiFunction; +import net.minecraft.core.MappedRegistry; +import net.minecraft.core.Registry; -+import net.minecraft.resources.RegistryOps; +import net.minecraft.resources.ResourceKey; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; -+import org.checkerframework.checker.nullness.qual.Nullable; + +public class AddableRegistryEntry> extends CraftRegistryEntry implements RegistryEntry.Addable { + @@ -472,16 +470,16 @@ index 0000000000000000000000000000000000000000..a93ee8a52c6ec8b8e9712e8449a9c0e6 + } + + @Override -+ public B fillBuilder(final @Nullable Conversions conversions, final TypedKey key, final M nms) { ++ public B fillBuilder(final Conversions conversions, final TypedKey key, final M nms) { + return this.builderFiller.fill(conversions, key, nms); + } +} diff --git a/src/main/java/io/papermc/paper/registry/entry/ModifiableRegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/ModifiableRegistryEntry.java new file mode 100644 -index 0000000000000000000000000000000000000000..cdc490c5254a80f8c38ff0b56c0fcd973df87f59 +index 0000000000000000000000000000000000000000..515a995e3862f8e7cb93d149315ea32e04a08716 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/entry/ModifiableRegistryEntry.java -@@ -0,0 +1,33 @@ +@@ -0,0 +1,32 @@ +package io.papermc.paper.registry.entry; + +import io.papermc.paper.registry.PaperRegistryBuilder; @@ -493,7 +491,6 @@ index 0000000000000000000000000000000000000000..cdc490c5254a80f8c38ff0b56c0fcd97 +import net.minecraft.resources.ResourceKey; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; -+import org.checkerframework.checker.nullness.qual.Nullable; + +public class ModifiableRegistryEntry> extends CraftRegistryEntry implements RegistryEntry.Modifiable { + @@ -511,15 +508,15 @@ index 0000000000000000000000000000000000000000..cdc490c5254a80f8c38ff0b56c0fcd97 + } + + @Override -+ public B fillBuilder(final @Nullable Conversions conversions, final TypedKey key, final M nms) { ++ public B fillBuilder(final Conversions conversions, final TypedKey key, final M nms) { + return this.builderFiller.fill(conversions, key, nms); + } +} diff --git a/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java -index 15991bf13894d850f360a520d1815711d25973ec..4712ff8be61994f4b0fa7c14a688c828ef13a175 100644 +index 15991bf13894d850f360a520d1815711d25973ec..f2e919705301cb23ed1938ca3c1976378249172c 100644 --- a/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java +++ b/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java -@@ -1,16 +1,24 @@ +@@ -1,7 +1,13 @@ package io.papermc.paper.registry.entry; +import io.papermc.paper.registry.PaperRegistryBuilder; @@ -533,10 +530,7 @@ index 15991bf13894d850f360a520d1815711d25973ec..4712ff8be61994f4b0fa7c14a688c828 import io.papermc.paper.registry.legacy.DelayedRegistryEntry; import java.util.function.BiFunction; import java.util.function.Supplier; - import net.minecraft.core.Registry; -+import net.minecraft.resources.RegistryOps; - import net.minecraft.resources.ResourceKey; - import org.bukkit.Keyed; +@@ -11,6 +17,7 @@ import org.bukkit.Keyed; import org.bukkit.NamespacedKey; import org.bukkit.craftbukkit.util.ApiVersion; import org.checkerframework.checker.nullness.qual.NonNull; @@ -544,13 +538,13 @@ index 15991bf13894d850f360a520d1815711d25973ec..4712ff8be61994f4b0fa7c14a688c828 import org.checkerframework.framework.qual.DefaultQualifier; @DefaultQualifier(NonNull.class) -@@ -32,6 +40,65 @@ public interface RegistryEntry extends RegistryEntryInfo extends RegistryEntryInfo(this); } + interface BuilderHolder> extends RegistryEntryInfo { + -+ B fillBuilder(@Nullable Conversions conversions, TypedKey key, M nms); ++ B fillBuilder(Conversions conversions, TypedKey key, M nms); + } + + /** @@ -610,7 +604,7 @@ index 15991bf13894d850f360a520d1815711d25973ec..4712ff8be61994f4b0fa7c14a688c828 static RegistryEntry entry( final ResourceKey> mcKey, final RegistryKey apiKey, -@@ -48,4 +115,24 @@ public interface RegistryEntry extends RegistryEntryInfo extends RegistryEntryInfo(mcKey, apiKey, apiRegistrySupplier); } diff --git a/patches/server/1022-Add-registry-entry-and-builders.patch b/patches/server/1022-Add-registry-entry-and-builders.patch new file mode 100644 index 0000000000..1b920c14d0 --- /dev/null +++ b/patches/server/1022-Add-registry-entry-and-builders.patch @@ -0,0 +1,483 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Bjarne Koll +Date: Thu, 13 Jun 2024 23:45:32 +0200 +Subject: [PATCH] Add registry entry and builders + + +diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java +index a688af29273ebfbb4f75dd74cd30627fc481c96c..9c0972023bc9be20ba81bbc2e1d6688b615760c0 100644 +--- a/src/main/java/io/papermc/paper/registry/PaperRegistries.java ++++ b/src/main/java/io/papermc/paper/registry/PaperRegistries.java +@@ -1,6 +1,8 @@ + package io.papermc.paper.registry; + + import io.papermc.paper.adventure.PaperAdventure; ++import io.papermc.paper.registry.data.PaperEnchantmentRegistryEntry; ++import io.papermc.paper.registry.data.PaperGameEventRegistryEntry; + import io.papermc.paper.registry.entry.RegistryEntry; + import io.papermc.paper.registry.tag.TagKey; + import java.util.Collections; +@@ -58,7 +60,7 @@ public final class PaperRegistries { + static { + REGISTRY_ENTRIES = List.of( + // built-ins +- entry(Registries.GAME_EVENT, RegistryKey.GAME_EVENT, GameEvent.class, CraftGameEvent::new), ++ writable(Registries.GAME_EVENT, RegistryKey.GAME_EVENT, GameEvent.class, CraftGameEvent::new, PaperGameEventRegistryEntry.PaperBuilder::new), + entry(Registries.INSTRUMENT, RegistryKey.INSTRUMENT, MusicInstrument.class, CraftMusicInstrument::new), + entry(Registries.MOB_EFFECT, RegistryKey.MOB_EFFECT, PotionEffectType.class, CraftPotionEffectType::new), + entry(Registries.STRUCTURE_TYPE, RegistryKey.STRUCTURE_TYPE, StructureType.class, CraftStructureType::new), +@@ -71,7 +73,7 @@ public final class PaperRegistries { + entry(Registries.TRIM_PATTERN, RegistryKey.TRIM_PATTERN, TrimPattern.class, CraftTrimPattern::new).delayed(), + entry(Registries.DAMAGE_TYPE, RegistryKey.DAMAGE_TYPE, DamageType.class, CraftDamageType::new).delayed(), + entry(Registries.WOLF_VARIANT, RegistryKey.WOLF_VARIANT, Wolf.Variant.class, CraftWolf.CraftVariant::new).delayed(), +- entry(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT, Enchantment.class, CraftEnchantment::new).withSerializationUpdater(FieldRename.ENCHANTMENT_RENAME).delayed(), ++ writable(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT, Enchantment.class, CraftEnchantment::new, PaperEnchantmentRegistryEntry.PaperBuilder::new).withSerializationUpdater(FieldRename.ENCHANTMENT_RENAME).delayed(), + entry(Registries.JUKEBOX_SONG, RegistryKey.JUKEBOX_SONG, JukeboxSong.class, CraftJukeboxSong::new).delayed(), + + // api-only +diff --git a/src/main/java/io/papermc/paper/registry/data/PaperEnchantmentRegistryEntry.java b/src/main/java/io/papermc/paper/registry/data/PaperEnchantmentRegistryEntry.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b9501554a0738776c265852184c7dbae3fe98c8c +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/data/PaperEnchantmentRegistryEntry.java +@@ -0,0 +1,234 @@ ++package io.papermc.paper.registry.data; ++ ++import com.google.common.base.Preconditions; ++import com.google.common.collect.Iterables; ++import com.google.common.collect.Lists; ++import io.papermc.paper.registry.PaperRegistryBuilder; ++import io.papermc.paper.registry.RegistryKey; ++import io.papermc.paper.registry.TypedKey; ++import io.papermc.paper.registry.data.util.Checks; ++import io.papermc.paper.registry.data.util.Conversions; ++import io.papermc.paper.registry.set.PaperRegistrySets; ++import io.papermc.paper.registry.set.RegistryKeySet; ++import java.util.Collections; ++import java.util.List; ++import java.util.Optional; ++import java.util.OptionalInt; ++import net.minecraft.core.HolderSet; ++import net.minecraft.core.component.DataComponentMap; ++import net.minecraft.core.registries.Registries; ++import net.minecraft.network.chat.Component; ++import net.minecraft.world.entity.EquipmentSlotGroup; ++import net.minecraft.world.item.Item; ++import net.minecraft.world.item.enchantment.Enchantment; ++import org.bukkit.craftbukkit.CraftEquipmentSlot; ++import org.bukkit.inventory.ItemType; ++import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++import org.jetbrains.annotations.Range; ++ ++import static io.papermc.paper.registry.data.util.Checks.asArgument; ++import static io.papermc.paper.registry.data.util.Checks.asArgumentMin; ++import static io.papermc.paper.registry.data.util.Checks.asConfigured; ++ ++@DefaultQualifier(NonNull.class) ++public class PaperEnchantmentRegistryEntry implements EnchantmentRegistryEntry { ++ ++ // Top level ++ protected @MonotonicNonNull Component description; ++ ++ // Definition ++ protected @MonotonicNonNull HolderSet supportedItems; ++ protected @Nullable HolderSet primaryItems; ++ protected OptionalInt weight = OptionalInt.empty(); ++ protected OptionalInt maxLevel = OptionalInt.empty(); ++ protected Enchantment.@MonotonicNonNull Cost minimumCost; ++ protected Enchantment.@MonotonicNonNull Cost maximumCost; ++ protected OptionalInt anvilCost = OptionalInt.empty(); ++ protected @MonotonicNonNull List activeSlots; ++ ++ // Exclusive ++ protected HolderSet exclusiveWith = HolderSet.empty(); // Paper added default to empty. ++ ++ // Effects ++ protected final DataComponentMap effects; ++ ++ protected final Conversions conversions; ++ ++ public PaperEnchantmentRegistryEntry( ++ final Conversions conversions, ++ final TypedKey ignoredKey, ++ final @Nullable Enchantment internal ++ ) { ++ this.conversions = conversions; ++ if (internal == null) { ++ this.effects = DataComponentMap.EMPTY; ++ return; ++ } ++ ++ // top level ++ this.description = internal.description(); ++ ++ // definition ++ final Enchantment.EnchantmentDefinition definition = internal.definition(); ++ this.supportedItems = definition.supportedItems(); ++ this.primaryItems = definition.primaryItems().orElse(null); ++ this.weight = OptionalInt.of(definition.weight()); ++ this.maxLevel = OptionalInt.of(definition.maxLevel()); ++ this.minimumCost = definition.minCost(); ++ this.maximumCost = definition.maxCost(); ++ this.anvilCost = OptionalInt.of(definition.anvilCost()); ++ this.activeSlots = definition.slots(); ++ ++ // exclusive ++ this.exclusiveWith = internal.exclusiveSet(); ++ ++ // effects ++ this.effects = internal.effects(); ++ } ++ ++ @Override ++ public net.kyori.adventure.text.Component description() { ++ return this.conversions.asAdventure(asConfigured(this.description, "description")); ++ } ++ ++ @Override ++ public RegistryKeySet supportedItems() { ++ return PaperRegistrySets.convertToApi(RegistryKey.ITEM, asConfigured(this.supportedItems, "supportedItems")); ++ } ++ ++ @Override ++ public @Nullable RegistryKeySet primaryItems() { ++ return this.primaryItems == null ? null : PaperRegistrySets.convertToApi(RegistryKey.ITEM, this.primaryItems); ++ } ++ ++ @Override ++ public @Range(from = 1, to = 1024) int weight() { ++ return asConfigured(this.weight, "weight"); ++ } ++ ++ @Override ++ public @Range(from = 1, to = 255) int maxLevel() { ++ return asConfigured(this.maxLevel, "maxLevel"); ++ } ++ ++ @Override ++ public EnchantmentCost minimumCost() { ++ final Enchantment.@MonotonicNonNull Cost cost = asConfigured(this.minimumCost, "minimumCost"); ++ return EnchantmentRegistryEntry.EnchantmentCost.of(cost.base(), cost.perLevelAboveFirst()); ++ } ++ ++ @Override ++ public EnchantmentCost maximumCost() { ++ final Enchantment.@MonotonicNonNull Cost cost = asConfigured(this.maximumCost, "maximumCost"); ++ return EnchantmentRegistryEntry.EnchantmentCost.of(cost.base(), cost.perLevelAboveFirst()); ++ } ++ ++ @Override ++ public @Range(from = 0, to = Integer.MAX_VALUE) int anvilCost() { ++ return asConfigured(this.anvilCost, "anvilCost"); ++ } ++ ++ @Override ++ public List activeSlots() { ++ return Collections.unmodifiableList(Lists.transform(asConfigured(this.activeSlots, "activeSlots"), CraftEquipmentSlot::getSlot)); ++ } ++ ++ @Override ++ public RegistryKeySet exclusiveWith() { ++ return PaperRegistrySets.convertToApi(RegistryKey.ENCHANTMENT, this.exclusiveWith); ++ } ++ ++ public static final class PaperBuilder extends PaperEnchantmentRegistryEntry implements EnchantmentRegistryEntry.Builder, ++ PaperRegistryBuilder { ++ ++ public PaperBuilder(final Conversions conversions, final TypedKey key, final @Nullable Enchantment internal) { ++ super(conversions, key, internal); ++ } ++ ++ @Override ++ public Builder description(final net.kyori.adventure.text.Component description) { ++ this.description = this.conversions.asVanilla(asArgument(description, "description")); ++ return this; ++ } ++ ++ @Override ++ public Builder supportedItems(final RegistryKeySet supportedItems) { ++ this.supportedItems = PaperRegistrySets.convertToNms(Registries.ITEM, this.conversions.lookup(), asArgument(supportedItems, "supportedItems")); ++ return this; ++ } ++ ++ @Override ++ public Builder primaryItems(final @Nullable RegistryKeySet primaryItems) { ++ this.primaryItems = primaryItems == null ? null : PaperRegistrySets.convertToNms(Registries.ITEM, this.conversions.lookup(), primaryItems); ++ return this; ++ } ++ ++ @Override ++ public Builder weight(final @Range(from = 1, to = 1024) int weight) { ++ this.weight = OptionalInt.of(Checks.asArgumentRange(weight, "weight", 1, 1024)); ++ return this; ++ } ++ ++ @Override ++ public Builder maxLevel(final @Range(from = 1, to = 255) int maxLevel) { ++ this.maxLevel = OptionalInt.of(Checks.asArgumentRange(maxLevel, "maxLevel", 1, 255)); ++ return this; ++ } ++ ++ @Override ++ public Builder minimumCost(final EnchantmentCost minimumCost) { ++ final EnchantmentCost validCost = asArgument(minimumCost, "minimumCost"); ++ this.minimumCost = Enchantment.dynamicCost(validCost.baseCost(), validCost.additionalPerLevelCost()); ++ return this; ++ } ++ ++ @Override ++ public Builder maximumCost(final EnchantmentCost maximumCost) { ++ final EnchantmentCost validCost = asArgument(maximumCost, "maximumCost"); ++ this.maximumCost = Enchantment.dynamicCost(validCost.baseCost(), validCost.additionalPerLevelCost()); ++ return this; ++ } ++ ++ @Override ++ public Builder anvilCost(final @Range(from = 0, to = Integer.MAX_VALUE) int anvilCost) { ++ Preconditions.checkArgument(anvilCost >= 0, "anvilCost must be non-negative"); ++ this.anvilCost = OptionalInt.of(asArgumentMin(anvilCost, "anvilCost", 0)); ++ return this; ++ } ++ ++ @Override ++ public Builder activeSlots(final Iterable activeSlots) { ++ this.activeSlots = Lists.newArrayList(Iterables.transform(asArgument(activeSlots, "activeSlots"), CraftEquipmentSlot::getNMSGroup)); ++ return this; ++ } ++ ++ @Override ++ public Builder exclusiveWith(final RegistryKeySet exclusiveWith) { ++ this.exclusiveWith = PaperRegistrySets.convertToNms(Registries.ENCHANTMENT, this.conversions.lookup(), asArgument(exclusiveWith, "exclusiveWith")); ++ return this; ++ } ++ ++ @Override ++ public Enchantment build() { ++ final Enchantment.EnchantmentDefinition def = new Enchantment.EnchantmentDefinition( ++ asConfigured(this.supportedItems, "supportedItems"), ++ Optional.ofNullable(this.primaryItems), ++ this.weight(), ++ this.maxLevel(), ++ asConfigured(this.minimumCost, "minimumCost"), ++ asConfigured(this.maximumCost, "maximumCost"), ++ this.anvilCost(), ++ Collections.unmodifiableList(asConfigured(this.activeSlots, "activeSlots")) ++ ); ++ return new Enchantment( ++ asConfigured(this.description, "description"), ++ def, ++ this.exclusiveWith, ++ this.effects ++ ); ++ } ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/data/PaperGameEventRegistryEntry.java b/src/main/java/io/papermc/paper/registry/data/PaperGameEventRegistryEntry.java +new file mode 100644 +index 0000000000000000000000000000000000000000..18f9463ae23ba2d9c65ffb7531a87c925a5a8d6f +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/data/PaperGameEventRegistryEntry.java +@@ -0,0 +1,57 @@ ++package io.papermc.paper.registry.data; ++ ++import io.papermc.paper.registry.PaperRegistryBuilder; ++import io.papermc.paper.registry.data.util.Conversions; ++import java.util.OptionalInt; ++import net.minecraft.world.level.gameevent.GameEvent; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++import org.jetbrains.annotations.Range; ++ ++import static io.papermc.paper.registry.data.util.Checks.asArgumentMin; ++import static io.papermc.paper.registry.data.util.Checks.asConfigured; ++ ++@DefaultQualifier(NonNull.class) ++public class PaperGameEventRegistryEntry implements GameEventRegistryEntry { ++ ++ protected OptionalInt range = OptionalInt.empty(); ++ ++ public PaperGameEventRegistryEntry( ++ final Conversions ignoredConversions, ++ final io.papermc.paper.registry.TypedKey ignoredKey, ++ final @Nullable GameEvent nms ++ ) { ++ if (nms == null) return; ++ ++ this.range = OptionalInt.of(nms.notificationRadius()); ++ } ++ ++ @Override ++ public @Range(from = 0, to = Integer.MAX_VALUE) int range() { ++ return asConfigured(this.range, "range"); ++ } ++ ++ public static final class PaperBuilder extends PaperGameEventRegistryEntry implements GameEventRegistryEntry.Builder, ++ PaperRegistryBuilder { ++ ++ public PaperBuilder( ++ final Conversions conversions, ++ final io.papermc.paper.registry.TypedKey key, ++ final @Nullable GameEvent nms ++ ) { ++ super(conversions, key, nms); ++ } ++ ++ @Override ++ public GameEventRegistryEntry.Builder range(final @Range(from = 0, to = Integer.MAX_VALUE) int range) { ++ this.range = OptionalInt.of(asArgumentMin(range, "range", 0)); ++ return this; ++ } ++ ++ @Override ++ public GameEvent build() { ++ return new GameEvent(this.range()); ++ } ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/data/util/Checks.java b/src/main/java/io/papermc/paper/registry/data/util/Checks.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a66cebe2b918a22b84e346127ffb671621050e5f +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/data/util/Checks.java +@@ -0,0 +1,48 @@ ++package io.papermc.paper.registry.data.util; ++ ++import java.util.OptionalInt; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++@DefaultQualifier(NonNull.class) ++public final class Checks { ++ ++ public static T asConfigured(final @Nullable T value, final String field) { ++ if (value == null) { ++ throw new IllegalStateException(field + " has not been configured"); ++ } ++ return value; ++ } ++ ++ public static int asConfigured(final OptionalInt value, final String field) { ++ if (value.isEmpty()) { ++ throw new IllegalStateException(field + " has not been configured"); ++ } ++ return value.getAsInt(); ++ } ++ ++ public static T asArgument(final @Nullable T value, final String field) { ++ if (value == null) { ++ throw new IllegalArgumentException("argument " + value + " cannot be null"); ++ } ++ return value; ++ } ++ ++ public static int asArgumentRange(final int value, final String field, final int min, final int max) { ++ if (value < min || value > max) { ++ throw new IllegalArgumentException("argument " + field + " must be [" + min + ", " + max + "]"); ++ } ++ return value; ++ } ++ ++ public static int asArgumentMin(final int value, final String field, final int min) { ++ if (value < min) { ++ throw new IllegalArgumentException("argument " + field + " must be [" + min + ",+inf)"); ++ } ++ return value; ++ } ++ ++ private Checks() { ++ } ++} +diff --git a/src/main/java/net/minecraft/world/level/gameevent/GameEvent.java b/src/main/java/net/minecraft/world/level/gameevent/GameEvent.java +index cc3c56224e64816b885c0131ce2a800a2efe3113..7acd9c71c5c4b487e792b8c36a8e52e10b691e98 100644 +--- a/src/main/java/net/minecraft/world/level/gameevent/GameEvent.java ++++ b/src/main/java/net/minecraft/world/level/gameevent/GameEvent.java +@@ -85,7 +85,7 @@ public record GameEvent(int notificationRadius) { + } + + private static Holder.Reference register(String id, int range) { +- return Registry.registerForHolder(BuiltInRegistries.GAME_EVENT, ResourceLocation.withDefaultNamespace(id), new GameEvent(range)); ++ return io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerForHolderWithListeners(BuiltInRegistries.GAME_EVENT, ResourceLocation.withDefaultNamespace(id), new GameEvent(range)); // Paper - run with listeners + } + + public static record Context(@Nullable Entity sourceEntity, @Nullable BlockState affectedState) { +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java b/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java +index ac9b4328cd55a68664a3f71186bc9a7be7cd9658..ea9fe1f8b1a1685ea975eba0ca418a831006065a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java +@@ -18,10 +18,12 @@ public class CraftGameEvent extends GameEvent implements Handleable handleKey; // Paper + private final net.minecraft.world.level.gameevent.GameEvent handle; + + public CraftGameEvent(NamespacedKey key, net.minecraft.world.level.gameevent.GameEvent handle) { + this.key = key; ++ this.handleKey = net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.GAME_EVENT, org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(key)); // Paper + this.handle = handle; + } + +@@ -30,6 +32,18 @@ public class CraftGameEvent extends GameEvent implements Handleable registries() { + return List.of( ++ arguments(Registries.ENCHANTMENT, (PaperRegistryBuilder.Filler) PaperEnchantmentRegistryEntry.PaperBuilder::new), ++ arguments(Registries.GAME_EVENT, (PaperRegistryBuilder.Filler) PaperGameEventRegistryEntry.PaperBuilder::new) + ); + } + +- @Disabled + @ParameterizedTest + @MethodSource("registries") + void testEquality(final ResourceKey> resourceKey, final PaperRegistryBuilder.Filler filler) {