From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Tue, 18 Jul 2023 14:47:02 -0700
Subject: [PATCH] Add Lifecycle Event system

This event system is separate from Bukkit's event system and is
meant for managing resources across reloads and from points in the
PluginBootstrap.

diff --git a/src/main/java/io/papermc/paper/plugin/bootstrap/BootstrapContext.java b/src/main/java/io/papermc/paper/plugin/bootstrap/BootstrapContext.java
index 4c47414fc08e1183b1e59369bacc4d7f7042f262..577a9d5aeae55a3b8452b6d873b51b30384c1fea 100644
--- a/src/main/java/io/papermc/paper/plugin/bootstrap/BootstrapContext.java
+++ b/src/main/java/io/papermc/paper/plugin/bootstrap/BootstrapContext.java
@@ -1,5 +1,7 @@
 package io.papermc.paper.plugin.bootstrap;
 
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
 import org.jetbrains.annotations.ApiStatus;
 import org.jspecify.annotations.NullMarked;
 
@@ -12,5 +14,13 @@ import org.jspecify.annotations.NullMarked;
 @ApiStatus.Experimental
 @NullMarked
 @ApiStatus.NonExtendable
-public interface BootstrapContext extends PluginProviderContext {
+public interface BootstrapContext extends PluginProviderContext, LifecycleEventOwner {
+
+    /**
+     * Get the lifecycle event manager for registering handlers
+     * for lifecycle events allowed on the {@link BootstrapContext}.
+     *
+     * @return the lifecycle event manager
+     */
+    LifecycleEventManager<BootstrapContext> getLifecycleManager();
 }
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEvent.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b8eafd3e79494d4a750cd9182387fbaead24011
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEvent.java
@@ -0,0 +1,17 @@
+package io.papermc.paper.plugin.lifecycle.event;
+
+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * Base type for all Lifecycle Events.
+ * <p>
+ * Lifecycle events are generally fired when the older
+ * event system is not available, like during early
+ * server initialization.
+ * @see LifecycleEvents
+ */
+@ApiStatus.Experimental
+@ApiStatus.NonExtendable
+public interface LifecycleEvent {
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventManager.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..e05cdb7ab166f92e270ea1b85e75f465878d05f2
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventManager.java
@@ -0,0 +1,53 @@
+package io.papermc.paper.plugin.lifecycle.event;
+
+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.types.LifecycleEventType;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Manages a plugin's lifecycle events. Can be obtained
+ * from {@link org.bukkit.plugin.Plugin} or {@link io.papermc.paper.plugin.bootstrap.BootstrapContext}.
+ *
+ * @param <O> the owning type, {@link org.bukkit.plugin.Plugin} or {@link io.papermc.paper.plugin.bootstrap.BootstrapContext}
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface LifecycleEventManager<O extends LifecycleEventOwner> {
+
+    /**
+     * Registers an event handler for a specific event type.
+     * <p>
+     * This is shorthand for creating a new {@link LifecycleEventHandlerConfiguration} and
+     * just passing in the {@link LifecycleEventHandler}.
+     * <pre>{@code
+     * LifecycleEventHandler<RegistrarEvent<Commands>> handler = new Handler();
+     * manager.registerEventHandler(LifecycleEvents.COMMANDS, handler);
+     * }</pre>
+     * is equivalent to
+     * <pre>{@code
+     * LifecycleEventHandler<RegistrarEvent<Commands>> handler = new Handler();
+     * manager.registerEventHandler(LifecycleEvents.COMMANDS.newHandler(handler));
+     * }</pre>
+     *
+     * @param eventType the event type to listen to
+     * @param eventHandler the handler for that event
+     * @param <E> the type of the event object
+     */
+    default <E extends LifecycleEvent> void registerEventHandler(final LifecycleEventType<? super O, ? extends E, ?> eventType, final LifecycleEventHandler<? super E> eventHandler) {
+        this.registerEventHandler(eventType.newHandler(eventHandler));
+    }
+
+    /**
+     * Registers an event handler configuration.
+     * <p>
+     * Configurations are created via {@link LifecycleEventType#newHandler(LifecycleEventHandler)}.
+     * Event types may have different configurations options available on the builder-like object
+     * returned by {@link LifecycleEventType#newHandler(LifecycleEventHandler)}.
+     *
+     * @param handlerConfiguration the handler configuration to register
+     */
+    void registerEventHandler(LifecycleEventHandlerConfiguration<? super O> handlerConfiguration);
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventOwner.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventOwner.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce5891eb110464a1c0cd7416712110851d010a1b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventOwner.java
@@ -0,0 +1,25 @@
+package io.papermc.paper.plugin.lifecycle.event;
+
+import io.papermc.paper.plugin.configuration.PluginMeta;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Implemented by types that are considered owners
+ * of registered handlers for lifecycle events. Generally
+ * the types that implement this interface also provide
+ * a {@link LifecycleEventManager} where you can register
+ * event handlers.
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface LifecycleEventOwner {
+
+    /**
+     * Get the plugin meta for this plugin.
+     *
+     * @return the plugin meta
+     */
+    PluginMeta getPluginMeta();
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/LifecycleEventHandler.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/LifecycleEventHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..3093ef23dd92f86240854065f7a7bb6c11ecf4fe
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/LifecycleEventHandler.java
@@ -0,0 +1,19 @@
+package io.papermc.paper.plugin.lifecycle.event.handler;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A handler for a specific event. Can be implemented
+ * in a concrete class or as a lambda.
+ *
+ * @param <E> the event
+ */
+@ApiStatus.Experimental
+@NullMarked
+@FunctionalInterface
+public interface LifecycleEventHandler<E extends LifecycleEvent> {
+
+    void run(E event);
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/LifecycleEventHandlerConfiguration.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/LifecycleEventHandlerConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..9b9f4655f222597b4e00519cfe128147bc438367
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/LifecycleEventHandlerConfiguration.java
@@ -0,0 +1,20 @@
+package io.papermc.paper.plugin.lifecycle.event.handler.configuration;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Base type for constructing configured event handlers for
+ * lifecycle events. Usually created via {@link io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType#newHandler(LifecycleEventHandler)}
+ * from event types in {@link io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents}
+ *
+ * @param <O>
+ */
+@SuppressWarnings("unused")
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface LifecycleEventHandlerConfiguration<O extends LifecycleEventOwner> {
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/MonitorLifecycleEventHandlerConfiguration.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/MonitorLifecycleEventHandlerConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..a2acc6e3867d6805c68e4c630aca3d14aa958a1d
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/MonitorLifecycleEventHandlerConfiguration.java
@@ -0,0 +1,27 @@
+package io.papermc.paper.plugin.lifecycle.event.handler.configuration;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Handler configuration for event types that allow "monitor" handlers.
+ *
+ * @param <O> the required owner type
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface MonitorLifecycleEventHandlerConfiguration<O extends LifecycleEventOwner> extends LifecycleEventHandlerConfiguration<O> {
+
+    /**
+     * Sets this handler configuration to be considered a "monitor".
+     * These handlers will run last and should only be used by plugins
+     * to observe changes from previously run handlers.
+     *
+     * @return this configuration for chaining
+     */
+    @Contract("-> this")
+    MonitorLifecycleEventHandlerConfiguration<O> monitor();
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/PrioritizedLifecycleEventHandlerConfiguration.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/PrioritizedLifecycleEventHandlerConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..100e5d169f1f644e54a042c697649f08fff1e6de
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/PrioritizedLifecycleEventHandlerConfiguration.java
@@ -0,0 +1,41 @@
+package io.papermc.paper.plugin.lifecycle.event.handler.configuration;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Handler configuration that allows both "monitor" and prioritized handlers.
+ * The default priority is 0.
+ *
+ * @param <O> the required owner type
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface PrioritizedLifecycleEventHandlerConfiguration<O extends LifecycleEventOwner> extends LifecycleEventHandlerConfiguration<O> {
+
+    /**
+     * Sets the priority for this handler. Resets
+     * all previous calls to {@link #monitor()}. A
+     * lower numeric value correlates to the handler
+     * being run earlier.
+     *
+     * @param priority the numerical priority
+     * @return this configuration for chaining
+     */
+    @Contract("_ -> this")
+    PrioritizedLifecycleEventHandlerConfiguration<O> priority(int priority);
+
+    /**
+     * Sets this handler configuration to be considered a "monitor".
+     * These handlers will run last and should only be used by plugins
+     * to observe any changes from previously ran handlers.
+     *
+     * @return this configuration for chaining
+     */
+    @Contract("-> this")
+    PrioritizedLifecycleEventHandlerConfiguration<O> monitor();
+
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/Registrar.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/Registrar.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd9c3605a8f5e6bdd31e42f18a45154d4074eb67
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/Registrar.java
@@ -0,0 +1,12 @@
+package io.papermc.paper.plugin.lifecycle.event.registrar;
+
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * To be implemented by types that provide ways to register types
+ * either on server start or during a reload
+ */
+@ApiStatus.Experimental
+@ApiStatus.NonExtendable
+public interface Registrar {
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/RegistrarEvent.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/RegistrarEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..7dca6be092a8b5deca9c45b152a96ffe72fe2533
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/RegistrarEvent.java
@@ -0,0 +1,28 @@
+package io.papermc.paper.plugin.lifecycle.event.registrar;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A lifecycle event that exposes a {@link Registrar} of some kind
+ * to allow management of various things. Look at implementations of
+ * {@link Registrar} for an idea of what uses this event.
+ *
+ * @param <R> registrar type
+ * @see ReloadableRegistrarEvent
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface RegistrarEvent<R extends Registrar> extends LifecycleEvent {
+
+    /**
+     * Get the registrar related to this event.
+     *
+     * @return the registrar
+     */
+    @Contract(pure = true)
+    R registrar();
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/ReloadableRegistrarEvent.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/ReloadableRegistrarEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..9bce1c13c8092238939fbbec6b499d1ca85e5b89
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/ReloadableRegistrarEvent.java
@@ -0,0 +1,39 @@
+package io.papermc.paper.plugin.lifecycle.event.registrar;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A lifecycle event that exposes a {@link Registrar} that is
+ * reloadable.
+ *
+ * @param <R> the registrar type
+ * @see RegistrarEvent
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface ReloadableRegistrarEvent<R extends Registrar> extends RegistrarEvent<R> {
+
+    /**
+     * Get the cause of this reload.
+     *
+     * @return the cause
+     */
+    @Contract(pure = true)
+    Cause cause();
+
+    @ApiStatus.Experimental
+    enum Cause {
+        /**
+         * The initial load of the server.
+         */
+        INITIAL,
+        /**
+         * A reload, triggered via one of the various mechanisms like
+         * the bukkit or minecraft reload commands.
+         */
+        RELOAD
+    }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventType.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventType.java
new file mode 100644
index 0000000000000000000000000000000000000000..75d9e20f53735ead4fa4aec478b4b72b85ca5e1e
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventType.java
@@ -0,0 +1,74 @@
+package io.papermc.paper.plugin.lifecycle.event.types;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+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.MonitorLifecycleEventHandlerConfiguration;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfiguration;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Base type for all types of lifecycle events. Differs from
+ * {@link LifecycleEvent} which is the actual event object, whereas
+ * this is an object representing the type of the event. Used
+ * to construct subtypes of {@link LifecycleEventHandlerConfiguration} for
+ * use in {@link LifecycleEventManager}
+ *
+ * @param <O> the required owner type
+ * @param <E> the event object type
+ * @param <C> the configuration type
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface LifecycleEventType<O extends LifecycleEventOwner, E extends LifecycleEvent, C extends LifecycleEventHandlerConfiguration<O>> {
+
+    /**
+     * Gets the name of the lifecycle event.
+     *
+     * @return the name
+     */
+    @Contract(pure = true)
+    String name();
+
+    /**
+     * Create a configuration for this event with the specified
+     * handler.
+     *
+     * @param handler the event handler
+     * @return a new configuration
+     * @see LifecycleEventManager#registerEventHandler(LifecycleEventHandlerConfiguration)
+     */
+    @Contract("_ -> new")
+    C newHandler(LifecycleEventHandler<? super E> handler);
+
+    /**
+     * Lifecycle event type that supports separate registration
+     * of handlers as "monitors" that are run last. Useful
+     * if a plugin wants to only observe the changes other handlers
+     * made.
+     *
+     * @param <O> the required owner type
+     * @param <E> the event object type
+     */
+    @ApiStatus.Experimental
+    @ApiStatus.NonExtendable
+    interface Monitorable<O extends LifecycleEventOwner, E extends LifecycleEvent> extends LifecycleEventType<O, E, MonitorLifecycleEventHandlerConfiguration<O>> {
+    }
+
+    /**
+     * Lifecycle event type that supports both {@link Monitorable "monitors"} and
+     * specific numeric-based priorities.
+     *
+     * @param <O> the required owner type
+     * @param <E> the event object type
+     */
+    @ApiStatus.Experimental
+    @ApiStatus.NonExtendable
+    interface Prioritizable<O extends LifecycleEventOwner, E extends LifecycleEvent> extends LifecycleEventType<O, E, PrioritizedLifecycleEventHandlerConfiguration<O>> {
+    }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProvider.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..e15e09c2a4d3f43db6a0159fa8af6179362ea8d6
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProvider.java
@@ -0,0 +1,24 @@
+package io.papermc.paper.plugin.lifecycle.event.types;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import java.util.Optional;
+import java.util.ServiceLoader;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Internal
+@NullMarked
+interface LifecycleEventTypeProvider {
+
+    Optional<LifecycleEventTypeProvider> INSTANCE = ServiceLoader.load(LifecycleEventTypeProvider.class)
+        .findFirst();
+
+    static LifecycleEventTypeProvider provider() {
+        return INSTANCE.orElseThrow();
+    }
+
+    <O extends LifecycleEventOwner, E extends LifecycleEvent> LifecycleEventType.Monitorable<O, E> monitor(String name, Class<? extends O> ownerType);
+
+    <O extends LifecycleEventOwner, E extends LifecycleEvent> LifecycleEventType.Prioritizable<O, E> prioritized(String name, Class<? extends O> ownerType);
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java
new file mode 100644
index 0000000000000000000000000000000000000000..f70814de0d6c40b2c1c9921b8abdd1162e1d3995
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java
@@ -0,0 +1,54 @@
+package io.papermc.paper.plugin.lifecycle.event.types;
+
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Holds various types of lifecycle events for
+ * use when creating event handler configurations
+ * in {@link LifecycleEventManager}.
+ */
+@ApiStatus.Experimental
+@NullMarked
+public final class LifecycleEvents {
+
+    //<editor-fold desc="helper methods" defaultstate="collapsed">
+    @ApiStatus.Internal
+    static <E extends LifecycleEvent> LifecycleEventType.Monitorable<Plugin, E> plugin(final String name) {
+        return monitor(name, Plugin.class);
+    }
+
+    @ApiStatus.Internal
+    static <E extends LifecycleEvent> LifecycleEventType.Prioritizable<Plugin, E> pluginPrioritized(final String name) {
+        return prioritized(name, Plugin.class);
+    }
+
+    @ApiStatus.Internal
+    static <E extends LifecycleEvent> LifecycleEventType.Monitorable<BootstrapContext, E> bootstrap(final String name) {
+        return monitor(name, BootstrapContext.class);
+    }
+
+    @ApiStatus.Internal
+    static <E extends LifecycleEvent> LifecycleEventType.Prioritizable<BootstrapContext, E> bootstrapPrioritized(final String name) {
+        return prioritized(name, BootstrapContext.class);
+    }
+
+    @ApiStatus.Internal
+    static <O extends LifecycleEventOwner, E extends LifecycleEvent, O2 extends O> LifecycleEventType.Monitorable<O, E> monitor(final String name, final Class<O2> ownerType) {
+        return LifecycleEventTypeProvider.provider().monitor(name, ownerType);
+    }
+
+    @ApiStatus.Internal
+    static <O extends LifecycleEventOwner, E extends LifecycleEvent> LifecycleEventType.Prioritizable<O, E> prioritized(final String name, final Class<? extends O> ownerType) {
+        return LifecycleEventTypeProvider.provider().prioritized(name, ownerType);
+    }
+    //</editor-fold>
+
+    private LifecycleEvents() {
+    }
+}
diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
index 94913f522d3c060c609f6ec7d7b0d92ea5587fc8..a0b02efdb3beed93cb1656e840f24cb98f5fd555 100644
--- a/src/main/java/org/bukkit/UnsafeValues.java
+++ b/src/main/java/org/bukkit/UnsafeValues.java
@@ -271,4 +271,12 @@ public interface UnsafeValues {
      */
     @Nullable org.bukkit.Color getSpawnEggLayerColor(org.bukkit.entity.EntityType entityType, int layer);
     // Paper end - spawn egg color visibility
+
+    // Paper start - lifecycle event API
+    /**
+     * @hidden
+     */
+    @org.jetbrains.annotations.ApiStatus.Internal
+    io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager<org.bukkit.plugin.Plugin> createPluginLifecycleEventManager(final org.bukkit.plugin.java.JavaPlugin plugin, final java.util.function.BooleanSupplier registrationCheck);
+    // Paper end - lifecycle event API
 }
diff --git a/src/main/java/org/bukkit/plugin/Plugin.java b/src/main/java/org/bukkit/plugin/Plugin.java
index 46fc37a36403c8fbc4c0c9f863d4d57eb3896bd4..0ff8b53f900092dc419d61a8ede0a7cd72a2e1e1 100644
--- a/src/main/java/org/bukkit/plugin/Plugin.java
+++ b/src/main/java/org/bukkit/plugin/Plugin.java
@@ -16,7 +16,7 @@ import org.jetbrains.annotations.Nullable;
  * <p>
  * The use of {@link PluginBase} is recommended for actual Implementation
  */
-public interface Plugin extends TabExecutor {
+public interface Plugin extends TabExecutor, io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner { // Paper
     /**
      * Returns the folder that the plugin data files are located in. The
      * folder may not yet exist.
@@ -224,4 +224,14 @@ public interface Plugin extends TabExecutor {
      */
     @NotNull
     public String getName();
+
+    // Paper start - lifecycle events
+    /**
+     * Get the lifecycle event manager for registering handlers
+     * for lifecycle events allowed on the {@link Plugin}.
+     *
+     * @return the lifecycle event manager
+     */
+    io.papermc.paper.plugin.lifecycle.event.@NotNull LifecycleEventManager<Plugin> getLifecycleManager();
+    // Paper end - lifecycle events
 }
diff --git a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
index 2d64fc065d53dcd8c01d05215c3e63aaf4428177..e0203f199700c397961a0667a79792497da7f796 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
@@ -48,6 +48,11 @@ public abstract class JavaPlugin extends PluginBase {
     private FileConfiguration newConfig = null;
     private File configFile = null;
     private Logger logger = null; // Paper - PluginLogger -> Logger
+    // Paper start - lifecycle events
+    @SuppressWarnings("deprecation")
+    private final io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager<org.bukkit.plugin.Plugin> lifecycleEventManager = org.bukkit.Bukkit.getUnsafe().createPluginLifecycleEventManager(this, () -> this.allowsLifecycleRegistration);
+    private boolean allowsLifecycleRegistration = true;
+    // Paper end
 
     public JavaPlugin() {
         // Paper start
@@ -279,7 +284,9 @@ public abstract class JavaPlugin extends PluginBase {
             isEnabled = enabled;
 
             if (isEnabled) {
+                try { // Paper - lifecycle events
                 onEnable();
+                } finally { this.allowsLifecycleRegistration = false; } // Paper - lifecycle events
             } else {
                 onDisable();
             }
@@ -457,4 +464,11 @@ public abstract class JavaPlugin extends PluginBase {
         }
         return plugin;
     }
+
+    // Paper start - lifecycle events
+    @Override
+    public final io.papermc.paper.plugin.lifecycle.event.@NotNull LifecycleEventManager<org.bukkit.plugin.Plugin> getLifecycleManager() {
+        return this.lifecycleEventManager;
+    }
+    // Paper end - lifecycle events
 }
diff --git a/src/test/java/org/bukkit/plugin/TestPlugin.java b/src/test/java/org/bukkit/plugin/TestPlugin.java
index 43b58e920e739bb949ac0673e9ef73ba7b500dc9..affe88cf8e98a787e197936f5fc443464a2343c6 100644
--- a/src/test/java/org/bukkit/plugin/TestPlugin.java
+++ b/src/test/java/org/bukkit/plugin/TestPlugin.java
@@ -133,4 +133,11 @@ public class TestPlugin extends PluginBase {
     public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
         throw new UnsupportedOperationException("Not supported.");
     }
+
+    // Paper start - lifecycle events
+    @Override
+    public io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager<Plugin> getLifecycleManager() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+    // Paper end - lifecycle events
 }