Overhaul event handling (designed by WetABQ#3417)

This commit is contained in:
KingRainbow44 2022-04-26 13:52:10 -04:00
parent 7e48bd1e65
commit 727fa8a67d
No known key found for this signature in database
GPG Key ID: FC2CB64B00D257BE
4 changed files with 115 additions and 36 deletions

View File

@ -3,9 +3,8 @@ package emu.grasscutter.plugin;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.server.event.Event;
import emu.grasscutter.server.event.EventHandler;
import emu.grasscutter.server.event.Listener;
import emu.grasscutter.server.event.HandlerPriority;
import emu.grasscutter.utils.Utils;
import org.reflections.Reflections;
import java.io.File;
import java.io.InputStreamReader;
@ -15,13 +14,14 @@ import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Stream;
/**
* Manages the server's plugins & the event system.
*/
public final class PluginManager {
private final Map<String, Plugin> plugins = new HashMap<>();
private final Map<Plugin, List<Listener>> listeners = new HashMap<>();
private final List<EventHandler<?>> listeners = new LinkedList<>();
public PluginManager() {
this.loadPlugins(); // Load all plugins from the plugins directory.
@ -68,7 +68,7 @@ public final class PluginManager {
JarEntry entry = entries.nextElement();
if(entry.isDirectory() || !entry.getName().endsWith(".class")) continue;
String className = entry.getName().replace(".class", "").replace("/", ".");
Class<?> clazz = loader.loadClass(className);
loader.loadClass(className);
}
Class<?> pluginClass = loader.loadClass(pluginConfig.mainClass);
@ -129,11 +129,10 @@ public final class PluginManager {
/**
* Registers a plugin's event listener.
* @param plugin The plugin instance.
* @param listener The event listener.
*/
public void registerListener(Plugin plugin, Listener listener) {
this.listeners.computeIfAbsent(plugin, k -> new ArrayList<>()).add(listener);
public void registerListener(EventHandler<?> listener) {
this.listeners.add(listener);
}
/**
@ -141,23 +140,24 @@ public final class PluginManager {
* @param event The event to invoke.
*/
public void invokeEvent(Event event) {
this.listeners.values().stream()
.flatMap(Collection::stream)
.forEach(listener -> this.invokeOnListener(listener, event));
Stream<EventHandler<?>> handlers = this.listeners.stream()
.filter(handler -> event.getClass().isInstance(event));
handlers.filter(handler -> handler.getPriority() == HandlerPriority.HIGH)
.toList().forEach(handler -> this.invokeHandler(event, handler));
handlers.filter(handler -> handler.getPriority() == HandlerPriority.NORMAL)
.toList().forEach(handler -> this.invokeHandler(event, handler));
handlers.filter(handler -> handler.getPriority() == HandlerPriority.LOW)
.toList().forEach(handler -> this.invokeHandler(event, handler));
}
/**
* Attempts to invoke the event on the provided listener.
* Performs logic checks then invokes the provided event handler.
* @param event The event passed through to the handler.
* @param handler The handler to invoke.
*/
private void invokeOnListener(Listener listener, Event event) {
try {
Class<?> listenerClass = listener.getClass();
Method[] methods = listenerClass.getMethods();
for (Method method : methods) {
if(!method.isAnnotationPresent(EventHandler.class)) return;
if(!method.getParameterTypes()[0].isAssignableFrom(event.getClass())) return;
method.invoke(listener, event);
}
} catch (Exception ignored) { }
private void invokeHandler(Event event, EventHandler<?> handler) {
if(!event.isCanceled() ||
(event.isCanceled() && handler.ignoresCanceled())
) handler.getCallback().accept(event);
}
}

View File

@ -1,11 +1,79 @@
package emu.grasscutter.server.event;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import emu.grasscutter.Grasscutter;
/**
* Declares a class as an event listener/handler.
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface EventHandler {
import java.util.function.Consumer;
public final class EventHandler<E extends Event> {
private E event;
private Consumer<Event> listener;
private HandlerPriority priority;
private boolean handleCanceled;
/**
* Gets which event this handler is handling.
* @return An event class.
*/
public Event handles() {
return this.event;
}
/**
* Returns the callback for the handler.
* @return A consumer callback.
*/
public Consumer<Event> getCallback() {
return this.listener;
}
/**
* Returns the handler's priority.
* @return The priority of the handler.
*/
public HandlerPriority getPriority() {
return this.priority;
}
/**
* Returns if the handler will ignore cancelled events.
* @return The ignore cancelled state.
*/
public boolean ignoresCanceled() {
return this.handleCanceled;
}
/**
* Sets the callback method for when the event is invoked.
* @param listener An event handler method.
* @return Method chaining.
*/
public EventHandler<E> listener(Consumer<Event> listener) {
this.listener = listener; return this;
}
/**
* Changes the handler's priority in handling events.
* @param priority The priority of the handler.
* @return Method chaining.
*/
public EventHandler<E> priority(HandlerPriority priority) {
this.priority = priority; return this;
}
/**
* Sets if the handler will ignore cancelled events.
* @param ignore If the handler should ignore cancelled events.
* @return Method chaining.
*/
public EventHandler<E> ignore(boolean ignore) {
this.handleCanceled = ignore; return this;
}
/**
* Registers the handler into the PluginManager.
*/
public void register() {
Grasscutter.getPluginManager().registerListener(this);
}
}

View File

@ -0,0 +1,18 @@
package emu.grasscutter.server.event;
public enum HandlerPriority {
/**
* The handler will be called before every other handler.
*/
HIGH,
/**
* The handler will be called the same time as other handlers.
*/
NORMAL,
/**
* The handler will be called after every other handler.
*/
LOW
}

View File

@ -1,7 +0,0 @@
package emu.grasscutter.server.event;
/**
* Implementing this interface declares a class as an event listener.
*/
public interface Listener {
}