Minestom/src/main/java/net/minestom/server/event/EventNodeImpl.java

386 lines
15 KiB
Java
Raw Normal View History

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;
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<>();
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();
}
@Override
2021-08-20 05:21:53 +02:00
public <E extends T> void call(@NotNull E event, @NotNull ListenerHandle<E> handle) {
2021-08-21 04:15:47 +02:00
final Handle<T> castedHandle = (Handle<T>) handle;
2021-08-20 05:21:53 +02:00
Check.argCondition(castedHandle.node != this, "Invalid handle owner");
2021-08-21 04:15:47 +02:00
if (!castedHandle.updated) castedHandle.update();
final List<Consumer<T>> listeners = castedHandle.listeners;
2021-08-19 06:45:23 +02:00
if (listeners.isEmpty()) return;
for (Consumer<T> listener : listeners) {
listener.accept(event);
2021-08-16 23:42:02 +02:00
}
}
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-20 05:57:29 +02:00
@Override
2021-08-21 04:21:03 +02:00
public boolean hasListener(@NotNull ListenerHandle<? extends T> handle) {
2021-08-21 04:15:47 +02:00
final Handle<T> castedHandle = (Handle<T>) handle;
if (!castedHandle.updated) castedHandle.update();
return !castedHandle.listeners.isEmpty();
2021-08-20 05:57:29 +02:00
}
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 void removeChildren(@NotNull String name) {
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-21 04:15:47 +02:00
childImpl.propagateEvents(this); // Propagate after setting the parent
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
childImpl.propagateEvents(parent); // Propagate before removing the parent
2021-08-19 06:45:23 +02:00
childImpl.parent = null;
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-21 04:15:47 +02:00
propagateEvent(parent, 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-16 23:42:02 +02:00
var listeners = entry.listeners;
2021-08-21 04:15:47 +02:00
if (listeners.remove(listener)) propagateEvent(parent, 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");
var 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-21 04:15:47 +02:00
nodeImpl.propagateEvents(this); // Propagate after setting the parent
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;
2021-08-21 04:15:47 +02:00
childImpl.propagateEvents(parent); // Propagate before removing the parent
2021-08-21 01:24:30 +02:00
childImpl.parent = null;
return true;
}
2021-08-16 23:42:02 +02:00
}
@Override
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-21 04:15:47 +02:00
if (added) propagateEvent(parent, (Class<? extends T>) eventType);
2021-08-19 07:12:53 +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-21 04:15:47 +02:00
if (removed) propagateEvent(parent, (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-21 04:15:47 +02:00
private void propagateEvents(EventNodeImpl<? super T> parent) {
this.listenerMap.keySet().forEach(aClass -> propagateEvent(parent, aClass));
2021-08-16 23:42:02 +02:00
}
2021-08-21 04:15:47 +02:00
private void propagateEvent(EventNodeImpl parent, Class<? extends T> eventClass) {
if (parent == null) return;
2021-08-20 10:13:08 +02:00
forTargetEvents(eventClass, type -> {
2021-08-21 04:15:47 +02:00
Handle<? super T> parentHandle = (Handle<? super T>) parent.handleMap.get(type);
2021-08-20 10:13:08 +02:00
if (parentHandle == null) return;
parentHandle.updated = false;
2021-08-21 04:15:47 +02:00
parent.propagateEvent(parent.parent, type);
2021-08-20 10:13:08 +02:00
});
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) {
final boolean nameCheck = node.getName().equals(name);
final boolean typeCheck = eventType.isAssignableFrom(((EventNodeImpl<?>) node).eventType);
return nameCheck && typeCheck;
}
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;
private final List<Consumer<E>> listeners = new CopyOnWriteArrayList<>();
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-20 05:57:29 +02:00
void update() {
synchronized (GLOBAL_CHILD_LOCK) {
2021-08-21 01:24:30 +02:00
this.listeners.clear();
2021-08-20 05:57:29 +02:00
recursiveUpdate(node);
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
final var children = targetNode.children;
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-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());
// Retrieve all filters used to retrieve potential handlers
for (var mappedEntry : mappedNodeCache.entrySet()) {
final EventNodeImpl<E> mappedNode = mappedEntry.getValue();
if (!mappedNode.eventType.isAssignableFrom(eventType)) continue;
final var mappedListeners = mappedNode.listenerMap;
if (mappedListeners.isEmpty())
continue; // The mapped node does not have any listener (perhaps throw a warning?)
forTargetEvents(eventType, type -> {
if (!mappedListeners.containsKey(type)) return; // No normal listener to this handle type
filters.add(mappedNode.filter);
});
}
// 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()) {
this.listeners.add(event -> {
for (var filter : filters) {
final Object handler = filter.castHandler(event);
final EventNode<E> mappedNode = mappedNodeCache.get(handler);
if (mappedNode != null) mappedNode.call(event);
}
});
}
}
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-21 01:24:30 +02:00
// Normal listeners
2021-08-20 03:00:25 +02:00
for (var listener : entry.listeners) {
if (predicate != null) {
// Ensure that the event is valid before running
2021-08-21 02:08:25 +02:00
this.listeners.add(e -> {
2021-08-20 03:00:25 +02:00
final var value = filter.getHandler(e);
if (!predicate.test(e, value)) return;
callListener(targetNode, listener, e);
});
} else {
// No predicate, run directly
2021-08-21 02:08:25 +02:00
this.listeners.add(e -> callListener(targetNode, listener, e));
2021-08-20 03:00:25 +02:00
}
}
2021-08-21 01:24:30 +02:00
// Bindings
2021-08-21 02:00:30 +02:00
final var bindingConsumers = entry.bindingConsumers;
if (!bindingConsumers.isEmpty()) { // Ensure no array clone
2021-08-21 02:08:25 +02:00
this.listeners.addAll(bindingConsumers);
2021-08-21 02:00:30 +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);
} catch (Exception e) {
result = EventListener.Result.EXCEPTION;
MinecraftServer.getExceptionManager().handleException(e);
}
if (result == EventListener.Result.EXPIRED) {
targetNode.removeListener(listener);
}
}
2021-08-16 23:42:02 +02:00
}
}