mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-21 15:41:38 +01:00
Merge pull request #412 from Minestom/event-node-map
Mapped Event nodes
This commit is contained in:
commit
6c8cc9f669
67
src/main/java/net/minestom/server/event/EventBinding.java
Normal file
67
src/main/java/net/minestom/server/event/EventBinding.java
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package net.minestom.server.event;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@ApiStatus.Experimental
|
||||||
|
public interface EventBinding<E extends Event> {
|
||||||
|
|
||||||
|
static <E extends Event, T> @NotNull FilteredBuilder<E, T> filtered(@NotNull EventFilter<E, T> filter, @NotNull Predicate<T> predicate) {
|
||||||
|
return new FilteredBuilder<>(filter, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull Collection<Class<? extends Event>> eventTypes();
|
||||||
|
|
||||||
|
@NotNull Consumer<@NotNull E> consumer(@NotNull Class<? extends Event> eventType);
|
||||||
|
|
||||||
|
class FilteredBuilder<E extends Event, T> {
|
||||||
|
private final EventFilter<E, T> filter;
|
||||||
|
private final Predicate<T> predicate;
|
||||||
|
private final Map<Class<? extends Event>, BiConsumer<Object, E>> mapped = new HashMap<>();
|
||||||
|
|
||||||
|
FilteredBuilder(EventFilter<E, T> filter, Predicate<T> predicate) {
|
||||||
|
this.filter = filter;
|
||||||
|
this.predicate = predicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <M extends E> FilteredBuilder<E, T> map(@NotNull Class<M> eventType,
|
||||||
|
@NotNull BiConsumer<@NotNull T, @NotNull M> consumer) {
|
||||||
|
//noinspection unchecked
|
||||||
|
this.mapped.put(eventType, (BiConsumer<Object, E>) consumer);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull EventBinding<E> build() {
|
||||||
|
final var copy = Map.copyOf(mapped);
|
||||||
|
final var eventTypes = copy.keySet();
|
||||||
|
|
||||||
|
Map<Class<? extends Event>, Consumer<E>> consumers = new HashMap<>(eventTypes.size());
|
||||||
|
for (var eventType : eventTypes) {
|
||||||
|
final var consumer = copy.get(eventType);
|
||||||
|
consumers.put(eventType, event -> {
|
||||||
|
final T handler = filter.getHandler(event);
|
||||||
|
if (!predicate.test(handler)) return;
|
||||||
|
consumer.accept(handler, event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return new EventBinding<>() {
|
||||||
|
@Override
|
||||||
|
public @NotNull Collection<Class<? extends Event>> eventTypes() {
|
||||||
|
return eventTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Consumer<E> consumer(@NotNull Class<? extends Event> eventType) {
|
||||||
|
return consumers.get(eventType);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,8 +4,10 @@ import net.minestom.server.entity.Entity;
|
|||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.event.trait.*;
|
import net.minestom.server.event.trait.*;
|
||||||
import net.minestom.server.instance.Instance;
|
import net.minestom.server.instance.Instance;
|
||||||
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.inventory.Inventory;
|
import net.minestom.server.inventory.Inventory;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -27,14 +29,16 @@ import java.util.function.Function;
|
|||||||
*/
|
*/
|
||||||
public interface EventFilter<E extends Event, H> {
|
public interface EventFilter<E extends Event, H> {
|
||||||
|
|
||||||
EventFilter<Event, ?> ALL = from(Event.class, null);
|
EventFilter<Event, ?> ALL = from(Event.class, null, null);
|
||||||
EventFilter<EntityEvent, Entity> ENTITY = from(EntityEvent.class, EntityEvent::getEntity);
|
EventFilter<EntityEvent, Entity> ENTITY = from(EntityEvent.class, Entity.class, EntityEvent::getEntity);
|
||||||
EventFilter<PlayerEvent, Player> PLAYER = from(PlayerEvent.class, PlayerEvent::getPlayer);
|
EventFilter<PlayerEvent, Player> PLAYER = from(PlayerEvent.class, Player.class, PlayerEvent::getPlayer);
|
||||||
EventFilter<ItemEvent, ItemStack> ITEM = from(ItemEvent.class, ItemEvent::getItemStack);
|
EventFilter<ItemEvent, ItemStack> ITEM = from(ItemEvent.class, ItemStack.class, ItemEvent::getItemStack);
|
||||||
EventFilter<InstanceEvent, Instance> INSTANCE = from(InstanceEvent.class, InstanceEvent::getInstance);
|
EventFilter<InstanceEvent, Instance> INSTANCE = from(InstanceEvent.class, Instance.class, InstanceEvent::getInstance);
|
||||||
EventFilter<InventoryEvent, Inventory> INVENTORY = from(InventoryEvent.class, InventoryEvent::getInventory);
|
EventFilter<InventoryEvent, Inventory> INVENTORY = from(InventoryEvent.class, Inventory.class, InventoryEvent::getInventory);
|
||||||
|
EventFilter<BlockEvent, Block> BLOCK = from(BlockEvent.class, Block.class, BlockEvent::getBlock);
|
||||||
|
|
||||||
static <E extends Event, H> EventFilter<E, H> from(@NotNull Class<E> eventType,
|
static <E extends Event, H> EventFilter<E, H> from(@NotNull Class<E> eventType,
|
||||||
|
@Nullable Class<H> handlerType,
|
||||||
@Nullable Function<E, H> handlerGetter) {
|
@Nullable Function<E, H> handlerGetter) {
|
||||||
return new EventFilter<>() {
|
return new EventFilter<>() {
|
||||||
@Override
|
@Override
|
||||||
@ -43,9 +47,14 @@ public interface EventFilter<E extends Event, H> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Class<E> getEventType() {
|
public @NotNull Class<E> eventType() {
|
||||||
return eventType;
|
return eventType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Class<H> handlerType() {
|
||||||
|
return handlerType;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,10 +67,23 @@ public interface EventFilter<E extends Event, H> {
|
|||||||
*/
|
*/
|
||||||
@Nullable H getHandler(@NotNull E event);
|
@Nullable H getHandler(@NotNull E event);
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
default @Nullable H castHandler(@NotNull Object event) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return getHandler((E) event);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The event type to filter on.
|
* The event type to filter on.
|
||||||
*
|
*
|
||||||
* @return The event type.
|
* @return The event type.
|
||||||
*/
|
*/
|
||||||
@NotNull Class<E> getEventType();
|
@NotNull Class<E> eventType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type returned by {@link #getHandler(Event)}.
|
||||||
|
*
|
||||||
|
* @return the handler type, null if not any
|
||||||
|
*/
|
||||||
|
@Nullable Class<H> handlerType();
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,15 @@
|
|||||||
package net.minestom.server.event;
|
package net.minestom.server.event;
|
||||||
|
|
||||||
import net.minestom.server.MinecraftServer;
|
|
||||||
import net.minestom.server.event.trait.CancellableEvent;
|
import net.minestom.server.event.trait.CancellableEvent;
|
||||||
import net.minestom.server.tag.Tag;
|
import net.minestom.server.tag.Tag;
|
||||||
import net.minestom.server.tag.TagReadable;
|
import net.minestom.server.tag.TagReadable;
|
||||||
import net.minestom.server.utils.validate.Check;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
|
||||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
|
||||||
import java.util.function.BiPredicate;
|
import java.util.function.BiPredicate;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
@ -27,7 +23,8 @@ import java.util.function.Predicate;
|
|||||||
*
|
*
|
||||||
* @param <T> The event type accepted by this node
|
* @param <T> The event type accepted by this node
|
||||||
*/
|
*/
|
||||||
public class EventNode<T extends Event> {
|
@ApiStatus.NonExtendable
|
||||||
|
public interface EventNode<T extends Event> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event node which accepts any event type with no filtering.
|
* Creates an event node which accepts any event type with no filtering.
|
||||||
@ -36,7 +33,7 @@ public class EventNode<T extends Event> {
|
|||||||
* @return An event node with no filtering
|
* @return An event node with no filtering
|
||||||
*/
|
*/
|
||||||
@Contract(value = "_ -> new", pure = true)
|
@Contract(value = "_ -> new", pure = true)
|
||||||
public static @NotNull EventNode<Event> all(@NotNull String name) {
|
static @NotNull EventNode<Event> all(@NotNull String name) {
|
||||||
return type(name, EventFilter.ALL);
|
return type(name, EventFilter.ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +52,7 @@ public class EventNode<T extends Event> {
|
|||||||
* @return A node with just an event type filter
|
* @return A node with just an event type filter
|
||||||
*/
|
*/
|
||||||
@Contract(value = "_, _ -> new", pure = true)
|
@Contract(value = "_, _ -> new", pure = true)
|
||||||
public static <E extends Event, V> @NotNull EventNode<E> type(@NotNull String name,
|
static <E extends Event, V> @NotNull EventNode<E> type(@NotNull String name,
|
||||||
@NotNull EventFilter<E, V> filter) {
|
@NotNull EventFilter<E, V> filter) {
|
||||||
return create(name, filter, null);
|
return create(name, filter, null);
|
||||||
}
|
}
|
||||||
@ -80,7 +77,7 @@ public class EventNode<T extends Event> {
|
|||||||
* @return A node with an event type filter as well as a condition on the event.
|
* @return A node with an event type filter as well as a condition on the event.
|
||||||
*/
|
*/
|
||||||
@Contract(value = "_, _, _ -> new", pure = true)
|
@Contract(value = "_, _, _ -> new", pure = true)
|
||||||
public static <E extends Event, V> @NotNull EventNode<E> event(@NotNull String name,
|
static <E extends Event, V> @NotNull EventNode<E> event(@NotNull String name,
|
||||||
@NotNull EventFilter<E, V> filter,
|
@NotNull EventFilter<E, V> filter,
|
||||||
@NotNull Predicate<E> predicate) {
|
@NotNull Predicate<E> predicate) {
|
||||||
return create(name, filter, (e, h) -> predicate.test(e));
|
return create(name, filter, (e, h) -> predicate.test(e));
|
||||||
@ -108,7 +105,7 @@ public class EventNode<T extends Event> {
|
|||||||
* @return A node with an event type filter as well as a condition on the event.
|
* @return A node with an event type filter as well as a condition on the event.
|
||||||
*/
|
*/
|
||||||
@Contract(value = "_, _, _ -> new", pure = true)
|
@Contract(value = "_, _, _ -> new", pure = true)
|
||||||
public static <E extends Event, V> @NotNull EventNode<E> type(@NotNull String name,
|
static <E extends Event, V> @NotNull EventNode<E> type(@NotNull String name,
|
||||||
@NotNull EventFilter<E, V> filter,
|
@NotNull EventFilter<E, V> filter,
|
||||||
@NotNull BiPredicate<E, V> predicate) {
|
@NotNull BiPredicate<E, V> predicate) {
|
||||||
return create(name, filter, predicate);
|
return create(name, filter, predicate);
|
||||||
@ -133,7 +130,7 @@ public class EventNode<T extends Event> {
|
|||||||
* @return A node with an event type filter as well as a condition on the event.
|
* @return A node with an event type filter as well as a condition on the event.
|
||||||
*/
|
*/
|
||||||
@Contract(value = "_, _, _ -> new", pure = true)
|
@Contract(value = "_, _, _ -> new", pure = true)
|
||||||
public static <E extends Event, V> @NotNull EventNode<E> value(@NotNull String name,
|
static <E extends Event, V> @NotNull EventNode<E> value(@NotNull String name,
|
||||||
@NotNull EventFilter<E, V> filter,
|
@NotNull EventFilter<E, V> filter,
|
||||||
@NotNull Predicate<V> predicate) {
|
@NotNull Predicate<V> predicate) {
|
||||||
return create(name, filter, (e, h) -> predicate.test(h));
|
return create(name, filter, (e, h) -> predicate.test(h));
|
||||||
@ -152,7 +149,7 @@ public class EventNode<T extends Event> {
|
|||||||
* @return A node with an event type filter as well as a handler with the provided tag
|
* @return A node with an event type filter as well as a handler with the provided tag
|
||||||
*/
|
*/
|
||||||
@Contract(value = "_, _, _ -> new", pure = true)
|
@Contract(value = "_, _, _ -> new", pure = true)
|
||||||
public static <E extends Event> @NotNull EventNode<E> tag(@NotNull String name,
|
static <E extends Event> @NotNull EventNode<E> tag(@NotNull String name,
|
||||||
@NotNull EventFilter<E, ? extends TagReadable> filter,
|
@NotNull EventFilter<E, ? extends TagReadable> filter,
|
||||||
@NotNull Tag<?> tag) {
|
@NotNull Tag<?> tag) {
|
||||||
return create(name, filter, (e, h) -> h.hasTag(tag));
|
return create(name, filter, (e, h) -> h.hasTag(tag));
|
||||||
@ -170,7 +167,7 @@ public class EventNode<T extends Event> {
|
|||||||
* @return A node with an event type filter as well as a handler with the provided tag
|
* @return A node with an event type filter as well as a handler with the provided tag
|
||||||
*/
|
*/
|
||||||
@Contract(value = "_, _, _, _ -> new", pure = true)
|
@Contract(value = "_, _, _, _ -> new", pure = true)
|
||||||
public static <E extends Event, V> @NotNull EventNode<E> tag(@NotNull String name,
|
static <E extends Event, V> @NotNull EventNode<E> tag(@NotNull String name,
|
||||||
@NotNull EventFilter<E, ? extends TagReadable> filter,
|
@NotNull EventFilter<E, ? extends TagReadable> filter,
|
||||||
@NotNull Tag<V> tag,
|
@NotNull Tag<V> tag,
|
||||||
@NotNull Predicate<@Nullable V> consumer) {
|
@NotNull Predicate<@Nullable V> consumer) {
|
||||||
@ -180,47 +177,8 @@ public class EventNode<T extends Event> {
|
|||||||
private static <E extends Event, V> EventNode<E> create(@NotNull String name,
|
private static <E extends Event, V> EventNode<E> create(@NotNull String name,
|
||||||
@NotNull EventFilter<E, V> filter,
|
@NotNull EventFilter<E, V> filter,
|
||||||
@Nullable BiPredicate<E, V> predicate) {
|
@Nullable BiPredicate<E, V> predicate) {
|
||||||
return new EventNode<>(name, filter, predicate != null ? (e, o) -> predicate.test(e, (V) o) : null);
|
//noinspection unchecked
|
||||||
}
|
return new EventNodeImpl<>(name, filter, predicate != null ? (e, o) -> predicate.test(e, (V) o) : null);
|
||||||
|
|
||||||
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<>();
|
|
||||||
private final Set<EventNode<T>> children = new CopyOnWriteArraySet<>();
|
|
||||||
|
|
||||||
protected final String name;
|
|
||||||
protected final EventFilter<T, ?> filter;
|
|
||||||
protected final BiPredicate<T, Object> predicate;
|
|
||||||
protected final Class<T> eventType;
|
|
||||||
private volatile int priority;
|
|
||||||
private volatile EventNode<? super T> parent;
|
|
||||||
|
|
||||||
protected EventNode(@NotNull String name,
|
|
||||||
@NotNull EventFilter<T, ?> filter,
|
|
||||||
@Nullable BiPredicate<T, Object> predicate) {
|
|
||||||
this.name = name;
|
|
||||||
this.filter = filter;
|
|
||||||
this.predicate = predicate;
|
|
||||||
this.eventType = filter.getEventType();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Condition to enter the node.
|
|
||||||
*
|
|
||||||
* @param event the called event
|
|
||||||
* @return true to enter the node, false otherwise
|
|
||||||
*/
|
|
||||||
protected boolean condition(@NotNull T event) {
|
|
||||||
if (predicate == null)
|
|
||||||
return true;
|
|
||||||
final var value = filter.getHandler(event);
|
|
||||||
try {
|
|
||||||
return predicate.test(event, value);
|
|
||||||
} catch (Exception e) {
|
|
||||||
MinecraftServer.getExceptionManager().handleException(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -232,45 +190,7 @@ public class EventNode<T extends Event> {
|
|||||||
*
|
*
|
||||||
* @param event the event to execute
|
* @param event the event to execute
|
||||||
*/
|
*/
|
||||||
public void call(@NotNull T event) {
|
void call(@NotNull T event);
|
||||||
final var eventClass = event.getClass();
|
|
||||||
if (!eventType.isAssignableFrom(eventClass)) {
|
|
||||||
// Invalid event type
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!condition(event)) {
|
|
||||||
// Cancelled by superclass
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Process listener list
|
|
||||||
final var entry = listenerMap.get(eventClass);
|
|
||||||
if (entry == null) {
|
|
||||||
// No listener nor children
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final var listeners = entry.listeners;
|
|
||||||
if (!listeners.isEmpty()) {
|
|
||||||
for (EventListener<T> listener : listeners) {
|
|
||||||
EventListener.Result result;
|
|
||||||
try {
|
|
||||||
result = listener.run(event);
|
|
||||||
} catch (Exception e) {
|
|
||||||
result = EventListener.Result.EXCEPTION;
|
|
||||||
MinecraftServer.getExceptionManager().handleException(e);
|
|
||||||
}
|
|
||||||
if (result == EventListener.Result.EXPIRED) {
|
|
||||||
listeners.remove(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Process children
|
|
||||||
if (entry.childCount > 0) {
|
|
||||||
this.children.stream()
|
|
||||||
.sorted(Comparator.comparing(EventNode::getPriority))
|
|
||||||
.forEach(child -> child.call(event));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a cancellable event with a callback to execute if the event is successful.
|
* Execute a cancellable event with a callback to execute if the event is successful.
|
||||||
@ -279,7 +199,7 @@ public class EventNode<T extends Event> {
|
|||||||
* @param event The event to execute
|
* @param event The event to execute
|
||||||
* @param successCallback A callback if the event is not cancelled
|
* @param successCallback A callback if the event is not cancelled
|
||||||
*/
|
*/
|
||||||
public void callCancellable(@NotNull T event, @NotNull Runnable successCallback) {
|
default void callCancellable(@NotNull T event, @NotNull Runnable successCallback) {
|
||||||
call(event);
|
call(event);
|
||||||
if (!(event instanceof CancellableEvent) || !((CancellableEvent) event).isCancelled()) {
|
if (!(event instanceof CancellableEvent) || !((CancellableEvent) event).isCancelled()) {
|
||||||
successCallback.run();
|
successCallback.run();
|
||||||
@ -287,25 +207,19 @@ public class EventNode<T extends Event> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
public @NotNull String getName() {
|
@NotNull Class<T> getEventType();
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
public int getPriority() {
|
@NotNull String getName();
|
||||||
return priority;
|
|
||||||
}
|
@Contract(pure = true)
|
||||||
|
int getPriority();
|
||||||
|
|
||||||
@Contract(value = "_ -> this")
|
@Contract(value = "_ -> this")
|
||||||
public @NotNull EventNode<T> setPriority(int priority) {
|
@NotNull EventNode<T> setPriority(int priority);
|
||||||
this.priority = priority;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
public @Nullable EventNode<? super T> getParent() {
|
@Nullable EventNode<? super T> getParent();
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an unmodifiable view of the children in this node.
|
* Returns an unmodifiable view of the children in this node.
|
||||||
@ -314,9 +228,7 @@ public class EventNode<T extends Event> {
|
|||||||
* @see #removeChild(EventNode)
|
* @see #removeChild(EventNode)
|
||||||
*/
|
*/
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
public @NotNull Set<@NotNull EventNode<T>> getChildren() {
|
@NotNull Set<@NotNull EventNode<T>> getChildren();
|
||||||
return Collections.unmodifiableSet(children);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locates all child nodes with the given name and event type recursively starting at this node.
|
* Locates all child nodes with the given name and event type recursively starting at this node.
|
||||||
@ -326,21 +238,7 @@ public class EventNode<T extends Event> {
|
|||||||
* @return All matching event nodes
|
* @return All matching event nodes
|
||||||
*/
|
*/
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
public <E extends T> @NotNull List<EventNode<E>> findChildren(@NotNull String name, Class<E> eventType) {
|
<E extends T> @NotNull List<EventNode<E>> findChildren(@NotNull String name, Class<E> eventType);
|
||||||
if (children.isEmpty()) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
synchronized (GLOBAL_CHILD_LOCK) {
|
|
||||||
List<EventNode<E>> result = new ArrayList<>();
|
|
||||||
for (EventNode<T> child : children) {
|
|
||||||
if (EventNode.equals(child, name, eventType)) {
|
|
||||||
result.add((EventNode<E>) child);
|
|
||||||
}
|
|
||||||
result.addAll(child.findChildren(name, eventType));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locates all child nodes with the given name and event type recursively starting at this node.
|
* Locates all child nodes with the given name and event type recursively starting at this node.
|
||||||
@ -349,8 +247,8 @@ public class EventNode<T extends Event> {
|
|||||||
* @return All matching event nodes
|
* @return All matching event nodes
|
||||||
*/
|
*/
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
public @NotNull List<EventNode<T>> findChildren(@NotNull String name) {
|
default @NotNull List<EventNode<T>> findChildren(@NotNull String name) {
|
||||||
return findChildren(name, eventType);
|
return findChildren(name, getEventType());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -362,21 +260,7 @@ public class EventNode<T extends Event> {
|
|||||||
* @param eventType The event node type to filter for
|
* @param eventType The event node type to filter for
|
||||||
* @param eventNode The replacement node
|
* @param eventNode The replacement node
|
||||||
*/
|
*/
|
||||||
public <E extends T> void replaceChildren(@NotNull String name, @NotNull Class<E> eventType, @NotNull EventNode<E> eventNode) {
|
<E extends T> void replaceChildren(@NotNull String name, @NotNull Class<E> eventType, @NotNull EventNode<E> eventNode);
|
||||||
if (children.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
synchronized (GLOBAL_CHILD_LOCK) {
|
|
||||||
for (EventNode<T> child : children) {
|
|
||||||
if (EventNode.equals(child, name, eventType)) {
|
|
||||||
removeChild(child);
|
|
||||||
addChild(eventNode);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
child.replaceChildren(name, eventType, eventNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces all children matching the given name and type recursively starting from this node.
|
* Replaces all children matching the given name and type recursively starting from this node.
|
||||||
@ -386,8 +270,8 @@ public class EventNode<T extends Event> {
|
|||||||
* @param name The node name to filter for
|
* @param name The node name to filter for
|
||||||
* @param eventNode The replacement node
|
* @param eventNode The replacement node
|
||||||
*/
|
*/
|
||||||
public void replaceChildren(@NotNull String name, @NotNull EventNode<T> eventNode) {
|
default void replaceChildren(@NotNull String name, @NotNull EventNode<T> eventNode) {
|
||||||
replaceChildren(name, eventType, eventNode);
|
replaceChildren(name, getEventType(), eventNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -396,29 +280,14 @@ public class EventNode<T extends Event> {
|
|||||||
* @param name The node name to filter for
|
* @param name The node name to filter for
|
||||||
* @param eventType The node type to filter for
|
* @param eventType The node type to filter for
|
||||||
*/
|
*/
|
||||||
public void removeChildren(@NotNull String name, @NotNull Class<? extends T> eventType) {
|
void removeChildren(@NotNull String name, @NotNull Class<? extends T> eventType);
|
||||||
if (children.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
synchronized (GLOBAL_CHILD_LOCK) {
|
|
||||||
for (EventNode<T> child : children) {
|
|
||||||
if (EventNode.equals(child, name, eventType)) {
|
|
||||||
removeChild(child);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
child.removeChildren(name, eventType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively removes children with the given name starting at this node.
|
* Recursively removes children with the given name starting at this node.
|
||||||
*
|
*
|
||||||
* @param name The node name to filter for
|
* @param name The node name to filter for
|
||||||
*/
|
*/
|
||||||
public void removeChildren(@NotNull String name) {
|
void removeChildren(@NotNull String name);
|
||||||
removeChildren(name, eventType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directly adds a child node to this node.
|
* Directly adds a child node to this node.
|
||||||
@ -427,27 +296,7 @@ public class EventNode<T extends Event> {
|
|||||||
* @return this, can be used for chaining
|
* @return this, can be used for chaining
|
||||||
*/
|
*/
|
||||||
@Contract(value = "_ -> this")
|
@Contract(value = "_ -> this")
|
||||||
public @NotNull EventNode<T> addChild(@NotNull EventNode<? extends T> child) {
|
@NotNull EventNode<T> addChild(@NotNull EventNode<? extends T> child);
|
||||||
synchronized (GLOBAL_CHILD_LOCK) {
|
|
||||||
Check.stateCondition(child.parent != null, "Node already has a parent");
|
|
||||||
Check.stateCondition(Objects.equals(parent, child), "Cannot have a child as parent");
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directly removes the given child from this node.
|
* Directly removes the given child from this node.
|
||||||
@ -456,100 +305,28 @@ public class EventNode<T extends Event> {
|
|||||||
* @return this, can be used for chaining
|
* @return this, can be used for chaining
|
||||||
*/
|
*/
|
||||||
@Contract(value = "_ -> this")
|
@Contract(value = "_ -> this")
|
||||||
public @NotNull EventNode<T> removeChild(@NotNull EventNode<? extends T> child) {
|
@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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(value = "_ -> this")
|
@Contract(value = "_ -> this")
|
||||||
public @NotNull EventNode<T> addListener(@NotNull EventListener<? extends T> listener) {
|
@NotNull EventNode<T> addListener(@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(value = "_, _ -> this")
|
@Contract(value = "_, _ -> this")
|
||||||
public <E extends T> @NotNull EventNode<T> addListener(@NotNull Class<E> eventType, @NotNull Consumer<@NotNull E> listener) {
|
default <E extends T> @NotNull EventNode<T> addListener(@NotNull Class<E> eventType, @NotNull Consumer<@NotNull E> listener) {
|
||||||
return addListener(EventListener.of(eventType, listener));
|
return addListener(EventListener.of(eventType, listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract(value = "_ -> this")
|
@Contract(value = "_ -> this")
|
||||||
public @NotNull EventNode<T> removeListener(@NotNull EventListener<? extends T> listener) {
|
@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)
|
|
||||||
return this;
|
|
||||||
var listeners = entry.listeners;
|
|
||||||
final boolean removed = listeners.remove(listener);
|
|
||||||
if (removed && parent != null) {
|
|
||||||
synchronized (parent.lock) {
|
|
||||||
parent.decreaseChildListenerCount(eventType, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void increaseChildListenerCount(Class<? extends T> eventClass, int count) {
|
@ApiStatus.Experimental
|
||||||
var entry = listenerMap.computeIfAbsent(eventClass, aClass -> new ListenerEntry<>());
|
void map(@NotNull EventNode<? extends T> node, @NotNull Object value);
|
||||||
ListenerEntry.addAndGet(entry, count);
|
|
||||||
if (parent != null) {
|
|
||||||
parent.increaseChildListenerCount(eventClass, count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void decreaseChildListenerCount(Class<? extends T> eventClass, int count) {
|
@ApiStatus.Experimental
|
||||||
var entry = listenerMap.computeIfAbsent(eventClass, aClass -> new ListenerEntry<>());
|
boolean unmap(@NotNull Object value);
|
||||||
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);
|
|
||||||
}
|
|
||||||
if (parent != null) {
|
|
||||||
parent.decreaseChildListenerCount(eventClass, count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean equals(EventNode<?> node, String name, Class<?> eventType) {
|
@ApiStatus.Experimental
|
||||||
final boolean nameCheck = node.getName().equals(name);
|
void register(@NotNull EventBinding<? extends T> binding);
|
||||||
final boolean typeCheck = eventType.isAssignableFrom(node.eventType);
|
|
||||||
return nameCheck && typeCheck;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ListenerEntry<T extends Event> {
|
@ApiStatus.Experimental
|
||||||
private static final AtomicIntegerFieldUpdater<ListenerEntry> CHILD_UPDATER =
|
void unregister(@NotNull EventBinding<? extends T> binding);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
365
src/main/java/net/minestom/server/event/EventNodeImpl.java
Normal file
365
src/main/java/net/minestom/server/event/EventNodeImpl.java
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
package net.minestom.server.event;
|
||||||
|
|
||||||
|
import net.minestom.server.MinecraftServer;
|
||||||
|
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.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.IntUnaryOperator;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
class EventNodeImpl<T extends Event> implements EventNode<T> {
|
||||||
|
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<>();
|
||||||
|
private final Set<EventNode<T>> children = new CopyOnWriteArraySet<>();
|
||||||
|
private final Map<Object, ListenerEntry<T>> mappedNodeCache = new WeakHashMap<>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
protected EventNodeImpl(@NotNull String name,
|
||||||
|
@NotNull EventFilter<T, ?> filter,
|
||||||
|
@Nullable BiPredicate<T, Object> predicate) {
|
||||||
|
this.name = name;
|
||||||
|
this.filter = filter;
|
||||||
|
this.predicate = predicate;
|
||||||
|
this.eventType = filter.eventType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void call(@NotNull T event) {
|
||||||
|
final var eventClass = event.getClass();
|
||||||
|
if (!eventType.isAssignableFrom(eventClass)) return; // Invalid event type
|
||||||
|
// Conditions
|
||||||
|
if (predicate != null) {
|
||||||
|
try {
|
||||||
|
final var value = filter.getHandler(event);
|
||||||
|
if (!predicate.test(event, value)) return;
|
||||||
|
} catch (Exception e) {
|
||||||
|
MinecraftServer.getExceptionManager().handleException(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Process listeners list
|
||||||
|
final var entry = listenerMap.get(eventClass);
|
||||||
|
if (entry == null) return; // No listener nor children
|
||||||
|
entry.call(event);
|
||||||
|
// Process children
|
||||||
|
if (entry.childCount > 0) {
|
||||||
|
this.children.stream()
|
||||||
|
.sorted(Comparator.comparing(EventNode::getPriority))
|
||||||
|
.forEach(child -> child.call(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <E extends T> @NotNull List<EventNode<E>> findChildren(@NotNull String name, Class<E> eventType) {
|
||||||
|
if (children.isEmpty()) return Collections.emptyList();
|
||||||
|
synchronized (GLOBAL_CHILD_LOCK) {
|
||||||
|
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) {
|
||||||
|
if (children.isEmpty()) return;
|
||||||
|
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) {
|
||||||
|
if (children.isEmpty()) return;
|
||||||
|
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");
|
||||||
|
final boolean result = this.children.add((EventNodeImpl<T>) childImpl);
|
||||||
|
if (result) {
|
||||||
|
childImpl.parent = this;
|
||||||
|
// Increase listener count
|
||||||
|
propagateNode(childImpl, IntUnaryOperator.identity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull EventNode<T> removeChild(@NotNull EventNode<? extends T> child) {
|
||||||
|
synchronized (GLOBAL_CHILD_LOCK) {
|
||||||
|
final boolean result = this.children.remove(child);
|
||||||
|
if (result) {
|
||||||
|
final var childImpl = (EventNodeImpl<? extends T>) child;
|
||||||
|
childImpl.parent = null;
|
||||||
|
// Decrease listener count
|
||||||
|
propagateNode(childImpl, count -> -count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull EventNode<T> addListener(@NotNull EventListener<? extends T> listener) {
|
||||||
|
synchronized (GLOBAL_CHILD_LOCK) {
|
||||||
|
final var eventType = listener.getEventType();
|
||||||
|
var entry = getEntry(eventType);
|
||||||
|
entry.listeners.add((EventListener<T>) listener);
|
||||||
|
propagateToParent(eventType, 1);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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) return this;
|
||||||
|
var listeners = entry.listeners;
|
||||||
|
final boolean removed = listeners.remove(listener);
|
||||||
|
if (removed) propagateToParent(eventType, -1);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void map(@NotNull EventNode<? extends T> node, @NotNull Object value) {
|
||||||
|
final var nodeImpl = (EventNodeImpl<? extends T>) node;
|
||||||
|
final var valueType = value.getClass();
|
||||||
|
synchronized (GLOBAL_CHILD_LOCK) {
|
||||||
|
nodeImpl.listenerMap.forEach((type, listenerEntry) -> {
|
||||||
|
final var entry = getEntry(type);
|
||||||
|
final boolean correct = entry.filters.stream().anyMatch(eventFilter -> {
|
||||||
|
final var handlerType = eventFilter.handlerType();
|
||||||
|
return handlerType != null && handlerType.isAssignableFrom(valueType);
|
||||||
|
});
|
||||||
|
Check.stateCondition(!correct, "The node filter {0} is not compatible with type {1}", nodeImpl.eventType, valueType);
|
||||||
|
synchronized (mappedNodeCache) {
|
||||||
|
entry.mappedNode.put(value, (EventNode<T>) nodeImpl);
|
||||||
|
mappedNodeCache.put(value, entry);
|
||||||
|
// TODO propagate
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean unmap(@NotNull Object value) {
|
||||||
|
synchronized (GLOBAL_CHILD_LOCK) {
|
||||||
|
synchronized (mappedNodeCache) {
|
||||||
|
var entry = mappedNodeCache.remove(value);
|
||||||
|
if (entry == null) return false;
|
||||||
|
final EventNode<T> previousNode = entry.mappedNode.remove(value);
|
||||||
|
if (previousNode != null) {
|
||||||
|
// TODO propagate
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void register(@NotNull EventBinding<? extends T> binding) {
|
||||||
|
synchronized (GLOBAL_CHILD_LOCK) {
|
||||||
|
for (var eventType : binding.eventTypes()) {
|
||||||
|
var entry = getEntry((Class<? extends T>) eventType);
|
||||||
|
final boolean added = entry.bindingConsumers.add((Consumer<T>) binding.consumer(eventType));
|
||||||
|
if (added) propagateToParent((Class<? extends T>) eventType, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregister(@NotNull EventBinding<? extends T> binding) {
|
||||||
|
synchronized (GLOBAL_CHILD_LOCK) {
|
||||||
|
for (var eventType : binding.eventTypes()) {
|
||||||
|
var entry = listenerMap.get(eventType);
|
||||||
|
if (entry == null) return;
|
||||||
|
final boolean removed = entry.bindingConsumers.remove(binding.consumer(eventType));
|
||||||
|
if (removed) propagateToParent((Class<? extends T>) eventType, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void propagateChildCountChange(Class<? extends T> eventClass, int count) {
|
||||||
|
var entry = getEntry(eventClass);
|
||||||
|
final int result = ListenerEntry.CHILD_UPDATER.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);
|
||||||
|
}
|
||||||
|
if (parent != null) {
|
||||||
|
parent.propagateChildCountChange(eventClass, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void propagateToParent(Class<? extends T> eventClass, int count) {
|
||||||
|
final var parent = this.parent;
|
||||||
|
if (parent != null) {
|
||||||
|
synchronized (parent.lock) {
|
||||||
|
parent.propagateChildCountChange(eventClass, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void propagateNode(EventNodeImpl<? extends T> child, IntUnaryOperator operator) {
|
||||||
|
synchronized (lock) {
|
||||||
|
final var listeners = child.listenerMap;
|
||||||
|
listeners.forEach((eventClass, eventListeners) -> {
|
||||||
|
final var entry = listeners.get(eventClass);
|
||||||
|
if (entry == null) return;
|
||||||
|
final int childCount = entry.listeners.size() + entry.childCount;
|
||||||
|
propagateChildCountChange(eventClass, operator.applyAsInt(childCount));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListenerEntry<T> getEntry(Class<? extends T> type) {
|
||||||
|
return listenerMap.computeIfAbsent(type, aClass -> new ListenerEntry<>(this, (Class<T>) aClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ListenerEntry<T extends Event> {
|
||||||
|
private static final List<EventFilter<? extends Event, ?>> FILTERS = List.of(
|
||||||
|
EventFilter.ENTITY,
|
||||||
|
EventFilter.ITEM, EventFilter.INSTANCE,
|
||||||
|
EventFilter.INVENTORY, EventFilter.BLOCK);
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private static final AtomicIntegerFieldUpdater<ListenerEntry> CHILD_UPDATER =
|
||||||
|
AtomicIntegerFieldUpdater.newUpdater(ListenerEntry.class, "childCount");
|
||||||
|
|
||||||
|
final EventNodeImpl<T> node;
|
||||||
|
final List<EventFilter<?, ?>> filters;
|
||||||
|
final List<EventListener<T>> listeners = new CopyOnWriteArrayList<>();
|
||||||
|
final Set<Consumer<T>> bindingConsumers = new CopyOnWriteArraySet<>();
|
||||||
|
final Map<Object, EventNode<T>> mappedNode = new WeakHashMap<>();
|
||||||
|
volatile int childCount;
|
||||||
|
|
||||||
|
ListenerEntry(EventNodeImpl<T> node, Class<T> eventType) {
|
||||||
|
this.node = node;
|
||||||
|
this.filters = FILTERS.stream().filter(eventFilter -> eventFilter.eventType().isAssignableFrom(eventType)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
void call(T event) {
|
||||||
|
// Event interfaces
|
||||||
|
if (!bindingConsumers.isEmpty()) {
|
||||||
|
for (var consumer : bindingConsumers) {
|
||||||
|
consumer.accept(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Mapped listeners
|
||||||
|
if (!mappedNode.isEmpty()) {
|
||||||
|
synchronized (node.mappedNodeCache) {
|
||||||
|
if (!mappedNode.isEmpty()) {
|
||||||
|
// Check mapped listeners for each individual event handler
|
||||||
|
for (var filter : filters) {
|
||||||
|
final var handler = filter.castHandler(event);
|
||||||
|
final var map = mappedNode.get(handler);
|
||||||
|
if (map != null) map.call(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Basic listeners
|
||||||
|
if (!listeners.isEmpty()) {
|
||||||
|
for (EventListener<T> listener : listeners) {
|
||||||
|
EventListener.Result result;
|
||||||
|
try {
|
||||||
|
result = listener.run(event);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result = EventListener.Result.EXCEPTION;
|
||||||
|
MinecraftServer.getExceptionManager().handleException(e);
|
||||||
|
}
|
||||||
|
if (result == EventListener.Result.EXPIRED) {
|
||||||
|
listeners.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ import java.util.function.Consumer;
|
|||||||
/**
|
/**
|
||||||
* Object containing all the global event listeners.
|
* Object containing all the global event listeners.
|
||||||
*/
|
*/
|
||||||
public final class GlobalEventHandler extends EventNode<Event> {
|
public final class GlobalEventHandler extends EventNodeImpl<Event> {
|
||||||
public GlobalEventHandler() {
|
public GlobalEventHandler() {
|
||||||
super("global", EventFilter.ALL, null);
|
super("global", EventFilter.ALL, null);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package net.minestom.server.instance;
|
|||||||
|
|
||||||
import com.extollit.gaming.ai.path.model.ColumnarOcclusionFieldList;
|
import com.extollit.gaming.ai.path.model.ColumnarOcclusionFieldList;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import net.minestom.server.coordinate.Vec;
|
import net.minestom.server.coordinate.Vec;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
@ -88,20 +89,18 @@ public class DynamicChunk extends Chunk {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tick(long time) {
|
public void tick(long time) {
|
||||||
if (tickableMap.isEmpty())
|
if (tickableMap.isEmpty()) return;
|
||||||
return;
|
Int2ObjectMaps.fastForEach(tickableMap, entry -> {
|
||||||
for (var entry : tickableMap.int2ObjectEntrySet()) {
|
|
||||||
final int index = entry.getIntKey();
|
final int index = entry.getIntKey();
|
||||||
final Block block = entry.getValue();
|
final Block block = entry.getValue();
|
||||||
final var handler = block.handler();
|
final BlockHandler handler = block.handler();
|
||||||
if (handler != null) {
|
if (handler == null) return;
|
||||||
final int x = ChunkUtils.blockIndexToChunkPositionX(index);
|
final int x = ChunkUtils.blockIndexToChunkPositionX(index);
|
||||||
final int y = ChunkUtils.blockIndexToChunkPositionY(index);
|
final int y = ChunkUtils.blockIndexToChunkPositionY(index);
|
||||||
final int z = ChunkUtils.blockIndexToChunkPositionZ(index);
|
final int z = ChunkUtils.blockIndexToChunkPositionZ(index);
|
||||||
final Vec blockPosition = new Vec(x, y, z);
|
final Vec blockPosition = new Vec(x, y, z);
|
||||||
handler.tick(new BlockHandler.Tick(block, instance, blockPosition));
|
handler.tick(new BlockHandler.Tick(block, instance, blockPosition));
|
||||||
}
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -69,6 +69,8 @@ final class BlockImpl implements Block {
|
|||||||
private final NBTCompound nbt;
|
private final NBTCompound nbt;
|
||||||
private final BlockHandler handler;
|
private final BlockHandler handler;
|
||||||
|
|
||||||
|
private int hashCode; // Cache
|
||||||
|
|
||||||
BlockImpl(@NotNull Registry.BlockEntry registry,
|
BlockImpl(@NotNull Registry.BlockEntry registry,
|
||||||
@NotNull Map<Map<String, String>, Block> propertyEntry,
|
@NotNull Map<Map<String, String>, Block> propertyEntry,
|
||||||
@NotNull Map<String, String> properties,
|
@NotNull Map<String, String> properties,
|
||||||
@ -151,7 +153,12 @@ final class BlockImpl implements Block {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(stateId(), nbt, handler);
|
int result = hashCode;
|
||||||
|
if (result == 0) {
|
||||||
|
result = Objects.hash(stateId(), nbt, handler);
|
||||||
|
this.hashCode = result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
Reference in New Issue
Block a user