Fix faulty config notifications.

The per-config-path notifications would keep showing up, even if you
removed the paths, then run 'ncp reload', then alter any of them and run
'ncp reload'.

To fix this, the configversion.created value is set to the current
build, if no config warnings are there - which is the same, as what the
notification suggests as an alternative to removing the paths and
running 'ncp reload'.

To do this, isConfigUpToDate had to be moved from Updates to
ConfigManager, which makes more sense anyway. In addition the 'created'
and saved 'values' are set to the biggest thing found, instead of the
prehistoric static value.

Further a negative 'created' value will not be overridden anymore,
allowing to silence the config notifications forever. Not necessarily
recommended for the general case, but it can be useful/necessary with
maintained blueprints, e.g. with administering multiple servers.

One of the next steps will be to remove the DefaultConfig.buildNumber in
favor of setting a build number for each and every path added. All
provided we don't run into nasty issues here.

Another follow up could be to create an extra registry/config log file
and write all the values there, and only print the first 5 in ingame
chat.
This commit is contained in:
asofold 2017-04-12 19:30:09 +02:00
parent 4b9e7c9fe3
commit 3d73a54fcd
4 changed files with 109 additions and 79 deletions

View File

@ -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).<br>
@ -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<String> problems = new LinkedList<String>();
final ConfigFile defaultConfig = new DefaultConfig();
final Map<String, Integer> lastChangedBuildNumbers = defaultConfig.getLastChangedBuildNumbers();
// TODO: Consider some behavior for entire nodes ?
for (final Entry<String, Integer> 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

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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<String> problems = new LinkedList<String>();
final ConfigFile defaultConfig = new DefaultConfig();
final Map<String, Integer> lastChangedBuildNumbers = defaultConfig.getLastChangedBuildNumbers();
int maxBuild = DefaultConfig.buildNumber;
// TODO: Consider some behavior for entire nodes ?
for (final Entry<String, Integer> 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()).