From a8bf6357aab10e6206f5462cb46f2b65fb143643 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Tue, 12 Mar 2019 04:21:46 -0700 Subject: [PATCH] Fixed the loading of the Serialized configs --- .../gmail/nossr50/config/ConfigManager.java | 7 +- .../config/hocon/SerializedConfigLoader.java | 224 ++++++++++++++++++ .../config/hocon/database/ConfigDatabase.java | 32 +-- .../database/UserConfigSectionDatabase.java | 3 - .../database/UserConfigSectionMySQL.java | 12 +- .../database/UserConfigSectionServer.java | 19 +- 6 files changed, 247 insertions(+), 50 deletions(-) create mode 100644 src/main/java/com/gmail/nossr50/config/hocon/SerializedConfigLoader.java diff --git a/src/main/java/com/gmail/nossr50/config/ConfigManager.java b/src/main/java/com/gmail/nossr50/config/ConfigManager.java index 0210d9fe8..ddb829fc3 100644 --- a/src/main/java/com/gmail/nossr50/config/ConfigManager.java +++ b/src/main/java/com/gmail/nossr50/config/ConfigManager.java @@ -3,6 +3,7 @@ package com.gmail.nossr50.config; import com.gmail.nossr50.config.collectionconfigs.RepairConfig; import com.gmail.nossr50.config.collectionconfigs.SalvageConfig; import com.gmail.nossr50.config.experience.ExperienceConfig; +import com.gmail.nossr50.config.hocon.SerializedConfigLoader; import com.gmail.nossr50.config.hocon.database.ConfigDatabase; import com.gmail.nossr50.config.party.ItemWeightConfig; import com.gmail.nossr50.config.skills.alchemy.PotionConfig; @@ -59,7 +60,7 @@ public final class ConfigManager { /* CONFIG INSTANCES */ - private ConfigDatabase configDatabase; + private SerializedConfigLoader configDatabase; private MainConfig mainConfig; private FishingTreasureConfig fishingTreasureConfig; private ExcavationTreasureConfig excavationTreasureConfig; @@ -90,7 +91,7 @@ public final class ConfigManager { // I'm pretty these are supposed to be done in a specific order, so don't rearrange them willy nilly //TODO: Not sure about the order of MainConfig - configDatabase = new ConfigDatabase(); + configDatabase = new SerializedConfigLoader<>(ConfigDatabase.class, "database_settings.conf", null); mainConfig = new MainConfig(); fishingTreasureConfig = new FishingTreasureConfig(); @@ -304,5 +305,5 @@ public final class ConfigManager { return experienceMapManager; } - public ConfigDatabase getConfigDatabase() { return configDatabase; } + public ConfigDatabase getConfigDatabase() { return configDatabase.getConfig(); } } diff --git a/src/main/java/com/gmail/nossr50/config/hocon/SerializedConfigLoader.java b/src/main/java/com/gmail/nossr50/config/hocon/SerializedConfigLoader.java new file mode 100644 index 000000000..9e174ba31 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/config/hocon/SerializedConfigLoader.java @@ -0,0 +1,224 @@ +package com.gmail.nossr50.config.hocon; + +import com.gmail.nossr50.config.ConfigConstants; +import com.gmail.nossr50.mcMMO; +import ninja.leaping.configurate.ConfigurationOptions; +import ninja.leaping.configurate.ValueType; +import ninja.leaping.configurate.commented.CommentedConfigurationNode; +import ninja.leaping.configurate.commented.SimpleCommentedConfigurationNode; +import ninja.leaping.configurate.hocon.HoconConfigurationLoader; +import ninja.leaping.configurate.objectmapping.ObjectMapper; +import ninja.leaping.configurate.objectmapping.ObjectMappingException; +import ninja.leaping.configurate.util.ConfigurationNodeWalker; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Iterator; +import java.util.Objects; + +/* + * This file is part of GriefPrevention, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * The code here has been modified from its source + */ + +/** + * Handles loading serialized configs with configurate + * @param the class type of the config + */ +public class SerializedConfigLoader { + private static final String CONFIG_HEADER = "Configuration files are now in the HOCON file format!\n" + + "\nHOCON is a lot less strict than YAML, so don't worry about the number of spaces and such!\n" + + "\nIt is recommended that you use a nice text editor to view and edit these files" + + "\n On Windows I recommend VS Code (Free by Microsoft) https://code.visualstudio.com/" + + "\n On Linux I recommend nvim (Free) https://neovim.io/\n" + + "\nIf you need help with the configuration files, feel free to come ask for support in our discord!" + + "\nOfficial mcMMO Discord - https://discord.gg/bJ7pFS9\n" + + "\nYou can also consult the new official wiki" + + "\nhttps://mcmmo.org/wiki - Keep in mind the wiki is a WIP and may not have information about everything in mcMMO!"; + + private static final ConfigurationOptions LOADER_OPTIONS = ConfigurationOptions.defaults().setHeader(CONFIG_HEADER); + + private static final String ROOT_NODE_ADDRESS = "mcMMO"; + + private final Path path; + + /** + * The parent configuration - values are inherited from this + */ + private final SerializedConfigLoader parent; + + /** + * The loader (mapped to a file) used to read/write the config to disk + */ + private HoconConfigurationLoader loader; + + /** + * A node representation of "whats actually in the file". + */ + private CommentedConfigurationNode fileData = SimpleCommentedConfigurationNode.root(LOADER_OPTIONS); + + /** + * A node representation of {@link #fileData}, merged with the data of {@link #parent}. + */ + private CommentedConfigurationNode data = SimpleCommentedConfigurationNode.root(LOADER_OPTIONS); + + /** + * The mapper instance used to populate the config instance + */ + private ObjectMapper.BoundInstance configMapper; + + public SerializedConfigLoader(Class clazz, String fileName, SerializedConfigLoader parent) { + this.parent = parent; + this.path = getPathFromFileName(fileName); + + try { + Files.createDirectories(path.getParent()); + if (Files.notExists(path)) { + Files.createFile(path); + } + + this.loader = HoconConfigurationLoader.builder().setPath(path).build(); + this.configMapper = ObjectMapper.forClass(clazz).bindToNew(); + + reload(); + save(); + } catch (Exception e) { + mcMMO.p.getLogger().severe("Failed to initialize config - "+path.toString()); + e.printStackTrace(); + } + } + + private Path getPathFromFileName(String fileName) + { + File configFile = new File(ConfigConstants.getConfigFolder(), fileName); + return configFile.toPath(); + } + + public T getConfig() { + return this.configMapper.getInstance(); + } + + public boolean save() { + try { + // save from the mapped object --> node + CommentedConfigurationNode saveNode = SimpleCommentedConfigurationNode.root(LOADER_OPTIONS); + this.configMapper.serialize(saveNode.getNode(ROOT_NODE_ADDRESS)); + + // before saving this config, remove any values already declared with the same value on the parent + if (this.parent != null) { + removeDuplicates(saveNode); + } + + // merge the values we need to write with the ones already declared in the file + saveNode.mergeValuesFrom(this.fileData); + + // save the data to disk + this.loader.save(saveNode); + return true; + } catch (IOException | ObjectMappingException e) { + mcMMO.p.getLogger().severe("Failed to save configuration - "+path.toString()); + e.printStackTrace(); + return false; + } + } + + public void reload() { + try { + // load settings from file + CommentedConfigurationNode loadedNode = this.loader.load(); + + // store "what's in the file" separately in memory + this.fileData = loadedNode; + + // make a copy of the file data + this.data = this.fileData.copy(); + + // merge with settings from parent + if (this.parent != null) { + this.parent.reload(); + this.data.mergeValuesFrom(this.parent.data); + } + + // populate the config object + populateInstance(); + } catch (Exception e) { + mcMMO.p.getLogger().severe("Failed to load configuration - "+path.toString()); + e.printStackTrace(); + } + } + + private void populateInstance() throws ObjectMappingException { + this.configMapper.populate(this.data.getNode(ROOT_NODE_ADDRESS)); + } + + /** + * Traverses the given {@code root} config node, removing any values which + * are also present and set to the same value on this configs "parent". + * + * @param root The node to process + */ + private void removeDuplicates(CommentedConfigurationNode root) { + if (this.parent == null) { + throw new IllegalStateException("parent is null"); + } + + Iterator> it = ConfigurationNodeWalker.DEPTH_FIRST_POST_ORDER.walkWithPath(root); + while (it.hasNext()) { + ConfigurationNodeWalker.VisitedNode next = it.next(); + CommentedConfigurationNode node = next.getNode(); + + // remove empty maps + if (node.hasMapChildren()) { + if (node.getChildrenMap().isEmpty()) { + node.setValue(null); + } + continue; + } + + // ignore list values + if (node.getParent() != null && node.getParent().getValueType() == ValueType.LIST) { + continue; + } + + // if the node already exists in the parent config, remove it + CommentedConfigurationNode parentValue = this.parent.data.getNode(next.getPath().getArray()); + if (Objects.equals(node.getValue(), parentValue.getValue())) { + node.setValue(null); + } + } + } + + public CommentedConfigurationNode getRootNode() { + return this.data.getNode(ROOT_NODE_ADDRESS); + } + + public Path getPath() { + return this.path; + } +} diff --git a/src/main/java/com/gmail/nossr50/config/hocon/database/ConfigDatabase.java b/src/main/java/com/gmail/nossr50/config/hocon/database/ConfigDatabase.java index 38140247e..24b1c7f4a 100644 --- a/src/main/java/com/gmail/nossr50/config/hocon/database/ConfigDatabase.java +++ b/src/main/java/com/gmail/nossr50/config/hocon/database/ConfigDatabase.java @@ -1,45 +1,17 @@ package com.gmail.nossr50.config.hocon.database; -import com.gmail.nossr50.config.Config; -import com.gmail.nossr50.config.ConfigConstants; import ninja.leaping.configurate.objectmapping.Setting; import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable; @ConfigSerializable -public class ConfigDatabase extends Config { - - public ConfigDatabase() { - super("mysql", ConfigConstants.getDataFolder(), ConfigConstants.RELATIVE_PATH_CONFIG_DIR, - true,true, false, true); - - initFullConfig(); //Load Config - } +public class ConfigDatabase { /* * CONFIG NODES */ @Setting(value = "MySQL", comment = "Settings for using MySQL or MariaDB database") - private UserConfigSectionMySQL userConfigSectionMySQL; - - /* - * CLASS OVERRIDES - */ - - @Override - public void unload() { - - } - - /** - * The version of this config - * - * @return - */ - @Override - public double getConfigVersion() { - return 1; - } + private UserConfigSectionMySQL userConfigSectionMySQL = new UserConfigSectionMySQL(); /* * GETTER BOILERPLATE diff --git a/src/main/java/com/gmail/nossr50/config/hocon/database/UserConfigSectionDatabase.java b/src/main/java/com/gmail/nossr50/config/hocon/database/UserConfigSectionDatabase.java index b8e8889e7..7cd5b05d7 100644 --- a/src/main/java/com/gmail/nossr50/config/hocon/database/UserConfigSectionDatabase.java +++ b/src/main/java/com/gmail/nossr50/config/hocon/database/UserConfigSectionDatabase.java @@ -1,6 +1,5 @@ package com.gmail.nossr50.config.hocon.database; -import com.gmail.nossr50.config.hocon.ConfigSection; import ninja.leaping.configurate.objectmapping.Setting; import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable; @@ -13,8 +12,6 @@ public class UserConfigSectionDatabase { @Setting(value = "Table_Prefix", comment = "The Prefix that will be used for tables in your DB") private String tablePrefix = "mcmmo_"; - - /* * GETTER BOILERPLATE */ diff --git a/src/main/java/com/gmail/nossr50/config/hocon/database/UserConfigSectionMySQL.java b/src/main/java/com/gmail/nossr50/config/hocon/database/UserConfigSectionMySQL.java index 17fc7a2ff..54ff7ba5b 100644 --- a/src/main/java/com/gmail/nossr50/config/hocon/database/UserConfigSectionMySQL.java +++ b/src/main/java/com/gmail/nossr50/config/hocon/database/UserConfigSectionMySQL.java @@ -11,13 +11,13 @@ public class UserConfigSectionMySQL { private boolean enabled = true; @Setting(value = "User", comment = "Your MySQL User Settings") - private UserConfigSectionUser userConfigSectionUser; + private UserConfigSectionUser userConfigSectionUser = new UserConfigSectionUser(); @Setting(value = "Database", comment = "Database settings for MySQL/MariaDB") - private UserConfigSectionDatabase userConfigSectionDatabase; + private UserConfigSectionDatabase userConfigSectionDatabase = new UserConfigSectionDatabase(); @Setting(value = "Server", comment = "Your MySQL/MariaDB server settings.") - private UserConfigSectionServer userConfigSectionServer; + private UserConfigSectionServer userConfigSectionServer = new UserConfigSectionServer(); /* * GETTER BOILERPLATE @@ -59,11 +59,11 @@ public class UserConfigSectionMySQL { switch (poolIdentifier) { case LOAD: - return userConfigSectionServer.getUserConfigSectionMaxPoolSize().getLoad(); + return userConfigSectionServer.getUserConfigSectionMaxConnections().getLoad(); case SAVE: - return userConfigSectionServer.getUserConfigSectionMaxPoolSize().getSave(); + return userConfigSectionServer.getUserConfigSectionMaxConnections().getSave(); case MISC: - return userConfigSectionServer.getUserConfigSectionMaxPoolSize().getMisc(); + return userConfigSectionServer.getUserConfigSectionMaxConnections().getMisc(); default: return 20; } diff --git a/src/main/java/com/gmail/nossr50/config/hocon/database/UserConfigSectionServer.java b/src/main/java/com/gmail/nossr50/config/hocon/database/UserConfigSectionServer.java index 734334561..823f8d5ca 100644 --- a/src/main/java/com/gmail/nossr50/config/hocon/database/UserConfigSectionServer.java +++ b/src/main/java/com/gmail/nossr50/config/hocon/database/UserConfigSectionServer.java @@ -6,22 +6,25 @@ import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable; @ConfigSerializable public class UserConfigSectionServer { - @Setting(value = "Use_SSL", comment = "Enables SSL for MySQL/MariaDB connections, newer versions of MySQL will spam your console if you aren't using SSL." + - " It is recommended that you turn this on if you are using a newer version of MySQL," + - " if you run into issues with SSL not being supported, turn this off.") + @Setting(value = "Use_SSL", comment = "Enables SSL for MySQL/MariaDB connections." + + "\nIf your SQL server supports SSL, it is recommended to have it on but not necessary." + + "\nIf you run into any issues involving SSL, its best to just turn this off.") private boolean useSSL = true; - @Setting(value = "Server_Port", comment = "Your MySQL/MariaDB server port") + @Setting(value = "Server_Port", comment = "Your MySQL/MariaDB server port" + + "\nThe default port is typically 3306 for MySQL, but every server configuration is different!") private int serverPort = 3306; - @Setting(value = "Server_Address", comment = "The address for your MySQL/MariaDB server") + @Setting(value = "Server_Address", comment = "The address for your MySQL/MariaDB server" + + "If the MySQL server is hosted on the same machine, you can use the localhost alias") private String serverAddress = "localhost"; - @Setting(value = "Max_Connections", comment = "This setting is the max simultaneous MySQL/MariaDB connections allowed at a time, this needs to be high enough to support multiple player logins in quick succession") - private UserConfigSectionMaxConnections userConfigSectionMaxConnections; + @Setting(value = "Max_Connections", comment = "This setting is the max simultaneous MySQL/MariaDB connections allowed at a time." + + "\nThis needs to be high enough to support multiple player logins in quick succession, it is recommended that you do not lower these values") + private UserConfigSectionMaxConnections userConfigSectionMaxConnections = new UserConfigSectionMaxConnections(); @Setting(value = "Max_Pool_Size", comment = "This setting is the max size of the pool of cached connections that we hold at any given time.") - private UserConfigSectionMaxPoolSize userConfigSectionMaxPoolSize; + private UserConfigSectionMaxPoolSize userConfigSectionMaxPoolSize = new UserConfigSectionMaxPoolSize(); /* * GETTER BOILERPLATE