Paper/patches/api/0477-Registry-Modification-...

399 lines
16 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Wed, 2 Mar 2022 13:36:21 -0800
Subject: [PATCH] Registry Modification API
diff --git a/src/main/java/io/papermc/paper/registry/RegistryBuilder.java b/src/main/java/io/papermc/paper/registry/RegistryBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..2bec1f1d750e796e1d26c3cd090d53de2672170f
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/RegistryBuilder.java
@@ -0,0 +1,11 @@
+package io.papermc.paper.registry;
+
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * To be implemented by any type used for modifying registries.
+ */
+@ApiStatus.NonExtendable
+@ApiStatus.Experimental
+public interface RegistryBuilder<T> {
+}
diff --git a/src/main/java/io/papermc/paper/registry/RegistryView.java b/src/main/java/io/papermc/paper/registry/RegistryView.java
new file mode 100644
index 0000000000000000000000000000000000000000..768f4370fc622b7a6c783fdd67a222f1e0570e1a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/RegistryView.java
@@ -0,0 +1,20 @@
+package io.papermc.paper.registry;
+
+import net.kyori.adventure.key.Key;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Provides read-only access to a registry.
+ *
+ * @param <T> registry object type
+ */
+@ApiStatus.Experimental
+@ApiStatus.NonExtendable
+public interface RegistryView<T> extends Iterable<T> {
+
+ @Nullable T get(final @NotNull Key key);
+
+ @NotNull T getOrThrow(final @NotNull Key key);
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryAdditionEvent.java b/src/main/java/io/papermc/paper/registry/event/RegistryAdditionEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..e70e0b79f300f6453b8f5a76defdb2300360f688
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryAdditionEvent.java
@@ -0,0 +1,33 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.TypedKey;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Event object for {@link RegistryEvents.Provider#addition()}. This
+ * event is fired right before a specific object is added to a registry.
+ * It provides a way for plugins to modify parts of this object.
+ *
+ * @param <T> object type
+ * @param <B> object builder type
+ */
+@ApiStatus.Experimental
+@ApiStatus.NonExtendable
+public interface RegistryAdditionEvent<T, B extends RegistryBuilder<T>> extends RegistryEvent<T> {
+
+ /**
+ * Gets the builder for the object being added to the registry.
+ *
+ * @return the object builder
+ */
+ @NotNull B builder();
+
+ /**
+ * Gets the key for this object in the registry.
+ *
+ * @return the key
+ */
+ @NotNull TypedKey<T> key();
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEvent.java b/src/main/java/io/papermc/paper/registry/event/RegistryEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4841b63fa4b6bf49d1e04339561d48ba8ae562d
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEvent.java
@@ -0,0 +1,41 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.RegistryView;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Base type for all registry events.
+ *
+ * @param <T> object type
+ */
+@ApiStatus.Experimental
+@ApiStatus.NonExtendable
+public interface RegistryEvent<T> extends LifecycleEvent {
+
+ /**
+ * Get the key for the registry this event pertains to.
+ *
+ * @return the registry key
+ */
+ @NotNull RegistryKey<T> registryKey();
+
+ /**
+ * Get a view of the registry which may or may not
+ * be complete based on the event.
+ *
+ * @return a registry view
+ */
+ @NotNull RegistryView<T> registry();
+
+ /**
+ * Get the name of the event.
+ *
+ * @return the event name
+ */
+ default @NotNull String eventName() {
+ return this.getClass().getSimpleName();
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEventProviderImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryEventProviderImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..0df562062e6468617597a49abf88e116dd693935
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventProviderImpl.java
@@ -0,0 +1,40 @@
+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.plugin.lifecycle.event.handler.LifecycleEventHandler;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfiguration;
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.ApiStatus;
+
+@ApiStatus.Internal
+@DefaultQualifier(NonNull.class)
+record RegistryEventProviderImpl<T, B extends RegistryBuilder<T>>(RegistryKey<T> registryKey) implements RegistryEvents.Provider<T, B> {
+
+ static <T, B extends RegistryBuilder<T>> RegistryEvents.Provider<T, B> create(final RegistryKey<T> registryKey) {
+ return new RegistryEventProviderImpl<>(registryKey);
+ }
+
+ @Override
+ public LifecycleEventType.Prioritizable<BootstrapContext, RegistryAdditionEvent<T, B>> addition() {
+ return RegistryEventTypeProvider.PROVIDER.registryAddition(this);
+ }
+
+ @Override
+ public PrioritizedLifecycleEventHandlerConfiguration<BootstrapContext> newAdditionHandler(final LifecycleEventHandler<? super RegistryAdditionEvent<T, B>> handler) {
+ return this.addition().newHandler(handler);
+ }
+
+ @Override
+ public LifecycleEventType.Prioritizable<BootstrapContext, RegistryPreFreezeEvent<T, B>> preFreeze() {
+ return RegistryEventTypeProvider.PROVIDER.registryPreFreeze(this);
+ }
+
+ @Override
+ public PrioritizedLifecycleEventHandlerConfiguration<BootstrapContext> newPreFreezeHandler(final LifecycleEventHandler<? super RegistryPreFreezeEvent<T, B>> handler) {
+ return this.preFreeze().newHandler(handler);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProvider.java b/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..118306c85e7c861a0c4010ba9e37f15ca49498a7
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProvider.java
@@ -0,0 +1,20 @@
+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.RegistryBuilder;
+import java.util.ServiceLoader;
+import org.jetbrains.annotations.ApiStatus;
+
+@ApiStatus.Internal
+interface RegistryEventTypeProvider {
+
+ RegistryEventTypeProvider PROVIDER = ServiceLoader.load(RegistryEventTypeProvider.class)
+ .findFirst()
+ .orElseThrow();
+
+
+ <T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryAdditionEvent<T, B>> registryAddition(RegistryEvents.Provider<T, B> type);
+
+ <T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryPreFreezeEvent<T, B>> registryPreFreeze(RegistryEvents.Provider<T, B> type);
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java b/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java
new file mode 100644
index 0000000000000000000000000000000000000000..0df2045dab9b19f5456a8946e0b827cbdcf31159
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java
@@ -0,0 +1,87 @@
+package io.papermc.paper.registry.event;
+
+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.LifecycleEventHandlerConfiguration;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfiguration;
+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+import static io.papermc.paper.registry.event.RegistryEventProviderImpl.create;
+
+/**
+ * Holds providers for {@link RegistryAdditionEvent} and {@link RegistryPreFreezeEvent}
+ * handlers for each applicable registry.
+ */
+@ApiStatus.Experimental
+public final class RegistryEvents {
+
+
+ /**
+ * Provider for each registry event type for a specific registry.
+ *
+ * @param <T> object type
+ * @param <B> object builder type
+ */
+ @ApiStatus.Experimental
+ @ApiStatus.NonExtendable
+ public interface Provider<T, B extends RegistryBuilder<T>> {
+
+ /**
+ * Gets the event type for {@link RegistryAdditionEvent} which is fired just before
+ * an object is added to a registry.
+ * <p>
+ * Can be used in {@link io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager#registerEventHandler(LifecycleEventType, LifecycleEventHandler)}
+ * to register a handler for {@link RegistryAdditionEvent}.
+ *
+ * @return the addition event type
+ */
+ LifecycleEventType.@NotNull Prioritizable<BootstrapContext, RegistryAdditionEvent<T, B>> addition();
+
+ /**
+ * Shortcut for calling {@link #addition()} followed by {@link LifecycleEventType#newHandler(LifecycleEventHandler)}.
+ * <p>
+ * Can be used in {@link io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager#registerEventHandler(LifecycleEventHandlerConfiguration)}
+ * to register a handler for {@link RegistryAdditionEvent}
+ *
+ * @param handler the event handler for {@link RegistryAdditionEvent}
+ * @return the configuration for further use
+ */
+ @NotNull PrioritizedLifecycleEventHandlerConfiguration<BootstrapContext> newAdditionHandler(@NotNull LifecycleEventHandler<? super RegistryAdditionEvent<T, B>> handler);
+
+ /**
+ * Gets the event type for {@link RegistryPreFreezeEvent} which is fired just before
+ * a registry is frozen. It allows for the registration of new objects.
+ * <p>
+ * Can be used in {@link io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager#registerEventHandler(LifecycleEventType, LifecycleEventHandler)}
+ * to register a handler for {@link RegistryPreFreezeEvent}.
+ *
+ * @return the pre-freeze event type
+ */
+ LifecycleEventType.@NotNull Prioritizable<BootstrapContext, RegistryPreFreezeEvent<T, B>> preFreeze();
+
+ /**
+ * Shortcut for calling {@link #preFreeze()} followed by {@link LifecycleEventType#newHandler(LifecycleEventHandler)}.
+ * <p>
+ * Can be used in {@link io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager#registerEventHandler(LifecycleEventHandlerConfiguration)}
+ * to register a handler for {@link RegistryPreFreezeEvent}
+ *
+ * @param handler the event handler for {@link RegistryPreFreezeEvent}
+ * @return the configuration for further use
+ */
+ @NotNull PrioritizedLifecycleEventHandlerConfiguration<BootstrapContext> newPreFreezeHandler(@NotNull LifecycleEventHandler<? super RegistryPreFreezeEvent<T, B>> handler);
+
+ /**
+ * Gets the registry key associated with this event type provider.
+ *
+ * @return the registry key
+ */
+ @NotNull RegistryKey<T> registryKey();
+ }
+
+ private RegistryEvents() {
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryPreFreezeEvent.java b/src/main/java/io/papermc/paper/registry/event/RegistryPreFreezeEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..8ca6cc14b544399e32477c5fbe8b58f3bb4f55c4
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryPreFreezeEvent.java
@@ -0,0 +1,27 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.registry.RegistryBuilder;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Event object for {@link RegistryEvents.Provider#preFreeze()}. This
+ * event is fired right before a registry is frozen disallowing further changes.
+ * It provides a way for plugins to add new objects to the registry.
+ *
+ * @param <T> object type
+ * @param <B> object builder type
+ */
+@ApiStatus.Experimental
+@ApiStatus.NonExtendable
+public interface RegistryPreFreezeEvent<T, B extends RegistryBuilder<T>> extends RegistryEvent<T> {
+
+ /**
+ * Get a view of the registry which supports
+ * the registering of new values.
+ *
+ * @return a writable registry view
+ */
+ @Override
+ @NotNull WritableRegistry<T, B> registry();
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java b/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..3cab150544f2bd7f8dd483f638817e5a36f462ed
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java
@@ -0,0 +1,27 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.RegistryView;
+import io.papermc.paper.registry.TypedKey;
+import java.util.function.Consumer;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * A registry view which supports registering new objects.
+ *
+ * @param <T> object type
+ * @param <B> object builder type
+ */
+@ApiStatus.NonExtendable
+@ApiStatus.Experimental
+public interface WritableRegistry<T, B extends RegistryBuilder<T>> extends RegistryView<T> {
+
+ /**
+ * Register a new value with the specified key.
+ *
+ * @param key the object's key (must be unique from others)
+ * @param value a consumer for the object's builder
+ */
+ void register(@NotNull TypedKey<T> key, @NotNull Consumer<? super B> value);
+}
diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java
index c63a2d5d21eb599939ec7ae5e57506ff746e4174..6aecb2d04479439d9d133f723ef160a693f699c6 100644
--- a/src/main/java/org/bukkit/Registry.java
+++ b/src/main/java/org/bukkit/Registry.java
@@ -331,6 +331,27 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
*/
@Nullable
T get(@NotNull NamespacedKey key);
+ // Paper start
+ /**
+ * Get the object by its key.
+ *
+ * @param key non-null key
+ * @return item or null if it does not exist
+ */
+ default @Nullable T get(final net.kyori.adventure.key.@NotNull Key key) {
+ return key instanceof final NamespacedKey nsKey ? this.get(nsKey) : this.get(new NamespacedKey(key.namespace(), key.value()));
+ }
+
+ /**
+ * Get the object by its typed key.
+ *
+ * @param typedKey non-null typed key
+ * @return item or null if it does not exist
+ */
+ default @Nullable T get(final io.papermc.paper.registry.@NotNull TypedKey<T> typedKey) {
+ return this.get(typedKey.key());
+ }
+ // Paper end
// Paper start - improve Registry
/**