From 6d45a88c0317a6cac33a4efec386f30995b07ada Mon Sep 17 00:00:00 2001 From: asofold Date: Fri, 14 Sep 2012 14:52:15 +0200 Subject: [PATCH] Improve config handling: 1. Fix world configs stored under the wrong name. 2. Add warnings if paths are used in world files that should only or can only be set in the global configuration file. --- .../nocheatplus/config/ConfPaths.java | 15 +- .../nocheatplus/config/ConfigManager.java | 13 +- .../nocheatplus/config/GlobalConfig.java | 19 +++ .../nocheatplus/config/PathUtils.java | 135 ++++++++++++++++++ 4 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 src/fr/neatmonster/nocheatplus/config/GlobalConfig.java create mode 100644 src/fr/neatmonster/nocheatplus/config/PathUtils.java diff --git a/src/fr/neatmonster/nocheatplus/config/ConfPaths.java b/src/fr/neatmonster/nocheatplus/config/ConfPaths.java index 1b08231f..205f7161 100644 --- a/src/fr/neatmonster/nocheatplus/config/ConfPaths.java +++ b/src/fr/neatmonster/nocheatplus/config/ConfPaths.java @@ -23,6 +23,7 @@ public abstract class ConfPaths { * , 88P , 88P , 88P * "8",P" "8",P" "8",P" */ + @GlobalConfig private static final String LOGGING = "logging."; public static final String LOGGING_ACTIVE = LOGGING + "active"; public static final String LOGGING_CONSOLE = LOGGING + "console"; @@ -38,6 +39,7 @@ public abstract class ConfPaths { * d8b Y8b Y8b 888 Y88D Y888 , 888 , 888 888 ,ee 888 888 888 888 , Y888 888P Y888 888P Y88D * d888b Y8b Y8b 888 d,dP "88,e8' "YeeP" 888 888 "88 888 888 888 "YeeP" "88 88" "88 88" d,dP */ + @GlobalConfig private static final String MISCELLANEOUS = "miscellaneous."; public static final String MISCELLANEOUS_ALLOWCLIENTMODS = MISCELLANEOUS + "allowclientmods"; public static final String MISCELLANEOUS_OPINCONSOLEONLY = MISCELLANEOUS + "opinconsoleonly"; @@ -45,7 +47,6 @@ public abstract class ConfPaths { public static final String MISCELLANEOUS_CHECKFORUPDATES = MISCELLANEOUS + "checkforupdates"; public static final String MISCELLANEOUS_READTIMEOUT = MISCELLANEOUS + "readtimeout"; public static final String MISCELLANEOUS_REPORTTOMETRICS = MISCELLANEOUS + "reporttometrics"; - private static final String MISCELLANEOUS_NOMOVEDTOOQUICKLY = MISCELLANEOUS + "nomovedtooquickly."; public static final String MISCELLANEOUS_NOMOVEDTOOQUICKLY_ENABLED = MISCELLANEOUS_NOMOVEDTOOQUICKLY + "enabled"; public static final String MISCELLANEOUS_NOMOVEDTOOQUICKLY_USEPROXY = MISCELLANEOUS_NOMOVEDTOOQUICKLY + "useproxy"; @@ -70,7 +71,9 @@ public abstract class ConfPaths { public static final String BLOCKBREAK_FASTBREAK_DEBUG = BLOCKBREAK_FASTBREAK + "debug"; private static final String BLOCKBREAK_FASTBREAK_BUCKETS = BLOCKBREAK + "buckets."; public static final String BLOCKBREAK_FASTBREAK_BUCKETS_CONTENTION = BLOCKBREAK_FASTBREAK_BUCKETS + "contention"; + @GlobalConfig public static final String BLOCKBREAK_FASTBREAK_BUCKETS_N = BLOCKBREAK_FASTBREAK_BUCKETS + "number"; + @GlobalConfig public static final String BLOCKBREAK_FASTBREAK_BUCKETS_DUR = BLOCKBREAK_FASTBREAK_BUCKETS + "duration"; public static final String BLOCKBREAK_FASTBREAK_BUCKETS_FACTOR = BLOCKBREAK_FASTBREAK_BUCKETS + "factor"; public static final String BLOCKBREAK_FASTBREAK_DELAY = BLOCKBREAK_FASTBREAK + "delay"; @@ -83,8 +86,10 @@ public abstract class ConfPaths { public static final String BLOCKBREAK_FREQUENCY_MOD_CREATIVE = BLOCKBREAK_FREQUENCY + "intervalcreative"; public static final String BLOCKBREAK_FREQUENCY_MOD_SURVIVAL = BLOCKBREAK_FREQUENCY + "intervalsurvival"; private static final String BLOCKBREAK_FREQUENCY_BUCKETS = BLOCKBREAK_FREQUENCY + "buckets."; + @GlobalConfig public static final String BLOCKBREAK_FREQUENCY_BUCKETS_DUR = BLOCKBREAK_FREQUENCY_BUCKETS + "duration"; public static final String BLOCKBREAK_FREQUENCY_BUCKETS_FACTOR = BLOCKBREAK_FREQUENCY_BUCKETS + "factor"; + @GlobalConfig public static final String BLOCKBREAK_FREQUENCY_BUCKETS_N = BLOCKBREAK_FREQUENCY_BUCKETS + "number"; private static final String BLOCKBREAK_FREQUENCY_SHORTTERM = BLOCKBREAK_FREQUENCY + "shortterm."; public static final String BLOCKBREAK_FREQUENCY_SHORTTERM_LIMIT = BLOCKBREAK_FREQUENCY_SHORTTERM + "limit"; @@ -169,6 +174,7 @@ public abstract class ConfPaths { // globalchat private static final String CHAT_GLOBALCHAT = CHAT + "globalchat."; public static final String CHAT_GLOBALCHAT_CHECK = CHAT_GLOBALCHAT + "active"; + public static final String CHAT_GLOBALCHAT_DEBUG = CHAT_GLOBALCHAT + "debug"; public static final String CHAT_GLOBALCHAT_LEVEL = CHAT_GLOBALCHAT + "level"; public static final String CHAT_GLOBALCHAT_ENGINE_MAXIMUM = CHAT_GLOBALCHAT + "maximum"; public static final String CHAT_GLOBALCHAT_FREQUENCY = CHAT_GLOBALCHAT + "frequency."; @@ -180,24 +186,29 @@ public abstract class ConfPaths { private static final String CHAT_GLOBALCHAT_GL = CHAT_GLOBALCHAT + "global."; public static final String CHAT_GLOBALCHAT_GL_CHECK = CHAT_GLOBALCHAT_GL + "active"; public static final String CHAT_GLOBALCHAT_GL_WEIGHT = CHAT_GLOBALCHAT_GL + "weight"; + @GlobalConfig public static final String CHAT_GLOBALCHAT_GL_WORDS = CHAT_GLOBALCHAT_GL + "words."; public static final String CHAT_GLOBALCHAT_GL_WORDS_CHECK = CHAT_GLOBALCHAT_GL_WORDS + "active"; + @GlobalConfig public static final String CHAT_GLOBALCHAT_GL_PREFIXES = CHAT_GLOBALCHAT_GL + "prefixes."; public static final String CHAT_GLOBALCHAT_GL_PREFIXES_CHECK = CHAT_GLOBALCHAT_GL_PREFIXES + "active"; + @GlobalConfig public static final String CHAT_GLOBALCHAT_GL_SIMILARITY = CHAT_GLOBALCHAT_GL + "similarity."; public static final String CHAT_GLOBALCHAT_GL_SIMILARITY_CHECK = CHAT_GLOBALCHAT_GL_SIMILARITY + "active"; // Extended per player checks. private static final String CHAT_GLOBALCHAT_PP = CHAT_GLOBALCHAT + "player."; public static final String CHAT_GLOBALCHAT_PP_CHECK = CHAT_GLOBALCHAT_PP + "active"; public static final String CHAT_GLOBALCHAT_PP_WEIGHT = CHAT_GLOBALCHAT_PP + "weight"; + @GlobalConfig public static final String CHAT_GLOBALCHAT_PP_PREFIXES = CHAT_GLOBALCHAT_PP + "prefixes."; public static final String CHAT_GLOBALCHAT_PP_PREFIXES_CHECK = CHAT_GLOBALCHAT_PP_PREFIXES + "active"; + @GlobalConfig public static final String CHAT_GLOBALCHAT_PP_WORDS = CHAT_GLOBALCHAT_PP + "words."; public static final String CHAT_GLOBALCHAT_PP_WORDS_CHECK = CHAT_GLOBALCHAT_PP_WORDS + "active"; + @GlobalConfig public static final String CHAT_GLOBALCHAT_PP_SIMILARITY = CHAT_GLOBALCHAT_PP + "similarity."; public static final String CHAT_GLOBALCHAT_PP_SIMILARITY_CHECK = CHAT_GLOBALCHAT_PP_SIMILARITY + "active"; // globalchat actions - public static final String CHAT_GLOBALCHAT_DEBUG = CHAT_GLOBALCHAT + "debug"; public static final String CHAT_GLOBALCHAT_ACTIONS = CHAT_GLOBALCHAT + "actions"; // nopwnage diff --git a/src/fr/neatmonster/nocheatplus/config/ConfigManager.java b/src/fr/neatmonster/nocheatplus/config/ConfigManager.java index 413f7350..b9779459 100644 --- a/src/fr/neatmonster/nocheatplus/config/ConfigManager.java +++ b/src/fr/neatmonster/nocheatplus/config/ConfigManager.java @@ -15,6 +15,7 @@ import java.util.logging.LogRecord; import java.util.logging.Logger; import org.bukkit.Bukkit; +import org.bukkit.configuration.MemoryConfiguration; import fr.neatmonster.nocheatplus.NoCheatPlus; import fr.neatmonster.nocheatplus.utilities.CheckUtils; @@ -192,6 +193,8 @@ public class ConfigManager { } CheckUtils.fileLogger = logger; + final MemoryConfiguration worldDefaults = PathUtils.getWorldsDefaultConfig(globalConfig); + // Try to obtain and parse the world-specific configuration files. final HashMap worldFiles = new HashMap(); if (plugin.getDataFolder().isDirectory()) @@ -199,16 +202,18 @@ public class ConfigManager { if (file.isFile()) { final String fileName = file.getName(); if (fileName.matches(".+_config.yml$")) { - final String worldname = fileName.substring(0, fileName.length() - 10); + final String worldname = fileName.substring(0, fileName.length() - 11); worldFiles.put(worldname, file); } } for (final Entry worldEntry : worldFiles.entrySet()) { final File worldFile = worldEntry.getValue(); + PathUtils.warnGlobalOnlyPaths(worldFile, worldEntry.getKey()); final ConfigFile worldConfig = new ConfigFile(); - worldConfig.setDefaults(globalConfig); + worldConfig.setDefaults(worldDefaults); + worldConfig.options().copyDefaults(true); try { - worldConfig.load(worldFile); + worldConfig.load(worldFile); worldsMap.put(worldEntry.getKey(), worldConfig); try{ worldConfig.save(worldFile); @@ -222,6 +227,8 @@ public class ConfigManager { + worldEntry.getKey() + " (see exception below). Continue with global default settings..."); e.printStackTrace(); } + worldConfig.setDefaults(globalConfig); + worldConfig.options().copyDefaults(true); worldConfig.regenerateActionLists(); } } diff --git a/src/fr/neatmonster/nocheatplus/config/GlobalConfig.java b/src/fr/neatmonster/nocheatplus/config/GlobalConfig.java new file mode 100644 index 00000000..d2ac6ee8 --- /dev/null +++ b/src/fr/neatmonster/nocheatplus/config/GlobalConfig.java @@ -0,0 +1,19 @@ +package fr.neatmonster.nocheatplus.config; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to indicate that a config path can only be set in the global configuration file. + * This can be added to parent fields, all other fields whose name starts with the name of the parent field will automatically be global only. + * @author asofold + * + */ +@Documented +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface GlobalConfig { +} diff --git a/src/fr/neatmonster/nocheatplus/config/PathUtils.java b/src/fr/neatmonster/nocheatplus/config/PathUtils.java new file mode 100644 index 00000000..d24e117a --- /dev/null +++ b/src/fr/neatmonster/nocheatplus/config/PathUtils.java @@ -0,0 +1,135 @@ +package fr.neatmonster.nocheatplus.config; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.MemoryConfiguration; + +import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.prefixtree.SimpleCharPrefixTree; + +public class PathUtils { + + /** Field names of ConfPaths. */ + private static final Set globalOnlyFields = new HashSet(); + + /** Paths of ConfPaths (field values). */ + private static final Set globalOnlyPaths = new LinkedHashSet(); + + private static final SimpleCharPrefixTree globalOnlyPrefixes = new SimpleCharPrefixTree(); + + static{ + initGlobalOnlyPaths(); + } + + /** + * Test/evaluation of using annotations to confine config paths to the global config. + * @return + */ + private static void initGlobalOnlyPaths(){ + globalOnlyFields.clear(); + globalOnlyPaths.clear(); + globalOnlyPrefixes.clear(); + for (final Field field : ConfPaths.class.getDeclaredFields()){ + final String name = field.getName(); + if (field.isAnnotationPresent(GlobalConfig.class)){ + globalOnlyFields.add(name); + addGlobalOnlyPath(field); + } + else{ + for (final String refName : globalOnlyFields){ + if (name.startsWith(refName) && field.getType() == String.class){ + addGlobalOnlyPath(field); + } + } + } + } + } + + private static void addGlobalOnlyPath(final Field field) { + try { + final String path = field.get(null).toString(); + globalOnlyPaths.add(path); + globalOnlyPrefixes.feed(path); + } catch (IllegalArgumentException e) { + } catch (IllegalAccessException e) { + } + } + + /** + * Warn on the console if paths are used. + * @param config + * @param paths + * @param msgHeader + */ + public static void warnPaths(ConfigFile config, Collection paths, String msgPrefix){ + final Logger log = Bukkit.getLogger(); + for (final String path : config.getKeys(true)){ + if (globalOnlyPrefixes.hasPrefix(path)) + log.warning("[NoCheatPlus] Config path '" + path + "'" + msgPrefix); + } + } + + /** + * Warn about paths that are only to be set in the global config. + * @param config + * @param paths + * @param configName + */ + public static void warnGlobalOnlyPaths(ConfigFile config, String configName){ + warnPaths(config, globalOnlyPaths, " (" + configName + ") should only be set in the global configuration."); + } + + public static void warnGlobalOnlyPaths(File file, String configName){ + final ConfigFile config = new ConfigFile(); + try { + config.load(file); + warnGlobalOnlyPaths(config, configName); + } catch (FileNotFoundException e) { + } catch (IOException e) { + } catch (InvalidConfigurationException e) { + } + } + + /** + * A config file only containing the entries that are not set as global only. + * @param defaultConfig + * @return + */ + public static MemoryConfiguration getWorldsDefaultConfig(final ConfigFile defaultConfig){ + final char sep = defaultConfig.options().pathSeparator(); + final MemoryConfiguration config = new ConfigFile(); + config.options().pathSeparator(sep); + final Map defaults = defaultConfig.getValues(false); + for (final Entry entry : defaults.entrySet()){ + final String part = entry.getKey(); + if (!part.isEmpty() && globalOnlyPrefixes.hasPrefix(part)) continue; + final Object value = entry.getValue(); + if (value instanceof ConfigurationSection) addSection(config, (ConfigurationSection) value, part, sep); + else config.set(part, value); + } + return config; + } + + public static void addSection(final MemoryConfiguration config, final ConfigurationSection section, final String path, final char sep) { + final Map values = section.getValues(false); + for (final Entry entry : values.entrySet()){ + final String fullPath = path + sep + entry.getKey(); + if (globalOnlyPrefixes.hasPrefix(fullPath)) continue; + final Object value = entry.getValue(); + if (value instanceof ConfigurationSection) addSection(config, (ConfigurationSection) value, fullPath, sep); + else config.set(fullPath, value); + } + } +}