Improve mapped node implementation. Add handler type to EventFilter

This commit is contained in:
TheMode 2021-08-14 21:20:51 +02:00
parent ebb8d03985
commit e5f0dc8061
2 changed files with 63 additions and 57 deletions

View File

@ -4,8 +4,10 @@ import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Player;
import net.minestom.server.event.trait.*;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.block.Block;
import net.minestom.server.inventory.Inventory;
import net.minestom.server.item.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -27,14 +29,16 @@ import java.util.function.Function;
*/
public interface EventFilter<E extends Event, H> {
EventFilter<Event, ?> ALL = from(Event.class, null);
EventFilter<EntityEvent, Entity> ENTITY = from(EntityEvent.class, EntityEvent::getEntity);
EventFilter<PlayerEvent, Player> PLAYER = from(PlayerEvent.class, PlayerEvent::getPlayer);
EventFilter<ItemEvent, ItemStack> ITEM = from(ItemEvent.class, ItemEvent::getItemStack);
EventFilter<InstanceEvent, Instance> INSTANCE = from(InstanceEvent.class, InstanceEvent::getInstance);
EventFilter<InventoryEvent, Inventory> INVENTORY = from(InventoryEvent.class, InventoryEvent::getInventory);
EventFilter<Event, ?> ALL = from(Event.class, null, null);
EventFilter<EntityEvent, Entity> ENTITY = from(EntityEvent.class, Entity.class, EntityEvent::getEntity);
EventFilter<PlayerEvent, Player> PLAYER = from(PlayerEvent.class, Player.class, PlayerEvent::getPlayer);
EventFilter<ItemEvent, ItemStack> ITEM = from(ItemEvent.class, ItemStack.class, ItemEvent::getItemStack);
EventFilter<InstanceEvent, Instance> INSTANCE = from(InstanceEvent.class, Instance.class, InstanceEvent::getInstance);
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,
@Nullable Class<H> handlerType,
@Nullable Function<E, H> handlerGetter) {
return new EventFilter<>() {
@Override
@ -43,9 +47,14 @@ public interface EventFilter<E extends Event, H> {
}
@Override
public @NotNull Class<E> getEventType() {
public @NotNull Class<E> 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);
@ApiStatus.Internal
default @Nullable H castHandler(@NotNull Object event) {
//noinspection unchecked
return getHandler((E) event);
}
/**
* The event type to filter on.
*
* @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();
}

View File

@ -1,7 +1,7 @@
package net.minestom.server.event;
import net.minestom.server.MinecraftServer;
import net.minestom.server.event.trait.*;
import net.minestom.server.event.trait.CancellableEvent;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagReadable;
import net.minestom.server.utils.validate.Check;
@ -16,8 +16,8 @@ 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.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* Represents a single node in an event graph.
@ -178,19 +178,13 @@ public class EventNode<T extends Event> {
return create(name, filter, (e, h) -> consumer.test(h.getTag(tag)));
}
public static <E extends Event, V> @NotNull Mapped<E, V> mapped(@NotNull String name,
@NotNull EventFilter<E, V> filter,
@NotNull V value) {
return new Mapped<>(name, filter, value);
}
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);
}
private static final Map<Class<? extends Event>, List<Function<Event, Object>>> HANDLER_SUPPLIERS = new ConcurrentHashMap<>();
private static final Map<Class<? extends Event>, List<EventFilter<?, ?>>> HANDLER_SUPPLIERS = new ConcurrentHashMap<>();
private static final Object GLOBAL_CHILD_LOCK = new Object();
private final Object lock = new Object();
@ -211,7 +205,7 @@ public class EventNode<T extends Event> {
this.name = name;
this.filter = filter;
this.predicate = predicate;
this.eventType = filter.getEventType();
this.eventType = filter.eventType();
}
/**
@ -254,8 +248,8 @@ public class EventNode<T extends Event> {
// Mapped listeners
if (!mappedNode.isEmpty()) {
// Check mapped listeners for each individual event handler
getEventMapping(eventClass).forEach(function -> {
final var handler = function.apply(event);
getEventFilters(eventClass).forEach(filter -> {
final var handler = filter.castHandler(event);
final var map = mappedNode.get(handler);
if (map != null) map.call(event);
});
@ -532,8 +526,19 @@ public class EventNode<T extends Event> {
return this;
}
public <E extends T, V> void map(@NotNull Mapped<E, V> map) {
this.mappedNode.put(map.value, (EventNode<T>) map);
public void map(@NotNull EventNode<? extends T> node, @NotNull Object value) {
final var nodeType = node.eventType;
final boolean correct = getEventFilters(nodeType).stream().anyMatch(eventFilter -> {
final var handlerType = eventFilter.handlerType();
return handlerType != null && handlerType.isAssignableFrom(value.getClass());
});
Check.stateCondition(!correct, "The node {0} is not compatible with objects of type {1}", nodeType, value.getClass());
//noinspection unchecked
this.mappedNode.put(value, (EventNode<T>) node);
}
public boolean unmap(@NotNull Object value) {
return mappedNode.remove(value) != null;
}
public <I> void addInter(@NotNull EventInterface<I> inter, @NotNull I value) {
@ -570,32 +575,20 @@ public class EventNode<T extends Event> {
return nameCheck && typeCheck;
}
// Returns a list of (event->object) functions used to retrieve handler.
// For example `PlayerUseItemEvent` should return a function to retrieve the player,
// and another for the item.
// All event trait are currently hardcoded.
private static List<Function<Event, Object>> getEventMapping(Class<? extends Event> eventClass) {
return HANDLER_SUPPLIERS.computeIfAbsent(eventClass, clazz -> {
List<Function<Event, Object>> result = new ArrayList<>();
if (PlayerEvent.class.isAssignableFrom(clazz)) {
result.add(e -> ((PlayerEvent) e).getPlayer());
} else if (EntityEvent.class.isAssignableFrom(clazz)) {
result.add(e -> ((EntityEvent) e).getEntity());
}
if (ItemEvent.class.isAssignableFrom(clazz)) {
result.add(e -> ((ItemEvent) e).getItemStack());
}
if (InstanceEvent.class.isAssignableFrom(clazz)) {
result.add(e -> ((InstanceEvent) e).getInstance());
}
if (InventoryEvent.class.isAssignableFrom(clazz)) {
result.add(e -> ((InventoryEvent) e).getInventory());
}
if (BlockEvent.class.isAssignableFrom(clazz)) {
result.add(e -> ((BlockEvent) e).getBlock());
}
return result;
});
private static final List<EventFilter<? extends Event, ?>> FILTERS = List.of(
EventFilter.ENTITY,
EventFilter.ITEM, EventFilter.INSTANCE,
EventFilter.INVENTORY, EventFilter.BLOCK);
/**
* Returns a list of (event->object) functions used to retrieve handler.
* For example `PlayerUseItemEvent` should return a function to retrieve the player,
* and another for the item.
* Event traits are currently hardcoded.
*/
private static List<EventFilter<?, ?>> getEventFilters(Class<? extends Event> eventType) {
return HANDLER_SUPPLIERS.computeIfAbsent(eventType, clazz ->
FILTERS.stream().filter(eventFilter -> eventFilter.eventType().isAssignableFrom(clazz)).collect(Collectors.toList()));
}
private static class ListenerEntry<T extends Event> {
@ -609,13 +602,4 @@ public class EventNode<T extends Event> {
return CHILD_UPDATER.addAndGet(entry, add);
}
}
public static final class Mapped<T extends Event, V> extends EventNode<T> {
private final V value;
Mapped(@NotNull String name, @NotNull EventFilter<T, ?> filter, @NotNull V value) {
super(name, filter, null);
this.value = value;
}
}
}