aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJake Potrebic <[email protected]>2024-02-09 12:12:01 -0800
committerGitHub <[email protected]>2024-02-09 12:12:01 -0800
commit934cd77da81b452770530c7690393648e1bb620b (patch)
treeab273d8f255cd080c16efeed3d9a3232f4cd3b01
parent9e171ef8ff0a0ec57ebc75772fc9de578c987059 (diff)
downloadPaper-934cd77da81b452770530c7690393648e1bb620b.tar.gz
Paper-934cd77da81b452770530c7690393648e1bb620b.zip
Lifecycle Event System (#9629)
* registering stuff event system * simply by removing a ton of unneeded generics * separate RegistryEvent and RegistrarEvent * add logic for removing hooks when a plugin is disabled * cleanup more * swap around the way things are registered * block further hook registrations for JavaPlugin * Slightly more extensible to support registry mod API * rename some types * more moving/renaming * remove 'hook' name * Rename to 'lifecycle' * move more impls for the server * add priorities * added lock for bootstrap event registration * slight refactor to allow 'register anywhere' event types * Move event type list to server impl * use builder pattern to create event handler configurations * add some more javadocs * fix some issues, slight refactors * call predicate and method renames * add owner aware events * rebased and refactored owner aware events * add single helper method for registering simple handler * compile fixes * check owner and fix generics on register helper * javadoc fixes and a few type renames * more javadoc fixes * move service loader file to correct location * rename to Monitorable and Prioritizable * add invalidation system for events after running them * block reloading plugins in certain situations * update test plugin * remove dummy events * rebase
-rw-r--r--patches/api/0459-Add-Lifecycle-Event-system.patch628
-rw-r--r--patches/server/1046-Add-Lifecycle-Event-system.patch781
2 files changed, 1409 insertions, 0 deletions
diff --git a/patches/api/0459-Add-Lifecycle-Event-system.patch b/patches/api/0459-Add-Lifecycle-Event-system.patch
new file mode 100644
index 0000000000..2285a3eaa9
--- /dev/null
+++ b/patches/api/0459-Add-Lifecycle-Event-system.patch
@@ -0,0 +1,628 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+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 08f2050356acaf74e3210416760e3873c2dafd2c..37dfdcfcbd14947e0550e7528aca68f452e53eb6 100644
+--- a/src/main/java/io/papermc/paper/plugin/bootstrap/BootstrapContext.java
++++ b/src/main/java/io/papermc/paper/plugin/bootstrap/BootstrapContext.java
+@@ -1,6 +1,9 @@
+ 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.jetbrains.annotations.NotNull;
+
+ /**
+ * Represents the context provided to a {@link PluginBootstrap} during both the bootstrapping and plugin
+@@ -10,5 +13,13 @@ import org.jetbrains.annotations.ApiStatus;
+ */
+ @ApiStatus.Experimental
+ @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
++ */
++ @NotNull 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
++ */
++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..3626ce3da17f20ec44f0c15baa13f40e1dc2bc9c
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventManager.java
+@@ -0,0 +1,52 @@
++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.jetbrains.annotations.NotNull;
++
++/**
++ * 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}
++ */
++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 @NotNull LifecycleEventType<? super O, ? extends E, ?> eventType, final @NotNull 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(@NotNull 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..1160474f94476b580426cec29756c4699e163bf7
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventOwner.java
+@@ -0,0 +1,24 @@
++package io.papermc.paper.plugin.lifecycle.event;
++
++import io.papermc.paper.plugin.configuration.PluginMeta;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * 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.
++ */
++public interface LifecycleEventOwner {
++
++ /**
++ * Get the plugin meta for this plugin.
++ *
++ * @return the plugin meta
++ */
++ @NotNull 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..8239ba3c0147c0e8e8d28987d3f543a67641892a
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/LifecycleEventHandler.java
+@@ -0,0 +1,18 @@
++package io.papermc.paper.plugin.lifecycle.event.handler;
++
++import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * A handler for a specific event. Can be implemented
++ * in a concrete class or as a lambda.
++ *
++ * @param <E> the event
++ */
++@FunctionalInterface
++public interface LifecycleEventHandler<E extends LifecycleEvent> {
++
++ void run(@NotNull 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..0831794fad1f6eb8960225909d40f4a3b20a2a3b
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/LifecycleEventHandlerConfiguration.java
+@@ -0,0 +1,18 @@
++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;
++
++/**
++ * 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")
++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..d307ede51a66279f2eeef4e5b41c71779503f0d4
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/MonitorLifecycleEventHandlerConfiguration.java
+@@ -0,0 +1,25 @@
++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;
++
++/**
++ * Handler configuration for event types that allow "monitor" handlers.
++ *
++ * @param <O> the required owner type
++ */
++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..1c404df0be359ceac7fb52fec03027c771395e07
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/PrioritizedLifecycleEventHandlerConfiguration.java
+@@ -0,0 +1,39 @@
++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;
++
++/**
++ * Handler configuration that allows both "monitor" and prioritized handlers.
++ * The default priority is 0.
++ *
++ * @param <O> the required owner type
++ */
++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
++ */
++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..2e5758d1af6215f33f89b12984a5594df592147f
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/RegistrarEvent.java
+@@ -0,0 +1,27 @@
++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.jetbrains.annotations.NotNull;
++
++/**
++ * 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
++ */
++public interface RegistrarEvent<R extends Registrar> extends LifecycleEvent {
++
++ /**
++ * Get the registrar related to this event.
++ *
++ * @return the registrar
++ */
++ @Contract(pure = true)
++ @NotNull 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..b8b439bdad2e47c7c715fe30e0c1e69aa25374dd
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/ReloadableRegistrarEvent.java
+@@ -0,0 +1,38 @@
++package io.papermc.paper.plugin.lifecycle.event.registrar;
++
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.Contract;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * A lifecycle event that exposes a {@link Registrar} that is
++ * reloadable.
++ *
++ * @param <R> the registrar type
++ * @see RegistrarEvent
++ */
++public interface ReloadableRegistrarEvent<R extends Registrar> extends RegistrarEvent<R> {
++
++ /**
++ * Get the cause of this reload.
++ *
++ * @return the cause
++ */
++ @Contract(pure = true)
++ @NotNull 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..92ea0374079a228ccc59c00fcf58abff2f6c46fe
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventType.java
+@@ -0,0 +1,73 @@
++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.jetbrains.annotations.NotNull;
++
++/**
++ * 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
++ */
++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)
++ @NotNull 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")
++ @NotNull C newHandler(@NotNull 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..1588f6943a909bed053a952e650e043c44028c2d
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProvider.java
+@@ -0,0 +1,18 @@
++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.ServiceLoader;
++import org.jetbrains.annotations.ApiStatus;
++
++interface LifecycleEventTypeProvider {
++
++ LifecycleEventTypeProvider PROVIDER = ServiceLoader.load(LifecycleEventTypeProvider.class)
++ .findFirst()
++ .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..304f978e40e1759bb19704cc5cec399500905195
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java
+@@ -0,0 +1,52 @@
++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;
++
++/**
++ * Holds various types of lifecycle events for
++ * use when creating event handler configurations
++ * in {@link LifecycleEventManager}.
++ */
++public final class LifecycleEvents {
++
++ //<editor-fold desc="helper methods" defaultstate="collapsed">
++ @ApiStatus.Internal
++ private static <E extends LifecycleEvent> LifecycleEventType.Monitorable<Plugin, E> plugin(final String name) {
++ return monitor(name, Plugin.class);
++ }
++
++ @ApiStatus.Internal
++ private static <E extends LifecycleEvent> LifecycleEventType.Prioritizable<Plugin, E> pluginPrioritized(final String name) {
++ return prioritized(name, Plugin.class);
++ }
++
++ @ApiStatus.Internal
++ private static <E extends LifecycleEvent> LifecycleEventType.Monitorable<BootstrapContext, E> bootstrap(final String name) {
++ return monitor(name, BootstrapContext.class);
++ }
++
++ @ApiStatus.Internal
++ private static <E extends LifecycleEvent> LifecycleEventType.Prioritizable<BootstrapContext, E> bootstrapPrioritized(final String name) {
++ return prioritized(name, BootstrapContext.class);
++ }
++
++ @ApiStatus.Internal
++ private 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
++ private 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 923d8655a84e26960d35d8dc6e4ebc0b10c295d5..890c07cfc2e64a52752e96d518578b5eb1afbd19 100644
+--- a/src/main/java/org/bukkit/UnsafeValues.java
++++ b/src/main/java/org/bukkit/UnsafeValues.java
+@@ -273,4 +273,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 4eb639fbb46a0848be207149ea433455550fae1c..ef431219fd2bce48bad63b6b92c99d54348d480e 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's 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 5cd236965de12392d8c7aa81307c0ff1cc8673b1..34037d3da2c536bac088e0ff629ee8f1daccc65b 100644
+--- a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
++++ b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
+@@ -47,6 +47,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
+@@ -278,7 +283,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();
+ }
+@@ -456,4 +463,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
+ }
diff --git a/patches/server/1046-Add-Lifecycle-Event-system.patch b/patches/server/1046-Add-Lifecycle-Event-system.patch
new file mode 100644
index 0000000000..cd457d61d5
--- /dev/null
+++ b/patches/server/1046-Add-Lifecycle-Event-system.patch
@@ -0,0 +1,781 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Tue, 18 Jul 2023 17:49:38 -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/PluginBootstrapContextImpl.java b/src/main/java/io/papermc/paper/plugin/bootstrap/PluginBootstrapContextImpl.java
+index 30b50e6294c6eaade5e17cfaf34600d122e6251c..0bb7694188d5fb75bb756ce75d0060ea980027ee 100644
+--- a/src/main/java/io/papermc/paper/plugin/bootstrap/PluginBootstrapContextImpl.java
++++ b/src/main/java/io/papermc/paper/plugin/bootstrap/PluginBootstrapContextImpl.java
+@@ -1,6 +1,8 @@
+ package io.papermc.paper.plugin.bootstrap;
+
+ import io.papermc.paper.plugin.configuration.PluginMeta;
++import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
++import io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEventManager;
+ import io.papermc.paper.plugin.provider.PluginProvider;
+ import java.nio.file.Path;
+ import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
+@@ -12,6 +14,10 @@ public final class PluginBootstrapContextImpl implements BootstrapContext {
+ private final Path dataFolder;
+ private final ComponentLogger logger;
+ private final Path pluginSource;
++ // Paper start - lifecycle events
++ private boolean allowsLifecycleRegistration = true;
++ private final PaperLifecycleEventManager<BootstrapContext> lifecycleEventManager = new PaperLifecycleEventManager<>(this, () -> this.allowsLifecycleRegistration); // Paper - lifecycle events
++ // Paper end - lifecycle events
+
+ public PluginBootstrapContextImpl(PluginMeta config, Path dataFolder, ComponentLogger logger, Path pluginSource) {
+ this.config = config;
+@@ -45,4 +51,20 @@ public final class PluginBootstrapContextImpl implements BootstrapContext {
+ public @NotNull Path getPluginSource() {
+ return this.pluginSource;
+ }
++
++ // Paper start - lifecycle event system
++ @Override
++ public @NotNull PluginMeta getPluginMeta() {
++ return this.config;
++ }
++
++ @Override
++ public LifecycleEventManager<BootstrapContext> getLifecycleManager() {
++ return this.lifecycleEventManager;
++ }
++
++ public void lockLifecycleEventRegistration() {
++ this.allowsLifecycleRegistration = false;
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f84c9c80e701231e5c33ac3c5573f1093e80f38b
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java
+@@ -0,0 +1,110 @@
++package io.papermc.paper.plugin.lifecycle.event;
++
++import com.google.common.base.Suppliers;
++import com.mojang.logging.LogUtils;
++import io.papermc.paper.plugin.bootstrap.BootstrapContext;
++import io.papermc.paper.plugin.lifecycle.event.registrar.PaperRegistrar;
++import io.papermc.paper.plugin.lifecycle.event.registrar.RegistrarEvent;
++import io.papermc.paper.plugin.lifecycle.event.registrar.RegistrarEventImpl;
++import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent;
++import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
++import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
++import io.papermc.paper.plugin.lifecycle.event.types.OwnerAwareLifecycleEvent;
++import java.util.ArrayList;
++import java.util.List;
++import java.util.Set;
++import java.util.function.Predicate;
++import java.util.function.Supplier;
++import org.bukkit.plugin.Plugin;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.checkerframework.framework.qual.DefaultQualifier;
++import org.slf4j.Logger;
++
++@DefaultQualifier(NonNull.class)
++public class LifecycleEventRunner {
++
++ private static final Logger LOGGER = LogUtils.getClassLogger();
++ private static final Supplier<Set<LifecycleEventType<?, ?, ?>>> BLOCKS_RELOADING = Suppliers.memoize(() -> Set.of( // lazy due to cyclic initialization
++ ));
++ public static final LifecycleEventRunner INSTANCE = new LifecycleEventRunner();
++
++ private final List<LifecycleEventType<?, ?, ?>> lifecycleEventTypes = new ArrayList<>();
++ private boolean blockPluginReloading = false;
++
++ public void checkRegisteredHandler(final LifecycleEventOwner owner, final LifecycleEventType<?, ?, ?> eventType) {
++ /*
++ Lifecycle event handlers for reloadable events that are registered from the BootstrapContext prevent
++ the server from reloading plugins. This is because reloading plugins requires disabling all the plugins,
++ running the reload logic (which would include places where these events should fire) and then re-enabling plugins.
++ */
++ if (owner instanceof BootstrapContext && BLOCKS_RELOADING.get().contains(eventType)) {
++ this.blockPluginReloading = true;
++ }
++ }
++
++ public boolean blocksPluginReloading() {
++ return this.blockPluginReloading;
++ }
++
++ public <O extends LifecycleEventOwner, E extends LifecycleEvent, ET extends LifecycleEventType<O, E, ?>> ET addEventType(final ET eventType) {
++ this.lifecycleEventTypes.add(eventType);
++ return eventType;
++ }
++
++ public <O extends LifecycleEventOwner, E extends PaperLifecycleEvent> void callEvent(final LifecycleEventType<O, ? super E, ?> eventType, final E event) {
++ this.callEvent(eventType, event, $ -> true);
++ }
++
++ public <O extends LifecycleEventOwner, E extends PaperLifecycleEvent> void callEvent(final LifecycleEventType<O, ? super E, ?> eventType, final E event, final Predicate<? super O> ownerPredicate) {
++ final AbstractLifecycleEventType<O, ? super E, ?, ?> lifecycleEventType = (AbstractLifecycleEventType<O, ? super E, ?, ?>) eventType;
++ lifecycleEventType.forEachHandler(registeredHandler -> {
++ try {
++ if (event instanceof final OwnerAwareLifecycleEvent<?> ownerAwareEvent) {
++ ownerAwareGenericHelper(ownerAwareEvent, registeredHandler.owner());
++ }
++ registeredHandler.lifecycleEventHandler().run(event);
++ } catch (final Throwable ex) {
++ LOGGER.error("Could not run '{}' lifecycle event handler from {}", lifecycleEventType.name(), registeredHandler.owner().getPluginMeta().getDisplayName(), ex);
++ } finally {
++ if (event instanceof final OwnerAwareLifecycleEvent<?> ownerAwareEvent) {
++ ownerAwareEvent.setOwner(null);
++ }
++ }
++ }, handler -> ownerPredicate.test(handler.owner()));
++ event.invalidate();
++ }
++
++ private static <O extends LifecycleEventOwner> void ownerAwareGenericHelper(final OwnerAwareLifecycleEvent<O> event, final LifecycleEventOwner possibleOwner) {
++ final @Nullable O owner = event.castOwner(possibleOwner);
++ if (owner != null) {
++ event.setOwner(owner);
++ } else {
++ throw new IllegalStateException("Found invalid owner " + possibleOwner + " for event " + event);
++ }
++ }
++
++ public void unregisterAllEventHandlersFor(final Plugin plugin) {
++ for (final LifecycleEventType<?, ?, ?> lifecycleEventType : this.lifecycleEventTypes) {
++ this.removeEventHandlersOwnedBy(lifecycleEventType, plugin);
++ }
++ }
++
++ private <O extends LifecycleEventOwner> void removeEventHandlersOwnedBy(final LifecycleEventType<O, ?, ?> eventType, final Plugin possibleOwner) {
++ final AbstractLifecycleEventType<O, ?, ?, ?> lifecycleEventType = (AbstractLifecycleEventType<O, ?, ?, ?>) eventType;
++ lifecycleEventType.removeMatching(registeredHandler -> registeredHandler.owner().getPluginMeta().getName().equals(possibleOwner.getPluginMeta().getName()));
++ }
++
++ @SuppressWarnings("unchecked")
++ public <O extends LifecycleEventOwner, R extends PaperRegistrar<? super O>> void callStaticRegistrarEvent(final LifecycleEventType<O, ? extends RegistrarEvent<? super R>, ?> lifecycleEventType, final R registrar, final Class<? extends O> ownerClass) {
++ this.callEvent((LifecycleEventType<O, RegistrarEvent<? super R>, ?>) lifecycleEventType, new RegistrarEventImpl<>(registrar, ownerClass), ownerClass::isInstance);
++ }
++
++ @SuppressWarnings("unchecked")
++ public <O extends LifecycleEventOwner, R extends PaperRegistrar<? super O>> void callReloadableRegistrarEvent(final LifecycleEventType<O, ? extends ReloadableRegistrarEvent<? super R>, ?> lifecycleEventType, final R registrar, final Class<? extends O> ownerClass, final ReloadableRegistrarEvent.Cause cause) {
++ this.callEvent((LifecycleEventType<O, ReloadableRegistrarEvent<? super R>, ?>) lifecycleEventType, new RegistrarEventImpl.ReloadableImpl<>(registrar, ownerClass, cause), ownerClass::isInstance);
++ }
++
++ private LifecycleEventRunner() {
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/PaperLifecycleEvent.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/PaperLifecycleEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e941405269a773e8a77e26ffd1afd84f53fadff5
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/PaperLifecycleEvent.java
+@@ -0,0 +1,10 @@
++package io.papermc.paper.plugin.lifecycle.event;
++
++public interface PaperLifecycleEvent extends LifecycleEvent {
++
++ // called after all handlers have been run. Can be
++ // used to invalid various contexts to plugins can't
++ // try to re-use them by storing them from the event
++ default void invalidate() {
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/PaperLifecycleEventManager.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/PaperLifecycleEventManager.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f1be5b9a29435bae0afd2bd951bfe88d1669e7eb
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/PaperLifecycleEventManager.java
+@@ -0,0 +1,26 @@
++package io.papermc.paper.plugin.lifecycle.event;
++
++import com.google.common.base.Preconditions;
++import io.papermc.paper.plugin.lifecycle.event.handler.configuration.AbstractLifecycleEventHandlerConfiguration;
++import io.papermc.paper.plugin.lifecycle.event.handler.configuration.LifecycleEventHandlerConfiguration;
++import java.util.function.BooleanSupplier;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public final class PaperLifecycleEventManager<O extends LifecycleEventOwner> implements LifecycleEventManager<O> {
++
++ private final O owner;
++ public final BooleanSupplier registrationCheck;
++
++ public PaperLifecycleEventManager(final O owner, final BooleanSupplier registrationCheck) {
++ this.owner = owner;
++ this.registrationCheck = registrationCheck;
++ }
++
++ @Override
++ public void registerEventHandler(final LifecycleEventHandlerConfiguration<? super O> handlerConfiguration) {
++ Preconditions.checkState(this.registrationCheck.getAsBoolean(), "Cannot register lifecycle event handlers");
++ ((AbstractLifecycleEventHandlerConfiguration<? super O, ?, ?>) handlerConfiguration).registerFrom(this.owner);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/AbstractLifecycleEventHandlerConfiguration.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/AbstractLifecycleEventHandlerConfiguration.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..6a85a4f581612efff04c1a955493aa2e32476277
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/AbstractLifecycleEventHandlerConfiguration.java
+@@ -0,0 +1,26 @@
++package io.papermc.paper.plugin.lifecycle.event.handler.configuration;
++
++import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
++import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
++import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
++import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public abstract class AbstractLifecycleEventHandlerConfiguration<O extends LifecycleEventOwner, E extends LifecycleEvent, CI extends AbstractLifecycleEventHandlerConfiguration<O, E, CI>> implements LifecycleEventHandlerConfiguration<O> {
++
++ private final LifecycleEventHandler<? super E> handler;
++ private final AbstractLifecycleEventType<O, E, ?, CI> type;
++
++ protected AbstractLifecycleEventHandlerConfiguration(final LifecycleEventHandler<? super E> handler, final AbstractLifecycleEventType<O, E, ?, CI> type) {
++ this.handler = handler;
++ this.type = type;
++ }
++
++ public abstract CI config();
++
++ public final void registerFrom(final O owner) {
++ this.type.tryRegister(owner, this.handler, this.config());
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/MonitorLifecycleEventHandlerConfigurationImpl.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/MonitorLifecycleEventHandlerConfigurationImpl.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e0699fcd0a098abc5e1206e7c0fa80b96eca7884
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/MonitorLifecycleEventHandlerConfigurationImpl.java
+@@ -0,0 +1,33 @@
++package io.papermc.paper.plugin.lifecycle.event.handler.configuration;
++
++import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
++import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
++import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
++import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public class MonitorLifecycleEventHandlerConfigurationImpl<O extends LifecycleEventOwner, E extends LifecycleEvent> extends AbstractLifecycleEventHandlerConfiguration<O, E, MonitorLifecycleEventHandlerConfigurationImpl<O, E>> implements MonitorLifecycleEventHandlerConfiguration<O> {
++
++ private boolean monitor = false;
++
++ public MonitorLifecycleEventHandlerConfigurationImpl(final LifecycleEventHandler<? super E> handler, final AbstractLifecycleEventType<O, E, ?, MonitorLifecycleEventHandlerConfigurationImpl<O, E>> eventType) {
++ super(handler, eventType);
++ }
++
++ @Override
++ public MonitorLifecycleEventHandlerConfigurationImpl<O, E> config() {
++ return this;
++ }
++
++ public boolean isMonitor() {
++ return this.monitor;
++ }
++
++ @Override
++ public MonitorLifecycleEventHandlerConfiguration<O> monitor() {
++ this.monitor = true;
++ return this;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/PrioritizedLifecycleEventHandlerConfigurationImpl.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/PrioritizedLifecycleEventHandlerConfigurationImpl.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c1d0070fc1594f7a7c29d7dc679da7b347a7140b
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/PrioritizedLifecycleEventHandlerConfigurationImpl.java
+@@ -0,0 +1,43 @@
++package io.papermc.paper.plugin.lifecycle.event.handler.configuration;
++
++import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
++import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
++import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
++import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
++import java.util.OptionalInt;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public class PrioritizedLifecycleEventHandlerConfigurationImpl<O extends LifecycleEventOwner, E extends LifecycleEvent> extends AbstractLifecycleEventHandlerConfiguration<O, E, PrioritizedLifecycleEventHandlerConfigurationImpl<O, E>> implements PrioritizedLifecycleEventHandlerConfiguration<O> {
++
++ private static final OptionalInt DEFAULT_PRIORITY = OptionalInt.of(0);
++ private static final OptionalInt MONITOR_PRIORITY = OptionalInt.empty();
++
++ private OptionalInt priority = DEFAULT_PRIORITY;
++
++ public PrioritizedLifecycleEventHandlerConfigurationImpl(final LifecycleEventHandler<? super E> handler, final AbstractLifecycleEventType<O, E, ?, PrioritizedLifecycleEventHandlerConfigurationImpl<O, E>> eventType) {
++ super(handler, eventType);
++ }
++
++ @Override
++ public PrioritizedLifecycleEventHandlerConfigurationImpl<O, E> config() {
++ return this;
++ }
++
++ public OptionalInt priority() {
++ return this.priority;
++ }
++
++ @Override
++ public PrioritizedLifecycleEventHandlerConfiguration<O> priority(final int priority) {
++ this.priority = OptionalInt.of(priority);
++ return this;
++ }
++
++ @Override
++ public PrioritizedLifecycleEventHandlerConfiguration<O> monitor() {
++ this.priority = MONITOR_PRIORITY;
++ return this;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/PaperRegistrar.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/PaperRegistrar.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..b2586c881988fbabe07eef1b43eb1b55f2d3fa52
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/PaperRegistrar.java
+@@ -0,0 +1,15 @@
++package io.papermc.paper.plugin.lifecycle.event.registrar;
++
++import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public interface PaperRegistrar<O extends LifecycleEventOwner> extends Registrar {
++
++ void setCurrentContext(@Nullable O owner);
++
++ default void invalidate() {
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/RegistrarEventImpl.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/RegistrarEventImpl.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..6d530c52aaf0dc2cdfe3bd56af557274a7f44256
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/RegistrarEventImpl.java
+@@ -0,0 +1,70 @@
++package io.papermc.paper.plugin.lifecycle.event.registrar;
++
++import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
++import io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEvent;
++import io.papermc.paper.plugin.lifecycle.event.types.OwnerAwareLifecycleEvent;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public class RegistrarEventImpl<R extends PaperRegistrar<? super O>, O extends LifecycleEventOwner> implements PaperLifecycleEvent, OwnerAwareLifecycleEvent<O>, RegistrarEvent<R> {
++
++ private final R registrar;
++ private final Class<? extends O> ownerClass;
++
++ public RegistrarEventImpl(final R registrar, final Class<? extends O> ownerClass) {
++ this.registrar = registrar;
++ this.ownerClass = ownerClass;
++ }
++
++ @Override
++ public R registrar() {
++ return this.registrar;
++ }
++
++ @Override
++ public final void setOwner(final @Nullable O owner) {
++ this.registrar.setCurrentContext(owner);
++ }
++
++ @Override
++ public final @Nullable O castOwner(final LifecycleEventOwner owner) {
++ return this.ownerClass.isInstance(owner) ? this.ownerClass.cast(owner) : null;
++ }
++
++ @Override
++ public void invalidate() {
++ this.registrar.invalidate();
++ }
++
++ @Override
++ public String toString() {
++ return "RegistrarEventImpl{" +
++ "registrar=" + this.registrar +
++ ", ownerClass=" + this.ownerClass +
++ '}';
++ }
++
++ public static class ReloadableImpl<R extends PaperRegistrar<? super O>, O extends LifecycleEventOwner> extends RegistrarEventImpl<R, O> implements ReloadableRegistrarEvent<R> {
++
++ private final ReloadableRegistrarEvent.Cause cause;
++
++ public ReloadableImpl(final R registrar, final Class<? extends O> ownerClass, final Cause cause) {
++ super(registrar, ownerClass);
++ this.cause = cause;
++ }
++
++ @Override
++ public Cause cause() {
++ return this.cause;
++ }
++
++ @Override
++ public String toString() {
++ return "ReloadableImpl{" +
++ "cause=" + this.cause +
++ "} " + super.toString();
++ }
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/AbstractLifecycleEventType.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/AbstractLifecycleEventType.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a65fb37f4a729e2fe9fb81af822db626ec7e6d7b
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/AbstractLifecycleEventType.java
+@@ -0,0 +1,50 @@
++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 io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner;
++import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
++import io.papermc.paper.plugin.lifecycle.event.handler.configuration.AbstractLifecycleEventHandlerConfiguration;
++import io.papermc.paper.plugin.lifecycle.event.handler.configuration.LifecycleEventHandlerConfiguration;
++import java.util.function.Consumer;
++import java.util.function.Predicate;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public abstract class AbstractLifecycleEventType<O extends LifecycleEventOwner, E extends LifecycleEvent, C extends LifecycleEventHandlerConfiguration<O>, CI extends AbstractLifecycleEventHandlerConfiguration<O, E, CI>> implements LifecycleEventType<O, E, C> {
++
++ private final String name;
++ private final Class<? extends O> ownerType;
++
++ protected AbstractLifecycleEventType(final String name, final Class<? extends O> ownerType) {
++ this.name = name;
++ this.ownerType = ownerType;
++ }
++
++ @Override
++ public String name() {
++ return this.name;
++ }
++
++ private void verifyOwner(final O owner) {
++ if (!this.ownerType.isInstance(owner)) {
++ throw new IllegalArgumentException("You cannot register the lifecycle event '" + this.name + "' on " + owner);
++ }
++ }
++
++ public abstract void forEachHandler(Consumer<? super RegisteredHandler<O, E>> consumer, Predicate<? super RegisteredHandler<O, E>> predicate);
++
++ public abstract void removeMatching(Predicate<? super RegisteredHandler<O, E>> predicate);
++
++ protected abstract void register(O owner, LifecycleEventHandler<? super E> handler, CI config);
++
++ public final void tryRegister(final O owner, final LifecycleEventHandler<? super E> handler, final CI config) {
++ this.verifyOwner(owner);
++ LifecycleEventRunner.INSTANCE.checkRegisteredHandler(owner, this);
++ this.register(owner, handler, config);
++ }
++
++ public record RegisteredHandler<O, E extends LifecycleEvent>(O owner, LifecycleEventHandler<? super E> lifecycleEventHandler) {
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..0886edad92b40276f268bd745b31bac359fd28af
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java
+@@ -0,0 +1,25 @@
++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 io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public final class LifecycleEventTypeProviderImpl implements LifecycleEventTypeProvider {
++
++ public static LifecycleEventTypeProviderImpl instance() {
++ return (LifecycleEventTypeProviderImpl) LifecycleEventTypeProvider.PROVIDER;
++ }
++
++ @Override
++ public <O extends LifecycleEventOwner, E extends LifecycleEvent> LifecycleEventType.Monitorable<O, E> monitor(final String name, final Class<? extends O> ownerType) {
++ return LifecycleEventRunner.INSTANCE.addEventType(new MonitorableLifecycleEventType<>(name, ownerType));
++ }
++
++ @Override
++ public <O extends LifecycleEventOwner, E extends LifecycleEvent> LifecycleEventType.Prioritizable<O, E> prioritized(final String name, final Class<? extends O> ownerType) {
++ return LifecycleEventRunner.INSTANCE.addEventType(new PrioritizableLifecycleEventType<>(name, ownerType));
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/MonitorableLifecycleEventType.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/MonitorableLifecycleEventType.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..6d92c1d3adf220154dfe7cba3a3f8158356c3e3c
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/MonitorableLifecycleEventType.java
+@@ -0,0 +1,54 @@
++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 io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
++import io.papermc.paper.plugin.lifecycle.event.handler.configuration.MonitorLifecycleEventHandlerConfiguration;
++import io.papermc.paper.plugin.lifecycle.event.handler.configuration.MonitorLifecycleEventHandlerConfigurationImpl;
++import java.util.ArrayList;
++import java.util.List;
++import java.util.function.Consumer;
++import java.util.function.Predicate;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public class MonitorableLifecycleEventType<O extends LifecycleEventOwner, E extends LifecycleEvent> extends AbstractLifecycleEventType<O, E, MonitorLifecycleEventHandlerConfiguration<O>, MonitorLifecycleEventHandlerConfigurationImpl<O, E>> implements LifecycleEventType.Monitorable<O, E> {
++
++ final List<RegisteredHandler<O, E>> handlers = new ArrayList<>();
++ int nonMonitorIdx = 0;
++
++ public MonitorableLifecycleEventType(final String name, final Class<? extends O> ownerType) {
++ super(name, ownerType);
++ }
++
++ @Override
++ public MonitorLifecycleEventHandlerConfigurationImpl<O, E> newHandler(final LifecycleEventHandler<? super E> handler) {
++ return new MonitorLifecycleEventHandlerConfigurationImpl<>(handler, this);
++ }
++
++ @Override
++ protected void register(final O owner, final LifecycleEventHandler<? super E> handler, final MonitorLifecycleEventHandlerConfigurationImpl<O, E> config) {
++ final RegisteredHandler<O, E> registeredHandler = new RegisteredHandler<>(owner, handler);
++ if (!config.isMonitor()) {
++ this.handlers.add(this.nonMonitorIdx, registeredHandler);
++ this.nonMonitorIdx++;
++ } else {
++ this.handlers.add(registeredHandler);
++ }
++ }
++
++ @Override
++ public void forEachHandler(final Consumer<? super RegisteredHandler<O, E>> consumer, final Predicate<? super RegisteredHandler<O, E>> predicate) {
++ for (final RegisteredHandler<O, E> handler : this.handlers) {
++ if (predicate.test(handler)) {
++ consumer.accept(handler);
++ }
++ }
++ }
++
++ @Override
++ public void removeMatching(final Predicate<? super RegisteredHandler<O, E>> predicate) {
++ this.handlers.removeIf(predicate);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/OwnerAwareLifecycleEvent.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/OwnerAwareLifecycleEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..3e7e7474f301c0725fa2bcd6e19e476fc35f2d5a
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/OwnerAwareLifecycleEvent.java
+@@ -0,0 +1,15 @@
++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 org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public interface OwnerAwareLifecycleEvent<O extends LifecycleEventOwner> extends LifecycleEvent {
++
++ void setOwner(@Nullable O owner);
++
++ @Nullable O castOwner(LifecycleEventOwner owner);
++}
+diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/PrioritizableLifecycleEventType.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/PrioritizableLifecycleEventType.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..6629f7fabf66ce761024268043cc30076ba8a3f1
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/PrioritizableLifecycleEventType.java
+@@ -0,0 +1,64 @@
++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 io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
++import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfiguration;
++import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfigurationImpl;
++import java.util.ArrayList;
++import java.util.Comparator;
++import java.util.List;
++import java.util.OptionalInt;
++import java.util.function.Consumer;
++import java.util.function.Predicate;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++@DefaultQualifier(NonNull.class)
++public class PrioritizableLifecycleEventType<O extends LifecycleEventOwner, E extends LifecycleEvent> extends AbstractLifecycleEventType<O, E, PrioritizedLifecycleEventHandlerConfiguration<O>, PrioritizedLifecycleEventHandlerConfigurationImpl<O, E>> implements LifecycleEventType.Prioritizable<O, E> {
++
++ private static final Comparator<PrioritizedHandler<?, ?>> COMPARATOR = Comparator.comparing(PrioritizedHandler::priority, (o1, o2) -> {
++ if (o1.equals(o2)) {
++ return 0;
++ } else if (o1.isEmpty()) {
++ return 1;
++ } else if (o2.isEmpty()) {
++ return -1;
++ } else {
++ return Integer.compare(o1.getAsInt(), o2.getAsInt());
++ }
++ });
++
++ private final List<PrioritizedHandler<O, E>> handlers = new ArrayList<>();
++
++ public PrioritizableLifecycleEventType(final String name, final Class<? extends O> ownerType) {
++ super(name, ownerType);
++ }
++
++ @Override
++ public PrioritizedLifecycleEventHandlerConfiguration<O> newHandler(final LifecycleEventHandler<? super E> handler) {
++ return new PrioritizedLifecycleEventHandlerConfigurationImpl<>(handler, this);
++ }
++
++ @Override
++ protected void register(final O owner, final LifecycleEventHandler<? super E> handler, final PrioritizedLifecycleEventHandlerConfigurationImpl<O, E> config) {
++ this.handlers.add(new PrioritizedHandler<>(new RegisteredHandler<>(owner, handler), config.priority()));
++ this.handlers.sort(COMPARATOR);
++ }
++
++ @Override
++ public void forEachHandler(final Consumer<? super RegisteredHandler<O, E>> consumer, final Predicate<? super RegisteredHandler<O, E>> predicate) {
++ for (final PrioritizedHandler<O, E> handler : this.handlers) {
++ if (predicate.test(handler.handler())) {
++ consumer.accept(handler.handler());
++ }
++ }
++ }
++
++ @Override
++ public void removeMatching(final Predicate<? super RegisteredHandler<O, E>> predicate) {
++ this.handlers.removeIf(prioritizedHandler -> predicate.test(prioritizedHandler.handler()));
++ }
++
++ private record PrioritizedHandler<O extends LifecycleEventOwner, E extends LifecycleEvent>(RegisteredHandler<O, E> handler, OptionalInt priority) {}
++}
+diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java
+index eeea1e6f7b1ed64567a3f90d8eb2e2cfd53e5912..eedbf46e04b5ae420f9bedcbc2bbb10643ba7e22 100644
+--- a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java
++++ b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java
+@@ -279,6 +279,15 @@ class PaperPluginInstanceManager {
+ + pluginName + " (Is it up to date?)", ex, plugin); // Paper
+ }
+
++ // Paper start - lifecycle event system
++ try {
++ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.unregisterAllEventHandlersFor(plugin);
++ } catch (Throwable ex) {
++ this.handlePluginException("Error occurred (in the plugin loader) while unregistering lifecycle event handlers for "
++ + pluginName + " (Is it up to date?)", ex, plugin);
++ }
++ // Paper end
++
+ try {
+ this.server.getMessenger().unregisterIncomingPluginChannel(plugin);
+ this.server.getMessenger().unregisterOutgoingPluginChannel(plugin);
+diff --git a/src/main/java/io/papermc/paper/plugin/storage/BootstrapProviderStorage.java b/src/main/java/io/papermc/paper/plugin/storage/BootstrapProviderStorage.java
+index 2e96308696e131f3f013469a395e5ddda2c5d529..65a66e484c1c39c5f41d97db52f31c67b4479d20 100644
+--- a/src/main/java/io/papermc/paper/plugin/storage/BootstrapProviderStorage.java
++++ b/src/main/java/io/papermc/paper/plugin/storage/BootstrapProviderStorage.java
+@@ -32,8 +32,9 @@ public class BootstrapProviderStorage extends SimpleProviderStorage<PluginBootst
+ @Override
+ public boolean load(PluginProvider<PluginBootstrap> provider, PluginBootstrap provided) {
+ try {
+- BootstrapContext context = PluginBootstrapContextImpl.create(provider, PluginInitializerManager.instance().pluginDirectoryPath());
++ PluginBootstrapContextImpl context = PluginBootstrapContextImpl.create(provider, PluginInitializerManager.instance().pluginDirectoryPath()); // Paper - lifecycle events
+ provided.bootstrap(context);
++ context.lockLifecycleEventRegistration(); // Paper - lifecycle events
+ return true;
+ } catch (Throwable e) {
+ LOGGER.error("Failed to run bootstrapper for %s. This plugin will not be loaded.".formatted(provider.getSource()), e);
+diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+index 4df3b94c8126f00188f5e125757411a0359728fa..14d3986ae6ec721f07dc82b37d62d3bea484ad15 100644
+--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+@@ -1005,6 +1005,11 @@ public final class CraftServer implements Server {
+
+ @Override
+ public void reload() {
++ // Paper start - lifecycle events
++ if (io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.blocksPluginReloading()) {
++ throw new IllegalStateException("A lifecycle event handler has been registered which makes reloading plugins not possible");
++ }
++ // Paper end - lifecycle events
+ org.spigotmc.WatchdogThread.hasStarted = false; // Paper - Disable watchdog early timeout on reload
+ this.reloadCount++;
+ this.configuration = YamlConfiguration.loadConfiguration(this.getConfigFile());
+diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java
+index d96399e9bf1a58db5a4a22e58abb99e7660e0694..66bdac50130f523f9dc4379b103b7a469f9ca36b 100644
+--- a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java
++++ b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java
+@@ -143,4 +143,11 @@ public class MinecraftInternalPlugin 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 @NotNull io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager<org.bukkit.plugin.Plugin> getLifecycleManager() {
++ throw new UnsupportedOperationException("Not supported.");
++ }
++ // Paper end - lifecycle events
+ }
+diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+index ca5312febcdd467889ad725c0263367bc5fe69f6..f276c5163d29d56cf4ed081d8e75cbcfd28d892f 100644
+--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+@@ -654,6 +654,13 @@ public final class CraftMagicNumbers implements UnsafeValues {
+ }
+ // Paper end - spawn egg color visibility
+
++ // Paper start - lifecycle event API
++ @Override
++ public 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) {
++ return new io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEventManager<>(plugin, registrationCheck);
++ }
++ // Paper end - lifecycle event API
++
+ /**
+ * This helper class represents the different NBT Tags.
+ * <p>
+diff --git a/src/main/resources/META-INF/services/io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventTypeProvider b/src/main/resources/META-INF/services/io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventTypeProvider
+new file mode 100644
+index 0000000000000000000000000000000000000000..808b1192b60348ad05f0bfbdeda6f94df4876743
+--- /dev/null
++++ b/src/main/resources/META-INF/services/io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventTypeProvider
+@@ -0,0 +1 @@
++io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventTypeProviderImpl
+diff --git a/src/test/java/io/papermc/paper/plugin/PaperTestPlugin.java b/src/test/java/io/papermc/paper/plugin/PaperTestPlugin.java
+index 1d14f530ef888102e47eeeaf0d1a6076e51871c4..90cf0c702ca2ff9de64d9718ecba5f2d128953a6 100644
+--- a/src/test/java/io/papermc/paper/plugin/PaperTestPlugin.java
++++ b/src/test/java/io/papermc/paper/plugin/PaperTestPlugin.java
+@@ -143,4 +143,11 @@ public class PaperTestPlugin 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<org.bukkit.plugin.Plugin> getLifecycleManager() {
++ throw new UnsupportedOperationException("Not supported.");
++ }
++ // Paper end - lifecycle events
+ }