diff --git a/src/main/java/com/falsepattern/lib/config/Config.java b/src/main/java/com/falsepattern/lib/config/Config.java
index 6ff3779..c892b83 100644
--- a/src/main/java/com/falsepattern/lib/config/Config.java
+++ b/src/main/java/com/falsepattern/lib/config/Config.java
@@ -33,9 +33,47 @@ import java.util.function.BiConsumer;
/**
* A modern configuration system to replace the old and obtuse forge config system.
- *
- * Note that just annotating a configuration class with {@link Config} is not enough, you must also register it using
+ *
+ * Note that just annotating a configuration class with {@link Config} is not enough, you MUST also register it using
* {@link ConfigurationManager#initialize(Class[])} or {@link ConfigurationManager#initialize(BiConsumer, Class[])}!
+ *
+ * For each config field, the following annotations are available:
+ *
+ *
+ * @Default* - you MUST add these annotations, they define the default value of the given config
+ * (not having a default is an error.)
+ *
+ * {@link Comment} - you SHOULD add this annotation to every config field and the config class itself, it will
+ * add a comment to the config file for users.
+ *
+ * {@link LangKey} - you MAY add this to provide localization support.
+ *
+ * {@link Ignore} - you MUST add this annotation to additional fields you don't want put in the config file
+ *
+ * {@link RangeInt}, {@link RangeDouble} - you SHOULD add this to Int/IntList, Double/DoubleList configs respectively
+ * to define upper/lower bounds for the values.
+ *
+ * {@link StringMaxLength} - you MAY add this to String/StringList configs if you know a definite maximum length
+ * for them.
+ *
+ * {@link Pattern} - you MAY add this to String/StringList configs to restrict the values using regex.
+ *
+ * {@link ListMaxLength} - you MAY add this to *List configs to limit their maximum length.
+ *
+ * {@link ListFixedLength} - you MAY add this to *List configs to lock them to a fixed length.
+ *
+ * {@link Name} - you SHOULD add this to config properties to give them user-friendly names in the config. SHOULD be
+ * "camelCase".
+ *
+ * {@link RequiresMcRestart}, {@link RequiresWorldRestart} - You MUST add these to configs if they require a game or
+ * world reload accordingly, to avoid broken state!
+ * You MUST NOT add these to configs that are meant to be changed by the user while actively playing.
+ *
+ * {@link Synchronize} - You MAY add this to the config class.
+ * Allows the config to be synchronized from server to client.
+ * Useful for keeping gameplay-specific state consistent.
+ *
+ * {@link NoSync} - You MUST add this to fields that you don't want to be synchronized by {@link Synchronize}.
*/
@StableAPI(since = "0.6.0")
@Documented
@@ -44,34 +82,82 @@ import java.util.function.BiConsumer;
public @interface Config {
/**
* The mod id that this configuration is associated with.
+ * If {@link #customPath()} is not set, this is used for the config file name (.minecraft/config/modid.cfg)
+ * @see #customPath()
*/
@StableAPI.Expose String modid();
/**
- * Root element category, defaults to "general". You must not specify an empty string.
+ * Root element category, defaults to "general". MUST NOT be an empty string.
*/
@StableAPI.Expose String category() default "general";
+ /**
+ * Define category migrations here.
+ * Used to rename config categories without losing data.
+ * @see #pathMigrations()
+ */
+ @StableAPI.Expose(since = "1.5.0") String[] categoryMigrations() default {};
+
+ /**
+ * A custom configuration file path. Relative to the .minecraft/config folder.
+ * The .cfg is automatically appended if no file extension is provided.
+ *
+ * Examples:
+ *
+ * customPath = "MyMod/magic" -> .minecraft/config/MyMod/magic.cfg
+ *
+ * customPath = "mymod.config" -> .minecraft/config/mymod.config
+ *
+ * @see #modid()
+ */
+ @StableAPI.Expose(since = "1.5.0") String customPath() default "";
+
+ /**
+ * Define file migration paths here.
+ * Used to relocate config files from older versions of the mod to a new location.
+ *
+ * NOTE: This only moves the current category, so you can even use this to split up/combine files. If every
+ * category from an old config file has been migrated, the old config file is automatically deleted.
+ *
+ * Only the FIRST successful migration is executed, and no migration happens if the new config file already contains
+ * the category of this config.
+ * @see #customPath()
+ * @see #categoryMigrations()
+ */
+ @StableAPI.Expose(since = "1.5.0") String[] pathMigrations() default {};
+
+ // region Common
+
+ /**
+ * The description of the configuration or the category. Should be in the primary language of the mod.
+ * @see LangKey
+ */
+ @StableAPI(since = "0.6.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.FIELD, ElementType.TYPE})
+ @interface Comment {
+ @StableAPI.Expose String[] value();
+ }
+
/**
* The lang file key of this configuration. Used in config GUIs.
+ *
+ * {@link LangKey#value()} + ".tooltip" is used for the hover tooltips. If the localization is not
+ * present, defaults to {@link Comment#value()}.
+ *
+ * If left empty. it's autogenerated from the modid, category, and config name. (config.MODID.CATEGORY.NAME)
+ *
+ * Also applies to categories, where it gets autogenerated from the modid and category (config.MODID.CATEGORY)
+ * @see Comment
*/
@StableAPI(since = "0.6.0")
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
@interface LangKey {
- @StableAPI.Expose String value();
- }
-
- /**
- * The description of the configuration.
- */
- @StableAPI(since = "0.6.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface Comment {
- @StableAPI.Expose String[] value();
+ @StableAPI.Expose String value() default "";
}
/**
@@ -84,194 +170,6 @@ public @interface Config {
@Target(ElementType.FIELD)
@interface Ignore {}
- /**
- * The default value for a boolean field. Not having a default is deprecated since 0.10, and will be strongly
- * enforced in 0.11+!
- */
- @StableAPI(since = "0.6.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface DefaultBoolean {
- @StableAPI.Expose boolean value();
- }
-
- /**
- * The range of possible values an int config can have.
- */
- @StableAPI(since = "0.6.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface RangeInt {
- @StableAPI.Expose int min() default Integer.MIN_VALUE;
-
- @StableAPI.Expose int max() default Integer.MAX_VALUE;
- }
-
- /**
- * The default value for an int field. Not having a default is deprecated since 0.10, and will be strongly
- * enforced in 0.11+!
- */
- @StableAPI(since = "0.6.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface DefaultInt {
- @StableAPI.Expose int value();
- }
-
- /**
- * The range of possible values a double config can have.
- */
- @StableAPI(since = "0.10.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface RangeDouble {
- @StableAPI.Expose double min() default -Double.MAX_VALUE;
-
- @StableAPI.Expose double max() default Double.MAX_VALUE;
- }
-
- /**
- * The default value for a double field. Not having a default is deprecated since 0.10, and will be strongly
- * enforced in 0.11+!
- */
- @StableAPI(since = "0.10.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface DefaultDouble {
- @StableAPI.Expose double value();
- }
-
- /**
- * The default value for a String field. Not having a default is deprecated since 0.10, and will be strongly
- * enforced in 0.11+!
- */
- @StableAPI(since = "0.6.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface DefaultString {
- @StableAPI.Expose String value();
- }
-
-
- /**
- * This annotation limits the maximum number of characters present in a string configuration.
- *
- * Note: If this annotation is not present, the maximum length will be implicitly set to 256 to avoid malicious
- * synchronizations that would make clients run out of memory!
- *
- * When used with a string list, this limit will apply to each element individually, not to the size of the list as a whole.
- */
- @StableAPI(since = "0.10.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface StringMaxLength {
- @StableAPI.Expose int value();
- }
-
- /**
- * A regex pattern for restricting the allowed strings.
- */
- @StableAPI(since = "0.6.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface Pattern {
- @StableAPI.Expose String value();
- }
-
- /**
- * The default value for an Enum field. Not having a default is deprecated since 0.10, and will be strongly
- * enforced in 0.11+!
- */
- @StableAPI(since = "0.6.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface DefaultEnum {
- @StableAPI.Expose String value();
- }
-
- /**
- * The default value for a string array field. Not having a default is deprecated since 0.10, and will be strongly
- * enforced in 0.11+!
- */
- @StableAPI(since = "0.6.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface DefaultStringList {
- @StableAPI.Expose String[] value();
- }
-
- /**
- * The default value for a double array field. Not having a default is deprecated since 0.10, and will be strongly
- * enforced in 0.11+!
- */
- @StableAPI(since = "0.10.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface DefaultDoubleList {
- @StableAPI.Expose double[] value();
- }
-
- /**
- * The default value for an int array field. Not having a default is deprecated since 0.10, and will be strongly
- * enforced in 0.11+!
- */
- @StableAPI(since = "0.10.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface DefaultIntList {
- @StableAPI.Expose int[] value();
- }
-
- /**
- * The default value for a boolean array field. Not having a default is deprecated since 0.10, and will be strongly
- * enforced in 0.11+!
- */
- @StableAPI(since = "0.10.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface DefaultBooleanList {
- @StableAPI.Expose boolean[] value();
- }
-
- /**
- * If this annotation is present, the list in the config will be forced to have exactly the amount of elements as
- * the default value.
- */
- @StableAPI(since = "0.10.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface ListFixedLength {}
-
- /**
- * This annotation limits the maximum number of elements present in an array configuration. Only effective if
- * {@link ListFixedLength} is NOT present.
- *
- * Note: If this annotation is not present, the maximum length will be implicitly set to 256 to avoid malicious
- * synchronizations that would make clients run out of memory!
- */
- @StableAPI(since = "0.10.0")
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- @interface ListMaxLength {
- @StableAPI.Expose int value();
- }
-
-
/**
* The name of this config property in the config file. If not specified, the field's name will be used instead.
*/
@@ -281,6 +179,11 @@ public @interface Config {
@Target(ElementType.FIELD)
@interface Name {
@StableAPI.Expose String value();
+
+ /**
+ * Use this to migrate from old config names. "" is resolved to the name of the field
+ */
+ @StableAPI.Expose(since = "1.5.0") String[] migrations() default {};
}
/**
@@ -301,6 +204,10 @@ public @interface Config {
@Target({ElementType.FIELD, ElementType.TYPE})
@interface RequiresWorldRestart {}
+ // endregion
+
+ // region Sync
+
/**
* Signals that this configuration class should be synchronized between the client and the server when
* joining a multiplayer instance.
@@ -322,4 +229,221 @@ public @interface Config {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface NoSync {}
+
+ // endregion
+
+ // region Boolean
+
+ /**
+ * The default value for a boolean field. Not having a default is deprecated since 0.10, and will be strongly
+ * enforced in 0.11+!
+ */
+ @StableAPI(since = "0.6.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface DefaultBoolean {
+ @StableAPI.Expose boolean value();
+ }
+
+ // endregion
+
+ // region Int
+
+ /**
+ * The default value for an int field.
+ * @see RangeInt
+ */
+ @StableAPI(since = "0.6.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface DefaultInt {
+ @StableAPI.Expose int value();
+ }
+
+ /**
+ * The range of possible values an int config can have.
+ */
+ @StableAPI(since = "0.6.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface RangeInt {
+ @StableAPI.Expose int min() default Integer.MIN_VALUE;
+
+ @StableAPI.Expose int max() default Integer.MAX_VALUE;
+ }
+
+ // endregion
+
+ // region Double
+
+ /**
+ * The default value for a double field.
+ * @see RangeDouble
+ */
+ @StableAPI(since = "0.10.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface DefaultDouble {
+ @StableAPI.Expose double value();
+ }
+
+ /**
+ * The range of possible values a double config can have.
+ */
+ @StableAPI(since = "0.10.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface RangeDouble {
+ @StableAPI.Expose double min() default -Double.MAX_VALUE;
+
+ @StableAPI.Expose double max() default Double.MAX_VALUE;
+ }
+
+ // endregion
+
+ // region String
+
+ /**
+ * The default value for a String field.
+ * @see StringMaxLength
+ * @see Pattern
+ */
+ @StableAPI(since = "0.6.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface DefaultString {
+ @StableAPI.Expose String value();
+ }
+
+ /**
+ * This annotation limits the maximum number of characters present in a string configuration.
+ *
+ * When used with a string list, this limit will apply to each element individually, not to the size of the list as a whole.
+ */
+ @StableAPI(since = "0.10.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface StringMaxLength {
+ @StableAPI.Expose int value();
+ }
+
+ /**
+ * A regex pattern for restricting the allowed strings.
+ */
+ @StableAPI(since = "0.6.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface Pattern {
+ @StableAPI.Expose String value();
+ }
+
+ // endregion
+
+ // region Enum
+
+ /**
+ * The default value for an Enum field.
+ */
+ @StableAPI(since = "0.6.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface DefaultEnum {
+ @StableAPI.Expose String value();
+ }
+
+ // endregion
+
+ // region Lists
+
+ /**
+ * The default value for a boolean array field.
+ * @see ListFixedLength
+ * @see ListMaxLength
+ */
+ @StableAPI(since = "0.10.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface DefaultBooleanList {
+ @StableAPI.Expose boolean[] value();
+ }
+
+ /**
+ * The default value for an int array field.
+ * @see ListFixedLength
+ * @see ListMaxLength
+ * @see RangeInt
+ */
+ @StableAPI(since = "0.10.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface DefaultIntList {
+ @StableAPI.Expose int[] value();
+ }
+
+ /**
+ * The default value for a double array field.
+ * @see ListFixedLength
+ * @see ListMaxLength
+ * @see RangeDouble
+ */
+ @StableAPI(since = "0.10.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface DefaultDoubleList {
+ @StableAPI.Expose double[] value();
+ }
+
+ /**
+ * The default value for a string array field.
+ * @see ListFixedLength
+ * @see ListMaxLength
+ * @see StringMaxLength
+ * @see Pattern
+ */
+ @StableAPI(since = "0.6.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface DefaultStringList {
+ @StableAPI.Expose String[] value();
+ }
+
+ /**
+ * If this annotation is present, the list in the config will be forced to have exactly the amount of elements as
+ * the default value.
+ * @see ListMaxLength
+ */
+ @StableAPI(since = "0.10.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface ListFixedLength {}
+
+ /**
+ * This annotation limits the maximum number of elements present in an array configuration. Only effective if
+ * {@link ListFixedLength} is NOT present.
+ */
+ @StableAPI(since = "0.10.0")
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface ListMaxLength {
+ @StableAPI.Expose int value();
+ }
+
+ // endregion
+
+
}
\ No newline at end of file
diff --git a/src/main/java/com/falsepattern/lib/config/ConfigException.java b/src/main/java/com/falsepattern/lib/config/ConfigException.java
index 216b010..0612777 100644
--- a/src/main/java/com/falsepattern/lib/config/ConfigException.java
+++ b/src/main/java/com/falsepattern/lib/config/ConfigException.java
@@ -35,6 +35,11 @@ public class ConfigException extends Exception {
super(message);
}
+ @StableAPI.Internal
+ public ConfigException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
@StableAPI.Internal
public ConfigException(Throwable cause) {
super(cause);
diff --git a/src/main/java/com/falsepattern/lib/config/ConfigurationManager.java b/src/main/java/com/falsepattern/lib/config/ConfigurationManager.java
index 67a1b36..bfcaf41 100644
--- a/src/main/java/com/falsepattern/lib/config/ConfigurationManager.java
+++ b/src/main/java/com/falsepattern/lib/config/ConfigurationManager.java
@@ -30,6 +30,7 @@ import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.val;
+import cpw.mods.fml.client.config.DummyConfigElement;
import cpw.mods.fml.client.config.IConfigElement;
import java.lang.reflect.Field;
@@ -138,4 +139,20 @@ public class ConfigurationManager {
return result;
}
}
+ /**
+ * @return The configuration elements, structured with DummyCategoryElement groups per config class
+ * @see #getConfigElementsMulti(Class[])
+ */
+ @SuppressWarnings("rawtypes")
+ @StableAPI.Expose(since = "1.5.0")
+ public static List getConfigElementsMultiStructured(Class>... configClasses) throws ConfigException {
+ if (configClasses.length == 0) {
+ return Collections.emptyList();
+ }
+ val result = new ArrayList();
+ for (val configClass: configClasses) {
+ result.add(ConfigurationManagerImpl.getConfigCategoryElement(configClass));
+ }
+ return result;
+ }
}
diff --git a/src/main/java/com/falsepattern/lib/config/SimpleGuiConfig.java b/src/main/java/com/falsepattern/lib/config/SimpleGuiConfig.java
index 917eebe..107458c 100644
--- a/src/main/java/com/falsepattern/lib/config/SimpleGuiConfig.java
+++ b/src/main/java/com/falsepattern/lib/config/SimpleGuiConfig.java
@@ -40,14 +40,20 @@ public class SimpleGuiConfig extends GuiConfig {
}
@StableAPI.Expose(since = "0.10.0")
- public SimpleGuiConfig(GuiScreen parent, String modID, String modName, Class>... configClasses)
+ public SimpleGuiConfig(GuiScreen parent, String modID, String title, Class>... configClasses)
+ throws ConfigException {
+ this(parent, modID, title, null, configClasses);
+ }
+
+ @StableAPI.Expose(since = "1.5.0")
+ public SimpleGuiConfig(GuiScreen parent, String modID, String title, String titleLine2, Class>... configClasses)
throws ConfigException {
super(parent,
- ConfigurationManager.getConfigElementsMulti(configClasses),
+ ConfigurationManager.getConfigElementsMultiStructured(configClasses),
modID,
false,
false,
- modName + " Configuration",
- I18n.format("falsepatternlib.gui.config.description"));
+ title,
+ titleLine2);
}
}
diff --git a/src/main/java/com/falsepattern/lib/internal/config/LibraryConfig.java b/src/main/java/com/falsepattern/lib/internal/config/LibraryConfig.java
index 0445d11..15d4791 100644
--- a/src/main/java/com/falsepattern/lib/internal/config/LibraryConfig.java
+++ b/src/main/java/com/falsepattern/lib/internal/config/LibraryConfig.java
@@ -27,7 +27,11 @@ import com.falsepattern.lib.config.Config;
import com.falsepattern.lib.config.ConfigurationManager;
import com.falsepattern.lib.internal.Tags;
-@Config(modid = Tags.MODID)
+@Config.Comment("Miscellaneous FalsePatternLib settings")
+@Config.LangKey
+@Config(modid = Tags.MODID,
+ category = "misc",
+ categoryMigrations = "general")
public class LibraryConfig {
static {
ConfigurationManager.selfInit();
@@ -36,14 +40,19 @@ public class LibraryConfig {
@Config.Comment({"Fixes the mod options menu in-game.",
"By default, the mod options when in already in a game will show \"Test1, Test2, DISABLED\" in bright red.",
"This replaces that interface with the one from the main menu."})
- @Config.LangKey("config.falsepatternlib.ingamemodoptionsfix")
+ @Config.LangKey
@Config.DefaultBoolean(true)
+ @Config.Name(value = "inGameModOptionsFix",
+ migrations = "")
public static boolean IN_GAME_MOD_OPTIONS_FIX;
- @Config.Comment("How \"loud\" the config error logging should be.")
- @Config.LangKey("config.falsepatternlib.configlogging")
+ @Config.Comment({"How the config error logging should be set up.",
+ "[None] disables all built-in config validation error logging"})
+ @Config.LangKey
@Config.DefaultEnum("Log")
- public static ValidationLogging CONFIG_ERROR_LOUDNESS;
+ @Config.Name(value = "configErrorLogging",
+ migrations = "CONFIG_ERROR_LOUDNESS")
+ public static ValidationLogging CONFIG_ERROR_LOGGING;
public enum ValidationLogging {
@StableAPI.Expose(since = "__INTERNAL__") None,
diff --git a/src/main/java/com/falsepattern/lib/internal/config/ToastConfig.java b/src/main/java/com/falsepattern/lib/internal/config/ToastConfig.java
index 16fcea0..a0d2187 100644
--- a/src/main/java/com/falsepattern/lib/internal/config/ToastConfig.java
+++ b/src/main/java/com/falsepattern/lib/internal/config/ToastConfig.java
@@ -27,10 +27,8 @@ import com.falsepattern.lib.config.Config;
import com.falsepattern.lib.config.ConfigurationManager;
import com.falsepattern.lib.internal.Tags;
-import cpw.mods.fml.relauncher.Side;
-import cpw.mods.fml.relauncher.SideOnly;
-
-@SideOnly(Side.CLIENT)
+@Config.Comment("Toast notification settings")
+@Config.LangKey
@Config(modid = Tags.MODID,
category = "toasts")
public class ToastConfig {
@@ -38,23 +36,29 @@ public class ToastConfig {
ConfigurationManager.selfInit();
}
- @Config.Comment("The maximum amount of toasts to show on the screen")
- @Config.LangKey("config.falsepatternlib.maxtoasts")
+ @Config.Comment("The maximum amount of toasts to show on the screen.")
+ @Config.LangKey
@Config.DefaultInt(5)
@Config.RangeInt(min = 1,
max = 10)
+ @Config.Name(value = "maxVisible",
+ migrations = "")
public static int MAX_VISIBLE;
- @Config.Comment("The amount of empty space from the top of the screen in pixels")
- @Config.LangKey("config.falsepatternlib.toastoffset")
+ @Config.Comment("The amount of empty space from the top of the screen in pixels.")
+ @Config.LangKey
@Config.DefaultInt(0)
@Config.RangeInt(min = 0,
max = 10000)
+ @Config.Name(value = "yOffset",
+ migrations = "")
public static int Y_OFFSET;
- @Config.Comment("Which side of the screen should toasts show up on")
- @Config.LangKey("config.falsepatternlib.toastalign")
+ @Config.Comment("Which side of the screen should toasts show up on.")
+ @Config.LangKey
@Config.DefaultEnum("Right")
+ @Config.Name(value = "align",
+ migrations = "")
public static Side ALIGN;
public static boolean leftAlign() {
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/ConfigElementProxy.java b/src/main/java/com/falsepattern/lib/internal/impl/config/ConfigElementProxy.java
index 6f42957..a6dede1 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/ConfigElementProxy.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/ConfigElementProxy.java
@@ -35,10 +35,12 @@ import java.util.regex.Pattern;
public class ConfigElementProxy implements IConfigElement {
private final ConfigElement element;
+ private final String comment;
private final Runnable syncCallback;
- public ConfigElementProxy(Property prop, Runnable syncCallback) {
+ public ConfigElementProxy(Property prop, String comment, Runnable syncCallback) {
this.element = new ConfigElement<>(prop);
+ this.comment = comment;
this.syncCallback = syncCallback;
}
@@ -74,7 +76,7 @@ public class ConfigElementProxy implements IConfigElement {
@Override
public String getComment() {
- return element.getComment();
+ return comment;
}
@Override
@@ -82,6 +84,7 @@ public class ConfigElementProxy implements IConfigElement {
return element.getChildElements();
}
+ // Caller sensitive
@Override
public ConfigGuiType getType() {
return element.getType();
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/ConfigFieldParameters.java b/src/main/java/com/falsepattern/lib/internal/impl/config/ConfigFieldParameters.java
new file mode 100644
index 0000000..828efef
--- /dev/null
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/ConfigFieldParameters.java
@@ -0,0 +1,38 @@
+/*
+ * This file is part of FalsePatternLib.
+ *
+ * Copyright (C) 2022-2024 FalsePattern
+ * All Rights Reserved
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * FalsePatternLib 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.
+ *
+ * FalsePatternLib 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 FalsePatternLib. If not, see .
+ */
+
+package com.falsepattern.lib.internal.impl.config;
+
+import com.github.bsideup.jabel.Desugar;
+
+import net.minecraftforge.common.config.Configuration;
+
+import java.lang.reflect.Field;
+
+@Desugar
+public record ConfigFieldParameters(
+ Field field,
+ Configuration configuration,
+ String modid,
+ String category
+) {}
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/ConfigurationManagerImpl.java b/src/main/java/com/falsepattern/lib/internal/impl/config/ConfigurationManagerImpl.java
index 551ef5a..aff731a 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/ConfigurationManagerImpl.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/ConfigurationManagerImpl.java
@@ -34,8 +34,11 @@ import com.google.common.collect.HashBiMap;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.val;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import net.minecraftforge.common.config.Configuration;
+import cpw.mods.fml.client.config.DummyConfigElement;
import cpw.mods.fml.client.config.IConfigElement;
import cpw.mods.fml.client.event.ConfigChangedEvent;
@@ -47,6 +50,7 @@ import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
@@ -64,7 +68,7 @@ import java.util.function.BiConsumer;
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ConfigurationManagerImpl {
- private static final Map configs = new HashMap<>();
+ private static final Map configs = new HashMap<>();
private static final Map>> configToClassMap = new HashMap<>();
private static final Map, ParsedConfiguration> parsedConfigMap = new HashMap<>();
private static final BiMap> serializedNames = HashBiMap.create();
@@ -81,9 +85,33 @@ public final class ConfigurationManagerImpl {
}
}
- static Configuration getForgeConfig(String modid) {
- return configs.computeIfAbsent(modid, (ignored) -> {
- val c = new Configuration(configDir.resolve(modid + ".cfg").toFile());
+ private static @Nullable Path resolveConfigFile(String pathSuffix, boolean create) throws ConfigException {
+ var thePath = configDir.resolve(pathSuffix);
+ val parent = thePath.getParent();
+ if (!Files.exists(parent)) {
+ if (!create) {
+ return null;
+ }
+ try {
+ Files.createDirectories(parent);
+ } catch (IOException e) {
+ throw new ConfigException(String.format("Failed to create config parent directory at %s", parent), e);
+ }
+ }
+ val fileName = thePath.getFileName().toString();
+ if (!fileName.contains(".")) {
+ thePath = parent.resolve(fileName + ".cfg");
+ }
+ return thePath;
+ }
+
+ static @NotNull Configuration getForgeConfig(String pathSuffix, boolean create) throws ConfigException {
+ val thePath = resolveConfigFile(pathSuffix, create);
+ if (thePath == null || !(create || Files.exists(thePath))) {
+ throw new ConfigException(String.format("Could not find config file at %s", pathSuffix));
+ }
+ return configs.computeIfAbsent(thePath, (ignored) -> {
+ val c = new Configuration(thePath.toFile());
c.load();
return c;
});
@@ -93,14 +121,14 @@ public final class ConfigurationManagerImpl {
if (!parsedConfigMap.containsKey(configClass)) {
throw new ConfigException("Class " + configClass.getName() + " is not a registered configuration!");
}
- parsedConfigMap.get(configClass).load();
+ parsedConfigMap.get(configClass).loadFile();
}
public static void save(Class> configClass) throws ConfigException {
if (!parsedConfigMap.containsKey(configClass)) {
throw new ConfigException("Class " + configClass.getName() + " is not a registered configuration!");
}
- parsedConfigMap.get(configClass).save();
+ parsedConfigMap.get(configClass).saveFile();
}
public static boolean validateFields(BiConsumer, Field> invalidFieldHandler, Class> configClass, boolean resetInvalid)
@@ -235,6 +263,21 @@ public final class ConfigurationManagerImpl {
initialized = true;
}
+ public static DummyConfigElement.DummyCategoryElement> getConfigCategoryElement(Class> configClass) throws ConfigException {
+ if (!parsedConfigMap.containsKey(configClass)) {
+ throw new ConfigException("Class " + configClass.getName() + " is not a registered configuration!");
+ }
+ val config = parsedConfigMap.get(configClass);
+ val entry = new DummyConfigElement.DummyCategoryElement<>(
+ config.category,
+ config.langKey,
+ config.getConfigElements()
+ );
+ entry.setRequiresWorldRestart(config.requiresWorldRestart());
+ entry.setRequiresMcRestart(config.requiresMcRestart());
+ return entry;
+ }
+
public static void sendSyncRequest() throws IOException {
val event = new SyncRequest();
event.transmit();
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/ParsedConfiguration.java b/src/main/java/com/falsepattern/lib/internal/impl/config/ParsedConfiguration.java
index c5cfd87..037e641 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/ParsedConfiguration.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/ParsedConfiguration.java
@@ -46,8 +46,11 @@ import java.io.DataOutput;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -76,6 +79,8 @@ public class ParsedConfiguration {
public final Class> configClass;
public final String modid;
public final String category;
+ public final String comment;
+ public final String langKey;
public final Configuration rawConfig;
public final boolean sync;
private final Map> fields = new HashMap<>();
@@ -88,14 +93,76 @@ public class ParsedConfiguration {
+ configClass.getName()
+ " does not have a @Config annotation!"));
val category = Optional.of(cfg.category().trim())
- .map((cat) -> cat.length() == 0 ? null : cat)
+ .map((cat) -> cat.isEmpty() ? null : cat)
.orElseThrow(() -> new ConfigException("Config class "
+ configClass.getName()
+ " has an empty category!"));
- val rawConfig = ConfigurationManagerImpl.getForgeConfig(cfg.modid());
+ val comment = Optional.ofNullable(configClass.getAnnotation(Config.Comment.class))
+ .map(Config.Comment::value)
+ .map((lines) -> String.join("\n", lines))
+ .orElse("");
+ val langKey = Optional.ofNullable(configClass.getAnnotation(Config.LangKey.class))
+ .map(Config.LangKey::value)
+ .map(x -> x.isEmpty() ? "config." + cfg.modid() + "." + category : x)
+ .orElse(category);
+ val path = Optional.of(cfg.customPath().trim())
+ .map(p -> p.isEmpty() ? null : p)
+ .orElse(cfg.modid());
+ val rawConfig = ConfigurationManagerImpl.getForgeConfig(path, true);
+ if (!rawConfig.hasCategory(category)) {
+ // Process migrations
+ migrate:
+ {
+ val categoryCandidates = new ArrayList<>(Arrays.asList(cfg.categoryMigrations()));
+ categoryCandidates.add(0, category);
+ for (var migration : cfg.pathMigrations()) {
+ migration = migration.trim();
+ try {
+ val oldConfig = ConfigurationManagerImpl.getForgeConfig(migration, false);
+ for (val fromCategory : categoryCandidates) {
+ if (oldConfig.hasCategory(fromCategory)) {
+ val oldCat = oldConfig.getCategory(fromCategory);
+ val newCat = rawConfig.getCategory(category);
+ val entries = oldCat.keySet();
+ for (val entry : entries) {
+ newCat.put(entry, oldCat.get(entry));
+ }
+ rawConfig.save();
+ oldConfig.removeCategory(oldConfig.getCategory(category));
+ if (oldConfig.getCategoryNames().isEmpty()) {
+ oldConfig.getConfigFile().delete();
+ } else {
+ oldConfig.save();
+ }
+ break migrate;
+ }
+ }
+ } catch (ConfigException ignored) {
+ }
+ }
+ // Try to migrate category in local file
+ categoryCandidates.remove(0);
+ for (val fromCategory : categoryCandidates) {
+ if (rawConfig.hasCategory(fromCategory)) {
+ val oldCategory = rawConfig.getCategory(fromCategory);
+ val newCategory = rawConfig.getCategory(category);
+ val entries = oldCategory.keySet();
+ for (val entry: entries) {
+ newCategory.put(entry, oldCategory.get(entry));
+ }
+ rawConfig.removeCategory(oldCategory);
+ break migrate;
+ }
+ }
+ }
+ }
+ rawConfig.setCategoryComment(category, comment);
+ rawConfig.setCategoryLanguageKey(category, langKey);
val parsedConfig = new ParsedConfiguration(configClass,
cfg.modid(),
category,
+ comment,
+ langKey,
rawConfig,
configClass.isAnnotationPresent(Config.Synchronize.class));
try {
@@ -106,11 +173,15 @@ public class ParsedConfiguration {
return parsedConfig;
}
- public void save() {
+ public void saveFile() {
+ saveFields();
+ rawConfig.save();
+ }
+
+ public void saveFields() {
for (val field : fields.values()) {
field.save();
}
- rawConfig.save();
}
//Happens when changed through the gui
@@ -121,8 +192,12 @@ public class ParsedConfiguration {
rawConfig.save();
}
- public void load() throws ConfigException {
+ public void loadFile() throws ConfigException {
ConfigurationManagerImpl.loadRawConfig(rawConfig);
+ loadFields();
+ }
+
+ public void loadFields() {
for (val field : fields.values()) {
field.load();
}
@@ -136,7 +211,7 @@ public class ParsedConfiguration {
syncFields.remove(key);
}
}
- while (syncFields.size() > 0) {
+ while (!syncFields.isEmpty()) {
val fieldName =
StringConfigField.receiveString(input, maxFieldNameLength, "field name", configClass.getName());
if (!syncFields.containsKey(fieldName)) {
@@ -175,6 +250,7 @@ public class ParsedConfiguration {
if (configClass.isAssignableFrom(Config.RequiresMcRestart.class)) {
cat.setRequiresMcRestart(true);
}
+ val nonFoundKeys = new HashSet<>(rawConfig.getCategory(category).keySet());
for (val field : configClass.getDeclaredFields()) {
if (field.getAnnotation(Config.Ignore.class) != null || (field.getModifiers() & Modifier.FINAL) != 0) {
continue;
@@ -182,14 +258,32 @@ public class ParsedConfiguration {
field.setAccessible(true);
maxFieldNameLength = Math.max(maxFieldNameLength, field.getName().length());
val fieldClass = field.getType();
- val name = Optional.ofNullable(field.getAnnotation(Config.Name.class))
- .map(Config.Name::value)
- .orElse(field.getName());
+ val nameAnnotation = Optional.ofNullable(field.getAnnotation(Config.Name.class));
+ val name = nameAnnotation.map(Config.Name::value)
+ .orElse(field.getName());
+ if (!cat.containsKey(name) && nameAnnotation.isPresent()) {
+ val migrations = nameAnnotation.get().migrations();
+ for (var migration: migrations) {
+ if (migration.isEmpty()) {
+ migration = field.getName();
+ }
+ if (cat.containsKey(migration)) {
+ val prop = cat.remove(migration);
+ prop.setName(name);
+ cat.put(name, prop);
+ nonFoundKeys.remove(migration);
+ break;
+ }
+ }
+ } else {
+ nonFoundKeys.remove(name);
+ }
AConfigField> configField;
+ val params = new ConfigFieldParameters(field, rawConfig, modid, category);
if (constructors.containsKey(fieldClass)) {
- fields.put(name, configField = constructors.get(fieldClass).construct(field, rawConfig, category));
+ fields.put(name, configField = constructors.get(fieldClass).construct(params));
} else if (fieldClass.isEnum()) {
- fields.put(name, configField = new EnumConfigField<>(field, rawConfig, category));
+ fields.put(name, configField = new EnumConfigField<>(params));
} else {
throw new ConfigException("Illegal config field: "
+ field.getName()
@@ -206,11 +300,16 @@ public class ParsedConfiguration {
cat.setRequiresWorldRestart(true);
}
configField.init();
- elements.computeIfAbsent(name, (name2) -> new ConfigElementProxy<>(configField.getProperty(), () -> {
+ elements.computeIfAbsent(name, (name2) -> new ConfigElementProxy<>(configField.getProperty(), configField.getComment(), () -> {
configField.load();
- save();
+ configField.save();
}));
}
+ val rawCategory = rawConfig.getCategory(category);
+ for (val key : nonFoundKeys) {
+ rawCategory.remove(key);
+ }
+ saveFile();
rawConfig.setCategoryPropertyOrder(category,
fieldsSorted().map((prop) -> prop.name).collect(Collectors.toList()));
}
@@ -224,6 +323,14 @@ public class ParsedConfiguration {
return fieldsSorted().map((field) -> elements.get(field.name)).collect(Collectors.toList());
}
+ public boolean requiresWorldRestart() {
+ return rawConfig.getCategory(category).requiresWorldRestart();
+ }
+
+ public boolean requiresMcRestart() {
+ return rawConfig.getCategory(category).requiresMcRestart();
+ }
+
public boolean validate(BiConsumer, Field> invalidFieldHandler, boolean resetInvalid) {
boolean valid = true;
for (val field : fields.values()) {
@@ -239,6 +346,6 @@ public class ParsedConfiguration {
}
private interface FieldRefConstructor {
- AConfigField> construct(Field field, Configuration configuration, String category) throws ConfigException;
+ AConfigField> construct(ConfigFieldParameters params) throws ConfigException;
}
}
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/event/ClientEventHandlerPre.java b/src/main/java/com/falsepattern/lib/internal/impl/config/event/ClientEventHandlerPre.java
index 78c4a22..fb6eb2e 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/event/ClientEventHandlerPre.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/event/ClientEventHandlerPre.java
@@ -53,7 +53,7 @@ public class ClientEventHandlerPre {
@SubscribeEvent
@SideOnly(Side.CLIENT)
public void onValidationErrorToast(ConfigValidationFailureEvent e) {
- if (LibraryConfig.CONFIG_ERROR_LOUDNESS == LibraryConfig.ValidationLogging.LogAndToast) {
+ if (LibraryConfig.CONFIG_ERROR_LOGGING == LibraryConfig.ValidationLogging.LogAndToast) {
e.toast();
}
}
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/event/CommonEventHandlerPre.java b/src/main/java/com/falsepattern/lib/internal/impl/config/event/CommonEventHandlerPre.java
index 82ebf7e..8a15f76 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/event/CommonEventHandlerPre.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/event/CommonEventHandlerPre.java
@@ -48,7 +48,7 @@ public class CommonEventHandlerPre {
@SubscribeEvent
public void onValidationErrorLog(ConfigValidationFailureEvent e) {
- if (LibraryConfig.CONFIG_ERROR_LOUDNESS != LibraryConfig.ValidationLogging.None) {
+ if (LibraryConfig.CONFIG_ERROR_LOGGING != LibraryConfig.ValidationLogging.None) {
e.logWarn();
}
}
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/AConfigField.java b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/AConfigField.java
index d62d7e0..7854c5f 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/AConfigField.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/AConfigField.java
@@ -23,7 +23,9 @@
package com.falsepattern.lib.internal.impl.config.fields;
import com.falsepattern.lib.config.Config;
+import com.falsepattern.lib.internal.impl.config.ConfigFieldParameters;
import com.falsepattern.lib.internal.impl.config.DeclOrderInternal;
+import lombok.Getter;
import lombok.val;
import net.minecraftforge.common.config.Configuration;
@@ -44,7 +46,9 @@ public abstract class AConfigField {
protected final String category;
protected final String langKey;
protected final Property.Type type;
+ @Getter
protected final Property property;
+ @Getter
protected final String comment;
private boolean uninitialized;
@@ -58,23 +62,26 @@ public abstract class AConfigField {
+ " annotation!");
}
- protected AConfigField(Field field, Configuration configuration, String category, Property.Type type) {
- this(field, configuration, category, type, false);
+ protected AConfigField(ConfigFieldParameters params, Property.Type type) {
+ this(params, type, false);
}
- protected AConfigField(Field field, Configuration configuration, String category, Property.Type type, boolean isList) {
- this.field = field;
- this.configuration = configuration;
- this.category = category;
+ protected AConfigField(ConfigFieldParameters params, Property.Type type, boolean isList) {
+ this.field = params.field();
+ this.configuration = params.configuration();
+ this.category = params.category();
comment = Optional.ofNullable(field.getAnnotation(Config.Comment.class))
.map(Config.Comment::value)
.map((lines) -> String.join("\n", lines))
.orElse("");
- name = Optional.ofNullable(field.getAnnotation(Config.Name.class))
- .map(Config.Name::value)
- .orElse(field.getName());
+ val nameAnnotation = Optional.ofNullable(field.getAnnotation(Config.Name.class));
+ name = nameAnnotation.map(Config.Name::value)
+ .orElse(field.getName());
langKey =
- Optional.ofNullable(field.getAnnotation(Config.LangKey.class)).map(Config.LangKey::value).orElse(name);
+ Optional.ofNullable(field.getAnnotation(Config.LangKey.class))
+ .map(Config.LangKey::value)
+ .map(x -> x.isEmpty() ? "config." + params.modid() + "." + params.category() + "." + name : x)
+ .orElse(name);
this.type = type;
val cat = configuration.getCategory(category);
uninitialized = !cat.containsKey(name);
@@ -125,13 +132,11 @@ public abstract class AConfigField {
uninitialized = false;
putField(getDefault());
putConfig(getDefault());
+ } else {
+ load();
}
}
- public Property getProperty() {
- return property;
- }
-
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public abstract boolean validateField();
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/AListConfigField.java b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/AListConfigField.java
index 05a29ab..7da7ab4 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/AListConfigField.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/AListConfigField.java
@@ -25,6 +25,7 @@ package com.falsepattern.lib.internal.impl.config.fields;
import com.falsepattern.lib.config.Config;
import com.falsepattern.lib.config.ConfigException;
import com.falsepattern.lib.config.event.ConfigValidationFailureEvent;
+import com.falsepattern.lib.internal.impl.config.ConfigFieldParameters;
import lombok.SneakyThrows;
import lombok.val;
@@ -41,9 +42,9 @@ public abstract class AListConfigField extends AConfigField {
protected final boolean fixedLength;
protected final int maxLength;
- protected AListConfigField(Field field, Configuration configuration, String category, Property.Type type)
+ protected AListConfigField(ConfigFieldParameters params, Property.Type type)
throws ConfigException {
- super(field, configuration, category, type, true);
+ super(params, type, true);
fixedLength = field.isAnnotationPresent(Config.ListFixedLength.class);
maxLength = Optional.ofNullable(field.getAnnotation(Config.ListMaxLength.class))
.map(Config.ListMaxLength::value)
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/BooleanConfigField.java b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/BooleanConfigField.java
index b02fb7d..8bbf852 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/BooleanConfigField.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/BooleanConfigField.java
@@ -24,6 +24,7 @@ package com.falsepattern.lib.internal.impl.config.fields;
import com.falsepattern.lib.config.Config;
import com.falsepattern.lib.config.event.ConfigValidationFailureEvent;
+import com.falsepattern.lib.internal.impl.config.ConfigFieldParameters;
import lombok.SneakyThrows;
import net.minecraftforge.common.config.Configuration;
@@ -39,8 +40,8 @@ public class BooleanConfigField extends AConfigField {
private final boolean primitive;
private final boolean defaultValue;
- public BooleanConfigField(Field field, Configuration configuration, String category) {
- super(field, configuration, category, Property.Type.BOOLEAN);
+ public BooleanConfigField(ConfigFieldParameters params) {
+ super(params, Property.Type.BOOLEAN);
primitive = field.getType().isPrimitive();
defaultValue = Optional.ofNullable(field.getAnnotation(Config.DefaultBoolean.class))
.map(Config.DefaultBoolean::value)
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/BooleanListConfigField.java b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/BooleanListConfigField.java
index aa01bd9..8bccab2 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/BooleanListConfigField.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/BooleanListConfigField.java
@@ -24,6 +24,7 @@ package com.falsepattern.lib.internal.impl.config.fields;
import com.falsepattern.lib.config.Config;
import com.falsepattern.lib.config.ConfigException;
+import com.falsepattern.lib.internal.impl.config.ConfigFieldParameters;
import lombok.val;
import net.minecraftforge.common.config.Configuration;
@@ -39,8 +40,8 @@ import java.util.Optional;
public class BooleanListConfigField extends AListConfigField {
private final boolean[] defaultValue;
- public BooleanListConfigField(Field field, Configuration configuration, String category) throws ConfigException {
- super(field, configuration, category, Property.Type.BOOLEAN);
+ public BooleanListConfigField(ConfigFieldParameters params) throws ConfigException {
+ super(params, Property.Type.BOOLEAN);
defaultValue = Optional.ofNullable(field.getAnnotation(Config.DefaultBooleanList.class))
.map(Config.DefaultBooleanList::value)
.orElseThrow(() -> noDefault(field, "DefaultBooleanList"));
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/DoubleConfigField.java b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/DoubleConfigField.java
index 0bdb186..1aad486 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/DoubleConfigField.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/DoubleConfigField.java
@@ -24,6 +24,7 @@ package com.falsepattern.lib.internal.impl.config.fields;
import com.falsepattern.lib.config.Config;
import com.falsepattern.lib.config.event.ConfigValidationFailureEvent;
+import com.falsepattern.lib.internal.impl.config.ConfigFieldParameters;
import lombok.SneakyThrows;
import lombok.val;
@@ -42,8 +43,8 @@ public class DoubleConfigField extends AConfigField {
private final double min;
private final double max;
- public DoubleConfigField(Field field, Configuration configuration, String category) {
- super(field, configuration, category, Property.Type.DOUBLE);
+ public DoubleConfigField(ConfigFieldParameters params) {
+ super(params, Property.Type.DOUBLE);
primitive = field.getType().isPrimitive();
val range = Optional.ofNullable(field.getAnnotation(Config.RangeDouble.class));
min = range.map(Config.RangeDouble::min).orElse(-Double.MAX_VALUE);
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/DoubleListConfigField.java b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/DoubleListConfigField.java
index dc0d0f8..d7efa8b 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/DoubleListConfigField.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/DoubleListConfigField.java
@@ -25,6 +25,7 @@ package com.falsepattern.lib.internal.impl.config.fields;
import com.falsepattern.lib.config.Config;
import com.falsepattern.lib.config.ConfigException;
import com.falsepattern.lib.config.event.ConfigValidationFailureEvent;
+import com.falsepattern.lib.internal.impl.config.ConfigFieldParameters;
import lombok.val;
import net.minecraftforge.common.config.Configuration;
@@ -42,8 +43,8 @@ public class DoubleListConfigField extends AListConfigField {
private final double max;
private final double[] defaultValue;
- public DoubleListConfigField(Field field, Configuration configuration, String category) throws ConfigException {
- super(field, configuration, category, Property.Type.DOUBLE);
+ public DoubleListConfigField(ConfigFieldParameters params) throws ConfigException {
+ super(params, Property.Type.DOUBLE);
val range = Optional.ofNullable(field.getAnnotation(Config.RangeDouble.class));
min = range.map(Config.RangeDouble::min).orElse(-Double.MAX_VALUE);
max = range.map(Config.RangeDouble::max).orElse(Double.MAX_VALUE);
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/EnumConfigField.java b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/EnumConfigField.java
index 5ea0d22..f3fbbbd 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/EnumConfigField.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/EnumConfigField.java
@@ -25,6 +25,7 @@ package com.falsepattern.lib.internal.impl.config.fields;
import com.falsepattern.lib.config.Config;
import com.falsepattern.lib.config.event.ConfigValidationFailureEvent;
import com.falsepattern.lib.internal.FPLog;
+import com.falsepattern.lib.internal.impl.config.ConfigFieldParameters;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import lombok.SneakyThrows;
@@ -48,8 +49,8 @@ public class EnumConfigField> extends AConfigField {
private final BiMap enumNameMap = HashBiMap.create();
@SuppressWarnings("unchecked")
- public EnumConfigField(Field field, Configuration configuration, String category) {
- super(field, configuration, category, Property.Type.STRING);
+ public EnumConfigField(ConfigFieldParameters params) {
+ super(params, Property.Type.STRING);
enumClass = (Class) field.getType();
val sj = new StringJoiner(", ", "[", "]");
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/IntConfigField.java b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/IntConfigField.java
index 756a90b..3e3b5af 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/IntConfigField.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/IntConfigField.java
@@ -24,6 +24,7 @@ package com.falsepattern.lib.internal.impl.config.fields;
import com.falsepattern.lib.config.Config;
import com.falsepattern.lib.config.event.ConfigValidationFailureEvent;
+import com.falsepattern.lib.internal.impl.config.ConfigFieldParameters;
import lombok.SneakyThrows;
import lombok.val;
@@ -42,8 +43,8 @@ public class IntConfigField extends AConfigField {
private final int min;
private final int max;
- public IntConfigField(Field field, Configuration configuration, String category) {
- super(field, configuration, category, Property.Type.INTEGER);
+ public IntConfigField(ConfigFieldParameters params) {
+ super(params, Property.Type.INTEGER);
primitive = field.getType().isPrimitive();
val range = Optional.ofNullable(field.getAnnotation(Config.RangeInt.class));
min = range.map(Config.RangeInt::min).orElse(Integer.MIN_VALUE);
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/IntListConfigField.java b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/IntListConfigField.java
index d8ac49e..8649571 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/IntListConfigField.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/IntListConfigField.java
@@ -25,6 +25,7 @@ package com.falsepattern.lib.internal.impl.config.fields;
import com.falsepattern.lib.config.Config;
import com.falsepattern.lib.config.ConfigException;
import com.falsepattern.lib.config.event.ConfigValidationFailureEvent;
+import com.falsepattern.lib.internal.impl.config.ConfigFieldParameters;
import lombok.val;
import net.minecraftforge.common.config.Configuration;
@@ -42,8 +43,8 @@ public class IntListConfigField extends AListConfigField {
private final int max;
private final int[] defaultValue;
- public IntListConfigField(Field field, Configuration configuration, String category) throws ConfigException {
- super(field, configuration, category, Property.Type.INTEGER);
+ public IntListConfigField(ConfigFieldParameters params) throws ConfigException {
+ super(params, Property.Type.INTEGER);
val range = Optional.ofNullable(field.getAnnotation(Config.RangeInt.class));
min = range.map(Config.RangeInt::min).orElse(Integer.MIN_VALUE);
max = range.map(Config.RangeInt::max).orElse(Integer.MAX_VALUE);
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/StringConfigField.java b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/StringConfigField.java
index ea0383e..337bc1f 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/StringConfigField.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/StringConfigField.java
@@ -24,6 +24,7 @@ package com.falsepattern.lib.internal.impl.config.fields;
import com.falsepattern.lib.config.Config;
import com.falsepattern.lib.config.event.ConfigValidationFailureEvent;
+import com.falsepattern.lib.internal.impl.config.ConfigFieldParameters;
import lombok.SneakyThrows;
import lombok.val;
@@ -42,8 +43,8 @@ public class StringConfigField extends AConfigField {
private final Pattern pattern;
private final String defaultValue;
- public StringConfigField(Field field, Configuration configuration, String category) {
- super(field, configuration, category, Property.Type.STRING);
+ public StringConfigField(ConfigFieldParameters params) {
+ super(params, Property.Type.STRING);
pattern = Optional.ofNullable(field.getAnnotation(Config.Pattern.class))
.map((ptr) -> Pattern.compile(ptr.value()))
.orElse(null);
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/StringListConfigField.java b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/StringListConfigField.java
index 1da6193..e3eba57 100644
--- a/src/main/java/com/falsepattern/lib/internal/impl/config/fields/StringListConfigField.java
+++ b/src/main/java/com/falsepattern/lib/internal/impl/config/fields/StringListConfigField.java
@@ -24,6 +24,7 @@ package com.falsepattern.lib.internal.impl.config.fields;
import com.falsepattern.lib.config.Config;
import com.falsepattern.lib.config.ConfigException;
+import com.falsepattern.lib.internal.impl.config.ConfigFieldParameters;
import lombok.val;
import net.minecraftforge.common.config.Configuration;
@@ -42,8 +43,8 @@ public class StringListConfigField extends AListConfigField {
private final Pattern pattern;
private final int maxStringLength;
- public StringListConfigField(Field field, Configuration configuration, String category) throws ConfigException {
- super(field, configuration, category, Property.Type.STRING);
+ public StringListConfigField(ConfigFieldParameters params) throws ConfigException {
+ super(params, Property.Type.STRING);
pattern = Optional.ofNullable(field.getAnnotation(Config.Pattern.class))
.map((ptr) -> Pattern.compile(ptr.value()))
.orElse(null);
diff --git a/src/main/resources/assets/falsepatternlib/lang/en_US.lang b/src/main/resources/assets/falsepatternlib/lang/en_US.lang
index 8f542af..29e69a3 100644
--- a/src/main/resources/assets/falsepatternlib/lang/en_US.lang
+++ b/src/main/resources/assets/falsepatternlib/lang/en_US.lang
@@ -1,23 +1,15 @@
-config.falsepatternlib.maxtoasts=Max Toasts
-config.falsepatternlib.maxtoasts.tooltip=The maximum amount of toasts to show on the screen
-config.falsepatternlib.toastoffset=Toast Y Offset
-config.falsepatternlib.toastoffset.tooltip=The amount of empty space from the top of the screen in pixels
-config.falsepatternlib.toastalign=Toast Alignment
-config.falsepatternlib.toastalign.tooltip=Which side of the screen should toasts show up on
-config.falsepatternlib.configlogging=Config error loudness
-config.falsepatternlib.configlogging.tooltip=How loud the config error logging should be. None disables all built-in config validation error logging
-config.falsepatternlib.updatecheck=Enable update checks
-config.falsepatternlib.updatecheck.tooltip=Used to control whether FalsePatternLib should check for outdated mods.
-config.falsepatternlib.disableinternet=FalsePatternLib Offline Mode
-config.falsepatternlib.disableinternet.tooltip=Used to control whether FalsePatternLib should be allowed to use the internet. If this is enabled, update checks and library downloads will be completely blocked.
-config.falsepatternlib.ingamemodoptionsfix=In-Game Mod Options Fix
-config.falsepatternlib.ingamemodoptionsfix.tooltip=Fixes the mod options menu in-game. By default, the mod options when in already in a game will show: "Test1, Test2, DISABLED\" in bright red. This replaces that interface with the one from the main menu.
-falsepatternlib.gui.config.description=Hover over a setting for more information.
-falsepatternlib.chat.updatesavailable=§4Outdated mods detected by %s! §r
-falsepatternlib.chat.modname=§b§l[%s]§r
-falsepatternlib.chat.currentversion= Current Version: §6%s§r
-falsepatternlib.chat.latestversion= Latest Version: §2%s§r
-falsepatternlib.chat.updateurlpre= Click
-falsepatternlib.chat.updateurl= §n§o§dHERE§r
-falsepatternlib.chat.updateurlpost= to visit update page
-falsepatternlib.chat.updateavailablefor=§1§l[%s]§r Current version: §6%s§r Latest version: §2%s§r
+config.falsepatternlib.misc=Miscellaneous
+config.falsepatternlib.misc.tooltip=Settings that don't fit into other categories
+config.falsepatternlib.misc.configErrorLogging=Config error logging
+config.falsepatternlib.misc.configErrorLogging.tooltip=How the config error logging should be set up.\n[None] disables all built-in config validation error logging.
+config.falsepatternlib.misc.inGameModOptionsFix=In-Game Mod Options Fix
+config.falsepatternlib.misc.inGameModOptionsFix.tooltip=Fixes the mod options menu in-game.
+
+config.falsepatternlib.toasts=Toast notifications
+config.falsepatternlib.toasts.tooltip=Common toast notification settings
+config.falsepatternlib.toasts.maxVisible=Max Toasts
+config.falsepatternlib.toasts.maxVisible.tooltip=The maximum amount of toasts to show on the screen.
+config.falsepatternlib.toasts.yOffset=Toast Y Offset
+config.falsepatternlib.toasts.yOffset.tooltip=The amount of empty space from the top of the screen in pixels.
+config.falsepatternlib.toasts.align=Toast Alignment
+config.falsepatternlib.toasts.align.tooltip=Which side of the screen should toasts show up on.
diff --git a/src/main/resources/assets/forge/lang/en_US.lang b/src/main/resources/assets/forge/lang/en_US.lang
new file mode 100644
index 0000000..def7261
--- /dev/null
+++ b/src/main/resources/assets/forge/lang/en_US.lang
@@ -0,0 +1,2 @@
+fml.configgui.tooltip.default=[default: %s]
+fml.configgui.tooltip.defaultNumeric=[range: %s ~ %s, default: %s]
\ No newline at end of file