settings system

This commit is contained in:
gnembon committed 2019-06-11 02:08:50 -03:00
1 parent c7739f1963
commit 92f40c36b8
9 files changed
+863 -21

No files matched your search

+13
View File
@@ -10,6 +10,7 @@ import carpet.commands.DistanceCommand;
import carpet.commands.DrawCommand;
import carpet.commands.InfoCommand;
import carpet.commands.LogCommand;
import carpet.commands.ParcetCommand;
import carpet.commands.PerimeterInfoCommand;
import carpet.commands.PlayerCommand;
import carpet.commands.ScriptCommand;
@@ -18,6 +19,8 @@ import carpet.commands.TickCommand;
import carpet.helpers.TickSpeed;
import carpet.logging.LoggerRegistry;
import carpet.script.CarpetScriptServer;
import carpet.settings.Settings;
import carpet.settings.SettingsManager;
import carpet.utils.HUDController;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.server.command.ServerCommandSource;
@@ -28,12 +31,21 @@ public class CarpetServer // static for now - easier to handle all around the co
public static final Random rand = new Random((int)((2>>16)*Math.random()));
public static MinecraftServer minecraft_server;
public static CarpetScriptServer scriptServer;
public static SettingsManager settingsManager;
static {
SettingsManager.parseSettingsClass(Settings.class);
//...
}
public static void init(MinecraftServer server) //aka constructor of this static singleton class
{
CarpetServer.minecraft_server = server;
}
public static void onServerLoaded(MinecraftServer server)
{
//new
settingsManager = new SettingsManager(server);
//old
CarpetSettings.apply_settings_from_conf(server);
scriptServer = new CarpetScriptServer();
}
@@ -65,6 +77,7 @@ public class CarpetServer // static for now - easier to handle all around the co
PerimeterInfoCommand.register(dispatcher);
DrawCommand.register(dispatcher);
ScriptCommand.register(dispatcher);
ParcetCommand.register(dispatcher);
//TestCommand.register(dispatcher);
}
+23 -21
View File
@@ -7,6 +7,7 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.dedicated.DedicatedServer;
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;
@@ -214,19 +215,19 @@ public class CarpetSettings
public static void apply_settings_from_conf(MinecraftServer server)
{
Map<String, String> conf = read_conf(server);
boolean is_locked = locked;
Pair<Map<String, String>,Boolean> conf = read_conf(server);
locked = false;
if (is_locked)
if (conf.getRight())
{
LOG.info("[CM]: Carpet Mod is locked by the administrator");
disable_commands_by_default();
}
for (String key: conf.keySet())
for (String key: conf.getLeft().keySet())
{
set(server.getCommandSource(), key, conf.get(key));
LOG.info("[CM]: loaded setting "+key+" as "+conf.get(key)+" from carpet.conf");
set(server.getCommandSource(), key, conf.getLeft().get(key));
LOG.info("[CM]: loaded setting "+key+" as "+conf.getLeft().get(key)+" from carpet.conf");
}
locked = is_locked;
locked = conf.getRight();
}
private static void disable_commands_by_default()
{
@@ -239,21 +240,21 @@ public class CarpetSettings
}
}
private static Map<String, String> read_conf(MinecraftServer server)
private static Pair<Map<String, String>,Boolean> read_conf(MinecraftServer server)
{
try
{
File settings_file = server.getLevelStorage().resolveFile(server.getLevelName(), "carpet.conf");
BufferedReader b = new BufferedReader(new FileReader(settings_file));
String line = "";
boolean confLocked = false;
Map<String,String> result = new HashMap<String, String>();
while ((line = b.readLine()) != null)
{
line = line.replaceAll("\\r|\\n", "");
if ("locked".equalsIgnoreCase(line))
{
disable_commands_by_default();
locked = true;
confLocked = true;
}
String[] fields = line.split("\\s+",2);
if (fields.length > 1)
@@ -272,16 +273,16 @@ public class CarpetSettings
}
}
b.close();
return result;
return Pair.of(result, confLocked);
}
catch(FileNotFoundException e)
{
return new HashMap<>();
return Pair.of(new HashMap<>(), false);
}
catch (IOException e)
{
e.printStackTrace();
return new HashMap<>();
return Pair.of(new HashMap<>(), false);
}
}
@@ -311,9 +312,10 @@ public class CarpetSettings
if (locked) return false;
if (settings_store.containsKey(setting_name))
{
Map<String, String> conf = read_conf(source.getMinecraftServer());
conf.put(setting_name, string_value);
write_conf(source.getMinecraftServer(), conf);
Pair<Map<String, String>,Boolean> conf = read_conf(source.getMinecraftServer());
conf.getLeft().put(setting_name, string_value);
write_conf(source.getMinecraftServer(), conf.getLeft()); // this may feels weird, but if conf
// is locked, it will never reach this point.
set(source, setting_name,string_value);
return true;
}
@@ -325,9 +327,9 @@ public class CarpetSettings
if (locked) return false;
if (settings_store.containsKey(setting_name))
{
Map<String, String> conf = read_conf(source.getMinecraftServer());
conf.remove(setting_name);
write_conf(source.getMinecraftServer(), conf);
Pair<Map<String, String>,Boolean> conf = read_conf(source.getMinecraftServer());
conf.getLeft().remove(setting_name);
write_conf(source.getMinecraftServer(), conf.getLeft());
set(source, setting_name,get(setting_name).getDefault());
return true;
}
@@ -386,7 +388,7 @@ public class CarpetSettings
public static CarpetSettingEntry[] find_nondefault(MinecraftServer server)
{
ArrayList<CarpetSettingEntry> res = new ArrayList<CarpetSettingEntry>();
Map <String,String> defaults = read_conf(server);
Map <String,String> defaults = read_conf(server).getLeft();
for (String rule: settings_store.keySet().stream().sorted().collect(Collectors.toList()))
{
if (!settings_store.get(rule).isDefault() || defaults.containsKey(rule))
@@ -400,7 +402,7 @@ public class CarpetSettings
{
ArrayList<CarpetSettingEntry> res = new ArrayList<CarpetSettingEntry>();
if (locked) return res.toArray(new CarpetSettingEntry[0]);
Map <String,String> defaults = read_conf(server);
Map <String,String> defaults = read_conf(server).getLeft();
for (String rule: settings_store.keySet().stream().sorted().collect(Collectors.toList()))
{
if (defaults.containsKey(rule))
@@ -0,0 +1,209 @@
package carpet.commands;
import carpet.CarpetServer;
import carpet.CarpetSettings;
import carpet.settings.ParsedRule;
import carpet.settings.RuleCategory;
import carpet.settings.SettingsManager;
import carpet.utils.Messenger;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.command.CommandManager;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.text.BaseText;
import net.minecraft.text.Text;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
import static net.minecraft.server.command.CommandSource.suggestMatching;
public class ParcetCommand
{
public static void register(CommandDispatcher<ServerCommandSource> dispatcher)
{
LiteralArgumentBuilder<ServerCommandSource> literalargumentbuilder = literal("parcet").requires((player) ->
player.hasPermissionLevel(2) && !CarpetServer.settingsManager.locked);
literalargumentbuilder.executes((context)->listAllSettings(context.getSource())).
then(literal("list").
executes( (c) -> listSettings(c.getSource(),
"All CarpetMod Settings",
SettingsManager.getRules())).
then(literal("defaults").
executes( (c)-> listSettings(c.getSource(),
"Current CarpetMod Startup Settings from carpet.conf",
CarpetServer.settingsManager.findStartupOverrides()))).
then(argument("tag",StringArgumentType.word()).
suggests( (c, b)->suggestMatching(Arrays.stream(RuleCategory.values()).map(r -> r.lowerCase), b)).
executes( (c) -> listSettings(c.getSource(),
String.format("CarpetMod Settings matching \"%s\"", StringArgumentType.getString(c, "tag")),
CarpetServer.settingsManager.getRulesMatching(StringArgumentType.getString(c, "tag"))))));
for (ParsedRule<?> rule: SettingsManager.getRules())
{
literalargumentbuilder.then(literal(rule.name).executes( (context) ->
displayRuleMenu(context.getSource(),rule)));
literalargumentbuilder.then(literal("removeDefault").
requires(s -> !CarpetServer.settingsManager.locked).
then(literal(rule.name).executes((context) ->
removeDefault(context.getSource(), rule))));
literalargumentbuilder.then(literal(rule.name).
requires(s -> !CarpetServer.settingsManager.locked).
then(argument("value", StringArgumentType.word()).
suggests((c, b)-> suggestMatching(rule.options,b)).
executes((context) ->
setRule(context.getSource(), rule, StringArgumentType.getString(context, "value")))));
literalargumentbuilder.then(literal("setDefault").
requires(s -> !CarpetServer.settingsManager.locked).
then(literal(rule.name).
then(argument("value", StringArgumentType.word()).
suggests((c, b)-> suggestMatching(rule.options,b)).
executes((context) ->
setDefault(context.getSource(), rule, StringArgumentType.getString(context, "value"))))));
}
dispatcher.register(literalargumentbuilder);
}
private static int displayRuleMenu(ServerCommandSource source, ParsedRule<?> rule)
{
PlayerEntity player;
try
{
player = source.getPlayer();
}
catch (CommandSyntaxException e)
{
Messenger.m(source, "w "+rule.name +" is set to: ","wb "+rule.getAsString());
return 1;
}
Messenger.m(player, "");
Messenger.m(player, "wb "+rule.name,"!/carpet "+rule.name,"^g refresh");
Messenger.m(player, "w "+rule.description);
rule.extraInfo.forEach(s -> Messenger.m(player, "g "+s));
List<BaseText> tags = new ArrayList<>();
tags.add(Messenger.c("w Tags: "));
for (String t: Arrays.stream(RuleCategory.values()).sorted().map(c -> c.lowerCase).collect(Collectors.toList()))
{
tags.add(Messenger.c("c ["+t+"]", "^g list all "+t+" settings","!/carpet list "+t));
tags.add(Messenger.c("w , "));
}
tags.remove(tags.size()-1);
Messenger.m(player, tags.toArray(new Object[0]));
Messenger.m(player, "w Current value: ",String.format("%s %s (%s value)",rule.getBoolValue()?"lb":"nb", rule.getAsString(),rule.isDefault()?"default":"modified"));
List<BaseText> options = new ArrayList<>();
options.add(Messenger.c("w Options: ", "y [ "));
for (String o: rule.options)
{
options.add(makeSetRuleButton(rule, o, false));
options.add(Messenger.c("w "));
}
options.remove(options.size()-1);
options.add(Messenger.c("y ]"));
Messenger.m(player, options.toArray(new Object[0]));
return 1;
}
private static int setRule(ServerCommandSource source, ParsedRule<?> rule, String newValue)
{
if (rule.set(source, newValue) != null)
Messenger.m(source, "w "+rule.toString()+", ", "c [change permanently?]",
"^w Click to keep the settings in carpet.conf to save across restarts",
"?/carpet setDefault "+rule.name+" "+rule.getAsString());
return 1;
}
private static int setDefault(ServerCommandSource source, ParsedRule<?> rule, String defaultValue)
{
CarpetServer.settingsManager.setDefaultRule(source, rule.name, defaultValue);
Messenger.m(source ,"gi rule "+ rule.name+" will now default to "+ defaultValue);
return 1;
}
private static int removeDefault(ServerCommandSource source, ParsedRule<?> rule)
{
CarpetServer.settingsManager.removeDefaultRule(source, rule.name);
Messenger.m(source ,"gi rule "+ rule.name+" defaults to Vanilla");
return 1;
}
private static BaseText displayInteractiveSetting(ParsedRule<?> rule)
{
List<Object> args = new ArrayList<>();
args.add("w - "+rule.name+" ");
args.add("!/carpet "+rule.name);
args.add("^y "+rule.description);
for (String option: rule.options)
{
args.add(makeSetRuleButton(rule, option, true));
args.add("w ");
}
args.remove(args.size()-1);
return Messenger.c(args.toArray(new Object[0]));
}
private static BaseText makeSetRuleButton(ParsedRule<?> rule, String option, boolean brackets) {
String color = "";
if (option.equals(rule.defaultAsString) && !brackets) color += "u";
color += option.equals(rule.getAsString()) ? "bl" : "y";
String baseText = color + (brackets ? " [" : " ") + option + (brackets ? "]" : "");
if (CarpetServer.settingsManager.locked) {
return Messenger.c(baseText, "^g Settings are locked");
}
return Messenger.c(baseText, "^g Switch to " + option, "?/carpet " + rule.name + " " + option);
}
private static int listSettings(ServerCommandSource source, String title, Collection<ParsedRule<?>> settings_list)
{
try
{
PlayerEntity player = source.getPlayer();
Messenger.m(player,String.format("wb %s:",title));
settings_list.forEach(e -> Messenger.m(player,displayInteractiveSetting(e)));
}
catch (CommandSyntaxException e)
{
Messenger.m(source, "w s:"+title);
settings_list.forEach(r -> Messenger.m(source, "w - "+ r.toString()));
}
return 1;
}
private static int listAllSettings(ServerCommandSource source)
{
listSettings(source, "Current CarpetMod Settings", CarpetServer.settingsManager.getNonDefault());
Messenger.m(source, "Carpet Mod version: "+ CarpetSettings.carpetVersion);
try
{
PlayerEntity player = source.getPlayer();
List<Object> tags = new ArrayList<>();
tags.add("w Browse Categories:\n");
for (String t : Arrays.stream(RuleCategory.values()).map(r -> r.lowerCase).collect(Collectors.toList()))
{
tags.add("c [" + t+"]");
tags.add("^g list all " + t + " settings");
tags.add("!/carpet list " + t);
tags.add("w ");
}
tags.remove(tags.size() - 1);
Messenger.m(player, tags.toArray(new Object[0]));
}
catch (CommandSyntaxException e)
{
}
return 1;
}
}
@@ -0,0 +1,170 @@
package carpet.settings;
import carpet.utils.Messenger;
import com.google.common.collect.ImmutableList;
import net.minecraft.server.command.ServerCommandSource;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
public final class ParsedRule<T> implements Comparable<ParsedRule> {
public final Rule rule;
public final Field field;
public final String name;
public final String description;
public final ImmutableList<String> extraInfo;
public final ImmutableList<RuleCategory> categories;
public final ImmutableList<String> options;
public boolean isStrict;
public final Class<T> type;
public final List<Validator<T>> validators;
public final T defaultValue;
public final String defaultAsString;
public static <T> T callConstructor(Class<T> cls) {
try {
Constructor<T> constr = cls.getDeclaredConstructor();
constr.setAccessible(true);
return constr.newInstance();
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
ParsedRule(Field field, Rule rule) {
this.rule = rule;
this.field = field;
this.name = rule.name().isEmpty() ? field.getName() : rule.name();
this.type = (Class<T>) field.getType();
this.description = rule.desc();
this.isStrict = rule.strict();
this.extraInfo = ImmutableList.copyOf(rule.extra());
this.categories = ImmutableList.copyOf(rule.category());
this.validators = new ArrayList<>();
for (Class v : rule.validate())
{
this.validators.add((Validator<T>) callConstructor(v));
}
if (categories.contains(RuleCategory.COMMANDS))
this.validators.add(callConstructor(Validator._COMMAND.class));
this.defaultValue = get();
this.defaultAsString = convertToString(this.defaultValue);
if (this.type == boolean.class) {
this.isStrict = true; // it has no sense otherwise
this.options = ImmutableList.of("true", "false");
} else if (this.type.isEnum()) {
this.isStrict = true; // it has no sense otherwise
this.options = Arrays.stream(this.type.getEnumConstants()).map(e -> ((Enum) e).name().toLowerCase(Locale.ROOT)).collect(ImmutableList.toImmutableList());
} else {
this.options = ImmutableList.copyOf(rule.options());
}
if (isStrict)
this.validators.add(callConstructor(Validator._STRICT.class));
}
public ParsedRule<T> set(ServerCommandSource source, String value) {
//if locked - prevent
if (type == String.class)
{
return set(source, (T) value, value);
}
else if (type == boolean.class)
{
return set(source, (T) (Object) Boolean.parseBoolean(value), value);
}
else if (type == int.class)
{
return set(source, (T) (Object) Integer.parseInt(value), value);
}
else if (type.isEnum())
{
String ucValue = value.toUpperCase(Locale.ROOT);
return set(source, (T) (Object) Enum.valueOf((Class<? extends Enum>) type, ucValue), value);
}
else
{
Messenger.m(source, "r Unknown type " + type.getSimpleName());
return null;
}
}
public ParsedRule<T> set(ServerCommandSource source, T value, String stringValue) {
try {
for (Validator<T> validator : this.validators)
{
boolean allowed = validator.validate(source, this, value, stringValue);
if (!allowed)
{
Messenger.m(source, "r Wrong value for " + name + ": " + stringValue);
return null;
}
}
this.field.set(null, value);
}
catch (IllegalAccessException e)
{
Messenger.m(source, "r Unable to access setting for "+name);
}
return this;
}
public T get() {
try {
return (T) this.field.get(null);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
public String getAsString() {
return convertToString(get());
}
public boolean getBoolValue() {
if (type == boolean.class) return (Boolean) get();
if (type.isAssignableFrom(Number.class)) return ((Number) get()).doubleValue() > 0;
return false;
}
public boolean isDefault() {
return defaultValue.equals(get());
}
public void resetToDefault(ServerCommandSource source) {
set(source, defaultValue, defaultAsString);
}
private static String convertToString(Object value) {
if (value instanceof Enum) return ((Enum) value).name().toLowerCase(Locale.ROOT);
return value.toString();
}
@Override
public boolean equals(Object obj) {
return obj.getClass() == ParsedRule.class && ((ParsedRule) obj).name.equals(this.name);
}
@Override
public int hashCode() {
return this.name.hashCode();
}
@Override
public int compareTo(ParsedRule o) {
return this.name.compareTo(o.name);
}
@Override
public String toString() {
return this.name + ": " + getAsString();
}
}
+58
View File
@@ -0,0 +1,58 @@
package carpet.settings;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Any field in this class annotated with this class is interpreted as a carpet rule.
* The field must be static and have a type of one of:
* - boolean
* - int
* - double
* - String
* - a subclass of Enum
* The default value of the rule will be the initial value of the field.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Rule {
/**
* The rule name, by default the same as the field name
*/
String name() default ""; // default same as field name
/**
* A description of the rule
*/
String desc();
/**
* Extra information about the rule
*/
String[] extra() default {};
/**
* A list of categories the rule is in
*/
RuleCategory[] category();
/**
* Options to select in menu.
* Inferred for booleans and enums. Otherwise, must be present.
*/
String[] options() default {};
/**
* if a rule is not strict - can take any value, otherwise it needs to match
* any of the options
* For enums, its always strict
*/
boolean strict() default true;
/**
* The class of the validator checked when the rule is changed.
*/
Class<? extends Validator>[] validate() default {};
}
@@ -0,0 +1,13 @@
package carpet.settings;
import java.util.Locale;
public enum RuleCategory {
BUGFIX, SURVIVAL, CREATIVE, EXPERIMENTAL, OPTIMIZATIONS, FEATURE, COMMANDS ;
public final String lowerCase;
RuleCategory() {
this.lowerCase = this.name().toLowerCase(Locale.ROOT);
}
}
@@ -0,0 +1,84 @@
package carpet.settings;
import static carpet.settings.RuleCategory.BUGFIX;
import static carpet.settings.RuleCategory.COMMANDS;
import static carpet.settings.RuleCategory.CREATIVE;
import static carpet.settings.RuleCategory.EXPERIMENTAL;
import static carpet.settings.RuleCategory.FEATURE;
import static carpet.settings.RuleCategory.OPTIMIZATIONS;
import static carpet.settings.RuleCategory.SURVIVAL;
public class Settings
{
@Rule(
desc = "Fixes server crashing supposedly on falling behind 60s in ONE tick, yeah bs.",
extra = "Fixed 1.12 watchdog crash in 1.13 pre-releases, reintroduced with 1.13, GG.",
category = BUGFIX,
validate = Validator.WIP.class
)
public static boolean watchdogCrashFix = false;
@Rule(
desc = "Enables /c and /s commands to quickly switch between camera and survival modes",
extra = "/c and /s commands are available to all players regardless of their permission levels",
category = COMMANDS
)
public static boolean commandCameramode = true;
@Rule(desc = "fill/clone/setblock and structure blocks cause block updates", category = CREATIVE)
public static boolean fillUpdates = true;
@Rule(
desc = "Customizable fill/clone volume limit",
options = {"32768", "250000", "1000000"},
validate = Validator.POSITIVE_NUMBER.class,
category = CREATIVE
)
public static int fillLimit = 32768;
@Rule(
desc = "Hoppers pointing to wool will count items passing through them",
extra = {
"Enables /counter command, and actions while placing red and green carpets on wool blocks",
"Use /counter <color?> reset to reset the counter, and /counter <color?> to query",
"In survival, place green carpet on same color wool to query, red to reset the counters",
"Counters are global and shared between players, 16 channels available",
"Items counted are destroyed, count up to one stack per tick per hopper"
},
category = COMMANDS
)
public static boolean hopperCounters = false;
@Rule(desc = "Explosions won't destroy blocks", category = CREATIVE)
public static boolean explosionNoBlockDamage = false;
@Rule(
desc = "Silverfish drop a gravel item when breaking out of a block",
category = {FEATURE, EXPERIMENTAL}
)
public static boolean silverFishDropGravel = false;
@Rule(desc = "Shulkers will respawn in end cities", category = {FEATURE, EXPERIMENTAL})
public static boolean shulkerSpawningInEndCities = false;
@Rule(
desc = "Portals won't let a creative player go through instantly",
extra = "Holding obsidian in either hand won't let you through at all",
category = CREATIVE
)
public static boolean portalCreativeDelay = false;
@Rule(desc = "Pistons can push block entities, like hoppers, chests etc.", category = {FEATURE, EXPERIMENTAL})
public static boolean movableTileEntities = false;
@Rule(
desc = "Empty shulker boxes can stack to 64 when dropped on the ground",
extra = "To move them around between inventories, use shift click to move entire stacks",
category = SURVIVAL
)
public static boolean stackableShulkerBoxes = false;
@Rule(desc = "Optimizes spawning", category = {OPTIMIZATIONS, EXPERIMENTAL})
public static boolean lagFreeSpawning = false;
}
@@ -0,0 +1,230 @@
package carpet.settings;
import carpet.CarpetSettings;
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 java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SettingsManager {
private static final Logger LOG = LogManager.getLogger();
private static Map<String, ParsedRule<?>> rules = new HashMap<>();
public boolean locked;
MinecraftServer server;
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);
CarpetSettings.LOG.error("REGISTERED RULE: "+parsed.name);
rules.put(parsed.name, parsed);
}
}
public ParsedRule getRule(String name) {
return rules.get(name);
}
public static Collection<ParsedRule<?>> getRules() {
CarpetSettings.LOG.error("grabbing rules");
List<ParsedRule<?>> r = rules.values().stream().sorted().collect(Collectors.toList());
CarpetSettings.LOG.error("Got: "+r.size()+" rules");
return r;
}
public Collection<ParsedRule<?>> findStartupOverrides()
{
Set<String> defaults = readSettingsFromConf().getLeft().keySet();
return rules.values().stream().filter(r -> defaults.contains(r.name)).
sorted().collect(Collectors.toList());
}
public Collection<ParsedRule<?>> 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(RuleCategory category) {
for (ParsedRule<?> rule : rules.values()) {
if (rule.type != boolean.class || !rule.categories.contains(category)) continue;
((ParsedRule<Boolean>) rule).set(server.getCommandSource(), false, "false");
}
}
private void writeSettingsToConf(Map<String, String> 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<Map<String, String>,Boolean> conf = readSettingsFromConf();
locked = false;
if (conf.getRight())
{
LOG.info("[CM]: Carpet Mod is locked by the administrator");
disableBooleanFromCategory(RuleCategory.COMMANDS);
}
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();
}
public Pair<Map<String, String>,Boolean> readSettingsFromConf()
{
try
{
BufferedReader reader = new BufferedReader(new FileReader(getFile()));
String line = "";
boolean confLocked = false;
Map<String,String> result = new HashMap<String, String>();
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<Map<String, String>,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<Map<String, String>,Boolean> conf = readSettingsFromConf();
conf.getLeft().remove(settingName);
writeSettingsToConf(conf.getLeft());
rules.get(settingName).resetToDefault(source);
return true;
}
return false;
}
public Collection<ParsedRule<?>> getRulesMatching(Predicate<ParsedRule<?>> predicate) {
return rules.values().stream().filter(predicate).collect(ImmutableList.toImmutableList());
}
public Collection<ParsedRule<?>> getRulesMatching(String search) {
String lcSearch = search.toLowerCase(Locale.ROOT);
return getRulesMatching(rule -> {
if (rule.name.toLowerCase(Locale.ROOT).contains(lcSearch)) return true;
for (RuleCategory c : rule.categories) if (c.lowerCase.equals(search)) return true;
return false;
});
}
}
@@ -0,0 +1,63 @@
package carpet.settings;
import carpet.CarpetServer;
import carpet.utils.Messenger;
import net.minecraft.server.command.ServerCommandSource;
import java.util.Locale;
public abstract class Validator<T> {
/**
* Validate the new value of a rule
* @return true if valid, false if new rule invalid.
*/
abstract boolean validate(ServerCommandSource source, ParsedRule<T> currentRule, T newValue, String string);
public static class POSITIVE_NUMBER<T extends Number> extends Validator<T> {
@Override
public boolean validate(ServerCommandSource source, ParsedRule<T> currentRule, T newValue, String string) {
return newValue.doubleValue() > 0;
}
}
public static class _COMMAND<T> extends Validator<T> {
@Override
public boolean validate(ServerCommandSource source, ParsedRule<T> currentRule, T newValue, String string) {
if (CarpetServer.settingsManager != null)
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
return true;
}
}
public static class WIP<T> extends Validator<T> {
@Override
public boolean validate(ServerCommandSource source, ParsedRule<T> currentRule, T newValue, String string) {
if (source != null)
try
{
Messenger.m(source, "r Work in progress - limited or no functionality available for "+currentRule.name);
}
catch (NullPointerException ignored) { }
return true;
}
}
public static class _STRICT<T> extends Validator<T> {
@Override
public boolean validate(ServerCommandSource source, ParsedRule<T> currentRule, T newValue, String string) {
if (!currentRule.options.contains(string.toLowerCase(Locale.ROOT)))
{
if (source != null)
try
{
Messenger.m(source, "r Valid options: " + currentRule.options.toString());
}
catch (NullPointerException ignored)
{
}
return false;
}
return true;
}
}
}