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); update(time);
ticks++; ticks++;
EventDispatcher.call(new EntityTickEvent(this), GlobalHandles.ENTITY_TICK); GlobalHandles.ENTITY_TICK.call(new EntityTickEvent(this));
// remove expired effects // remove expired effects
effectTick(time); effectTick(time);

View File

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

View File

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

View File

@ -188,23 +188,9 @@ public interface EventNode<T extends Event> {
*/ */
default void call(@NotNull T event) { default void call(@NotNull T event) {
//noinspection unchecked //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. * Gets the handle of an event type.
* *
@ -215,20 +201,6 @@ public interface EventNode<T extends Event> {
@ApiStatus.Experimental @ApiStatus.Experimental
<E extends T> @NotNull ListenerHandle<E> getHandle(@NotNull Class<E> handleType); <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. * 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)}. * 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(); 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 @Override
public <E extends T> @NotNull ListenerHandle<E> getHandle(@NotNull Class<E> handleType) { public <E extends T> @NotNull ListenerHandle<E> getHandle(@NotNull Class<E> handleType) {
//noinspection unchecked //noinspection unchecked
@ -58,13 +46,6 @@ class EventNodeImpl<T extends Event> implements EventNode<T> {
aClass -> new Handle<>(this, (Class<T>) aClass)); 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 @Override
public <E extends T> @NotNull List<EventNode<E>> findChildren(@NotNull String name, Class<E> eventType) { public <E extends T> @NotNull List<EventNode<E>> findChildren(@NotNull String name, Class<E> eventType) {
synchronized (GLOBAL_CHILD_LOCK) { synchronized (GLOBAL_CHILD_LOCK) {
@ -286,6 +267,22 @@ class EventNodeImpl<T extends Event> implements EventNode<T> {
this.eventType = eventType; 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() { void update() {
synchronized (GLOBAL_CHILD_LOCK) { synchronized (GLOBAL_CHILD_LOCK) {
this.listenersCache.clear(); this.listenersCache.clear();
@ -325,7 +322,7 @@ class EventNodeImpl<T extends Event> implements EventNode<T> {
for (var mappedEntry : mappedNodeCache.entrySet()) { for (var mappedEntry : mappedNodeCache.entrySet()) {
final EventNodeImpl<E> mappedNode = mappedEntry.getValue(); final EventNodeImpl<E> mappedNode = mappedEntry.getValue();
final Handle<E> handle = (Handle<E>) mappedNode.getHandle(eventType); 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); filters.add(mappedNode.filter);
handlers.put(mappedEntry.getKey(), handle); handlers.put(mappedEntry.getKey(), handle);
} }

View File

@ -1,6 +1,7 @@
package net.minestom.server.event; package net.minestom.server.event;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/** /**
* Represents a key to access an {@link EventNode} listeners. * Represents a key to access an {@link EventNode} listeners.
@ -11,4 +12,17 @@ import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Experimental @ApiStatus.Experimental
@ApiStatus.NonExtendable @ApiStatus.NonExtendable
public interface ListenerHandle<E extends Event> { 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) { if (result) {
PlayerChunkLoadEvent playerChunkLoadEvent = new PlayerChunkLoadEvent(player, chunkX, chunkZ); PlayerChunkLoadEvent playerChunkLoadEvent = new PlayerChunkLoadEvent(player, chunkX, chunkZ);
EventDispatcher.call(playerChunkLoadEvent, GlobalHandles.PLAYER_CHUNK_LOAD); GlobalHandles.PLAYER_CHUNK_LOAD.call(playerChunkLoadEvent);
} }
return result; return result;

View File

@ -732,7 +732,7 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta
// Tick event // Tick event
{ {
// Process tick events // 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 // Set last tick age
this.lastTickAge = time; this.lastTickAge = time;
} }

View File

@ -275,7 +275,7 @@ public class InstanceContainer extends Instance {
.whenComplete((chunk, throwable) -> { .whenComplete((chunk, throwable) -> {
// TODO run in the instance thread? // TODO run in the instance thread?
cacheChunk(chunk); 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) { synchronized (loadingChunks) {
this.loadingChunks.remove(ChunkUtils.getChunkIndex(chunk)); 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.coordinate.Pos;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.GlobalHandles; import net.minestom.server.event.GlobalHandles;
import net.minestom.server.event.player.PlayerMoveEvent; import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.instance.Instance; import net.minestom.server.instance.Instance;
@ -54,7 +53,7 @@ public class PlayerPositionListener {
} }
PlayerMoveEvent playerMoveEvent = new PlayerMoveEvent(player, newPosition); 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) // True if the event call changed the player position (possibly a teleport)
if (!playerMoveEvent.isCancelled() && currentPosition.equals(player.getPosition())) { if (!playerMoveEvent.isCancelled() && currentPosition.equals(player.getPosition())) {
// Move the player // Move the player

View File

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