From 016f1ad8390aa3e7cf1e455e19cfe3e43e7e96cf Mon Sep 17 00:00:00 2001 From: FalsePattern <30945458+FalsePattern@users.noreply.github.com> Date: Fri, 6 May 2022 23:48:24 +0200 Subject: [PATCH] backport config annotations --- .../com/falsepattern/lib/config/Config.java | 27 ++--- .../lib/config/ConfigurationManager.java | 112 ++++++++++++++++++ .../lib/internal/FalsePatternLib.java | 6 +- 3 files changed, 126 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/falsepattern/lib/config/ConfigurationManager.java diff --git a/src/main/java/com/falsepattern/lib/config/Config.java b/src/main/java/com/falsepattern/lib/config/Config.java index 3764399..f6ddf25 100644 --- a/src/main/java/com/falsepattern/lib/config/Config.java +++ b/src/main/java/com/falsepattern/lib/config/Config.java @@ -25,21 +25,6 @@ public @interface Config { */ String category() default "general"; - 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, ElementType.TYPE}) @interface LangKey { @@ -64,6 +49,12 @@ public @interface Config { int max() default Integer.MAX_VALUE; } + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + @interface DefaultInt { + int value() default 0; + } + @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @interface RangeDouble { @@ -72,6 +63,12 @@ public @interface Config { double max() default Double.MAX_VALUE; } + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + @interface DefaultDouble { + double value() default 0d; + } + @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @interface Name { diff --git a/src/main/java/com/falsepattern/lib/config/ConfigurationManager.java b/src/main/java/com/falsepattern/lib/config/ConfigurationManager.java new file mode 100644 index 0000000..a747e81 --- /dev/null +++ b/src/main/java/com/falsepattern/lib/config/ConfigurationManager.java @@ -0,0 +1,112 @@ +package com.falsepattern.lib.config; + +import cpw.mods.fml.client.event.ConfigChangedEvent; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.val; +import lombok.var; +import net.minecraft.launchwrapper.Launch; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.common.config.Property; + +import java.nio.file.Path; +import java.util.*; + +/** + * Class for controlling the loading of configuration files. + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ConfigurationManager { + private static final Map>> configs = new HashMap<>(); + + private static final ConfigurationManager instance = new ConfigurationManager(); + + private static boolean initialized = false; + + private static Path configDir; + + /** + * Registers a configuration class to be loaded. + * @param config The class to register. + */ + public static void registerConfig(Class config) throws IllegalAccessException { + val cfg = Optional.of(config.getAnnotation(Config.class)).orElseThrow(() -> new IllegalArgumentException("Class " + config.getName() + " does not have a @Config annotation!")); + val cfgSet = configs.computeIfAbsent(cfg.modid(), (ignored) -> new HashSet<>()); + cfgSet.add(config); + processConfig(config); + } + + /** + * Internal, do not use. + */ + public static void init() { + if (initialized) return; + configDir = Launch.minecraftHome.toPath().resolve("config"); + MinecraftForge.EVENT_BUS.register(instance); + initialized = true; + } + + /** + * Internal, do not use. + * @param event The event. + */ + @SubscribeEvent + public void onConfigChanged(ConfigChangedEvent.OnConfigChangedEvent event) { + val configClasses = configs.get(event.modID); + if (configClasses == null) + return; + configClasses.forEach((config) -> { + try { + processConfig(config); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + } + + private static void processConfig(Class configClass) throws IllegalAccessException { + val cfg = configClass.getAnnotation(Config.class); + val category = cfg.category(); + val rawConfig = new Configuration(configDir.resolve(cfg.name()).toFile()); + val cat = rawConfig.getCategory(category); + + for (val field: configClass.getDeclaredFields()) { + if (field.getAnnotation(Config.Ignore.class) != null) continue; + field.setAccessible(true); + val comment = Optional.of(field.getAnnotation(Config.Comment.class)).map(Config.Comment::value).map((lines) -> String.join("\n", lines)).orElse(""); + val name = Optional.of(field.getAnnotation(Config.Name.class)).map(Config.Name::value).orElse(field.getName()); + val langKey = Optional.of(field.getAnnotation(Config.LangKey.class)).map(Config.LangKey::value).orElse(name); + var boxed = false; + Property prop = cat.get(name); + prop.comment = comment; + prop.setLanguageKey(langKey); + if ((boxed = field.getType().equals(Integer.class)) || field.getType().equals(int.class)) { + val range = Optional.of(field.getAnnotation(Config.RangeInt.class)); + prop.setMinValue(range.map(Config.RangeInt::min).orElse(Integer.MIN_VALUE)); + prop.setMaxValue(range.map(Config.RangeInt::max).orElse(Integer.MAX_VALUE)); + prop.setDefaultValue(Optional.of(field.getAnnotation(Config.DefaultInt.class)).map(Config.DefaultInt::value).orElse(boxed ? (Integer)field.get(null) : field.getInt(null))); + field.setInt(null, prop.getInt()); + } else if ((boxed = field.getType().equals(Double.class)) || field.getType().equals(double.class)) { + val range = Optional.of(field.getAnnotation(Config.RangeDouble.class)); + prop.setMinValue(range.map(Config.RangeDouble::min).orElse(Double.MIN_VALUE)); + prop.setMaxValue(range.map(Config.RangeDouble::max).orElse(Double.MAX_VALUE)); + prop.setDefaultValue(Optional.of(field.getAnnotation(Config.DefaultDouble.class)).map(Config.DefaultDouble::value).orElse(boxed ? (Double) field.get(null) : field.getDouble(null))); + field.setDouble(null, prop.getDouble()); + } else if ((boxed = field.getType().equals(Boolean.class)) || field.getType().equals(boolean.class)) { + prop.setDefaultValue(boxed ? (Boolean)field.get(null) : field.getBoolean(null)); + field.setBoolean(null, prop.getBoolean()); + } else if (field.getType().equals(String.class)) { + prop.setDefaultValue((String)field.get(null)); + field.set(null, prop.getString()); + } + if (field.isAnnotationPresent(Config.RequiresMcRestart.class)) { + cat.setRequiresMcRestart(true); + } + if (field.isAnnotationPresent(Config.RequiresWorldRestart.class)) { + cat.setRequiresWorldRestart(true); + } + } + } +} diff --git a/src/main/java/com/falsepattern/lib/internal/FalsePatternLib.java b/src/main/java/com/falsepattern/lib/internal/FalsePatternLib.java index a7149ec..d4391ed 100644 --- a/src/main/java/com/falsepattern/lib/internal/FalsePatternLib.java +++ b/src/main/java/com/falsepattern/lib/internal/FalsePatternLib.java @@ -1,19 +1,16 @@ package com.falsepattern.lib.internal; +import com.falsepattern.lib.config.ConfigurationManager; import com.falsepattern.lib.util.ResourceUtil; import com.google.common.eventbus.EventBus; import cpw.mods.fml.common.DummyModContainer; import cpw.mods.fml.common.LoadController; import cpw.mods.fml.common.MetadataCollection; -import cpw.mods.fml.common.ModMetadata; import lombok.Getter; -import lombok.val; import net.minecraft.launchwrapper.Launch; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.Arrays; - /** * Utility class used by FalsePatternLib's internal code. This can change between versions without notice, so do not use this in your code! */ @@ -26,6 +23,7 @@ public class FalsePatternLib extends DummyModContainer { public FalsePatternLib() { super(MetadataCollection.from(ResourceUtil.getResourceFromJar("/mcmod.info", FalsePatternLib.class), Tags.MODID).getMetadataForId(Tags.MODID, null)); + ConfigurationManager.init(); log.info("Version " + Tags.VERSION + " initialized!"); }