package carpet.settings; import carpet.CarpetServer; import carpet.utils.Translations; 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; import static carpet.utils.Translations.tr; public final class ParsedRule implements Comparable { public final Field field; public final String name; public final String description; public final ImmutableList extraInfo; public final ImmutableList categories; public final ImmutableList options; public boolean isStrict; public boolean isClient; public final Class type; public final List> validators; public final T defaultValue; public final String defaultAsString; ParsedRule(Field field, Rule rule) { this.field = field; this.name = rule.name().isEmpty() ? field.getName() : rule.name(); this.type = (Class) 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) callConstructor(v)); if (categories.contains(RuleCategory.COMMAND)) { this.validators.add(callConstructor(Validator._COMMAND.class)); if (this.type == String.class) { this.isStrict = false; this.validators.add((Validator) callConstructor(Validator._COMMAND_LEVEL_VALIDATOR.class)); } } this.isClient = categories.contains(RuleCategory.CLIENT); if (this.isClient) { this.validators.add(callConstructor(Validator._CLIENT.class)); } this.defaultValue = get(); this.defaultAsString = convertToString(this.defaultValue); if (rule.options().length > 0) { this.options = ImmutableList.copyOf(rule.options()); } else if (this.type == boolean.class){ this.options = ImmutableList.of("true","false"); } else if(this.type == String.class && categories.contains(RuleCategory.COMMAND)) { this.options = ImmutableList.of("true", "false", "ops"); } else if (this.type.isEnum()) { this.options = Arrays.stream(this.type.getEnumConstants()).map(e -> ((Enum) e).name().toLowerCase(Locale.ROOT)).collect(ImmutableList.toImmutableList()); } else { this.options = ImmutableList.of(); } if (isStrict && !this.options.isEmpty()) { if (this.type == boolean.class || this.type == int.class || this.type == double.class || this.type == float.class) { this.validators.add(callConstructor(Validator._STRICT_IGNORECASE.class)); } else { this.validators.add(callConstructor(Validator._STRICT.class)); } } } private T callConstructor(Class cls) { try { Constructor constr = cls.getDeclaredConstructor(); constr.setAccessible(true); return constr.newInstance(); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } } public ParsedRule set(ServerCommandSource source, String value) { if (CarpetServer.settingsManager != null && CarpetServer.settingsManager.locked) return null; 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 == double.class) { return set(source, (T) (Object) Double.parseDouble(value), value); } else if (type.isEnum()) { String ucValue = value.toUpperCase(Locale.ROOT); return set(source, (T) (Object) Enum.valueOf((Class) type, ucValue), value); } else { Messenger.m(source, "r Unknown type " + type.getSimpleName()); return null; } } ParsedRule set(ServerCommandSource source, T value, String stringValue) { try { for (Validator validator : this.validators) { value = validator.validate(source, this, value, stringValue); if (value == null) { if (source != null) { Messenger.m(source, "r Wrong value for " + name + ": " + stringValue); if (validator.description() != null) Messenger.m(source, "r " + validator.description()); } return null; } } if (!value.equals(get()) || source == null) { this.field.set(null, value); if (source != null) CarpetServer.settingsManager.notifyRuleChanged(source, this, stringValue); } } catch (IllegalAccessException e) { Messenger.m(source, "r Unable to access setting for "+name); return null; } 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(); } private String translationKey() { return String.format("rule.%s.name", name); } public String translatedName(){ String key = translationKey(); return Translations.hasTranslation(key) ? tr(key) + String.format(" (%s)", name): name; } public String translatedDescription() { return tr(String.format("rule.%s.desc", (name)), description); } public List translatedExtras() { if (!Translations.hasTranslations()) return extraInfo; String keyBase = String.format("rule.%s.extra.", name); List extras = new ArrayList<>(); int i = 0; while (Translations.hasTranslation(keyBase+i)) { extras.add(Translations.tr(keyBase+i)); i++; } return (extras.isEmpty()) ? extraInfo : extras; } }