mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-06 16:37:38 +01:00
Make EventNode a class, fast exist when no children can listen to an event
This commit is contained in:
parent
f8eb211d0d
commit
65c303766f
@ -1,55 +1,209 @@
|
||||
package net.minestom.server.event;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface EventNode<T extends Event> {
|
||||
public class EventNode<T extends Event> {
|
||||
|
||||
static <E extends Event> EventNode<E> type(@NotNull EventFilter<E, ?> filter) {
|
||||
return new EventNodeImpl<>(filter);
|
||||
public static <E extends Event> EventNode<E> type(@NotNull EventFilter<E, ?> filter) {
|
||||
return new EventNode<>(filter);
|
||||
}
|
||||
|
||||
static EventNode<Event> all() {
|
||||
public static EventNode<Event> all() {
|
||||
return type(EventFilter.ALL);
|
||||
}
|
||||
|
||||
static <E extends Event, H> EventNodeConditional<E, H> conditional(@NotNull EventFilter<E, H> filter,
|
||||
@NotNull BiPredicate<E, H> predicate) {
|
||||
public static <E extends Event, H> EventNodeConditional<E, H> conditional(@NotNull EventFilter<E, H> filter,
|
||||
@NotNull BiPredicate<E, H> predicate) {
|
||||
return new EventNodeConditional<>(filter, predicate);
|
||||
}
|
||||
|
||||
static <E extends Event, H> EventNodeConditional<E, H> conditionalEvent(@NotNull EventFilter<E, H> filter,
|
||||
@NotNull Predicate<E> predicate) {
|
||||
public static <E extends Event, H> EventNodeConditional<E, H> conditionalEvent(@NotNull EventFilter<E, H> filter,
|
||||
@NotNull Predicate<E> predicate) {
|
||||
return conditional(filter, (e, h) -> predicate.test(e));
|
||||
}
|
||||
|
||||
static <E extends Event, H> EventNodeConditional<E, H> conditionalHandler(@NotNull EventFilter<E, H> filter,
|
||||
@NotNull Predicate<H> predicate) {
|
||||
public static <E extends Event, H> EventNodeConditional<E, H> conditionalHandler(@NotNull EventFilter<E, H> filter,
|
||||
@NotNull Predicate<H> predicate) {
|
||||
return conditional(filter, (e, h) -> predicate.test(h));
|
||||
}
|
||||
|
||||
static <E extends Event, H> EventNodeList<E, H> list(@NotNull EventFilter<E, H> filter) {
|
||||
public static <E extends Event, H> EventNodeList<E, H> list(@NotNull EventFilter<E, H> filter) {
|
||||
return new EventNodeList<>(filter);
|
||||
}
|
||||
|
||||
void call(@NotNull T event);
|
||||
private volatile String name = "unknown";
|
||||
|
||||
void addChild(@NotNull EventNode<? extends T> child);
|
||||
private final Map<Class<? extends T>, List<EventListener<T>>> listenerMap = new ConcurrentHashMap<>();
|
||||
private final Map<Object, RedirectionEntry<T>> redirectionMap = new ConcurrentHashMap<>();
|
||||
private final Set<EventNode<T>> children = new CopyOnWriteArraySet<>();
|
||||
|
||||
void removeChild(@NotNull EventNode<? extends T> child);
|
||||
protected final EventFilter<T, ?> filter;
|
||||
|
||||
void addListener(@NotNull EventListener<? extends T> listener);
|
||||
// Tree data
|
||||
private volatile EventNode<? super T> parent;
|
||||
private final Object lock = new Object();
|
||||
private final Object2IntMap<Class<? extends T>> childEventMap = new Object2IntOpenHashMap<>();
|
||||
|
||||
void removeListener(@NotNull EventListener<? extends T> listener);
|
||||
protected EventNode(EventFilter<T, ?> filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
<E extends T, V> void map(@NotNull EventFilter<E, V> filter, @NotNull V value, @NotNull EventNode<E> node);
|
||||
/**
|
||||
* Condition to enter the node.
|
||||
*
|
||||
* @param event the called event
|
||||
* @return true to enter the node, false otherwise
|
||||
*/
|
||||
protected boolean condition(@NotNull T event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void removeMap(@NotNull Object value);
|
||||
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) {
|
||||
final var entry = redirectionMap.get(handler);
|
||||
if (entry != null) {
|
||||
entry.node.call(event);
|
||||
}
|
||||
}
|
||||
// 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));
|
||||
}
|
||||
|
||||
@NotNull String getName();
|
||||
public void addChild(@NotNull EventNode<? extends T> child) {
|
||||
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 int childCount = eventListeners.size();
|
||||
increaseListenerCount(eventClass, childCount);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull List<@NotNull EventNode<T>> getChildren();
|
||||
public void removeChild(@NotNull EventNode<? extends T> child) {
|
||||
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();
|
||||
decreaseListenerCount(eventClass, childCount);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(@NotNull EventListener<? extends T> listener) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeListener(@NotNull EventListener<? extends T> listener) {
|
||||
final var eventType = listener.getEventType();
|
||||
var listeners = listenerMap.get(eventType);
|
||||
if (listeners == null || listeners.isEmpty())
|
||||
return;
|
||||
final boolean removed = listeners.remove(listener);
|
||||
if (removed && parent != null) {
|
||||
synchronized (parent.lock) {
|
||||
parent.decreaseListenerCount(eventType, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public <E extends T, V> void map(@NotNull EventFilter<E, V> filter, @NotNull V value, @NotNull EventNode<E> node) {
|
||||
RedirectionEntry<E> entry = new RedirectionEntry<>();
|
||||
entry.filter = filter;
|
||||
entry.node = node;
|
||||
this.redirectionMap.put(value, (RedirectionEntry<T>) entry);
|
||||
}
|
||||
|
||||
public void removeMap(@NotNull Object value) {
|
||||
this.redirectionMap.remove(value);
|
||||
}
|
||||
|
||||
public @NotNull String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(@NotNull String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public @NotNull Set<@NotNull EventNode<T>> getChildren() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RedirectionEntry<E extends Event> {
|
||||
EventFilter<E, ?> filter;
|
||||
EventNode<E> node;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
public class EventNodeConditional<T extends Event, H> extends EventNodeImpl<T, H> {
|
||||
public class EventNodeConditional<T extends Event, H> extends EventNode<T> {
|
||||
|
||||
private volatile BiPredicate<T, H> predicate;
|
||||
|
||||
@ -15,7 +15,7 @@ public class EventNodeConditional<T extends Event, H> extends EventNodeImpl<T, H
|
||||
|
||||
@Override
|
||||
protected boolean condition(@NotNull T event) {
|
||||
return predicate.test(event, filter.getHandler(event));
|
||||
return predicate.test(event, (H) filter.getHandler(event));
|
||||
}
|
||||
|
||||
public @NotNull BiPredicate<T, H> getPredicate() {
|
||||
|
@ -1,116 +0,0 @@
|
||||
package net.minestom.server.event;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
class EventNodeImpl<T extends Event, H> implements EventNode<T> {
|
||||
|
||||
private final String name = "debug";
|
||||
|
||||
private final Map<Class<? extends T>, List<EventListener<T>>> listenerMap = new ConcurrentHashMap<>();
|
||||
private final Map<Object, RedirectionEntry<T>> redirectionMap = new ConcurrentHashMap<>();
|
||||
private final List<EventNode<T>> children = new CopyOnWriteArrayList<>();
|
||||
|
||||
protected final EventFilter<T, H> filter;
|
||||
|
||||
protected EventNodeImpl(EventFilter<T, H> filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition to enter the node.
|
||||
*
|
||||
* @param event the called event
|
||||
* @return true to enter the node, false otherwise
|
||||
*/
|
||||
protected boolean condition(@NotNull T event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void call(@NotNull T event) {
|
||||
if (!filter.getEventType().isAssignableFrom(event.getClass())) {
|
||||
// Invalid event type
|
||||
return;
|
||||
}
|
||||
if (!condition(event)) {
|
||||
// Cancelled by superclass
|
||||
return;
|
||||
}
|
||||
// Process redirection
|
||||
final H handler = filter.getHandler(event);
|
||||
final var entry = redirectionMap.get(handler);
|
||||
if (entry != null) {
|
||||
entry.node.call(event);
|
||||
}
|
||||
// Process listener list
|
||||
final var listeners = listenerMap.get(event.getClass());
|
||||
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
|
||||
this.children.forEach(eventNode -> eventNode.call(event));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChild(@NotNull EventNode<? extends T> child) {
|
||||
this.children.add((EventNode<T>) child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeChild(@NotNull EventNode<? extends T> child) {
|
||||
this.children.remove(child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(@NotNull EventListener<? extends T> listener) {
|
||||
this.listenerMap.computeIfAbsent(listener.getEventType(), aClass -> new CopyOnWriteArrayList<>())
|
||||
.add((EventListener<T>) listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(@NotNull EventListener<? extends T> listener) {
|
||||
var listeners = listenerMap.get(listener.getEventType());
|
||||
if (listeners == null || listeners.isEmpty())
|
||||
return;
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends T, V> void map(@NotNull EventFilter<E, V> filter, @NotNull V value, @NotNull EventNode<E> node) {
|
||||
RedirectionEntry<E> entry = new RedirectionEntry<>();
|
||||
entry.filter = filter;
|
||||
entry.node = node;
|
||||
this.redirectionMap.put(value, (RedirectionEntry<T>) entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMap(@NotNull Object value) {
|
||||
this.redirectionMap.remove(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<@NotNull EventNode<T>> getChildren() {
|
||||
return Collections.unmodifiableList(children);
|
||||
}
|
||||
|
||||
private static class RedirectionEntry<E extends Event> {
|
||||
EventFilter<E, ?> filter;
|
||||
EventNode<E> node;
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class EventNodeList<T extends Event, H> extends EventNodeImpl<T, H> {
|
||||
public class EventNodeList<T extends Event, H> extends EventNode<T> {
|
||||
|
||||
private final List<H> entries = new CopyOnWriteArrayList<>();
|
||||
|
||||
|
@ -137,16 +137,18 @@ public class PlayerInit {
|
||||
|
||||
// EVENT REGISTERING
|
||||
|
||||
var empty = EventNode.all();
|
||||
empty.setName("empty");
|
||||
empty.addListener(EventListener.of(PlayerMoveEvent.class, (event) -> {
|
||||
}));
|
||||
|
||||
var node = EventNode.type(EventFilter.PLAYER);
|
||||
node.setName("node");
|
||||
node.addListener(EventListener.builder(PlayerTickEvent.class)
|
||||
.handler(playerTickEvent -> System.out.println("Player tick!"))
|
||||
.expirationCount(2)
|
||||
.build());
|
||||
|
||||
var empty = EventNode.all();
|
||||
empty.addListener(EventListener.of(PlayerMoveEvent.class, (event) -> {
|
||||
}));
|
||||
|
||||
/*
|
||||
* Map a node to a single element
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user