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.entity.metadata.EntityMeta;
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.instance.AddEntityToInstanceEvent;
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.EntityTracker;
import net.minestom.server.instance.Instance;
@ -77,7 +81,7 @@ import java.util.function.UnaryOperator;
* <p>
* 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 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;
private final MutableNBTCompound nbtCompound = new MutableNBTCompound();
private final Scheduler scheduler = Scheduler.newScheduler();
private final EventNode<EntityEvent> eventNode = EventNode.type(toString(), EventFilter.ENTITY);
private final Set<Permission> permissions = new CopyOnWriteArraySet<>();
protected UUID uuid;
@ -1543,6 +1548,12 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ta
TagReadable.fromCompound(nbtCompound.toCompound()));
}
@Override
@ApiStatus.Experimental
public @NotNull EventNode<EntityEvent> eventNode() {
return eventNode;
}
/**
* 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> {
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
protected Handle<T> computeValue(Class<?> type) {
protected ListenerHandle<T> computeValue(Class<?> type) {
//noinspection unchecked
return new Handle<>((Class<T>) type);
return createHandle((Class<T>) type);
}
};
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();
}
protected @NotNull ListenerHandle<T> createHandle(@NotNull Class<T> listenerType) {
return new Handle<>(listenerType);
}
@Override
@SuppressWarnings("unchecked")
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")
final class Handle<E extends Event> implements ListenerHandle<E> {
non-sealed class Handle<E extends Event> implements ListenerHandle<E> {
private static final VarHandle UPDATED;
static {

View File

@ -2,7 +2,7 @@ package net.minestom.server.event;
import org.jetbrains.annotations.NotNull;
import java.util.function.Consumer;
import java.util.List;
/**
* Object containing all the global event listeners.
@ -12,12 +12,34 @@ public final class GlobalEventHandler extends EventNodeImpl<Event> {
super("global", EventFilter.ALL, null);
}
/**
* @deprecated use {@link #addListener(Class, Consumer)}
*/
@Deprecated
public <V extends Event> boolean addEventCallback(@NotNull Class<V> eventClass, @NotNull EventCallback<V> eventCallback) {
addListener(eventClass, eventCallback::run);
return true;
@Override
protected @NotNull ListenerHandle<Event> createHandle(@NotNull Class<Event> listenerType) {
return new GlobalHandle<>(listenerType);
}
@SuppressWarnings("unchecked")
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;
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.EntityEvent;
import net.minestom.server.event.trait.ItemEvent;
import net.minestom.server.event.trait.RecursiveEvent;
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
public void testCall() {
var node = EventNode.all("main");
@ -234,4 +244,25 @@ public class EventNodeTest {
node.call(new ItemTestEvent(item));
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());
}
}