diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 419ee236..bc95438c 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -18,6 +18,7 @@ package baritone.api; import baritone.api.utils.SettingsUtil; +import baritone.api.utils.TypeUtils; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.init.Blocks; @@ -27,6 +28,9 @@ import net.minecraft.util.text.ITextComponent; import java.awt.*; import java.lang.reflect.Field; +import java.lang.reflect.Parameter; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.*; import java.util.List; import java.util.function.Consumer; @@ -150,7 +154,7 @@ public final class Settings { /** * Blocks that Baritone will attempt to avoid (Used in avoidance) */ - public final Setting> blocksToAvoid = new Setting<>(new LinkedList<>(Arrays.asList( + public final Setting> blocksToAvoid = new Setting<>(new ArrayList<>(Arrays.asList( // Leave Empty by Default ))); @@ -789,11 +793,12 @@ public final class Settings { */ public final List> allSettings; + public final Map, Type> settingTypes; + public final class Setting { public T value; public final T defaultValue; private String name; - private final Class klass; @SuppressWarnings("unchecked") private Setting(T value) { @@ -802,7 +807,6 @@ public final class Settings { } this.value = value; this.defaultValue = value; - this.klass = (Class) value.getClass(); } /** @@ -820,7 +824,8 @@ public final class Settings { } public Class getValueClass() { - return klass; + // noinspection unchecked + return (Class) TypeUtils.resolveBaseClass(getType()); } @Override @@ -834,14 +839,21 @@ public final class Settings { public void reset() { value = defaultValue; } + + public final Type getType() { + return settingTypes.get(this); + } } // here be dragons Settings() { Field[] temp = getClass().getFields(); - HashMap> tmpByName = new HashMap<>(); - List> tmpAll = new ArrayList<>(); + + Map> tmpByName = new HashMap<>(); + List> tmpAll = new ArrayList<>(); + Map, Type> tmpSettingTypes = new HashMap<>(); + try { for (Field field : temp) { if (field.getType().equals(Setting.class)) { @@ -854,13 +866,15 @@ public final class Settings { } tmpByName.put(name, setting); tmpAll.add(setting); + tmpSettingTypes.put(setting, ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]); } } } catch (IllegalAccessException e) { throw new IllegalStateException(e); } - byLowerName = Collections.unmodifiableMap(tmpByName); - allSettings = Collections.unmodifiableList(tmpAll); + byLowerName = Collections.unmodifiableMap(tmpByName); + allSettings = Collections.unmodifiableList(tmpAll); + settingTypes = Collections.unmodifiableMap(tmpSettingTypes); } @SuppressWarnings("unchecked") diff --git a/src/api/java/baritone/api/utils/SettingsUtil.java b/src/api/java/baritone/api/utils/SettingsUtil.java index f5cb17e1..fc0abb28 100644 --- a/src/api/java/baritone/api/utils/SettingsUtil.java +++ b/src/api/java/baritone/api/utils/SettingsUtil.java @@ -21,12 +21,13 @@ import baritone.api.Settings; import net.minecraft.block.Block; import net.minecraft.item.Item; import net.minecraft.util.EnumFacing; -import net.minecraft.util.ResourceLocation; import java.awt.*; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; @@ -36,7 +37,6 @@ import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.Stream; import static net.minecraft.client.Minecraft.getMinecraft; @@ -45,8 +45,6 @@ public class SettingsUtil { private static final Path SETTINGS_PATH = getMinecraft().gameDir.toPath().resolve("baritone").resolve("settings.txt"); private static final Pattern SETTING_PATTERN = Pattern.compile("^(?[^ ]+) +(?[^ ]+)"); // 2 words separated by spaces - private static final Map, SettingsIO> map; - private static boolean isComment(String line) { return line.startsWith("#") || line.startsWith("//"); } @@ -120,11 +118,11 @@ public class SettingsUtil { if (setting.getName().equals("logger")) { return "logger"; } - SettingsIO io = map.get(setting.getValueClass()); + Parser io = Parser.getParser(setting.getType()); if (io == null) { throw new IllegalStateException("Missing " + setting.getValueClass() + " " + setting.getName()); } - return setting.getName() + " " + io.toString.apply(setting.value); + return setting.getName() + " " + io.toString(new ParserContext(setting), setting.value); } public static void parseAndApply(Settings settings, String settingName, String settingValue) throws IllegalStateException, NumberFormatException { @@ -133,47 +131,126 @@ public class SettingsUtil { throw new IllegalStateException("No setting by that name"); } Class intendedType = setting.getValueClass(); - SettingsIO ioMethod = map.get(intendedType); - Object parsed = ioMethod.parser.apply(settingValue); + Parser ioMethod = Parser.getParser(setting.getType()); + Object parsed = ioMethod.parse(new ParserContext(setting), settingValue); if (!intendedType.isInstance(parsed)) { throw new IllegalStateException(ioMethod + " parser returned incorrect type, expected " + intendedType + " got " + parsed + " which is " + parsed.getClass()); } setting.value = parsed; } - private enum SettingsIO { + private interface ISettingParser { + + T parse(ParserContext context, String raw); + + String toString(ParserContext context, T value); + + boolean accepts(Type type); + } + + private static class ParserContext { + + private final Settings.Setting setting; + + public ParserContext(Settings.Setting setting) { + this.setting = setting; + } + + final Settings.Setting getSetting() { + return this.setting; + } + } + + private enum Parser implements ISettingParser { + DOUBLE(Double.class, Double::parseDouble), BOOLEAN(Boolean.class, Boolean::parseBoolean), INTEGER(Integer.class, Integer::parseInt), - FLOAT(Float.class, Float::parseFloat), + FLOAT(Float.class,Float::parseFloat), LONG(Long.class, Long::parseLong), + ENUMFACING(EnumFacing.class, EnumFacing::byName), + COLOR( + Color.class, + str -> new Color(Integer.parseInt(str.split(",")[0]), Integer.parseInt(str.split(",")[1]), Integer.parseInt(str.split(",")[2])), + color -> color.getRed() + "," + color.getGreen() + "," + color.getBlue() + ), + BLOCK( + Block.class, + str -> BlockUtils.stringToBlockRequired(str.trim()), + BlockUtils::blockToString + ), + ITEM( + Item.class, + str -> Item.getByNameOrId(str.trim()), + item -> Item.REGISTRY.getNameForObject(item).toString() + ), + LIST() { - BLOCK_LIST(LinkedList.class, str -> Stream.of(str.split(",")).map(String::trim).map(BlockUtils::stringToBlockRequired).collect(Collectors.toCollection(LinkedList::new)), list -> ((LinkedList) list).stream().map(BlockUtils::blockToString).collect(Collectors.joining(","))), - ITEM_LIST(ArrayList.class, str -> Stream.of(str.split(",")).map(String::trim).map(Item::getByNameOrId).collect(Collectors.toCollection(ArrayList::new)), list -> ((ArrayList) list).stream().map(Item.REGISTRY::getNameForObject).map(ResourceLocation::toString).collect(Collectors.joining(","))), - COLOR(Color.class, str -> new Color(Integer.parseInt(str.split(",")[0]), Integer.parseInt(str.split(",")[1]), Integer.parseInt(str.split(",")[2])), color -> color.getRed() + "," + color.getGreen() + "," + color.getBlue()), - ENUMFACING(EnumFacing.class, EnumFacing::byName); + @Override + public Object parse(ParserContext context, String raw) { + Type type = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0]; + Parser parser = Parser.getParser(type); + return Arrays.stream(raw.split(",")) + .map(s -> parser.parse(context, s)) + .collect(Collectors.toList()); + } - Class klass; - Function parser; - Function toString; + @Override + public String toString(ParserContext context, Object value) { + Type type = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0]; + Parser parser = Parser.getParser(type); - SettingsIO(Class klass, Function parser) { + return ((List) value).stream() + .map(o -> parser.toString(context, o)) + .collect(Collectors.joining(",")); + } + + @Override + public boolean accepts(Type type) { + return List.class.isAssignableFrom(TypeUtils.resolveBaseClass(type)); + } + }; + + private final Class klass; + private final Function parser; + private final Function toString; + + Parser() { + this.klass = null; + this.parser = null; + this.toString = null; + } + + Parser(Class klass, Function parser) { this(klass, parser, Object::toString); } - SettingsIO(Class klass, Function parser, Function toString) { + Parser(Class klass, Function parser, Function toString) { this.klass = klass; this.parser = parser::apply; this.toString = x -> toString.apply((T) x); } - } - static { - HashMap, SettingsIO> tempMap = new HashMap<>(); - for (SettingsIO type : SettingsIO.values()) { - tempMap.put(type.klass, type); + @Override + public Object parse(ParserContext context, String raw) { + return this.parser.apply(raw); + } + + @Override + public String toString(ParserContext context, Object value) { + return this.toString.apply(value); + } + + @Override + public boolean accepts(Type type) { + return type instanceof Class && this.klass.isAssignableFrom((Class) type); + } + + public static Parser getParser(Type type) { + return Arrays.stream(values()) + .filter(parser -> parser.accepts(type)) + .findFirst().orElse(null); } - map = Collections.unmodifiableMap(tempMap); } } diff --git a/src/api/java/baritone/api/utils/TypeUtils.java b/src/api/java/baritone/api/utils/TypeUtils.java new file mode 100644 index 00000000..457cc449 --- /dev/null +++ b/src/api/java/baritone/api/utils/TypeUtils.java @@ -0,0 +1,44 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * @author Brady + * @since 4/20/2019 + */ +public final class TypeUtils { + + private TypeUtils() {} + + /** + * Resolves the "base type" for the specified type. For example, if the specified + * type is {@code List}, then {@code List.class} will be returned. If the + * specified type is already a class, then it is directly returned. + * + * @param type The type to resolve + * @return The base class + */ + public static Class resolveBaseClass(Type type) { + return type instanceof Class ? (Class) type + : type instanceof ParameterizedType ? (Class) ((ParameterizedType) type).getRawType() + : null; + } +}