diff --git a/src/main/java/emu/grasscutter/Configuration.java b/src/main/java/emu/grasscutter/Configuration.java index 7f7bf16fe..7adc334c1 100644 --- a/src/main/java/emu/grasscutter/Configuration.java +++ b/src/main/java/emu/grasscutter/Configuration.java @@ -1,12 +1,7 @@ package emu.grasscutter; -import com.google.gson.JsonObject; -import emu.grasscutter.Grasscutter.*; -import emu.grasscutter.game.mail.Mail.*; +import emu.grasscutter.utils.ConfigContainer; -import java.io.FileReader; -import java.lang.reflect.Field; -import java.util.Arrays; import java.util.Locale; import static emu.grasscutter.Grasscutter.config; @@ -17,56 +12,14 @@ import static emu.grasscutter.Grasscutter.config; * Use `import static emu.grasscutter.Configuration.*;` * to import all configuration constants. */ -public final class Configuration { - private static int version() { - return 1; - } - - /** - * Attempts to update the server's existing configuration to the latest configuration. - */ - public static void updateConfig() { - try { // Check if the server is using a legacy config. - JsonObject configObject = Grasscutter.getGsonFactory() - .fromJson(new FileReader(Grasscutter.configFile), JsonObject.class); - if(!configObject.has("version")) { - Grasscutter.getLogger().info("Updating legacy configuration..."); - Grasscutter.saveConfig(null); - } - } catch (Exception ignored) { } - - var existing = config.version; - var latest = version(); - - if(existing == latest) - return; - - // Create a new configuration instance. - Configuration updated = new Configuration(); - // Update all configuration fields. - Field[] fields = Configuration.class.getDeclaredFields(); - Arrays.stream(fields).forEach(field -> { - try { - field.set(updated, field.get(config)); - } catch (Exception exception) { - Grasscutter.getLogger().error("Failed to update a configuration field.", exception); - } - }); - - try { // Save configuration & reload. - Grasscutter.saveConfig(updated); - Grasscutter.reloadConfig(); - } catch (Exception exception) { - Grasscutter.getLogger().warn("Failed to inject the updated configuration.", exception); - } - } +public final class Configuration extends ConfigContainer { /* * Constants */ // 'c' is short for 'config' and makes code look 'cleaner'. - public static final Configuration c = config; + public static final ConfigContainer c = config; public static final Locale LANGUAGE = config.language.language; public static final Locale FALLBACK_LANGUAGE = config.language.fallback; @@ -135,178 +88,4 @@ public final class Configuration { public static int lr(int left, int right) { return left == 0 ? right : left; } - - /* - * Configuration data. - */ - - public Structure folderStructure; - public Database databaseInfo; - public Language language; - public Account account; - public Server server; - - // DO NOT. TOUCH. THE VERSION NUMBER. - public int version = version(); - - /* Option containers. */ - - public static class Database { - public String connectionUri = "mongodb://localhost:27017"; - public String collection = "grasscutter"; - } - - public static class Structure { - public String resources = "./resources/"; - public String data = "./data/"; - public String packets = "./packets/"; - public String keys = "./keys/"; - public String scripts = "./resources/scripts/"; - public String plugins = "./plugins/"; - - // UNUSED (potentially added later?) - // public String dumps = "./dumps/"; - } - - public static class Server { - public ServerDebugMode debugLevel = ServerDebugMode.NONE; - public ServerRunMode runMode = ServerRunMode.HYBRID; - - public Dispatch dispatch = new Dispatch(); - public Game game = new Game(); - } - - public static class Language { - public Locale language = Locale.getDefault(); - public Locale fallback = Locale.US; - } - - public static class Account { - public boolean autoCreate = false; - public String[] defaultPermissions = {}; - } - - /* Server options. */ - - public static class Dispatch { - public String bindAddress = "0.0.0.0"; - /* This is the address used in URLs. */ - public String accessAddress = "127.0.0.1"; - - public int bindPort = 443; - /* This is the port used in URLs. */ - public int accessPort = 443; - - public Encryption encryption = new Encryption(); - public Policies policies = new Policies(); - public Region[] regions = {}; - - public String defaultName = "Grasscutter"; - } - - public static class Game { - public String bindAddress = "0.0.0.0"; - /* This is the address used in the default region. */ - public String accessAddress = "127.0.0.1"; - - public int bindPort = 443; - /* This is the port used in the default region. */ - public int accessPort = 443; - - public GameOptions gameOptions = new GameOptions(); - public JoinOptions joinOptions = new JoinOptions(); - public ConsoleAccount serverAccount = new ConsoleAccount(); - } - - /* Data containers. */ - - public static class Encryption { - public boolean useEncryption = true; - /* Should 'https' be appended to URLs? */ - public boolean useInRouting = true; - public String keystore = "./keystore.p12"; - public String keystorePassword = "123456"; - } - - public static class Policies { - public CORS cors = new CORS(); - - public static class CORS { - public boolean enabled = false; - public String[] allowedOrigins = new String[]{"*"}; - } - } - - public static class GameOptions { - public InventoryLimits inventoryLimits = new InventoryLimits(); - public AvatarLimits avatarLimits = new AvatarLimits(); - public int worldEntityLimit = 1000; // Unenforced. TODO: Implement. - - public boolean watchGachaConfig = false; - public boolean enableShopItems = true; - public boolean staminaUsage = true; - public Rates rates = new Rates(); - - public Database databaseInfo = new Database(); - - public static class InventoryLimits { - public int weapons = 2000; - public int relics = 2000; - public int materials = 2000; - public int furniture = 2000; - public int all = 30000; - } - - public static class AvatarLimits { - public int singlePlayerTeam = 4; - public int multiplayerTeam = 4; - } - - public static class Rates { - public float adventureExp = 1.0f; - public float mora = 1.0f; - public float leyLines = 1.0f; - } - } - - public static class JoinOptions { - public int[] welcomeEmotes = {2007, 1002, 4010}; - public String welcomeMessage = "Welcome to a Grasscutter server."; - public Mail welcomeMail = new Mail(); - - public static class Mail { - public String title = "Welcome to Grasscutter!"; - public String content = """ - Hi there!\r - First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! \r - \r - Check out our:\r - - """; - public String sender = "Lawnmower"; - public MailItem[] items = { - new MailItem(13509, 1, 1), - new MailItem(201, 99999, 1) - }; - } - } - - public static class ConsoleAccount { - public int avatarId = 10000007; - public int nameCardId = 210001; - public int adventureRank = 1; - public int worldLevel = 0; - - public String nickName = "Server"; - public String signature = "Welcome to Grasscutter!"; - } - - /* Objects. */ - - public static class Region { - public String Name = "os_usa"; - public String Title = "Grasscutter"; - public String Ip = "127.0.0.1"; - public int Port = 22102; - } } \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index 6ba8bd986..73e761e6e 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -1,14 +1,13 @@ package emu.grasscutter; import java.io.*; -import java.lang.reflect.Field; -import java.util.Arrays; import java.util.Calendar; import emu.grasscutter.command.CommandMap; import emu.grasscutter.plugin.PluginManager; import emu.grasscutter.plugin.api.ServerHook; import emu.grasscutter.scripts.ScriptLoader; +import emu.grasscutter.utils.ConfigContainer; import emu.grasscutter.utils.Utils; import org.jline.reader.EndOfFileException; import org.jline.reader.LineReader; @@ -52,16 +51,16 @@ public final class Grasscutter { private static PluginManager pluginManager; public static final Reflections reflector = new Reflections("emu.grasscutter"); - public static final Configuration config; + public static ConfigContainer config; static { // Declare logback configuration. System.setProperty("logback.configurationFile", "src/main/resources/logback.xml"); // Load server configuration. - config = Grasscutter.loadConfig(); + Grasscutter.loadConfig(); // Attempt to update configuration. - Configuration.updateConfig(); + ConfigContainer.updateConfig(); // Load translation files. Grasscutter.loadLanguage(); @@ -144,34 +143,20 @@ public final class Grasscutter { /** * Attempts to load the configuration from a file. - * @return The config from the file, or a new instance. */ - public static Configuration loadConfig() { + public static void loadConfig() { try (FileReader file = new FileReader(configFile)) { - return gson.fromJson(file, Configuration.class); - } catch (Exception e) { + config = gson.fromJson(file, ConfigContainer.class); + } catch (Exception exception) { Grasscutter.saveConfig(null); - return new Configuration(); + config = new ConfigContainer(); + } catch (Error error) { + // Occurred probably from an outdated config file. + Grasscutter.saveConfig(null); + config = new ConfigContainer(); } } - /** - * Attempts to reload the configuration from the file. - * Uses reflection to **replace** the fields in the config. - */ - public static void reloadConfig() { - Configuration fileConfig = Grasscutter.loadConfig(); - - Field[] fields = Configuration.class.getDeclaredFields(); - Arrays.stream(fields).forEach(field -> { - try { - field.set(config, field.get(fileConfig)); - } catch (Exception exception) { - Grasscutter.getLogger().error("Failed to update a configuration field.", exception); - } - }); - } - public static void loadLanguage() { var locale = config.language.language; language = Language.getLanguage(Utils.getLanguageCode(locale)); @@ -181,8 +166,8 @@ public final class Grasscutter { * Saves the provided server configuration. * @param config The configuration to save, or null for a new one. */ - public static void saveConfig(@Nullable Configuration config) { - if(config == null) config = new Configuration(); + public static void saveConfig(@Nullable ConfigContainer config) { + if(config == null) config = new ConfigContainer(); try (FileWriter file = new FileWriter(configFile)) { file.write(gson.toJson(config)); @@ -231,7 +216,7 @@ public final class Grasscutter { } } - public static Configuration getConfig() { + public static ConfigContainer getConfig() { return config; } diff --git a/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java b/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java index 40b8994ec..9414a89c4 100644 --- a/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java @@ -16,7 +16,7 @@ public final class ReloadCommand implements CommandHandler { public void execute(Player sender, Player targetPlayer, List args) { CommandHandler.sendMessage(sender, translate(sender, "commands.reload.reload_start")); - Grasscutter.reloadConfig(); + Grasscutter.loadConfig(); Grasscutter.loadLanguage(); Grasscutter.getGameServer().getGachaManager().load(); Grasscutter.getGameServer().getDropManager().load(); diff --git a/src/main/java/emu/grasscutter/utils/ConfigContainer.java b/src/main/java/emu/grasscutter/utils/ConfigContainer.java new file mode 100644 index 000000000..a848dff39 --- /dev/null +++ b/src/main/java/emu/grasscutter/utils/ConfigContainer.java @@ -0,0 +1,229 @@ +package emu.grasscutter.utils; + +import com.google.gson.JsonObject; +import emu.grasscutter.Grasscutter; + +import java.io.FileReader; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Locale; + +import static emu.grasscutter.Grasscutter.config; + +/** + * *when your JVM fails* + */ +public class ConfigContainer { + private static int version() { + return 1; + } + + /** + * Attempts to update the server's existing configuration to the latest + */ + public static void updateConfig() { + try { // Check if the server is using a legacy config. + JsonObject configObject = Grasscutter.getGsonFactory() + .fromJson(new FileReader(Grasscutter.configFile), JsonObject.class); + if(!configObject.has("version")) { + Grasscutter.getLogger().info("Updating legacy .."); + Grasscutter.saveConfig(null); + } + } catch (Exception ignored) { } + + var existing = config.version; + var latest = version(); + + if(existing == latest) + return; + + // Create a new configuration instance. + ConfigContainer updated = new ConfigContainer(); + // Update all configuration fields. + Field[] fields = ConfigContainer.class.getDeclaredFields(); + Arrays.stream(fields).forEach(field -> { + try { + field.set(updated, field.get(config)); + } catch (Exception exception) { + Grasscutter.getLogger().error("Failed to update a configuration field.", exception); + } + }); updated.version = version(); + + try { // Save configuration & reload. + Grasscutter.saveConfig(updated); + Grasscutter.loadConfig(); + } catch (Exception exception) { + Grasscutter.getLogger().warn("Failed to inject the updated ", exception); + } + } + + public Structure folderStructure = new Structure(); + public Database databaseInfo = new Database(); + public Language language = new Language(); + public Account account = new Account(); + public Server server = new Server(); + + // DO NOT. TOUCH. THE VERSION NUMBER. + public int version = version(); + + /* Option containers. */ + + public static class Database { + public String connectionUri = "mongodb://localhost:27017"; + public String collection = "grasscutter"; + } + + public static class Structure { + public String resources = "./resources/"; + public String data = "./data/"; + public String packets = "./packets/"; + public String keys = "./keys/"; + public String scripts = "./resources/scripts/"; + public String plugins = "./plugins/"; + + // UNUSED (potentially added later?) + // public String dumps = "./dumps/"; + } + + public static class Server { + public Grasscutter.ServerDebugMode debugLevel = Grasscutter.ServerDebugMode.NONE; + public Grasscutter.ServerRunMode runMode = Grasscutter.ServerRunMode.HYBRID; + + public Dispatch dispatch = new Dispatch(); + public Game game = new Game(); + } + + public static class Language { + public Locale language = Locale.getDefault(); + public Locale fallback = Locale.US; + } + + public static class Account { + public boolean autoCreate = false; + public String[] defaultPermissions = {}; + } + + /* Server options. */ + + public static class Dispatch { + public String bindAddress = "0.0.0.0"; + /* This is the address used in URLs. */ + public String accessAddress = "127.0.0.1"; + + public int bindPort = 443; + /* This is the port used in URLs. */ + public int accessPort = 443; + + public Encryption encryption = new Encryption(); + public Policies policies = new Policies(); + public Region[] regions = {}; + + public String defaultName = "Grasscutter"; + } + + public static class Game { + public String bindAddress = "0.0.0.0"; + /* This is the address used in the default region. */ + public String accessAddress = "127.0.0.1"; + + public int bindPort = 443; + /* This is the port used in the default region. */ + public int accessPort = 443; + + public GameOptions gameOptions = new GameOptions(); + public JoinOptions joinOptions = new JoinOptions(); + public ConsoleAccount serverAccount = new ConsoleAccount(); + } + + /* Data containers. */ + + public static class Encryption { + public boolean useEncryption = true; + /* Should 'https' be appended to URLs? */ + public boolean useInRouting = true; + public String keystore = "./keystore.p12"; + public String keystorePassword = "123456"; + } + + public static class Policies { + public Policies.CORS cors = new Policies.CORS(); + + public static class CORS { + public boolean enabled = false; + public String[] allowedOrigins = new String[]{"*"}; + } + } + + public static class GameOptions { + public GameOptions.InventoryLimits inventoryLimits = new GameOptions.InventoryLimits(); + public GameOptions.AvatarLimits avatarLimits = new GameOptions.AvatarLimits(); + public int worldEntityLimit = 1000; // Unenforced. TODO: Implement. + + public boolean watchGachaConfig = false; + public boolean enableShopItems = true; + public boolean staminaUsage = true; + public GameOptions.Rates rates = new GameOptions.Rates(); + + public Database databaseInfo = new Database(); + + public static class InventoryLimits { + public int weapons = 2000; + public int relics = 2000; + public int materials = 2000; + public int furniture = 2000; + public int all = 30000; + } + + public static class AvatarLimits { + public int singlePlayerTeam = 4; + public int multiplayerTeam = 4; + } + + public static class Rates { + public float adventureExp = 1.0f; + public float mora = 1.0f; + public float leyLines = 1.0f; + } + } + + public static class JoinOptions { + public int[] welcomeEmotes = {2007, 1002, 4010}; + public String welcomeMessage = "Welcome to a Grasscutter server."; + public JoinOptions.Mail welcomeMail = new JoinOptions.Mail(); + + public static class Mail { + public String title = "Welcome to Grasscutter!"; + public String content = """ + Hi there!\r + First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! \r + \r + Check out our:\r + + """; + public String sender = "Lawnmower"; + public emu.grasscutter.game.mail.Mail.MailItem[] items = { + new emu.grasscutter.game.mail.Mail.MailItem(13509, 1, 1), + new emu.grasscutter.game.mail.Mail.MailItem(201, 99999, 1) + }; + } + } + + public static class ConsoleAccount { + public int avatarId = 10000007; + public int nameCardId = 210001; + public int adventureRank = 1; + public int worldLevel = 0; + + public String nickName = "Server"; + public String signature = "Welcome to Grasscutter!"; + } + + /* Objects. */ + + public static class Region { + public String Name = "os_usa"; + public String Title = "Grasscutter"; + public String Ip = "127.0.0.1"; + public int Port = 22102; + } +} diff --git a/src/main/java/emu/grasscutter/utils/Utils.java b/src/main/java/emu/grasscutter/utils/Utils.java index 6b8365170..4af62bfb4 100644 --- a/src/main/java/emu/grasscutter/utils/Utils.java +++ b/src/main/java/emu/grasscutter/utils/Utils.java @@ -11,7 +11,6 @@ import java.util.Map; import java.util.Random; import java.util.Locale; -import emu.grasscutter.Configuration; import emu.grasscutter.Grasscutter; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; @@ -175,7 +174,7 @@ public final class Utils { * Checks for required files and folders before startup. */ public static void startupCheck() { - Configuration config = Grasscutter.getConfig(); + ConfigContainer config = Grasscutter.getConfig(); Logger logger = Grasscutter.getLogger(); boolean exit = false;