2021-06-02 08:17:21 +02:00
|
|
|
package net.minestom.server.event;
|
|
|
|
|
2021-06-03 03:27:30 +02:00
|
|
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|
|
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
2021-06-03 09:47:48 +02:00
|
|
|
import net.minestom.server.utils.validate.Check;
|
2021-06-02 10:19:23 +02:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
2021-06-02 08:17:21 +02:00
|
|
|
|
2021-06-03 09:47:48 +02:00
|
|
|
import java.util.*;
|
2021-06-03 03:27:30 +02:00
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
|
|
|
import java.util.concurrent.CopyOnWriteArraySet;
|
2021-06-02 21:08:10 +02:00
|
|
|
import java.util.function.BiPredicate;
|
2021-06-03 20:46:11 +02:00
|
|
|
import java.util.function.Consumer;
|
2021-06-02 08:17:21 +02:00
|
|
|
import java.util.function.Predicate;
|
|
|
|
|
2021-06-03 04:26:47 +02:00
|
|
|
public class EventNode<T extends Event> {
|
2021-06-02 08:17:21 +02:00
|
|
|
|
2021-06-03 04:26:47 +02:00
|
|
|
public static <E extends Event, V> EventNode<E> type(@NotNull EventFilter<E, V> filter) {
|
2021-06-03 03:56:05 +02:00
|
|
|
return new EventNode<>(filter, (e, v) -> true);
|
2021-06-02 11:18:26 +02:00
|
|
|
}
|
|
|
|
|
2021-06-03 04:26:47 +02:00
|
|
|
public static EventNode<Event> all() {
|
2021-06-02 20:23:50 +02:00
|
|
|
return type(EventFilter.ALL);
|
2021-06-02 11:29:16 +02:00
|
|
|
}
|
|
|
|
|
2021-06-03 04:26:47 +02:00
|
|
|
public static <E extends Event, V> EventNode<E> predicate(@NotNull EventFilter<E, V> filter,
|
|
|
|
@NotNull BiPredicate<E, V> predicate) {
|
|
|
|
return new EventNode<>(filter, (e, o) -> predicate.test(e, (V) o));
|
2021-06-02 08:17:21 +02:00
|
|
|
}
|
|
|
|
|
2021-06-03 04:26:47 +02:00
|
|
|
public static <E extends Event, V> EventNode<E> predicateEvent(@NotNull EventFilter<E, V> filter,
|
|
|
|
@NotNull Predicate<E> predicate) {
|
2021-06-03 03:56:05 +02:00
|
|
|
return predicate(filter, (e, h) -> predicate.test(e));
|
2021-06-02 21:08:10 +02:00
|
|
|
}
|
|
|
|
|
2021-06-03 04:26:47 +02:00
|
|
|
public static <E extends Event, V> EventNode<E> predicateValue(@NotNull EventFilter<E, V> filter,
|
|
|
|
@NotNull Predicate<V> predicate) {
|
2021-06-03 03:56:05 +02:00
|
|
|
return predicate(filter, (e, h) -> predicate.test(h));
|
2021-06-02 21:08:10 +02:00
|
|
|
}
|
|
|
|
|
2021-06-03 03:27:30 +02:00
|
|
|
private final Map<Class<? extends T>, List<EventListener<T>>> listenerMap = new ConcurrentHashMap<>();
|
2021-06-03 05:59:13 +02:00
|
|
|
private final Map<Object, EventNode<T>> redirectionMap = new ConcurrentHashMap<>();
|
2021-06-03 04:26:47 +02:00
|
|
|
private final Set<EventNode<T>> children = new CopyOnWriteArraySet<>();
|
2021-06-02 10:19:23 +02:00
|
|
|
|
2021-06-03 04:26:47 +02:00
|
|
|
protected final EventFilter<T, ?> filter;
|
|
|
|
protected final BiPredicate<T, Object> predicate;
|
2021-06-03 03:56:05 +02:00
|
|
|
private volatile String name = "unknown";
|
2021-06-02 19:52:05 +02:00
|
|
|
|
2021-06-03 03:27:30 +02:00
|
|
|
// Tree data
|
2021-06-03 03:37:41 +02:00
|
|
|
private static final Object GLOBAL_CHILD_LOCK = new Object();
|
2021-06-03 04:26:47 +02:00
|
|
|
private volatile EventNode<? super T> parent;
|
2021-06-03 03:27:30 +02:00
|
|
|
private final Object lock = new Object();
|
|
|
|
private final Object2IntMap<Class<? extends T>> childEventMap = new Object2IntOpenHashMap<>();
|
2021-06-02 10:19:23 +02:00
|
|
|
|
2021-06-03 04:26:47 +02:00
|
|
|
protected EventNode(EventFilter<T, ?> filter, BiPredicate<T, Object> predicate) {
|
2021-06-03 03:27:30 +02:00
|
|
|
this.filter = filter;
|
2021-06-03 03:56:05 +02:00
|
|
|
this.predicate = predicate;
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
2021-06-02 19:50:23 +02:00
|
|
|
|
2021-06-03 03:27:30 +02:00
|
|
|
/**
|
|
|
|
* Condition to enter the node.
|
|
|
|
*
|
|
|
|
* @param event the called event
|
|
|
|
* @return true to enter the node, false otherwise
|
|
|
|
*/
|
|
|
|
protected boolean condition(@NotNull T event) {
|
2021-06-03 04:26:47 +02:00
|
|
|
final var value = filter.getHandler(event);
|
|
|
|
return predicate.test(event, value);
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
2021-06-03 00:48:37 +02:00
|
|
|
|
2021-06-03 03:27:30 +02:00
|
|
|
public void call(@NotNull T event) {
|
|
|
|
final var eventClass = event.getClass();
|
|
|
|
if (!filter.getEventType().isAssignableFrom(eventClass)) {
|
|
|
|
// Invalid event type
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!condition(event)) {
|
|
|
|
// Cancelled by superclass
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Process redirection
|
|
|
|
final Object handler = filter.getHandler(event);
|
|
|
|
if (handler != null) {
|
2021-06-03 05:59:13 +02:00
|
|
|
final var node = redirectionMap.get(handler);
|
|
|
|
if (node != null) {
|
|
|
|
node.call(event);
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Process listener list
|
|
|
|
final var listeners = listenerMap.get(eventClass);
|
|
|
|
if (listeners != null && !listeners.isEmpty()) {
|
|
|
|
listeners.forEach(listener -> {
|
|
|
|
final EventListener.Result result = listener.run(event);
|
|
|
|
if (result == EventListener.Result.EXPIRED) {
|
|
|
|
listeners.remove(listener);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
// Process children
|
|
|
|
synchronized (lock) {
|
|
|
|
final int childCount = childEventMap.getInt(eventClass);
|
|
|
|
if (childCount < 1) {
|
|
|
|
// No listener in children
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.children.forEach(eventNode -> eventNode.call(event));
|
|
|
|
}
|
2021-06-03 00:48:37 +02:00
|
|
|
|
2021-06-03 04:41:08 +02:00
|
|
|
public EventNode<T> addChild(@NotNull EventNode<? extends T> child) {
|
2021-06-03 03:37:41 +02:00
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
2021-06-03 09:47:48 +02:00
|
|
|
Check.stateCondition(Objects.equals(parent, child), "Cannot have a child as parent");
|
2021-06-03 04:26:47 +02:00
|
|
|
final boolean result = this.children.add((EventNode<T>) child);
|
2021-06-03 03:37:41 +02:00
|
|
|
if (result) {
|
|
|
|
child.parent = this;
|
|
|
|
// Increase listener count
|
|
|
|
synchronized (lock) {
|
|
|
|
child.listenerMap.forEach((eventClass, eventListeners) -> {
|
|
|
|
final int childCount = eventListeners.size() + child.childEventMap.getInt(eventClass);
|
|
|
|
increaseListenerCount(eventClass, childCount);
|
|
|
|
});
|
|
|
|
}
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
|
|
|
}
|
2021-06-03 04:41:08 +02:00
|
|
|
return this;
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
2021-06-02 09:50:18 +02:00
|
|
|
|
2021-06-03 04:41:08 +02:00
|
|
|
public EventNode<T> removeChild(@NotNull EventNode<? extends T> child) {
|
2021-06-03 03:37:41 +02:00
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
|
|
|
final boolean result = this.children.remove(child);
|
|
|
|
if (result) {
|
|
|
|
child.parent = null;
|
|
|
|
// Decrease listener count
|
|
|
|
synchronized (lock) {
|
|
|
|
child.listenerMap.forEach((eventClass, eventListeners) -> {
|
|
|
|
final int childCount = eventListeners.size() + child.childEventMap.getInt(eventClass);
|
|
|
|
decreaseListenerCount(eventClass, childCount);
|
|
|
|
});
|
|
|
|
}
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
|
|
|
}
|
2021-06-03 04:41:08 +02:00
|
|
|
return this;
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-03 04:41:08 +02:00
|
|
|
public EventNode<T> addListener(@NotNull EventListener<? extends T> listener) {
|
2021-06-03 03:37:41 +02:00
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
|
|
|
final var eventType = listener.getEventType();
|
|
|
|
this.listenerMap.computeIfAbsent(eventType, aClass -> new CopyOnWriteArrayList<>())
|
|
|
|
.add((EventListener<T>) listener);
|
|
|
|
if (parent != null) {
|
|
|
|
synchronized (parent.lock) {
|
|
|
|
parent.increaseListenerCount(eventType, 1);
|
|
|
|
}
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
|
|
|
}
|
2021-06-03 04:41:08 +02:00
|
|
|
return this;
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-03 04:41:08 +02:00
|
|
|
public EventNode<T> removeListener(@NotNull EventListener<? extends T> listener) {
|
2021-06-03 03:37:41 +02:00
|
|
|
synchronized (GLOBAL_CHILD_LOCK) {
|
|
|
|
final var eventType = listener.getEventType();
|
|
|
|
var listeners = listenerMap.get(eventType);
|
|
|
|
if (listeners == null || listeners.isEmpty())
|
2021-06-03 04:41:08 +02:00
|
|
|
return this;
|
2021-06-03 03:37:41 +02:00
|
|
|
final boolean removed = listeners.remove(listener);
|
|
|
|
if (removed && parent != null) {
|
|
|
|
synchronized (parent.lock) {
|
|
|
|
parent.decreaseListenerCount(eventType, 1);
|
|
|
|
}
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
|
|
|
}
|
2021-06-03 04:41:08 +02:00
|
|
|
return this;
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-03 20:46:11 +02:00
|
|
|
public <E extends T> EventNode<T> addListener(@NotNull Class<E> eventType, @NotNull Consumer<@NotNull E> listener) {
|
|
|
|
return addListener(EventListener.of(eventType, listener));
|
|
|
|
}
|
|
|
|
|
2021-06-03 05:59:13 +02:00
|
|
|
public <E extends T> EventNode<T> map(@NotNull Object value, @NotNull EventNode<E> node) {
|
|
|
|
this.redirectionMap.put(value, (EventNode<T>) node);
|
2021-06-03 04:41:08 +02:00
|
|
|
return this;
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-03 04:41:08 +02:00
|
|
|
public EventNode<T> removeMap(@NotNull Object value) {
|
2021-06-03 03:27:30 +02:00
|
|
|
this.redirectionMap.remove(value);
|
2021-06-03 04:41:08 +02:00
|
|
|
return this;
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public @NotNull String getName() {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2021-06-03 04:41:08 +02:00
|
|
|
public EventNode<T> setName(@NotNull String name) {
|
2021-06-03 03:27:30 +02:00
|
|
|
this.name = name;
|
2021-06-03 04:41:08 +02:00
|
|
|
return this;
|
2021-06-03 03:27:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-03 04:26:47 +02:00
|
|
|
public @NotNull Set<@NotNull EventNode<T>> getChildren() {
|
2021-06-03 03:27:30 +02:00
|
|
|
return Collections.unmodifiableSet(children);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void increaseListenerCount(Class<? extends T> eventClass, int count) {
|
|
|
|
final int current = childEventMap.getInt(eventClass);
|
|
|
|
final int result = current + count;
|
|
|
|
this.childEventMap.put(eventClass, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void decreaseListenerCount(Class<? extends T> eventClass, int count) {
|
|
|
|
final int current = childEventMap.getInt(eventClass);
|
|
|
|
final int result = current - count;
|
|
|
|
if (result == 0) {
|
|
|
|
this.childEventMap.removeInt(eventClass);
|
|
|
|
} else if (result > 0) {
|
|
|
|
this.childEventMap.put(eventClass, result);
|
|
|
|
} else {
|
|
|
|
throw new IllegalStateException("Something wrong happened, listener count: " + result);
|
|
|
|
}
|
|
|
|
}
|
2021-06-02 08:17:21 +02:00
|
|
|
}
|