Move optimized event calling inside ListenerHandle

This commit is contained in:
TheMode 2021-08-24 15:35:09 +02:00
parent aed441123e
commit 5ddd97cee2
11 changed files with 39 additions and 62 deletions

View File

@ -449,7 +449,7 @@ public class Entity implements Viewable, Tickable, TagHandler, PermissionHandler
update(time);
ticks++;
EventDispatcher.call(new EntityTickEvent(this), GlobalHandles.ENTITY_TICK);
GlobalHandles.ENTITY_TICK.call(new EntityTickEvent(this));
// remove expired effects
effectTick(time);

View File

@ -351,7 +351,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
}
// Tick event
EventDispatcher.call(new PlayerTickEvent(this), GlobalHandles.PLAYER_TICK);
GlobalHandles.PLAYER_TICK.call(new PlayerTickEvent(this));
}
@Override

View File

@ -10,10 +10,6 @@ public final class EventDispatcher {
MinecraftServer.getGlobalEventHandler().call(event);
}
public static <E extends Event> void call(@NotNull E event, @NotNull ListenerHandle<E> handle) {
MinecraftServer.getGlobalEventHandler().call(event, handle);
}
public static <E extends Event> ListenerHandle<E> getHandle(@NotNull Class<E> handleType) {
return MinecraftServer.getGlobalEventHandler().getHandle(handleType);
}

View File

@ -188,23 +188,9 @@ public interface EventNode<T extends Event> {
*/
default void call(@NotNull T event) {
//noinspection unchecked
call(event, getHandle((Class<T>) event.getClass()));
getHandle((Class<T>) event.getClass()).call(event);
}
/**
* Calls an event starting from this node.
* <p>
* The event handle can be retrieved using {@link #getHandle(Class)}
* and is useful to avoid map lookups for high-frequency events.
*
* @param event the event to call
* @param handle the event handle linked to this node
* @param <E> the event type
* @throws IllegalArgumentException if {@code handle}'s owner is not {@code this}
*/
@ApiStatus.Experimental
<E extends T> void call(@NotNull E event, @NotNull ListenerHandle<E> handle);
/**
* Gets the handle of an event type.
*
@ -215,20 +201,6 @@ public interface EventNode<T extends Event> {
@ApiStatus.Experimental
<E extends T> @NotNull ListenerHandle<E> getHandle(@NotNull Class<E> handleType);
/**
* Gets if any listener has been registered for the given handle.
* May trigger an update if the cached data is not correct.
* <p>
* Useful if you are able to avoid expensive computation in the case where
* the event is unused. Be aware that {@link #call(Event, ListenerHandle)}
* has similar optimization built-in.
*
* @param handle the listener handle
* @return true if the event has 1 or more listeners
*/
@ApiStatus.Experimental
boolean hasListener(@NotNull ListenerHandle<? extends T> handle);
/**
* Execute a cancellable event with a callback to execute if the event is successful.
* Event conditions and propagation is the same as {@link #call(Event)}.

View File

@ -39,18 +39,6 @@ class EventNodeImpl<T extends Event> implements EventNode<T> {
this.eventType = filter.eventType();
}
@Override
public <E extends T> void call(@NotNull E event, @NotNull ListenerHandle<E> handle) {
final Handle<T> castedHandle = (Handle<T>) handle;
Check.argCondition(castedHandle.node != this, "Invalid handle owner");
if (!castedHandle.updated) castedHandle.update();
final Consumer<T>[] listeners = castedHandle.listeners;
if (listeners.length == 0) return;
for (Consumer<T> listener : listeners) {
listener.accept(event);
}
}
@Override
public <E extends T> @NotNull ListenerHandle<E> getHandle(@NotNull Class<E> handleType) {
//noinspection unchecked
@ -58,13 +46,6 @@ class EventNodeImpl<T extends Event> implements EventNode<T> {
aClass -> new Handle<>(this, (Class<T>) aClass));
}
@Override
public boolean hasListener(@NotNull ListenerHandle<? extends T> handle) {
final Handle<T> castedHandle = (Handle<T>) handle;
if (!castedHandle.updated) castedHandle.update();
return castedHandle.listeners.length > 0;
}
@Override
public <E extends T> @NotNull List<EventNode<E>> findChildren(@NotNull String name, Class<E> eventType) {
synchronized (GLOBAL_CHILD_LOCK) {
@ -286,6 +267,22 @@ class EventNodeImpl<T extends Event> implements EventNode<T> {
this.eventType = eventType;
}
@Override
public void call(@NotNull E event) {
if (!updated) update();
final Consumer<E>[] listeners = this.listeners;
if (listeners.length == 0) return;
for (Consumer<E> listener : listeners) {
listener.accept(event);
}
}
@Override
public boolean hasListener() {
if (!updated) update();
return listeners.length > 0;
}
void update() {
synchronized (GLOBAL_CHILD_LOCK) {
this.listenersCache.clear();
@ -325,7 +322,7 @@ class EventNodeImpl<T extends Event> implements EventNode<T> {
for (var mappedEntry : mappedNodeCache.entrySet()) {
final EventNodeImpl<E> mappedNode = mappedEntry.getValue();
final Handle<E> handle = (Handle<E>) mappedNode.getHandle(eventType);
if (!mappedNode.hasListener(handle)) continue; // Implicit update
if (!handle.hasListener()) continue; // Implicit update
filters.add(mappedNode.filter);
handlers.put(mappedEntry.getKey(), handle);
}

View File

@ -1,6 +1,7 @@
package net.minestom.server.event;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Represents a key to access an {@link EventNode} listeners.
@ -11,4 +12,17 @@ import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Experimental
@ApiStatus.NonExtendable
public interface ListenerHandle<E extends Event> {
void call(@NotNull E event);
/**
* Gets if any listener has been registered for the given handle.
* May trigger an update if the cached data is not correct.
* <p>
* Useful if you are able to avoid expensive computation in the case where
* the event is unused. Be aware that {@link #call(Event)}
* has similar optimization built-in.
*
* @return true if the event has 1 or more listeners
*/
boolean hasListener();
}

