Local node support (#688)

This commit is contained in:
TheMode 2022-03-05 11:11:39 +01:00 committed by GitHub
parent 7c874bb588
commit 0cb2600929
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 94 additions and 40 deletions

View File

@ -15,9 +15,13 @@ import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec; import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.entity.metadata.EntityMeta;
import net.minestom.server.event.EventDispatcher; import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.EventFilter;
import net.minestom.server.event.EventHandler;
import net.minestom.server.event.EventNode;
import net.minestom.server.event.entity.*; import net.minestom.server.event.entity.*;
import net.minestom.server.event.instance.AddEntityToInstanceEvent; import net.minestom.server.event.instance.AddEntityToInstanceEvent;
import net.minestom.server.event.instance.RemoveEntityFromInstanceEvent; import net.minestom.server.event.instance.RemoveEntityFromInstanceEvent;
import net.minestom.server.event.trait.EntityEvent;
import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.EntityTracker; import net.minestom.server.instance.EntityTracker;
import net.minestom.server.instance.Instance; import net.minestom.server.instance.Instance;
@ -77,7 +81,7 @@ import java.util.function.UnaryOperator;
* <p> * <p>
* To create your own entity you probably want to extends {@link LivingEntity} or {@link EntityCreature} instead. * To create your own entity you probably want to extends {@link LivingEntity} or {@link EntityCreature} instead.
*/ */
public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, TagHandler, PermissionHandler, HoverEventSource<ShowEntity>, Sound.Emitter { public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, EventHandler<EntityEvent>, TagHandler, PermissionHandler, HoverEventSource<ShowEntity>, Sound.Emitter {
private static final Int2ObjectSyncMap<Entity> ENTITY_BY_ID = Int2ObjectSyncMap.hashmap(); private static final Int2ObjectSyncMap<Entity> ENTITY_BY_ID = Int2ObjectSyncMap.hashmap();
private static final Map<UUID, Entity> ENTITY_BY_UUID = new ConcurrentHashMap<>(); private static final Map<UUID, Entity> ENTITY_BY_UUID = new ConcurrentHashMap<>();
@ -143,6 +147,7 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ta
protected final Set<Player> viewers = viewEngine.set; protected final Set<Player> viewers = viewEngine.set;
private final MutableNBTCompound nbtCompound = new MutableNBTCompound(); private final MutableNBTCompound nbtCompound = new MutableNBTCompound();
private final Scheduler scheduler = Scheduler.newScheduler(); private final Scheduler scheduler = Scheduler.newScheduler();
private final EventNode<EntityEvent> eventNode = EventNode.type(toString(), EventFilter.ENTITY);
private final Set<Permission> permissions = new CopyOnWriteArraySet<>(); private final Set<Permission> permissions = new CopyOnWriteArraySet<>();
protected UUID uuid; protected UUID uuid;
@ -1543,6 +1548,12 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ta
TagReadable.fromCompound(nbtCompound.toCompound())); TagReadable.fromCompound(nbtCompound.toCompound()));
} }
@Override
@ApiStatus.Experimental
public @NotNull EventNode<EntityEvent> eventNode() {
return eventNode;
}
/** /**
* Applies knockback to the entity * Applies knockback to the entity
* *

View File

@ -0,0 +1,13 @@
package net.minestom.server.event;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Represents an element which can have {@link Event} listeners assigned to it.
*/
@ApiStatus.Experimental
@ApiStatus.NonExtendable
public interface EventHandler<T extends Event> {
@NotNull EventNode<T> eventNode();
}

View File

