diff --git a/src/api/java/baritone/api/event/listener/IEventBus.java b/src/api/java/baritone/api/event/listener/IEventBus.java index 52240a7c0..78118f5f8 100644 --- a/src/api/java/baritone/api/event/listener/IEventBus.java +++ b/src/api/java/baritone/api/event/listener/IEventBus.java @@ -21,6 +21,8 @@ * A type of {@link IGameEventListener} that can have additional listeners * registered so that they receive the events that are dispatched to this * listener. + *

+ * Listeners with higher priority will be called first. * * @author Brady * @since 11/14/2018 @@ -29,8 +31,17 @@ public interface IEventBus extends IGameEventListener { /** * Registers the specified {@link IGameEventListener} to this event bus + * using a default priority {@code 0} * * @param listener The listener */ void registerEventListener(IGameEventListener listener); + + /** + * Registers the specified {@link IGameEventListener} to this event bus. + * + * @param priority The listener priority + * @param listener The listener + */ + void registerEventListener(int priority, IGameEventListener listener); } diff --git a/src/main/java/baritone/event/GameEventHandler.java b/src/main/java/baritone/event/GameEventHandler.java index d716ff849..40ebb3ac4 100644 --- a/src/main/java/baritone/event/GameEventHandler.java +++ b/src/main/java/baritone/event/GameEventHandler.java @@ -27,6 +27,8 @@ import baritone.cache.CachedChunk; import baritone.cache.WorldProvider; import baritone.utils.BlockStateInterface; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntArrayList; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; @@ -43,7 +45,9 @@ public final class GameEventHandler implements IEventBus, Helper { private final Baritone baritone; + // reading/iterating `listeners` is allowed without synchronization private final List listeners = new CopyOnWriteArrayList<>(); + private final IntList priorities = new IntArrayList(); public GameEventHandler(Baritone baritone) { this.baritone = baritone; @@ -184,6 +188,16 @@ public void onPathEvent(PathEvent event) { @Override public final void registerEventListener(IGameEventListener listener) { - this.listeners.add(listener); + this.registerEventListener(0, listener); + } + + @Override + public final synchronized void registerEventListener(int priority, IGameEventListener listener) { + int i = 0; + while (i < this.listeners.size() && priority <= this.priorities.getInt(i)) { + ++i; + } + this.listeners.add(i, listener); + this.priorities.add(i, priority); } } diff --git a/src/test/java/baritone/event/GameEventHandlerTest.java b/src/test/java/baritone/event/GameEventHandlerTest.java new file mode 100644 index 000000000..04788c727 --- /dev/null +++ b/src/test/java/baritone/event/GameEventHandlerTest.java @@ -0,0 +1,68 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.event; + +import baritone.api.event.listener.AbstractGameEventListener; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class GameEventHandlerTest { + + @Test + public void testListenerOrder() { + GameEventHandler bus = new GameEventHandler(null /* baritone */); + + List output = new ArrayList<>(); + + bus.registerEventListener(0, new TestEventListener("0 0", output)); + bus.registerEventListener(0, new TestEventListener("0 1", output)); + bus.registerEventListener(1, new TestEventListener("1 2", output)); + bus.registerEventListener(new TestEventListener("_ 3", output)); + bus.registerEventListener(-1, new TestEventListener("-1 4", output)); + bus.registerEventListener(1, new TestEventListener("1 5", output)); + bus.registerEventListener(0, new TestEventListener("0 6", output)); + + bus.onPlayerDeath(); + + assertEquals(new ArrayList<>(Arrays.asList( + "1 2", "1 5", "0 0", "0 1", "_ 3", "0 6", "-1 4" + )), + output + ); + } + + private static class TestEventListener implements AbstractGameEventListener { + private final String id; + private final List output; + + public TestEventListener(String id, List output) { + this.id = id; + this.output = output; + } + + @Override + public void onPlayerDeath() { // the only event without an argument ☺ + output.add(id); + } + } +}