Fix issue when exiting server when plugin manager hasn't loaded

This commit is contained in:
KingRainbow44 2022-06-22 00:09:31 -04:00
parent 91d232d625
commit 6175e957b5

View File

@ -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,349 +39,343 @@ 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);
private static LineReader consoleLineReader = null; private static LineReader consoleLineReader = null;
private static Language language; private static Language language;
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
public static final File configFile = new File("./config.json"); public static final File configFile = new File("./config.json");
private static int day; // Current day of week. private static int day; // Current day of week.
private static HttpServer httpServer; private static HttpServer httpServer;
private static GameServer gameServer; private static GameServer gameServer;
private static PluginManager pluginManager; private static PluginManager pluginManager;
private static AuthenticationSystem authenticationSystem; private static AuthenticationSystem authenticationSystem;
private static PermissionHandler permissionHandler; private static PermissionHandler permissionHandler;
public static final Reflections reflector = new Reflections("emu.grasscutter"); public static final Reflections reflector = new Reflections("emu.grasscutter");
public static ConfigContainer config; public static ConfigContainer config;
static { static {
// Declare logback configuration. // Declare logback configuration.
System.setProperty("logback.configurationFile", "src/main/resources/logback.xml"); System.setProperty("logback.configurationFile", "src/main/resources/logback.xml");
// Load server configuration. // Load server configuration.
Grasscutter.loadConfig(); Grasscutter.loadConfig();
// Attempt to update configuration. // Attempt to update configuration.
ConfigContainer.updateConfig(); ConfigContainer.updateConfig();
// Load translation files. // Load translation files.
Grasscutter.loadLanguage(); Grasscutter.loadLanguage();
// Check server structure. // Check server structure.
Utils.startupCheck(); Utils.startupCheck();
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Crypto.loadKeys(); // Load keys from buffers. Crypto.loadKeys(); // Load keys from buffers.
// Parse arguments. // Parse arguments.
boolean exitEarly = false; boolean exitEarly = false;
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" -> { }
Tools.createGachaMapping(DATA("gacha_mappings.js")); exitEarly = true; case "-gachamap" -> {
} Tools.createGachaMapping(DATA("gacha_mappings.js"));
case "-version" -> { exitEarly = true;
System.out.println("Grasscutter version: " + BuildConfig.VERSION + "-" + BuildConfig.GIT_HASH); exitEarly = true; }
} case "-version" -> {
} System.out.println("Grasscutter version: " + BuildConfig.VERSION + "-" + BuildConfig.GIT_HASH);
} exitEarly = true;
}
}
}
// Exit early if argument sets it. // Exit early if argument sets it.
if(exitEarly) System.exit(0); if (exitEarly) System.exit(0);
// Initialize server. // Initialize server.
Grasscutter.getLogger().info(translate("messages.status.starting")); Grasscutter.getLogger().info(translate("messages.status.starting"));
Grasscutter.getLogger().info(translate("messages.status.game_version", GameConstants.VERSION)); Grasscutter.getLogger().info(translate("messages.status.game_version", GameConstants.VERSION));
Grasscutter.getLogger().info(translate("messages.status.version", BuildConfig.VERSION, BuildConfig.GIT_HASH)); Grasscutter.getLogger().info(translate("messages.status.version", BuildConfig.VERSION, BuildConfig.GIT_HASH));
// Load all resources. // Load all resources.
Grasscutter.updateDayOfWeek(); Grasscutter.updateDayOfWeek();
ResourceLoader.loadAll(); ResourceLoader.loadAll();
ScriptLoader.init(); ScriptLoader.init();
EnergyManager.initialize();
DungeonChallenge.initialize();
// Initialize database. // Initialize database.
DatabaseManager.initialize(); DatabaseManager.initialize();
// Initialize the default systems. // Initialize the default systems.
authenticationSystem = new DefaultAuthentication(); authenticationSystem = new DefaultAuthentication();
permissionHandler = new DefaultPermissionHandler(); permissionHandler = new DefaultPermissionHandler();
// Create server instances. // Create server instances.
httpServer = new HttpServer(); httpServer = new HttpServer();
gameServer = new GameServer(); gameServer = new GameServer();
// Create a server hook instance with both servers. // Create a server hook instance with both servers.
new ServerHook(gameServer, httpServer); new ServerHook(gameServer, httpServer);
// Create plugin manager instance. // Create plugin manager instance.
pluginManager = new PluginManager(); pluginManager = new PluginManager();
// Add HTTP routes after loading plugins. // Add HTTP routes after loading plugins.
httpServer.addRouter(HttpServer.UnhandledRequestRouter.class); httpServer.addRouter(HttpServer.UnhandledRequestRouter.class);
httpServer.addRouter(HttpServer.DefaultRequestRouter.class); httpServer.addRouter(HttpServer.DefaultRequestRouter.class);
httpServer.addRouter(RegionHandler.class); httpServer.addRouter(RegionHandler.class);
httpServer.addRouter(LogHandler.class); httpServer.addRouter(LogHandler.class);
httpServer.addRouter(GenericHandler.class); httpServer.addRouter(GenericHandler.class);
httpServer.addRouter(AnnouncementsHandler.class); httpServer.addRouter(AnnouncementsHandler.class);
httpServer.addRouter(DispatchHandler.class); httpServer.addRouter(DispatchHandler.class);
httpServer.addRouter(GachaHandler.class); httpServer.addRouter(GachaHandler.class);
httpServer.addRouter(DocumentationServerHandler.class); httpServer.addRouter(DocumentationServerHandler.class);
// TODO: find a better place? // Start servers.
StaminaManager.initialize(); var runMode = SERVER.runMode;
if (runMode == ServerRunMode.HYBRID) {
httpServer.start();
gameServer.start();
} else if (runMode == ServerRunMode.DISPATCH_ONLY) {
httpServer.start();
} else if (runMode == ServerRunMode.GAME_ONLY) {
gameServer.start();
} else {
getLogger().error(translate("messages.status.run_mode_error", runMode));
getLogger().error(translate("messages.status.run_mode_help"));
getLogger().error(translate("messages.status.shutdown"));
System.exit(1);
}
// Start servers. // Enable all plugins.
var runMode = SERVER.runMode; pluginManager.enablePlugins();
if (runMode == ServerRunMode.HYBRID) {
httpServer.start();
gameServer.start();
} else if (runMode == ServerRunMode.DISPATCH_ONLY) {
httpServer.start();
} else if (runMode == ServerRunMode.GAME_ONLY) {
gameServer.start();
} else {
getLogger().error(translate("messages.status.run_mode_error", runMode));
getLogger().error(translate("messages.status.run_mode_help"));
getLogger().error(translate("messages.status.shutdown"));
System.exit(1);
}
// Enable all plugins. // Hook into shutdown event.
pluginManager.enablePlugins(); Runtime.getRuntime().addShutdownHook(new Thread(Grasscutter::onShutdown));
// Hook into shutdown event. // Open console.
Runtime.getRuntime().addShutdownHook(new Thread(Grasscutter::onShutdown)); startConsole();
}
// Open console. /**
startConsole(); * Server shutdown event.
} */
private static void onShutdown() {
// Disable all plugins.
if(pluginManager != null)
pluginManager.disablePlugins();
}
/** /*
* Server shutdown event. * Methods for the language system component.
*/ */
private static void onShutdown() {
// Disable all plugins.
pluginManager.disablePlugins();
}
/* public static void loadLanguage() {
* Methods for the language system component. var locale = config.language.language;
*/ language = Language.getLanguage(Utils.getLanguageCode(locale));
}
public static void loadLanguage() { /*
var locale = config.language.language; * Methods for the configuration system component.
language = Language.getLanguage(Utils.getLanguageCode(locale)); */
}
/* /**
* Methods for the configuration system component. * Attempts to load the configuration from a file.
*/ */
public static void loadConfig() {
// Check if config.json exists. If not, we generate a new config.
if (!configFile.exists()) {
getLogger().info("config.json could not be found. Generating a default configuration ...");
config = new ConfigContainer();
Grasscutter.saveConfig(config);
return;
}
/** // If the file already exists, we attempt to load it.
* Attempts to load the configuration from a file. try (FileReader file = new FileReader(configFile)) {
*/ config = gson.fromJson(file, ConfigContainer.class);
public static void loadConfig() { } catch (Exception exception) {
// Check if config.json exists. If not, we generate a new config. getLogger().error("There was an error while trying to load the configuration from config.json. Please make sure that there are no syntax errors. If you want to start with a default configuration, delete your existing config.json.");
if (!configFile.exists()) { System.exit(1);
getLogger().info("config.json could not be found. Generating a default configuration ..."); }
config = new ConfigContainer(); }
Grasscutter.saveConfig(config);
return;
}
// If the file already exists, we attempt to load it. /**
try (FileReader file = new FileReader(configFile)) { * Saves the provided server configuration.
config = gson.fromJson(file, ConfigContainer.class); *
} catch (Exception exception) { * @param config The configuration to save, or null for a new one.
getLogger().error("There was an error while trying to load the configuration from config.json. Please make sure that there are no syntax errors. If you want to start with a default configuration, delete your existing config.json."); */
System.exit(1); public static void saveConfig(@Nullable ConfigContainer config) {
} if (config == null) config = new ConfigContainer();
}
/** try (FileWriter file = new FileWriter(configFile)) {
* Saves the provided server configuration. file.write(gson.toJson(config));
* @param config The configuration to save, or null for a new one. } catch (IOException ignored) {
*/ Grasscutter.getLogger().error("Unable to write to config file.");
public static void saveConfig(@Nullable ConfigContainer config) { } catch (Exception e) {
if(config == null) config = new ConfigContainer(); Grasscutter.getLogger().error("Unable to save config file.", e);
}
}
try (FileWriter file = new FileWriter(configFile)) { /*
file.write(gson.toJson(config)); * Getters for the various server components.
} catch (IOException ignored) { */
Grasscutter.getLogger().error("Unable to write to config file.");
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to save config file.", e);
}
}
/* public static ConfigContainer getConfig() {
* Getters for the various server components. return config;
*/ }
public static ConfigContainer getConfig() { public static Language getLanguage() {
return config; return language;
} }
public static Language getLanguage() { public static void setLanguage(Language language) {
return language;
}
public static void setLanguage(Language language) {
Grasscutter.language = language; Grasscutter.language = language;
} }
public static Language getLanguage(String langCode) { public static Language getLanguage(String langCode) {
return Language.getLanguage(langCode); return Language.getLanguage(langCode);
} }
public static Logger getLogger() { public static Logger getLogger() {
return log; return log;
} }
public static LineReader getConsole() { public static LineReader getConsole() {
if (consoleLineReader == null) { if (consoleLineReader == null) {
Terminal terminal = null; Terminal terminal = null;
try { try {
terminal = TerminalBuilder.builder().jna(true).build(); terminal = TerminalBuilder.builder().jna(true).build();
} catch (Exception e) { } catch (Exception e) {
try { try {
// Fallback to a dumb jline terminal. // Fallback to a dumb jline terminal.
terminal = TerminalBuilder.builder().dumb(true).build(); terminal = TerminalBuilder.builder().dumb(true).build();
} catch (Exception ignored) { } catch (Exception ignored) {
// When dumb is true, build() never throws. // When dumb is true, build() never throws.
} }
} }
consoleLineReader = LineReaderBuilder.builder() consoleLineReader = LineReaderBuilder.builder()
.terminal(terminal) .terminal(terminal)
.build(); .build();
} }
return consoleLineReader; return consoleLineReader;
} }
public static Gson getGsonFactory() { public static Gson getGsonFactory() {
return gson; return gson;
} }
public static HttpServer getHttpServer() { public static HttpServer getHttpServer() {
return httpServer; return httpServer;
} }
public static GameServer getGameServer() { public static GameServer getGameServer() {
return gameServer; return gameServer;
} }
public static PluginManager getPluginManager() { public static PluginManager getPluginManager() {
return pluginManager; return pluginManager;
} }
public static AuthenticationSystem getAuthenticationSystem() { public static AuthenticationSystem getAuthenticationSystem() {
return authenticationSystem; return authenticationSystem;
} }
public static PermissionHandler getPermissionHandler() { public static PermissionHandler getPermissionHandler() {
return permissionHandler; return permissionHandler;
} }
public static int getCurrentDayOfWeek() { public static int getCurrentDayOfWeek() {
return day; return day;
} }
/* /*
* Utility methods. * Utility methods.
*/ */
public static void updateDayOfWeek() { public static void updateDayOfWeek() {
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
day = calendar.get(Calendar.DAY_OF_WEEK); day = calendar.get(Calendar.DAY_OF_WEEK);
} }
public static void startConsole() { public static void startConsole() {
// Console should not start in dispatch only mode. // Console should not start in dispatch only mode.
if (SERVER.runMode == ServerRunMode.DISPATCH_ONLY) { if (SERVER.runMode == ServerRunMode.DISPATCH_ONLY) {
getLogger().info(translate("messages.dispatch.no_commands_error")); getLogger().info(translate("messages.dispatch.no_commands_error"));
return; return;
} }
getLogger().info(translate("messages.status.done")); getLogger().info(translate("messages.status.done"));
String input = null; String input = null;
boolean isLastInterrupted = false; boolean isLastInterrupted = false;
while (config.server.game.enableConsole) { while (config.server.game.enableConsole) {
try { try {
input = consoleLineReader.readLine("> "); input = consoleLineReader.readLine("> ");
} catch (UserInterruptException e) { } catch (UserInterruptException e) {
if (!isLastInterrupted) { if (!isLastInterrupted) {
isLastInterrupted = true; isLastInterrupted = true;
Grasscutter.getLogger().info("Press Ctrl-C again to shutdown."); Grasscutter.getLogger().info("Press Ctrl-C again to shutdown.");
continue; continue;
} else { } else {
Runtime.getRuntime().exit(0); Runtime.getRuntime().exit(0);
} }
} catch (EndOfFileException e) { } catch (EndOfFileException e) {
Grasscutter.getLogger().info("EOF detected."); Grasscutter.getLogger().info("EOF detected.");
continue; continue;
} catch (IOError e) { } catch (IOError e) {
Grasscutter.getLogger().error("An IO error occurred.", e); Grasscutter.getLogger().error("An IO error occurred.", e);
continue; continue;
} }
isLastInterrupted = false; isLastInterrupted = false;
try { try {
CommandMap.getInstance().invoke(null, null, input); CommandMap.getInstance().invoke(null, null, input);
} catch (Exception e) { } catch (Exception e) {
Grasscutter.getLogger().error(translate("messages.game.command_error"), e); Grasscutter.getLogger().error(translate("messages.game.command_error"), e);
} }
} }
} }
/** /**
* 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) { */
Grasscutter.authenticationSystem = authenticationSystem; public static void setAuthenticationSystem(AuthenticationSystem authenticationSystem) {
} Grasscutter.authenticationSystem = authenticationSystem;
}
/** /**
* 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) { */
Grasscutter.permissionHandler = permissionHandler; public static void setPermissionHandler(PermissionHandler permissionHandler) {
} Grasscutter.permissionHandler = permissionHandler;
}
/* /*
* Enums for the configuration. * Enums for the configuration.
*/ */
public enum ServerRunMode { public enum ServerRunMode {
HYBRID, DISPATCH_ONLY, GAME_ONLY HYBRID, DISPATCH_ONLY, GAME_ONLY
} }
public enum ServerDebugMode { public enum ServerDebugMode {
ALL, MISSING, NONE ALL, MISSING, NONE
} }
} }