View File

@ -280,7 +280,7 @@ public abstract class Chunk implements BlockGetter, BlockSetter, Viewable, Ticka
if (result) {
PlayerChunkLoadEvent playerChunkLoadEvent = new PlayerChunkLoadEvent(player, chunkX, chunkZ);
EventDispatcher.call(playerChunkLoadEvent, GlobalHandles.PLAYER_CHUNK_LOAD);
GlobalHandles.PLAYER_CHUNK_LOAD.call(playerChunkLoadEvent);
}
return result;

View File

@ -732,7 +732,7 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta
// Tick event
{
// Process tick events
EventDispatcher.call(new InstanceTickEvent(this, time, lastTickAge), GlobalHandles.INSTANCE_TICK);
GlobalHandles.INSTANCE_TICK.call(new InstanceTickEvent(this, time, lastTickAge));
// Set last tick age
this.lastTickAge = time;
}

View File

@ -275,7 +275,7 @@ public class InstanceContainer extends Instance {
.whenComplete((chunk, throwable) -> {
// TODO run in the instance thread?
cacheChunk(chunk);
EventDispatcher.call(new InstanceChunkLoadEvent(this, chunkX, chunkZ), GlobalHandles.INSTANCE_CHUNK_LOAD);
GlobalHandles.INSTANCE_CHUNK_LOAD.call(new InstanceChunkLoadEvent(this, chunkX, chunkZ));
synchronized (loadingChunks) {
this.loadingChunks.remove(ChunkUtils.getChunkIndex(chunk));
}

View File

@ -2,7 +2,6 @@ package net.minestom.server.listener;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player;
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.GlobalHandles;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.instance.Instance;
@ -54,7 +53,7 @@ public class PlayerPositionListener {
}
PlayerMoveEvent playerMoveEvent = new PlayerMoveEvent(player, newPosition);
EventDispatcher.call(playerMoveEvent, GlobalHandles.PLAYER_MOVE);
GlobalHandles.PLAYER_MOVE.call(playerMoveEvent);
// True if the event call changed the player position (possibly a teleport)
if (!playerMoveEvent.isCancelled() && currentPosition.equals(player.getPosition())) {
// Move the player

View File

@ -2,7 +2,6 @@ package net.minestom.server.listener.manager;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player;
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.GlobalHandles;
import net.minestom.server.event.player.PlayerPacketEvent;
import net.minestom.server.listener.*;
@ -90,7 +89,7 @@ public final class PacketListenerManager {
// Event
PlayerPacketEvent playerPacketEvent = new PlayerPacketEvent(player, packet);
EventDispatcher.call(playerPacketEvent, GlobalHandles.PLAYER_PACKET);
GlobalHandles.PLAYER_PACKET.call(playerPacketEvent);
if (playerPacketEvent.isCancelled()) {
return;
}