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.
This commit is contained in:
Jake Potrebic 2023-07-18 17:49:38 -07:00
parent e5c2af7f5e
commit ef5bac21b2
20 changed files with 614 additions and 1 deletions

View file

@ -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
}

View file

@ -0,0 +1,100 @@
package io.papermc.paper.plugin.lifecycle.event;
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.function.Predicate;
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;
@DefaultQualifier(NonNull.class)
public class LifecycleEventRunner {
public static final LifecycleEventRunner INSTANCE = new LifecycleEventRunner();
private final List<LifecycleEventType<?, ?, ?>> lifecycleEventTypes = new ArrayList<>();
private boolean blockPluginReloading = false;
public <O extends LifecycleEventOwner> void checkRegisteredHandler(final O owner, final AbstractLifecycleEventType<O, ?, ?> 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 (eventType.blocksReloading(owner)) {
this.blockPluginReloading = true;
}
}
public boolean blocksPluginReloading() {
return this.blockPluginReloading;
}
public <O extends LifecycleEventOwner, E extends LifecycleEvent, ET extends LifecycleEventType<O, E, ?>> void addEventType(final ET eventType) {
this.lifecycleEventTypes.add(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(event, registeredHandler -> {
try {
if (event instanceof final OwnerAwareLifecycleEvent<?> ownerAwareEvent) {
ownerAwareGenericHelper(ownerAwareEvent, registeredHandler.owner());
}
registeredHandler.lifecycleEventHandler().run(event);
} catch (final Throwable ex) {
throw new RuntimeException("Could not run '%s' lifecycle event handler from %s".formatted(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() {
}
}

View file

@ -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() {
}
}

View file

@ -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);
}
}

View file

@ -0,0 +1,28 @@
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> implements LifecycleEventHandlerConfiguration<O> {
private final LifecycleEventHandler<? super E> handler;
private final AbstractLifecycleEventType<O, E, ?> type;
protected AbstractLifecycleEventHandlerConfiguration(final LifecycleEventHandler<? super E> handler, final AbstractLifecycleEventType<O, E, ?> type) {
this.handler = handler;
this.type = type;
}
public final void registerFrom(final O owner) {
this.type.tryRegister(owner, this);
}
public LifecycleEventHandler<? super E> handler() {
return this.handler;
}
}

View file

@ -0,0 +1,28 @@
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> implements MonitorLifecycleEventHandlerConfiguration<O> {
private boolean monitor = false;
public MonitorLifecycleEventHandlerConfigurationImpl(final LifecycleEventHandler<? super E> handler, final AbstractLifecycleEventType<O, E, ?> eventType) {
super(handler, eventType);
}
public boolean isMonitor() {
return this.monitor;
}
@Override
public MonitorLifecycleEventHandlerConfiguration<O> monitor() {
this.monitor = true;
return this;
}
}

View file

@ -0,0 +1,40 @@
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>
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, ?> eventType) {
super(handler, eventType);
}
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;
}
}

View file

@ -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() {
}
}

View file

@ -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();
}
}
}

View file

@ -0,0 +1,62 @@
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.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>> 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;
LifecycleEventRunner.INSTANCE.addEventType(this);
}
@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 boolean blocksReloading(final O eventOwner) {
return eventOwner instanceof BootstrapContext;
}
public abstract boolean hasHandlers();
public abstract void forEachHandler(E event, Consumer<RegisteredHandler<O, E>> consumer, Predicate<RegisteredHandler<O, E>> predicate);
public abstract void removeMatching(Predicate<RegisteredHandler<O, E>> predicate);
protected abstract void register(O owner, AbstractLifecycleEventHandlerConfiguration<O, E> config);
public final void tryRegister(final O owner, final AbstractLifecycleEventHandlerConfiguration<O, E> config) {
this.verifyOwner(owner);
LifecycleEventRunner.INSTANCE.checkRegisteredHandler(owner, this);
this.register(owner, config);
}
public record RegisteredHandler<O extends LifecycleEventOwner, E extends LifecycleEvent>(O owner, AbstractLifecycleEventHandlerConfiguration<O, E> config) {
public LifecycleEventHandler<? super E> lifecycleEventHandler() {
return this.config().handler();
}
}
}

