mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-27 07:34:27 +00:00
Change plugin manager logic
Merge pull request #1346 from Grasscutters/plugin-priority
This commit is contained in:
commit
362dd67470
@ -43,7 +43,7 @@ sourceCompatibility = JavaVersion.VERSION_17
|
|||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
|
||||||
group = 'xyz.grasscutters'
|
group = 'xyz.grasscutters'
|
||||||
version = '1.2.1-dev'
|
version = '1.2.2-dev'
|
||||||
|
|
||||||
|
|
||||||
sourceCompatibility = 17
|
sourceCompatibility = 17
|
||||||
|
@ -9,7 +9,11 @@
|
|||||||
"pattern": "^[A-Za-z\\d_.-]+$"
|
"pattern": "^[A-Za-z\\d_.-]+$"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [ "name", "description", "mainClass" ],
|
"required": [
|
||||||
|
"name",
|
||||||
|
"description",
|
||||||
|
"mainClass"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
"description": "The unique name of plugin.",
|
"description": "The unique name of plugin.",
|
||||||
@ -22,7 +26,10 @@
|
|||||||
},
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"description": "A plugin revision identifier.",
|
"description": "A plugin revision identifier.",
|
||||||
"type": [ "string", "number" ]
|
"type": [
|
||||||
|
"string",
|
||||||
|
"number"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"description": "Human readable plugin summary.",
|
"description": "Human readable plugin summary.",
|
||||||
@ -44,6 +51,13 @@
|
|||||||
"description": "The URL to the plugin's site",
|
"description": "The URL to the plugin's site",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uri"
|
"format": "uri"
|
||||||
|
},
|
||||||
|
"loadAfter": {
|
||||||
|
"description": "Plugins to load before this plugin.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,25 +1,34 @@
|
|||||||
package emu.grasscutter;
|
package emu.grasscutter;
|
||||||
|
|
||||||
import java.io.*;
|
import ch.qos.logback.classic.Logger;
|
||||||
import java.util.Calendar;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import emu.grasscutter.auth.AuthenticationSystem;
|
import emu.grasscutter.auth.AuthenticationSystem;
|
||||||
import emu.grasscutter.auth.DefaultAuthentication;
|
import emu.grasscutter.auth.DefaultAuthentication;
|
||||||
import emu.grasscutter.command.CommandMap;
|
import emu.grasscutter.command.CommandMap;
|
||||||
import emu.grasscutter.command.DefaultPermissionHandler;
|
import emu.grasscutter.command.DefaultPermissionHandler;
|
||||||
import emu.grasscutter.command.PermissionHandler;
|
import emu.grasscutter.command.PermissionHandler;
|
||||||
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
||||||
|
import emu.grasscutter.data.ResourceLoader;
|
||||||
|
import emu.grasscutter.database.DatabaseManager;
|
||||||
import emu.grasscutter.game.managers.energy.EnergyManager;
|
import emu.grasscutter.game.managers.energy.EnergyManager;
|
||||||
import emu.grasscutter.game.managers.stamina.StaminaManager;
|
import emu.grasscutter.game.managers.stamina.StaminaManager;
|
||||||
import emu.grasscutter.plugin.PluginManager;
|
import emu.grasscutter.plugin.PluginManager;
|
||||||
import emu.grasscutter.plugin.api.ServerHook;
|
import emu.grasscutter.plugin.api.ServerHook;
|
||||||
import emu.grasscutter.scripts.ScriptLoader;
|
import emu.grasscutter.scripts.ScriptLoader;
|
||||||
|
import emu.grasscutter.server.game.GameServer;
|
||||||
import emu.grasscutter.server.http.HttpServer;
|
import emu.grasscutter.server.http.HttpServer;
|
||||||
import emu.grasscutter.server.http.dispatch.DispatchHandler;
|
import emu.grasscutter.server.http.dispatch.DispatchHandler;
|
||||||
import emu.grasscutter.server.http.handlers.*;
|
|
||||||
import emu.grasscutter.server.http.dispatch.RegionHandler;
|
import emu.grasscutter.server.http.dispatch.RegionHandler;
|
||||||
import emu.grasscutter.server.http.documentation.DocumentationServerHandler;
|
import emu.grasscutter.server.http.documentation.DocumentationServerHandler;
|
||||||
|
import emu.grasscutter.server.http.handlers.AnnouncementsHandler;
|
||||||
|
import emu.grasscutter.server.http.handlers.GachaHandler;
|
||||||
|
import emu.grasscutter.server.http.handlers.GenericHandler;
|
||||||
|
import emu.grasscutter.server.http.handlers.LogHandler;
|
||||||
|
import emu.grasscutter.tools.Tools;
|
||||||
import emu.grasscutter.utils.ConfigContainer;
|
import emu.grasscutter.utils.ConfigContainer;
|
||||||
|
import emu.grasscutter.utils.Crypto;
|
||||||
|
import emu.grasscutter.utils.Language;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
import org.jline.reader.EndOfFileException;
|
import org.jline.reader.EndOfFileException;
|
||||||
import org.jline.reader.LineReader;
|
import org.jline.reader.LineReader;
|
||||||
@ -30,21 +39,13 @@ import org.jline.terminal.TerminalBuilder;
|
|||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
|
|
||||||
import ch.qos.logback.classic.Logger;
|
|
||||||
import emu.grasscutter.data.ResourceLoader;
|
|
||||||
import emu.grasscutter.database.DatabaseManager;
|
|
||||||
import emu.grasscutter.utils.Language;
|
|
||||||
import emu.grasscutter.server.game.GameServer;
|
|
||||||
import emu.grasscutter.tools.Tools;
|
|
||||||
import emu.grasscutter.utils.Crypto;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
import static emu.grasscutter.Configuration.DATA;
|
||||||
|
import static emu.grasscutter.Configuration.SERVER;
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
import static emu.grasscutter.Configuration.*;
|
|
||||||
|
|
||||||
public final class Grasscutter {
|
public final class Grasscutter {
|
||||||
private static final Logger log = (Logger) LoggerFactory.getLogger(Grasscutter.class);
|
private static final Logger log = (Logger) LoggerFactory.getLogger(Grasscutter.class);
|
||||||
@ -90,13 +91,16 @@ public final class Grasscutter {
|
|||||||
for (String arg : args) {
|
for (String arg : args) {
|
||||||
switch (arg.toLowerCase()) {
|
switch (arg.toLowerCase()) {
|
||||||
case "-handbook" -> {
|
case "-handbook" -> {
|
||||||
Tools.createGmHandbook(); exitEarly = true;
|
Tools.createGmHandbook();
|
||||||
|
exitEarly = true;
|
||||||
}
|
}
|
||||||
case "-gachamap" -> {
|
case "-gachamap" -> {
|
||||||
Tools.createGachaMapping(DATA("gacha_mappings.js")); exitEarly = true;
|
Tools.createGachaMapping(DATA("gacha_mappings.js"));
|
||||||
|
exitEarly = true;
|
||||||
}
|
}
|
||||||
case "-version" -> {
|
case "-version" -> {
|
||||||
System.out.println("Grasscutter version: " + BuildConfig.VERSION + "-" + BuildConfig.GIT_HASH); exitEarly = true;
|
System.out.println("Grasscutter version: " + BuildConfig.VERSION + "-" + BuildConfig.GIT_HASH);
|
||||||
|
exitEarly = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,8 +117,6 @@ public final class Grasscutter {
|
|||||||
Grasscutter.updateDayOfWeek();
|
Grasscutter.updateDayOfWeek();
|
||||||
ResourceLoader.loadAll();
|
ResourceLoader.loadAll();
|
||||||
ScriptLoader.init();
|
ScriptLoader.init();
|
||||||
EnergyManager.initialize();
|
|
||||||
DungeonChallenge.initialize();
|
|
||||||
|
|
||||||
// Initialize database.
|
// Initialize database.
|
||||||
DatabaseManager.initialize();
|
DatabaseManager.initialize();
|
||||||
@ -142,9 +144,6 @@ public final class Grasscutter {
|
|||||||
httpServer.addRouter(GachaHandler.class);
|
httpServer.addRouter(GachaHandler.class);
|
||||||
httpServer.addRouter(DocumentationServerHandler.class);
|
httpServer.addRouter(DocumentationServerHandler.class);
|
||||||
|
|
||||||
// TODO: find a better place?
|
|
||||||
StaminaManager.initialize();
|
|
||||||
|
|
||||||
// Start servers.
|
// Start servers.
|
||||||
var runMode = SERVER.runMode;
|
var runMode = SERVER.runMode;
|
||||||
if (runMode == ServerRunMode.HYBRID) {
|
if (runMode == ServerRunMode.HYBRID) {
|
||||||
@ -176,6 +175,7 @@ public final class Grasscutter {
|
|||||||
*/
|
*/
|
||||||
private static void onShutdown() {
|
private static void onShutdown() {
|
||||||
// Disable all plugins.
|
// Disable all plugins.
|
||||||
|
if(pluginManager != null)
|
||||||
pluginManager.disablePlugins();
|
pluginManager.disablePlugins();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,6 +215,7 @@ public final class Grasscutter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the provided server configuration.
|
* Saves the provided server configuration.
|
||||||
|
*
|
||||||
* @param config The configuration to save, or null for a new one.
|
* @param config The configuration to save, or null for a new one.
|
||||||
*/
|
*/
|
||||||
public static void saveConfig(@Nullable ConfigContainer config) {
|
public static void saveConfig(@Nullable ConfigContainer config) {
|
||||||
@ -350,6 +351,7 @@ public final class Grasscutter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the authentication system for the server.
|
* Sets the authentication system for the server.
|
||||||
|
*
|
||||||
* @param authenticationSystem The authentication system to use.
|
* @param authenticationSystem The authentication system to use.
|
||||||
*/
|
*/
|
||||||
public static void setAuthenticationSystem(AuthenticationSystem authenticationSystem) {
|
public static void setAuthenticationSystem(AuthenticationSystem authenticationSystem) {
|
||||||
@ -358,6 +360,7 @@ public final class Grasscutter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the permission handler for the server.
|
* Sets the permission handler for the server.
|
||||||
|
*
|
||||||
* @param permissionHandler The permission handler to use.
|
* @param permissionHandler The permission handler to use.
|
||||||
*/
|
*/
|
||||||
public static void setPermissionHandler(PermissionHandler permissionHandler) {
|
public static void setPermissionHandler(PermissionHandler permissionHandler) {
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package emu.grasscutter.command;
|
package emu.grasscutter.command;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.game.Account;
|
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
|
|
||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -11,6 +9,7 @@ import java.util.*;
|
|||||||
@SuppressWarnings({"UnusedReturnValue", "unused"})
|
@SuppressWarnings({"UnusedReturnValue", "unused"})
|
||||||
public final class CommandMap {
|
public final class CommandMap {
|
||||||
private final Map<String, CommandHandler> commands = new HashMap<>();
|
private final Map<String, CommandHandler> commands = new HashMap<>();
|
||||||
|
private final Map<String, CommandHandler> aliases = new HashMap<>();
|
||||||
private final Map<String, Command> annotations = new HashMap<>();
|
private final Map<String, Command> annotations = new HashMap<>();
|
||||||
private final Map<String, Integer> targetPlayerIds = new HashMap<>();
|
private final Map<String, Integer> targetPlayerIds = new HashMap<>();
|
||||||
private static final String consoleId = "console";
|
private static final String consoleId = "console";
|
||||||
@ -45,7 +44,7 @@ public final class CommandMap {
|
|||||||
// Register aliases.
|
// Register aliases.
|
||||||
if (annotation.aliases().length > 0) {
|
if (annotation.aliases().length > 0) {
|
||||||
for (String alias : annotation.aliases()) {
|
for (String alias : annotation.aliases()) {
|
||||||
this.commands.put(alias, command);
|
this.aliases.put(alias, command);
|
||||||
this.annotations.put(alias, annotation);
|
this.annotations.put(alias, annotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,6 +59,7 @@ public final class CommandMap {
|
|||||||
*/
|
*/
|
||||||
public CommandMap unregisterCommand(String label) {
|
public CommandMap unregisterCommand(String label) {
|
||||||
Grasscutter.getLogger().debug("Unregistered command: " + label);
|
Grasscutter.getLogger().debug("Unregistered command: " + label);
|
||||||
|
|
||||||
CommandHandler handler = this.commands.get(label);
|
CommandHandler handler = this.commands.get(label);
|
||||||
if (handler == null) return this;
|
if (handler == null) return this;
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ public final class CommandMap {
|
|||||||
// Unregister aliases.
|
// Unregister aliases.
|
||||||
if (annotation.aliases().length > 0) {
|
if (annotation.aliases().length > 0) {
|
||||||
for (String alias : annotation.aliases()) {
|
for (String alias : annotation.aliases()) {
|
||||||
this.commands.remove(alias);
|
this.aliases.remove(alias);
|
||||||
this.annotations.remove(alias);
|
this.annotations.remove(alias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,7 +78,9 @@ public final class CommandMap {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Command> getAnnotationsAsList() { return new LinkedList<>(this.annotations.values()); }
|
public List<Command> getAnnotationsAsList() {
|
||||||
|
return new LinkedList<>(this.annotations.values());
|
||||||
|
}
|
||||||
|
|
||||||
public HashMap<String, Command> getAnnotations() {
|
public HashMap<String, Command> getAnnotations() {
|
||||||
return new LinkedHashMap<>(this.annotations);
|
return new LinkedHashMap<>(this.annotations);
|
||||||
@ -142,7 +144,7 @@ public final class CommandMap {
|
|||||||
}
|
}
|
||||||
if (targetUidStr != null) {
|
if (targetUidStr != null) {
|
||||||
if (targetUidStr.equals("")) { // Clears the default targetPlayer.
|
if (targetUidStr.equals("")) { // Clears the default targetPlayer.
|
||||||
targetPlayerIds.remove(playerId);
|
this.targetPlayerIds.remove(playerId);
|
||||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.clear_target");
|
CommandHandler.sendTranslatedMessage(player, "commands.execution.clear_target");
|
||||||
} else { // Sets default targetPlayer to the UID provided.
|
} else { // Sets default targetPlayer to the UID provided.
|
||||||
try {
|
try {
|
||||||
@ -151,7 +153,7 @@ public final class CommandMap {
|
|||||||
if (targetPlayer == null) {
|
if (targetPlayer == null) {
|
||||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error");
|
CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error");
|
||||||
} else {
|
} else {
|
||||||
targetPlayerIds.put(playerId, uid);
|
this.targetPlayerIds.put(playerId, uid);
|
||||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.set_target", targetUidStr);
|
CommandHandler.sendTranslatedMessage(player, "commands.execution.set_target", targetUidStr);
|
||||||
CommandHandler.sendTranslatedMessage(player, targetPlayer.isOnline() ? "commands.execution.set_target_online" : "commands.execution.set_target_offline", targetUidStr);
|
CommandHandler.sendTranslatedMessage(player, targetPlayer.isOnline() ? "commands.execution.set_target_online" : "commands.execution.set_target_offline", targetUidStr);
|
||||||
}
|
}
|
||||||
@ -164,11 +166,19 @@ public final class CommandMap {
|
|||||||
|
|
||||||
// Get command handler.
|
// Get command handler.
|
||||||
CommandHandler handler = this.commands.get(label);
|
CommandHandler handler = this.commands.get(label);
|
||||||
|
if(handler == null)
|
||||||
|
// Try to get the handler by alias.
|
||||||
|
handler = this.aliases.get(label);
|
||||||
|
|
||||||
|
// Check if the handler is still null.
|
||||||
if (handler == null) {
|
if (handler == null) {
|
||||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.unknown_command", label);
|
CommandHandler.sendTranslatedMessage(player, "commands.generic.unknown_command", label);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the command's annotation.
|
||||||
|
Command annotation = this.annotations.get(label);
|
||||||
|
|
||||||
// If any @UID argument is present, override targetPlayer with it.
|
// If any @UID argument is present, override targetPlayer with it.
|
||||||
for (int i = 0; i < args.size(); i++) {
|
for (int i = 0; i < args.size(); i++) {
|
||||||
String arg = args.get(i);
|
String arg = args.get(i);
|
||||||
@ -191,8 +201,8 @@ public final class CommandMap {
|
|||||||
|
|
||||||
// If there's still no targetPlayer at this point, use previously-set target
|
// If there's still no targetPlayer at this point, use previously-set target
|
||||||
if (targetPlayer == null) {
|
if (targetPlayer == null) {
|
||||||
if (targetPlayerIds.containsKey(playerId)) {
|
if (this.targetPlayerIds.containsKey(playerId)) {
|
||||||
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(targetPlayerIds.get(playerId), true); // We check every time in case the target is deleted after being targeted
|
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(this.targetPlayerIds.get(playerId), true); // We check every time in case the target is deleted after being targeted
|
||||||
if (targetPlayer == null) {
|
if (targetPlayer == null) {
|
||||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error");
|
CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error");
|
||||||
return;
|
return;
|
||||||
@ -204,32 +214,36 @@ public final class CommandMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for permissions.
|
// Check for permissions.
|
||||||
if (!Grasscutter.getPermissionHandler().checkPermission(player, targetPlayer, this.annotations.get(label).permission(), this.annotations.get(label).permissionTargeted())) {
|
if (!Grasscutter.getPermissionHandler().checkPermission(player, targetPlayer, annotation.permission(), this.annotations.get(label).permissionTargeted())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if command has unfulfilled constraints on targetPlayer
|
// Check if command has unfulfilled constraints on targetPlayer
|
||||||
Command.TargetRequirement targetRequirement = this.annotations.get(label).targetRequirement();
|
Command.TargetRequirement targetRequirement = annotation.targetRequirement();
|
||||||
if (targetRequirement != Command.TargetRequirement.NONE) {
|
if (targetRequirement != Command.TargetRequirement.NONE) {
|
||||||
if (targetPlayer == null) {
|
if (targetPlayer == null) {
|
||||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target");
|
CommandHandler.sendTranslatedMessage(null, "commands.execution.need_target");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((targetRequirement == Command.TargetRequirement.ONLINE) && !targetPlayer.isOnline()) {
|
if ((targetRequirement == Command.TargetRequirement.ONLINE) && !targetPlayer.isOnline()) {
|
||||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_online");
|
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_online");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((targetRequirement == Command.TargetRequirement.OFFLINE) && targetPlayer.isOnline()) {
|
if ((targetRequirement == Command.TargetRequirement.OFFLINE) && targetPlayer.isOnline()) {
|
||||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_offline");
|
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_offline");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke execute method for handler.
|
// Copy player and handler to final properties.
|
||||||
boolean threading = this.annotations.get(label).threading();
|
|
||||||
final Player targetPlayerF = targetPlayer; // Is there a better way to do this?
|
final Player targetPlayerF = targetPlayer; // Is there a better way to do this?
|
||||||
Runnable runnable = () -> handler.execute(player, targetPlayerF, args);
|
final CommandHandler handlerF = handler; // Is there a better way to do this?
|
||||||
if(threading) {
|
|
||||||
|
// Invoke execute method for handler.
|
||||||
|
Runnable runnable = () -> handlerF.execute(player, targetPlayerF, args);
|
||||||
|
if (annotation.threading()) {
|
||||||
new Thread(runnable).start();
|
new Thread(runnable).start();
|
||||||
} else {
|
} else {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
@ -242,10 +256,11 @@ public final class CommandMap {
|
|||||||
private void scan() {
|
private void scan() {
|
||||||
Reflections reflector = Grasscutter.reflector;
|
Reflections reflector = Grasscutter.reflector;
|
||||||
Set<Class<?>> classes = reflector.getTypesAnnotatedWith(Command.class);
|
Set<Class<?>> classes = reflector.getTypesAnnotatedWith(Command.class);
|
||||||
|
|
||||||
classes.forEach(annotated -> {
|
classes.forEach(annotated -> {
|
||||||
try {
|
try {
|
||||||
Command cmdData = annotated.getAnnotation(Command.class);
|
Command cmdData = annotated.getAnnotation(Command.class);
|
||||||
Object object = annotated.newInstance();
|
Object object = annotated.getDeclaredConstructor().newInstance();
|
||||||
if (object instanceof CommandHandler)
|
if (object instanceof CommandHandler)
|
||||||
this.registerCommand(cmdData.label(), (CommandHandler) object);
|
this.registerCommand(cmdData.label(), (CommandHandler) object);
|
||||||
else Grasscutter.getLogger().error("Class " + annotated.getName() + " is not a CommandHandler!");
|
else Grasscutter.getLogger().error("Class " + annotated.getName() + " is not a CommandHandler!");
|
||||||
|
@ -6,11 +6,11 @@ import emu.grasscutter.tools.Tools;
|
|||||||
import emu.grasscutter.utils.FileUtils;
|
import emu.grasscutter.utils.FileUtils;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.FileInputStream;
|
||||||
import java.nio.file.FileSystems;
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import static emu.grasscutter.Configuration.DATA;
|
import static emu.grasscutter.Configuration.DATA;
|
||||||
|
|
||||||
@ -18,10 +18,11 @@ public class DataLoader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a data file by its name. If the file isn't found within the /data directory then it will fallback to the default within the jar resources
|
* Load a data file by its name. If the file isn't found within the /data directory then it will fallback to the default within the jar resources
|
||||||
* @see #load(String, boolean)
|
*
|
||||||
* @param resourcePath The path to the data file to be loaded.
|
* @param resourcePath The path to the data file to be loaded.
|
||||||
* @return InputStream of the data file.
|
* @return InputStream of the data file.
|
||||||
* @throws FileNotFoundException
|
* @throws FileNotFoundException
|
||||||
|
* @see #load(String, boolean)
|
||||||
*/
|
*/
|
||||||
public static InputStream load(String resourcePath) throws FileNotFoundException {
|
public static InputStream load(String resourcePath) throws FileNotFoundException {
|
||||||
return load(resourcePath, true);
|
return load(resourcePath, true);
|
||||||
@ -29,6 +30,7 @@ public class DataLoader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a data file by its name.
|
* Load a data file by its name.
|
||||||
|
*
|
||||||
* @param resourcePath The path to the data file to be loaded.
|
* @param resourcePath The path to the data file to be loaded.
|
||||||
* @param useFallback If the file does not exist in the /data directory, should it use the default file in the jar?
|
* @param useFallback If the file does not exist in the /data directory, should it use the default file in the jar?
|
||||||
* @return InputStream of the data file.
|
* @return InputStream of the data file.
|
||||||
@ -53,9 +55,7 @@ public class DataLoader {
|
|||||||
|
|
||||||
if (filenames == null) {
|
if (filenames == null) {
|
||||||
Grasscutter.getLogger().error("We were unable to locate your default data files.");
|
Grasscutter.getLogger().error("We were unable to locate your default data files.");
|
||||||
}
|
} else for (Path file : filenames) {
|
||||||
|
|
||||||
for (Path file : filenames) {
|
|
||||||
String relativePath = String.valueOf(file).split("defaults[\\\\\\/]data[\\\\\\/]")[1];
|
String relativePath = String.valueOf(file).split("defaults[\\\\\\/]data[\\\\\\/]")[1];
|
||||||
|
|
||||||
CheckAndCopyData(relativePath);
|
CheckAndCopyData(relativePath);
|
||||||
|
@ -990,7 +990,7 @@ public class Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void interactWith(int gadgetEntityId, GadgetInteractReq req) {
|
public void interactWith(int gadgetEntityId, GadgetInteractReq opType) {
|
||||||
GameEntity entity = getScene().getEntityById(gadgetEntityId);
|
GameEntity entity = getScene().getEntityById(gadgetEntityId);
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
return;
|
return;
|
||||||
@ -1023,7 +1023,7 @@ public class Player {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean shouldDelete = gadget.getContent().onInteract(this, req);
|
boolean shouldDelete = gadget.getContent().onInteract(this, opType);
|
||||||
|
|
||||||
if (shouldDelete) {
|
if (shouldDelete) {
|
||||||
entity.getScene().removeEntity(entity);
|
entity.getScene().removeEntity(entity);
|
||||||
|
@ -7,11 +7,13 @@ public final class PluginConfig {
|
|||||||
public String name, description, version;
|
public String name, description, version;
|
||||||
public String mainClass;
|
public String mainClass;
|
||||||
public String[] authors;
|
public String[] authors;
|
||||||
|
public String[] loadAfter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to validate this config instance.
|
* Attempts to validate this config instance.
|
||||||
* @return True if the config is valid, false otherwise.
|
* @return True if the config is valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
public boolean validate() {
|
public boolean validate() {
|
||||||
return name != null && description != null && mainClass != null;
|
return name != null && description != null && mainClass != null;
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,41 @@
|
|||||||
package emu.grasscutter.plugin;
|
package emu.grasscutter.plugin;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.server.event.Event;
|
import emu.grasscutter.server.event.*;
|
||||||
import emu.grasscutter.server.event.EventHandler;
|
|
||||||
import emu.grasscutter.server.event.HandlerPriority;
|
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
import java.io.File;
|
import javax.annotation.Nullable;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.*;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.MalformedURLException;
|
import java.net.*;
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.*;
|
||||||
import java.util.jar.JarFile;
|
|
||||||
|
|
||||||
import static emu.grasscutter.Configuration.*;
|
import static emu.grasscutter.Configuration.PLUGIN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the server's plugins and the event system.
|
* Manages the server's plugins and the event system.
|
||||||
*/
|
*/
|
||||||
public final class PluginManager {
|
public final class PluginManager {
|
||||||
private final Map<String, Plugin> plugins = new HashMap<>();
|
/* All loaded plugins. */
|
||||||
private final List<EventHandler<? extends Event>> listeners = new LinkedList<>();
|
private final Map<String, Plugin> plugins = new LinkedHashMap<>();
|
||||||
|
/* All currently registered listeners per plugin. */
|
||||||
|
private final Map<Plugin, List<EventHandler<? extends Event>>> listeners = new LinkedHashMap<>();
|
||||||
|
|
||||||
public PluginManager() {
|
public PluginManager() {
|
||||||
this.loadPlugins(); // Load all plugins from the plugins directory.
|
this.loadPlugins(); // Load all plugins from the plugins directory.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Data about an unloaded plugin. */
|
||||||
|
@AllArgsConstructor @Getter
|
||||||
|
static class PluginData {
|
||||||
|
private Plugin plugin;
|
||||||
|
private PluginIdentifier identifier;
|
||||||
|
private URLClassLoader classLoader;
|
||||||
|
private String[] dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads plugins from the config-specified directory.
|
* Loads plugins from the config-specified directory.
|
||||||
*/
|
*/
|
||||||
@ -59,36 +65,59 @@ public final class PluginManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Create a class loader for the plugins.
|
||||||
URLClassLoader classLoader = new URLClassLoader(pluginNames);
|
URLClassLoader classLoader = new URLClassLoader(pluginNames);
|
||||||
|
// Create a list of plugins that require dependencies.
|
||||||
|
List<PluginData> dependencies = new ArrayList<>();
|
||||||
|
|
||||||
plugins.forEach(plugin -> {
|
// Initialize all plugins.
|
||||||
|
for(var plugin : plugins) {
|
||||||
try {
|
try {
|
||||||
URL url = plugin.toURI().toURL();
|
URL url = plugin.toURI().toURL();
|
||||||
try (URLClassLoader loader = new URLClassLoader(new URL[]{url})) {
|
try (URLClassLoader loader = new URLClassLoader(new URL[]{url})) {
|
||||||
URL configFile = loader.findResource("plugin.json"); // Find the plugin.json file for each plugin.
|
// Find the plugin.json file for each plugin.
|
||||||
|
URL configFile = loader.findResource("plugin.json");
|
||||||
|
// Open the config file for reading.
|
||||||
InputStreamReader fileReader = new InputStreamReader(configFile.openStream());
|
InputStreamReader fileReader = new InputStreamReader(configFile.openStream());
|
||||||
|
|
||||||
|
// Create a plugin config instance from the config file.
|
||||||
PluginConfig pluginConfig = Grasscutter.getGsonFactory().fromJson(fileReader, PluginConfig.class);
|
PluginConfig pluginConfig = Grasscutter.getGsonFactory().fromJson(fileReader, PluginConfig.class);
|
||||||
|
// Check if the plugin config is valid.
|
||||||
if (!pluginConfig.validate()) {
|
if (!pluginConfig.validate()) {
|
||||||
Utils.logObject(pluginConfig);
|
Utils.logObject(pluginConfig);
|
||||||
Grasscutter.getLogger().warn("Plugin " + plugin.getName() + " has an invalid config file.");
|
Grasscutter.getLogger().warn("Plugin " + plugin.getName() + " has an invalid config file.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a JAR file instance from the plugin's URL.
|
||||||
JarFile jarFile = new JarFile(plugin);
|
JarFile jarFile = new JarFile(plugin);
|
||||||
|
// Load all class files from the JAR file.
|
||||||
Enumeration<JarEntry> entries = jarFile.entries();
|
Enumeration<JarEntry> entries = jarFile.entries();
|
||||||
while (entries.hasMoreElements()) {
|
while (entries.hasMoreElements()) {
|
||||||
JarEntry entry = entries.nextElement();
|
JarEntry entry = entries.nextElement();
|
||||||
if(entry.isDirectory() || !entry.getName().endsWith(".class") || entry.getName().contains("module-info")) continue;
|
if (entry.isDirectory() || !entry.getName().endsWith(".class") || entry.getName().contains("module-info"))
|
||||||
|
continue;
|
||||||
String className = entry.getName().replace(".class", "").replace("/", ".");
|
String className = entry.getName().replace(".class", "").replace("/", ".");
|
||||||
classLoader.loadClass(className); // Use the same class loader for ALL plugins.
|
classLoader.loadClass(className); // Use the same class loader for ALL plugins.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a plugin instance.
|
||||||
Class<?> pluginClass = classLoader.loadClass(pluginConfig.mainClass);
|
Class<?> pluginClass = classLoader.loadClass(pluginConfig.mainClass);
|
||||||
Plugin pluginInstance = (Plugin) pluginClass.getDeclaredConstructor().newInstance();
|
Plugin pluginInstance = (Plugin) pluginClass.getDeclaredConstructor().newInstance();
|
||||||
this.loadPlugin(pluginInstance, PluginIdentifier.fromPluginConfig(pluginConfig), loader);
|
// Close the file reader.
|
||||||
|
fileReader.close();
|
||||||
|
|
||||||
fileReader.close(); // Close the file reader.
|
// Check if the plugin has alternate dependencies.
|
||||||
|
if(pluginConfig.loadAfter != null && pluginConfig.loadAfter.length > 0) {
|
||||||
|
// Add the plugin to a "load later" list.
|
||||||
|
dependencies.add(new PluginData(
|
||||||
|
pluginInstance, PluginIdentifier.fromPluginConfig(pluginConfig),
|
||||||
|
loader, pluginConfig.loadAfter));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the plugin.
|
||||||
|
this.loadPlugin(pluginInstance, PluginIdentifier.fromPluginConfig(pluginConfig), loader);
|
||||||
} catch (ClassNotFoundException ignored) {
|
} catch (ClassNotFoundException ignored) {
|
||||||
Grasscutter.getLogger().warn("Plugin " + plugin.getName() + " has an invalid main class.");
|
Grasscutter.getLogger().warn("Plugin " + plugin.getName() + " has an invalid main class.");
|
||||||
} catch (FileNotFoundException ignored) {
|
} catch (FileNotFoundException ignored) {
|
||||||
@ -97,11 +126,41 @@ public final class PluginManager {
|
|||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
Grasscutter.getLogger().error("Failed to load plugin: " + plugin.getName(), exception);
|
Grasscutter.getLogger().error("Failed to load plugin: " + plugin.getName(), exception);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// Load plugins with dependencies.
|
||||||
|
int depth = 0; final int maxDepth = 30;
|
||||||
|
while(!dependencies.isEmpty()) {
|
||||||
|
// Check if the depth is too high.
|
||||||
|
if(depth >= maxDepth) {
|
||||||
|
Grasscutter.getLogger().error("Failed to load plugins with dependencies.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get the next plugin to load.
|
||||||
|
var pluginData = dependencies.get(0);
|
||||||
|
|
||||||
|
// Check if the plugin's dependencies are loaded.
|
||||||
|
if(!this.plugins.keySet().containsAll(List.of(pluginData.getDependencies()))) {
|
||||||
|
depth++; // Increase depth counter.
|
||||||
|
continue; // Continue to next plugin.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the plugin from the list of dependencies.
|
||||||
|
dependencies.remove(pluginData);
|
||||||
|
|
||||||
|
// Load the plugin.
|
||||||
|
this.loadPlugin(pluginData.getPlugin(), pluginData.getIdentifier(), pluginData.getClassLoader());
|
||||||
|
} catch (Exception exception) {
|
||||||
|
Grasscutter.getLogger().error("Failed to load a plugin.", exception); depth++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the specified plugin.
|
* Load the specified plugin.
|
||||||
|
*
|
||||||
* @param plugin The plugin instance.
|
* @param plugin The plugin instance.
|
||||||
*/
|
*/
|
||||||
private void loadPlugin(Plugin plugin, PluginIdentifier identifier, URLClassLoader classLoader) {
|
private void loadPlugin(Plugin plugin, PluginIdentifier identifier, URLClassLoader classLoader) {
|
||||||
@ -111,15 +170,24 @@ public final class PluginManager {
|
|||||||
try {
|
try {
|
||||||
Class<Plugin> pluginClass = Plugin.class;
|
Class<Plugin> pluginClass = Plugin.class;
|
||||||
Method method = pluginClass.getDeclaredMethod("initializePlugin", PluginIdentifier.class, URLClassLoader.class);
|
Method method = pluginClass.getDeclaredMethod("initializePlugin", PluginIdentifier.class, URLClassLoader.class);
|
||||||
method.setAccessible(true); method.invoke(plugin, identifier, classLoader); method.setAccessible(false);
|
method.setAccessible(true);
|
||||||
|
method.invoke(plugin, identifier, classLoader);
|
||||||
|
method.setAccessible(false);
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
Grasscutter.getLogger().warn("Failed to add plugin identifier: " + identifier.name);
|
Grasscutter.getLogger().warn("Failed to add plugin identifier: " + identifier.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the plugin to the list of loaded plugins.
|
// Add the plugin to the list of loaded plugins.
|
||||||
this.plugins.put(identifier.name, plugin);
|
this.plugins.put(identifier.name, plugin);
|
||||||
|
// Create a collection for the plugin's listeners.
|
||||||
|
this.listeners.put(plugin, new LinkedList<>());
|
||||||
|
|
||||||
// Call the plugin's onLoad method.
|
// Call the plugin's onLoad method.
|
||||||
|
try {
|
||||||
plugin.onLoad();
|
plugin.onLoad();
|
||||||
|
} catch (Throwable exception) {
|
||||||
|
Grasscutter.getLogger().error("Failed to load plugin: " + identifier.name, exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,7 +196,11 @@ public final class PluginManager {
|
|||||||
public void enablePlugins() {
|
public void enablePlugins() {
|
||||||
this.plugins.forEach((name, plugin) -> {
|
this.plugins.forEach((name, plugin) -> {
|
||||||
Grasscutter.getLogger().info("Enabling plugin: " + name);
|
Grasscutter.getLogger().info("Enabling plugin: " + name);
|
||||||
|
try {
|
||||||
plugin.onEnable();
|
plugin.onEnable();
|
||||||
|
} catch (Throwable exception) {
|
||||||
|
Grasscutter.getLogger().error("Failed to enable plugin: " + name, exception);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,20 +210,27 @@ public final class PluginManager {
|
|||||||
public void disablePlugins() {
|
public void disablePlugins() {
|
||||||
this.plugins.forEach((name, plugin) -> {
|
this.plugins.forEach((name, plugin) -> {
|
||||||
Grasscutter.getLogger().info("Disabling plugin: " + name);
|
Grasscutter.getLogger().info("Disabling plugin: " + name);
|
||||||
|
try {
|
||||||
plugin.onDisable();
|
plugin.onDisable();
|
||||||
|
} catch (Throwable exception) {
|
||||||
|
Grasscutter.getLogger().error("Failed to disable plugin: " + name, exception);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a plugin's event listener.
|
* Registers a plugin's event listener.
|
||||||
|
*
|
||||||
|
* @param plugin The plugin registering the listener.
|
||||||
* @param listener The event listener.
|
* @param listener The event listener.
|
||||||
*/
|
*/
|
||||||
public void registerListener(EventHandler<? extends Event> listener) {
|
public void registerListener(Plugin plugin, EventHandler<? extends Event> listener) {
|
||||||
this.listeners.add(listener);
|
this.listeners.get(plugin).add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke the provided event on all registered event listeners.
|
* Invoke the provided event on all registered event listeners.
|
||||||
|
*
|
||||||
* @param event The event to invoke.
|
* @param event The event to invoke.
|
||||||
*/
|
*/
|
||||||
public void invokeEvent(Event event) {
|
public void invokeEvent(Event event) {
|
||||||
@ -161,27 +240,70 @@ public final class PluginManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check an event to handlers for the priority.
|
* Check an event to handlers for the priority.
|
||||||
|
*
|
||||||
* @param event The event being called.
|
* @param event The event being called.
|
||||||
* @param priority The priority to call for.
|
* @param priority The priority to call for.
|
||||||
*/
|
*/
|
||||||
private void checkAndFilter(Event event, HandlerPriority priority) {
|
private void checkAndFilter(Event event, HandlerPriority priority) {
|
||||||
this.listeners.stream()
|
// Create a collection of listeners.
|
||||||
|
List<EventHandler<? extends Event>> listeners = new LinkedList<>();
|
||||||
|
|
||||||
|
// Add all listeners from every plugin.
|
||||||
|
this.listeners.values().forEach(listeners::addAll);
|
||||||
|
|
||||||
|
listeners.stream()
|
||||||
|
// Filter the listeners by priority.
|
||||||
.filter(handler -> handler.handles().isInstance(event))
|
.filter(handler -> handler.handles().isInstance(event))
|
||||||
.filter(handler -> handler.getPriority() == priority)
|
.filter(handler -> handler.getPriority() == priority)
|
||||||
|
// Invoke the event.
|
||||||
.toList().forEach(handler -> this.invokeHandler(event, handler));
|
.toList().forEach(handler -> this.invokeHandler(event, handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a plugin's instance by its name.
|
* Gets a plugin's instance by its name.
|
||||||
|
*
|
||||||
* @param name The name of the plugin.
|
* @param name The name of the plugin.
|
||||||
* @return Either null, or the plugin's instance.
|
* @return Either null, or the plugin's instance.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public Plugin getPlugin(String name) {
|
public Plugin getPlugin(String name) {
|
||||||
return this.plugins.get(name);
|
return this.plugins.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables a plugin.
|
||||||
|
*
|
||||||
|
* @param plugin The plugin to enable.
|
||||||
|
*/
|
||||||
|
public void enablePlugin(Plugin plugin) {
|
||||||
|
try {
|
||||||
|
// Call the plugin's onEnable method.
|
||||||
|
plugin.onEnable();
|
||||||
|
} catch (Exception exception) {
|
||||||
|
Grasscutter.getLogger().error("Failed to enable plugin: " + plugin.getName(), exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables a plugin.
|
||||||
|
*
|
||||||
|
* @param plugin The plugin to disable.
|
||||||
|
*/
|
||||||
|
public void disablePlugin(Plugin plugin) {
|
||||||
|
try {
|
||||||
|
// Call the plugin's onDisable method.
|
||||||
|
plugin.onDisable();
|
||||||
|
} catch (Exception exception) {
|
||||||
|
Grasscutter.getLogger().error("Failed to disable plugin: " + plugin.getName(), exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Un-register all listeners.
|
||||||
|
this.listeners.remove(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs logic checks then invokes the provided event handler.
|
* Performs logic checks then invokes the provided event handler.
|
||||||
|
*
|
||||||
* @param event The event passed through to the handler.
|
* @param event The event passed through to the handler.
|
||||||
* @param handler The handler to invoke.
|
* @param handler The handler to invoke.
|
||||||
*/
|
*/
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package emu.grasscutter.server.event;
|
package emu.grasscutter.server.event;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.plugin.Plugin;
|
||||||
import emu.grasscutter.utils.EventConsumer;
|
import emu.grasscutter.utils.EventConsumer;
|
||||||
|
|
||||||
public final class EventHandler<T extends Event> {
|
public final class EventHandler<T extends Event> {
|
||||||
@ -75,7 +76,7 @@ public final class EventHandler<T extends Event> {
|
|||||||
/**
|
/**
|
||||||
* Registers the handler into the PluginManager.
|
* Registers the handler into the PluginManager.
|
||||||
*/
|
*/
|
||||||
public void register() {
|
public void register(Plugin plugin) {
|
||||||
Grasscutter.getPluginManager().registerListener(this);
|
Grasscutter.getPluginManager().registerListener(plugin, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user