2021-08-16 23:42:02 +02:00
|
|
|
package net.minestom.server.event;
|
|
|
|
|
2021-08-20 03:00:25 +02:00
|
|
|
import net.minestom.server.MinecraftServer;
|
2021-08-20 10:05:44 +02:00
|
|
|
import net.minestom.server.event.trait.RecursiveEvent;
|
2021-08-16 23:42:02 +02:00
|
|
|
import net.minestom.server.utils.validate.Check;
|
|
|
|
import org.jetbrains.annotations.Contract;
|
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
|
|
|
|
import java.util.*;
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
|
|
|
import java.util.concurrent.CopyOnWriteArraySet;
|
2021-08-21 10:02:57 +02:00
|
|
|
import java.util.function.BiConsumer;
|
2021-08-16 23:42:02 +02:00
|
|
|
import java.util.function.BiPredicate;
|
|
|
|
import java.util.function.Consumer;
|
|
|
|
|
|
|
|
class EventNodeImpl<T extends Event> implements EventNode<T> {
|
|
|
|
private static final Object GLOBAL_CHILD_LOCK = new Object();
|
|
|
|
|
2021-08-19 06:45:23 +02:00
|
|
|
private final Map<Class<? extends T>, Handle<T>> handleMap = new ConcurrentHashMap<>();
|
2021-08-16 23:42:02 +02:00
|
|
|
private final Map<Class<? extends T>, ListenerEntry<T>> listenerMap = new ConcurrentHashMap<>();
|
2021-08-19 22:21:51 +02:00
|
|
|
private final Set<EventNodeImpl<T>> children = new CopyOnWriteArraySet<>();
|
2021-08-21 01:24:30 +02:00
|
|
|
private final Map<Object, EventNodeImpl<T>> mappedNodeCache = new WeakHashMap<>();
|
2021-08-16 23:42:02 +02:00
|
|
|
|
|
|
|
private final String name;
|
|
|
|
private final EventFilter<T, ?> filter;
|
|
|
|
private final BiPredicate<T, Object> predicate;
|
|
|
|
private final Class<T> eventType;
|
|
|
|
private volatile int priority;
|
|
|
|
private volatile EventNodeImpl<? super T> parent;
|
|
|
|
|
2021-08-21 02:08:25 +02:00
|
|
|
EventNodeImpl(@NotNull String name,
|
|
|
|
@NotNull EventFilter<T, ?> filter,
|
|
|
|
@Nullable BiPredicate<T, Object> predicate) {
|
2021-08-16 23:42:02 +02:00
|
|
|
this.name = name;
|
|
|
|
this.filter = filter;
|
|
|
|
this.predicate = predicate;
|
|
|
|
this.eventType = filter.eventType();
|
|
|
|
}
|
|
|
|
|
2021-08-19 06:45:23 +02:00
|
|
|
@Override
|
2021-08-20 05:21:53 +02:00
|
|
|
public <E extends T> @NotNull ListenerHandle<E> getHandle(@NotNull Class<E> handleType) {
|
|
|
|
//noinspection unchecked
|
2021-08-19 06:45:23 +02:00
|
|
|
return (ListenerHandle<E>) handleMap.computeIfAbsent(handleType,
|
|
|
|
aClass -> new Handle<>(this, (Class<T>) aClass));
|
|
|
|
}
|
|
|
|
|
2021-08-16 23:42:02 +02:00
|
|
|
@Override
|
|
|
|
public <E extends T> @NotNull List<EventNode<E>> findChildren(@NotNull String name, Class<E> eventType) {
|
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
2021-08-19 02:53:13 +02:00
|
|
|
if (children.isEmpty()) return Collections.emptyList();
|
2021-08-16 23:42:02 +02:00
|
|
|
List<EventNode<E>> result = new ArrayList<>();
|
|
|
|
for (EventNode<T> child : children) {
|
|
|
|
if (equals(child, name, eventType)) {
|
|
|
|
result.add((EventNode<E>) child);
|
|
|
|
}
|
|
|
|
result.addAll(child.findChildren(name, eventType));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Contract(pure = true)
|
|
|
|
public @NotNull Set<@NotNull EventNode<T>> getChildren() {
|
|
|
|
return Collections.unmodifiableSet(children);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public <E extends T> void replaceChildren(@NotNull String name, @NotNull Class<E> eventType, @NotNull EventNode<E> eventNode) {
|
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
2021-08-17 02:33:48 +02:00
|
|
|
if (children.isEmpty()) return;
|
2021-08-16 23:42:02 +02:00
|
|
|
for (EventNode<T> child : children) {
|
|
|
|
if (equals(child, name, eventType)) {
|
|
|
|
removeChild(child);
|
|
|
|
addChild(eventNode);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
child.replaceChildren(name, eventType, eventNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void removeChildren(@NotNull String name, @NotNull Class<? extends T> eventType) {
|
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
2021-08-17 02:33:48 +02:00
|
|
|
if (children.isEmpty()) return;
|
2021-08-16 23:42:02 +02:00
|
|
|
for (EventNode<T> child : children) {
|
|
|
|
if (equals(child, name, eventType)) {
|
|
|
|
removeChild(child);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
child.removeChildren(name, eventType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public @NotNull EventNode<T> addChild(@NotNull EventNode<? extends T> child) {
|
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
|
|
|
final var childImpl = (EventNodeImpl<? extends T>) child;
|
|
|
|
Check.stateCondition(childImpl.parent != null, "Node already has a parent");
|
|
|
|
Check.stateCondition(Objects.equals(parent, child), "Cannot have a child as parent");
|
2021-08-21 04:15:47 +02:00
|
|
|
if (!children.add((EventNodeImpl<T>) childImpl)) return this; // Couldn't add the child (already present?)
|
2021-08-19 06:45:23 +02:00
|
|
|
childImpl.parent = this;
|
2021-08-24 11:49:44 +02:00
|
|
|
childImpl.invalidateEventsFor(this);
|
2021-08-16 23:42:02 +02:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public @NotNull EventNode<T> removeChild(@NotNull EventNode<? extends T> child) {
|
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
2021-08-19 06:45:23 +02:00
|
|
|
final var childImpl = (EventNodeImpl<? extends T>) child;
|
2021-08-21 04:15:47 +02:00
|
|
|
final boolean result = this.children.remove(childImpl);
|
|
|
|
if (!result) return this; // Child not found
|
2021-08-19 06:45:23 +02:00
|
|
|
childImpl.parent = null;
|
2021-08-24 11:49:44 +02:00
|
|
|
childImpl.invalidateEventsFor(this);
|
2021-08-16 23:42:02 +02:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public @NotNull EventNode<T> addListener(@NotNull EventListener<? extends T> listener) {
|
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
2021-08-19 22:53:20 +02:00
|
|
|
final var eventType = listener.eventType();
|
2021-08-21 04:15:47 +02:00
|
|
|
ListenerEntry<T> entry = getEntry(eventType);
|
2021-08-16 23:42:02 +02:00
|
|
|
entry.listeners.add((EventListener<T>) listener);
|
2021-08-24 11:49:44 +02:00
|
|
|
invalidateEvent(eventType);
|
2021-08-16 23:42:02 +02:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public @NotNull EventNode<T> removeListener(@NotNull EventListener<? extends T> listener) {
|
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
2021-08-19 22:53:20 +02:00
|
|
|
final var eventType = listener.eventType();
|
2021-08-21 04:15:47 +02:00
|
|
|
ListenerEntry<T> entry = listenerMap.get(eventType);
|
|
|
|
if (entry == null) return this; // There is no listener with such type
|
2021-08-24 11:49:44 +02:00
|
|
|
if (entry.listeners.remove(listener)) invalidateEvent(eventType);
|
2021-08-16 23:42:02 +02:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void map(@NotNull EventNode<? extends T> node, @NotNull Object value) {
|
2021-08-21 01:24:30 +02:00
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
|
|
|
final var nodeImpl = (EventNodeImpl<? extends T>) node;
|
|
|
|
Check.stateCondition(nodeImpl.parent != null, "Node already has a parent");
|
|
|
|
Check.stateCondition(Objects.equals(parent, nodeImpl), "Cannot map to self");
|
2021-08-23 18:43:20 +02:00
|
|
|
EventNodeImpl<T> previous = this.mappedNodeCache.put(value, (EventNodeImpl<T>) nodeImpl);
|
2021-08-21 04:15:47 +02:00
|
|
|
if (previous != null) previous.parent = null;
|
2021-08-21 01:24:30 +02:00
|
|
|
nodeImpl.parent = this;
|
2021-08-24 11:49:44 +02:00
|
|
|
nodeImpl.invalidateEventsFor(this);
|
2021-08-21 01:24:30 +02:00
|
|
|
}
|
2021-08-16 23:42:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean unmap(@NotNull Object value) {
|
2021-08-21 01:24:30 +02:00
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
|
|
|
final var mappedNode = this.mappedNodeCache.remove(value);
|
2021-08-21 04:15:47 +02:00
|
|
|
if (mappedNode == null) return false; // Mapped node not found
|
2021-08-21 01:24:30 +02:00
|
|
|
final var childImpl = (EventNodeImpl<? extends T>) mappedNode;
|
|
|
|
childImpl.parent = null;
|
2021-08-24 11:49:44 +02:00
|
|
|
childImpl.invalidateEventsFor(this);
|
2021-08-21 01:24:30 +02:00
|
|
|
return true;
|
|
|
|
}
|
2021-08-16 23:42:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2021-08-17 02:24:21 +02:00
|
|
|
public void register(@NotNull EventBinding<? extends T> binding) {
|
2021-08-19 07:12:53 +02:00
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
|
|
|
for (var eventType : binding.eventTypes()) {
|
|
|
|
ListenerEntry<T> entry = getEntry((Class<? extends T>) eventType);
|
|
|
|
final boolean added = entry.bindingConsumers.add((Consumer<T>) binding.consumer(eventType));
|
2021-08-24 11:49:44 +02:00
|
|
|
if (added) invalidateEvent((Class<? extends T>) eventType);
|
2021-08-19 07:12:53 +02:00
|
|
|
}
|
|
|
|
}
|
2021-08-17 02:24:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void unregister(@NotNull EventBinding<? extends T> binding) {
|
2021-08-19 07:12:53 +02:00
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
|
|
|
for (var eventType : binding.eventTypes()) {
|
|
|
|
ListenerEntry<T> entry = listenerMap.get(eventType);
|
|
|
|
if (entry == null) return;
|
|
|
|
final boolean removed = entry.bindingConsumers.remove(binding.consumer(eventType));
|
2021-08-24 11:49:44 +02:00
|
|
|
if (removed) invalidateEvent((Class<? extends T>) eventType);
|
2021-08-19 07:12:53 +02:00
|
|
|
}
|
|
|
|
}
|
2021-08-16 23:42:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public @NotNull Class<T> getEventType() {
|
|
|
|
return eventType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public @NotNull String getName() {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getPriority() {
|
|
|
|
return priority;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public @NotNull EventNode<T> setPriority(int priority) {
|
|
|
|
this.priority = priority;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public @Nullable EventNode<? super T> getParent() {
|
|
|
|
return parent;
|
|
|
|
}
|
|
|
|
|
2021-08-24 11:49:44 +02:00
|
|
|
private void invalidateEventsFor(EventNodeImpl<? super T> node) {
|
|
|
|
for (Class<? extends T> eventType : listenerMap.keySet()) {
|
|
|
|
node.invalidateEvent(eventType);
|
|
|
|
}
|
2021-08-26 13:24:16 +02:00
|
|
|
// TODO bindings?
|
|
|
|
for (EventNodeImpl<T> child : children) {
|
|
|
|
child.invalidateEventsFor(node);
|
|
|
|
}
|
2021-08-16 23:42:02 +02:00
|
|
|
}
|
|
|
|
|
2021-08-24 11:49:44 +02:00
|
|
|
private void invalidateEvent(Class<? extends T> eventClass) {
|
2021-08-20 10:13:08 +02:00
|
|
|
forTargetEvents(eventClass, type -> {
|
2021-08-24 11:49:44 +02:00
|
|
|
Handle<? super T> handle = handleMap.get(type);
|
|
|
|
if (handle != null) handle.updated = false;
|
2021-08-20 10:13:08 +02:00
|
|
|
});
|
2021-08-24 11:49:44 +02:00
|
|
|
final EventNodeImpl<? super T> parent = this.parent;
|
|
|
|
if (parent != null) parent.invalidateEvent(eventClass);
|
2021-08-16 23:42:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private ListenerEntry<T> getEntry(Class<? extends T> type) {
|
2021-08-19 06:45:23 +02:00
|
|
|
return listenerMap.computeIfAbsent(type, aClass -> new ListenerEntry<>());
|
2021-08-16 23:42:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static boolean equals(EventNode<?> node, String name, Class<?> eventType) {
|
2021-08-24 11:49:44 +02:00
|
|
|
return node.getName().equals(name) && eventType.isAssignableFrom((node.getEventType()));
|
2021-08-16 23:42:02 +02:00
|
|
|
}
|
|
|
|
|
2021-08-20 10:13:08 +02:00
|
|
|
private static void forTargetEvents(Class<?> type, Consumer<Class<?>> consumer) {
|
|
|
|
consumer.accept(type);
|
|
|
|
// Recursion
|
|
|
|
if (RecursiveEvent.class.isAssignableFrom(type)) {
|
2021-08-20 10:27:22 +02:00
|
|
|
final Class<?> superclass = type.getSuperclass();
|
|
|
|
if (superclass != null && RecursiveEvent.class.isAssignableFrom(superclass)) {
|
|
|
|
forTargetEvents(superclass, consumer);
|
2021-08-20 10:05:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-16 23:42:02 +02:00
|
|
|
private static class ListenerEntry<T extends Event> {
|
|
|
|
final List<EventListener<T>> listeners = new CopyOnWriteArrayList<>();
|
2021-08-19 07:12:53 +02:00
|
|
|
final Set<Consumer<T>> bindingConsumers = new CopyOnWriteArraySet<>();
|
2021-08-19 06:45:23 +02:00
|
|
|
}
|
2021-08-16 23:42:02 +02:00
|
|
|
|
2021-08-19 06:45:23 +02:00
|
|
|
private static final class Handle<E extends Event> implements ListenerHandle<E> {
|
2021-08-20 05:57:29 +02:00
|
|
|
private final EventNodeImpl<E> node;
|
2021-08-19 06:45:23 +02:00
|
|
|
private final Class<E> eventType;
|
2021-08-24 15:12:39 +02:00
|
|
|
private Consumer<E>[] listeners = new Consumer[0];
|
|
|
|
private final List<Consumer<E>> listenersCache = new ArrayList<>();
|
2021-08-19 06:45:23 +02:00
|
|
|
private volatile boolean updated;
|
|
|
|
|
2021-08-20 05:57:29 +02:00
|
|
|
Handle(EventNodeImpl<E> node, Class<E> eventType) {
|
2021-08-19 06:45:23 +02:00
|
|
|
this.node = node;
|
|
|
|
this.eventType = eventType;
|
2021-08-16 23:42:02 +02:00
|
|
|
}
|
2021-08-20 03:00:25 +02:00
|
|
|
|
2021-08-24 15:35:09 +02:00
|
|
|
@Override
|
|
|
|
public void call(@NotNull E event) {
|
|
|
|
if (!updated) update();
|
|
|
|
final Consumer<E>[] listeners = this.listeners;
|
|
|
|
if (listeners.length == 0) return;
|
|
|
|
for (Consumer<E> listener : listeners) {
|
|
|
|
listener.accept(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean hasListener() {
|
|
|
|
if (!updated) update();
|
|
|
|
return listeners.length > 0;
|
|
|
|
}
|
|
|
|
|
2021-08-20 05:57:29 +02:00
|
|
|
void update() {
|
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
2021-08-24 15:12:39 +02:00
|
|
|
this.listenersCache.clear();
|
2021-08-20 05:57:29 +02:00
|
|
|
recursiveUpdate(node);
|
2021-08-24 15:12:39 +02:00
|
|
|
this.listeners = listenersCache.toArray(Consumer[]::new);
|
2021-08-21 01:24:30 +02:00
|
|
|
this.updated = true;
|
2021-08-20 05:57:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void recursiveUpdate(EventNodeImpl<E> targetNode) {
|
2021-08-21 01:24:30 +02:00
|
|
|
// Standalone listeners
|
2021-08-21 02:00:30 +02:00
|
|
|
forTargetEvents(eventType, type -> {
|
2021-08-21 02:08:25 +02:00
|
|
|
final ListenerEntry<E> entry = targetNode.listenerMap.get(type);
|
|
|
|
if (entry != null) appendEntries(entry, targetNode);
|
2021-08-20 10:05:44 +02:00
|
|
|
});
|
2021-08-21 01:24:30 +02:00
|
|
|
// Mapped nodes
|
2021-08-21 02:00:30 +02:00
|
|
|
handleMappedNode(targetNode);
|
2021-08-20 03:00:25 +02:00
|
|
|
// Add children
|
2021-08-22 05:02:44 +02:00
|
|
|
final Set<EventNodeImpl<E>> children = targetNode.children;
|
2021-08-20 03:00:25 +02:00
|
|
|
if (children.isEmpty()) return;
|
|
|
|
children.stream()
|
2021-08-21 02:00:30 +02:00
|
|
|
.filter(child -> child.eventType.isAssignableFrom(eventType)) // Invalid event type
|
2021-08-20 03:00:25 +02:00
|
|
|
.sorted(Comparator.comparing(EventNode::getPriority))
|
2021-08-20 05:57:29 +02:00
|
|
|
.forEach(this::recursiveUpdate);
|
2021-08-20 03:00:25 +02:00
|
|
|
}
|
|
|
|
|
2021-08-22 05:02:44 +02:00
|
|
|
/**
|
|
|
|
* Add the node's listeners from {@link EventNode#map(EventNode, Object)}.
|
|
|
|
* The goal is to limit the amount of map lookup.
|
|
|
|
*/
|
2021-08-21 02:00:30 +02:00
|
|
|
private void handleMappedNode(EventNodeImpl<E> targetNode) {
|
|
|
|
final var mappedNodeCache = targetNode.mappedNodeCache;
|
|
|
|
if (mappedNodeCache.isEmpty()) return;
|
|
|
|
Set<EventFilter<E, ?>> filters = new HashSet<>(mappedNodeCache.size());
|
2021-08-22 04:17:42 +02:00
|
|
|
Map<Object, Handle<E>> handlers = new HashMap<>(mappedNodeCache.size());
|
2021-08-21 02:00:30 +02:00
|
|
|
// Retrieve all filters used to retrieve potential handlers
|
|
|
|
for (var mappedEntry : mappedNodeCache.entrySet()) {
|
|
|
|
final EventNodeImpl<E> mappedNode = mappedEntry.getValue();
|
2021-08-22 05:02:44 +02:00
|
|
|
final Handle<E> handle = (Handle<E>) mappedNode.getHandle(eventType);
|
2021-08-24 15:35:09 +02:00
|
|
|
if (!handle.hasListener()) continue; // Implicit update
|
2021-08-21 10:02:57 +02:00
|
|
|
filters.add(mappedNode.filter);
|
2021-08-22 04:17:42 +02:00
|
|
|
handlers.put(mappedEntry.getKey(), handle);
|
2021-08-21 02:00:30 +02:00
|
|
|
}
|
|
|
|
// If at least one mapped node listen to this handle type,
|
|
|
|
// loop through them and forward to mapped node if there is a match
|
|
|
|
if (!filters.isEmpty()) {
|
2021-08-21 07:01:48 +02:00
|
|
|
final var filterList = List.copyOf(filters);
|
|
|
|
final int size = filterList.size();
|
2021-08-21 10:02:57 +02:00
|
|
|
final BiConsumer<EventFilter<E, ?>, E> mapper = (filter, event) -> {
|
|
|
|
final Object handler = filter.castHandler(event);
|
2021-08-22 05:02:44 +02:00
|
|
|
final Handle<E> handle = handlers.get(handler);
|
|
|
|
if (handle != null) { // Run the listeners of the mapped node
|
2021-08-22 04:17:42 +02:00
|
|
|
if (!handle.updated) handle.update();
|
|
|
|
for (Consumer<E> listener : handle.listeners) {
|
|
|
|
listener.accept(event);
|
|
|
|
}
|
|
|
|
}
|
2021-08-21 10:02:57 +02:00
|
|
|
};
|
2021-08-21 07:01:48 +02:00
|
|
|
if (size == 1) {
|
|
|
|
final var firstFilter = filterList.get(0);
|
2021-08-21 10:02:57 +02:00
|
|
|
// Common case where there is only one filter
|
2021-08-24 15:12:39 +02:00
|
|
|
this.listenersCache.add(event -> mapper.accept(firstFilter, event));
|
2021-08-21 07:01:48 +02:00
|
|
|
} else if (size == 2) {
|
|
|
|
final var firstFilter = filterList.get(0);
|
|
|
|
final var secondFilter = filterList.get(1);
|
2021-08-24 15:12:39 +02:00
|
|
|
this.listenersCache.add(event -> {
|
2021-08-21 10:02:57 +02:00
|
|
|
mapper.accept(firstFilter, event);
|
|
|
|
mapper.accept(secondFilter, event);
|
2021-08-21 07:01:48 +02:00
|
|
|
});
|
|
|
|
} else {
|
2021-08-24 15:12:39 +02:00
|
|
|
this.listenersCache.add(event -> {
|
2021-08-21 07:01:48 +02:00
|
|
|
for (var filter : filterList) {
|
2021-08-21 10:02:57 +02:00
|
|
|
mapper.accept(filter, event);
|
2021-08-21 07:01:48 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2021-08-21 02:00:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-22 05:02:44 +02:00
|
|
|
/**
|
|
|
|
* Add listeners from {@link EventNode#addListener(EventListener)} and
|
|
|
|
* {@link EventNode#register(EventBinding)} to the handle list.
|
|
|
|
* <p>
|
|
|
|
* Most computation should ideally be done outside the consumers as a one-time cost.
|
|
|
|
*/
|
2021-08-21 02:08:25 +02:00
|
|
|
private void appendEntries(ListenerEntry<E> entry, EventNodeImpl<E> targetNode) {
|
2021-08-20 03:00:25 +02:00
|
|
|
final var filter = targetNode.filter;
|
|
|
|
final var predicate = targetNode.predicate;
|
2021-08-22 03:08:37 +02:00
|
|
|
|
|
|
|
final boolean hasPredicate = predicate != null;
|
2021-08-24 15:12:39 +02:00
|
|
|
final EventListener<E>[] listenersCopy = entry.listeners.toArray(EventListener[]::new);
|
|
|
|
final Consumer<E>[] bindingsCopy = entry.bindingConsumers.toArray(Consumer[]::new);
|
|
|
|
final boolean listenersEmpty = listenersCopy.length == 0;
|
|
|
|
final boolean bindingsEmpty = bindingsCopy.length == 0;
|
|
|
|
if (!hasPredicate && listenersEmpty && bindingsEmpty)
|
2021-08-22 03:08:37 +02:00
|
|
|
return; // Nothing to run
|
|
|
|
|
2021-08-24 15:12:39 +02:00
|
|
|
if (!hasPredicate && bindingsEmpty && listenersCopy.length == 1) {
|
2021-08-22 03:08:37 +02:00
|
|
|
// Only one normal listener
|
2021-08-24 15:12:39 +02:00
|
|
|
final EventListener<E> listener = listenersCopy[0];
|
|
|
|
this.listenersCache.add(e -> callListener(targetNode, listener, e));
|
2021-08-22 03:08:37 +02:00
|
|
|
return;
|
|
|
|
}
|
2021-08-22 04:39:53 +02:00
|
|
|
// Worse case scenario, try to run everything
|
2021-08-24 15:12:39 +02:00
|
|
|
this.listenersCache.add(e -> {
|
2021-08-22 03:08:37 +02:00
|
|
|
if (hasPredicate) {
|
2021-08-22 05:02:44 +02:00
|
|
|
final Object value = filter.getHandler(e);
|
2021-08-22 18:35:19 +02:00
|
|
|
try {
|
|
|
|
if (!predicate.test(e, value)) return;
|
|
|
|
} catch (Throwable t) {
|
|
|
|
MinecraftServer.getExceptionManager().handleException(t);
|
|
|
|
return;
|
|
|
|
}
|
2021-08-22 03:08:37 +02:00
|
|
|
}
|
2021-08-24 15:12:39 +02:00
|
|
|
if (!listenersEmpty) {
|
2021-08-22 03:08:37 +02:00
|
|
|
for (EventListener<E> listener : listenersCopy) {
|
2021-08-20 03:00:25 +02:00
|
|
|
callListener(targetNode, listener, e);
|
2021-08-22 03:08:37 +02:00
|
|
|
}
|
2021-08-20 03:00:25 +02:00
|
|
|
}
|
2021-08-24 15:12:39 +02:00
|
|
|
if (!bindingsEmpty) {
|
2021-08-22 03:08:37 +02:00
|
|
|
for (Consumer<E> eConsumer : bindingsCopy) {
|
2021-08-22 18:35:19 +02:00
|
|
|
try {
|
|
|
|
eConsumer.accept(e);
|
|
|
|
} catch (Throwable t) {
|
|
|
|
MinecraftServer.getExceptionManager().handleException(t);
|
|
|
|
}
|
2021-08-22 03:08:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2021-08-20 03:00:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static <E extends Event> void callListener(EventNodeImpl<E> targetNode, EventListener<E> listener, E event) {
|
|
|
|
EventListener.Result result;
|
|
|
|
try {
|
|
|
|
result = listener.run(event);
|
2021-08-22 18:35:19 +02:00
|
|
|
} catch (Throwable t) {
|
2021-08-20 03:00:25 +02:00
|
|
|
result = EventListener.Result.EXCEPTION;
|
2021-08-22 18:35:19 +02:00
|
|
|
MinecraftServer.getExceptionManager().handleException(t);
|
2021-08-20 03:00:25 +02:00
|
|
|
}
|
|
|
|
if (result == EventListener.Result.EXPIRED) {
|
|
|
|
targetNode.removeListener(listener);
|
|
|
|
}
|
|
|
|
}
|
2021-08-16 23:42:02 +02:00
|
|
|
}
|
|
|
|
}
|