소스 검색

Fixed the loading of the Serialized configs

nossr50 6 년 전
부모
커밋
a8bf6357aa

+ 4 - 3
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> 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(); }
 }

+ 224 - 0
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 <T> the class type of the config
+ */
+public class SerializedConfigLoader<T> {
+    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<T>.BoundInstance configMapper;
+
+    public SerializedConfigLoader(Class<T> 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<ConfigurationNodeWalker.VisitedNode<CommentedConfigurationNode>> it = ConfigurationNodeWalker.DEPTH_FIRST_POST_ORDER.walkWithPath(root);
+        while (it.hasNext()) {
+            ConfigurationNodeWalker.VisitedNode<CommentedConfigurationNode> 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;
+    }
+}

+ 2 - 30
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

+ 0 - 3
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
      */

+ 6 - 6
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;
         }

+ 11 - 8
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