diff --git a/src/main/java/net/minestom/server/event/EventNode.java b/src/main/java/net/minestom/server/event/EventNode.java index 26a00a011..5d166b00e 100644 --- a/src/main/java/net/minestom/server/event/EventNode.java +++ b/src/main/java/net/minestom/server/event/EventNode.java @@ -213,6 +213,20 @@ public interface EventNode { */ @NotNull ListenerHandle getHandle(@NotNull Class handleType); + /** + * Gets if any listener has been registered for the given handle. + * May trigger an update if the cached data is not correct. + *

+ * Useful if you are able to avoid expensive computation in the case where + * the event is unused. Be aware that {@link #call(Event, ListenerHandle)} + * has similar optimization built-in. + * + * @param handle the listener handle + * @param the event type + * @return true if the event has 1 or more listeners + */ + boolean hasListener(@NotNull ListenerHandle handle); + /** * Execute a cancellable event with a callback to execute if the event is successful. * Event conditions and propagation is the same as {@link #call(Event)}. diff --git a/src/main/java/net/minestom/server/event/EventNodeImpl.java b/src/main/java/net/minestom/server/event/EventNodeImpl.java index 09edc9f94..15fb2b3ba 100644 --- a/src/main/java/net/minestom/server/event/EventNodeImpl.java +++ b/src/main/java/net/minestom/server/event/EventNodeImpl.java @@ -43,11 +43,7 @@ class EventNodeImpl implements EventNode { Check.argCondition(castedHandle.node != this, "Invalid handle owner"); List> listeners = castedHandle.listeners; if (!castedHandle.updated) { - synchronized (GLOBAL_CHILD_LOCK) { - listeners.clear(); - castedHandle.update(this); - castedHandle.updated = true; - } + castedHandle.update(); } if (listeners.isEmpty()) return; for (Consumer listener : listeners) { @@ -62,6 +58,16 @@ class EventNodeImpl implements EventNode { aClass -> new Handle<>(this, (Class) aClass)); } + @Override + public boolean hasListener(@NotNull ListenerHandle handle) { + var castedHandle = (Handle) handle; + List> listeners = castedHandle.listeners; + if (!castedHandle.updated) { + castedHandle.update(); + } + return !listeners.isEmpty(); + } + @Override public @NotNull List> findChildren(@NotNull String name, Class eventType) { synchronized (GLOBAL_CHILD_LOCK) { @@ -255,17 +261,25 @@ class EventNodeImpl implements EventNode { } private static final class Handle implements ListenerHandle { - private final EventNode node; + private final EventNodeImpl node; private final Class eventType; private final List> listeners = new CopyOnWriteArrayList<>(); private volatile boolean updated; - Handle(EventNode node, Class eventType) { + Handle(EventNodeImpl node, Class eventType) { this.node = node; this.eventType = eventType; } - void update(EventNodeImpl targetNode) { + void update() { + synchronized (GLOBAL_CHILD_LOCK) { + listeners.clear(); + recursiveUpdate(node); + updated = true; + } + } + + private void recursiveUpdate(EventNodeImpl targetNode) { final var handleType = eventType; ListenerEntry entry = targetNode.listenerMap.get(handleType); if (entry != null) appendEntry(listeners, entry, targetNode); @@ -275,7 +289,7 @@ class EventNodeImpl implements EventNode { children.stream() .filter(child -> child.eventType.isAssignableFrom(handleType)) // Invalid event type .sorted(Comparator.comparing(EventNode::getPriority)) - .forEach(this::update); + .forEach(this::recursiveUpdate); } static void appendEntry(List> handleListeners, ListenerEntry entry, EventNodeImpl targetNode) {