diff --git a/src/main/java/net/tfminecraft/conditionalevents/managers/EventsManager.java b/src/main/java/net/tfminecraft/conditionalevents/managers/EventsManager.java index 300bba1..0045b8f 100644 --- a/src/main/java/net/tfminecraft/conditionalevents/managers/EventsManager.java +++ b/src/main/java/net/tfminecraft/conditionalevents/managers/EventsManager.java @@ -16,20 +16,36 @@ import org.bukkit.event.Event; import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; import java.util.UUID; import java.util.List; +import java.util.Map; public class EventsManager { private ConditionalEvents plugin; private ArrayList events; + // Every listener asks for its type's events on each Bukkit event (and the ProtocolLib listener on each + // outgoing chat packet), so they are grouped once per load instead of scanning all events each time. + // Replaced as a whole on reload; the ProtocolLib listener reads it off the main thread. + private volatile Map> eventsByType; public EventsManager(ConditionalEvents plugin){ this.plugin = plugin; - this.events = new ArrayList(); + setEvents(new ArrayList()); } public void setEvents(ArrayList events) { this.events = events; + Map> byType = new EnumMap<>(EventType.class); + for(CEEvent event : events){ + byType.computeIfAbsent(event.getEventType(), type -> new ArrayList<>()).add(event); + } + this.eventsByType = byType; + } + + private List eventsOfType(EventType eventType){ + return eventsByType.getOrDefault(eventType, Collections.emptyList()); } public ArrayList getEvents() { @@ -173,8 +189,9 @@ public void checkSingularEvent(ConditionEvent conditionEvent,CEEvent event){ public ArrayList getValidEvents(EventType eventType){ ArrayList validEvents = new ArrayList(); - for(CEEvent event : events){ - if(event.getEventType().equals(eventType) && event.isEnabled()){ + // /ce enable and /ce disable change isEnabled at runtime, so it is checked here, not in the index + for(CEEvent event : eventsOfType(eventType)){ + if(event.isEnabled()){ validEvents.add(event); } } @@ -182,13 +199,7 @@ public ArrayList getValidEvents(EventType eventType){ } public ArrayList getEventsByType(EventType eventType){ - ArrayList validEvents = new ArrayList<>(); - for(CEEvent event : events){ - if(event.getEventType().equals(eventType)){ - validEvents.add(event); - } - } - return validEvents; + return new ArrayList<>(eventsOfType(eventType)); } private CheckConditionsResult checkConditions(ConditionEvent conditionEvent, boolean isPlaceholderAPI){ diff --git a/src/test/java/net/tfminecraft/conditionalevents/managers/EventsManagerIndexTest.java b/src/test/java/net/tfminecraft/conditionalevents/managers/EventsManagerIndexTest.java new file mode 100644 index 0000000..ded4969 --- /dev/null +++ b/src/test/java/net/tfminecraft/conditionalevents/managers/EventsManagerIndexTest.java @@ -0,0 +1,68 @@ +package net.tfminecraft.conditionalevents.managers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import net.tfminecraft.conditionalevents.ConditionalEvents; +import net.tfminecraft.conditionalevents.model.CEEvent; +import net.tfminecraft.conditionalevents.model.EventType; + +class EventsManagerIndexTest { + + private static CEEvent event(String name, EventType type, boolean enabled) { + CEEvent event = new CEEvent(name); + event.setEventType(type); + event.setEnabled(enabled); + return event; + } + + @Test + void validEventsAreThatTypesEnabledEventsInFileOrder() { + EventsManager manager = new EventsManager(mock(ConditionalEvents.class)); + CEEvent first = event("first", EventType.ITEM_INTERACT, true); + CEEvent command = event("command", EventType.PLAYER_COMMAND, true); + CEEvent off = event("off", EventType.ITEM_INTERACT, false); + CEEvent second = event("second", EventType.ITEM_INTERACT, true); + manager.setEvents(new ArrayList<>(List.of(first, command, off, second))); + + assertEquals(List.of(first, second), manager.getValidEvents(EventType.ITEM_INTERACT)); + assertEquals(List.of(first, off, second), manager.getEventsByType(EventType.ITEM_INTERACT)); + assertEquals(List.of(command), manager.getValidEvents(EventType.PLAYER_COMMAND)); + assertTrue(manager.getValidEvents(EventType.PROTOCOLLIB_RECEIVE_MESSAGE).isEmpty()); + assertTrue(manager.getEventsByType(EventType.PROTOCOLLIB_RECEIVE_MESSAGE).isEmpty()); + } + + @Test + void enablingAtRuntimeAndReloadingAreSeen() { + EventsManager manager = new EventsManager(mock(ConditionalEvents.class)); + assertTrue(manager.getValidEvents(EventType.ITEM_INTERACT).isEmpty()); + CEEvent off = event("off", EventType.ITEM_INTERACT, false); + manager.setEvents(new ArrayList<>(List.of(off))); + assertTrue(manager.getValidEvents(EventType.ITEM_INTERACT).isEmpty()); + off.setEnabled(true); + assertEquals(List.of(off), manager.getValidEvents(EventType.ITEM_INTERACT)); + + CEEvent reloaded = event("reloaded", EventType.PLAYER_JOIN, true); + manager.setEvents(new ArrayList<>(List.of(reloaded))); + assertTrue(manager.getValidEvents(EventType.ITEM_INTERACT).isEmpty()); + assertEquals(List.of(reloaded), manager.getValidEvents(EventType.PLAYER_JOIN)); + } + + @Test + void callersGetTheirOwnLists() { + EventsManager manager = new EventsManager(mock(ConditionalEvents.class)); + CEEvent join = event("join", EventType.PLAYER_JOIN, true); + manager.setEvents(new ArrayList<>(List.of(join))); + ArrayList copy = manager.getEventsByType(EventType.PLAYER_JOIN); + copy.clear(); + assertEquals(List.of(join), manager.getEventsByType(EventType.PLAYER_JOIN)); + assertNotSame(manager.getValidEvents(EventType.PLAYER_JOIN), manager.getValidEvents(EventType.PLAYER_JOIN)); + } +}