diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/ConfigMigrator.java b/leaf-server/src/main/java/org/dreeam/leaf/config/ConfigMigrator.java new file mode 100644 index 00000000..1e24b38e --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/ConfigMigrator.java @@ -0,0 +1,100 @@ +package org.dreeam.leaf.config; + +import io.github.thatsmusic99.configurationmaster.api.ConfigFile; +import io.github.thatsmusic99.configurationmaster.api.ConfigSection; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +public class ConfigMigrator { + + private static final String BACKUP_PREFIX = "backup-migration-"; + + public void migrate() throws Exception { + File oldConfigFile = new File(LeafConfig.I_CONFIG_FOLDER, LeafConfig.I_GLOBAL_CONFIG_FILE); + if (!oldConfigFile.exists()) { + return; + } + + // Ensure config/leaf directory exists + LeafConfig.createDirectory(LeafConfig.I_LEAF_CONFIG_FOLDER); + + // Load the old config + ConfigFile oldConfig = ConfigFile.loadConfig(oldConfigFile); + + // Create backup + createBackup(oldConfigFile); + + // Create separate config files for each category in config/leaf folder + Map newConfigs = new HashMap<>(); + + for (EnumConfigCategory category : EnumConfigCategory.getCategoryValues()) { + File newConfigFile = new File(LeafConfig.I_LEAF_CONFIG_FOLDER, category.getFileName()); + ConfigFile newConfig = ConfigFile.loadConfig(newConfigFile); + newConfigs.put(category, newConfig); + + // Set up basic structure + newConfig.set("config-version", "3.0"); + newConfig.addComments("config-version", String.format("Leaf %s Config\nGitHub Repo: https://github.com/Winds-Studio/Leaf", category.name())); + } + + // Migrate data from old config to new configs + migrateConfigData(oldConfig, newConfigs); + + // Save all new config files + for (ConfigFile newConfig : newConfigs.values()) { + newConfig.save(); + } + + LeafConfig.LOGGER.info("Config migration completed. Old config backed up."); + LeafConfig.LOGGER.info("New config files created in config/leaf/ folder."); + } + + private void createBackup(File oldConfigFile) throws Exception { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss"); + String timestamp = dateFormat.format(new Date()); + String backupName = BACKUP_PREFIX + timestamp + "-" + oldConfigFile.getName(); + Path backupPath = oldConfigFile.toPath().resolveSibling(backupName); + + Files.copy(oldConfigFile.toPath(), backupPath, StandardCopyOption.REPLACE_EXISTING); + LeafConfig.LOGGER.info("Created backup: {}", backupName); + } + + private void migrateConfigData(ConfigFile oldConfig, Map newConfigs) { + // Iterate through each category and migrate relevant sections + for (EnumConfigCategory category : EnumConfigCategory.getCategoryValues()) { + ConfigFile newConfig = newConfigs.get(category); + String basePath = category.getBaseKeyName(); + + // Get the section from old config + ConfigSection oldSection = oldConfig.getConfigSection(basePath); + if (oldSection != null) { + // Copy all values from the old section to the new config + copySection(oldSection, newConfig, basePath); + LeafConfig.LOGGER.debug("Migrated {} section to {}", basePath, category.getFileName()); + } + } + } + + private void copySection(ConfigSection source, ConfigFile target, String basePath) { + // Get all keys from the source section (shallow keys only) + for (String key : source.getKeys(false)) { + String fullPath = basePath + "." + key; + Object value = source.get(key); + + if (value instanceof ConfigSection) { + // Recursively copy subsections + copySection((ConfigSection) value, target, fullPath); + } else { + // Copy the value + target.set(fullPath, value); + } + } + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/ConfigModules.java b/leaf-server/src/main/java/org/dreeam/leaf/config/ConfigModules.java index 0a030cfa..758f8e5b 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/ConfigModules.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/ConfigModules.java @@ -15,10 +15,32 @@ public abstract class ConfigModules extends LeafConfig { private static final Set MODULES = new HashSet<>(); - public LeafGlobalConfig config; + public LeafCategoryConfig config; + private EnumConfigCategory category; public ConfigModules() { - this.config = LeafConfig.config(); + // Determine category from the module's base path + this.category = determineCategory(); + this.config = LeafConfig.config(this.category); + } + + // Abstract method that modules must implement to specify their base path + public abstract String getBasePath(); + + // Determine category from base path + private EnumConfigCategory determineCategory() { + String basePath = getBasePath(); + for (EnumConfigCategory cat : EnumConfigCategory.getCategoryValues()) { + if (basePath.startsWith(cat.getBaseKeyName())) { + return cat; + } + } + // Fallback to MISC if no category matches + return EnumConfigCategory.MISC; + } + + public EnumConfigCategory getCategory() { + return category; } public static void initModules() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { @@ -38,7 +60,8 @@ public abstract class ConfigModules extends LeafConfig { } if (!enabledExperimentalModules.isEmpty()) { - LeafConfig.LOGGER.warn("You have following experimental module(s) enabled: {}, please proceed with caution!", enabledExperimentalModules.stream().map(f -> f.getDeclaringClass().getSimpleName() + "." + f.getName()).toList()); + LeafConfig.LOGGER.warn("You have following experimental module(s) enabled: {}, please proceed with caution!", + enabledExperimentalModules.stream().map(f -> f.getDeclaringClass().getSimpleName() + "." + f.getName()).toList()); } } @@ -47,11 +70,13 @@ public abstract class ConfigModules extends LeafConfig { module.onPostLoaded(); } - // Save config to disk - try { - LeafConfig.config().saveConfig(); - } catch (Exception e) { - LeafConfig.LOGGER.error("Failed to save config file!", e); + // Save all config files to disk + for (EnumConfigCategory category : EnumConfigCategory.getCategoryValues()) { + try { + LeafConfig.config(category).saveConfig(); + } catch (Exception e) { + LeafConfig.LOGGER.error("Failed to save config file for category {}!", category.name(), e); + } } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/ConfigStatus.java b/leaf-server/src/main/java/org/dreeam/leaf/config/ConfigStatus.java new file mode 100644 index 00000000..da4fda8e --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/ConfigStatus.java @@ -0,0 +1,39 @@ +package org.dreeam.leaf.config; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +public class ConfigStatus { + + public static Map getStatus() { + Map status = new HashMap<>(); + + File oldConfigFile = new File(LeafConfig.I_CONFIG_FOLDER, LeafConfig.I_GLOBAL_CONFIG_FILE); + status.put("oldConfigExists", oldConfigFile.exists()); + status.put("oldConfigFile", LeafConfig.I_GLOBAL_CONFIG_FILE); + + Map categoryFiles = new HashMap<>(); + for (EnumConfigCategory category : EnumConfigCategory.getCategoryValues()) { + File categoryFile = new File(LeafConfig.I_CONFIG_FOLDER, category.getFileName()); + categoryFiles.put(category.getFileName(), categoryFile.exists()); + } + status.put("categoryFiles", categoryFiles); + + boolean needsMigration = oldConfigFile.exists() && !allCategoryFilesExist(); + status.put("needsMigration", needsMigration); + status.put("multiFileSystemReady", allCategoryFilesExist()); + + return status; + } + + private static boolean allCategoryFilesExist() { + for (EnumConfigCategory category : EnumConfigCategory.getCategoryValues()) { + File categoryFile = new File(LeafConfig.I_CONFIG_FOLDER, category.getFileName()); + if (!categoryFile.exists()) { + return false; + } + } + return true; + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/EnumConfigCategory.java b/leaf-server/src/main/java/org/dreeam/leaf/config/EnumConfigCategory.java index ebbaf002..fd4c87a3 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/EnumConfigCategory.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/EnumConfigCategory.java @@ -1,25 +1,40 @@ package org.dreeam.leaf.config; public enum EnumConfigCategory { - ASYNC("async"), - PERF("performance"), - FIXES("fixes"), - GAMEPLAY("gameplay-mechanisms"), - NETWORK("network"), - MISC("misc"); + ASYNC("async", "leaf-async.yml"), + PERF("performance", "leaf-performance.yml"), + FIXES("fixes", "leaf-fixes.yml"), + GAMEPLAY("gameplay-mechanisms", "leaf-gameplay.yml"), + NETWORK("network", "leaf-network.yml"), + MISC("misc", "leaf-misc.yml"); private final String baseKeyName; + private final String fileName; private static final EnumConfigCategory[] VALUES = EnumConfigCategory.values(); - EnumConfigCategory(String baseKeyName) { + EnumConfigCategory(String baseKeyName, String fileName) { this.baseKeyName = baseKeyName; + this.fileName = fileName; } public String getBaseKeyName() { return this.baseKeyName; } + public String getFileName() { + return this.fileName; + } + public static EnumConfigCategory[] getCategoryValues() { return VALUES; } + + public static EnumConfigCategory fromBaseKeyName(String baseKeyName) { + for (EnumConfigCategory category : VALUES) { + if (category.baseKeyName.equals(baseKeyName)) { + return category; + } + } + return null; + } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/LeafCategoryConfig.java b/leaf-server/src/main/java/org/dreeam/leaf/config/LeafCategoryConfig.java new file mode 100644 index 00000000..4a4ade08 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/LeafCategoryConfig.java @@ -0,0 +1,211 @@ +package org.dreeam.leaf.config; + +import io.github.thatsmusic99.configurationmaster.api.ConfigFile; +import io.github.thatsmusic99.configurationmaster.api.ConfigSection; + +import java.io.File; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class LeafCategoryConfig { + + private static final String CURRENT_VERSION = "3.0"; + private static final String CURRENT_REGION = Locale.getDefault().getCountry().toUpperCase(Locale.ROOT); + private static final boolean isCN = CURRENT_REGION.equals("CN"); + + private final EnumConfigCategory category; + private ConfigFile configFile; + + public LeafCategoryConfig(EnumConfigCategory category, boolean init) throws Exception { + this.category = category; + this.configFile = ConfigFile.loadConfig(new File(LeafConfig.I_LEAF_CONFIG_FOLDER, category.getFileName())); + + // Add version and header comments + configFile.set("config-version", CURRENT_VERSION); + configFile.addComments("config-version", pickStringRegionBased( + String.format("Leaf %s Config\nGitHub Repo: https://github.com/Winds-Studio/Leaf\nDiscord: https://discord.com/invite/gfgAwdSEuM", + category.name()), + String.format("Leaf %s Config\nGitHub Repo: https://github.com/Winds-Studio/Leaf\nQQ Group: 619278377", + category.name()) + )); + + // Pre-structure the config + structureConfig(); + } + + protected void structureConfig() { + createTitledSection(category.name(), category.getBaseKeyName()); + } + + public void saveConfig() throws Exception { + configFile.save(); + } + + public EnumConfigCategory getCategory() { + return category; + } + + // Config Utilities + public void createTitledSection(String title, String path) { + configFile.addSection(title); + configFile.addDefault(path, null); + } + + public boolean getBoolean(String path, boolean def, String comment) { + configFile.addDefault(path, def, comment); + return configFile.getBoolean(path, def); + } + + public boolean getBoolean(String path, boolean def) { + configFile.addDefault(path, def); + return configFile.getBoolean(path, def); + } + + public String getString(String path, String def, String comment) { + configFile.addDefault(path, def, comment); + return configFile.getString(path, def); + } + + public String getString(String path, String def) { + configFile.addDefault(path, def); + return configFile.getString(path, def); + } + + public double getDouble(String path, double def, String comment) { + configFile.addDefault(path, def, comment); + return configFile.getDouble(path, def); + } + + public double getDouble(String path, double def) { + configFile.addDefault(path, def); + return configFile.getDouble(path, def); + } + + public int getInt(String path, int def, String comment) { + configFile.addDefault(path, def, comment); + return configFile.getInteger(path, def); + } + + public int getInt(String path, int def) { + configFile.addDefault(path, def); + return configFile.getInteger(path, def); + } + + public long getLong(String path, long def, String comment) { + configFile.addDefault(path, def, comment); + return configFile.getLong(path, def); + } + + public long getLong(String path, long def) { + configFile.addDefault(path, def); + return configFile.getLong(path, def); + } + + public List getList(String path, List def, String comment) { + configFile.addDefault(path, def, comment); + return configFile.getStringList(path); + } + + public List getList(String path, List def) { + configFile.addDefault(path, def); + return configFile.getStringList(path); + } + + public ConfigSection getConfigSection(String path, Map defaultKeyValue, String comment) { + configFile.addDefault(path, null, comment); + configFile.makeSectionLenient(path); + defaultKeyValue.forEach((string, object) -> configFile.addExample(path + "." + string, object)); + return configFile.getConfigSection(path); + } + + public ConfigSection getConfigSection(String path, Map defaultKeyValue) { + configFile.addDefault(path, null); + configFile.makeSectionLenient(path); + defaultKeyValue.forEach((string, object) -> configFile.addExample(path + "." + string, object)); + return configFile.getConfigSection(path); + } + + // Getter methods + public Boolean getBoolean(String path) { + String value = configFile.getString(path, null); + return value == null ? null : Boolean.parseBoolean(value); + } + + public String getString(String path) { + return configFile.getString(path, null); + } + + public Double getDouble(String path) { + String value = configFile.getString(path, null); + if (value == null) { + return null; + } + try { + return Double.parseDouble(value); + } catch (NumberFormatException e) { + LeafConfig.LOGGER.warn("{} is not a valid number, skipped! Please check your configuration.", path, e); + return null; + } + } + + public Integer getInt(String path) { + String value = configFile.getString(path, null); + if (value == null) { + return null; + } + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + LeafConfig.LOGGER.warn("{} is not a valid number, skipped! Please check your configuration.", path, e); + return null; + } + } + + public Long getLong(String path) { + String value = configFile.getString(path, null); + if (value == null) { + return null; + } + try { + return Long.parseLong(value); + } catch (NumberFormatException e) { + LeafConfig.LOGGER.warn("{} is not a valid number, skipped! Please check your configuration.", path, e); + return null; + } + } + + public List getList(String path) { + return configFile.getList(path, null); + } + + public ConfigSection getConfigSection(String path) { + configFile.addDefault(path, null); + configFile.makeSectionLenient(path); + return configFile.getConfigSection(path); + } + + public void addComment(String path, String comment) { + configFile.addComment(path, comment); + } + + public void addCommentIfCN(String path, String comment) { + if (isCN) { + configFile.addComment(path, comment); + } + } + + public void addCommentIfNonCN(String path, String comment) { + if (!isCN) { + configFile.addComment(path, comment); + } + } + + public void addCommentRegionBased(String path, String en, String cn) { + configFile.addComment(path, isCN ? cn : en); + } + + public String pickStringRegionBased(String en, String cn) { + return isCN ? cn : en; + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/LeafConfig.java b/leaf-server/src/main/java/org/dreeam/leaf/config/LeafConfig.java index e525e72f..10f8dcae 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/LeafConfig.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/LeafConfig.java @@ -26,13 +26,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.Enumeration; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -40,21 +34,18 @@ import java.util.jar.JarFile; /* * Yoinked from: https://github.com/xGinko/AnarchyExploitFixes/ & https://github.com/LuminolMC/Luminol * @author: @xGinko & @MrHua269 + * + * Rewritten by @Taiyou */ public class LeafConfig { public static final Logger LOGGER = LogManager.getLogger(LeafConfig.class.getSimpleName()); protected static final File I_CONFIG_FOLDER = new File("config"); + protected static final File I_LEAF_CONFIG_FOLDER = new File(I_CONFIG_FOLDER, "leaf"); protected static final String I_CONFIG_PKG = "org.dreeam.leaf.config.modules"; protected static final String I_GLOBAL_CONFIG_FILE = "leaf-global.yml"; - protected static final String I_LEVEL_CONFIG_FILE = "leaf-world-defaults.yml"; // Leaf TODO - Per level config - private static LeafGlobalConfig leafGlobalConfig; - - //private static int preMajorVer; - private static int preMinorVer; - //private static int currMajorVer; - private static int currMinorVer; + private static Map categoryConfigs = new HashMap<>(); /* Load & Reload */ @@ -84,6 +75,10 @@ public class LeafConfig { LOGGER.info("Loading config..."); purgeOutdated(); + + // Auto-migrate if old config exists + autoMigrateIfNeeded(); + loadConfig(true); LOGGER.info("Successfully loaded config in {}ms.", (System.nanoTime() - begin) / 1_000_000); @@ -95,17 +90,58 @@ public class LeafConfig { /* Load Global Config */ private static void loadConfig(boolean init) throws Exception { - // Create config folder - createDirectory(I_CONFIG_FOLDER); + // Create leaf config folder + createDirectory(I_LEAF_CONFIG_FOLDER); - leafGlobalConfig = new LeafGlobalConfig(init); + // Load all category configs + categoryConfigs.clear(); + for (EnumConfigCategory category : EnumConfigCategory.getCategoryValues()) { + categoryConfigs.put(category, new LeafCategoryConfig(category, init)); + } // Load config modules ConfigModules.initModules(); } - public static LeafGlobalConfig config() { - return leafGlobalConfig; + public static LeafCategoryConfig config(EnumConfigCategory category) { + return categoryConfigs.get(category); + } + + // For backward compatibility - returns misc config + @Deprecated + public static LeafCategoryConfig config() { + return categoryConfigs.get(EnumConfigCategory.MISC); + } + + /* Auto-migration logic */ + + private static void autoMigrateIfNeeded() { + File oldConfigFile = new File(I_CONFIG_FOLDER, I_GLOBAL_CONFIG_FILE); + if (!oldConfigFile.exists()) { + return; // No old config to migrate + } + + // Check if any category files are missing + boolean needsMigration = false; + for (EnumConfigCategory category : EnumConfigCategory.getCategoryValues()) { + File categoryFile = new File(I_LEAF_CONFIG_FOLDER, category.getFileName()); + if (!categoryFile.exists()) { + needsMigration = true; + break; + } + } + + if (needsMigration) { + LOGGER.info("Detected old config file. Automatically migrating to multi-file system..."); + try { + ConfigMigrator migrator = new ConfigMigrator(); + migrator.migrate(); + LOGGER.info("Migration completed successfully!"); + } catch (Exception e) { + LOGGER.error("Auto-migration failed!", e); + throw new RuntimeException("Failed to migrate config", e); + } + } } /* Create config folder */ @@ -203,40 +239,42 @@ public class LeafConfig { } } - // TODO - public static void loadConfigVersion(String preVer, String currVer) { - int currMinor; - int preMinor; - - // First time user - if (preVer == null) { - - } - } - /* Register Spark profiler extra server configurations */ - private static List buildSparkExtraConfigs() { - List extraConfigs = new ArrayList<>(Arrays.asList( - "config/leaf-global.yml", - "config/gale-global.yml", - "config/gale-world-defaults.yml" - )); + public static void regSparkExtraConfig() { + if (GlobalConfiguration.get().spark.enabled || Bukkit.getServer().getPluginManager().getPlugin("spark") != null) { + List extraConfigs = new ArrayList<>(); - @Nullable String existing = System.getProperty("spark.serverconfigs.extra"); - if (existing != null) { - extraConfigs.addAll(Arrays.asList(existing.split(","))); + // Add all category config files from config/leaf folder (individual files) + for (EnumConfigCategory category : EnumConfigCategory.getCategoryValues()) { + extraConfigs.add("config/leaf/" + category.getFileName()); + } + + // Add other configs + extraConfigs.addAll(Arrays.asList( + "config/gale-global.yml", + "config/gale-world-defaults.yml" + )); + + @Nullable String existing = System.getProperty("spark.serverconfigs.extra"); + if (existing != null) { + extraConfigs.addAll(Arrays.asList(existing.split(","))); + } + + // Use same way in spark's BukkitServerConfigProvider#getNestedFiles to get all world configs + // It may spam in the spark profiler, but it's ok, since spark uses YamlConfigParser.INSTANCE to + // get configs defined in extra config flag instead of using SplitYamlConfigParser.INSTANCE + for (World world : Bukkit.getWorlds()) { + Path galeWorldFolder = world.getWorldFolder().toPath().resolve("gale-world.yml"); + extraConfigs.add(galeWorldFolder.toString().replace("\\", "/").replace("./", "")); // Gale world config + } + + String extraConfigsStr = String.join(",", extraConfigs); + String hiddenPaths = String.join(",", buildSparkHiddenPaths()); + + System.setProperty("spark.serverconfigs.extra", extraConfigsStr); + System.setProperty("spark.serverconfigs.hiddenpaths", hiddenPaths); } - - // Use same way in spark's BukkitServerConfigProvider#getNestedFiles to get all world configs - // It may spam in the spark profiler, but it's ok, since spark uses YamlConfigParser.INSTANCE to - // get configs defined in extra config flag instead of using SplitYamlConfigParser.INSTANCE - for (World world : Bukkit.getWorlds()) { - Path galeWorldFolder = world.getWorldFolder().toPath().resolve("gale-world.yml"); - extraConfigs.add(galeWorldFolder.toString().replace("\\", "/").replace("./", "")); // Gale world config - } - - return extraConfigs; } private static List buildSparkHiddenPaths() { @@ -249,16 +287,6 @@ public class LeafConfig { return extraHidden; } - public static void regSparkExtraConfig() { - if (GlobalConfiguration.get().spark.enabled || Bukkit.getServer().getPluginManager().getPlugin("spark") != null) { - String extraConfigs = String.join(",", buildSparkExtraConfigs()); - String hiddenPaths = String.join(",", buildSparkHiddenPaths()); - - System.setProperty("spark.serverconfigs.extra", extraConfigs); - System.setProperty("spark.serverconfigs.hiddenpaths", hiddenPaths); - } - } - /* Purge and backup old Leaf config & Pufferfish config */ private static void purgeOutdated() { @@ -295,7 +323,7 @@ public class LeafConfig { if (foundLegacy) { LOGGER.warn("Found legacy Leaf config files, move to backup directory: {}", backupDir); - LOGGER.warn("New Leaf config located at config/ folder, You need to transfer config to the new one manually and restart the server!"); + LOGGER.warn("New Leaf config located at config/leaf/ folder, You need to transfer config to the new one manually and restart the server!"); } } catch (IOException e) { LOGGER.error("Failed to purge old configs.", e); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/LeafGlobalConfig.java b/leaf-server/src/main/java/org/dreeam/leaf/config/LeafGlobalConfig.java index eba6cdf6..3dbc8ebf 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/LeafGlobalConfig.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/LeafGlobalConfig.java @@ -10,7 +10,7 @@ import java.util.Map; public class LeafGlobalConfig { - private static final String CURRENT_VERSION = "3.0"; + private static final String CURRENT_VERSION = "4.0"; private static final String CURRENT_REGION = Locale.getDefault().getCountry().toUpperCase(Locale.ROOT); // It will be in uppercase by default, just make sure private static final boolean isCN = CURRENT_REGION.equals("CN"); @@ -19,7 +19,6 @@ public class LeafGlobalConfig { public LeafGlobalConfig(boolean init) throws Exception { configFile = ConfigFile.loadConfig(new File(LeafConfig.I_CONFIG_FOLDER, LeafConfig.I_GLOBAL_CONFIG_FILE)); - LeafConfig.loadConfigVersion(getString("config-version"), CURRENT_VERSION); configFile.set("config-version", CURRENT_VERSION); configFile.addComments("config-version", pickStringRegionBased("""