diff --git a/pom.xml b/pom.xml index 65b6d6140..70e6f096a 100755 --- a/pom.xml +++ b/pom.xml @@ -99,6 +99,8 @@ org.apache.tomcat:tomcat-jdbc org.apache.tomcat:tomcat-juli org.bstats:bstats-bukkit + org.spongepowered:configurate-yaml + org.spongepowered:configurate-core diff --git a/src/main/java/com/gmail/nossr50/config/ConfigLoaderConfigurable.java b/src/main/java/com/gmail/nossr50/config/ConfigLoaderConfigurable.java new file mode 100644 index 000000000..4d0262732 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/config/ConfigLoaderConfigurable.java @@ -0,0 +1,251 @@ +package com.gmail.nossr50.config; + +import com.gmail.nossr50.mcMMO; +import com.google.common.io.Files; +import ninja.leaping.configurate.ConfigurationNode; +import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable; +import ninja.leaping.configurate.yaml.YAMLConfigurationLoader; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +/** + * Handles loading and cacheing configuration settings from a configurable compatible config file + */ +@ConfigSerializable +public abstract class ConfigLoaderConfigurable { + + /* PATH VARS */ + + public final File DIRECTORY_DATA_FOLDER; //Directory that the file is in + public final String FILE_RELATIVE_PATH; //Relative Path to the file + protected final String DIRECTORY_DEFAULTS = "defaults"; + + /* LOADERS */ + + private YAMLConfigurationLoader defaultCopyLoader; + private YAMLConfigurationLoader userCopyLoader; + + /* CONFIG FILES */ + + private File resourceConfigCopy; //Copy of the default config from the JAR (file is copied so that admins can easily compare to defaults) + private File resourceUserCopy; //File in the /$MCMMO_ROOT/mcMMO/ directory that may contain user edited settings + + /* ROOT NODES */ + + private ConfigurationNode rootNode = null; + private ConfigurationNode defaultRootNode = null; + + //TODO: Needed? + //private ConfigurationLoader configManager; + + public ConfigLoaderConfigurable(File pathToDataFolder, String relativePath) { + /* + * These must be at the top + */ + mkdirDefaults(); // Make our default config dir + DIRECTORY_DATA_FOLDER = pathToDataFolder; //Data Folder for our plugin + FILE_RELATIVE_PATH = relativePath; //Relative path to config from a parent folder + + //Attempt IO Operations + try { + //Makes sure we have valid Files corresponding to this config + initConfigFiles(); + + //Init Config Loaders + initConfigLoaders(); + + //Load Config Nodes + loadConfig(); + + //Attempt to update user file, and then load it into memory + readConfig(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Initializes the default copy File and the user config File + * @throws IOException + */ + private void initConfigFiles() throws IOException { + //Init our config copy + resourceConfigCopy = initDefaultConfig(); + + //Init the user file + resourceUserCopy = initUserConfig(); + } + + /** + * Loads the root node for the default config File and user config File + */ + private void loadConfig() + { + try { + final ConfigurationNode defaultConfig = this.defaultCopyLoader.load(); + defaultRootNode = defaultConfig; + + final ConfigurationNode userConfig = this.userCopyLoader.load(); + rootNode = userConfig; + + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Initializes the YAMLConfigurationLoaders for this config + */ + private void initConfigLoaders() + { + this.defaultCopyLoader = YAMLConfigurationLoader.builder().setPath(resourceConfigCopy.toPath()).build(); + this.userCopyLoader = YAMLConfigurationLoader.builder().setPath(resourceUserCopy.toPath()).build(); + } + + /** + * Copies a new file from the JAR to the defaults directory and uses that new file to initialize our resourceConfigCopy + * @see ConfigLoaderConfigurable#resourceConfigCopy + * @throws IOException + */ + private File initDefaultConfig() throws IOException { + return copyDefaultFromJar(getDefaultConfigCopyRelativePath(), true); + } + + /** + * Attemps to load the config file if it exists, if it doesn't it copies a new one from within the JAR + * @return user config File + * @see ConfigLoaderConfigurable#resourceUserCopy + * @throws IOException + */ + private File initUserConfig() throws IOException { + File userCopy = new File(DIRECTORY_DATA_FOLDER, FILE_RELATIVE_PATH); //Load the user file; + + if(userCopy.exists()) + { + // Yay + return userCopy; + } + else + { + //If it's gone we copy default files + //Note that we don't copy the values from the default copy put in /defaults/ that file exists only as a reference to admins and is unreliable + return copyDefaultFromJar(FILE_RELATIVE_PATH, false); + } + } + + /** + * Used to make a new config file at a specified relative output path inside the data directory by copying the matching file found in that same relative path within the JAR + * @param relativeOutputPath the path to the output file + * @param deleteOld whether or not to delete the existing output file on disk + * @return a copy of the default config within the JAR + * @throws IOException + */ + private File copyDefaultFromJar(String relativeOutputPath, boolean deleteOld) throws IOException + { + /* + * Gen a Default config from inside the JAR + */ + mcMMO.p.getLogger().info("Preparing to copy internal resource file (in JAR) - "+FILE_RELATIVE_PATH); + InputStream inputStream = mcMMO.p.getResource(FILE_RELATIVE_PATH); + + byte[] buffer = new byte[inputStream.available()]; + inputStream.read(buffer); + + //This is a copy of the default file, which we will overwrite every time mcMMO loads + File targetFile = new File(DIRECTORY_DATA_FOLDER, relativeOutputPath); + + //Wipe old default file on disk + if (targetFile.exists() && deleteOld) + { + mcMMO.p.getLogger().info("Updating file " + relativeOutputPath); + targetFile.delete(); //Necessary? + } + + if(!targetFile.exists()) + targetFile.createNewFile(); //New File Boys + + Files.write(buffer, targetFile); + mcMMO.p.getLogger().info("Created config file - " + relativeOutputPath); + + inputStream.close(); //Close the input stream + + return targetFile; + } + + /** + * The path to the defaults directory + * @return the path to the defaults directory + */ + private String getDefaultConfigCopyRelativePath() { + return DIRECTORY_DEFAULTS + File.separator + FILE_RELATIVE_PATH; + } + + /** + * Creates the defaults directory + */ + private void mkdirDefaults() { + //Make Default Subdirectory + File defaultsDir = new File (DIRECTORY_DATA_FOLDER, "defaults"); + + if(!defaultsDir.exists()) + defaultsDir.mkdir(); + } + + /** + * Configs are always versioned to the last version of mcMMO + * @return Config Version String (mirrors mcMMO version) + */ + public String getConfigVersion() + { + return mcMMO.p.getDescription().getVersion(); + } + + /** + * Attempts to read the loaded config file + * Config will have any necessary updates applied + * Config will be compared to the default config to see if it is missing any nodes + * Config will have any missing nodes inserted with their default value + */ + public void readConfig() { + mcMMO.p.getLogger().info("Attempting to read " + FILE_RELATIVE_PATH + "."); + + int version = this.rootNode.getNode("ConfigVersion").getInt(); + mcMMO.p.getLogger().info(FILE_RELATIVE_PATH + " version is " + version); + + updateConfig(); + + //Update config version + updateConfigVersion(); + } + + /** + * Compares the users config file to the default and adds any missing nodes and applies any necessary updates + */ + private void updateConfig() + { + for(Object key : defaultRootNode.getChildrenMap().keySet()) + { + + } + } + + /** + * Performs any necessary operations to update this config + */ + private void updateConfigVersion() { + // Set a version for our config + /*this.rootNode.getNode("ConfigVersion").setValue(getConfigVersion()); + mcMMO.p.getLogger().info("Updated config to ["+getConfigVersion()+"] - " + FILE_RELATIVE_PATH);*/ + } + + /** + * Returns the root node of this config + * @return the root node of this config + */ + public ConfigurationNode getRootNode() { + return rootNode; + } +} diff --git a/src/main/java/com/gmail/nossr50/config/ConfigurableTest.java b/src/main/java/com/gmail/nossr50/config/ConfigurableTest.java new file mode 100644 index 000000000..d2c95827a --- /dev/null +++ b/src/main/java/com/gmail/nossr50/config/ConfigurableTest.java @@ -0,0 +1,21 @@ +package com.gmail.nossr50.config; + +import com.gmail.nossr50.mcMMO; + +public class ConfigurableTest extends ConfigLoaderConfigurable { + + public final static String relativePath = "configurabletest.yml"; + private static ConfigurableTest instance; + + public ConfigurableTest() { + super(mcMMO.p.getDataFolder(), relativePath); + } + + public static ConfigurableTest getInstance() { + if(instance == null) + instance = new ConfigurableTest(); + + return instance; + } + +} diff --git a/src/main/java/com/gmail/nossr50/mcMMO.java b/src/main/java/com/gmail/nossr50/mcMMO.java index 365fb124d..d18340113 100644 --- a/src/main/java/com/gmail/nossr50/mcMMO.java +++ b/src/main/java/com/gmail/nossr50/mcMMO.java @@ -414,6 +414,13 @@ public class mcMMO extends JavaPlugin { } private void loadConfigFiles() { + // New Config System + + ConfigurableTest.getInstance(); //Init + + // End New Config System + + // Force the loading of config files TreasureConfig.getInstance(); HiddenConfig.getInstance(); diff --git a/src/main/resources/configurabletest.yml b/src/main/resources/configurabletest.yml new file mode 100644 index 000000000..43a8559f6 --- /dev/null +++ b/src/main/resources/configurabletest.yml @@ -0,0 +1,3 @@ +TheBidoofNotation: + MudkipSquareRoot: 7 + CharmanderDivisor: 2 \ No newline at end of file