diff --git a/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java b/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java index 1562c03e3..4203024a3 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java @@ -22,7 +22,9 @@ import static java.util.Objects.requireNonNull; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import com.google.common.base.VerifyException; +import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.HashMultimap; +import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimap; import com.google.common.reflect.TypeToken; import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -87,7 +89,7 @@ public class VelocityEventManager implements EventManager { private final ExecutorService asyncExecutor; private final PluginManager pluginManager; - private final Multimap, HandlerRegistration> handlersByType = HashMultimap.create(); + private final ListMultimap, HandlerRegistration> handlersByType = ArrayListMultimap.create(); private final LoadingCache, HandlersCache> handlersCache = Caffeine.newBuilder().build(this::bakeHandlers); diff --git a/proxy/src/test/java/com/velocitypowered/proxy/event/EventTest.java b/proxy/src/test/java/com/velocitypowered/proxy/event/EventTest.java index e3cb7119a..740b21e22 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/event/EventTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/event/EventTest.java @@ -28,6 +28,7 @@ import com.velocitypowered.api.event.PostOrder; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.proxy.testutil.FakePluginManager; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; @@ -82,6 +83,37 @@ public class EventTest { result++; thread = Thread.currentThread(); } + + } + + @Test + void listenerOrderPreserved() throws Exception { + final AtomicLong listenerAInvoked = new AtomicLong(); + final AtomicLong listenerBInvoked = new AtomicLong(); + final AtomicLong listenerCInvoked = new AtomicLong(); + + eventManager.register(FakePluginManager.PLUGIN_A, TestEvent.class, event -> { + listenerAInvoked.set(System.nanoTime()); + return null; + }); + eventManager.register(FakePluginManager.PLUGIN_B, TestEvent.class, event -> { + listenerBInvoked.set(System.nanoTime()); + return null; + }); + eventManager.register(FakePluginManager.PLUGIN_A, TestEvent.class, event -> { + listenerCInvoked.set(System.nanoTime()); + return null; + }); + + try { + eventManager.fire(new TestEvent()).get(); + } finally { + eventManager.unregisterListeners(FakePluginManager.PLUGIN_A); + } + + // Check that the order is A < B < C. Check only that A < B and B < C as B < C and A < B => A < C. + assertTrue(listenerAInvoked.get() < listenerBInvoked.get(), "Listener B invoked before A!"); + assertTrue(listenerBInvoked.get() < listenerCInvoked.get(), "Listener C invoked before B!"); } @Test