diff options
Diffstat (limited to 'patches/server/0948-Add-Lifecycle-Event-system.patch')
-rw-r--r-- | patches/server/0948-Add-Lifecycle-Event-system.patch | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/patches/server/0948-Add-Lifecycle-Event-system.patch b/patches/server/0948-Add-Lifecycle-Event-system.patch new file mode 100644 index 0000000000..a1499842cb --- /dev/null +++ b/patches/server/0948-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 834b85f24df023642f8abf7213fe578ac8c17a3e..3e82ea07ca4194844c5528446e2c4a46ff4acee5 100644 +--- a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java ++++ b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java +@@ -293,6 +293,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 19934047295cb8ad3783bb73a03075916f1508ae..17a50b11da960a24bfea0ec780faf1718ca992d4 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1013,6 +1013,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 00cf4a0daa227e6b24ed052873290ff3fdae3119..6fc7b20c0f8f14c1d6a47177f9ccf402e88153e6 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -662,6 +662,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 + } |