package carpet.settings; import com.google.common.collect.ImmutableList; import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.util.TriConsumer; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.stream.Collectors; public class SettingsManager { private static final Logger LOG = LogManager.getLogger(); private static Map> rules = new HashMap<>(); public boolean locked; private MinecraftServer server; private static List, String>> observers = new ArrayList<>(); public SettingsManager(MinecraftServer server) { this.server = server; loadConfigurationFromConf(); } public static void parseSettingsClass(Class settingsClass) { for (Field f : settingsClass.getDeclaredFields()) { Rule rule = f.getAnnotation(Rule.class); if (rule == null) continue; ParsedRule parsed = new ParsedRule(f, rule); rules.put(parsed.name, parsed); } } public static void addRuleObserver(TriConsumer, String> observer) { observers.add(observer); } static void notifyRuleChanged(ServerCommandSource source, ParsedRule rule, String userTypedValue) { observers.forEach(observer -> observer.accept(source, rule, userTypedValue)); } public static Iterable getCategories() { Set categories = new HashSet<>(); getRules().stream().map(r -> r.categories).forEach(categories::addAll); return categories; } public static ParsedRule getRule(String name) { return rules.get(name); } public static Collection> getRules() { return rules.values().stream().sorted().collect(Collectors.toList()); } public Collection> findStartupOverrides() { Set defaults = readSettingsFromConf().getLeft().keySet(); return rules.values().stream().filter(r -> defaults.contains(r.name)). sorted().collect(Collectors.toList()); } public Collection> getNonDefault() { return rules.values().stream().filter(r -> !r.isDefault()).sorted().collect(Collectors.toList()); } private File getFile() { return server.getLevelStorage().resolveFile(server.getLevelName(), "carpet.conf"); } public void disableBooleanFromCategory(String category) { for (ParsedRule rule : rules.values()) { if (rule.type != boolean.class || !rule.categories.contains(category)) continue; ((ParsedRule) rule).set(server.getCommandSource(), false, "false"); } } private void writeSettingsToConf(Map values) { if (locked) return; try { FileWriter fw = new FileWriter(getFile()); for (String key: values.keySet()) { fw.write(key+" "+values.get(key)+"\n"); } fw.close(); } catch (IOException e) { e.printStackTrace(); LOG.error("[CM]: failed write the carpet.conf"); } ///todo is it really needed? resendCommandTree(); } public void notifyPlayersCommandsChanged() { if (server.getPlayerManager() == null) { return; } for (ServerPlayerEntity entityplayermp : server.getPlayerManager().getPlayerList()) { server.getCommandManager().sendCommandTree(entityplayermp); } } private void loadConfigurationFromConf() { for (ParsedRule rule : rules.values()) rule.resetToDefault(server.getCommandSource()); Pair,Boolean> conf = readSettingsFromConf(); locked = false; if (conf.getRight()) { LOG.info("[CM]: Carpet Mod is locked by the administrator"); disableBooleanFromCategory(RuleCategory.COMMAND); } for (String key: conf.getLeft().keySet()) { if (rules.get(key).set(server.getCommandSource(), conf.getLeft().get(key)) != null) LOG.info("[CM]: loaded setting "+key+" as "+conf.getLeft().get(key)+" from carpet.conf"); } locked = conf.getRight(); } private Pair,Boolean> readSettingsFromConf() { try { BufferedReader reader = new BufferedReader(new FileReader(getFile())); String line = ""; boolean confLocked = false; Map result = new HashMap(); while ((line = reader.readLine()) != null) { line = line.replaceAll("\\r|\\n", ""); if ("locked".equalsIgnoreCase(line)) { confLocked = true; } String[] fields = line.split("\\s+",2); if (fields.length > 1) { if (!rules.containsKey(fields[0])) { LOG.error("[CM]: Setting " + fields[0] + " is not a valid - ignoring..."); continue; } ParsedRule rule = rules.get(fields[0]); if (!(rule.options.contains(fields[1])) && rule.isStrict) { LOG.error("[CM]: The value of " + fields[1] + " for " + fields[0] + " is not valid - ignoring..."); continue; } result.put(fields[0],fields[1]); } } reader.close(); return Pair.of(result, confLocked); } catch(FileNotFoundException e) { return Pair.of(new HashMap<>(), false); } catch (IOException e) { e.printStackTrace(); return Pair.of(new HashMap<>(), false); } } // stores different defaults in the file public boolean setDefaultRule(ServerCommandSource source, String settingName, String stringValue) { if (locked) return false; if (rules.containsKey(settingName)) { Pair,Boolean> conf = readSettingsFromConf(); conf.getLeft().put(settingName, stringValue); writeSettingsToConf(conf.getLeft()); // this may feels weird, but if conf // is locked, it will never reach this point. rules.get(settingName).set(source,stringValue); return true; } return false; } // removes overrides of the default values in the file public boolean removeDefaultRule(ServerCommandSource source, String settingName) { if (locked) return false; if (rules.containsKey(settingName)) { Pair,Boolean> conf = readSettingsFromConf(); conf.getLeft().remove(settingName); writeSettingsToConf(conf.getLeft()); rules.get(settingName).resetToDefault(source); return true; } return false; } public Collection> getRulesMatching(String search) { String lcSearch = search.toLowerCase(Locale.ROOT); return rules.values().stream().filter(rule -> { if (rule.name.toLowerCase(Locale.ROOT).contains(lcSearch)) return true; for (String c : rule.categories) if (c.toLowerCase(Locale.ROOT).equals(search)) return true; return false; }).collect(ImmutableList.toImmutableList()); } public int printAllRulesToLog() { PrintStream ps = System.out; ps.println("# Carpet Settings"); for (Map.Entry> e : new TreeMap<>(rules).entrySet()) { ParsedRule rule = e.getValue(); ps.println("## " + rule.name); ps.println(rule.description+" "); for (String extra : rule.extraInfo) ps.println(extra + " "); ps.println("* Type: `" + rule.type.getSimpleName() + "` "); ps.println("* Default value: `" + rule.defaultAsString + "` "); String optionString = rule.options.stream().map(s -> "`" + s + "`").collect(Collectors.joining(", ")); ps.println((rule.isStrict?"* Required":"* Suggested")+" options: " + optionString + " "); ps.println("* Categories: " + rule.categories.stream().map(s -> "`" + s.toUpperCase(Locale.ROOT) + "`").collect(Collectors.joining(", ")) + " "); boolean preamble = false; for (Validator validator : rule.validators) { if(validator.description() != null) { if (!preamble) { ps.println("* Additional notes: "); preamble = true; } ps.println(" * "+validator.description()+" "); } } ps.println(" "); } return 1; } }