diff --git a/api/src/main/java/com/discordsrv/api/eventbus/EventPriority.java b/api/src/main/java/com/discordsrv/api/eventbus/EventPriorities.java similarity index 58% rename from api/src/main/java/com/discordsrv/api/eventbus/EventPriority.java rename to api/src/main/java/com/discordsrv/api/eventbus/EventPriorities.java index 440f6c16..cbea4d52 100644 --- a/api/src/main/java/com/discordsrv/api/eventbus/EventPriority.java +++ b/api/src/main/java/com/discordsrv/api/eventbus/EventPriorities.java @@ -23,47 +23,19 @@ package com.discordsrv.api.eventbus; -import com.discordsrv.api.events.Processable; - /** * A simple enum to dictate the order that event listeners will be executed, going from {@link #POST} to {@link #POST}. */ -public enum EventPriority { +@SuppressWarnings("unused") // "API" +public class EventPriorities { - /** - * This is the first in the priority order, this should be used to observe the event before any processing. - */ - PRE, - - /** - * This is the earliest in the processing. This should be used to cancel events. - */ - EARLIEST, - - /** - * This should be used to modify events. - */ - EARLY, - - /** - * The default priority, right in the middle of the priority order. Use this if you need to override - * one of DiscordSRV's implementations for {@link Processable}s. - */ - DEFAULT, - - /** - * This is where DiscordSRV's integrations for other plugins will process {@link Processable}'s. - */ - LATE, - - /** - * This is where DiscordSRV's default implementations for {@link Processable}'s will run. - */ - LAST, - - /** - * This is the last in the priority order, this should be used to observe the event after all processing is complete. - */ - POST + public static final byte PRE = Byte.MIN_VALUE; + public static final byte EARLIEST = (byte) -96; + public static final byte EARLY = (byte) -48; + public static final byte DEFAULT = (byte) 0; + public static final byte LATE = (byte) 48; + public static final byte LAST = (byte) 96; + public static final byte POST = Byte.MAX_VALUE; + private EventPriorities() {} } diff --git a/api/src/main/java/com/discordsrv/api/eventbus/Subscribe.java b/api/src/main/java/com/discordsrv/api/eventbus/Subscribe.java index 54453561..5c7940f1 100644 --- a/api/src/main/java/com/discordsrv/api/eventbus/Subscribe.java +++ b/api/src/main/java/com/discordsrv/api/eventbus/Subscribe.java @@ -33,7 +33,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Placed on a public non-abstract non-static method that has only 1 parameters, + * Placed on a public non-abstract non-static method that has only 1 parameter, * being an event extending {@link Event} or {@link net.dv8tion.jda.api.events.GenericEvent}. * * You can register a listener through {@link EventBus#subscribe(Object)}, {@link DiscordSRVApi#eventBus()} to get the event bus. @@ -52,7 +52,8 @@ public @interface Subscribe { /** * The priority for this event listener, this determines the order that event listeners receive events. * @return the priority of this event listener + * @see EventPriorities */ - EventPriority priority() default EventPriority.DEFAULT; + byte priority() default 0; } diff --git a/api/src/main/java/com/discordsrv/api/events/channel/GameChannelLookupEvent.java b/api/src/main/java/com/discordsrv/api/events/channel/GameChannelLookupEvent.java index a2a87247..fcb151da 100644 --- a/api/src/main/java/com/discordsrv/api/events/channel/GameChannelLookupEvent.java +++ b/api/src/main/java/com/discordsrv/api/events/channel/GameChannelLookupEvent.java @@ -24,7 +24,7 @@ package com.discordsrv.api.events.channel; import com.discordsrv.api.channel.GameChannel; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.events.Processable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,7 +32,7 @@ import org.jetbrains.annotations.Nullable; /** * This event is used to lookup {@link GameChannel}s by their name (and optionally plugin name). * This is also used to determine which plugin's channel should take priority when multiple plugins - * define channels with the same name ({@link EventPriority}). + * define channels with the same name ({@link EventPriorities}). * * @see #isDefault() */ diff --git a/api/src/main/java/com/discordsrv/api/events/lifecycle/DiscordSRVShuttingDownEvent.java b/api/src/main/java/com/discordsrv/api/events/lifecycle/DiscordSRVShuttingDownEvent.java index a9885e3b..df29da88 100644 --- a/api/src/main/java/com/discordsrv/api/events/lifecycle/DiscordSRVShuttingDownEvent.java +++ b/api/src/main/java/com/discordsrv/api/events/lifecycle/DiscordSRVShuttingDownEvent.java @@ -23,20 +23,20 @@ package com.discordsrv.api.events.lifecycle; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.events.Event; /** * Indicates that DiscordSRV is shutting down. *

* DiscordSRV's own systems will shut down at the following times: - * {@link EventPriority#EARLY}
+ * {@link EventPriorities#EARLY}
* - DiscordSRV's own modules shutdown
* - * {@link EventPriority#LATE}
+ * {@link EventPriorities#LATE}
* - Discord connections are shutdown
* - * {@link EventPriority#LAST}
+ * {@link EventPriorities#LAST}
* - DiscordSRV's scheduler is shutdown
*/ public class DiscordSRVShuttingDownEvent implements Event { diff --git a/api/src/main/java/com/discordsrv/api/events/message/receive/game/AwardMessageReceiveEvent.java b/api/src/main/java/com/discordsrv/api/events/message/receive/game/AwardMessageReceiveEvent.java index 7c31cbb3..4246d3fe 100644 --- a/api/src/main/java/com/discordsrv/api/events/message/receive/game/AwardMessageReceiveEvent.java +++ b/api/src/main/java/com/discordsrv/api/events/message/receive/game/AwardMessageReceiveEvent.java @@ -25,7 +25,7 @@ package com.discordsrv.api.events.message.receive.game; import com.discordsrv.api.channel.GameChannel; import com.discordsrv.api.component.MinecraftComponent; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.events.PlayerEvent; import com.discordsrv.api.player.DiscordSRVPlayer; import org.jetbrains.annotations.NotNull; @@ -33,7 +33,7 @@ import org.jetbrains.annotations.Nullable; /** * Indicates that an advancement or achievement message was received will be processed - * at {@link EventPriority#DEFAULT} unless cancelled or processed by a 3rd party. + * at {@link EventPriorities#DEFAULT} unless cancelled or processed by a 3rd party. */ public class AwardMessageReceiveEvent extends AbstractGameMessageReceiveEvent implements PlayerEvent { diff --git a/api/src/main/java/com/discordsrv/api/events/message/receive/game/DeathMessageReceiveEvent.java b/api/src/main/java/com/discordsrv/api/events/message/receive/game/DeathMessageReceiveEvent.java index 1ed94a97..9e25e8cd 100644 --- a/api/src/main/java/com/discordsrv/api/events/message/receive/game/DeathMessageReceiveEvent.java +++ b/api/src/main/java/com/discordsrv/api/events/message/receive/game/DeathMessageReceiveEvent.java @@ -25,7 +25,7 @@ package com.discordsrv.api.events.message.receive.game; import com.discordsrv.api.channel.GameChannel; import com.discordsrv.api.component.MinecraftComponent; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.events.PlayerEvent; import com.discordsrv.api.player.DiscordSRVPlayer; import org.jetbrains.annotations.NotNull; @@ -33,7 +33,7 @@ import org.jetbrains.annotations.Nullable; /** * Indicates that a death message was received and will be processed - * at {@link EventPriority#DEFAULT} unless cancelled or processed by a 3rd party. + * at {@link EventPriorities#DEFAULT} unless cancelled or processed by a 3rd party. */ public class DeathMessageReceiveEvent extends AbstractGameMessageReceiveEvent implements PlayerEvent { diff --git a/api/src/main/java/com/discordsrv/api/events/message/receive/game/GameChatMessageReceiveEvent.java b/api/src/main/java/com/discordsrv/api/events/message/receive/game/GameChatMessageReceiveEvent.java index a385d839..34639a79 100644 --- a/api/src/main/java/com/discordsrv/api/events/message/receive/game/GameChatMessageReceiveEvent.java +++ b/api/src/main/java/com/discordsrv/api/events/message/receive/game/GameChatMessageReceiveEvent.java @@ -25,7 +25,7 @@ package com.discordsrv.api.events.message.receive.game; import com.discordsrv.api.channel.GameChannel; import com.discordsrv.api.component.MinecraftComponent; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.events.PlayerEvent; import com.discordsrv.api.player.DiscordSRVPlayer; import org.jetbrains.annotations.NotNull; @@ -33,7 +33,7 @@ import org.jetbrains.annotations.Nullable; /** * Indicates that a chat message was received and will be processed - * at {@link EventPriority#DEFAULT} unless cancelled or processed by a 3rd party. + * at {@link EventPriorities#DEFAULT} unless cancelled or processed by a 3rd party. */ public class GameChatMessageReceiveEvent extends AbstractGameMessageReceiveEvent implements PlayerEvent { diff --git a/api/src/main/java/com/discordsrv/api/events/message/receive/game/JoinMessageReceiveEvent.java b/api/src/main/java/com/discordsrv/api/events/message/receive/game/JoinMessageReceiveEvent.java index b11421c4..b0ca8805 100644 --- a/api/src/main/java/com/discordsrv/api/events/message/receive/game/JoinMessageReceiveEvent.java +++ b/api/src/main/java/com/discordsrv/api/events/message/receive/game/JoinMessageReceiveEvent.java @@ -25,7 +25,7 @@ package com.discordsrv.api.events.message.receive.game; import com.discordsrv.api.channel.GameChannel; import com.discordsrv.api.component.MinecraftComponent; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.events.PlayerEvent; import com.discordsrv.api.player.DiscordSRVPlayer; import org.jetbrains.annotations.NotNull; @@ -33,7 +33,7 @@ import org.jetbrains.annotations.Nullable; /** * Indicates that a join message was received and will be processed - * at {@link EventPriority#DEFAULT} unless cancelled or processed by a 3rd party. + * at {@link EventPriorities#DEFAULT} unless cancelled or processed by a 3rd party. */ public class JoinMessageReceiveEvent extends AbstractGameMessageReceiveEvent implements PlayerEvent { diff --git a/api/src/main/java/com/discordsrv/api/events/message/receive/game/LeaveMessageReceiveEvent.java b/api/src/main/java/com/discordsrv/api/events/message/receive/game/LeaveMessageReceiveEvent.java index 887cfa19..bb25d934 100644 --- a/api/src/main/java/com/discordsrv/api/events/message/receive/game/LeaveMessageReceiveEvent.java +++ b/api/src/main/java/com/discordsrv/api/events/message/receive/game/LeaveMessageReceiveEvent.java @@ -25,7 +25,7 @@ package com.discordsrv.api.events.message.receive.game; import com.discordsrv.api.channel.GameChannel; import com.discordsrv.api.component.MinecraftComponent; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.events.PlayerEvent; import com.discordsrv.api.player.DiscordSRVPlayer; import org.jetbrains.annotations.NotNull; @@ -33,7 +33,7 @@ import org.jetbrains.annotations.Nullable; /** * Indicates that a leave message was received and will be processed - * at {@link EventPriority#DEFAULT} unless cancelled or processed by a 3rd party. + * at {@link EventPriorities#DEFAULT} unless cancelled or processed by a 3rd party. */ public class LeaveMessageReceiveEvent extends AbstractGameMessageReceiveEvent implements PlayerEvent { diff --git a/api/src/main/java/com/discordsrv/api/events/message/receive/game/ServerSwitchMessageReceiveEvent.java b/api/src/main/java/com/discordsrv/api/events/message/receive/game/ServerSwitchMessageReceiveEvent.java index 8c105198..fa2ae4c7 100644 --- a/api/src/main/java/com/discordsrv/api/events/message/receive/game/ServerSwitchMessageReceiveEvent.java +++ b/api/src/main/java/com/discordsrv/api/events/message/receive/game/ServerSwitchMessageReceiveEvent.java @@ -24,7 +24,7 @@ package com.discordsrv.api.events.message.receive.game; import com.discordsrv.api.component.MinecraftComponent; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.events.PlayerEvent; import com.discordsrv.api.player.DiscordSRVPlayer; import org.jetbrains.annotations.NotNull; @@ -32,7 +32,7 @@ import org.jetbrains.annotations.Nullable; /** * Indicates that a server switch message was received and will be processed - * at {@link EventPriority#DEFAULT} unless cancelled or processed by a 3rd party. + * at {@link EventPriorities#DEFAULT} unless cancelled or processed by a 3rd party. */ public class ServerSwitchMessageReceiveEvent extends AbstractGameMessageReceiveEvent implements PlayerEvent { diff --git a/bukkit/src/main/java/com/discordsrv/bukkit/integration/EssentialsXIntegration.java b/bukkit/src/main/java/com/discordsrv/bukkit/integration/EssentialsXIntegration.java index 479a542b..b9701352 100644 --- a/bukkit/src/main/java/com/discordsrv/bukkit/integration/EssentialsXIntegration.java +++ b/bukkit/src/main/java/com/discordsrv/bukkit/integration/EssentialsXIntegration.java @@ -20,7 +20,7 @@ package com.discordsrv.bukkit.integration; import com.discordsrv.api.channel.GameChannel; import com.discordsrv.api.component.MinecraftComponent; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.eventbus.Subscribe; import com.discordsrv.api.events.channel.GameChannelLookupEvent; import com.discordsrv.api.events.message.receive.game.GameChatMessageReceiveEvent; @@ -149,7 +149,7 @@ public class EssentialsXIntegration )); } - @Subscribe(priority = EventPriority.LAST) + @Subscribe(priority = EventPriorities.LAST) public void onGameChannelLookup(GameChannelLookupEvent event) { if (checkProcessor(event) || !discordSRV.server().getPluginManager().isPluginEnabled("EssentialsChat")) { return; diff --git a/bukkit/src/main/java/com/discordsrv/bukkit/integration/chat/GriefPreventionChatIntegration.java b/bukkit/src/main/java/com/discordsrv/bukkit/integration/chat/GriefPreventionChatIntegration.java index ae8756d2..c79882a9 100644 --- a/bukkit/src/main/java/com/discordsrv/bukkit/integration/chat/GriefPreventionChatIntegration.java +++ b/bukkit/src/main/java/com/discordsrv/bukkit/integration/chat/GriefPreventionChatIntegration.java @@ -18,7 +18,7 @@ package com.discordsrv.bukkit.integration.chat; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.eventbus.Subscribe; import com.discordsrv.api.events.message.receive.game.GameChatMessageReceiveEvent; import com.discordsrv.api.player.DiscordSRVPlayer; @@ -50,7 +50,7 @@ public class GriefPreventionChatIntegration extends PluginIntegration im HandlerList.unregisterAll(this); } - @Subscribe(priority = EventPriority.EARLY) + @Subscribe(priority = EventPriorities.EARLY) public void onGameChatMessageReceive(GameChatMessageReceiveEvent event) { Player player = discordSRV.server().getPlayer(event.getPlayer().uniqueId()); if (!player.hasMetadata("mcMMO: Player Data")) { diff --git a/common/src/main/java/com/discordsrv/common/core/eventbus/EventBusImpl.java b/common/src/main/java/com/discordsrv/common/core/eventbus/EventBusImpl.java index ff5612a7..e6e2749a 100644 --- a/common/src/main/java/com/discordsrv/common/core/eventbus/EventBusImpl.java +++ b/common/src/main/java/com/discordsrv/common/core/eventbus/EventBusImpl.java @@ -20,7 +20,6 @@ package com.discordsrv.common.core.eventbus; import com.discordsrv.api.eventbus.EventBus; import com.discordsrv.api.eventbus.EventListener; -import com.discordsrv.api.eventbus.EventPriority; import com.discordsrv.api.eventbus.Subscribe; import com.discordsrv.api.eventbus.internal.EventStateHolder; import com.discordsrv.api.events.Cancellable; @@ -37,36 +36,40 @@ import net.dv8tion.jda.api.events.GenericEvent; import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.InvocationTargetException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; import static com.discordsrv.common.util.ExceptionUtil.minifyException; public class EventBusImpl implements EventBus { - private static final List, ThreadLocal>> STATES = Arrays.asList( - Pair.of(event -> event instanceof Cancellable && ((Cancellable) event).isCancelled(), EventStateHolder.CANCELLED), - Pair.of(event -> event instanceof Processable && ((Processable) event).isProcessed(), EventStateHolder.PROCESSED) + private static final List> STATES = Arrays.asList( + new State<>(Cancellable.class, Cancellable::isCancelled, EventStateHolder.CANCELLED), + new State<>(Processable.class, Processable::isProcessed, EventStateHolder.PROCESSED) ); private final Map> listeners = new ConcurrentHashMap<>(); - private final List allListeners = new CopyOnWriteArrayList<>(); + private final Map, List> listenersByEvent = new ConcurrentHashMap<>(); private final Logger logger; public EventBusImpl(DiscordSRV discordSRV) { this.logger = new NamedLogger(discordSRV, "EVENT_BUS"); + + // For debug generation subscribe(this); } public void shutdown() { listeners.clear(); - allListeners.clear(); + listenersByEvent.clear(); } @Override @@ -87,7 +90,10 @@ public class EventBusImpl implements EventBus { } listeners.put(eventListener, methods); - allListeners.addAll(methods); + for (EventListenerImpl method : methods) { + listenersByEvent.computeIfAbsent(method.eventClass(), key -> new CopyOnWriteArrayList<>()) + .add(method); + } logger.debug("Listener " + eventListener.getClass().getName() + " subscribed"); } @@ -123,7 +129,8 @@ public class EventBusImpl implements EventBus { int parameters = parameterTypes.length; List suppressed = new ArrayList<>(); - if (Void.class.isAssignableFrom(method.getReturnType())) { + Class returnType = method.getReturnType(); + if (Void.class.isAssignableFrom(returnType)) { suppressed.add(createReasonException("Must return void")); } @@ -149,6 +156,14 @@ public class EventBusImpl implements EventBus { } } + MethodHandle handle = null; + MethodType methodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes()); + try { + handle = MethodHandles.lookup().findVirtual(listenerClass, method.getName(), methodType); + } catch (ReflectiveOperationException e) { + suppressedMethods.add(e); + } + if (!suppressed.isEmpty()) { Exception methodException = new InvalidListenerMethodException("Method " + method.getName() + "(" + (parameters > 0 ? Arrays.stream(method.getParameterTypes()) @@ -159,7 +174,7 @@ public class EventBusImpl implements EventBus { return; } - EventListenerImpl listener = new EventListenerImpl(eventListener, listenerClass, annotation, firstParameter, method); + EventListenerImpl listener = new EventListenerImpl(eventListener, listenerClass, annotation, firstParameter, method, handle); methods.add(listener); } @@ -172,7 +187,14 @@ public class EventBusImpl implements EventBus { public void unsubscribe(@NotNull Object eventListener) { List removed = listeners.remove(eventListener); if (removed != null) { - allListeners.removeAll(removed); + for (EventListenerImpl listener : removed) { + Class eventClass = listener.eventClass(); + List listeners = listenersByEvent.get(eventClass); + listeners.remove(listener); + if (listeners.isEmpty()) { + listenersByEvent.remove(eventClass); + } + } logger.debug("Listener " + eventListener.getClass().getName() + " unsubscribed"); } } @@ -187,86 +209,102 @@ public class EventBusImpl implements EventBus { publishEvent(event); } + private void gatherListeners(Class eventClass, List listeners) { + List listenersForEvent = this.listenersByEvent.get(eventClass); + if (listenersForEvent == null) { + return; + } + listeners.addAll(listenersForEvent); + } + private void publishEvent(Object event) { - List states = new ArrayList<>(STATES.size()); - for (Pair, ThreadLocal> entry : STATES) { - if (entry.getKey().apply(event)) { - // If the state is already set before listeners, we mark it as being changed by a 'unknown' event listener - states.add(true); - entry.getValue().set(EventStateHolder.UNKNOWN_LISTENER); - continue; + Class eventClass = event.getClass(); + + Map, Boolean> states = new HashMap<>(STATES.size()); + for (State state : STATES) { + if (state.eventClass().isAssignableFrom(eventClass)) { + boolean value = state.statePredicate().test(event); + states.put(state, value); + + if (value) { + state.stateHolder().set(EventStateHolder.UNKNOWN_LISTENER); + } } - states.add(false); } - Class eventClass = event.getClass(); - for (EventPriority priority : EventPriority.values()) { - for (EventListenerImpl eventListener : allListeners) { - if (eventListener.isIgnoringCancelled() && event instanceof Cancellable && ((Cancellable) event).isCancelled()) { - continue; + List listeners = new ArrayList<>(); + while (!Object.class.equals(eventClass)) { + gatherListeners(eventClass, listeners); + for (Class anInterface : eventClass.getInterfaces()) { + gatherListeners(anInterface, listeners); + } + + eventClass = eventClass.getSuperclass(); + } + + listeners.sort(Comparator.comparingInt(EventListenerImpl::priority)); + + for (EventListenerImpl eventListener : listeners) { + if (eventListener.isIgnoringCancelled() && event instanceof Cancellable && ((Cancellable) event).isCancelled()) { + continue; + } + + long startTime = System.currentTimeMillis(); + try { + Object listener = eventListener.listener(); + eventListener.handle().invoke(listener, event); + } catch (Throwable e) { + String eventClassName = eventClass.getName(); + if (eventListener.className().startsWith("com.discordsrv")) { + logger.error("Failed to pass " + eventClassName + " to " + eventListener, e); + } else { + // Print the listener failing without references to the DiscordSRV event bus + // as it isn't relevant to the exception, and often causes users to suspect DiscordSRV is doing something wrong when it isn't + //noinspection CallToPrintStackTrace + e.printStackTrace(); } - if (eventListener.priority() != priority) { - continue; - } - if (!eventListener.eventClass().isAssignableFrom(eventClass)) { + TestHelper.fail(e); + } + long timeTaken = System.currentTimeMillis() - startTime; + logger.trace(eventListener + " took " + timeTaken + "ms to execute"); + + for (Map.Entry, Boolean> entry : states.entrySet()) { + State state = entry.getKey(); + boolean currentValue = entry.getValue(); + boolean newValue = state.statePredicate().test(event); + + if (currentValue == newValue) { continue; } - long startTime = System.currentTimeMillis(); - try { - Object listener = eventListener.listener(); - eventListener.method().invoke(listener, event); - } catch (IllegalAccessException e) { - logger.error("Failed to access listener method: " + eventListener.methodName() + " in " + eventListener.className(), e); - TestHelper.fail(e); - } catch (InvocationTargetException e) { - String eventClassName = eventClass.getName(); - Throwable cause = e.getCause(); - if (eventListener.className().startsWith("com.discordsrv")) { - logger.error("Failed to pass " + eventClassName + " to " + eventListener, cause); - } else { - // Print the listener failing without references to the DiscordSRV event bus - // as it isn't relevant to the exception, and often causes users to suspect DiscordSRV is doing something wrong when it isn't - //noinspection CallToPrintStackTrace - e.getCause().printStackTrace(); - } - TestHelper.fail(cause); - } - long timeTaken = System.currentTimeMillis() - startTime; - logger.trace(eventListener + " took " + timeTaken + "ms to execute"); - - for (int index = 0; index < STATES.size(); index++) { - Pair, ThreadLocal> state = STATES.get(index); - - boolean current = states.get(index); - boolean updated = state.getKey().apply(event); - states.set(index, updated); - - ThreadLocal stateHolder = state.getValue(); - if (current != updated) { - if (updated) { - stateHolder.set(eventListener); - } else { - stateHolder.remove(); - } - } + if (currentValue) { + state.stateHolder().set(eventListener); + } else { + state.stateHolder().set(EventStateHolder.UNKNOWN_LISTENER); } } } // Clear the states - for (Pair, ThreadLocal> state : STATES) { - state.getValue().remove(); + for (State state : states.keySet()) { + state.stateHolder().remove(); } } @Subscribe public void onDebugGenerate(DebugGenerateEvent event) { - StringBuilder builder = new StringBuilder("Registered listeners (" + listeners.size() + "/" + allListeners.size() + "):\n"); + StringBuilder builder = new StringBuilder("Registered listeners\n"); + builder.append(" (").append(listeners.size()).append(" listeners classes)\n"); + builder.append(" (for ").append(listenersByEvent.size()).append(" events)\n"); + builder.append(" (for a total of ") + .append(listeners.values().stream().mapToInt(List::size).sum()) + .append(" individual listeners methods)\n"); for (Map.Entry> entry : listeners.entrySet()) { Object listener = entry.getKey(); List eventListeners = entry.getValue(); + eventListeners.sort(Comparator.comparingInt(EventListenerImpl::priority)); + builder.append('\n') .append(listener) .append(" (") @@ -280,11 +318,37 @@ public class EventBusImpl implements EventBus { .append(": ") .append(eventListener.methodName()) .append(" @ ") - .append(eventListener.priority().name()) + .append(eventListener.priority()) .append('\n'); } } event.addFile(new TextDebugFile("event-bus.txt", builder)); } + + private static class State { + + private final Class eventClass; + private final Predicate statePredicate; + private final ThreadLocal stateHolder; + + @SuppressWarnings("unchecked") // Converting generic to Object is easier down the line + public State(Class eventClass, Predicate statePredicate, ThreadLocal stateHolder) { + this.eventClass = eventClass; + this.statePredicate = (Predicate) statePredicate; + this.stateHolder = stateHolder; + } + + public Class eventClass() { + return eventClass; + } + + public Predicate statePredicate() { + return statePredicate; + } + + public ThreadLocal stateHolder() { + return stateHolder; + } + } } diff --git a/common/src/main/java/com/discordsrv/common/core/eventbus/EventListenerImpl.java b/common/src/main/java/com/discordsrv/common/core/eventbus/EventListenerImpl.java index 4ef8b4ad..7b1a1721 100644 --- a/common/src/main/java/com/discordsrv/common/core/eventbus/EventListenerImpl.java +++ b/common/src/main/java/com/discordsrv/common/core/eventbus/EventListenerImpl.java @@ -19,10 +19,10 @@ package com.discordsrv.common.core.eventbus; import com.discordsrv.api.eventbus.EventListener; -import com.discordsrv.api.eventbus.EventPriority; import com.discordsrv.api.eventbus.Subscribe; import org.jetbrains.annotations.NotNull; +import java.lang.invoke.MethodHandle; import java.lang.reflect.Method; public class EventListenerImpl implements EventListener { @@ -32,20 +32,22 @@ public class EventListenerImpl implements EventListener { private final Subscribe annotation; private final Class eventClass; private final Method method; + private final MethodHandle handle; - public EventListenerImpl(Object listener, Class listenerClass, Subscribe annotation, Class eventClass, Method method) { + public EventListenerImpl(Object listener, Class listenerClass, Subscribe annotation, Class eventClass, Method method, MethodHandle handle) { this.listener = listener; this.listenerClass = listenerClass; this.annotation = annotation; this.eventClass = eventClass; this.method = method; + this.handle = handle; } public boolean isIgnoringCancelled() { return annotation.ignoreCancelled(); } - public EventPriority priority() { + public byte priority() { return annotation.priority(); } @@ -72,6 +74,10 @@ public class EventListenerImpl implements EventListener { return method.getName(); } + public MethodHandle handle() { + return handle; + } + @Override public String toString() { return "EventListenerImpl{" + className() + "#" + methodName() + "}"; diff --git a/common/src/main/java/com/discordsrv/common/core/module/ModuleManager.java b/common/src/main/java/com/discordsrv/common/core/module/ModuleManager.java index 8372d711..5e172abb 100644 --- a/common/src/main/java/com/discordsrv/common/core/module/ModuleManager.java +++ b/common/src/main/java/com/discordsrv/common/core/module/ModuleManager.java @@ -21,7 +21,7 @@ package com.discordsrv.common.core.module; import com.discordsrv.api.discord.connection.details.DiscordCacheFlag; import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent; import com.discordsrv.api.discord.connection.details.DiscordMemberCachePolicy; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.eventbus.Subscribe; import com.discordsrv.api.events.lifecycle.DiscordSRVReadyEvent; import com.discordsrv.api.events.lifecycle.DiscordSRVShuttingDownEvent; @@ -200,7 +200,7 @@ public class ModuleManager { } } - @Subscribe(priority = EventPriority.EARLY) + @Subscribe(priority = EventPriorities.EARLY) public void onShuttingDown(DiscordSRVShuttingDownEvent event) { modules.stream() .sorted((m1, m2) -> Integer.compare(m2.shutdownOrder(), m1.shutdownOrder())) diff --git a/common/src/main/java/com/discordsrv/common/core/scheduler/StandardScheduler.java b/common/src/main/java/com/discordsrv/common/core/scheduler/StandardScheduler.java index be3ab330..f2d361c5 100644 --- a/common/src/main/java/com/discordsrv/common/core/scheduler/StandardScheduler.java +++ b/common/src/main/java/com/discordsrv/common/core/scheduler/StandardScheduler.java @@ -18,7 +18,7 @@ package com.discordsrv.common.core.scheduler; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.eventbus.Subscribe; import com.discordsrv.api.events.lifecycle.DiscordSRVShuttingDownEvent; import com.discordsrv.common.DiscordSRV; @@ -82,7 +82,7 @@ public class StandardScheduler implements Scheduler { this.forkJoinPool = forkJoinPool; } - @Subscribe(priority = EventPriority.LAST) + @Subscribe(priority = EventPriorities.LAST) public void onShuttingDown(DiscordSRVShuttingDownEvent event) { executorService.shutdownNow(); scheduledExecutorService.shutdownNow(); diff --git a/common/src/main/java/com/discordsrv/common/discord/connection/jda/JDAConnectionManager.java b/common/src/main/java/com/discordsrv/common/discord/connection/jda/JDAConnectionManager.java index 9aa0f08e..8226100f 100644 --- a/common/src/main/java/com/discordsrv/common/discord/connection/jda/JDAConnectionManager.java +++ b/common/src/main/java/com/discordsrv/common/discord/connection/jda/JDAConnectionManager.java @@ -25,7 +25,7 @@ import com.discordsrv.api.discord.connection.details.DiscordMemberCachePolicy; import com.discordsrv.api.discord.connection.jda.errorresponse.ErrorCallbackContext; import com.discordsrv.api.discord.entity.DiscordUser; import com.discordsrv.api.discord.entity.guild.DiscordGuildMember; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.eventbus.Subscribe; import com.discordsrv.api.events.lifecycle.DiscordSRVShuttingDownEvent; import com.discordsrv.api.events.placeholder.PlaceholderLookupEvent; @@ -221,7 +221,7 @@ public class JDAConnectionManager implements DiscordConnectionManager { event.addFile(new TextDebugFile("jda_connection_manager.txt", builder)); } - @Subscribe(priority = EventPriority.EARLIEST) + @Subscribe(priority = EventPriorities.EARLIEST) public void onPlaceholderLookup(PlaceholderLookupEvent event) { if (event.isProcessed()) { return; @@ -433,7 +433,7 @@ public class JDAConnectionManager implements DiscordConnectionManager { } } - @Subscribe(priority = EventPriority.LATE) + @Subscribe(priority = EventPriorities.LATE) public void onDSRVShuttingDown(DiscordSRVShuttingDownEvent event) { shutdown(DEFAULT_SHUTDOWN_TIMEOUT); } diff --git a/common/src/main/java/com/discordsrv/common/feature/PresenceUpdaterModule.java b/common/src/main/java/com/discordsrv/common/feature/PresenceUpdaterModule.java index 258d370b..c4c71cf7 100644 --- a/common/src/main/java/com/discordsrv/common/feature/PresenceUpdaterModule.java +++ b/common/src/main/java/com/discordsrv/common/feature/PresenceUpdaterModule.java @@ -18,7 +18,7 @@ package com.discordsrv.common.feature; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.eventbus.Subscribe; import com.discordsrv.api.events.lifecycle.DiscordSRVShuttingDownEvent; import com.discordsrv.api.reload.ReloadResult; @@ -56,7 +56,7 @@ public class PresenceUpdaterModule extends AbstractModule { setPresenceOrSchedule(); } - @Subscribe(priority = EventPriority.EARLIEST) + @Subscribe(priority = EventPriorities.EARLIEST) public void onDiscordSRVShuttingDown(DiscordSRVShuttingDownEvent event) { serverState.set(ServerState.STOPPING); setPresenceOrSchedule(); diff --git a/common/src/main/java/com/discordsrv/common/feature/channel/global/GlobalChannelLookupModule.java b/common/src/main/java/com/discordsrv/common/feature/channel/global/GlobalChannelLookupModule.java index 490d26cd..677d32de 100644 --- a/common/src/main/java/com/discordsrv/common/feature/channel/global/GlobalChannelLookupModule.java +++ b/common/src/main/java/com/discordsrv/common/feature/channel/global/GlobalChannelLookupModule.java @@ -18,7 +18,7 @@ package com.discordsrv.common.feature.channel.global; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.eventbus.Subscribe; import com.discordsrv.api.events.channel.GameChannelLookupEvent; import com.discordsrv.common.DiscordSRV; @@ -30,7 +30,7 @@ public class GlobalChannelLookupModule extends AbstractModule { super(discordSRV); } - @Subscribe(priority = EventPriority.LATE) + @Subscribe(priority = EventPriorities.LATE) public void onGameChannelLookup(GameChannelLookupEvent event) { if (event.getChannelName().equalsIgnoreCase("global")) { if (checkProcessor(event)) { diff --git a/common/src/main/java/com/discordsrv/common/feature/messageforwarding/game/AwardMessageModule.java b/common/src/main/java/com/discordsrv/common/feature/messageforwarding/game/AwardMessageModule.java index 3f88606a..61da20b1 100644 --- a/common/src/main/java/com/discordsrv/common/feature/messageforwarding/game/AwardMessageModule.java +++ b/common/src/main/java/com/discordsrv/common/feature/messageforwarding/game/AwardMessageModule.java @@ -22,7 +22,7 @@ import com.discordsrv.api.channel.GameChannel; import com.discordsrv.api.component.MinecraftComponent; import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessageCluster; import com.discordsrv.api.discord.entity.message.SendableDiscordMessage; -import com.discordsrv.api.eventbus.EventPriority; +import com.discordsrv.api.eventbus.EventPriorities; import com.discordsrv.api.eventbus.Subscribe; import com.discordsrv.api.events.message.forward.game.AwardMessageForwardedEvent; import com.discordsrv.api.events.message.receive.game.AwardMessageReceiveEvent; @@ -65,7 +65,7 @@ public class AwardMessageModule extends AbstractGameMessageModule