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

318 lines
13 KiB
Java
Raw Normal View History

2021-06-02 08:17:21 +02:00
package net.minestom.server.event;
2021-06-03 21:39:38 +02:00
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagReadable;
import net.minestom.server.utils.validate.Check;
2021-06-08 14:15:30 +02:00
import org.jetbrains.annotations.Contract;
2021-06-02 10:19:23 +02:00
import org.jetbrains.annotations.NotNull;
2021-06-03 21:39:38 +02:00
import org.jetbrains.annotations.Nullable;
2021-06-02 08:17:21 +02:00
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
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-08 14:15:30 +02:00
@Contract(value = "_ -> new", pure = true)
public static @NotNull EventNode<Event> all(@NotNull String name) {
2021-06-04 04:10:13 +02:00
return type(name, EventFilter.ALL);
2021-06-02 11:29:16 +02:00
}
2021-06-08 14:15:30 +02:00
@Contract(value = "_, _, _ -> new", pure = true)
public static <E extends Event, V> @NotNull EventNode<E> type(@NotNull String name,
@NotNull EventFilter<E, V> filter,
@NotNull BiPredicate<E, V> predicate) {
2021-06-04 04:39:45 +02:00
return create(name, filter, predicate);
2021-06-02 08:17:21 +02:00
}
2021-06-08 14:15:30 +02:00
@Contract(value = "_, _ -> new", pure = true)
public static <E extends Event, V> @NotNull EventNode<E> type(@NotNull String name,
@NotNull EventFilter<E, V> filter) {
2021-06-04 04:39:45 +02:00
return create(name, filter, null);
2021-06-03 21:59:27 +02:00
}
2021-06-08 14:15:30 +02:00
@Contract(value = "_, _, _ -> new", pure = true)
public static <E extends Event, V> @NotNull EventNode<E> event(@NotNull String name,
@NotNull EventFilter<E, V> filter,
@NotNull Predicate<E> predicate) {
2021-06-04 04:39:45 +02:00
return create(name, filter, (e, h) -> predicate.test(e));
}
2021-06-08 14:15:30 +02:00
@Contract(value = "_, _, _ -> new", pure = true)
public static <E extends Event, V> @NotNull EventNode<E> value(@NotNull String name,
@NotNull EventFilter<E, V> filter,
@NotNull Predicate<V> predicate) {
2021-06-04 04:39:45 +02:00
return create(name, filter, (e, h) -> predicate.test(h));
}
2021-06-08 14:15:30 +02:00
@Contract(value = "_, _, _ -> new", pure = true)
public static <E extends Event> @NotNull EventNode<E> tag(@NotNull String name,
@NotNull EventFilter<E, ? extends TagReadable> filter,
@NotNull Tag<?> tag) {
2021-06-04 04:39:45 +02:00
return create(name, filter, (e, h) -> h.hasTag(tag));
2021-06-03 21:39:38 +02:00
}
2021-06-08 14:15:30 +02:00
@Contract(value = "_, _, _, _ -> new", pure = true)
public static <E extends Event, V> @NotNull EventNode<E> tag(@NotNull String name,
@NotNull EventFilter<E, ? extends TagReadable> filter,
@NotNull Tag<V> tag,
@NotNull Predicate<@Nullable V> consumer) {
2021-06-04 04:39:45 +02:00
return create(name, filter, (e, h) -> consumer.test(h.getTag(tag)));
}
private static <E extends Event, V> EventNode<E> create(@NotNull String name,
@NotNull EventFilter<E, V> filter,
@Nullable BiPredicate<E, V> predicate) {
return new EventNode<>(name, filter, predicate != null ? (e, o) -> predicate.test(e, (V) o) : null);
2021-06-03 21:39:38 +02:00
}
private static final Object GLOBAL_CHILD_LOCK = new Object();
private final Object lock = new Object();
private final Map<Class<? extends T>, ListenerEntry<T>> listenerMap = 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-04 04:10:13 +02:00
protected final String name;
2021-06-03 04:26:47 +02:00
protected final EventFilter<T, ?> filter;
protected final BiPredicate<T, Object> predicate;
2021-06-04 03:48:51 +02:00
protected final Class<T> eventType;
2021-06-08 13:47:10 +02:00
private volatile int priority;
2021-06-03 04:26:47 +02:00
private volatile EventNode<? super T> parent;
2021-06-02 10:19:23 +02:00
2021-06-04 04:39:45 +02:00
protected EventNode(@NotNull String name,
@NotNull EventFilter<T, ?> filter,
@Nullable BiPredicate<T, Object> predicate) {
2021-06-04 04:10:13 +02:00
this.name = name;
this.filter = filter;
2021-06-03 03:56:05 +02:00
this.predicate = predicate;
2021-06-04 03:48:51 +02:00
this.eventType = filter.getEventType();
}
2021-06-02 19:50:23 +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-04 04:39:45 +02:00
if (predicate == null)
return true;
2021-06-03 04:26:47 +02:00
final var value = filter.getHandler(event);
return predicate.test(event, value);
}
public void call(@NotNull T event) {
if (!eventType.isInstance(event)) {
// Invalid event type
return;
}
if (!condition(event)) {
// Cancelled by superclass
return;
}
// Process listener list
final var entry = listenerMap.get(event.getClass());
if (entry == null) {
// No listener nor children
return;
}
final var listeners = entry.listeners;
2021-06-06 07:55:30 +02:00
if (!listeners.isEmpty()) {
for (EventListener<T> listener : listeners) {
final EventListener.Result result = listener.run(event);
if (result == EventListener.Result.EXPIRED) {
listeners.remove(listener);
}
}
}
// Process children
if (entry.childCount > 0) {
2021-06-08 13:47:10 +02:00
this.children.stream()
.sorted(Comparator.comparing(EventNode::getPriority))
.forEach(child -> child.call(event));
}
}
2021-06-04 03:48:51 +02:00
public void callCancellable(@NotNull T event, @NotNull Runnable successCallback) {
call(event);
if (!(event instanceof CancellableEvent) || !((CancellableEvent) event).isCancelled()) {
successCallback.run();
}
}
2021-06-08 14:15:30 +02:00
@Contract(pure = true)
public @NotNull String getName() {
return name;
}
2021-06-08 14:15:30 +02:00
@Contract(pure = true)
2021-06-08 13:47:10 +02:00
public int getPriority() {
return priority;
}
2021-06-08 14:15:30 +02:00
@Contract(value = "_ -> this")
public @NotNull EventNode<T> setPriority(int priority) {
2021-06-08 13:47:10 +02:00
this.priority = priority;
return this;
}
2021-06-08 14:15:30 +02:00
@Contract(pure = true)
2021-06-04 03:48:51 +02:00
public @Nullable EventNode<? super T> getParent() {
return parent;
}
2021-06-08 14:15:30 +02:00
@Contract(pure = true)
2021-06-04 03:48:51 +02:00
public @NotNull Set<@NotNull EventNode<T>> getChildren() {
return Collections.unmodifiableSet(children);
}
2021-06-08 16:55:21 +02:00
@Contract(pure = true)
public <E extends T> @NotNull List<EventNode<E>> findChildren(@NotNull String name, Class<E> eventType) {
synchronized (GLOBAL_CHILD_LOCK) {
if (children.isEmpty()) {
return Collections.emptyList();
}
List<EventNode<E>> result = new ArrayList<>();
this.children.forEach(child -> {
final boolean nameCheck = child.getName().equals(name);
final boolean typeCheck = child.eventType.isAssignableFrom(eventType);
if (nameCheck && typeCheck) {
result.add((EventNode<E>) child);
}
result.addAll(child.findChildren(name, eventType));
});
return result;
}
2021-06-04 03:48:51 +02:00
}
2021-06-08 16:55:21 +02:00
@Contract(pure = true)
public @NotNull List<EventNode<T>> findChildren(@NotNull String name) {
return findChildren(name, eventType);
2021-06-04 03:48:51 +02:00
}
2021-06-08 14:15:30 +02:00
@Contract(value = "_ -> this")
public @NotNull EventNode<T> addChild(@NotNull EventNode<? extends T> child) {
synchronized (GLOBAL_CHILD_LOCK) {
2021-06-04 00:30:48 +02:00
Check.stateCondition(child.parent != null, "Node already has a parent");
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);
if (result) {
child.parent = this;
// Increase listener count
synchronized (lock) {
child.listenerMap.forEach((eventClass, eventListeners) -> {
final var entry = child.listenerMap.get(eventClass);
if (entry == null)
return;
final int childCount = entry.listeners.size() + entry.childCount;
increaseChildListenerCount(eventClass, childCount);
});
}
}
}
2021-06-03 04:41:08 +02:00
return this;
}
2021-06-02 09:50:18 +02:00
2021-06-08 14:15:30 +02:00
@Contract(value = "_ -> this")
public @NotNull EventNode<T> removeChild(@NotNull EventNode<? extends T> child) {
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 var entry = child.listenerMap.get(eventClass);
if (entry == null)
return;
final int childCount = entry.listeners.size() + entry.childCount;
decreaseChildListenerCount(eventClass, childCount);
});
}
}
}
2021-06-03 04:41:08 +02:00
return this;
}
2021-06-08 14:15:30 +02:00
@Contract(value = "_ -> this")
public @NotNull EventNode<T> addListener(@NotNull EventListener<? extends T> listener) {
return addListener0(listener);
}
2021-06-08 14:15:30 +02:00
@Contract(value = "_, _ -> this")
public <E extends T> @NotNull EventNode<T> addListener(@NotNull Class<E> eventType, @NotNull Consumer<@NotNull E> listener) {
return addListener0(EventListener.of(eventType, listener));
}
2021-06-08 14:15:30 +02:00
@Contract(value = "_ -> this")
public @NotNull EventNode<T> removeListener(@NotNull EventListener<? extends T> listener) {
synchronized (GLOBAL_CHILD_LOCK) {
final var eventType = listener.getEventType();
var entry = listenerMap.get(eventType);
if (entry == null)
2021-06-03 04:41:08 +02:00
return this;
var listeners = entry.listeners;
final boolean removed = listeners.remove(listener);
if (removed && parent != null) {
synchronized (parent.lock) {
parent.decreaseChildListenerCount(eventType, 1);
}
}
}
2021-06-03 04:41:08 +02:00
return this;
}
private EventNode<T> addListener0(@NotNull EventListener<? extends T> listener) {
synchronized (GLOBAL_CHILD_LOCK) {
final var eventType = listener.getEventType();
var entry = listenerMap.computeIfAbsent(eventType, aClass -> new ListenerEntry<>());
entry.listeners.add((EventListener<T>) listener);
if (parent != null) {
synchronized (parent.lock) {
parent.increaseChildListenerCount(eventType, 1);
}
}
}
return this;
}
private void increaseChildListenerCount(Class<? extends T> eventClass, int count) {
var entry = listenerMap.computeIfAbsent(eventClass, aClass -> new ListenerEntry<>());
ListenerEntry.addAndGet(entry, count);
2021-06-06 07:55:30 +02:00
if (parent != null) {
parent.increaseChildListenerCount(eventClass, count);
}
}
private void decreaseChildListenerCount(Class<? extends T> eventClass, int count) {
var entry = listenerMap.computeIfAbsent(eventClass, aClass -> new ListenerEntry<>());
final int result = ListenerEntry.addAndGet(entry, -count);
if (result == 0 && entry.listeners.isEmpty()) {
this.listenerMap.remove(eventClass);
} else if (result < 0) {
throw new IllegalStateException("Something wrong happened, listener count: " + result);
}
2021-06-06 07:55:30 +02:00
if (parent != null) {
parent.decreaseChildListenerCount(eventClass, count);
}
}
private static class ListenerEntry<T extends Event> {
private static final AtomicIntegerFieldUpdater<ListenerEntry> CHILD_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(ListenerEntry.class, "childCount");
List<EventListener<T>> listeners = new CopyOnWriteArrayList<>();
volatile int childCount;
private static int addAndGet(ListenerEntry<?> entry, int add) {
return CHILD_UPDATER.addAndGet(entry, add);
}
}
2021-06-02 08:17:21 +02:00
}