aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/unapplied/server/0991-Add-registry-entry-and-builders.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/unapplied/server/0991-Add-registry-entry-and-builders.patch')
-rw-r--r--patches/unapplied/server/0991-Add-registry-entry-and-builders.patch575
1 files changed, 575 insertions, 0 deletions
diff --git a/patches/unapplied/server/0991-Add-registry-entry-and-builders.patch b/patches/unapplied/server/0991-Add-registry-entry-and-builders.patch
new file mode 100644
index 0000000000..36bfa68a40
--- /dev/null
+++ b/patches/unapplied/server/0991-Add-registry-entry-and-builders.patch
@@ -0,0 +1,575 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Bjarne Koll <[email protected]>
+Date: Thu, 13 Jun 2024 23:45:32 +0200
+Subject: [PATCH] Add registry entry and builders
+
+Feature patch
+
+diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
+index 86c9f87cdb41c0d1ccc2a61b501f969cfaae47bc..fd024576e70e0c121c1477a0b7777af18159b7c4 100644
+--- a/src/main/java/io/papermc/paper/registry/PaperRegistries.java
++++ b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
+@@ -2,6 +2,9 @@ package io.papermc.paper.registry;
+
+ import com.google.common.base.Preconditions;
+ 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.data.PaperPaintingVariantRegistryEntry;
+ import io.papermc.paper.registry.entry.RegistryEntry;
+ import io.papermc.paper.registry.tag.TagKey;
+ import java.util.Collections;
+@@ -78,7 +81,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.STRUCTURE_TYPE, RegistryKey.STRUCTURE_TYPE, StructureType.class, CraftStructureType::new),
+ entry(Registries.MOB_EFFECT, RegistryKey.MOB_EFFECT, PotionEffectType.class, CraftPotionEffectType::new),
+ entry(Registries.BLOCK, RegistryKey.BLOCK, BlockType.class, CraftBlockType::new),
+@@ -100,10 +103,10 @@ 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(),
+ entry(Registries.BANNER_PATTERN, RegistryKey.BANNER_PATTERN, PatternType.class, CraftPatternType::new).delayed(),
+- entry(Registries.PAINTING_VARIANT, RegistryKey.PAINTING_VARIANT, Art.class, CraftArt::new).delayed(),
++ writable(Registries.PAINTING_VARIANT, RegistryKey.PAINTING_VARIANT, Art.class, CraftArt::new, PaperPaintingVariantRegistryEntry.PaperBuilder::new).delayed(),
+ entry(Registries.INSTRUMENT, RegistryKey.INSTRUMENT, MusicInstrument.class, CraftMusicInstrument::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..1b1c6838452d3001070a5d43cc49e3a09de7153d
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/registry/data/PaperEnchantmentRegistryEntry.java
+@@ -0,0 +1,230 @@
++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.jetbrains.annotations.Range;
++import org.jspecify.annotations.Nullable;
++
++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;
++
++public class PaperEnchantmentRegistryEntry implements EnchantmentRegistryEntry {
++
++ // Top level
++ protected @Nullable Component description;
++
++ // Definition
++ protected @Nullable HolderSet<Item> supportedItems;
++ protected @Nullable HolderSet<Item> primaryItems;
++ protected OptionalInt weight = OptionalInt.empty();
++ protected OptionalInt maxLevel = OptionalInt.empty();
++ protected Enchantment.@Nullable Cost minimumCost;
++ protected Enchantment.@Nullable Cost maximumCost;
++ protected OptionalInt anvilCost = OptionalInt.empty();
++ protected @Nullable List<EquipmentSlotGroup> activeSlots;
++
++ // Exclusive
++ protected HolderSet<Enchantment> exclusiveWith = HolderSet.empty(); // Paper added default to empty.
++
++ // Effects
++ protected DataComponentMap effects;
++
++ protected final Conversions conversions;
++
++ public PaperEnchantmentRegistryEntry(
++ final Conversions conversions,
++ final TypedKey<org.bukkit.enchantments.Enchantment> 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<ItemType> supportedItems() {
++ return PaperRegistrySets.convertToApi(RegistryKey.ITEM, asConfigured(this.supportedItems, "supportedItems"));
++ }
++
++ @Override
++ public @Nullable RegistryKeySet<ItemType> 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.Cost cost = asConfigured(this.minimumCost, "minimumCost");
++ return EnchantmentRegistryEntry.EnchantmentCost.of(cost.base(), cost.perLevelAboveFirst());
++ }
++
++ @Override
++ public EnchantmentCost maximumCost() {
++ final Enchantment.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<org.bukkit.inventory.EquipmentSlotGroup> activeSlots() {
++ return Collections.unmodifiableList(Lists.transform(asConfigured(this.activeSlots, "activeSlots"), CraftEquipmentSlot::getSlot));
++ }
++
++ @Override
++ public RegistryKeySet<org.bukkit.enchantments.Enchantment> exclusiveWith() {
++ return PaperRegistrySets.convertToApi(RegistryKey.ENCHANTMENT, this.exclusiveWith);
++ }
++
++ public static final class PaperBuilder extends PaperEnchantmentRegistryEntry implements EnchantmentRegistryEntry.Builder,
++ PaperRegistryBuilder<Enchantment, org.bukkit.enchantments.Enchantment> {
++
++ public PaperBuilder(final Conversions conversions, final TypedKey<org.bukkit.enchantments.Enchantment> 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<ItemType> supportedItems) {
++ this.supportedItems = PaperRegistrySets.convertToNms(Registries.ITEM, this.conversions.lookup(), asArgument(supportedItems, "supportedItems"));
++ return this;
++ }
++
++ @Override
++ public Builder primaryItems(final @Nullable RegistryKeySet<ItemType> 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<org.bukkit.inventory.EquipmentSlotGroup> activeSlots) {
++ this.activeSlots = Lists.newArrayList(Iterables.transform(asArgument(activeSlots, "activeSlots"), CraftEquipmentSlot::getNMSGroup));
++ return this;
++ }
++
++ @Override
++ public Builder exclusiveWith(final RegistryKeySet<org.bukkit.enchantments.Enchantment> 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..2100e8aca6f7ae7b90545bd3f4d4b800dba65daa
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/registry/data/PaperGameEventRegistryEntry.java
+@@ -0,0 +1,54 @@
++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.jetbrains.annotations.Range;
++import org.jspecify.annotations.Nullable;
++
++import static io.papermc.paper.registry.data.util.Checks.asArgumentMin;
++import static io.papermc.paper.registry.data.util.Checks.asConfigured;
++
++public class PaperGameEventRegistryEntry implements GameEventRegistryEntry {
++
++ protected OptionalInt range = OptionalInt.empty();
++
++ public PaperGameEventRegistryEntry(
++ final Conversions ignoredConversions,
++ final io.papermc.paper.registry.TypedKey<org.bukkit.GameEvent> ignoredKey,
++ final @Nullable GameEvent internal
++ ) {
++ if (internal == null) return;
++
++ this.range = OptionalInt.of(internal.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<GameEvent, org.bukkit.GameEvent> {
++
++ public PaperBuilder(
++ final Conversions conversions,
++ final io.papermc.paper.registry.TypedKey<org.bukkit.GameEvent> key,
++ final @Nullable GameEvent internal
++ ) {
++ super(conversions, key, internal);
++ }
++
++ @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/PaperPaintingVariantRegistryEntry.java b/src/main/java/io/papermc/paper/registry/data/PaperPaintingVariantRegistryEntry.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..68b3d747f759f615a3c942de3f4d7a0fe856cd84
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/registry/data/PaperPaintingVariantRegistryEntry.java
+@@ -0,0 +1,120 @@
++package io.papermc.paper.registry.data;
++
++import io.papermc.paper.adventure.PaperAdventure;
++import io.papermc.paper.registry.PaperRegistryBuilder;
++import io.papermc.paper.registry.TypedKey;
++import io.papermc.paper.registry.data.util.Conversions;
++import java.util.Optional;
++import java.util.OptionalInt;
++import net.kyori.adventure.key.Key;
++import net.minecraft.network.chat.Component;
++import net.minecraft.resources.ResourceLocation;
++import net.minecraft.world.entity.decoration.PaintingVariant;
++import org.bukkit.Art;
++import org.jetbrains.annotations.Range;
++import org.jspecify.annotations.NullMarked;
++import org.jspecify.annotations.Nullable;
++
++import static io.papermc.paper.registry.data.util.Checks.asArgument;
++import static io.papermc.paper.registry.data.util.Checks.asArgumentRange;
++import static io.papermc.paper.registry.data.util.Checks.asConfigured;
++
++@NullMarked
++public class PaperPaintingVariantRegistryEntry implements PaintingVariantRegistryEntry {
++
++ protected OptionalInt width = OptionalInt.empty();
++ protected OptionalInt height = OptionalInt.empty();
++ protected @Nullable Component title;
++ protected @Nullable Component author;
++ protected @Nullable ResourceLocation assetId;
++
++ protected final Conversions conversions;
++
++ public PaperPaintingVariantRegistryEntry(
++ final Conversions conversions,
++ final TypedKey<Art> ignoredKey,
++ final @Nullable PaintingVariant internal
++ ) {
++ this.conversions = conversions;
++ if (internal == null) return;
++
++ this.width = OptionalInt.of(internal.width());
++ this.height = OptionalInt.of(internal.height());
++ this.title = internal.title().orElse(null);
++ this.author = internal.author().orElse(null);
++ this.assetId = internal.assetId();
++ }
++
++ @Override
++ public @Range(from = 1, to = 16) int width() {
++ return asConfigured(this.width, "width");
++ }
++
++ @Override
++ public @Range(from = 1, to = 16) int height() {
++ return asConfigured(this.height, "height");
++ }
++
++ @Override
++ public net.kyori.adventure.text.@Nullable Component title() {
++ return this.title == null ? null : this.conversions.asAdventure(this.title);
++ }
++
++ @Override
++ public net.kyori.adventure.text.@Nullable Component author() {
++ return this.author == null ? null : this.conversions.asAdventure(this.author);
++ }
++
++ @Override
++ public Key assetId() {
++ return PaperAdventure.asAdventure(asConfigured(this.assetId, "assetId"));
++ }
++
++ public static final class PaperBuilder extends PaperPaintingVariantRegistryEntry implements PaintingVariantRegistryEntry.Builder, PaperRegistryBuilder<PaintingVariant, Art> {
++
++ public PaperBuilder(final Conversions conversions, final TypedKey<Art> key, final @Nullable PaintingVariant internal) {
++ super(conversions, key, internal);
++ }
++
++ @Override
++ public Builder width(final @Range(from = 1, to = 16) int width) {
++ this.width = OptionalInt.of(asArgumentRange(width, "width", 1, 16));
++ return this;
++ }
++
++ @Override
++ public Builder height(final @Range(from = 1, to = 16) int height) {
++ this.height = OptionalInt.of(asArgumentRange(height, "height", 1, 16));
++ return this;
++ }
++
++ @Override
++ public Builder title(final net.kyori.adventure.text.@Nullable Component title) {
++ this.title = this.conversions.asVanilla(title);
++ return this;
++ }
++
++ @Override
++ public Builder author(final net.kyori.adventure.text.@Nullable Component author) {
++ this.author = this.conversions.asVanilla(author);
++ return this;
++ }
++
++ @Override
++ public Builder assetId(final Key assetId) {
++ this.assetId = PaperAdventure.asVanilla(asArgument(assetId, "assetId"));
++ return this;
++ }
++
++ @Override
++ public PaintingVariant build() {
++ return new PaintingVariant(
++ this.width(),
++ this.height(),
++ asConfigured(this.assetId, "assetId"),
++ Optional.ofNullable(this.title),
++ Optional.ofNullable(this.author)
++ );
++ }
++ }
++}
+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..cfcf814eb527e6b82a2319f28df43b085ca5c168
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/registry/data/package-info.java
+@@ -0,0 +1,4 @@
++@NullMarked
++package io.papermc.paper.registry.data;
++
++import org.jspecify.annotations.NullMarked;
+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..9d61fad3989540b77734e57935104696e3c2b5b8
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/registry/data/util/Checks.java
+@@ -0,0 +1,45 @@
++package io.papermc.paper.registry.data.util;
++
++import java.util.OptionalInt;
++import org.jspecify.annotations.Nullable;
++
++public final class Checks {
++
++ public static <T> 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> T asArgument(final @Nullable T value, final String field) {
++ if (value == null) {
++ throw new IllegalArgumentException("argument " + field + " 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<GameEvent> 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<net.minecraf
+ }
+
+ private final NamespacedKey key;
++ private final net.minecraft.resources.ResourceKey<net.minecraft.world.level.gameevent.GameEvent> 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<net.minecraf
+ return this.handle;
+ }
+
++ // Paper start
++ @Override
++ public int getRange() {
++ return this.handle.notificationRadius();
++ }
++
++ @Override
++ public int getVibrationLevel() {
++ return net.minecraft.world.level.gameevent.vibrations.VibrationSystem.getGameEventFrequency(this.handleKey);
++ }
++ // Paper end
++
+ @NotNull
+ @Override
+ public NamespacedKey getKey() {