aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/1022-Registry-Modification-API.patch
diff options
context:
space:
mode:
authorSpottedleaf <[email protected]>2024-07-17 10:24:53 -0700
committerSpottedleaf <[email protected]>2024-07-17 10:28:32 -0700
commit00b949f1bbbf444e2b5e7b8de7c9b14fbd2133c6 (patch)
tree82639515bc5e9ae00c1e639e72137ed51e1ac688 /patches/server/1022-Registry-Modification-API.patch
parent967f98aa81da851740aeb429778e46159fd188df (diff)
downloadPaper-00b949f1bbbf444e2b5e7b8de7c9b14fbd2133c6.tar.gz
Paper-00b949f1bbbf444e2b5e7b8de7c9b14fbd2133c6.zip
Remove Moonrise utils to MCUtils, remove duplicated/unused utils
Diffstat (limited to 'patches/server/1022-Registry-Modification-API.patch')
-rw-r--r--patches/server/1022-Registry-Modification-API.patch1421
1 files changed, 0 insertions, 1421 deletions
diff --git a/patches/server/1022-Registry-Modification-API.patch b/patches/server/1022-Registry-Modification-API.patch
deleted file mode 100644
index dc555c750d..0000000000
--- a/patches/server/1022-Registry-Modification-API.patch
+++ /dev/null
@@ -1,1421 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Mon, 27 Feb 2023 18:28:39 -0800
-Subject: [PATCH] Registry Modification API
-
-== AT ==
-public net.minecraft.core.MappedRegistry validateWrite(Lnet/minecraft/resources/ResourceKey;)V
-public net.minecraft.resources.RegistryOps lookupProvider
-public net.minecraft.resources.RegistryOps$HolderLookupAdapter
-
-diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
-index 1e098dc25bd338ff179491ff3382ac56aad9948e..a688af29273ebfbb4f75dd74cd30627fc481c96c 100644
---- a/src/main/java/io/papermc/paper/registry/PaperRegistries.java
-+++ b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
-@@ -2,6 +2,7 @@ package io.papermc.paper.registry;
-
- import io.papermc.paper.adventure.PaperAdventure;
- import io.papermc.paper.registry.entry.RegistryEntry;
-+import io.papermc.paper.registry.tag.TagKey;
- import java.util.Collections;
- import java.util.IdentityHashMap;
- import java.util.List;
-@@ -46,6 +47,7 @@ import org.checkerframework.framework.qual.DefaultQualifier;
-
- import static io.papermc.paper.registry.entry.RegistryEntry.apiOnly;
- import static io.papermc.paper.registry.entry.RegistryEntry.entry;
-+import static io.papermc.paper.registry.entry.RegistryEntry.writable;
-
- @DefaultQualifier(NonNull.class)
- public final class PaperRegistries {
-@@ -128,6 +130,15 @@ public final class PaperRegistries {
- return ResourceKey.create((ResourceKey<? extends Registry<M>>) PaperRegistries.registryToNms(typedKey.registryKey()), PaperAdventure.asVanilla(typedKey.key()));
- }
-
-+ public static <M, T> TagKey<T> fromNms(final net.minecraft.tags.TagKey<M> tagKey) {
-+ return TagKey.create(registryFromNms(tagKey.registry()), CraftNamespacedKey.fromMinecraft(tagKey.location()));
-+ }
-+
-+ @SuppressWarnings({"unchecked", "RedundantCast"})
-+ public static <M, T> net.minecraft.tags.TagKey<M> toNms(final TagKey<T> tagKey) {
-+ return net.minecraft.tags.TagKey.create((ResourceKey<? extends Registry<M>>) registryToNms(tagKey.registryKey()), PaperAdventure.asVanilla(tagKey.key()));
-+ }
-+
- private PaperRegistries() {
- }
- }
-diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java b/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java
-index d591e3a2e19d5358a0d25a5a681368943622d231..f05ebf453406a924da3de6fb250f4793a1b3c612 100644
---- a/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java
-+++ b/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java
-@@ -80,6 +80,14 @@ public class PaperRegistryAccess implements RegistryAccess {
- return possiblyUnwrap(registryHolder.get());
- }
-
-+ public <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> WritableCraftRegistry<M, T, B> getWritableRegistry(final RegistryKey<T> key) {
-+ final Registry<T> registry = this.getRegistry(key);
-+ if (registry instanceof WritableCraftRegistry<?, T, ?>) {
-+ return (WritableCraftRegistry<M, T, B>) registry;
-+ }
-+ throw new IllegalArgumentException(key + " does not point to a writable registry");
-+ }
-+
- private static <T extends Keyed> Registry<T> possiblyUnwrap(final Registry<T> registry) {
- if (registry instanceof final DelayedRegistry<T, ?> delayedRegistry) { // if not coming from legacy, unwrap the delayed registry
- 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..528c6ee1739d92f766f3904acd7fc5734c93388a
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/PaperRegistryBuilder.java
-@@ -0,0 +1,26 @@
-+package io.papermc.paper.registry;
-+
-+import io.papermc.paper.registry.data.util.Conversions;
-+import net.minecraft.resources.RegistryOps;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+
-+public interface PaperRegistryBuilder<M, T> extends RegistryBuilder<T> {
-+
-+ M build();
-+
-+ @FunctionalInterface
-+ interface Filler<M, T, B extends PaperRegistryBuilder<M, T>> {
-+
-+ B fill(Conversions conversions, TypedKey<T> key, @Nullable M nms);
-+
-+ default Factory<M, T, B> asFactory() {
-+ return (lookup, key) -> this.fill(lookup, key, null);
-+ }
-+ }
-+
-+ @FunctionalInterface
-+ interface Factory<M, T, B extends PaperRegistryBuilder<M, T>> {
-+
-+ B create(Conversions conversions, TypedKey<T> key);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java b/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..a7f2b264d4f37f5293ae72195b4c78faf35351c9
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java
-@@ -0,0 +1,183 @@
-+package io.papermc.paper.registry;
-+
-+import com.google.common.base.Preconditions;
-+import com.mojang.serialization.Lifecycle;
-+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
-+import io.papermc.paper.plugin.entrypoint.Entrypoint;
-+import io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler;
-+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner;
-+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import io.papermc.paper.registry.entry.RegistryEntry;
-+import io.papermc.paper.registry.entry.RegistryEntryInfo;
-+import io.papermc.paper.registry.event.RegistryEntryAddEventImpl;
-+import io.papermc.paper.registry.event.RegistryEventMap;
-+import io.papermc.paper.registry.event.RegistryEventProvider;
-+import io.papermc.paper.registry.event.RegistryFreezeEvent;
-+import io.papermc.paper.registry.event.RegistryFreezeEventImpl;
-+import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
-+import io.papermc.paper.registry.event.type.RegistryEntryAddEventTypeImpl;
-+import io.papermc.paper.registry.event.type.RegistryLifecycleEventType;
-+import java.util.Optional;
-+import net.kyori.adventure.key.Key;
-+import net.minecraft.core.Holder;
-+import net.minecraft.core.MappedRegistry;
-+import net.minecraft.core.RegistrationInfo;
-+import net.minecraft.core.Registry;
-+import net.minecraft.core.WritableRegistry;
-+import net.minecraft.core.registries.BuiltInRegistries;
-+import net.minecraft.resources.ResourceKey;
-+import net.minecraft.resources.ResourceLocation;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.intellij.lang.annotations.Subst;
-+
-+public final class PaperRegistryListenerManager {
-+
-+ public static final PaperRegistryListenerManager INSTANCE = new PaperRegistryListenerManager();
-+
-+ public final RegistryEventMap valueAddHooks = new RegistryEventMap("value add");
-+ public final RegistryEventMap freezeHooks = new RegistryEventMap("freeze");
-+
-+ private PaperRegistryListenerManager() {
-+ }
-+
-+ /**
-+ * For {@link Registry#register(Registry, String, Object)}
-+ */
-+ public <M> M registerWithListeners(final Registry<M> registry, final String id, final M nms) {
-+ return this.registerWithListeners(registry, ResourceLocation.withDefaultNamespace(id), nms);
-+ }
-+
-+ /**
-+ * For {@link Registry#register(Registry, ResourceLocation, Object)}
-+ */
-+ public <M> M registerWithListeners(final Registry<M> registry, final ResourceLocation loc, final M nms) {
-+ return this.registerWithListeners(registry, ResourceKey.create(registry.key(), loc), nms);
-+ }
-+
-+ /**
-+ * For {@link Registry#register(Registry, ResourceKey, Object)}
-+ */
-+ public <M> M registerWithListeners(final Registry<M> registry, final ResourceKey<M> key, final M nms) {
-+ return this.registerWithListeners(registry, key, nms, RegistrationInfo.BUILT_IN, PaperRegistryListenerManager::registerWithInstance, BuiltInRegistries.BUILT_IN_CONVERSIONS);
-+ }
-+
-+ /**
-+ * For {@link Registry#registerForHolder(Registry, ResourceLocation, Object)}
-+ */
-+ public <M> Holder.Reference<M> registerForHolderWithListeners(final Registry<M> registry, final ResourceLocation loc, final M nms) {
-+ return this.registerForHolderWithListeners(registry, ResourceKey.create(registry.key(), loc), nms);
-+ }
-+
-+ /**
-+ * For {@link Registry#registerForHolder(Registry, ResourceKey, Object)}
-+ */
-+ public <M> Holder.Reference<M> registerForHolderWithListeners(final Registry<M> registry, final ResourceKey<M> key, final M nms) {
-+ return this.registerWithListeners(registry, key, nms, RegistrationInfo.BUILT_IN, WritableRegistry::register, BuiltInRegistries.BUILT_IN_CONVERSIONS);
-+ }
-+
-+ public <M> void registerWithListeners(
-+ final Registry<M> registry,
-+ final ResourceKey<M> key,
-+ final M nms,
-+ final RegistrationInfo registrationInfo,
-+ final Conversions conversions
-+ ) {
-+ this.registerWithListeners(registry, key, nms, registrationInfo, WritableRegistry::register, conversions);
-+ }
-+
-+ // TODO remove Keyed
-+ public <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>, R> R registerWithListeners(
-+ final Registry<M> registry,
-+ final ResourceKey<M> key,
-+ final M nms,
-+ final RegistrationInfo registrationInfo,
-+ final RegisterMethod<M, R> registerMethod,
-+ final Conversions conversions
-+ ) {
-+ Preconditions.checkState(LaunchEntryPointHandler.INSTANCE.hasEntered(Entrypoint.BOOTSTRAPPER), registry.key() + " tried to run modification listeners before bootstrappers have been called"); // verify that bootstrappers have been called
-+ final @Nullable RegistryEntryInfo<M, T> entry = PaperRegistries.getEntry(registry.key());
-+ if (!RegistryEntry.Modifiable.isModifiable(entry) || !this.valueAddHooks.hasHooks(entry.apiKey())) {
-+ return registerMethod.register((WritableRegistry<M>) registry, key, nms, registrationInfo);
-+ }
-+ final RegistryEntry.Modifiable<M, T, B> modifiableEntry = RegistryEntry.Modifiable.asModifiable(entry);
-+ @SuppressWarnings("PatternValidation") final TypedKey<T> typedKey = TypedKey.create(entry.apiKey(), Key.key(key.location().getNamespace(), key.location().getPath()));
-+ final B builder = modifiableEntry.fillBuilder(conversions, typedKey, nms);
-+ return this.registerWithListeners(registry, modifiableEntry, key, nms, builder, registrationInfo, registerMethod, conversions);
-+ }
-+
-+ <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>> void registerWithListeners( // TODO remove Keyed
-+ final WritableRegistry<M> registry,
-+ final RegistryEntryInfo<M, T> entry,
-+ final ResourceKey<M> key,
-+ final B builder,
-+ final RegistrationInfo registrationInfo,
-+ final Conversions conversions
-+ ) {
-+ if (!RegistryEntry.Modifiable.isModifiable(entry) || !this.valueAddHooks.hasHooks(entry.apiKey())) {
-+ registry.register(key, builder.build(), registrationInfo);
-+ return;
-+ }
-+ this.registerWithListeners(registry, RegistryEntry.Modifiable.asModifiable(entry), key, null, builder, registrationInfo, WritableRegistry::register, conversions);
-+ }
-+
-+ public <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>, R> R registerWithListeners( // TODO remove Keyed
-+ final Registry<M> registry,
-+ final RegistryEntry.Modifiable<M, T, B> entry,
-+ final ResourceKey<M> key,
-+ final @Nullable M oldNms,
-+ final B builder,
-+ RegistrationInfo registrationInfo,
-+ final RegisterMethod<M, R> registerMethod,
-+ final Conversions conversions
-+ ) {
-+ @Subst("namespace:key") final ResourceLocation beingAdded = key.location();
-+ @SuppressWarnings("PatternValidation") final TypedKey<T> typedKey = TypedKey.create(entry.apiKey(), Key.key(beingAdded.getNamespace(), beingAdded.getPath()));
-+ final RegistryEntryAddEventImpl<T, B> event = entry.createEntryAddEvent(typedKey, builder, conversions);
-+ LifecycleEventRunner.INSTANCE.callEvent(this.valueAddHooks.getHook(entry.apiKey()), event);
-+ if (oldNms != null) {
-+ ((MappedRegistry<M>) registry).clearIntrusiveHolder(oldNms);
-+ }
-+ final M newNms = event.builder().build();
-+ if (oldNms != null && !newNms.equals(oldNms)) {
-+ registrationInfo = new RegistrationInfo(Optional.empty(), Lifecycle.experimental());
-+ }
-+ return registerMethod.register((WritableRegistry<M>) registry, key, newNms, registrationInfo);
-+ }
-+
-+ private static <M> M registerWithInstance(final WritableRegistry<M> writableRegistry, final ResourceKey<M> key, final M value, final RegistrationInfo registrationInfo) {
-+ writableRegistry.register(key, value, registrationInfo);
-+ return value;
-+ }
-+
-+ @FunctionalInterface
-+ public interface RegisterMethod<M, R> {
-+
-+ R register(WritableRegistry<M> writableRegistry, ResourceKey<M> key, M value, RegistrationInfo registrationInfo);
-+ }
-+
-+ public <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>> void runFreezeListeners(final ResourceKey<? extends Registry<M>> resourceKey, final Conversions conversions) {
-+ final @Nullable RegistryEntryInfo<M, T> entry = PaperRegistries.getEntry(resourceKey);
-+ if (!RegistryEntry.Addable.isAddable(entry) || !this.freezeHooks.hasHooks(entry.apiKey())) {
-+ return;
-+ }
-+ final RegistryEntry.Addable<M, T, B> writableEntry = RegistryEntry.Addable.asAddable(entry);
-+ final WritableCraftRegistry<M, T, B> writableRegistry = PaperRegistryAccess.instance().getWritableRegistry(entry.apiKey());
-+ final RegistryFreezeEventImpl<T, B> event = writableEntry.createFreezeEvent(writableRegistry, conversions);
-+ LifecycleEventRunner.INSTANCE.callEvent(this.freezeHooks.getHook(entry.apiKey()), event);
-+ }
-+
-+ public <T, B extends RegistryBuilder<T>> RegistryEntryAddEventType<T, B> getRegistryValueAddEventType(final RegistryEventProvider<T, B> type) {
-+ if (!RegistryEntry.Modifiable.isModifiable(PaperRegistries.getEntry(type.registryKey()))) {
-+ throw new IllegalArgumentException(type.registryKey() + " does not support RegistryEntryAddEvent");
-+ }
-+ return this.valueAddHooks.getOrCreate(type, RegistryEntryAddEventTypeImpl::new);
-+ }
-+
-+ public <T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryFreezeEvent<T, B>> getRegistryFreezeEventType(final RegistryEventProvider<T, B> type) {
-+ if (!RegistryEntry.Addable.isAddable(PaperRegistries.getEntry(type.registryKey()))) {
-+ throw new IllegalArgumentException(type.registryKey() + " does not support RegistryFreezeEvent");
-+ }
-+ return this.freezeHooks.getOrCreate(type, RegistryLifecycleEventType::new);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java b/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..78317c7ab42a666f19634593a8f3b696700764c8
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java
-@@ -0,0 +1,92 @@
-+package io.papermc.paper.registry;
-+
-+import com.mojang.serialization.Lifecycle;
-+import io.papermc.paper.adventure.PaperAdventure;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import io.papermc.paper.registry.entry.RegistryEntry;
-+import io.papermc.paper.registry.event.WritableRegistry;
-+import java.util.Optional;
-+import java.util.function.BiFunction;
-+import java.util.function.Consumer;
-+import net.minecraft.core.MappedRegistry;
-+import net.minecraft.core.RegistrationInfo;
-+import net.minecraft.resources.ResourceKey;
-+import org.bukkit.Keyed;
-+import org.bukkit.NamespacedKey;
-+import org.bukkit.craftbukkit.CraftRegistry;
-+import org.bukkit.craftbukkit.util.ApiVersion;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+
-+public class WritableCraftRegistry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends CraftRegistry<T, M> {
-+
-+ private static final RegistrationInfo FROM_PLUGIN = new RegistrationInfo(Optional.empty(), Lifecycle.experimental());
-+
-+ private final RegistryEntry.BuilderHolder<M, T, B> entry;
-+ private final MappedRegistry<M> registry;
-+ private final PaperRegistryBuilder.Factory<M, T, ? extends B> builderFactory;
-+ private final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit;
-+
-+ public WritableCraftRegistry(
-+ final RegistryEntry.BuilderHolder<M, T, B> entry,
-+ final Class<?> classToPreload,
-+ final MappedRegistry<M> registry,
-+ final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater,
-+ final PaperRegistryBuilder.Factory<M, T, ? extends B> builderFactory,
-+ final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit
-+ ) {
-+ super(classToPreload, registry, null, serializationUpdater);
-+ this.entry = entry;
-+ this.registry = registry;
-+ this.builderFactory = builderFactory;
-+ this.minecraftToBukkit = minecraftToBukkit;
-+ }
-+
-+ public void register(final TypedKey<T> key, final Consumer<? super B> value, final Conversions conversions) {
-+ final ResourceKey<M> resourceKey = ResourceKey.create(this.registry.key(), PaperAdventure.asVanilla(key.key()));
-+ this.registry.validateWrite(resourceKey);
-+ final B builder = this.newBuilder(conversions, key);
-+ value.accept(builder);
-+ PaperRegistryListenerManager.INSTANCE.registerWithListeners(
-+ this.registry,
-+ RegistryEntry.Modifiable.asModifiable(this.entry),
-+ resourceKey,
-+ builder,
-+ FROM_PLUGIN,
-+ conversions
-+ );
-+ }
-+
-+ @Override
-+ public final @Nullable T createBukkit(final NamespacedKey namespacedKey, final @Nullable M minecraft) {
-+ if (minecraft == null) {
-+ return null;
-+ }
-+ return this.minecraftToBukkit(namespacedKey, minecraft);
-+ }
-+
-+ public WritableRegistry<T, B> createApiWritableRegistry(final Conversions conversions) {
-+ return new ApiWritableRegistry(conversions);
-+ }
-+
-+ public T minecraftToBukkit(final NamespacedKey namespacedKey, final M minecraft) {
-+ return this.minecraftToBukkit.apply(namespacedKey, minecraft);
-+ }
-+
-+ protected B newBuilder(final Conversions conversions, final TypedKey<T> key) {
-+ return this.builderFactory.create(conversions, key);
-+ }
-+
-+ public class ApiWritableRegistry implements WritableRegistry<T, B> {
-+
-+ private final Conversions conversions;
-+
-+ public ApiWritableRegistry(final Conversions conversions) {
-+ this.conversions = conversions;
-+ }
-+
-+ @Override
-+ public void register(final TypedKey<T> key, final Consumer<? super B> value) {
-+ WritableCraftRegistry.this.register(key, value, this.conversions);
-+ }
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/data/util/Conversions.java b/src/main/java/io/papermc/paper/registry/data/util/Conversions.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..eda5cc7d45ef59ccc1c9c7e027c1f044f1dcc86b
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/data/util/Conversions.java
-@@ -0,0 +1,36 @@
-+package io.papermc.paper.registry.data.util;
-+
-+import com.mojang.serialization.JavaOps;
-+import io.papermc.paper.adventure.WrapperAwareSerializer;
-+import net.kyori.adventure.text.Component;
-+import net.minecraft.resources.RegistryOps;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-+import org.jetbrains.annotations.Contract;
-+
-+@DefaultQualifier(NonNull.class)
-+public class Conversions {
-+
-+ private final RegistryOps.RegistryInfoLookup lookup;
-+ private final WrapperAwareSerializer serializer;
-+
-+ public Conversions(final RegistryOps.RegistryInfoLookup lookup) {
-+ this.lookup = lookup;
-+ this.serializer = new WrapperAwareSerializer(() -> RegistryOps.create(JavaOps.INSTANCE, lookup));
-+ }
-+
-+ public RegistryOps.RegistryInfoLookup lookup() {
-+ return this.lookup;
-+ }
-+
-+ @Contract("null -> null; !null -> !null")
-+ public net.minecraft.network.chat.@Nullable Component asVanilla(final @Nullable Component adventure) {
-+ if (adventure == null) return null;
-+ return this.serializer.serialize(adventure);
-+ }
-+
-+ public Component asAdventure(final net.minecraft.network.chat.@Nullable Component vanilla) {
-+ return vanilla == null ? Component.empty() : this.serializer.deserialize(vanilla);
-+ }
-+}
-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..aeec9b3ae2911f041d000b3db72f37974020ba60
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/entry/AddableRegistryEntry.java
-@@ -0,0 +1,44 @@
-+package io.papermc.paper.registry.entry;
-+
-+import io.papermc.paper.registry.PaperRegistryBuilder;
-+import io.papermc.paper.registry.RegistryHolder;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.TypedKey;
-+import io.papermc.paper.registry.WritableCraftRegistry;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import java.util.function.BiFunction;
-+import net.minecraft.core.MappedRegistry;
-+import net.minecraft.core.Registry;
-+import net.minecraft.resources.ResourceKey;
-+import org.bukkit.Keyed;
-+import org.bukkit.NamespacedKey;
-+
-+public class AddableRegistryEntry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends CraftRegistryEntry<M, T> implements RegistryEntry.Addable<M, T, B> {
-+
-+ private final PaperRegistryBuilder.Filler<M, T, B> builderFiller;
-+
-+ protected AddableRegistryEntry(
-+ final ResourceKey<? extends Registry<M>> mcKey,
-+ final RegistryKey<T> apiKey,
-+ final Class<?> classToPreload,
-+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
-+ final PaperRegistryBuilder.Filler<M, T, B> builderFiller
-+ ) {
-+ super(mcKey, apiKey, classToPreload, minecraftToBukkit);
-+ this.builderFiller = builderFiller;
-+ }
-+
-+ private WritableCraftRegistry<M, T, B> createRegistry(final Registry<M> registry) {
-+ return new WritableCraftRegistry<>(this, this.classToPreload, (MappedRegistry<M>) registry, this.updater, this.builderFiller.asFactory(), this.minecraftToBukkit);
-+ }
-+
-+ @Override
-+ public RegistryHolder<T> createRegistryHolder(final Registry<M> nmsRegistry) {
-+ return new RegistryHolder.Memoized<>(() -> this.createRegistry(nmsRegistry));
-+ }
-+
-+ @Override
-+ public B fillBuilder(final Conversions conversions, final TypedKey<T> 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..515a995e3862f8e7cb93d149315ea32e04a08716
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/entry/ModifiableRegistryEntry.java
-@@ -0,0 +1,32 @@
-+package io.papermc.paper.registry.entry;
-+
-+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.Conversions;
-+import java.util.function.BiFunction;
-+import net.minecraft.core.Registry;
-+import net.minecraft.resources.ResourceKey;
-+import org.bukkit.Keyed;
-+import org.bukkit.NamespacedKey;
-+
-+public class ModifiableRegistryEntry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends CraftRegistryEntry<M, T> implements RegistryEntry.Modifiable<M, T, B> {
-+
-+ protected final PaperRegistryBuilder.Filler<M, T, B> builderFiller;
-+
-+ protected ModifiableRegistryEntry(
-+ final ResourceKey<? extends Registry<M>> mcKey,
-+ final RegistryKey<T> apiKey,
-+ final Class<?> toPreload,
-+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
-+ final PaperRegistryBuilder.Filler<M, T, B> builderFiller
-+ ) {
-+ super(mcKey, apiKey, toPreload, minecraftToBukkit);
-+ this.builderFiller = builderFiller;
-+ }
-+
-+ @Override
-+ public B fillBuilder(final Conversions conversions, final TypedKey<T> 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..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,7 +1,13 @@
- package io.papermc.paper.registry.entry;
-
-+import io.papermc.paper.registry.PaperRegistryBuilder;
- import io.papermc.paper.registry.RegistryHolder;
- import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.TypedKey;
-+import io.papermc.paper.registry.WritableCraftRegistry;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import io.papermc.paper.registry.event.RegistryEntryAddEventImpl;
-+import io.papermc.paper.registry.event.RegistryFreezeEventImpl;
- import io.papermc.paper.registry.legacy.DelayedRegistryEntry;
- import java.util.function.BiFunction;
- import java.util.function.Supplier;
-@@ -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;
-+import org.checkerframework.checker.nullness.qual.Nullable;
- import org.checkerframework.framework.qual.DefaultQualifier;
-
- @DefaultQualifier(NonNull.class)
-@@ -32,6 +39,65 @@ public interface RegistryEntry<M, B extends Keyed> extends RegistryEntryInfo<M,
- return new DelayedRegistryEntry<>(this);
- }
-
-+ interface BuilderHolder<M, T, B extends PaperRegistryBuilder<M, T>> extends RegistryEntryInfo<M, T> {
-+
-+ B fillBuilder(Conversions conversions, TypedKey<T> key, M nms);
-+ }
-+
-+ /**
-+ * Can mutate values being added to the registry
-+ */
-+ interface Modifiable<M, T, B extends PaperRegistryBuilder<M, T>> extends BuilderHolder<M, T, B> {
-+
-+ static boolean isModifiable(final @Nullable RegistryEntryInfo<?, ?> entry) {
-+ return entry instanceof RegistryEntry.Modifiable<?, ?, ?> || (entry instanceof final DelayedRegistryEntry<?, ?> delayed && delayed.delegate() instanceof RegistryEntry.Modifiable<?, ?, ?>);
-+ }
-+
-+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> Modifiable<M, T, B> asModifiable(final RegistryEntryInfo<M, T> entry) { // TODO remove Keyed
-+ return (Modifiable<M, T, B>) possiblyUnwrap(entry);
-+ }
-+
-+ default RegistryEntryAddEventImpl<T, B> createEntryAddEvent(final TypedKey<T> key, final B initialBuilder, final Conversions conversions) {
-+ return new RegistryEntryAddEventImpl<>(key, initialBuilder, this.apiKey(), conversions);
-+ }
-+ }
-+
-+ /**
-+ * Can only add new values to the registry, not modify any values.
-+ */
-+ interface Addable<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends BuilderHolder<M, T, B> { // TODO remove Keyed
-+
-+ default RegistryFreezeEventImpl<T, B> createFreezeEvent(final WritableCraftRegistry<M, T, B> writableRegistry, final Conversions conversions) {
-+ return new RegistryFreezeEventImpl<>(this.apiKey(), writableRegistry.createApiWritableRegistry(conversions), conversions);
-+ }
-+
-+ static boolean isAddable(final @Nullable RegistryEntryInfo<?, ?> entry) {
-+ return entry instanceof RegistryEntry.Addable<?, ?, ?> || (entry instanceof final DelayedRegistryEntry<?, ?> delayed && delayed.delegate() instanceof RegistryEntry.Addable<?, ?, ?>);
-+ }
-+
-+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> Addable<M, T, B> asAddable(final RegistryEntryInfo<M, T> entry) {
-+ return (Addable<M, T, B>) possiblyUnwrap(entry);
-+ }
-+ }
-+
-+ /**
-+ * Can mutate values and add new values.
-+ */
-+ interface Writable<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends Modifiable<M, T, B>, Addable<M, T, B> { // TODO remove Keyed
-+
-+ static boolean isWritable(final @Nullable RegistryEntryInfo<?, ?> entry) {
-+ return entry instanceof RegistryEntry.Writable<?, ?, ?> || (entry instanceof final DelayedRegistryEntry<?, ?> delayed && delayed.delegate() instanceof RegistryEntry.Writable<?, ?, ?>);
-+ }
-+
-+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> Writable<M, T, B> asWritable(final RegistryEntryInfo<M, T> entry) { // TODO remove Keyed
-+ return (Writable<M, T, B>) possiblyUnwrap(entry);
-+ }
-+ }
-+
-+ private static <M, B extends Keyed> RegistryEntryInfo<M, B> possiblyUnwrap(final RegistryEntryInfo<M, B> entry) {
-+ return entry instanceof final DelayedRegistryEntry<M, B> delayed ? delayed.delegate() : entry;
-+ }
-+
- static <M, B extends Keyed> RegistryEntry<M, B> entry(
- final ResourceKey<? extends Registry<M>> mcKey,
- final RegistryKey<B> apiKey,
-@@ -48,4 +114,24 @@ public interface RegistryEntry<M, B extends Keyed> extends RegistryEntryInfo<M,
- ) {
- return new ApiRegistryEntry<>(mcKey, apiKey, apiRegistrySupplier);
- }
-+
-+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntry<M, T> modifiable(
-+ final ResourceKey<? extends Registry<M>> mcKey,
-+ final RegistryKey<T> apiKey,
-+ final Class<?> toPreload,
-+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
-+ final PaperRegistryBuilder.Filler<M, T, B> filler
-+ ) {
-+ return new ModifiableRegistryEntry<>(mcKey, apiKey, toPreload, minecraftToBukkit, filler);
-+ }
-+
-+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntry<M, T> writable(
-+ final ResourceKey<? extends Registry<M>> mcKey,
-+ final RegistryKey<T> apiKey,
-+ final Class<?> toPreload,
-+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
-+ final PaperRegistryBuilder.Filler<M, T, B> filler
-+ ) {
-+ return new WritableRegistryEntry<>(mcKey, apiKey, toPreload, minecraftToBukkit, filler);
-+ }
- }
-diff --git a/src/main/java/io/papermc/paper/registry/entry/WritableRegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/WritableRegistryEntry.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..562accce731630327d116afd1c9d559df7e386bd
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/entry/WritableRegistryEntry.java
-@@ -0,0 +1,22 @@
-+package io.papermc.paper.registry.entry;
-+
-+import io.papermc.paper.registry.PaperRegistryBuilder;
-+import io.papermc.paper.registry.RegistryKey;
-+import java.util.function.BiFunction;
-+import net.minecraft.core.Registry;
-+import net.minecraft.resources.ResourceKey;
-+import org.bukkit.Keyed;
-+import org.bukkit.NamespacedKey;
-+
-+public class WritableRegistryEntry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends AddableRegistryEntry<M, T, B> implements RegistryEntry.Writable<M, T, B> { // TODO remove Keyed
-+
-+ protected WritableRegistryEntry(
-+ final ResourceKey<? extends Registry<M>> mcKey,
-+ final RegistryKey<T> apiKey,
-+ final Class<?> classToPreload,
-+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
-+ final PaperRegistryBuilder.Filler<M, T, B> builderFiller
-+ ) {
-+ super(mcKey, apiKey, classToPreload, minecraftToBukkit, builderFiller);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEventImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEventImpl.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..cc9c8fd313f530777af80ad79e03903f3f8f9829
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEventImpl.java
-@@ -0,0 +1,30 @@
-+package io.papermc.paper.registry.event;
-+
-+import io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEvent;
-+import io.papermc.paper.registry.PaperRegistries;
-+import io.papermc.paper.registry.RegistryBuilder;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.TypedKey;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import io.papermc.paper.registry.set.NamedRegistryKeySetImpl;
-+import io.papermc.paper.registry.tag.Tag;
-+import io.papermc.paper.registry.tag.TagKey;
-+import net.minecraft.core.HolderSet;
-+import net.minecraft.resources.RegistryOps;
-+import org.bukkit.Keyed;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+
-+public record RegistryEntryAddEventImpl<T, B extends RegistryBuilder<T>>(
-+ TypedKey<T> key,
-+ B builder,
-+ RegistryKey<T> registryKey,
-+ Conversions conversions
-+) implements RegistryEntryAddEvent<T, B>, PaperLifecycleEvent {
-+
-+ @Override
-+ public @NonNull <V extends Keyed> Tag<V> getOrCreateTag(final TagKey<V> tagKey) {
-+ final RegistryOps.RegistryInfo<Object> registryInfo = this.conversions.lookup().lookup(PaperRegistries.registryToNms(tagKey.registryKey())).orElseThrow();
-+ final HolderSet.Named<?> tagSet = registryInfo.getter().getOrThrow(PaperRegistries.toNms(tagKey));
-+ return new NamedRegistryKeySetImpl<>(tagKey, tagSet);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEventMap.java b/src/main/java/io/papermc/paper/registry/event/RegistryEventMap.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..f5ea23173dcbe491742c3dd051c147ef397307a0
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventMap.java
-@@ -0,0 +1,44 @@
-+package io.papermc.paper.registry.event;
-+
-+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
-+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner;
-+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
-+import io.papermc.paper.registry.RegistryBuilder;
-+import io.papermc.paper.registry.RegistryKey;
-+import java.util.HashMap;
-+import java.util.Map;
-+import java.util.Objects;
-+import java.util.function.BiFunction;
-+
-+public final class RegistryEventMap {
-+
-+ private final Map<RegistryKey<?>, LifecycleEventType<BootstrapContext, ? extends RegistryEvent<?>, ?>> hooks = new HashMap<>();
-+ private final String name;
-+
-+ public RegistryEventMap(final String name) {
-+ this.name = name;
-+ }
-+
-+ @SuppressWarnings("unchecked")
-+ public <T, B extends RegistryBuilder<T>, E extends RegistryEvent<T>, ET extends LifecycleEventType<BootstrapContext, E, ?>> ET getOrCreate(final RegistryEventProvider<T, B> type, final BiFunction<? super RegistryEventProvider<T, B>, ? super String, ET> eventTypeCreator) {
-+ final ET registerHook;
-+ if (this.hooks.containsKey(type.registryKey())) {
-+ registerHook = (ET) this.hooks.get(type.registryKey());
-+ } else {
-+ registerHook = eventTypeCreator.apply(type, this.name);
-+ LifecycleEventRunner.INSTANCE.addEventType(registerHook);
-+ this.hooks.put(type.registryKey(), registerHook);
-+ }
-+ return registerHook;
-+ }
-+
-+ @SuppressWarnings("unchecked")
-+ public <T, E extends RegistryEvent<T>> LifecycleEventType<BootstrapContext, E, ?> getHook(final RegistryKey<T> registryKey) {
-+ return (LifecycleEventType<BootstrapContext, E, ?>) Objects.requireNonNull(this.hooks.get(registryKey), "No hook for " + registryKey);
-+ }
-+
-+ public boolean hasHooks(final RegistryKey<?> registryKey) {
-+ return this.hooks.containsKey(registryKey);
-+ }
-+
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProviderImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProviderImpl.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..34c842ffa355e3c8001dd7b8551bcb49229a6391
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProviderImpl.java
-@@ -0,0 +1,24 @@
-+package io.papermc.paper.registry.event;
-+
-+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
-+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
-+import io.papermc.paper.registry.PaperRegistryListenerManager;
-+import io.papermc.paper.registry.RegistryBuilder;
-+import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
-+
-+public class RegistryEventTypeProviderImpl implements RegistryEventTypeProvider {
-+
-+ public static RegistryEventTypeProviderImpl instance() {
-+ return (RegistryEventTypeProviderImpl) RegistryEventTypeProvider.provider();
-+ }
-+
-+ @Override
-+ public <T, B extends RegistryBuilder<T>> RegistryEntryAddEventType<T, B> registryEntryAdd(final RegistryEventProvider<T, B> type) {
-+ return PaperRegistryListenerManager.INSTANCE.getRegistryValueAddEventType(type);
-+ }
-+
-+ @Override
-+ public <T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryFreezeEvent<T, B>> registryFreeze(final RegistryEventProvider<T, B> type) {
-+ return PaperRegistryListenerManager.INSTANCE.getRegistryFreezeEventType(type);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEventImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEventImpl.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..63957d2509e68ccc6eb2fd9ecaa35bfad7b71b81
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEventImpl.java
-@@ -0,0 +1,28 @@
-+package io.papermc.paper.registry.event;
-+
-+import io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEvent;
-+import io.papermc.paper.registry.PaperRegistries;
-+import io.papermc.paper.registry.RegistryBuilder;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import io.papermc.paper.registry.set.NamedRegistryKeySetImpl;
-+import io.papermc.paper.registry.tag.Tag;
-+import io.papermc.paper.registry.tag.TagKey;
-+import net.minecraft.core.HolderSet;
-+import net.minecraft.resources.RegistryOps;
-+import org.bukkit.Keyed;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+
-+public record RegistryFreezeEventImpl<T, B extends RegistryBuilder<T>>(
-+ RegistryKey<T> registryKey,
-+ WritableRegistry<T, B> registry,
-+ Conversions conversions
-+) implements RegistryFreezeEvent<T, B>, PaperLifecycleEvent {
-+
-+ @Override
-+ public @NonNull <V extends Keyed> Tag<V> getOrCreateTag(final TagKey<V> tagKey) {
-+ final RegistryOps.RegistryInfo<Object> registryInfo = this.conversions.lookup().lookup(PaperRegistries.registryToNms(tagKey.registryKey())).orElseThrow();
-+ final HolderSet.Named<?> tagSet = registryInfo.getter().getOrThrow(PaperRegistries.toNms(tagKey));
-+ return new NamedRegistryKeySetImpl<>(tagKey, tagSet);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/package-info.java b/src/main/java/io/papermc/paper/registry/event/package-info.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..14d2d9766b8dee763f220c397aba3ad432d02aaa
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/package-info.java
-@@ -0,0 +1,5 @@
-+@DefaultQualifier(NonNull.class)
-+package io.papermc.paper.registry.event;
-+
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-diff --git a/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventTypeImpl.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventTypeImpl.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..5d709ed04e1078b631f5b9c74ca35f042251e14f
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventTypeImpl.java
-@@ -0,0 +1,32 @@
-+package io.papermc.paper.registry.event.type;
-+
-+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
-+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
-+import io.papermc.paper.plugin.lifecycle.event.types.PrioritizableLifecycleEventType;
-+import io.papermc.paper.registry.RegistryBuilder;
-+import io.papermc.paper.registry.event.RegistryEntryAddEvent;
-+import io.papermc.paper.registry.event.RegistryEventProvider;
-+import java.util.function.Consumer;
-+import java.util.function.Predicate;
-+
-+public class RegistryEntryAddEventTypeImpl<T, B extends RegistryBuilder<T>> extends PrioritizableLifecycleEventType<BootstrapContext, RegistryEntryAddEvent<T, B>, RegistryEntryAddConfiguration<T>> implements RegistryEntryAddEventType<T, B> {
-+
-+ public RegistryEntryAddEventTypeImpl(final RegistryEventProvider<T, B> type, final String eventName) {
-+ super(type.registryKey() + " / " + eventName, BootstrapContext.class);
-+ }
-+
-+ @Override
-+ public RegistryEntryAddConfiguration<T> newHandler(final LifecycleEventHandler<? super RegistryEntryAddEvent<T, B>> handler) {
-+ return new RegistryEntryAddHandlerConfiguration<>(handler, this);
-+ }
-+
-+ @Override
-+ public void forEachHandler(final RegistryEntryAddEvent<T, B> event, final Consumer<RegisteredHandler<BootstrapContext, RegistryEntryAddEvent<T, B>>> consumer, final Predicate<RegisteredHandler<BootstrapContext, RegistryEntryAddEvent<T, B>>> predicate) {
-+ super.forEachHandler(event, consumer, predicate.and(handler -> this.matchesTarget(event, handler)));
-+ }
-+
-+ private boolean matchesTarget(final RegistryEntryAddEvent<T, B> event, final RegisteredHandler<BootstrapContext, RegistryEntryAddEvent<T, B>> handler) {
-+ final RegistryEntryAddHandlerConfiguration<T, B> config = (RegistryEntryAddHandlerConfiguration<T, B>) handler.config();
-+ return config.filter() == null || config.filter().test(event.key());
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddHandlerConfiguration.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddHandlerConfiguration.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..548f5bf979e88708e98d04dfe22ccaa300c91ddd
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddHandlerConfiguration.java
-@@ -0,0 +1,42 @@
-+package io.papermc.paper.registry.event.type;
-+
-+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
-+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
-+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfigurationImpl;
-+import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
-+import io.papermc.paper.registry.RegistryBuilder;
-+import io.papermc.paper.registry.TypedKey;
-+import io.papermc.paper.registry.event.RegistryEntryAddEvent;
-+import java.util.function.Predicate;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.jetbrains.annotations.Contract;
-+
-+public class RegistryEntryAddHandlerConfiguration<T, B extends RegistryBuilder<T>> extends PrioritizedLifecycleEventHandlerConfigurationImpl<BootstrapContext, RegistryEntryAddEvent<T, B>> implements RegistryEntryAddConfiguration<T> {
-+
-+ private @Nullable Predicate<TypedKey<T>> filter;
-+
-+ public RegistryEntryAddHandlerConfiguration(final LifecycleEventHandler<? super RegistryEntryAddEvent<T, B>> handler, final AbstractLifecycleEventType<BootstrapContext, RegistryEntryAddEvent<T, B>, ?> eventType) {
-+ super(handler, eventType);
-+ }
-+
-+ @Contract(pure = true)
-+ public @Nullable Predicate<TypedKey<T>> filter() {
-+ return this.filter;
-+ }
-+
-+ @Override
-+ public RegistryEntryAddConfiguration<T> filter(final Predicate<TypedKey<T>> filter) {
-+ this.filter = filter;
-+ return this;
-+ }
-+
-+ @Override
-+ public RegistryEntryAddConfiguration<T> priority(final int priority) {
-+ return (RegistryEntryAddConfiguration<T>) super.priority(priority);
-+ }
-+
-+ @Override
-+ public RegistryEntryAddConfiguration<T> monitor() {
-+ return (RegistryEntryAddConfiguration<T>) super.monitor();
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/type/RegistryLifecycleEventType.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryLifecycleEventType.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..dcc0f6b337840a78d38abdf2eb3f4bbd1676f58f
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryLifecycleEventType.java
-@@ -0,0 +1,14 @@
-+package io.papermc.paper.registry.event.type;
-+
-+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
-+import io.papermc.paper.plugin.lifecycle.event.types.PrioritizableLifecycleEventType;
-+import io.papermc.paper.registry.RegistryBuilder;
-+import io.papermc.paper.registry.event.RegistryEvent;
-+import io.papermc.paper.registry.event.RegistryEventProvider;
-+
-+public final class RegistryLifecycleEventType<T, B extends RegistryBuilder<T>, E extends RegistryEvent<T>> extends PrioritizableLifecycleEventType.Simple<BootstrapContext, E> {
-+
-+ public RegistryLifecycleEventType(final RegistryEventProvider<T, B> type, final String eventName) {
-+ super(type.registryKey() + " / " + eventName, BootstrapContext.class);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java
-index 5562e8da5ebaef2a3add46e88d64358b7737b59e..e5880f76cdb8ebf01fcefdf77ba9b95674b997a8 100644
---- a/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java
-+++ b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java
-@@ -1,12 +1,13 @@
- package io.papermc.paper.registry.legacy;
-
-+import io.papermc.paper.registry.tag.Tag;
-+import io.papermc.paper.registry.tag.TagKey;
- import java.util.Iterator;
- import java.util.function.Supplier;
- import java.util.stream.Stream;
- import org.bukkit.Keyed;
- import org.bukkit.NamespacedKey;
- import org.bukkit.Registry;
--import org.bukkit.craftbukkit.CraftRegistry;
- import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
- import org.checkerframework.checker.nullness.qual.Nullable;
- import org.jetbrains.annotations.NotNull;
-@@ -52,4 +53,14 @@ public final class DelayedRegistry<T extends Keyed, R extends Registry<T>> imple
- public NamespacedKey getKey(final T value) {
- return this.delegate().getKey(value);
- }
-+
-+ @Override
-+ public boolean hasTag(final TagKey<T> key) {
-+ return this.delegate().hasTag(key);
-+ }
-+
-+ @Override
-+ public @NotNull Tag<T> getTag(final TagKey<T> key) {
-+ return this.delegate().getTag(key);
-+ }
- }
-diff --git a/src/main/java/io/papermc/paper/registry/package-info.java b/src/main/java/io/papermc/paper/registry/package-info.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..0b80179ff90e085568d7ceafd9b17511789dc99b
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/package-info.java
-@@ -0,0 +1,5 @@
-+@DefaultQualifier(NonNull.class)
-+package io.papermc.paper.registry;
-+
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-diff --git a/src/main/java/io/papermc/paper/registry/set/NamedRegistryKeySetImpl.java b/src/main/java/io/papermc/paper/registry/set/NamedRegistryKeySetImpl.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..e8c2c18a1ed5cd587266bd415170610781531a12
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/set/NamedRegistryKeySetImpl.java
-@@ -0,0 +1,76 @@
-+package io.papermc.paper.registry.set;
-+
-+import com.google.common.collect.ImmutableList;
-+import com.google.common.collect.Iterables;
-+import io.papermc.paper.registry.PaperRegistries;
-+import io.papermc.paper.registry.RegistryAccess;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.TypedKey;
-+import io.papermc.paper.registry.tag.Tag;
-+import io.papermc.paper.registry.tag.TagKey;
-+import java.util.Collection;
-+import java.util.Set;
-+import net.kyori.adventure.key.Key;
-+import net.minecraft.core.Holder;
-+import net.minecraft.core.HolderSet;
-+import org.bukkit.Keyed;
-+import org.bukkit.NamespacedKey;
-+import org.bukkit.Registry;
-+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Unmodifiable;
-+
-+@DefaultQualifier(NonNull.class)
-+public record NamedRegistryKeySetImpl<T extends Keyed, M>( // TODO remove Keyed
-+ TagKey<T> tagKey,
-+ HolderSet.Named<M> namedSet
-+) implements Tag<T>, org.bukkit.Tag<T> {
-+
-+ @Override
-+ public @Unmodifiable Collection<TypedKey<T>> values() {
-+ final ImmutableList.Builder<TypedKey<T>> builder = ImmutableList.builder();
-+ for (final Holder<M> holder : this.namedSet) {
-+ builder.add(TypedKey.create(this.tagKey.registryKey(), CraftNamespacedKey.fromMinecraft(((Holder.Reference<?>) holder).key().location())));
-+ }
-+ return builder.build();
-+ }
-+
-+ @Override
-+ public RegistryKey<T> registryKey() {
-+ return this.tagKey.registryKey();
-+ }
-+
-+ @Override
-+ public boolean contains(final TypedKey<T> valueKey) {
-+ return Iterables.any(this.namedSet, h -> {
-+ return PaperRegistries.fromNms(((Holder.Reference<?>) h).key()).equals(valueKey);
-+ });
-+ }
-+
-+ @Override
-+ public @Unmodifiable Collection<T> resolve(final Registry<T> registry) {
-+ final ImmutableList.Builder<T> builder = ImmutableList.builder();
-+ for (final Holder<M> holder : this.namedSet) {
-+ builder.add(registry.getOrThrow(CraftNamespacedKey.fromMinecraft(((Holder.Reference<?>) holder).key().location())));
-+ }
-+ return builder.build();
-+ }
-+
-+ @Override
-+ public boolean isTagged(final T item) {
-+ return this.getValues().contains(item);
-+ }
-+
-+ @Override
-+ public Set<T> getValues() {
-+ return Set.copyOf(this.resolve(RegistryAccess.registryAccess().getRegistry(this.registryKey())));
-+ }
-+
-+ @Override
-+ public @NotNull NamespacedKey getKey() {
-+ final Key key = this.tagKey().key();
-+ return new NamespacedKey(key.namespace(), key.value());
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/set/PaperRegistrySets.java b/src/main/java/io/papermc/paper/registry/set/PaperRegistrySets.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..f09ce9c8547ef05153847245746473dd9a8acbe6
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/set/PaperRegistrySets.java
-@@ -0,0 +1,48 @@
-+package io.papermc.paper.registry.set;
-+
-+import io.papermc.paper.registry.PaperRegistries;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.TypedKey;
-+import java.util.ArrayList;
-+import java.util.List;
-+import net.minecraft.core.Holder;
-+import net.minecraft.core.HolderSet;
-+import net.minecraft.core.Registry;
-+import net.minecraft.resources.RegistryOps;
-+import net.minecraft.resources.ResourceKey;
-+import org.bukkit.Keyed;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-+
-+@DefaultQualifier(NonNull.class)
-+public final class PaperRegistrySets {
-+
-+ public static <A extends Keyed, M> HolderSet<M> convertToNms(final ResourceKey<? extends Registry<M>> resourceKey, final RegistryOps.RegistryInfoLookup lookup, final RegistryKeySet<A> registryKeySet) { // TODO remove Keyed
-+ if (registryKeySet instanceof NamedRegistryKeySetImpl<A, ?>) {
-+ return ((NamedRegistryKeySetImpl<A, M>) registryKeySet).namedSet();
-+ } else {
-+ final RegistryOps.RegistryInfo<M> registryInfo = lookup.lookup(resourceKey).orElseThrow();
-+ return HolderSet.direct(key -> {
-+ return registryInfo.getter().getOrThrow(PaperRegistries.toNms(key));
-+ }, registryKeySet.values());
-+ }
-+ }
-+
-+ public static <A extends Keyed, M> RegistryKeySet<A> convertToApi(final RegistryKey<A> registryKey, final HolderSet<M> holders) { // TODO remove Keyed
-+ if (holders instanceof final HolderSet.Named<M> named) {
-+ return new NamedRegistryKeySetImpl<>(PaperRegistries.fromNms(named.key()), named);
-+ } else {
-+ final List<TypedKey<A>> keys = new ArrayList<>();
-+ for (final Holder<M> holder : holders) {
-+ if (!(holder instanceof final Holder.Reference<M> reference)) {
-+ throw new UnsupportedOperationException("Cannot convert a holder set containing direct holders");
-+ }
-+ keys.add(PaperRegistries.fromNms(reference.key()));
-+ }
-+ return RegistrySet.keySet(registryKey, keys);
-+ }
-+ }
-+
-+ private PaperRegistrySets() {
-+ }
-+}
-diff --git a/src/main/java/net/minecraft/core/MappedRegistry.java b/src/main/java/net/minecraft/core/MappedRegistry.java
-index edbbafd1705345282e5e6251eb71bfde5793b7d4..f22d22ebcedcc9c20225677844c86a1ad27c4211 100644
---- a/src/main/java/net/minecraft/core/MappedRegistry.java
-+++ b/src/main/java/net/minecraft/core/MappedRegistry.java
-@@ -441,4 +441,12 @@ public class MappedRegistry<T> implements WritableRegistry<T> {
- public HolderLookup.RegistryLookup<T> asLookup() {
- return this.lookup;
- }
-+ // Paper start
-+ // used to clear intrusive holders from GameEvent, Item, Block, EntityType, and Fluid from unused instances of those types
-+ public void clearIntrusiveHolder(final T instance) {
-+ if (this.unregisteredIntrusiveHolders != null) {
-+ this.unregisteredIntrusiveHolders.remove(instance);
-+ }
-+ }
-+ // Paper end
- }
-diff --git a/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java b/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java
-index 44b7927081b476813505cab6b3a2da2ec2942c54..0497318e8f647453f38f3a16a8be6bd9aa19253f 100644
---- a/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java
-+++ b/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java
-@@ -288,6 +288,17 @@ public class BuiltInRegistries {
- Registries.ENCHANTMENT_PROVIDER_TYPE, EnchantmentProviderTypes::bootstrap
- );
- public static final Registry<? extends Registry<?>> REGISTRY = WRITABLE_REGISTRY;
-+ // Paper start - add built-in registry conversions
-+ public static final io.papermc.paper.registry.data.util.Conversions BUILT_IN_CONVERSIONS = new io.papermc.paper.registry.data.util.Conversions(new net.minecraft.resources.RegistryOps.RegistryInfoLookup() {
-+ @Override
-+ public <T> java.util.Optional<net.minecraft.resources.RegistryOps.RegistryInfo<T>> lookup(final ResourceKey<? extends Registry<? extends T>> registryRef) {
-+ final Registry<T> registry = net.minecraft.server.RegistryLayer.STATIC_ACCESS.registryOrThrow(registryRef);
-+ return java.util.Optional.of(
-+ new net.minecraft.resources.RegistryOps.RegistryInfo<>(registry.asLookup(), registry.asTagAddingLookup(), Lifecycle.experimental())
-+ );
-+ }
-+ });
-+ // Paper end - add built-in registry conversions
-
- private static <T> Registry<T> registerSimple(ResourceKey<? extends Registry<T>> key, BuiltInRegistries.RegistryBootstrap<T> initializer) {
- return internalRegister(key, new MappedRegistry<>(key, Lifecycle.stable(), false), initializer);
-@@ -328,6 +339,7 @@ public class BuiltInRegistries {
- }
- public static void bootStrap(Runnable runnable) {
- // Paper end
-+ REGISTRY.freeze(); // Paper - freeze main registry early
- createContents();
- runnable.run(); // Paper
- freeze();
-@@ -346,6 +358,7 @@ public class BuiltInRegistries {
- REGISTRY.freeze();
-
- for (Registry<?> registry : REGISTRY) {
-+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), BUILT_IN_CONVERSIONS); // Paper
- registry.freeze();
- }
- }
-diff --git a/src/main/java/net/minecraft/resources/RegistryDataLoader.java b/src/main/java/net/minecraft/resources/RegistryDataLoader.java
-index abadf4abe08dc3bb6612b42cbb3f7df3ffa28ce9..3053243866c655829fe2e980446b4abf1da6d37c 100644
---- a/src/main/java/net/minecraft/resources/RegistryDataLoader.java
-+++ b/src/main/java/net/minecraft/resources/RegistryDataLoader.java
-@@ -115,7 +115,7 @@ public class RegistryDataLoader {
- );
-
- public static RegistryAccess.Frozen load(ResourceManager resourceManager, RegistryAccess registryManager, List<RegistryDataLoader.RegistryData<?>> entries) {
-- return load((loader, infoGetter) -> loader.loadFromResources(resourceManager, infoGetter), registryManager, entries);
-+ return load((loader, infoGetter, conversions) -> loader.loadFromResources(resourceManager, infoGetter, conversions), registryManager, entries); // Paper - pass conversions
- }
-
- public static RegistryAccess.Frozen load(
-@@ -124,7 +124,7 @@ public class RegistryDataLoader {
- RegistryAccess registryManager,
- List<RegistryDataLoader.RegistryData<?>> entries
- ) {
-- return load((loader, infoGetter) -> loader.loadFromNetwork(data, factory, infoGetter), registryManager, entries);
-+ return load((loader, infoGetter, conversions) -> loader.loadFromNetwork(data, factory, infoGetter, conversions), registryManager, entries); // Paper - pass conversions
- }
-
- private static RegistryAccess.Frozen load(
-@@ -133,9 +133,11 @@ public class RegistryDataLoader {
- Map<ResourceKey<?>, Exception> map = new HashMap<>();
- List<RegistryDataLoader.Loader<?>> list = entries.stream().map(entry -> entry.create(Lifecycle.stable(), map)).collect(Collectors.toUnmodifiableList());
- RegistryOps.RegistryInfoLookup registryInfoLookup = createContext(baseRegistryManager, list);
-- list.forEach(loader -> loadable.apply((RegistryDataLoader.Loader<?>)loader, registryInfoLookup));
-+ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(registryInfoLookup); // Paper - create conversions
-+ list.forEach(loader -> loadable.apply((RegistryDataLoader.Loader<?>)loader, registryInfoLookup, conversions));
- list.forEach(loader -> {
- Registry<?> registry = loader.registry();
-+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(loader.registry.key(), conversions); // Paper - run pre-freeze listeners
-
- try {
- registry.freeze();
-@@ -193,13 +195,13 @@ public class RegistryDataLoader {
- }
-
- private static <E> void loadElementFromResource(
-- WritableRegistry<E> registry, Decoder<E> decoder, RegistryOps<JsonElement> ops, ResourceKey<E> key, Resource resource, RegistrationInfo entryInfo
-+ WritableRegistry<E> registry, Decoder<E> decoder, RegistryOps<JsonElement> ops, ResourceKey<E> key, Resource resource, RegistrationInfo entryInfo, io.papermc.paper.registry.data.util.Conversions conversions
- ) throws IOException {
- try (Reader reader = resource.openAsReader()) {
- JsonElement jsonElement = JsonParser.parseReader(reader);
- DataResult<E> dataResult = decoder.parse(ops, jsonElement);
- E object = dataResult.getOrThrow();
-- registry.register(key, object, entryInfo);
-+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners(registry, key, object, entryInfo, conversions); // Paper - register with listeners
- }
- }
-
-@@ -208,7 +210,8 @@ public class RegistryDataLoader {
- RegistryOps.RegistryInfoLookup infoGetter,
- WritableRegistry<E> registry,
- Decoder<E> elementDecoder,
-- Map<ResourceKey<?>, Exception> errors
-+ Map<ResourceKey<?>, Exception> errors,
-+ io.papermc.paper.registry.data.util.Conversions conversions // Paper - pass conversions
- ) {
- String string = Registries.elementsDirPath(registry.key());
- FileToIdConverter fileToIdConverter = FileToIdConverter.json(string);
-@@ -221,7 +224,7 @@ public class RegistryDataLoader {
- RegistrationInfo registrationInfo = REGISTRATION_INFO_CACHE.apply(resource.knownPackInfo());
-
- try {
-- loadElementFromResource(registry, elementDecoder, registryOps, resourceKey, resource, registrationInfo);
-+ loadElementFromResource(registry, elementDecoder, registryOps, resourceKey, resource, registrationInfo, conversions); // Paper - pass conversions
- } catch (Exception var15) {
- errors.put(
- resourceKey,
-@@ -237,7 +240,8 @@ public class RegistryDataLoader {
- RegistryOps.RegistryInfoLookup infoGetter,
- WritableRegistry<E> registry,
- Decoder<E> decoder,
-- Map<ResourceKey<?>, Exception> loadingErrors
-+ Map<ResourceKey<?>, Exception> loadingErrors,
-+ io.papermc.paper.registry.data.util.Conversions conversions // Paper - pass conversions
- ) {
- List<RegistrySynchronization.PackedRegistryEntry> list = data.get(registry.key());
- if (list != null) {
-@@ -264,7 +268,7 @@ public class RegistryDataLoader {
-
- try {
- Resource resource = factory.getResourceOrThrow(resourceLocation);
-- loadElementFromResource(registry, decoder, registryOps2, resourceKey, resource, NETWORK_REGISTRATION_INFO);
-+ loadElementFromResource(registry, decoder, registryOps2, resourceKey, resource, NETWORK_REGISTRATION_INFO, conversions); // Paper - pass conversions
- } catch (Exception var18) {
- loadingErrors.put(resourceKey, new IllegalStateException("Failed to parse local data", var18));
- }
-@@ -274,22 +278,23 @@ public class RegistryDataLoader {
- }
-
- static record Loader<T>(RegistryDataLoader.RegistryData<T> data, WritableRegistry<T> registry, Map<ResourceKey<?>, Exception> loadingErrors) {
-- public void loadFromResources(ResourceManager resourceManager, RegistryOps.RegistryInfoLookup infoGetter) {
-- RegistryDataLoader.loadContentsFromManager(resourceManager, infoGetter, this.registry, this.data.elementCodec, this.loadingErrors);
-+ public void loadFromResources(ResourceManager resourceManager, RegistryOps.RegistryInfoLookup infoGetter, io.papermc.paper.registry.data.util.Conversions conversions) { // Paper - pass conversions
-+ RegistryDataLoader.loadContentsFromManager(resourceManager, infoGetter, this.registry, this.data.elementCodec, this.loadingErrors, conversions); // Paper - pass conversions
- }
-
- public void loadFromNetwork(
- Map<ResourceKey<? extends Registry<?>>, List<RegistrySynchronization.PackedRegistryEntry>> data,
- ResourceProvider factory,
-- RegistryOps.RegistryInfoLookup infoGetter
-+ RegistryOps.RegistryInfoLookup infoGetter,
-+ io.papermc.paper.registry.data.util.Conversions conversions // Paper
- ) {
-- RegistryDataLoader.loadContentsFromNetwork(data, factory, infoGetter, this.registry, this.data.elementCodec, this.loadingErrors);
-+ RegistryDataLoader.loadContentsFromNetwork(data, factory, infoGetter, this.registry, this.data.elementCodec, this.loadingErrors, conversions); // Paper - pass conversions
- }
- }
-
- @FunctionalInterface
- interface LoadingFunction {
-- void apply(RegistryDataLoader.Loader<?> loader, RegistryOps.RegistryInfoLookup infoGetter);
-+ void apply(RegistryDataLoader.Loader<?> loader, RegistryOps.RegistryInfoLookup infoGetter, io.papermc.paper.registry.data.util.Conversions conversions); // Paper - pass conversions
- }
-
- public static record RegistryData<T>(ResourceKey<? extends Registry<T>> key, Codec<T> elementCodec, boolean requiredNonEmpty) {
-diff --git a/src/main/java/net/minecraft/server/ReloadableServerRegistries.java b/src/main/java/net/minecraft/server/ReloadableServerRegistries.java
-index 397bdacab9517354875ebc0bc68d35059b3c318b..908431652a0fea79b5a0cee1efd0c7a7d524b614 100644
---- a/src/main/java/net/minecraft/server/ReloadableServerRegistries.java
-+++ b/src/main/java/net/minecraft/server/ReloadableServerRegistries.java
-@@ -47,15 +47,16 @@ public class ReloadableServerRegistries {
- ) {
- RegistryAccess.Frozen frozen = dynamicRegistries.getAccessForLoading(RegistryLayer.RELOADABLE);
- RegistryOps<JsonElement> registryOps = new ReloadableServerRegistries.EmptyTagLookupWrapper(frozen).createSerializationContext(JsonOps.INSTANCE);
-+ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(registryOps.lookupProvider); // Paper
- List<CompletableFuture<WritableRegistry<?>>> list = LootDataType.values()
-- .map(type -> scheduleElementParse((LootDataType<?>)type, registryOps, resourceManager, prepareExecutor))
-+ .map(type -> scheduleElementParse((LootDataType<?>)type, registryOps, resourceManager, prepareExecutor, conversions)) // Paper
- .toList();
- CompletableFuture<List<WritableRegistry<?>>> completableFuture = Util.sequence(list);
- return completableFuture.thenApplyAsync(registries -> apply(dynamicRegistries, (List<WritableRegistry<?>>)registries), prepareExecutor);
- }
-
- private static <T> CompletableFuture<WritableRegistry<?>> scheduleElementParse(
-- LootDataType<T> type, RegistryOps<JsonElement> ops, ResourceManager resourceManager, Executor prepareExecutor
-+ LootDataType<T> type, RegistryOps<JsonElement> ops, ResourceManager resourceManager, Executor prepareExecutor, io.papermc.paper.registry.data.util.Conversions conversions // Paper
- ) {
- return CompletableFuture.supplyAsync(
- () -> {
-@@ -66,7 +67,7 @@ public class ReloadableServerRegistries {
- SimpleJsonResourceReloadListener.scanDirectory(resourceManager, string, GSON, map);
- map.forEach(
- (id, json) -> type.deserialize(id, ops, json)
-- .ifPresent(value -> writableRegistry.register(ResourceKey.create(type.registryKey(), id), (T)value, DEFAULT_REGISTRATION_INFO))
-+ .ifPresent(value -> io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners(writableRegistry, ResourceKey.create(type.registryKey(), id), value, DEFAULT_REGISTRATION_INFO, conversions)) // Paper - register with listeners
- );
- return writableRegistry;
- },
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
-index d21b7e39d71c785f47f790e1ad4be33a8e8e6e51..a47421425a8d5d2f07e08890fded0f7bfec4efb7 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
-@@ -156,11 +156,11 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
- private final Map<NamespacedKey, B> cache = new HashMap<>();
- private final Map<B, NamespacedKey> byValue = new java.util.IdentityHashMap<>(); // Paper - improve Registry
- private final net.minecraft.core.Registry<M> minecraftRegistry;
-- private final BiFunction<NamespacedKey, M, B> minecraftToBukkit;
-+ private final BiFunction<? super NamespacedKey, M, B> minecraftToBukkit; // Paper
- private final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater; // Paper - rename to make it *clear* what it is *only* for
- private boolean init;
-
-- public CraftRegistry(Class<?> bukkitClass, net.minecraft.core.Registry<M> minecraftRegistry, BiFunction<NamespacedKey, M, B> minecraftToBukkit, BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater) { // Paper - relax preload class
-+ public CraftRegistry(Class<?> bukkitClass, net.minecraft.core.Registry<M> minecraftRegistry, BiFunction<? super NamespacedKey, M, B> minecraftToBukkit, BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater) { // Paper - relax preload class
- this.bukkitClass = bukkitClass;
- this.minecraftRegistry = minecraftRegistry;
- this.minecraftToBukkit = minecraftToBukkit;
-@@ -233,4 +233,17 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
- return this.byValue.get(value);
- }
- // Paper end - improve Registry
-+
-+ // Paper start - RegistrySet API
-+ @Override
-+ public boolean hasTag(final io.papermc.paper.registry.tag.TagKey<B> key) {
-+ return this.minecraftRegistry.getTag(net.minecraft.tags.TagKey.create(this.minecraftRegistry.key(), io.papermc.paper.adventure.PaperAdventure.asVanilla(key.key()))).isPresent();
-+ }
-+
-+ @Override
-+ public io.papermc.paper.registry.tag.Tag<B> getTag(final io.papermc.paper.registry.tag.TagKey<B> key) {
-+ final net.minecraft.core.HolderSet.Named<M> namedHolderSet = this.minecraftRegistry.getTag(io.papermc.paper.registry.PaperRegistries.toNms(key)).orElseThrow();
-+ return new io.papermc.paper.registry.set.NamedRegistryKeySetImpl<>(key, namedHolderSet);
-+ }
-+ // Paper end - RegistrySet API
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-index ca1c0955be0d0f78972f39c4bd4afffd46c2fb45..0984843ff443ba40406839d06f83304902ee4f43 100644
---- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-@@ -679,6 +679,21 @@ public final class CraftMagicNumbers implements UnsafeValues {
- }
- // Paper end - lifecycle event API
-
-+ // Paper start - hack to get tags for non server-backed registries
-+ @Override
-+ public <A extends Keyed, M> io.papermc.paper.registry.tag.Tag<A> getTag(final io.papermc.paper.registry.tag.TagKey<A> tagKey) { // TODO remove Keyed
-+ if (tagKey.registryKey() != io.papermc.paper.registry.RegistryKey.ENTITY_TYPE || tagKey.registryKey() != io.papermc.paper.registry.RegistryKey.FLUID) {
-+ throw new UnsupportedOperationException(tagKey.registryKey() + " doesn't have tags");
-+ }
-+ final net.minecraft.resources.ResourceKey<? extends net.minecraft.core.Registry<M>> nmsKey = io.papermc.paper.registry.PaperRegistries.registryToNms(tagKey.registryKey());
-+ final net.minecraft.core.Registry<M> nmsRegistry = org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry().registryOrThrow(nmsKey);
-+ return nmsRegistry
-+ .getTag(io.papermc.paper.registry.PaperRegistries.toNms(tagKey))
-+ .map(named -> new io.papermc.paper.registry.set.NamedRegistryKeySetImpl<>(tagKey, named))
-+ .orElse(null);
-+ }
-+ // Paper end - hack to get tags for non server-backed registries
-+
- /**
- * This helper class represents the different NBT Tags.
- * <p>
-diff --git a/src/main/resources/META-INF/services/io.papermc.paper.registry.event.RegistryEventTypeProvider b/src/main/resources/META-INF/services/io.papermc.paper.registry.event.RegistryEventTypeProvider
-new file mode 100644
-index 0000000000000000000000000000000000000000..8bee1a5ed877a04e4d027593df1f42cefdd824e7
---- /dev/null
-+++ b/src/main/resources/META-INF/services/io.papermc.paper.registry.event.RegistryEventTypeProvider
-@@ -0,0 +1 @@
-+io.papermc.paper.registry.event.RegistryEventTypeProviderImpl
-diff --git a/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java b/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..f27e5e0037b719b1fc10703f8d298d2326b00432
---- /dev/null
-+++ b/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java
-@@ -0,0 +1,34 @@
-+package io.papermc.paper.registry;
-+
-+import io.papermc.paper.registry.data.util.Conversions;
-+import java.util.List;
-+import java.util.Map;
-+import net.minecraft.core.Registry;
-+import net.minecraft.resources.RegistryOps;
-+import net.minecraft.resources.ResourceKey;
-+import org.bukkit.support.AbstractTestingBase;
-+import org.junit.jupiter.api.Disabled;
-+import org.junit.jupiter.params.ParameterizedTest;
-+import org.junit.jupiter.params.provider.Arguments;
-+import org.junit.jupiter.params.provider.MethodSource;
-+
-+import static org.junit.jupiter.api.Assertions.assertEquals;
-+
-+class RegistryBuilderTest extends AbstractTestingBase {
-+
-+ static List<Arguments> registries() {
-+ return List.of(
-+ );
-+ }
-+
-+ @Disabled
-+ @ParameterizedTest
-+ @MethodSource("registries")
-+ <M, T> void testEquality(final ResourceKey<? extends Registry<M>> resourceKey, final PaperRegistryBuilder.Filler<M, T, ?> filler) {
-+ final Registry<M> registry = AbstractTestingBase.REGISTRY_CUSTOM.registryOrThrow(resourceKey);
-+ for (final Map.Entry<ResourceKey<M>, M> entry : registry.entrySet()) {
-+ final M built = filler.fill(new Conversions(new RegistryOps.HolderLookupAdapter(AbstractTestingBase.REGISTRY_CUSTOM)), PaperRegistries.fromNms(entry.getKey()), entry.getValue()).build();
-+ assertEquals(entry.getValue(), built);
-+ }
-+ }
-+}