Grasscutter/src/main/java/emu/grasscutter/Grasscutter.java

361 lines
10 KiB
Java
Raw Normal View History

2022-04-17 12:43:07 +00:00
package emu.grasscutter;
import java.io.*;
import java.util.Calendar;
2022-04-17 12:43:07 +00:00
import emu.grasscutter.auth.AuthenticationSystem;
import emu.grasscutter.auth.DefaultAuthentication;
import emu.grasscutter.command.CommandMap;
import emu.grasscutter.game.managers.EnergyManager.EnergyManager;
2022-05-14 16:10:43 +00:00
import emu.grasscutter.game.managers.StaminaManager.StaminaManager;
2022-04-23 05:17:35 +00:00
import emu.grasscutter.plugin.PluginManager;
2022-05-03 01:20:24 +00:00
import emu.grasscutter.plugin.api.ServerHook;
import emu.grasscutter.scripts.ScriptLoader;
import emu.grasscutter.server.http.HttpServer;
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.documentation.DocumentationServerHandler;
import emu.grasscutter.utils.ConfigContainer;
2022-04-18 05:11:27 +00:00
import emu.grasscutter.utils.Utils;
import org.jline.reader.EndOfFileException;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.UserInterruptException;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
2022-04-19 04:35:01 +00:00
import org.reflections.Reflections;
2022-04-17 12:43:07 +00:00
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;
2022-04-17 12:43:07 +00:00
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.Crypto;
import javax.annotation.Nullable;
import static emu.grasscutter.utils.Language.translate;
import static emu.grasscutter.Configuration.*;
2022-04-18 05:11:27 +00:00
public final class Grasscutter {
private static final Logger log = (Logger) LoggerFactory.getLogger(Grasscutter.class);
private static LineReader consoleLineReader = null;
private static Language language;
2022-04-18 05:11:27 +00:00
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
public static final File configFile = new File("./config.json");
2022-05-05 05:24:49 +00:00
private static int day; // Current day of week.
2022-05-05 05:24:49 +00:00
private static HttpServer httpServer;
2022-04-17 12:43:07 +00:00
private static GameServer gameServer;
2022-04-23 05:17:35 +00:00
private static PluginManager pluginManager;
private static AuthenticationSystem authenticationSystem;
2022-05-05 05:24:49 +00:00
2022-04-26 04:39:05 +00:00
public static final Reflections reflector = new Reflections("emu.grasscutter");
public static ConfigContainer config;
2022-05-07 01:39:30 +00:00
2022-04-18 05:11:27 +00:00
static {
2022-04-19 04:35:01 +00:00
// Declare logback configuration.
System.setProperty("logback.configurationFile", "src/main/resources/logback.xml");
2022-05-05 05:24:49 +00:00
2022-04-19 04:35:01 +00:00
// Load server configuration.
Grasscutter.loadConfig();
// Attempt to update configuration.
ConfigContainer.updateConfig();
// Load translation files.
Grasscutter.loadLanguage();
2022-05-05 05:24:49 +00:00
2022-04-18 05:11:27 +00:00
// Check server structure.
Utils.startupCheck();
}
2022-05-05 05:24:49 +00:00
public static void main(String[] args) throws Exception {
Crypto.loadKeys(); // Load keys from buffers.
// Parse arguments.
2022-05-07 01:04:39 +00:00
boolean exitEarly = false;
2022-04-17 12:43:07 +00:00
for (String arg : args) {
switch (arg.toLowerCase()) {
2022-04-23 05:17:35 +00:00
case "-handbook" -> {
2022-05-07 01:04:39 +00:00
Tools.createGmHandbook(); exitEarly = true;
2022-04-23 05:17:35 +00:00
}
case "-gachamap" -> {
Tools.createGachaMapping(DATA("gacha_mappings.js")); exitEarly = true;
}
case "-version" -> {
2022-05-11 06:23:58 +00:00
System.out.println("Grasscutter version: " + BuildConfig.VERSION + "-" + BuildConfig.GIT_HASH); exitEarly = true;
}
2022-04-17 12:43:07 +00:00
}
2022-05-07 01:04:39 +00:00
}
// Exit early if argument sets it.
if(exitEarly) System.exit(0);
2022-04-18 05:11:27 +00:00
// Initialize server.
Grasscutter.getLogger().info(translate("messages.status.starting"));
Grasscutter.getLogger().info(translate("messages.status.game_version", GameConstants.VERSION));
Grasscutter.getLogger().info(translate("messages.status.version", BuildConfig.VERSION, BuildConfig.GIT_HASH));
2022-04-18 05:11:27 +00:00
// Load all resources.
Grasscutter.updateDayOfWeek();
2022-04-17 12:43:07 +00:00
ResourceLoader.loadAll();
ScriptLoader.init();
EnergyManager.initialize();
2022-05-07 01:52:10 +00:00
// Initialize database.
2022-04-17 12:43:07 +00:00
DatabaseManager.initialize();
// Initialize the default authentication system.
authenticationSystem = new DefaultAuthentication();
2022-04-23 05:17:35 +00:00
// Create server instances.
httpServer = new HttpServer();
gameServer = new GameServer();
2022-05-03 01:20:24 +00:00
// Create a server hook instance with both servers.
new ServerHook(gameServer, httpServer);
// Create plugin manager instance.
pluginManager = new PluginManager();
// Add HTTP routes after loading plugins.
httpServer.addRouter(HttpServer.UnhandledRequestRouter.class);
httpServer.addRouter(HttpServer.DefaultRequestRouter.class);
httpServer.addRouter(RegionHandler.class);
httpServer.addRouter(LogHandler.class);
httpServer.addRouter(GenericHandler.class);
httpServer.addRouter(AnnouncementsHandler.class);
httpServer.addRouter(DispatchHandler.class);
httpServer.addRouter(GachaHandler.class);
httpServer.addRouter(DocumentationServerHandler.class);
2022-05-14 16:08:33 +00:00
// TODO: find a better place?
StaminaManager.initialize();
2022-04-18 05:11:27 +00:00
// Start servers.
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);
}
2022-04-23 05:17:35 +00:00
// Enable all plugins.
pluginManager.enablePlugins();
2022-04-23 05:17:35 +00:00
// Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(Grasscutter::onShutdown));
// Open console.
startConsole();
}
2022-04-23 05:17:35 +00:00
/**
* Server shutdown event.
*/
private static void onShutdown() {
// Disable all plugins.
pluginManager.disablePlugins();
}
/*
* Methods for the language system component.
*/
public static void loadLanguage() {
var locale = config.language.language;
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.
2022-04-17 12:43:07 +00:00
try (FileReader file = new FileReader(configFile)) {
config = gson.fromJson(file, ConfigContainer.class);
} catch (Exception exception) {
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);
}
2022-04-17 12:43:07 +00:00
}
/**
* Saves the provided server configuration.
* @param config The configuration to save, or null for a new one.
*/
public static void saveConfig(@Nullable ConfigContainer config) {
if(config == null) config = new ConfigContainer();
2022-04-17 12:43:07 +00:00
try (FileWriter file = new FileWriter(configFile)) {
file.write(gson.toJson(config));
} catch (IOException ignored) {
Grasscutter.getLogger().error("Unable to write to config file.");
2022-04-17 12:43:07 +00:00
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to save config file.", e);
2022-04-17 12:43:07 +00:00
}
}
2022-05-05 05:24:49 +00:00
/*
* Getters for the various server components.
*/
public static ConfigContainer getConfig() {
2022-04-18 05:11:27 +00:00
return config;
}
public static Language getLanguage() {
return language;
}
public static void setLanguage(Language language) {
Grasscutter.language = language;
}
public static Language getLanguage(String langCode) {
return Language.getLanguage(langCode);
}
2022-04-18 05:11:27 +00:00
public static Logger getLogger() {
return log;
}
public static LineReader getConsole() {
if (consoleLineReader == null) {
Terminal terminal = null;
try {
terminal = TerminalBuilder.builder().jna(true).build();
} catch (Exception e) {
try {
// Fallback to a dumb jline terminal.
terminal = TerminalBuilder.builder().dumb(true).build();
} catch (Exception ignored) {
// When dumb is true, build() never throws.
}
}
consoleLineReader = LineReaderBuilder.builder()
.terminal(terminal)
.build();
}
return consoleLineReader;
}
2022-04-18 05:11:27 +00:00
public static Gson getGsonFactory() {
return gson;
}
public static HttpServer getHttpServer() {
return httpServer;
2022-04-18 05:11:27 +00:00
}
public static GameServer getGameServer() {
return gameServer;
}
2022-05-05 05:24:49 +00:00
2022-04-23 05:17:35 +00:00
public static PluginManager getPluginManager() {
return pluginManager;
}
public static AuthenticationSystem getAuthenticationSystem() {
return authenticationSystem;
}
2022-05-05 05:24:49 +00:00
public static int getCurrentDayOfWeek() {
return day;
}
/*
* Utility methods.
*/
public static void updateDayOfWeek() {
Calendar calendar = Calendar.getInstance();
day = calendar.get(Calendar.DAY_OF_WEEK);
}
public static void startConsole() {
// Console should not start in dispatch only mode.
if (SERVER.runMode == ServerRunMode.DISPATCH_ONLY) {
getLogger().info(translate("messages.dispatch.no_commands_error"));
return;
}
getLogger().info(translate("messages.status.done"));
String input = null;
boolean isLastInterrupted = false;
2022-05-17 00:29:23 +00:00
while (config.server.game.enableConsole) {
try {
input = consoleLineReader.readLine("> ");
} catch (UserInterruptException e) {
if (!isLastInterrupted) {
isLastInterrupted = true;
Grasscutter.getLogger().info("Press Ctrl-C again to shutdown.");
continue;
} else {
Runtime.getRuntime().exit(0);
}
} catch (EndOfFileException e) {
Grasscutter.getLogger().info("EOF detected.");
continue;
} catch (IOError e) {
Grasscutter.getLogger().error("An IO error occurred.", e);
continue;
}
isLastInterrupted = false;
try {
CommandMap.getInstance().invoke(null, null, input);
} catch (Exception e) {
Grasscutter.getLogger().error(translate("messages.game.command_error"), e);
}
}
}
2022-05-05 05:24:49 +00:00
/**
* Sets the authentication system for the server.
* @param authenticationSystem The authentication system to use.
*/
public static void setAuthenticationSystem(AuthenticationSystem authenticationSystem) {
Grasscutter.authenticationSystem = authenticationSystem;
}
/*
* Enums for the configuration.
*/
2022-05-01 05:52:09 +00:00
public enum ServerRunMode {
HYBRID, DISPATCH_ONLY, GAME_ONLY
}
2022-05-05 05:24:49 +00:00
2022-05-01 05:52:09 +00:00
public enum ServerDebugMode {
ALL, MISSING, NONE
}
2022-04-17 12:43:07 +00:00
}