View file

@ -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 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 new PrioritizableLifecycleEventType.Simple<>(name, ownerType);
}
}

View file

@ -0,0 +1,63 @@
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.AbstractLifecycleEventHandlerConfiguration;
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>> 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 boolean hasHandlers() {
return !this.handlers.isEmpty();
}
@Override
public MonitorLifecycleEventHandlerConfigurationImpl<O, E> newHandler(final LifecycleEventHandler<? super E> handler) {
return new MonitorLifecycleEventHandlerConfigurationImpl<>(handler, this);
}
@Override
protected void register(final O owner, final AbstractLifecycleEventHandlerConfiguration<O, E> config) {
if (!(config instanceof final MonitorLifecycleEventHandlerConfigurationImpl<?,?> monitor)) {
throw new IllegalArgumentException("Configuration must be a MonitorLifecycleEventHandlerConfiguration");
}
final RegisteredHandler<O, E> registeredHandler = new RegisteredHandler<>(owner, config);
if (!monitor.isMonitor()) {
this.handlers.add(this.nonMonitorIdx, registeredHandler);
this.nonMonitorIdx++;
} else {
this.handlers.add(registeredHandler);
}
}
@Override
public void forEachHandler(final E event, final Consumer<RegisteredHandler<O, E>> consumer, final Predicate<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<RegisteredHandler<O, E>> predicate) {
this.handlers.removeIf(predicate);
}
}

View file

@ -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);
}

View file

@ -0,0 +1,79 @@
package io.papermc.paper.plugin.lifecycle.event.types;
import com.google.common.base.Preconditions;
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.AbstractLifecycleEventHandlerConfiguration;
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.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 PrioritizableLifecycleEventType<
O extends LifecycleEventOwner,
E extends LifecycleEvent,
C extends PrioritizedLifecycleEventHandlerConfiguration<O>
> extends AbstractLifecycleEventType<O, E, C> {
private static final Comparator<RegisteredHandler<?, ?>> COMPARATOR = Comparator.comparing(handler -> ((PrioritizedLifecycleEventHandlerConfigurationImpl<?, ?>) handler.config()).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<RegisteredHandler<O, E>> handlers = new ArrayList<>();
public PrioritizableLifecycleEventType(final String name, final Class<? extends O> ownerType) {
super(name, ownerType);
}
@Override
public boolean hasHandlers() {
return !this.handlers.isEmpty();
}
@Override
protected void register(final O owner, final AbstractLifecycleEventHandlerConfiguration<O, E> config) {
Preconditions.checkArgument(config instanceof PrioritizedLifecycleEventHandlerConfigurationImpl<?, ?>, "Configuration must be a PrioritizedLifecycleEventHandlerConfiguration");
this.handlers.add(new RegisteredHandler<>(owner, config));
this.handlers.sort(COMPARATOR);
}
@Override
public void forEachHandler(final E event, final Consumer<RegisteredHandler<O, E>> consumer, final Predicate<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<RegisteredHandler<O, E>> predicate) {
this.handlers.removeIf(predicate);
}
public static class Simple<O extends LifecycleEventOwner, E extends LifecycleEvent> extends PrioritizableLifecycleEventType<O, E, PrioritizedLifecycleEventHandlerConfiguration<O>> implements LifecycleEventType.Prioritizable<O, E> {
public Simple(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);
}
}
}

View file

@ -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);

View file

@ -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);

View file

@ -1062,6 +1062,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());

View file

@ -649,6 +649,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>

View file

@ -0,0 +1 @@
io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventTypeProviderImpl

View file

@ -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
}