diff --git a/src/main/java/emu/grasscutter/plugin/Plugin.java b/src/main/java/emu/grasscutter/plugin/Plugin.java index 447f1477d..3e2f7255b 100644 --- a/src/main/java/emu/grasscutter/plugin/Plugin.java +++ b/src/main/java/emu/grasscutter/plugin/Plugin.java @@ -6,6 +6,7 @@ import emu.grasscutter.plugin.api.ServerHelper; import emu.grasscutter.plugin.api.ServerHook; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.utils.FileUtils; +import lombok.EqualsAndHashCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,6 +15,7 @@ import java.io.InputStream; import java.net.URLClassLoader; /** The base class for all plugins to extend. */ +@EqualsAndHashCode @SuppressWarnings("removal") public abstract class Plugin { private final ServerHelper server = ServerHook.getInstance(); diff --git a/src/main/java/emu/grasscutter/plugin/PluginManager.java b/src/main/java/emu/grasscutter/plugin/PluginManager.java index 463bab337..60684d15d 100644 --- a/src/main/java/emu/grasscutter/plugin/PluginManager.java +++ b/src/main/java/emu/grasscutter/plugin/PluginManager.java @@ -1,13 +1,14 @@ package emu.grasscutter.plugin; -import static emu.grasscutter.utils.lang.Language.translate; - import emu.grasscutter.Grasscutter; import emu.grasscutter.server.event.Event; import emu.grasscutter.server.event.EventHandler; -import emu.grasscutter.server.event.HandlerPriority; import emu.grasscutter.utils.FileUtils; import emu.grasscutter.utils.JsonUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import javax.annotation.Nullable; import java.io.File; import java.io.FileNotFoundException; import java.io.InputStreamReader; @@ -18,16 +19,15 @@ import java.net.URLClassLoader; import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import javax.annotation.Nullable; -import lombok.AllArgsConstructor; -import lombok.Getter; + +import static emu.grasscutter.utils.lang.Language.translate; /** Manages the server's plugins and the event system. */ public final class PluginManager { /* All loaded plugins. */ private final Map plugins = new LinkedHashMap<>(); /* All currently registered listeners per plugin. */ - private final Map>> listeners = new LinkedHashMap<>(); + private final Map, List>> handlers = new LinkedHashMap<>(); public PluginManager() { this.loadPlugins(); // Load all plugins from the plugins directory. @@ -184,8 +184,6 @@ public final class PluginManager { // Add the plugin to the list of loaded plugins. this.plugins.put(identifier.name, plugin); - // Create a collection for the plugin's listeners. - this.listeners.put(plugin, new ArrayList<>()); // Call the plugin's onLoad method. try { @@ -221,11 +219,74 @@ public final class PluginManager { /** * Registers a plugin's event listener. * - * @param plugin The plugin registering the listener. * @param listener The event listener. */ - public void registerListener(Plugin plugin, EventHandler listener) { - this.listeners.get(plugin).add(listener); + public void registerListener(EventHandler listener) { + // Check if the handlers map contains the event type. + if (!this.handlers.containsKey(listener.handles())) + this.handlers.put(listener.handles(), new LinkedList<>()); + + // Add the listener to the list of handlers. + this.handlers.get(listener.handles()).add(listener); + + this.sortListeners(); // Sort the listeners by priority. + } + + /** + * Removes all event listeners registered by the specified plugin. + * + * @param plugin The plugin. + */ + public void removeListeners(Plugin plugin) { + var newMap = new HashMap< + Class, + List> + >(); + + // Remove the plugin's listeners. + this.handlers.forEach((event, handlers) -> { + // Add the event to the new map. + newMap.put(event, new LinkedList<>()); + + // Remove the plugin's listeners. + handlers.forEach(handler -> { + if (!handler.registrar().equals(plugin)) + newMap.get(event).add(handler); + }); + }); + + // Replace the old map with the new one. + this.handlers.clear(); + this.handlers.putAll(newMap); + } + + /** + * Sorts the event listeners by priority. + * This method should be called after a listener has been registered. + */ + private void sortListeners() { + // Create a new map to store the sorted listeners. + var newMap = new HashMap< + Class, + List> + >(); + + // Sort the listeners by priority. + this.handlers.forEach((event, handlers) -> { + // Add the event to the new map. + newMap.put(event, new LinkedList<>()); + + // Sort the handlers by priority. + var sorted = handlers.stream() + .sorted(Comparator.comparingInt(handler -> + handler.getPriority().ordinal())) + .toList(); + newMap.get(event).addAll(sorted); + }); + + // Replace the old map with the new one. + this.handlers.clear(); + this.handlers.putAll(newMap); } /** @@ -234,25 +295,8 @@ public final class PluginManager { * @param event The event to invoke. */ public void invokeEvent(Event event) { - EnumSet.allOf(HandlerPriority.class).forEach(priority -> this.checkAndFilter(event, priority)); - } - - /** - * Check an event to handlers for the priority. - * - * @param event The event being called. - * @param priority The priority to call for. - */ - private void checkAndFilter(Event event, HandlerPriority priority) { - // Add all listeners from every plugin. - var listeners = new ArrayList<>(this.listeners.values()); - listeners.stream() - .flatMap(Collection::stream) - // Filter the listeners by priority. - .filter(handler -> handler.handles().isInstance(event)) - .filter(handler -> handler.getPriority() == priority) - // Invoke the event. - .forEach(handler -> this.invokeHandler(event, handler)); + this.handlers.get(event.getClass()) + .forEach(handler -> this.invokeHandler(event, handler)); } /** @@ -295,7 +339,7 @@ public final class PluginManager { } // Un-register all listeners. - this.listeners.remove(plugin); + this.removeListeners(plugin); } /** diff --git a/src/main/java/emu/grasscutter/server/event/EventHandler.java b/src/main/java/emu/grasscutter/server/event/EventHandler.java index a28d2e1a3..c43cfc1a3 100644 --- a/src/main/java/emu/grasscutter/server/event/EventHandler.java +++ b/src/main/java/emu/grasscutter/server/event/EventHandler.java @@ -59,6 +59,7 @@ public final class EventHandler { private EventConsumer listener; private HandlerPriority priority; private boolean handleCanceled; + private Plugin plugin; public EventHandler(Class eventClass) { this.eventClass = eventClass; @@ -100,6 +101,15 @@ public final class EventHandler { return this.handleCanceled; } + /** + * Returns the plugin that registered this handler. + * + * @return The plugin that registered this handler. + */ + public Plugin registrar() { + return this.plugin; + } + /** * Sets the callback method for when the event is invoked. * @@ -135,6 +145,7 @@ public final class EventHandler { /** Registers the handler into the PluginManager. */ public void register(Plugin plugin) { - Grasscutter.getPluginManager().registerListener(plugin, this); + this.plugin = plugin; + Grasscutter.getPluginManager().registerListener(this); } }