From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jake Potrebic 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 diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java index 40ac461d2f1906059377c77229612f540e827d75..7a2fb53ab280893b1c9c886ccb3480b695343cbf 100644 --- a/src/main/java/io/papermc/paper/registry/PaperRegistries.java +++ b/src/main/java/io/papermc/paper/registry/PaperRegistries.java @@ -44,6 +44,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 { 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 > WritableCraftRegistry getWritableRegistry(final RegistryKey key) { + final Registry registry = this.getRegistry(key); + if (registry instanceof WritableCraftRegistry) { + return (WritableCraftRegistry) registry; + } + throw new IllegalArgumentException(key + " does not point to a writable registry"); + } + private static Registry possiblyUnwrap(final Registry registry) { if (registry instanceof final DelayedRegistry 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..4cf32102a134ebef67d3893cfd24bf0add321eb9 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/PaperRegistryBuilder.java @@ -0,0 +1,24 @@ +package io.papermc.paper.registry; + +import org.checkerframework.checker.nullness.qual.Nullable; + +public interface PaperRegistryBuilder extends RegistryBuilder { + + M build(); + + @FunctionalInterface + interface Filler> { + + B fill(final TypedKey key, final @Nullable M nms); + + default Factory asFactory() { + return key -> this.fill(key, null); + } + } + + @FunctionalInterface + interface Factory> { + + B create(final TypedKey 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..e2605a9324b9dc91cd910e4bb974d5a911b58b1f --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java @@ -0,0 +1,167 @@ +package io.papermc.paper.registry; + +import com.google.common.base.Preconditions; +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.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 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.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 registerWithListeners(final Registry registry, final String id, final M nms) { + return this.registerWithListeners(registry, ResourceLocation.withDefaultNamespace(id), nms); + } + + /** + * For {@link Registry#register(Registry, ResourceLocation, Object)} + */ + public M registerWithListeners(final Registry 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 registerWithListeners(final Registry registry, final ResourceKey key, final M nms) { + return this.registerWithListeners(registry, key, nms, RegistrationInfo.BUILT_IN, PaperRegistryListenerManager::registerWithInstance); + } + + /** + * For {@link Registry#registerForHolder(Registry, ResourceLocation, Object)} + */ + public Holder.Reference registerForHolderWithListeners(final Registry 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 Holder.Reference registerForHolderWithListeners(final Registry registry, final ResourceKey key, final M nms) { + return this.registerWithListeners(registry, key, nms, RegistrationInfo.BUILT_IN, WritableRegistry::register); + } + + public > void registerWithListeners( + final Registry registry, + final ResourceKey key, + final M nms, + final RegistrationInfo registrationInfo + ) { + this.registerWithListeners(registry, key, nms, registrationInfo, WritableRegistry::register); + } + + public , R> R registerWithListeners( + final Registry registry, + final ResourceKey key, + final M nms, + final RegistrationInfo registrationInfo, + final RegisterMethod registerMethod + ) { + 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 entry = PaperRegistries.getEntry(registry.key()); + if (!(entry instanceof RegistryEntry.Modifiable) || !this.valueAddHooks.hasHooks(entry.apiKey())) { + return registerMethod.register((WritableRegistry) registry, key, nms, registrationInfo); + } + final RegistryEntry.Modifiable modifiableEntry = (RegistryEntry.Modifiable) entry; + @SuppressWarnings("PatternValidation") final TypedKey typedKey = TypedKey.create(entry.apiKey(), Key.key(key.location().getNamespace(), key.location().getPath())); + final B builder = modifiableEntry.fillBuilder(typedKey, nms); + return this.registerWithListeners(registry, modifiableEntry, key, nms, builder, registrationInfo, registerMethod); + } + + public > void registerWithListeners( + final Registry registry, + final RegistryEntry.Modifiable entry, + final ResourceKey key, + final @Nullable M oldNms, + final B builder, + final RegistrationInfo registrationInfo + ) { + this.registerWithListeners(registry, entry, key, oldNms, builder, registrationInfo, WritableRegistry::register); + } + + public , R> R registerWithListeners( + final Registry registry, + final RegistryEntry.Modifiable entry, + final ResourceKey key, + final @Nullable M oldNms, + final B builder, + final RegistrationInfo registrationInfo, + final RegisterMethod registerMethod + ) { + @Subst("namespace:key") final ResourceLocation beingAdded = key.location(); + @SuppressWarnings("PatternValidation") final TypedKey typedKey = TypedKey.create(entry.apiKey(), Key.key(beingAdded.getNamespace(), beingAdded.getPath())); + final RegistryEntryAddEventImpl event = entry.createEntryAddEvent(typedKey, builder); + LifecycleEventRunner.INSTANCE.callEvent(this.valueAddHooks.getHook(entry.apiKey()), event); + if (oldNms != null) { + ((MappedRegistry) registry).clearIntrusiveHolder(oldNms); + } + final M newNms = event.builder().build(); + return registerMethod.register((WritableRegistry) registry, key, newNms, registrationInfo); + } + + private static M registerWithInstance(final WritableRegistry writableRegistry, final ResourceKey key, final M value, final RegistrationInfo registrationInfo) { + writableRegistry.register(key, value, registrationInfo); + return value; + } + + @FunctionalInterface + public interface RegisterMethod { + + R register(WritableRegistry writableRegistry, ResourceKey key, M value, RegistrationInfo registrationInfo); + } + + public > void runFreezeListeners(final ResourceKey> resourceKey) { + final @Nullable RegistryEntryInfo entry = PaperRegistries.getEntry(resourceKey); + if (!(entry instanceof RegistryEntry.Writable) || !this.freezeHooks.hasHooks(entry.apiKey())) { + return; + } + final RegistryEntry.Writable writableEntry = (RegistryEntry.Writable) entry; + final RegistryFreezeEventImpl event = writableEntry.createFreezeEvent(PaperRegistryAccess.instance().getWritableRegistry(entry.apiKey())); + LifecycleEventRunner.INSTANCE.callEvent(this.freezeHooks.getHook(entry.apiKey()), event); + } + + public > RegistryEntryAddEventType getRegistryValueAddEventType(final RegistryEventProvider type) { + if (!(PaperRegistries.getEntry(type.registryKey()) instanceof RegistryEntry.Modifiable)) { + throw new IllegalArgumentException(type.registryKey() + " does not support RegistryAdditionEvent"); + } + return this.valueAddHooks.getOrCreate(type, RegistryEntryAddEventTypeImpl::new); + } + + public > LifecycleEventType.Prioritizable> getRegistryFreezeEventType(final RegistryEventProvider type) { + if (!(PaperRegistries.getEntry(type.registryKey()) instanceof RegistryEntry.Writable)) { + throw new IllegalArgumentException(type.registryKey() + " does not support RegistryPreFreezeEvent"); + } + 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..aff4895d5add72e22ae9f723b77843435ff044aa --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java @@ -0,0 +1,85 @@ +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.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> extends CraftRegistry { + + private static final RegistrationInfo FROM_PLUGIN = new RegistrationInfo(Optional.empty(), Lifecycle.experimental()); + + private final RegistryEntry.BuilderHolder entry; + private final MappedRegistry registry; + public final WritableRegistry apiWritableRegistry = new ApiWritableRegistry(); + private final PaperRegistryBuilder.Factory builderFactory; + private final BiFunction minecraftToBukkit; + + public WritableCraftRegistry( + final RegistryEntry.BuilderHolder entry, + final Class classToPreload, + final MappedRegistry registry, + final BiFunction serializationUpdater, + final PaperRegistryBuilder.Factory builderFactory, + final BiFunction minecraftToBukkit + ) { + super(classToPreload, registry, null, serializationUpdater); + this.entry = entry; + this.registry = registry; + this.builderFactory = builderFactory; + this.minecraftToBukkit = minecraftToBukkit; + } + + public void register(final TypedKey key, final Consumer value) { + final ResourceKey resourceKey = ResourceKey.create(this.registry.key(), PaperAdventure.asVanilla(key.key())); + this.registry.validateWrite(resourceKey); + final B builder = this.newBuilder(key); + value.accept(builder); + if (this.entry instanceof final RegistryEntry.Modifiable modifiable && PaperRegistryListenerManager.INSTANCE.valueAddHooks.hasHooks(this.entry.apiKey())) { + PaperRegistryListenerManager.INSTANCE.registerWithListeners( + this.registry, + modifiable, + resourceKey, + null, + builder, + FROM_PLUGIN + ); + } else { + this.registry.register(resourceKey, builder.build(), FROM_PLUGIN); + } + } + + @Override + public final @Nullable T createBukkit(final NamespacedKey namespacedKey, final @Nullable M minecraft) { + if (minecraft == null) { + return null; + } + return this.minecraftToBukkit(namespacedKey, minecraft); + } + + public T minecraftToBukkit(final NamespacedKey namespacedKey, final M minecraft) { + return this.minecraftToBukkit.apply(namespacedKey, minecraft); + } + + protected B newBuilder(final TypedKey key) { + return this.builderFactory.create(key); + } + + public class ApiWritableRegistry implements WritableRegistry { + @Override + public void register(final TypedKey key, final Consumer value) { + WritableCraftRegistry.this.register(key, value); + } + } +} 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..a0f24d396e3a258be1978b2baa3f8e07ff54538f --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/entry/AddableRegistryEntry.java @@ -0,0 +1,43 @@ +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 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> extends CraftRegistryEntry implements RegistryEntry.Addable { + + private final PaperRegistryBuilder.Filler builderFiller; + + protected AddableRegistryEntry( + final ResourceKey> mcKey, + final RegistryKey apiKey, + final Class classToPreload, + final BiFunction minecraftToBukkit, + final PaperRegistryBuilder.Filler builderFiller + ) { + super(mcKey, apiKey, classToPreload, minecraftToBukkit); + this.builderFiller = builderFiller; + } + + private WritableCraftRegistry createRegistry(final Registry registry) { + return new WritableCraftRegistry<>(this, this.classToPreload, (MappedRegistry) registry, this.updater, this.builderFiller.asFactory(), this.minecraftToBukkit); + } + + @Override + public RegistryHolder createRegistryHolder(final Registry nmsRegistry) { + return new RegistryHolder.Memoized<>(() -> this.createRegistry(nmsRegistry)); + } + + @Override + public B fillBuilder(final TypedKey key, final M nms) { + return this.builderFiller.fill(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..df10008f6bde1047c8a13523c0a8fc01222a4e62 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/entry/ModifiableRegistryEntry.java @@ -0,0 +1,31 @@ +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 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> extends CraftRegistryEntry implements RegistryEntry.Modifiable { + + protected final PaperRegistryBuilder.Filler builderFiller; + + protected ModifiableRegistryEntry( + final ResourceKey> mcKey, + final RegistryKey apiKey, + final Class toPreload, + final BiFunction minecraftToBukkit, + final PaperRegistryBuilder.Filler builderFiller + ) { + super(mcKey, apiKey, toPreload, minecraftToBukkit); + this.builderFiller = builderFiller; + } + + @Override + public B fillBuilder(final TypedKey key, final M nms) { + return this.builderFiller.fill(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..c49170d469dca809dc42421168d14e57e24d734b 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,12 @@ 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.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; @@ -32,6 +37,37 @@ public interface RegistryEntry extends RegistryEntryInfo(this); } + interface BuilderHolder> extends RegistryEntryInfo { + + B fillBuilder(TypedKey key, M nms); + } + + /** + * Can mutate values being added to the registry + */ + interface Modifiable> extends BuilderHolder { + + default RegistryEntryAddEventImpl createEntryAddEvent(final TypedKey key, final B initialBuilder) { + return new RegistryEntryAddEventImpl<>(key, initialBuilder, this.apiKey()); + } + } + + /** + * Can only add new values to the registry, not modify any values. + */ + interface Addable> extends BuilderHolder { // TODO remove Keyed + + default RegistryFreezeEventImpl createFreezeEvent(final WritableCraftRegistry writableRegistry) { + return new RegistryFreezeEventImpl<>(this.apiKey(), writableRegistry.apiWritableRegistry); + } + } + + /** + * Can mutate values and add new values. + */ + interface Writable> extends Modifiable, Addable { // TODO remove Keyed + } + static RegistryEntry entry( final ResourceKey> mcKey, final RegistryKey apiKey, @@ -48,4 +84,24 @@ public interface RegistryEntry extends RegistryEntryInfo(mcKey, apiKey, apiRegistrySupplier); } + + static > RegistryEntry modifiable( + final ResourceKey> mcKey, + final RegistryKey apiKey, + final Class toPreload, + final BiFunction minecraftToBukkit, + final PaperRegistryBuilder.Filler filler + ) { + return new ModifiableRegistryEntry<>(mcKey, apiKey, toPreload, minecraftToBukkit, filler); + } + + static > RegistryEntry writable( + final ResourceKey> mcKey, + final RegistryKey apiKey, + final Class toPreload, + final BiFunction minecraftToBukkit, + final PaperRegistryBuilder.Filler 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> extends AddableRegistryEntry implements RegistryEntry.Writable { // TODO remove Keyed + + protected WritableRegistryEntry( + final ResourceKey> mcKey, + final RegistryKey apiKey, + final Class classToPreload, + final BiFunction minecraftToBukkit, + final PaperRegistryBuilder.Filler 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..a1d747c1a46d3aa8bcdef82bebb48f1fd760e61f --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEventImpl.java @@ -0,0 +1,13 @@ +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.TypedKey; + +public record RegistryEntryAddEventImpl>( + TypedKey key, + B builder, + RegistryKey registryKey +) implements RegistryEntryAddEvent, 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..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, LifecycleEventType, ?>> hooks = new HashMap<>(); + private final String name; + + public RegistryEventMap(final String name) { + this.name = name; + } + + @SuppressWarnings("unchecked") + public , E extends RegistryEvent, ET extends LifecycleEventType> ET getOrCreate(final RegistryEventProvider type, final BiFunction, ? 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 > LifecycleEventType getHook(final RegistryKey registryKey) { + return (LifecycleEventType) 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 > RegistryEntryAddEventType registryEntryAdd(final RegistryEventProvider type) { + return PaperRegistryListenerManager.INSTANCE.getRegistryValueAddEventType(type); + } + + @Override + public > LifecycleEventType.Prioritizable> registryFreeze(final RegistryEventProvider 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..2db5d33d0b72ec3c9ff1c3042d9246dfa4344346 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEventImpl.java @@ -0,0 +1,11 @@ +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; + +public record RegistryFreezeEventImpl>( + RegistryKey registryKey, + WritableRegistry registry +) implements RegistryFreezeEvent, 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/event/type/RegistryEntryAddEventTypeImpl.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventTypeImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..32303ea9b3da736cbe26d06e57f5dcc3aa32a99b --- /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> extends PrioritizableLifecycleEventType, RegistryEntryAddConfiguration> implements RegistryEntryAddEventType { + + public RegistryEntryAddEventTypeImpl(final RegistryEventProvider type, final String eventName) { + super(type.registryKey() + " / " + eventName, BootstrapContext.class); + } + + @Override + public RegistryEntryAddConfiguration newHandler(final LifecycleEventHandler> handler) { + return new RegistryEntryAddHandlerConfiguration<>(handler, this); + } + + @Override + public void forEachHandler(final RegistryEntryAddEvent event, final Consumer>> consumer, final Predicate>> predicate) { + super.forEachHandler(event, consumer, predicate.and(handler -> this.matchesTarget(event, handler))); + } + + private boolean matchesTarget(final RegistryEntryAddEvent event, final RegisteredHandler> handler) { + final RegistryEntryAddHandlerConfiguration config = (RegistryEntryAddHandlerConfiguration) handler.config(); + return config.target() == null || event.key().equals(config.target()); + } +} 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..53df2dd1a9e1cef90bd8504c717b1cc6374b6f4e --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddHandlerConfiguration.java @@ -0,0 +1,39 @@ +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 org.checkerframework.checker.nullness.qual.Nullable; + +public class RegistryEntryAddHandlerConfiguration> extends PrioritizedLifecycleEventHandlerConfigurationImpl> implements RegistryEntryAddConfiguration { + + private @Nullable TypedKey target; + + public RegistryEntryAddHandlerConfiguration(final LifecycleEventHandler> handler, final AbstractLifecycleEventType, ?> eventType) { + super(handler, eventType); + } + + public @Nullable TypedKey target() { + return this.target; + } + + @Override + public RegistryEntryAddConfiguration onlyFor(final TypedKey key) { + this.target = key; + return this; + } + + @Override + public RegistryEntryAddConfiguration priority(final int priority) { + return (RegistryEntryAddConfiguration) super.priority(priority); + } + + @Override + public RegistryEntryAddConfiguration monitor() { + return (RegistryEntryAddConfiguration) 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, E extends RegistryEvent> extends PrioritizableLifecycleEventType.Simple { + + public RegistryLifecycleEventType(final RegistryEventProvider type, final String eventName) { + super(type.registryKey() + " / " + eventName, BootstrapContext.class); + } +} 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 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 implements WritableRegistry { public HolderLookup.RegistryLookup 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..c96bcd815ec9bf76625b5bedef461da49d7240ce 100644 --- a/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java +++ b/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java @@ -328,6 +328,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 +347,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 abadf4abe08dc3bb6612b42cbb3f7df3ffa28ce9..56d8dcf2f3fd042ed1e7ed01a0ce5d90c89ee502 100644 --- a/src/main/java/net/minecraft/resources/RegistryDataLoader.java +++ b/src/main/java/net/minecraft/resources/RegistryDataLoader.java @@ -136,6 +136,7 @@ public class RegistryDataLoader { list.forEach(loader -> loadable.apply((RegistryDataLoader.Loader)loader, registryInfoLookup)); list.forEach(loader -> { Registry registry = loader.registry(); + io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(loader.registry.key()); // Paper - run pre-freeze listeners try { registry.freeze(); @@ -199,7 +200,7 @@ public class RegistryDataLoader { JsonElement jsonElement = JsonParser.parseReader(reader); DataResult 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); // Paper - register with listeners } } diff --git a/src/main/java/net/minecraft/server/ReloadableServerRegistries.java b/src/main/java/net/minecraft/server/ReloadableServerRegistries.java index 397bdacab9517354875ebc0bc68d35059b3c318b..705fe42b52414fbac14ec107fda5ad0989a5a115 100644 --- a/src/main/java/net/minecraft/server/ReloadableServerRegistries.java +++ b/src/main/java/net/minecraft/server/ReloadableServerRegistries.java @@ -66,7 +66,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)) // 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..43d686a9958cff96f5b15d93e920c8f2313aa65b 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 implements Registry { private final Map cache = new HashMap<>(); private final Map byValue = new java.util.IdentityHashMap<>(); // Paper - improve Registry private final net.minecraft.core.Registry minecraftRegistry; - private final BiFunction minecraftToBukkit; + private final BiFunction minecraftToBukkit; // Paper private final BiFunction serializationUpdater; // Paper - rename to make it *clear* what it is *only* for private boolean init; - public CraftRegistry(Class bukkitClass, net.minecraft.core.Registry minecraftRegistry, BiFunction minecraftToBukkit, BiFunction serializationUpdater) { // Paper - relax preload class + public CraftRegistry(Class bukkitClass, net.minecraft.core.Registry minecraftRegistry, BiFunction minecraftToBukkit, BiFunction serializationUpdater) { // Paper - relax preload class this.bukkitClass = bukkitClass; this.minecraftRegistry = minecraftRegistry; this.minecraftToBukkit = minecraftToBukkit; 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