@ -20,11 +20,11 @@ import java.util.function.Consumer;
non-sealed class EventNodeImpl<T extends Event> implements EventNode<T> { non-sealed class EventNodeImpl<T extends Event> implements EventNode<T> {
private static final Object GLOBAL_CHILD_LOCK = new Object(); private static final Object GLOBAL_CHILD_LOCK = new Object();
private final ClassValue<Handle<T>> handleMap = new ClassValue<>() { private final ClassValue<ListenerHandle<T>> handleMap = new ClassValue<>() {
@Override @Override
protected Handle<T> computeValue(Class<?> type) { protected ListenerHandle<T> computeValue(Class<?> type) {
//noinspection unchecked //noinspection unchecked
return new Handle<>((Class<T>) type); return createHandle((Class<T>) type);
} }
}; };
private final Map<Class<? extends T>, ListenerEntry<T>> listenerMap = new ConcurrentHashMap<>(); private final Map<Class<? extends T>, ListenerEntry<T>> listenerMap = new ConcurrentHashMap<>();
@ -47,6 +47,10 @@ non-sealed class EventNodeImpl<T extends Event> implements EventNode<T> {
this.eventType = filter.eventType(); this.eventType = filter.eventType();
} }
protected @NotNull ListenerHandle<T> createHandle(@NotNull Class<T> listenerType) {
return new Handle<>(listenerType);
}
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <E extends T> @NotNull ListenerHandle<E> getHandle(@NotNull Class<E> handleType) { public <E extends T> @NotNull ListenerHandle<E> getHandle(@NotNull Class<E> handleType) {
@ -307,7 +311,7 @@ non-sealed class EventNodeImpl<T extends Event> implements EventNode<T> {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final class Handle<E extends Event> implements ListenerHandle<E> { non-sealed class Handle<E extends Event> implements ListenerHandle<E> {
private static final VarHandle UPDATED; private static final VarHandle UPDATED;
static { static {

View File

@ -2,7 +2,7 @@ package net.minestom.server.event;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.function.Consumer; import java.util.List;
/** /**
* Object containing all the global event listeners. * Object containing all the global event listeners.
@ -12,12 +12,34 @@ public final class GlobalEventHandler extends EventNodeImpl<Event> {
super("global", EventFilter.ALL, null); super("global", EventFilter.ALL, null);
} }
/** @Override
* @deprecated use {@link #addListener(Class, Consumer)} protected @NotNull ListenerHandle<Event> createHandle(@NotNull Class<Event> listenerType) {
*/ return new GlobalHandle<>(listenerType);
@Deprecated }
public <V extends Event> boolean addEventCallback(@NotNull Class<V> eventClass, @NotNull EventCallback<V> eventCallback) {
addListener(eventClass, eventCallback::run); @SuppressWarnings("unchecked")
return true; final class GlobalHandle<E extends Event> extends Handle<E> {
// Represents the filters where the handler has a node
private static final List<EventFilter<?, ?>> HANDLER_FILTERS = List.of(EventFilter.ENTITY);
// Local nodes handling
private final EventFilter<E, EventHandler<? super E>>[] localFilters;
GlobalHandle(Class<E> eventType) {
super(eventType);
// Filters with EventHandler support
this.localFilters = (EventFilter<E, EventHandler<? super E>>[]) HANDLER_FILTERS.stream()
.filter(filter -> filter.eventType().isAssignableFrom(eventType)).toArray(EventFilter[]::new);
}
@Override
public void call(@NotNull E event) {
// Per-handler listeners
for (var filter : localFilters) {
var handle = filter.getHandler(event);
if (handle != null) handle.eventNode().call(event);
}
// Global listeners
super.call(event);
}
} }
} }

View File

@ -1,27 +0,0 @@
package net.minestom.server.event.handler;
import net.minestom.server.event.Event;
import net.minestom.server.event.EventCallback;
import net.minestom.server.event.EventNode;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Represents an element which can have {@link Event} listeners assigned to it.
* <p>
* Use {@link EventNode} directly.
*/
@Deprecated
public interface EventHandler<T extends Event> {
@ApiStatus.Internal
@Deprecated(forRemoval = true)
@NotNull EventNode<T> getEventNode();
@Deprecated
default <V extends T> boolean addEventCallback(@NotNull Class<V> eventClass, @NotNull EventCallback<V> eventCallback) {
var node = getEventNode();
node.addListener(eventClass, eventCallback::run);
return true;
}
}

View File

@ -1,6 +1,9 @@
package net.minestom.server.event; package net.minestom.server.event;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
import net.minestom.server.event.trait.CancellableEvent; import net.minestom.server.event.trait.CancellableEvent;
import net.minestom.server.event.trait.EntityEvent;
import net.minestom.server.event.trait.ItemEvent; import net.minestom.server.event.trait.ItemEvent;
import net.minestom.server.event.trait.RecursiveEvent; import net.minestom.server.event.trait.RecursiveEvent;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
@ -45,6 +48,13 @@ public class EventNodeTest {
} }
} }
record EntityTestEvent(Entity entity) implements EntityEvent {
@Override
public @NotNull Entity getEntity() {
return entity;
}
}
@Test @Test
public void testCall() { public void testCall() {
var node = EventNode.all("main"); var node = EventNode.all("main");
@ -234,4 +244,25 @@ public class EventNodeTest {
node.call(new ItemTestEvent(item)); node.call(new ItemTestEvent(item));
assertFalse(result.get()); assertFalse(result.get());
} }
@Test
public void entityLocal() {
var node = new GlobalEventHandler();
var entity = new Entity(EntityType.ZOMBIE);
AtomicBoolean result = new AtomicBoolean(false);
var listener = EventListener.of(EntityTestEvent.class, event -> result.set(true));
entity.eventNode().addListener(listener);
assertFalse(result.get());
node.call(new EntityTestEvent(entity));
assertTrue(result.get());
result.set(false);
entity.eventNode().removeListener(listener);
node.call(new EntityTestEvent(entity));
assertFalse(result.get());
}
} }