Paper/patches/server/0497-Registry-Modification-API.patch
2024-02-09 12:18:35 -08:00

1355 lines
68 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Mon, 27 Feb 2023 18:28:39 -0800
Subject: [PATCH] Registry Modification API
== AT ==
public net.minecraft.server.RegistryLayer STATIC_ACCESS
public net.minecraft.core.MappedRegistry validateWrite(Lnet/minecraft/resources/ResourceKey;)V
diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
new file mode 100644
index 0000000000000000000000000000000000000000..abecec33b6c0dba7183057efbc1425d94958b458
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
@@ -0,0 +1,85 @@
+package io.papermc.paper.registry;
+
+import com.google.common.collect.ImmutableList;
+import io.papermc.paper.registry.entry.RegistryEntry;
+import io.papermc.paper.world.structure.ConfiguredStructure;
+import io.papermc.paper.world.structure.PaperConfiguredStructure;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import net.minecraft.core.Registry;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.world.item.enchantment.Enchantment;
+import net.minecraft.world.level.levelgen.structure.Structure;
+import org.bukkit.GameEvent;
+import org.bukkit.Keyed;
+import org.bukkit.MusicInstrument;
+import org.bukkit.craftbukkit.CraftGameEvent;
+import org.bukkit.craftbukkit.CraftMusicInstrument;
+import org.bukkit.craftbukkit.enchantments.CraftEnchantment;
+import org.bukkit.craftbukkit.generator.structure.CraftStructure;
+import org.bukkit.craftbukkit.generator.structure.CraftStructureType;
+import org.bukkit.craftbukkit.inventory.trim.CraftTrimMaterial;
+import org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern;
+import org.bukkit.craftbukkit.potion.CraftPotionEffectType;
+import org.bukkit.generator.structure.StructureType;
+import org.bukkit.inventory.meta.trim.TrimMaterial;
+import org.bukkit.inventory.meta.trim.TrimPattern;
+import org.bukkit.potion.PotionEffectType;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import static io.papermc.paper.registry.entry.RegistryEntry.immutable;
+import static io.papermc.paper.registry.entry.RegistryEntry.immutableBuiltIn;
+import static io.papermc.paper.registry.entry.RegistryEntry.writableBuiltIn;
+
+/**
+ * Note: Newly added registries should also be added to
+ * RegistriesArgumentProvider in the test package.
+ */
+public final class PaperRegistries {
+
+ @Deprecated(forRemoval = true)
+ private static final RegistryKey<ConfiguredStructure> CONFIGURED_STRUCTURE = RegistryKeyImpl.create("worldgen/structure");
+ @Deprecated(forRemoval = true)
+ static final RegistryEntry<Structure, ConfiguredStructure> CONFIGURED_STRUCTURE_ENTRY = immutable(CONFIGURED_STRUCTURE, Registries.STRUCTURE, ConfiguredStructure.class, PaperConfiguredStructure::minecraftToBukkit).delay();
+
+ static final List<RegistryEntry<?, ?>> REGISTRY_ENTRIES;
+ static {
+ REGISTRY_ENTRIES = ImmutableList.<RegistryEntry<?, ?>>builder()
+ // built-ins
+ .add(immutableBuiltIn(RegistryKey.GAME_EVENT, Registries.GAME_EVENT, GameEvent.class, CraftGameEvent::new))
+ .add(immutableBuiltIn(RegistryKey.STRUCTURE_TYPE, Registries.STRUCTURE_TYPE, StructureType.class, CraftStructureType::new))
+ .add(immutableBuiltIn(RegistryKey.INSTRUMENT, Registries.INSTRUMENT, MusicInstrument.class, CraftMusicInstrument::new))
+ .add(immutableBuiltIn(RegistryKey.ENCHANTMENT, Registries.ENCHANTMENT, Enchantment.class, CraftEnchantment::new))
+ .add(immutableBuiltIn(RegistryKey.MOB_EFFECT, Registries.MOB_EFFECT, PotionEffectType.class, CraftPotionEffectType::new))
+
+ // data-drivens
+ .add(immutable(RegistryKey.STRUCTURE, Registries.STRUCTURE, org.bukkit.generator.structure.Structure.class, CraftStructure::new).delay())
+ .add(immutable(RegistryKey.TRIM_MATERIAL, Registries.TRIM_MATERIAL, TrimMaterial.class, CraftTrimMaterial::new).delay())
+ .add(immutable(RegistryKey.TRIM_PATTERN, Registries.TRIM_PATTERN, TrimPattern.class, CraftTrimPattern::new).delay())
+ .build();
+ }
+
+ private static final Map<RegistryKey<?>, RegistryEntry<?, ?>> BY_REGISTRY_KEY = REGISTRY_ENTRIES.stream()
+ .collect(Collectors.toMap(RegistryEntry::key, Function.identity(), (e1, e2) -> {throw new IllegalArgumentException("duplicate keys");}, IdentityHashMap::new));
+
+ private static final Map<ResourceKey<?>, RegistryEntry<?, ?>> BY_RESOURCE_KEY = REGISTRY_ENTRIES.stream()
+ .collect(Collectors.toMap(RegistryEntry::resourceKey, Function.identity(), (e1, e2) -> {throw new IllegalArgumentException("duplicate keys");}, IdentityHashMap::new));
+
+
+ @SuppressWarnings("unchecked")
+ public static <M, T extends Keyed> @Nullable RegistryEntry<M, T> getEntry(final ResourceKey<? extends Registry<M>> resourceKey) {
+ return (RegistryEntry<M, T>) BY_RESOURCE_KEY.get(resourceKey);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <M, T extends Keyed> @Nullable RegistryEntry<M, T> getEntry(final RegistryKey<? super T> registryKey) {
+ return (RegistryEntry<M, T>) BY_REGISTRY_KEY.get(registryKey);
+ }
+
+ private PaperRegistries() {
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java b/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac7b5af4d2fcb8519f6d5cb62022b2a2b12b0c8a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java
@@ -0,0 +1,102 @@
+package io.papermc.paper.registry;
+
+import io.papermc.paper.registry.entry.RegistryEntry;
+import io.papermc.paper.registry.legacy.DelayedRegistryEntry;
+import io.papermc.paper.registry.legacy.LegacyRegistryIdentifiers;
+import io.papermc.paper.world.structure.ConfiguredStructure;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BooleanSupplier;
+import net.minecraft.server.MinecraftServer;
+import org.bukkit.Keyed;
+import org.bukkit.Registry;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.VisibleForTesting;
+
+public class PaperRegistryAccess implements RegistryAccess {
+
+ private final Map<RegistryKey<?>, Registry<?>> registries = new IdentityHashMap<>();
+
+ public static PaperRegistryAccess instance() {
+ return (PaperRegistryAccess) RegistryAccess.registryAccess();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Deprecated
+ @Override
+ public <T extends Keyed> @Nullable Registry<T> getRegistry(final Class<T> type) {
+ if (type == ConfiguredStructure.class) { // manually handle "duplicate" registries to avoid polluting maps in PaperRegistries
+ return (Registry<T>) this.getRegistry0(PaperRegistries.CONFIGURED_STRUCTURE_ENTRY, true);
+ }
+ return this.getRegistry0(byType(type), true);
+ }
+
+ @Override
+ public <T extends Keyed> Registry<T> getRegistry(final RegistryKey<T> key) {
+ return Objects.requireNonNull(this.getRegistry0(key, false), "This shouldn't happen");
+ }
+
+ private <T extends Keyed> @Nullable Registry<T> getRegistry0(final @Nullable RegistryKey<T> key, final boolean fromLegacy) {
+ if (key == null) {
+ return null;
+ }
+ final @Nullable RegistryEntry<?, T> entry = PaperRegistries.getEntry(key);
+ if (entry == null) {
+ if (!fromLegacy) throw new IllegalArgumentException(key + " is not a recognized key");
+ return null;
+ }
+ return this.getRegistry0(entry, fromLegacy);
+ }
+
+ @SuppressWarnings({"unchecked", "deprecation"})
+ private <M, T extends Keyed> @Nullable Registry<T> getRegistry0(final RegistryEntry<M, T> entry, final boolean fromLegacy) {
+ final @Nullable Registry<T> registry = (Registry<T>) this.registries.get(entry.key());
+ if (registry != null) {
+ return registry;
+ }
+ if (entry instanceof final RegistryEntry.BuiltIn<?,?> builtIn) { // provide built-ins as needed
+ return (Registry<T>) this.registries.computeIfAbsent(entry.key(), ignored -> builtIn.createRegistry());
+ } else if (entry instanceof DelayedRegistryEntry<?,?>) {
+ // delayed registry entries are only for static final fields in org.bukkit.Registry for data-driven registries
+ return entry.createRegistry(null);
+ }
+ if (!fromLegacy) throw new IllegalArgumentException("You cannot access this registry: " + entry.key() + " yet!");
+ return null;
+ }
+
+ 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");
+ }
+
+ public <M, T extends Keyed> void createRegistry(final net.minecraft.core.Registry<M> registry) {
+ final @Nullable RegistryEntry<M, T> entry = PaperRegistries.getEntry(registry.key());
+ if (entry == null) { // not handled in API
+ return;
+ }
+ if (this.registries.containsKey(entry.key())) {
+ throw new IllegalArgumentException(registry.key() + " has already been created");
+ }
+ this.registries.put(entry.key(), entry.createRegistry(registry));
+ }
+
+ public net.minecraft.core.RegistryAccess getDelayedRegistryAccess() {
+ return MinecraftServer.getServer().registryAccess();
+ }
+
+ public BooleanSupplier delayedValidCheck() {
+ //noinspection ConstantValue
+ return () -> MinecraftServer.getServer() != null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Deprecated
+ @VisibleForTesting
+ static <T extends Keyed> @Nullable RegistryKey<T> byType(final Class<T> type) {
+ return (RegistryKey<T>) LegacyRegistryIdentifiers.CLASS_TO_KEY_MAP.get(type);
+ }
+}
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..3ddde5f0d8f60b83a002b415247e16f74d268e82
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/PaperRegistryBuilder.java
@@ -0,0 +1,6 @@
+package io.papermc.paper.registry;
+
+public interface PaperRegistryBuilder<M, T> extends RegistryBuilder<T> {
+
+ M build();
+}
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..cbf8c7e1ef45a01ae9ee63d3b44be1b2d32b8d33
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java
@@ -0,0 +1,97 @@
+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.entry.RegistryEntry;
+import io.papermc.paper.registry.event.RegistryAdditionEvent;
+import io.papermc.paper.registry.event.RegistryAdditionEventImpl;
+import io.papermc.paper.registry.event.RegistryEventMap;
+import io.papermc.paper.registry.event.RegistryEvents;
+import io.papermc.paper.registry.event.RegistryPreFreezeEvent;
+import io.papermc.paper.registry.event.RegistryPreFreezeEventImpl;
+import net.kyori.adventure.key.Key;
+import net.minecraft.core.MappedRegistry;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.resources.ResourceLocation;
+import org.bukkit.craftbukkit.CraftRegistry;
+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 additionHooks = new RegistryEventMap("addition");
+ public final RegistryEventMap preFreezeHooks = new RegistryEventMap("pre-freeze");
+
+ private PaperRegistryListenerManager() {
+ }
+
+ public <M> M registerWithListeners(final Registry<M> registry, final String id, final M nms) {
+ return this.registerWithListeners(registry, new ResourceLocation(id), nms);
+ }
+
+ public <M> M registerWithListeners(final Registry<M> registry, final ResourceLocation loc, final M nms) {
+ return this.registerWithListeners(registry, ResourceKey.create(registry.key(), loc), nms);
+ }
+
+ public <M> M registerWithListeners(final Registry<M> registry, final ResourceKey<M> key, final M nms) {
+ return this.registerWithListeners(registry, key, nms, Lifecycle.stable());
+ }
+
+ public <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>> M registerWithListeners(final Registry<M> registry, final ResourceKey<M> key, final M nms, final Lifecycle lifecycle) {
+ 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 RegistryEntry<M, T> entry = PaperRegistries.getEntry(registry.key());
+ if (!(entry instanceof RegistryEntry.Modifiable<?, ?, ?>) || !this.additionHooks.hasHooks(entry.key())) {
+ ((net.minecraft.core.WritableRegistry<M>) registry).register(key, nms, lifecycle);
+ return nms;
+ }
+ final CraftRegistry<T, M> craftRegistry = (CraftRegistry<T, M>) PaperRegistryAccess.instance().getRegistry(entry.key());
+ final RegistryEntry.Modifiable<M, T, B> modifiableEntry = (RegistryEntry.Modifiable<M, T, B>) entry;
+ @SuppressWarnings("PatternValidation") final TypedKey<T> typedKey = TypedKey.create(entry.key(), Key.key(key.location().getNamespace(), key.location().getPath()));
+ final B builder = modifiableEntry.fillBuilder(typedKey, nms);
+ return this.registerWithListeners(registry, craftRegistry.view, modifiableEntry, key, nms, builder, lifecycle);
+ }
+
+ public <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>> M registerWithListeners(final Registry<M> registry, final RegistryView<T> registryView, final RegistryEntry.Modifiable<M, T, B> entry, final ResourceKey<M> key, final @Nullable M oldNms, final B builder, final Lifecycle lifecycle) {
+ @Subst("namespace:key") final ResourceLocation beingAdded = key.location();
+ @SuppressWarnings("PatternValidation") final TypedKey<T> typedKey = TypedKey.create(entry.key(), Key.key(beingAdded.getNamespace(), beingAdded.getPath()));
+ final RegistryAdditionEventImpl<T, B> event = entry.createAdditionEvent(typedKey, builder, registryView);
+ LifecycleEventRunner.INSTANCE.callEvent(this.additionHooks.getHook(entry.key(), RegistryEvents.Provider::addition), event);
+ if (oldNms != null) {
+ ((MappedRegistry<M>) registry).clearIntrusiveHolder(oldNms);
+ }
+ final M newNms = event.builder().build();
+ ((net.minecraft.core.WritableRegistry<M>) registry).register(key, newNms, lifecycle);
+ return newNms;
+ }
+
+ public <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>> void runFreezeListeners(final ResourceKey<? extends Registry<M>> resourceKey) {
+ final @Nullable RegistryEntry<M, T> entry = PaperRegistries.getEntry(resourceKey);
+ if (!(entry instanceof RegistryEntry.Writable<M, ?, ?>) || !this.preFreezeHooks.hasHooks(entry.key())) {
+ return;
+ }
+ final RegistryPreFreezeEventImpl<T, B> event = ((RegistryEntry.Writable<M, T, B>) entry).createPreFreezeEvent(PaperRegistryAccess.instance().getWritableRegistry(entry.key()));
+ LifecycleEventRunner.INSTANCE.callEvent(this.preFreezeHooks.getHook(entry.key(), RegistryEvents.Provider::preFreeze), event);
+ }
+
+ public <T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryAdditionEvent<T, B>> getRegistryAdditionEventType(final RegistryEvents.Provider<T, B> type) {
+ if (!(PaperRegistries.getEntry(type.registryKey()) instanceof RegistryEntry.Modifiable)) {
+ throw new IllegalArgumentException(type.registryKey() + " does not support RegistryAdditionEvent");
+ }
+ return this.additionHooks.getOrCreate(type, RegistryEvents.Provider::addition);
+ }
+
+ public <T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryPreFreezeEvent<T, B>> getRegistryPreFreezeEventType(final RegistryEvents.Provider<T, B> type) {
+ if (!(PaperRegistries.getEntry(type.registryKey()) instanceof RegistryEntry.Writable)) {
+ throw new IllegalArgumentException(type.registryKey() + " does not support RegistryPreFreezeEvent");
+ }
+ return this.preFreezeHooks.getOrCreate(type, RegistryEvents.Provider::preFreeze);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/SimpleWritableCraftRegistry.java b/src/main/java/io/papermc/paper/registry/SimpleWritableCraftRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..437640b3cacc396297f2ac7c44bba0af734e69d0
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/SimpleWritableCraftRegistry.java
@@ -0,0 +1,31 @@
+package io.papermc.paper.registry;
+
+import io.papermc.paper.registry.entry.RegistryEntry;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import net.minecraft.core.MappedRegistry;
+import net.minecraft.core.Registry;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+
+public final class SimpleWritableCraftRegistry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends WritableCraftRegistry<M, T, B> {
+
+ private final Function<? super TypedKey<T>, ? extends B> newBuilder;
+ private final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit;
+
+ public SimpleWritableCraftRegistry(final RegistryEntry.Writable<M, T, B> entry, final Registry<M> nmsRegistry, final Function<? super TypedKey<T>, ? extends B> newBuilder, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit) {
+ super(entry, (MappedRegistry<M>) nmsRegistry);
+ this.newBuilder = newBuilder;
+ this.minecraftToBukkit = minecraftToBukkit;
+ }
+
+ @Override
+ public T minecraftToBukkit(final NamespacedKey namespacedKey, final M minecraft) {
+ return this.minecraftToBukkit.apply(namespacedKey, minecraft);
+ }
+
+ @Override
+ public B newBuilder(final TypedKey<T> key) {
+ return this.newBuilder.apply(key);
+ }
+}
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..2bc44526556d83cd8e28c4e10ab034754b2016f3
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java
@@ -0,0 +1,48 @@
+package io.papermc.paper.registry;
+
+import com.mojang.serialization.Lifecycle;
+import io.papermc.paper.adventure.PaperAdventure;
+import io.papermc.paper.registry.entry.RegistryEntry;
+import io.papermc.paper.registry.event.PaperWritableRegistry;
+import io.papermc.paper.registry.event.WritableRegistry;
+import java.util.function.Consumer;
+import net.minecraft.core.MappedRegistry;
+import net.minecraft.resources.ResourceKey;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.bukkit.craftbukkit.CraftRegistry;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public abstract class WritableCraftRegistry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends CraftRegistry<T, M> {
+
+ private final RegistryEntry.Writable<M, T, B> entry;
+ private final MappedRegistry<M> registry;
+ public final WritableRegistry<T, B> modifiableView;
+
+ public WritableCraftRegistry(final RegistryEntry.Writable<M, T, B> entry, final MappedRegistry<M> registry) {
+ super(entry.preloadClass(), registry, null);
+ this.entry = entry;
+ this.registry = registry;
+ this.modifiableView = new PaperWritableRegistry<>(this.registry, this);
+ }
+
+ public void register(final TypedKey<T> key, final Consumer<? super B> value) {
+ final ResourceKey<M> resourceKey = ResourceKey.create(this.registry.key(), PaperAdventure.asVanilla(key.key()));
+ this.registry.validateWrite(resourceKey);
+ final B builder = this.newBuilder(key);
+ value.accept(builder);
+ PaperRegistryListenerManager.INSTANCE.registerWithListeners(this.registry, this.view, this.entry, resourceKey, null, builder, Lifecycle.experimental());
+ }
+
+ @Override
+ public final @Nullable T createBukkit(final NamespacedKey namespacedKey, final @Nullable M minecraft) {
+ if (minecraft == null) {
+ return null;
+ }
+ return this.minecraftToBukkit(namespacedKey, minecraft);
+ }
+
+ public abstract T minecraftToBukkit(NamespacedKey namespacedKey, M minecraft);
+
+ protected abstract B newBuilder(TypedKey<T> key);
+}
diff --git a/src/main/java/io/papermc/paper/registry/entry/ImmutableRegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/ImmutableRegistryEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..df8e6c2796301d08f5c8f99b030beb02eeb2bd94
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/entry/ImmutableRegistryEntry.java
@@ -0,0 +1,54 @@
+package io.papermc.paper.registry.entry;
+
+import com.google.common.base.Preconditions;
+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;
+import org.bukkit.craftbukkit.CraftRegistry;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class ImmutableRegistryEntry<M, T extends Keyed> implements RegistryEntry<M, T> {
+
+ private final RegistryKey<T> key;
+ private final Class<?> preloadClass;
+ private final ResourceKey<? extends Registry<M>> resourceKey;
+ protected final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit;
+
+ public ImmutableRegistryEntry(final RegistryKey<T> key, final ResourceKey<? extends Registry<M>> resourceKey, final Class<?> preloadClass, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit) {
+ this.key = key;
+ this.preloadClass = preloadClass;
+ this.resourceKey = resourceKey;
+ this.minecraftToBukkit = minecraftToBukkit;
+ }
+
+ @Override
+ public RegistryKey<T> key() {
+ return this.key;
+ }
+
+ @Override
+ public ResourceKey<? extends Registry<M>> resourceKey() {
+ return this.resourceKey;
+ }
+
+ @Override
+ public Class<?> preloadClass() {
+ return this.preloadClass;
+ }
+
+ @Override
+ public org.bukkit.Registry<T> createRegistry(final @Nullable Registry<M> registry) {
+ Preconditions.checkState(registry != null, "Cannot access " + this.resourceKey() + " yet");
+ return new CraftRegistry<>(this.preloadClass, registry, this.minecraftToBukkit);
+ }
+
+ public static class BuiltIn<M, T extends Keyed> extends ImmutableRegistryEntry<M, T> implements RegistryEntry.BuiltIn<M, T> {
+
+ public BuiltIn(final RegistryKey<T> key, final ResourceKey<? extends Registry<M>> resourceKey, final Class<?> preloadClass, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit) {
+ super(key, resourceKey, preloadClass, minecraftToBukkit);
+ }
+ }
+}
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..4cc2ac1cf634b17e1b0c86f6ca61fc884b9719d1
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/entry/ModifiableRegistryEntry.java
@@ -0,0 +1,40 @@
+package io.papermc.paper.registry.entry;
+
+import io.papermc.paper.registry.PaperRegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.RegistryView;
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.event.RegistryAdditionEventImpl;
+import java.util.function.BiFunction;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.ResourceKey;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class ModifiableRegistryEntry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends ImmutableRegistryEntry<M, T> implements RegistryEntry.Modifiable<M, T, B> {
+
+ protected final BiFunction<? super TypedKey<T>, ? super @Nullable M, ? extends B> newBuilder;
+
+ ModifiableRegistryEntry(final RegistryKey<T> key, final ResourceKey<? extends Registry<M>> resourceKey, final Class<?> preloadClass, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit, final BiFunction<? super TypedKey<T>, ? super @Nullable M, ? extends B> newBuilder) {
+ super(key, resourceKey, preloadClass, minecraftToBukkit);
+ this.newBuilder = newBuilder;
+ }
+
+ @Override
+ public RegistryAdditionEventImpl<T, B> createAdditionEvent(final TypedKey<T> key, final B initialBuilder, final RegistryView<T> view) {
+ return new RegistryAdditionEventImpl<>(key, initialBuilder, this.key(), view);
+ }
+
+ @Override
+ public B fillBuilder(final TypedKey<T> key, final M nms) {
+ return this.newBuilder.apply(key, nms);
+ }
+
+ public static class BuiltIn<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends ModifiableRegistryEntry<M, T, B> implements RegistryEntry.BuiltIn<M, T> {
+
+ public BuiltIn(final RegistryKey<T> key, final ResourceKey<? extends Registry<M>> resourceKey, final Class<?> preloadClass, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit, final BiFunction<? super TypedKey<T>, ? super @Nullable M, ? extends B> newBuilder) {
+ super(key, resourceKey, preloadClass, minecraftToBukkit, newBuilder);
+ }
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d281558ac37c51df04bb1cdd143af5d26d0e037
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java
@@ -0,0 +1,94 @@
+package io.papermc.paper.registry.entry;
+
+import com.google.common.base.Preconditions;
+import io.papermc.paper.registry.PaperRegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.RegistryView;
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.WritableCraftRegistry;
+import io.papermc.paper.registry.event.RegistryAdditionEventImpl;
+import io.papermc.paper.registry.event.RegistryPreFreezeEventImpl;
+import io.papermc.paper.registry.legacy.DelayedRegistryEntry;
+import java.util.function.BiFunction;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.server.RegistryLayer;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public interface RegistryEntry<M, T extends Keyed> {
+
+ ResourceKey<? extends Registry<M>> resourceKey();
+
+ RegistryKey<T> key();
+
+ Class<?> preloadClass();
+
+ org.bukkit.Registry<T> createRegistry(@Nullable Registry<M> registry);
+
+ /**
+ * This should only be used if the registry instance needs to exist early due to the need
+ * to populate a field in {@link org.bukkit.Registry}. Data-driven registries shouldn't exist
+ * as a field, but instead be gotten via {@link io.papermc.paper.registry.RegistryAccess#getRegistry(RegistryKey)}
+ */
+ @Deprecated
+ default RegistryEntry<M, T> delay() {
+ Preconditions.checkState(!(this instanceof RegistryEntry.BuiltIn<M,T>), "Cannot delay a built-in registry");
+ return new DelayedRegistryEntry<>(this);
+ }
+
+ interface BuiltIn<M, T extends Keyed> extends RegistryEntry<M, T> {
+
+ default org.bukkit.Registry<T> createRegistry() {
+ return this.createRegistry(RegistryLayer.STATIC_ACCESS.registryOrThrow(this.resourceKey()));
+ }
+ }
+
+ interface Modifiable<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends RegistryEntry<M, T> {
+
+ RegistryAdditionEventImpl<T, B> createAdditionEvent(TypedKey<T> key, B initialBuilder, RegistryView<T> view);
+
+ B fillBuilder(TypedKey<T> key, M nms);
+ }
+
+ interface Writable<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends Modifiable<M, T, B> {
+
+ @Override
+ WritableCraftRegistry<M, T, B> createRegistry(@Nullable Registry<M> nmsRegistry);
+
+ RegistryPreFreezeEventImpl<T, B> createPreFreezeEvent(WritableCraftRegistry<M, T, B> registry);
+
+ interface BuiltIn<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends Writable<M, T, B>, RegistryEntry.BuiltIn<M, T> {
+
+ @Override
+ default WritableCraftRegistry<M, T, B> createRegistry() {
+ return this.createRegistry(RegistryLayer.STATIC_ACCESS.registryOrThrow(this.resourceKey()));
+ }
+ }
+ }
+
+ static <M, T extends Keyed> RegistryEntry<M, T> immutable(final RegistryKey<T> key, final ResourceKey<? extends Registry<M>> resourceKey, final Class<?> preloadClass, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit) {
+ return new ImmutableRegistryEntry<>(key, resourceKey, preloadClass, minecraftToBukkit);
+ }
+
+ static <M, T extends Keyed> RegistryEntry<M, T> immutableBuiltIn(final RegistryKey<T> key, final ResourceKey<? extends Registry<M>> resourceKey, final Class<?> preloadClass, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit) {
+ return new ImmutableRegistryEntry.BuiltIn<>(key, resourceKey, preloadClass, minecraftToBukkit);
+ }
+
+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntry<M, T> modifiable(final RegistryKey<T> key, final ResourceKey<? extends Registry<M>> resourceKey, final Class<?> preloadClass, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit, final BiFunction<? super TypedKey<T>, ? super @Nullable M, ? extends B> newBuilder) {
+ return new ModifiableRegistryEntry<>(key, resourceKey, preloadClass, minecraftToBukkit, newBuilder);
+ }
+
+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntry<M, T> modifiableBuiltIn(final RegistryKey<T> key, final ResourceKey<? extends Registry<M>> resourceKey, final Class<?> preloadClass, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit, final BiFunction<? super TypedKey<T>, ? super @Nullable M, ? extends B> newBuilder) {
+ return new ModifiableRegistryEntry.BuiltIn<>(key, resourceKey, preloadClass, minecraftToBukkit, newBuilder);
+ }
+
+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntry<M, T> writable(final RegistryKey<T> key, final ResourceKey<? extends Registry<M>> resourceKey, final Class<?> preloadClass, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit, final BiFunction<? super TypedKey<T>, ? super @Nullable M, ? extends B> newBuilder) {
+ return new WritableRegistryEntry<>(key,resourceKey, preloadClass, minecraftToBukkit, newBuilder);
+ }
+
+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntry<M, T> writableBuiltIn(final RegistryKey<T> key, final ResourceKey<? extends Registry<M>> resourceKey, final Class<?> preloadClass, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit, final BiFunction<? super TypedKey<T>, ? super @Nullable M, ? extends B> newBuilder) {
+ return new WritableRegistryEntry.BuiltIn<>(key,resourceKey, preloadClass, minecraftToBukkit, newBuilder);
+ }
+}
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..bf6747b4d9a34ddb53acdec0dabfc8dad8699e37
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/entry/WritableRegistryEntry.java
@@ -0,0 +1,45 @@
+package io.papermc.paper.registry.entry;
+
+import com.google.common.base.Preconditions;
+import io.papermc.paper.registry.PaperRegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.SimpleWritableCraftRegistry;
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.WritableCraftRegistry;
+import io.papermc.paper.registry.event.RegistryPreFreezeEventImpl;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.ResourceKey;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class WritableRegistryEntry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends ModifiableRegistryEntry<M, T, B> implements RegistryEntry.Writable<M, T, B> {
+
+ protected WritableRegistryEntry(final RegistryKey<T> key, final ResourceKey<? extends Registry<M>> resourceKey, final Class<?> preloadClass, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit, final BiFunction<? super TypedKey<T>, ? super @Nullable M, ? extends B> newBuilder) {
+ super(key, resourceKey, preloadClass, minecraftToBukkit, newBuilder);
+ }
+
+ @Override
+ public WritableCraftRegistry<M, T, B> createRegistry(final @Nullable Registry<M> registry) {
+ Preconditions.checkState(registry != null, "Cannot access " + this.resourceKey() + " yet");
+ return new SimpleWritableCraftRegistry<>(this, registry, this.passNullNmsObject(this.newBuilder), this.minecraftToBukkit);
+ }
+
+ @Override
+ public RegistryPreFreezeEventImpl<T, B> createPreFreezeEvent(final WritableCraftRegistry<M, T, B> registry) {
+ return new RegistryPreFreezeEventImpl<>(this.key(), registry.modifiableView);
+ }
+
+ protected Function<? super TypedKey<T>, ? extends B> passNullNmsObject(final BiFunction<? super TypedKey<T>, ? super @Nullable M, ? extends B> biFunction) {
+ return t -> biFunction.apply(t, null);
+ }
+
+ public static class BuiltIn<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends WritableRegistryEntry<M, T, B> implements RegistryEntry.Writable.BuiltIn<M, T, B> {
+
+ public BuiltIn(final RegistryKey<T> key, final ResourceKey<? extends Registry<M>> resourceKey, final Class<?> preloadClass, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit, final BiFunction<? super TypedKey<T>, ? super @Nullable M, ? extends B> newBuilder) {
+ super(key, resourceKey, preloadClass, minecraftToBukkit, newBuilder);
+ }
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/entry/package-info.java b/src/main/java/io/papermc/paper/registry/entry/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..e4c94d6860e0f5b643cde1ded20b5503c02a4866
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/entry/package-info.java
@@ -0,0 +1,5 @@
+@DefaultQualifier(NonNull.class)
+package io.papermc.paper.registry.entry;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
diff --git a/src/main/java/io/papermc/paper/registry/event/PaperRegistryView.java b/src/main/java/io/papermc/paper/registry/event/PaperRegistryView.java
new file mode 100644
index 0000000000000000000000000000000000000000..468115c3695bb3bc1529560c624b93462fd02491
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/PaperRegistryView.java
@@ -0,0 +1,53 @@
+package io.papermc.paper.registry.event;
+
+import com.google.common.collect.Iterators;
+import io.papermc.paper.registry.RegistryView;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.function.BiFunction;
+import net.kyori.adventure.key.Key;
+import net.minecraft.core.MappedRegistry;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.ResourceLocation;
+import org.bukkit.NamespacedKey;
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class PaperRegistryView<M, T> implements RegistryView<T> {
+
+ protected final MappedRegistry<M> registry;
+ private final BiFunction<? super Key, M, T> minecraftToBukkit;
+
+ public PaperRegistryView(final Registry<M> registry, final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit) {
+ this.registry = (MappedRegistry<M>) registry;
+ this.minecraftToBukkit = convert(minecraftToBukkit);
+ }
+
+ @Override
+ public T get(final Key key) {
+ final @Nullable M value = this.registry.beforeFrozenView().get(new ResourceLocation(key.namespace(), key.value()));
+ if (value == null) {
+ return null;
+ }
+
+ return this.minecraftToBukkit.apply(key, value);
+ }
+
+ @Override
+ public T getOrThrow(final Key key) {
+ final @Nullable T value = this.get(key);
+ if (value == null) {
+ throw new NoSuchElementException("No value found for key " + key);
+ }
+ return value;
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return Iterators.transform(this.registry.beforeFrozenView().entrySet().iterator(), input -> this.minecraftToBukkit.apply(CraftNamespacedKey.fromMinecraft(input.getKey()), input.getValue()));
+ }
+
+ private static <M, T> BiFunction<? super Key, M, T> convert(final BiFunction<? super NamespacedKey, M, T> original) {
+ return (key, m) -> original.apply(key instanceof final NamespacedKey ns ? ns : new NamespacedKey(key.namespace(), key.value()), m);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/PaperWritableRegistry.java b/src/main/java/io/papermc/paper/registry/event/PaperWritableRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..85296647e9a1bfcbaa9c94f1e7a47722727cd7c8
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/PaperWritableRegistry.java
@@ -0,0 +1,24 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.registry.PaperRegistryBuilder;
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.WritableCraftRegistry;
+import java.util.function.Consumer;
+import net.minecraft.core.Registry;
+import org.bukkit.Keyed;
+import org.jetbrains.annotations.NotNull;
+
+public class PaperWritableRegistry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends PaperRegistryView<M, T> implements WritableRegistry<T, B> {
+
+ private final WritableCraftRegistry<M, T, B> writableRegistry;
+
+ public PaperWritableRegistry(final Registry<M> registry, final WritableCraftRegistry<M, T, B> writableRegistry) {
+ super(registry, writableRegistry::minecraftToBukkit);
+ this.writableRegistry = writableRegistry;
+ }
+
+ @Override
+ public void register(final @NotNull TypedKey<T> key, final Consumer<? super B> value) {
+ this.writableRegistry.register(key, value);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryAdditionEventImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryAdditionEventImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe2619b2e590cbd048a0bb9f06f82a3025d9dfaf
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryAdditionEventImpl.java
@@ -0,0 +1,15 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEvent;
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.RegistryView;
+import io.papermc.paper.registry.TypedKey;
+
+public record RegistryAdditionEventImpl<T, B extends RegistryBuilder<T>>(
+ TypedKey<T> key,
+ B builder,
+ RegistryKey<T> registryKey,
+ RegistryView<T> registry
+) implements RegistryAdditionEvent<T, B>, PaperLifecycleEvent {
+}
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..f644f9db6cdef375aedb7aafc777c2204f3c1b73
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventMap.java
@@ -0,0 +1,43 @@
+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.function.Function;
+
+public final class RegistryEventMap {
+
+ private final Map<RegistryKey<?>, LifecycleEventType.Prioritizable<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, B>> LifecycleEventType.Prioritizable<BootstrapContext, E> getOrCreate(final RegistryEvents.Provider<T, B> type, final Function<RegistryEvents.Provider<T, B>, LifecycleEventType.Prioritizable<BootstrapContext, E>> genericHelper) {
+ final RegistryLifecycleEventType<T, B, E> registerHook;
+ if (this.hooks.containsKey(type.registryKey())) {
+ registerHook = (RegistryLifecycleEventType<T, B, E>) this.hooks.get(type.registryKey());
+ } else {
+ registerHook = new RegistryLifecycleEventType<>(type, this.name);
+ LifecycleEventRunner.INSTANCE.addEventType(registerHook);
+ this.hooks.put(type.registryKey(), registerHook);
+ }
+ return registerHook;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T, B extends RegistryBuilder<T>, E extends RegistryEvent<T, B>> LifecycleEventType.Prioritizable<BootstrapContext, E> getHook(final RegistryKey<T> registryKey, final Function<RegistryEvents.Provider<T, B>, LifecycleEventType.Prioritizable<BootstrapContext, E>> genericHelper) {
+ return (RegistryLifecycleEventType<T, B, E>) this.hooks.get(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..cf04f6426d56ee6ae4d23c47f458d1ecece6e834
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProviderImpl.java
@@ -0,0 +1,23 @@
+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;
+
+public class RegistryEventTypeProviderImpl implements RegistryEventTypeProvider {
+
+ public static RegistryEventTypeProviderImpl instance() {
+ return (RegistryEventTypeProviderImpl) RegistryEventTypeProvider.PROVIDER;
+ }
+
+ @Override
+ public <T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryAdditionEvent<T, B>> registryAddition(final RegistryEvents.Provider<T, B> type) {
+ return PaperRegistryListenerManager.INSTANCE.getRegistryAdditionEventType(type);
+ }
+
+ @Override
+ public <T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryPreFreezeEvent<T, B>> registryPreFreeze(final RegistryEvents.Provider<T, B> type) {
+ return PaperRegistryListenerManager.INSTANCE.getRegistryPreFreezeEventType(type);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryLifecycleEventType.java b/src/main/java/io/papermc/paper/registry/event/RegistryLifecycleEventType.java
new file mode 100644
index 0000000000000000000000000000000000000000..576b17846d6e7f7567fedc1ef98ff090126d04a8
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryLifecycleEventType.java
@@ -0,0 +1,12 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.lifecycle.event.types.PrioritizableLifecycleEventType;
+import io.papermc.paper.registry.RegistryBuilder;
+
+public final class RegistryLifecycleEventType<T, B extends RegistryBuilder<T>, E extends RegistryEvent<T, B>> extends PrioritizableLifecycleEventType<BootstrapContext, E> {
+
+ RegistryLifecycleEventType(final RegistryEvents.Provider<T, B> type, final String eventName) {
+ super(type.registryKey() + " / " + eventName, BootstrapContext.class);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryPreFreezeEventImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryPreFreezeEventImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..b07cdd257e299205b695e0122a48c2f4225aef17
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryPreFreezeEventImpl.java
@@ -0,0 +1,12 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEvent;
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import net.kyori.adventure.key.Keyed;
+
+public record RegistryPreFreezeEventImpl<T extends Keyed, B extends RegistryBuilder<T>>(
+ RegistryKey<T> registryKey,
+ WritableRegistry<T, B> registry
+) implements RegistryPreFreezeEvent<T, B>, PaperLifecycleEvent {
+}
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/legacy/DelayedRegistry.java b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..9fdbb6ee847e2b041c86973229fbb89fe3f7719b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java
@@ -0,0 +1,53 @@
+package io.papermc.paper.registry.legacy;
+
+import java.util.Iterator;
+import java.util.function.BooleanSupplier;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.NotNull;
+import oshi.util.Memoizer;
+
+/**
+ * This is to support the now-deprecated fields in {@link Registry} for
+ * data-driven registries.
+ */
+@Deprecated
+public final class DelayedRegistry<T extends Keyed> implements Registry<T> {
+
+ private final Supplier<? extends Registry<T>> delegate;
+ private final BooleanSupplier validCheck;
+
+ public DelayedRegistry(final Supplier<? extends Registry<T>> delegate, final BooleanSupplier validCheck) {
+ this.delegate = Memoizer.memoize(delegate);
+ this.validCheck = validCheck;
+ }
+
+ private void checkValid() {
+ if (!this.validCheck.getAsBoolean()) {
+ throw new IllegalStateException("You are trying to access this registry too early!");
+ }
+ }
+
+ @Override
+ public @Nullable T get(final NamespacedKey key) {
+ this.checkValid();
+ return this.delegate.get().get(key);
+ }
+
+
+ @Override
+ public Iterator<T> iterator() {
+ this.checkValid();
+ return this.delegate.get().iterator();
+ }
+
+ @Override
+ public @NotNull Stream<T> stream() {
+ this.checkValid();
+ return this.delegate.get().stream();
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistryEntry.java b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistryEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ade945a6b8bfbc07595512847202d0086026ab3
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistryEntry.java
@@ -0,0 +1,42 @@
+package io.papermc.paper.registry.legacy;
+
+import io.papermc.paper.registry.PaperRegistryAccess;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.entry.RegistryEntry;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.ResourceKey;
+import org.bukkit.Keyed;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+@Deprecated
+public class DelayedRegistryEntry<M, T extends Keyed> implements RegistryEntry<M, T> {
+
+ private final RegistryEntry<M, T> delegate;
+
+ public DelayedRegistryEntry(final RegistryEntry<M, T> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public ResourceKey<? extends Registry<M>> resourceKey() {
+ return this.delegate.resourceKey();
+ }
+
+ @Override
+ public RegistryKey<T> key() {
+ return this.delegate.key();
+ }
+
+ @Override
+ public Class<?> preloadClass() {
+ return this.delegate.preloadClass();
+ }
+
+ @Override
+ public org.bukkit.Registry<T> createRegistry(final @Nullable Registry<M> registry) {
+ if (registry == null && !PaperRegistryAccess.instance().delayedValidCheck().getAsBoolean()) {
+ return new DelayedRegistry<>(() -> this.delegate.createRegistry(PaperRegistryAccess.instance().getDelayedRegistryAccess().registryOrThrow(this.resourceKey())), PaperRegistryAccess.instance().delayedValidCheck());
+ }
+ return this.delegate.createRegistry(registry);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/legacy/LegacyRegistryIdentifiers.java b/src/main/java/io/papermc/paper/registry/legacy/LegacyRegistryIdentifiers.java
new file mode 100644
index 0000000000000000000000000000000000000000..18dab3d1a5c873cfef09a8eba69d1e6d228b62eb
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/legacy/LegacyRegistryIdentifiers.java
@@ -0,0 +1,32 @@
+package io.papermc.paper.registry.legacy;
+
+import com.google.common.collect.ImmutableMap;
+import io.papermc.paper.registry.RegistryKey;
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.util.Map;
+
+@Deprecated
+public final class LegacyRegistryIdentifiers {
+
+ public static final Map<Class<?>, RegistryKey<?>> CLASS_TO_KEY_MAP;
+
+ static {
+ final ImmutableMap.Builder<Class<?>, RegistryKey<?>> builder = ImmutableMap.builder();
+ try {
+ for (final Field field : RegistryKey.class.getFields()) {
+ if (field.getType() == RegistryKey.class) {
+ // get the legacy type from the RegistryKey generic parameter on the field
+ final Class<?> legacyType = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
+ builder.put(legacyType, (RegistryKey<?>) field.get(null));
+ }
+ }
+ } catch (final ReflectiveOperationException ex) {
+ throw new RuntimeException(ex);
+ }
+ CLASS_TO_KEY_MAP = builder.build();
+ }
+
+ private LegacyRegistryIdentifiers() {
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/legacy/package-info.java b/src/main/java/io/papermc/paper/registry/legacy/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..4396982af55872fafbfeaf8161ad6f392726c773
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/legacy/package-info.java
@@ -0,0 +1,5 @@
+@DefaultQualifier(NonNull.class)
+package io.papermc.paper.registry.legacy;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
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/net/minecraft/core/MappedRegistry.java b/src/main/java/net/minecraft/core/MappedRegistry.java
index 10e1ba2208a902951035574797073e57f67adad9..22d874234db391747d2bf7feed1a21a7275c6638 100644
--- a/src/main/java/net/minecraft/core/MappedRegistry.java
+++ b/src/main/java/net/minecraft/core/MappedRegistry.java
@@ -81,6 +81,14 @@ public class MappedRegistry<T> implements WritableRegistry<T> {
return MappedRegistry.this.getTags().map(Pair::getSecond);
}
};
+ // Paper start
+ @Nullable
+ private Map<ResourceLocation, T> beforeFrozen = new HashMap<>(2048);
+ public Map<ResourceLocation, T> beforeFrozenView() {
+ Validate.validState(this.beforeFrozen != null, "Cannot get this after the registry is frozen");
+ return Collections.unmodifiableMap(this.beforeFrozen);
+ }
+ // Paper end
public MappedRegistry(ResourceKey<? extends Registry<T>> key, Lifecycle lifecycle) {
this(key, lifecycle, false);
@@ -162,6 +170,7 @@ public class MappedRegistry<T> implements WritableRegistry<T> {
}
this.lifecycles.put(value, lifecycle);
+ Objects.requireNonNull(this.beforeFrozen).put(key.location(), value); // Paper
this.registryLifecycle = this.registryLifecycle.add(lifecycle);
this.holdersInOrder = null;
return reference;
@@ -335,6 +344,7 @@ public class MappedRegistry<T> implements WritableRegistry<T> {
return this;
} else {
this.frozen = true;
+ this.beforeFrozen = null; // Paper
this.byValue.forEach((value, entry) -> {
entry.bindValue(value);
});
@@ -457,4 +467,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(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 b366389fd1aed47e04884e9e495df83ec7398ca3..22e2c992387e4efb6ff2b18fa3b82a3686e3297e 100644
--- a/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java
+++ b/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java
@@ -343,6 +343,7 @@ public class BuiltInRegistries {
}
public static void bootStrap(Runnable runnable) {
// Paper end
+ REGISTRY.freeze(); // Paper - freeze main registry early
createContents();
runnable.run(); // Paper
freeze();
@@ -362,6 +363,7 @@ public class BuiltInRegistries {
REGISTRY.freeze();
for(Registry<?> registry : REGISTRY) {
+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key()); // Paper
registry.freeze();
}
diff --git a/src/main/java/net/minecraft/resources/RegistryDataLoader.java b/src/main/java/net/minecraft/resources/RegistryDataLoader.java
index e1bddbbdbeea79115d0f987065c34e0f7f04d377..fd6986e931099f7843bd6f4fc9c0ddc4806b500d 100644
--- a/src/main/java/net/minecraft/resources/RegistryDataLoader.java
+++ b/src/main/java/net/minecraft/resources/RegistryDataLoader.java
@@ -61,10 +61,12 @@ public class RegistryDataLoader {
}).toList();
RegistryOps.RegistryInfoLookup registryInfoLookup = createContext(baseRegistryManager, list);
list.forEach((loader) -> {
+ io.papermc.paper.registry.PaperRegistryAccess.instance().createRegistry(loader.getFirst()); // Paper
loader.getSecond().load(resourceManager, registryInfoLookup);
});
list.forEach((loader) -> {
Registry<?> registry = loader.getFirst();
+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key()); // Paper
try {
registry.freeze();
@@ -143,7 +145,7 @@ public class RegistryDataLoader {
DataResult<E> dataResult = decoder.parse(registryOps, jsonElement);
E object = dataResult.getOrThrow(false, (error) -> {
});
- newRegistry.register(resourceKey, object, resource.isBuiltin() ? Lifecycle.stable() : dataResult.lifecycle());
+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners(newRegistry, resourceKey, object, resource.isBuiltin() ? Lifecycle.stable() : dataResult.lifecycle()); // Paper
} catch (Exception var20) {
exceptions.put(resourceKey, new IllegalStateException(String.format(Locale.ROOT, "Failed to parse %s from pack %s", resourceLocation, resource.sourcePackId()), var20));
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
index 72c2bc09ce6eefc63c3bab5a8f183e48316d0196..7de20b041a0ec88043275f59546a892f26bdaff1 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
@@ -82,52 +82,20 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
return ((Handleable<M>) bukkit).getHandle();
}
- /**
- * Note: Newly added registries should also be added to RegistriesArgumentProvider in the test package
- *
- * @param bukkitClass the bukkit class of the registry
- * @param registryHolder the minecraft registry holder
- * @return the bukkit registry of the provided class
- */
- public static <B extends Keyed> Registry<?> createRegistry(Class<B> bukkitClass, RegistryAccess registryHolder) {
- if (bukkitClass == Enchantment.class) {
- return new CraftRegistry<>(Enchantment.class, registryHolder.registryOrThrow(Registries.ENCHANTMENT), CraftEnchantment::new);
- }
- if (bukkitClass == GameEvent.class) {
- return new CraftRegistry<>(GameEvent.class, registryHolder.registryOrThrow(Registries.GAME_EVENT), CraftGameEvent::new);
- }
- if (bukkitClass == MusicInstrument.class) {
- return new CraftRegistry<>(MusicInstrument.class, registryHolder.registryOrThrow(Registries.INSTRUMENT), CraftMusicInstrument::new);
- }
- if (bukkitClass == PotionEffectType.class) {
- return new CraftRegistry<>(PotionEffectType.class, registryHolder.registryOrThrow(Registries.MOB_EFFECT), CraftPotionEffectType::new);
- }
- if (bukkitClass == Structure.class) {
- return new CraftRegistry<>(Structure.class, registryHolder.registryOrThrow(Registries.STRUCTURE), CraftStructure::new);
- }
- if (bukkitClass == StructureType.class) {
- return new CraftRegistry<>(StructureType.class, BuiltInRegistries.STRUCTURE_TYPE, CraftStructureType::new);
- }
- if (bukkitClass == TrimMaterial.class) {
- return new CraftRegistry<>(TrimMaterial.class, registryHolder.registryOrThrow(Registries.TRIM_MATERIAL), CraftTrimMaterial::new);
- }
- if (bukkitClass == TrimPattern.class) {
- return new CraftRegistry<>(TrimPattern.class, registryHolder.registryOrThrow(Registries.TRIM_PATTERN), CraftTrimPattern::new);
- }
-
- return null;
- }
+ // Paper - replace with PaperRegistries
- private final Class<? super B> bukkitClass;
+ private final Class<?> bukkitClass; // Paper - this is just the class that needs to load to its static fields are initialized first
private final Map<NamespacedKey, B> cache = new HashMap<>();
private final net.minecraft.core.Registry<M> minecraftRegistry;
- private final BiFunction<NamespacedKey, M, B> minecraftToBukkit;
+ private final BiFunction<? super NamespacedKey, M, B> minecraftToBukkit; // Paper
+ public final io.papermc.paper.registry.RegistryView<B> view; // Paper
private boolean init;
- public CraftRegistry(Class<? super B> bukkitClass, net.minecraft.core.Registry<M> minecraftRegistry, BiFunction<NamespacedKey, M, B> minecraftToBukkit) {
+ public CraftRegistry(Class<?> bukkitClass, net.minecraft.core.Registry<M> minecraftRegistry, BiFunction<? super NamespacedKey, M, B> minecraftToBukkit) { // Paper
this.bukkitClass = bukkitClass;
this.minecraftRegistry = minecraftRegistry;
this.minecraftToBukkit = minecraftToBukkit;
+ this.view = new io.papermc.paper.registry.event.PaperRegistryView<>(this.minecraftRegistry, this.minecraftToBukkit); // Paper
}
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 24365002bde70f75116e220ba614d4b9db9b0134..2ee73132f4ceaf745908865e2f5bf00a6f28f813 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -279,7 +279,7 @@ public final class CraftServer implements Server {
protected final DedicatedServer console;
protected final DedicatedPlayerList playerList;
private final Map<String, World> worlds = new LinkedHashMap<String, World>();
- private final Map<Class<?>, Registry<?>> registries = new HashMap<>();
+ // private final Map<Class<?>, Registry<?>> registries = new HashMap<>(); // Paper - replace
private YamlConfiguration configuration;
private YamlConfiguration commandsConfiguration;
private final Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
@@ -2663,7 +2663,7 @@ public final class CraftServer implements Server {
@Override
public <T extends Keyed> Registry<T> getRegistry(Class<T> aClass) {
- return (Registry<T>) this.registries.computeIfAbsent(aClass, key -> CraftRegistry.createRegistry(aClass, this.console.registryAccess()));
+ return io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(aClass); // Paper
}
@Deprecated
diff --git a/src/main/resources/META-INF/services/io.papermc.paper.registry.RegistryAccess b/src/main/resources/META-INF/services/io.papermc.paper.registry.RegistryAccess
new file mode 100644
index 0000000000000000000000000000000000000000..8a083d45004f82fc9c51c219fb20f34624adb501
--- /dev/null
+++ b/src/main/resources/META-INF/services/io.papermc.paper.registry.RegistryAccess
@@ -0,0 +1 @@
+io.papermc.paper.registry.PaperRegistryAccess
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/DummyRegistryAccess.java b/src/test/java/io/papermc/paper/registry/DummyRegistryAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..64fec4a1b81e51c790734c525fed886e31c30e67
--- /dev/null
+++ b/src/test/java/io/papermc/paper/registry/DummyRegistryAccess.java
@@ -0,0 +1,22 @@
+package io.papermc.paper.registry;
+
+import java.util.function.BooleanSupplier;
+import net.minecraft.core.RegistryAccess;
+import org.bukkit.support.AbstractTestingBase;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public class DummyRegistryAccess extends PaperRegistryAccess {
+
+ @Override
+ public RegistryAccess getDelayedRegistryAccess() {
+ return AbstractTestingBase.REGISTRY_CUSTOM;
+ }
+
+ @Override
+ public BooleanSupplier delayedValidCheck() {
+ //noinspection ConstantValue
+ return () -> AbstractTestingBase.REGISTRY_CUSTOM != null;
+ }
+}
diff --git a/src/test/java/io/papermc/paper/registry/LegacyRegistryIdentifierTest.java b/src/test/java/io/papermc/paper/registry/LegacyRegistryIdentifierTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9d00e65639521eecd44bd2be3e012264c3785f5
--- /dev/null
+++ b/src/test/java/io/papermc/paper/registry/LegacyRegistryIdentifierTest.java
@@ -0,0 +1,20 @@
+package io.papermc.paper.registry;
+
+import org.bukkit.GameEvent;
+import org.bukkit.MusicInstrument;
+import org.bukkit.inventory.meta.trim.TrimPattern;
+import org.bukkit.support.AbstractTestingBase;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+@Deprecated
+class LegacyRegistryIdentifierTest extends AbstractTestingBase {
+
+ @Test
+ void testSeveralConversions() {
+ assertSame(RegistryKey.GAME_EVENT, PaperRegistryAccess.byType(GameEvent.class));
+ assertSame(RegistryKey.TRIM_PATTERN, PaperRegistryAccess.byType(TrimPattern.class));
+ assertSame(RegistryKey.INSTRUMENT, PaperRegistryAccess.byType(MusicInstrument.class));
+ }
+}
diff --git a/src/test/java/org/bukkit/support/DummyServer.java b/src/test/java/org/bukkit/support/DummyServer.java
index 3b3e44c5ed24f653f7dc1e5d3d4f0ff76084f277..deec35b073365b0fce66914a92b36e633d345605 100644
--- a/src/test/java/org/bukkit/support/DummyServer.java
+++ b/src/test/java/org/bukkit/support/DummyServer.java
@@ -43,11 +43,6 @@ public final class DummyServer {
when(instance.getLootTable(any())).then(mock -> new CraftLootTable(mock.getArgument(0),
AbstractTestingBase.DATA_PACK.getLootData().getLootTable(CraftNamespacedKey.toMinecraft(mock.getArgument(0)))));
- when(instance.getRegistry(any())).then((Answer<Registry<?>>) mock -> {
- Class<? extends Keyed> aClass = mock.getArgument(0);
- return registers.computeIfAbsent(aClass, key -> CraftRegistry.createRegistry(aClass, AbstractTestingBase.REGISTRY_CUSTOM));
- });
-
// Paper start - testing additions
final Thread currentThread = Thread.currentThread();
when(instance.isPrimaryThread()).thenAnswer(ignored -> Thread.currentThread().equals(currentThread));
diff --git a/src/test/resources/META-INF/services/io.papermc.paper.registry.RegistryAccess b/src/test/resources/META-INF/services/io.papermc.paper.registry.RegistryAccess
new file mode 100644
index 0000000000000000000000000000000000000000..6c7526bbc7318f510f81f4073a158f7136017a56
--- /dev/null
+++ b/src/test/resources/META-INF/services/io.papermc.paper.registry.RegistryAccess
@@ -0,0 +1 @@
+io.papermc.paper.registry.DummyRegistryAccess