From 9c7d20b3a1e47c6f1331475b74fa1536c10fb9ee Mon Sep 17 00:00:00 2001 From: LexManos Date: Mon, 10 Oct 2016 22:15:35 -0700 Subject: [PATCH] Initial pass a re-working the configuration system. Work in progress. --- .../common/ForgeModContainer.java | 3 + .../minecraftforge/common/config/Config.java | 91 ++++ .../common/config/ConfigManager.java | 309 ++++++++++++ .../common/config/ITypeAdapter.java | 33 ++ .../common/config/TypeAdapters.java | 462 ++++++++++++++++++ .../fml/common/FMLModContainer.java | 3 + .../net/minecraftforge/fml/common/Loader.java | 4 + .../net/minecraftforge/debug/ConfigTest.java | 74 +++ 8 files changed, 979 insertions(+) create mode 100644 src/main/java/net/minecraftforge/common/config/Config.java create mode 100644 src/main/java/net/minecraftforge/common/config/ConfigManager.java create mode 100644 src/main/java/net/minecraftforge/common/config/ITypeAdapter.java create mode 100644 src/main/java/net/minecraftforge/common/config/TypeAdapters.java create mode 100644 src/test/java/net/minecraftforge/debug/ConfigTest.java diff --git a/src/main/java/net/minecraftforge/common/ForgeModContainer.java b/src/main/java/net/minecraftforge/common/ForgeModContainer.java index 10ac6bd54b..b074386f1a 100644 --- a/src/main/java/net/minecraftforge/common/ForgeModContainer.java +++ b/src/main/java/net/minecraftforge/common/ForgeModContainer.java @@ -50,6 +50,8 @@ import net.minecraft.stats.StatList; import net.minecraft.world.storage.SaveHandler; import net.minecraft.world.storage.WorldInfo; import net.minecraftforge.classloading.FMLForgePlugin; +import net.minecraftforge.common.config.Config; +import net.minecraftforge.common.config.ConfigManager; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; import net.minecraftforge.common.model.animation.CapabilityAnimation; @@ -394,6 +396,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC NetworkRegistry.INSTANCE.register(this, this.getClass(), "*", evt.getASMHarvestedData()); ForgeNetworkHandler.registerChannel(this, evt.getSide()); + ConfigManager.load(this.getModId(), Config.Type.INSTANCE); } @Subscribe diff --git a/src/main/java/net/minecraftforge/common/config/Config.java b/src/main/java/net/minecraftforge/common/config/Config.java new file mode 100644 index 0000000000..2f2ed854e0 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/config/Config.java @@ -0,0 +1,91 @@ +/* + * Minecraft Forge + * Copyright (c) 2016. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Config +{ + /** + * The mod id that this configuration is associated with. + */ + String modid(); + /** + * A user friendly name for the config file, + * the default will be modid + */ + String name() default ""; + + /** + * The type this is, right now the only value is INSTANCE. + * This is intended to be expanded upon later for more Forge controlled + * configs. + */ + Type type() default Type.INSTANCE; + + public static enum Type + { + /** + * Loaded once, directly after mod construction. Before pre-init. + * This class must have static fields. + */ + INSTANCE(true); + + + private boolean isStatic = true; + private Type(boolean isStatic) { this.isStatic = isStatic; } + public boolean isStatic(){ return this.isStatic; } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface LangKey + { + String value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface Comment + { + String[] value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface RangeInt + { + int min() default Integer.MIN_VALUE; + int max() default Integer.MAX_VALUE; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface RangeDouble + { + double min() default Double.MIN_VALUE; + double max() default Double.MAX_VALUE; + } +} diff --git a/src/main/java/net/minecraftforge/common/config/ConfigManager.java b/src/main/java/net/minecraftforge/common/config/ConfigManager.java new file mode 100644 index 0000000000..420cbd9240 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/config/ConfigManager.java @@ -0,0 +1,309 @@ +/* + * Minecraft Forge + * Copyright (c) 2016. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common.config; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; + +import org.apache.logging.log4j.Level; + +import com.google.common.base.Joiner; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +import net.minecraftforge.common.config.Config.Comment; +import net.minecraftforge.common.config.Config.LangKey; +import net.minecraftforge.common.config.Config.RangeDouble; +import net.minecraftforge.common.config.Config.RangeInt; +import net.minecraftforge.fml.common.FMLLog; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.LoaderException; +import net.minecraftforge.fml.common.discovery.ASMDataTable; +import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData; +import net.minecraftforge.fml.common.discovery.asm.ModAnnotation.EnumHolder; + +public class ConfigManager +{ + private static Map> asm_data = Maps.newHashMap(); + private static Map, ITypeAdapter> ADAPTERS = Maps.newHashMap(); + private static Map, ITypeAdapter.Map> MAP_ADAPTERS = Maps.newHashMap(); + private static Map CONFIGS = Maps.newHashMap(); + + static + { + register(boolean.class, TypeAdapters.bool); + register(boolean[].class, TypeAdapters.boolA); + register(Boolean.class, TypeAdapters.Bool); + register(Boolean[].class, TypeAdapters.BoolA); + register(float.class, TypeAdapters.flt); + register(float[].class, TypeAdapters.fltA); + register(Float.class, TypeAdapters.Flt); + register(Float[].class, TypeAdapters.FltA); + register(double.class, TypeAdapters.dbl); + register(double[].class, TypeAdapters.dblA); + register(Double.class, TypeAdapters.Dbl); + register(Double[].class, TypeAdapters.DblA); + register(byte.class, TypeAdapters.byt); + register(byte[].class, TypeAdapters.bytA); + register(Byte.class, TypeAdapters.Byt); + register(Byte[].class, TypeAdapters.BytA); + register(char.class, TypeAdapters.chr); + register(char[].class, TypeAdapters.chrA); + register(Character.class, TypeAdapters.Chr); + register(Character[].class, TypeAdapters.ChrA); + register(short.class, TypeAdapters.shrt); + register(short[].class, TypeAdapters.shrtA); + register(Short.class, TypeAdapters.Shrt); + register(Short[].class, TypeAdapters.ShrtA); + register(int.class, TypeAdapters.int_); + register(int[].class, TypeAdapters.intA); + register(Integer.class, TypeAdapters.Int); + register(Integer[].class, TypeAdapters.IntA); + register(String.class, TypeAdapters.Str); + register(String[].class, TypeAdapters.StrA); + } + private static void register(Class cls, ITypeAdapter adpt) + { + ADAPTERS.put(cls, adpt); + if (adpt instanceof ITypeAdapter.Map) + MAP_ADAPTERS.put(cls, (ITypeAdapter.Map)adpt); + } + + public static void loadData(ASMDataTable data) + { + FMLLog.fine("Loading @Config anotation data"); + for (ASMData target : data.getAll(Config.class.getName())) + { + String modid = (String)target.getAnnotationInfo().get("modid"); + Multimap map = asm_data.get(modid); + if (map == null) + { + map = ArrayListMultimap.create(); + asm_data.put(modid, map); + } + + EnumHolder tholder = (EnumHolder)target.getAnnotationInfo().get("type"); + Config.Type type = tholder == null ? Config.Type.INSTANCE : Config.Type.valueOf(tholder.getValue()); + + map.put(type, target); + } + } + + public static void load(String modid, Config.Type type) + { + FMLLog.fine("Attempting to inject @Config classes into %s for type %s", modid, type); + ClassLoader mcl = Loader.instance().getModClassLoader(); + File configDir = Loader.instance().getConfigDir(); + Multimap map = asm_data.get(modid); + + if (map == null) + return; + + for (ASMData targ : map.get(type)) + { + try + { + Class cls = Class.forName(targ.getClassName(), true, mcl); + String name = (String)targ.getAnnotationInfo().get("name"); + if (name == null) + name = modid; + File file = new File(configDir, name + ".cfg"); + + Configuration cfg = CONFIGS.get(file.getAbsolutePath()); + if (cfg == null) + { + cfg = new Configuration(file); + cfg.load(); + CONFIGS.put(file.getAbsolutePath(), cfg); + } + + createConfig(cfg, cls, modid, type == Config.Type.INSTANCE); + + cfg.save(); + + } + catch (Exception e) + { + FMLLog.log(Level.ERROR, e, "An error occurred trying to load a config for %s into %s", modid, targ.getClassName()); + throw new LoaderException(e); + } + } + } + + // ======================================================= + // INTERNAL + // ======================================================= + private static void createConfig(Configuration cfg, Class cls, String modid, boolean isStatic) + { + String category = "general"; + for (Field f : cls.getDeclaredFields()) + { + if (!Modifier.isPublic(f.getModifiers())) + continue; + if (Modifier.isStatic(f.getModifiers()) != isStatic) + continue; + + createConfig(modid, category, cfg, f.getType(), f, null); + } + } + + private static final Joiner NEW_LINE = Joiner.on('\n'); + private static final Joiner PIPE = Joiner.on('|'); + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static void createConfig(String modid, String category, Configuration cfg, Class ftype, Field f, Object instance) + { + Property prop = null; + + String comment = null; + Comment ca = f.getAnnotation(Comment.class); + if (ca != null) + comment = NEW_LINE.join(ca.value()); + + String langKey = modid + "." + category + "." + f.getName().toLowerCase(Locale.ENGLISH); + LangKey la = f.getAnnotation(LangKey.class); + if (la != null) + langKey = la.value(); + + ITypeAdapter adapter = ADAPTERS.get(ftype); + + if (adapter != null) + { + prop = adapter.getProp(cfg, category, f, instance, comment); + set(instance, f, adapter.getValue(prop)); + } + else if (ftype.getSuperclass() == Enum.class) + { + Enum enu = (Enum)get(instance, f); + prop = cfg.get(category, f.getName(), enu.name(), comment); + prop.setValidationPattern(makePattern((Class)ftype)); + set(instance, f, Enum.valueOf((Class)ftype, prop.getString())); + } + else if (ftype == Map.class) + { + String sub = category + "." + f.getName().toLowerCase(Locale.ENGLISH); + Map m = (Map)get(instance, f); + ParameterizedType type = (ParameterizedType)f.getGenericType(); + Type mtype = type.getActualTypeArguments()[1]; + + cfg.getCategory(sub).setComment(comment); + + for (Entry e : m.entrySet()) + { + ITypeAdapter.Map adpt = MAP_ADAPTERS.get(mtype); + + if (adpt != null) + { + prop = adpt.getProp(cfg, sub, e.getKey(), e.getValue()); + } + else if (mtype instanceof Class && ((Class)mtype).getSuperclass() == Enum.class) + { + prop = TypeAdapters.Str.getProp(cfg, sub, e.getKey(), ((Enum)e.getValue()).name()); + prop.setValidationPattern(makePattern((Class)mtype)); + } + else + throw new RuntimeException("Unknown type in map! " + f.getDeclaringClass() + "/" + f.getName() + " " + mtype); + + prop.setLanguageKey(langKey + "." + e.getKey().toLowerCase(Locale.ENGLISH)); + + } + prop = null; + } + else if (ftype.getSuperclass() == Object.class) //Only support classes that are one level below Object. + { + String sub = category + "." + f.getName().toLowerCase(Locale.ENGLISH); + Object sinst = get(instance, f); + for (Field sf : ftype.getDeclaredFields()) + { + if (!Modifier.isPublic(sf.getModifiers())) + continue; + + createConfig(modid, sub, cfg, sf.getType(), sf, sinst); + } + } + // TODO Lists ? other stuff + else + throw new RuntimeException("Unknown type in config! " + f.getDeclaringClass() + "/" + f.getName() + " " + ftype); + + + if (prop != null) + { + prop.setLanguageKey(langKey); + RangeInt ia = f.getAnnotation(RangeInt.class); + if (ia != null) + { + prop.setMinValue(ia.min()); + prop.setMaxValue(ia.max()); + if (comment != null) + prop.setComment(NEW_LINE.join(new String[]{comment, "Min: " + ia.min(), "Max: " + ia.max()})); + else + prop.setComment(NEW_LINE.join(new String[]{"Min: " + ia.min(), "Max: " + ia.max()})); + } + RangeDouble da = f.getAnnotation(RangeDouble.class); + if (da != null) + { + prop.setMinValue(da.min()); + prop.setMaxValue(da.max()); + if (comment != null) + prop.setComment(NEW_LINE.join(new String[]{comment, "Min: " + da.min(), "Max: " + da.max()})); + else + prop.setComment(NEW_LINE.join(new String[]{"Min: " + da.min(), "Max: " + da.max()})); + } + + //TODO List length values + } + } + private static void set(Object instance, Field f, Object v) + { + try { + f.set(instance, v); + } catch (Exception e) { + e.printStackTrace(); + } + } + private static Object get(Object instance, Field f) + { + try { + return f.get(instance); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + @SuppressWarnings("rawtypes") + private static Pattern makePattern(Class cls) + { + List lst = Lists.newArrayList(); + for (Enum e : cls.getEnumConstants()) + lst.add(e.name()); + return Pattern.compile(PIPE.join(lst)); + } + +} diff --git a/src/main/java/net/minecraftforge/common/config/ITypeAdapter.java b/src/main/java/net/minecraftforge/common/config/ITypeAdapter.java new file mode 100644 index 0000000000..dec99d121b --- /dev/null +++ b/src/main/java/net/minecraftforge/common/config/ITypeAdapter.java @@ -0,0 +1,33 @@ +/* + * Minecraft Forge + * Copyright (c) 2016. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package net.minecraftforge.common.config; + +import java.lang.reflect.Field; + +interface ITypeAdapter +{ + Property getProp(Configuration cfg, String category, Field field, Object instance, String comment); + + Object getValue(Property prop); + + public interface Map extends ITypeAdapter + { + Property getProp(Configuration cfg, String category, String name, Object value); + } +} diff --git a/src/main/java/net/minecraftforge/common/config/TypeAdapters.java b/src/main/java/net/minecraftforge/common/config/TypeAdapters.java new file mode 100644 index 0000000000..ab2dc517c4 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/config/TypeAdapters.java @@ -0,0 +1,462 @@ +/* + * Minecraft Forge + * Copyright (c) 2016. + * + * This library 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 version 2.1 + * of the License. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common.config; + +import java.lang.reflect.Field; +//========================================================= +// Run away thar' be dragons! +//========================================================= + +import com.google.common.primitives.Booleans; +import com.google.common.primitives.Bytes; +import com.google.common.primitives.Doubles; +import com.google.common.primitives.Floats; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Shorts; + +import scala.actors.threadpool.Arrays; + +@SuppressWarnings("unchecked") +class TypeAdapters +{ + /* + * boolean, boolean[], Boolean, Boolean[] + * float, float[], Float, Float[] + * double, double[], Double, Double[] + * byte, byte[], Byte, Byte[] + * char, char[], Character, Character[] + * short, short[], Short, Short[] + * int, int[], Integer, Integer[] + * String, String[] + */ + static ITypeAdapter bool = new TypeAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), getBoolean(instance, field), comment); + } + public Object getValue(Property prop) { + return prop.getBoolean(); + } + }; + static ITypeAdapter boolA = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), (boolean[])getObject(instance, field), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, (boolean[])value, null); + } + public Object getValue(Property prop) { + return prop.getBooleanList(); + } + }; + static ITypeAdapter Bool = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), (Boolean)getObject(instance, field), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, (Boolean)value, null); + } + public Object getValue(Property prop) { + return Boolean.valueOf(prop.getBoolean()); + } + }; + static ITypeAdapter BoolA = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), Booleans.toArray(Arrays.asList((Boolean[])getObject(instance, field))), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, (Boolean)value, null); + } + public Object getValue(Property prop) { + return Booleans.asList(prop.getBooleanList()).toArray(new Boolean[prop.getBooleanList().length]); + } + }; + static ITypeAdapter flt = new TypeAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), getFloat(instance, field), comment); + } + public Object getValue(Property prop) { + return (float)prop.getDouble(); + } + }; + static ITypeAdapter fltA = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), Doubles.toArray(Floats.asList((float[])getObject(instance, field))), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, Doubles.toArray(Floats.asList((float[])value)), null); + } + public Object getValue(Property prop) { + return Floats.toArray(Doubles.asList(prop.getDoubleList())); + } + }; + static ITypeAdapter Flt = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), (Float)getObject(instance, field), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, (Float)value, null); + } + public Object getValue(Property prop) { + return Float.valueOf((float)prop.getDouble()); + } + }; + static ITypeAdapter FltA = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), Doubles.toArray(Arrays.asList((Float[])getObject(instance, field))), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, Doubles.toArray(Arrays.asList((Float[])value)), null); + } + public Object getValue(Property prop) { + return Floats.asList(Floats.toArray(Doubles.asList(prop.getDoubleList()))).toArray(new Float[prop.getDoubleList().length]); + } + }; + static ITypeAdapter dbl = new TypeAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), getDouble(instance, field), comment); + } + public Object getValue(Property prop) { + return prop.getDouble(); + } + }; + static ITypeAdapter dblA = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), (double[])getObject(instance, field), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, (double[])value, null); + } + public Object getValue(Property prop) { + return prop.getDoubleList(); + } + }; + static ITypeAdapter Dbl = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), (Double)getObject(instance, field), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, (Double)value, null); + } + public Object getValue(Property prop) { + return Double.valueOf(prop.getDouble()); + } + }; + static ITypeAdapter DblA = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), Doubles.toArray(Arrays.asList((Double[])getObject(instance, field))), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, Doubles.toArray(Arrays.asList((Double[])value)), null); + } + public Object getValue(Property prop) { + return Doubles.asList(prop.getDoubleList()).toArray(new Double[prop.getDoubleList().length]); + } + }; + static ITypeAdapter byt = new TypeAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), getByte(instance, field), comment, Byte.MIN_VALUE, Byte.MAX_VALUE); + } + public Object getValue(Property prop) { + return (byte)prop.getInt(); + } + }; + static ITypeAdapter bytA = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), Ints.toArray(Bytes.asList((byte[])getObject(instance, field))), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, Ints.toArray(Bytes.asList((byte[])value)), null); + } + public Object getValue(Property prop) { + return Bytes.toArray(Ints.asList(prop.getIntList())); + } + }; + static ITypeAdapter Byt = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), (Byte)getObject(instance, field), comment, Byte.MIN_VALUE, Byte.MAX_VALUE); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, (Byte)value, null, Byte.MIN_VALUE, Byte.MAX_VALUE); + } + public Object getValue(Property prop) { + return Byte.valueOf((byte)prop.getInt()); + } + }; + static ITypeAdapter BytA = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), Ints.toArray(Arrays.asList((Byte[])getObject(instance, field))), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, Ints.toArray(Arrays.asList((Byte[])value)), null); + } + public Object getValue(Property prop) { + return Bytes.asList(Bytes.toArray(Ints.asList(prop.getIntList()))).toArray(new Byte[prop.getIntList().length]); + } + }; + static ITypeAdapter chr = new TypeAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), getChar(instance, field), comment, Character.MIN_VALUE, Character.MAX_VALUE); + } + public Object getValue(Property prop) { + return (char)prop.getInt(); + } + }; + static ITypeAdapter chrA = new MapAdapter() { + private int[] toPrim(char[] v) { + if (v == null) return new int[0]; + int[] ret = new int[v.length]; + for (int x = 0; x < v.length; x++) + ret[x] = v[x]; + return ret; + } + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), toPrim((char[])getObject(instance, field)), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, toPrim((char[])value), null); + } + public Object getValue(Property prop) { + int[] v = prop.getIntList(); + char[] ret = new char[v.length]; + for (int x = 0; x < v.length; x++) + ret[x] = (char)v[x]; + return ret; + } + }; + static ITypeAdapter Chr = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), (Character)getObject(instance, field), comment, Character.MIN_VALUE, Character.MAX_VALUE); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, (Character)value, null, Character.MIN_VALUE, Character.MAX_VALUE); + } + public Object getValue(Property prop) { + return Character.valueOf((char)prop.getInt()); + } + }; + static ITypeAdapter ChrA = new MapAdapter() { + private int[] toPrim(Character[] v) { + if (v == null) return new int[0]; + int[] ret = new int[v.length]; + for (int x = 0; x < v.length; x++) + ret[x] = v[x] == null ? 0 : v[x]; + return ret; + } + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), toPrim((Character[])getObject(instance, field)), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, toPrim((Character[])value), null); + } + public Object getValue(Property prop) { + int[] v = prop.getIntList(); + Character[] ret = new Character[v.length]; + for (int x = 0; x < v.length; x++) + ret[x] = Character.valueOf((char)v[x]); + return ret; + } + }; + static ITypeAdapter shrt = new TypeAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), getShort(instance, field), comment, Short.MIN_VALUE, Short.MAX_VALUE); + } + public Object getValue(Property prop) { + return (short)prop.getInt(); + } + }; + static ITypeAdapter shrtA = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), Ints.toArray(Shorts.asList((short[])getObject(instance, field))), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, Ints.toArray(Shorts.asList((short[])value)), null); + } + public Object getValue(Property prop) { + return Shorts.toArray(Ints.asList(prop.getIntList())); + } + }; + static ITypeAdapter Shrt = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), (Short)getObject(instance, field), comment, Short.MIN_VALUE, Short.MAX_VALUE); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, (Short)value, null, Short.MIN_VALUE, Short.MAX_VALUE); + } + public Object getValue(Property prop) { + return Short.valueOf((short)prop.getInt()); + } + }; + static ITypeAdapter ShrtA = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), Ints.toArray(Arrays.asList((Short[])getObject(instance, field))), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, Ints.toArray(Arrays.asList((Short[])value)), null); + } + public Object getValue(Property prop) { + int[] v = prop.getIntList(); + Short[] ret = new Short[v.length]; + for (int x = 0; x < ret.length; x++) + ret[x] = Short.valueOf((short)v[x]); + return ret; + } + }; + static ITypeAdapter int_ = new TypeAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), getInt(instance, field), comment, Integer.MIN_VALUE, Integer.MAX_VALUE); + } + public Object getValue(Property prop) { + return prop.getInt(); + } + }; + static ITypeAdapter intA = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), (int[])getObject(instance, field), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, (int[])value, null); + } + public Object getValue(Property prop) { + return prop.getIntList(); + } + }; + static ITypeAdapter Int = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), (Integer)getObject(instance, field), comment, Integer.MIN_VALUE, Integer.MAX_VALUE); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, (Integer)value, null, Integer.MIN_VALUE, Integer.MAX_VALUE); + } + public Object getValue(Property prop) { + return (Integer)prop.getInt(); + } + }; + static ITypeAdapter IntA = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), Ints.toArray(Arrays.asList((Integer[])getObject(instance, field))), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, Ints.toArray(Arrays.asList((Integer[])value)), null); + } + public Object getValue(Property prop) { + return Ints.asList(prop.getIntList()).toArray(new Integer[prop.getIntList().length]); + } + }; + static ITypeAdapter.Map Str = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), (String)getObject(instance, field), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, (String)value, null); + } + public Object getValue(Property prop) { + return prop.getString(); + } + }; + static ITypeAdapter StrA = new MapAdapter() { + public Property getProp(Configuration cfg, String category, Field field, Object instance, String comment) { + return cfg.get(category, field.getName(), (String[])getObject(instance, field), comment); + } + public Property getProp(Configuration cfg, String category, String name, Object value) { + return cfg.get(category, name, (String[])value, null); + } + public Object getValue(Property prop) { + return prop.getStringList(); + } + }; + + + private static abstract class TypeAdapter implements ITypeAdapter + { + public static boolean getBoolean(Object instance, Field f) + { + try { + return f.getBoolean(instance); + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + public static int getInt(Object instance, Field f) + { + try { + return f.getInt(instance); + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + public static Object getObject(Object instance, Field f) + { + try { + return f.get(instance); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + public static byte getByte(Object instance, Field f) + { + try { + return f.getByte(instance); + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + public static char getChar(Object instance, Field f) + { + try { + return f.getChar(instance); + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + public static double getDouble(Object instance, Field f) + { + try { + return f.getDouble(instance); + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + public static float getFloat(Object instance, Field f) + { + try { + return f.getFloat(instance); + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + public static short getShort(Object instance, Field f) + { + try { + return f.getShort(instance); + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + } + private static abstract class MapAdapter extends TypeAdapter implements ITypeAdapter.Map {} +} diff --git a/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java b/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java index ac424bfc97..a6a67ba94b 100644 --- a/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java +++ b/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java @@ -35,6 +35,8 @@ import java.util.Properties; import java.util.Set; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.config.Config; +import net.minecraftforge.common.config.ConfigManager; import net.minecraftforge.fml.common.Mod.Instance; import net.minecraftforge.fml.common.Mod.Metadata; import net.minecraftforge.fml.common.asm.transformers.BlamingTransformer; @@ -574,6 +576,7 @@ public class FMLModContainer implements ModContainer } ProxyInjector.inject(this, event.getASMHarvestedData(), FMLCommonHandler.instance().getSide(), getLanguageAdapter()); AutomaticEventSubscriber.inject(this, event.getASMHarvestedData(), FMLCommonHandler.instance().getSide()); + ConfigManager.load(this.getModId(), Config.Type.INSTANCE); processFieldAnnotations(event.getASMHarvestedData()); } diff --git a/src/main/java/net/minecraftforge/fml/common/Loader.java b/src/main/java/net/minecraftforge/fml/common/Loader.java index da69d59c59..e026779bb4 100644 --- a/src/main/java/net/minecraftforge/fml/common/Loader.java +++ b/src/main/java/net/minecraftforge/fml/common/Loader.java @@ -35,6 +35,7 @@ import java.util.Set; import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.capabilities.CapabilityManager; +import net.minecraftforge.common.config.ConfigManager; import net.minecraftforge.fml.common.LoaderState.ModState; import net.minecraftforge.fml.common.ModContainer.Disableable; import net.minecraftforge.fml.common.ProgressManager.ProgressBar; @@ -541,6 +542,9 @@ public class Loader } } } + + ConfigManager.loadData(discoverer.getASMTable()); + modController.transition(LoaderState.CONSTRUCTING, false); modController.distributeStateMessage(LoaderState.CONSTRUCTING, modClassLoader, discoverer.getASMTable(), reverseDependencies); diff --git a/src/test/java/net/minecraftforge/debug/ConfigTest.java b/src/test/java/net/minecraftforge/debug/ConfigTest.java new file mode 100644 index 0000000000..484ef2abc3 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/ConfigTest.java @@ -0,0 +1,74 @@ +package net.minecraftforge.debug; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.config.Config; +import net.minecraftforge.common.config.Config.*; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; + +@Mod(modid = ConfigTest.MODID, name = "ConfigTest", version = "1.0") +public class ConfigTest +{ + public static final String MODID = "config_test"; + + @Mod.EventHandler + public void preInit(FMLPreInitializationEvent event) { + MinecraftForge.EVENT_BUS.register(this); + } + + @Config(modid = MODID, type = Type.INSTANCE, name = MODID + "_types") + public static class CONFIG_TYPES + { + public static boolean bool = false; + public static boolean[] boolA = {false, true}; + public static Boolean Bool = false; + public static Boolean[] BoolA = {false, true}; + public static float flt = 1.0f; + public static float[] fltA = {1.0f, 2.0f}; + public static Float Flt = 1.0f; + public static Float[] FltA = {1.0f, 2.0f}; + public static double dbl = 1.0d; + public static double[] dblA = {1.0d, 2.0d}; + public static Double Dbl = 1.0D; + public static Double[] DblA = {1.0D, 2.0D}; + public static byte byt = 1; + public static byte[] bytA = {1, 2}; + public static Byte Byt = 1; + public static Byte[] BytA = {1, 2}; + public static char chr = 'a'; + public static char[] chrA = {'a', 'b'}; + public static Character Chr = 'A'; + public static Character[] ChrA = {'A', 'B'}; + public static short srt = 1; + public static short[] srtA = {1, 2}; + public static Short Srt = 1; + public static Short[] SrtA = {1, 2}; + public static int int_ = 1; + public static int[] intA = {1, 2}; + public static Integer Int = 1; + public static Integer[] IntA = {1, 2}; + public static String Str = "STRING!"; + public static String[] StrA = {"STR", "ING!"}; + public static TEST enu = TEST.BIG; + public static NestedType Inner = new NestedType(); + + public enum TEST { BIG, BAD, WOLF; } + public static class NestedType + { + public String HeyLook = "I'm Inside!"; + } + } + @Config(modid = MODID) + public static class CONFIG_ANNOTATIONS + { + @RangeDouble(min = -10.5, max = 100.5) + public static double DoubleRange = 10.0; + + @RangeInt(min = -10, max = 100) + public static double IntRange = 10; + + @LangKey("this.is.not.a.good.key") + @Comment({"This is a really long", "Multi-line comment"}) + public static String Comments = "Hi Tv!"; + } +}