diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfigManager.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfigManager.java index 727413ec..fad20d91 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfigManager.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfigManager.java @@ -18,14 +18,18 @@ import java.io.File; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.MemoryConfiguration; import org.bukkit.plugin.Plugin; import fr.neatmonster.nocheatplus.actions.ActionFactory; import fr.neatmonster.nocheatplus.logging.StaticLog; +import fr.neatmonster.nocheatplus.utilities.StringUtil; /** * Central location for everything that's described in the configuration file(s).
@@ -205,6 +209,9 @@ public class ConfigManager { // Try to obtain and parse the global configuration file. final File globalFile = new File(plugin.getDataFolder(), "config.yml"); final ConfigFile defaultConfig = new DefaultConfig(); + final int maxBuildContained = Math.max(DefaultConfig.buildNumber, + defaultConfig.getMaxLastChangedBuildNumber()); + // TODO: Detect changes to the configuration (only save back if necessary.). PathUtils.processPaths(globalFile, "global config", false); final ConfigFile globalConfig = new ConfigFile(); globalConfig.setDefaults(defaultConfig); @@ -215,11 +222,21 @@ public class ConfigManager { // Quick shallow ugly fix: only save back if loading was successful. try { if (globalConfig.getBoolean(ConfPaths.SAVEBACKCONFIG)){ + boolean overrideCreated = false; if (!globalConfig.contains(ConfPaths.CONFIGVERSION_CREATED)){ // Workaround. - globalConfig.set(ConfPaths.CONFIGVERSION_CREATED, DefaultConfig.buildNumber); + overrideCreated = true; } - globalConfig.set(ConfPaths.CONFIGVERSION_SAVED, DefaultConfig.buildNumber); + if (!overrideCreated && globalConfig.getInt(ConfPaths.CONFIGVERSION_CREATED, 0) >= 0 + && ConfigManager.isConfigUpToDate(globalConfig) == null) { + // Workaround: Update the created build number, to not warn on further changes. + overrideCreated = true; + } + globalConfig.set(ConfPaths.CONFIGVERSION_SAVED, maxBuildContained); + if (overrideCreated) { + globalConfig.set(ConfPaths.CONFIGVERSION_CREATED, maxBuildContained); + } + // TODO: Only save back if really changed? globalConfig.save(globalFile); } } catch (final Exception e) { @@ -235,8 +252,8 @@ public class ConfigManager { globalConfig.options().header("This configuration was auto-generated by NoCheatPlus."); globalConfig.options().copyHeader(true); try { - globalConfig.set(ConfPaths.CONFIGVERSION_CREATED, DefaultConfig.buildNumber); - globalConfig.set(ConfPaths.CONFIGVERSION_SAVED, DefaultConfig.buildNumber); + globalConfig.set(ConfPaths.CONFIGVERSION_CREATED, maxBuildContained); + globalConfig.set(ConfPaths.CONFIGVERSION_SAVED, maxBuildContained); globalConfig.save(globalFile); } catch (final Exception e) { StaticLog.logSevere(e); @@ -271,7 +288,9 @@ public class ConfigManager { worldConfig.load(worldFile); newWorldsMap.put(worldEntry.getKey(), worldConfig); try{ - if (worldConfig.getBoolean(ConfPaths.SAVEBACKCONFIG)) worldConfig.save(worldFile); + if (worldConfig.getBoolean(ConfPaths.SAVEBACKCONFIG)) { + worldConfig.save(worldFile); + } } catch (final Exception e){ StaticLog.logSevere("Couldn't save back world-specific configuration for " + worldEntry.getKey() + " (see exception below)."); StaticLog.logSevere(e); @@ -288,6 +307,68 @@ public class ConfigManager { isInitialized = true; } + /** + * + * @param globalConfig + * @return null if everything is fine, a string with a message stating problems otherwise. + */ + public static String isConfigUpToDate(ConfigFile globalConfig) { + Object created_o = globalConfig.get(ConfPaths.CONFIGVERSION_CREATED); + int buildCreated = -1; + if (created_o != null && created_o instanceof Integer) { + buildCreated = ((Integer) created_o).intValue(); + } + // Silence version checking with a value < 0. + if (buildCreated < 0) { + return null; + } + final int maxBuildContained = Math.max(DefaultConfig.buildNumber, + globalConfig.getMaxLastChangedBuildNumber()); + // Legacy build number comparison. + if (buildCreated < DefaultConfig.buildNumber) { + // Potentially outdated Configuration. + return "Your configuration might be outdated.\n" + "Some settings could have changed, you should regenerate it!"; + } + else if (buildCreated > maxBuildContained) { + // Installed an older version of NCP. + return "Your configuration seems to be created by a newer plugin version.\n" + "Some settings could have changed, you should regenerate it!"; + } + // So far so good... test individual paths. + final List problems = new LinkedList(); + final ConfigFile defaultConfig = new DefaultConfig(); + final Map lastChangedBuildNumbers = defaultConfig.getLastChangedBuildNumbers(); + // TODO: Consider some behavior for entire nodes ? + for (final Entry entry : lastChangedBuildNumbers.entrySet()) { + final int defaultBuild = entry.getValue(); + if (defaultBuild <= buildCreated) { + // Ignore, might've been changed on purpose. + continue; + } + final String path = entry.getKey(); + final Object defaultValue = defaultConfig.get(path); + if (defaultValue instanceof ConfigurationSection) { + problems.add("Changed with build " + defaultBuild + ", can not handle entire configuration sections yet: " + path); + continue; + } + final Object currentValue = globalConfig.get(path); + if (currentValue == null || defaultValue == null) { + // To be handled elsewhere (@Moved / whatever). + continue; + } + if (defaultBuild > buildCreated && !defaultValue.equals(currentValue)) { + problems.add("Changed with build " + defaultBuild + ": " + path); + continue; + } + } + if (!problems.isEmpty()) { + problems.add(0, "The following configuration default values have changed:"); + problems.add("(Remove/update individual values or set configversion.created to " + maxBuildContained + " to ignore all, then reload the configuration with the 'ncp reload' command.)"); + return StringUtil.join(problems, "\n"); + } + // No errors could be determined (or versions coudl not be determined): ignore. + return null; + } + /** * Informal test if the init method completed (no details are reflected). * @return diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/RawConfigFile.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/RawConfigFile.java index 4f6ca79e..2852513e 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/RawConfigFile.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/RawConfigFile.java @@ -346,8 +346,14 @@ public class RawConfigFile extends YamlConfiguration { * * @param path * @param value + * Must be greater than zero. + * @throws IllegalArgumentException + * If the value is equal to or smaller than zero. */ public void setLastChangedBuildNumber(String path, int value) { + if (value <= 0) { + throw new IllegalArgumentException("Build number must be greater than zero. Got " + value + " for path: " + path); + } lastChangedBuildNumbers.put(path, value); } @@ -363,4 +369,20 @@ public class RawConfigFile extends YamlConfiguration { return lastChangedBuildNumbers; } + /** + * Get the maximum of all last-changed-build-number values, stored for + * individual paths (default to 0). + * + * @return + */ + public int getMaxLastChangedBuildNumber() { + int max = 0; + for (Integer v : lastChangedBuildNumbers.values()) { + if (v != null) { + max = Math.max(max, v); + } + } + return max; + } + } diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/NoCheatPlus.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/NoCheatPlus.java index 9c6b6c4c..553e1922 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/NoCheatPlus.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/NoCheatPlus.java @@ -121,7 +121,6 @@ import fr.neatmonster.nocheatplus.players.DataManager; import fr.neatmonster.nocheatplus.players.PlayerData; import fr.neatmonster.nocheatplus.players.PlayerMessageSender; import fr.neatmonster.nocheatplus.stats.Counters; -import fr.neatmonster.nocheatplus.updates.Updates; import fr.neatmonster.nocheatplus.utilities.ColorUtil; import fr.neatmonster.nocheatplus.utilities.OnDemandTickListener; import fr.neatmonster.nocheatplus.utilities.ReflectionUtil; @@ -1194,7 +1193,7 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI { * @param config */ private void setInstanceMembers(final ConfigFile config) { - configProblems = Updates.isConfigUpToDate(config); + configProblems = ConfigManager.isConfigUpToDate(config); useSubscriptions = config.getBoolean(ConfPaths.LOGGING_BACKEND_INGAMECHAT_SUBSCRIPTIONS); clearExemptionsOnJoin = config.getBoolean(ConfPaths.COMPATIBILITY_EXEMPTIONS_REMOVE_JOIN); clearExemptionsOnLeave = config.getBoolean(ConfPaths.COMPATIBILITY_EXEMPTIONS_REMOVE_LEAVE); diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/updates/Updates.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/updates/Updates.java index 2d87d603..87918091 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/updates/Updates.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/updates/Updates.java @@ -14,80 +14,8 @@ */ package fr.neatmonster.nocheatplus.updates; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.bukkit.configuration.ConfigurationSection; - -import fr.neatmonster.nocheatplus.config.ConfPaths; -import fr.neatmonster.nocheatplus.config.ConfigFile; -import fr.neatmonster.nocheatplus.config.DefaultConfig; -import fr.neatmonster.nocheatplus.utilities.StringUtil; - public class Updates { - /** - * - * @param globalConfig - * @return null if everything is fine, a string with a message stating problems otherwise. - */ - public static String isConfigUpToDate(ConfigFile globalConfig) { - Object created_o = globalConfig.get(ConfPaths.CONFIGVERSION_CREATED); - int buildCreated = -1; - if (created_o != null && created_o instanceof Integer) { - buildCreated = ((Integer) created_o).intValue(); - } - if (buildCreated < 0) { - return null; - } - if (buildCreated < DefaultConfig.buildNumber) { - // Potentially outdated Configuration. - return "Your configuration might be outdated.\n" + "Some settings could have changed, you should regenerate it!"; - } - else if (buildCreated > DefaultConfig.buildNumber) { - // Installed an older version of NCP. - return "Your configuration seems to be created by a newer plugin version.\n" + "Some settings could have changed, you should regenerate it!"; - } - // So far so good... test individual paths. - final List problems = new LinkedList(); - final ConfigFile defaultConfig = new DefaultConfig(); - final Map lastChangedBuildNumbers = defaultConfig.getLastChangedBuildNumbers(); - int maxBuild = DefaultConfig.buildNumber; - // TODO: Consider some behavior for entire nodes ? - for (final Entry entry : lastChangedBuildNumbers.entrySet()) { - final int defaultBuild = entry.getValue(); - if (defaultBuild <= buildCreated) { - // Ignore, might've been changed on purpose. - continue; - } - final String path = entry.getKey(); - final Object defaultValue = defaultConfig.get(path); - if (defaultValue instanceof ConfigurationSection) { - problems.add("Changed with build " + defaultBuild + ", can not handle entire configuration sections yet: " + path); - continue; - } - final Object currentValue = globalConfig.get(path); - if (currentValue == null || defaultValue == null) { - // To be handled elsewhere (@Moved / whatever). - continue; - } - if (defaultBuild > buildCreated && !defaultValue.equals(currentValue)) { - problems.add("Changed with build " + defaultBuild + ": " + path); - maxBuild = Math.max(defaultBuild, maxBuild); - continue; - } - } - if (!problems.isEmpty()) { - problems.add(0, "The following configuration default values have changed:"); - problems.add("(Remove/update individual values or set configversion.created to " + maxBuild + " to ignore all, then reload the configuration with the 'ncp reload' command.)"); - return StringUtil.join(problems, "\n"); - } - // No errors could be determined (or versions coudl not be determined): ignore. - return null; - } - /** * To be called from an async task. * @param versionString Current version string (getDescription().getVersion()).