From 216945eb55aa020da21a1731e584b581229b8617 Mon Sep 17 00:00:00 2001
From: xpple
Date: Wed, 11 Sep 2024 18:04:03 +0200
Subject: [PATCH 1/3] Add onChange attribute
---
.../dev/xpple/betterconfig/api/Config.java | 20 ++++++
.../impl/BetterConfigInternals.java | 68 ++++++++++++++-----
.../betterconfig/impl/ModConfigImpl.java | 32 ++++++---
.../betterconfig/util/CheckedRunnable.java | 6 ++
.../java/dev/xpple/betterconfig/Configs.java | 6 ++
.../java/dev/xpple/betterconfig/Configs.java | 6 ++
6 files changed, 109 insertions(+), 29 deletions(-)
create mode 100644 common/src/main/java/dev/xpple/betterconfig/util/CheckedRunnable.java
diff --git a/common/src/main/java/dev/xpple/betterconfig/api/Config.java b/common/src/main/java/dev/xpple/betterconfig/api/Config.java
index 7eb0b25..578dc84 100644
--- a/common/src/main/java/dev/xpple/betterconfig/api/Config.java
+++ b/common/src/main/java/dev/xpple/betterconfig/api/Config.java
@@ -53,6 +53,24 @@
*
*
*
+ * To track changes to the configs, you can use the {@link Config#onChange()} attribute.
+ * Its value represents the name of a method where the changes can be tracked. The method
+ * should have two parameters; one for the old value and one for the new value. For
+ * example:
+ *
+ * {@code
+ * @Config(onChange = "exampleOnChange")
+ * public static String exampleString = "defaultString";
+ * public static void exampleOnChange(String oldValue, String newValue) {
+ * System.out.println("Old: " + oldValue + ", new: " + newValue);
+ * }
+ * }
+ *
+ * Both values are deep copies of the config that was changed, so they can be modified
+ * freely without care for the original object.
+ *
+ *
+ *
* To make a configuration unmodifiable by commands, mark it with {@code readOnly = true}.
* Enabling this will ignore all update annotations. To make a configuration temporary,
* that is, to disable loading and saving from a config file, set {@code temporary} to
@@ -83,6 +101,8 @@
Putter putter() default @Putter;
Remover remover() default @Remover;
+ String onChange() default "";
+
boolean readOnly() default false;
boolean temporary() default false;
String condition() default "";
diff --git a/common/src/main/java/dev/xpple/betterconfig/impl/BetterConfigInternals.java b/common/src/main/java/dev/xpple/betterconfig/impl/BetterConfigInternals.java
index 73cdf3b..feb0ec8 100644
--- a/common/src/main/java/dev/xpple/betterconfig/impl/BetterConfigInternals.java
+++ b/common/src/main/java/dev/xpple/betterconfig/impl/BetterConfigInternals.java
@@ -5,6 +5,7 @@
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import dev.xpple.betterconfig.BetterConfigCommon;
import dev.xpple.betterconfig.api.Config;
+import dev.xpple.betterconfig.util.CheckedRunnable;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -19,6 +20,7 @@
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
+import java.util.function.BiConsumer;
public class BetterConfigInternals {
@@ -47,12 +49,12 @@ public static void init(ModConfigImpl, ?> modConfig) {
String fieldName = field.getName();
modConfig.getConfigs().put(fieldName, field);
+ modConfig.getAnnotations().put(fieldName, annotation);
try {
modConfig.getDefaults().put(fieldName, modConfig.getGson().fromJson(modConfig.getGson().toJsonTree(field.get(null)), field.getGenericType()));
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
- modConfig.getAnnotations().put(fieldName, annotation);
if (!annotation.comment().isEmpty()) {
modConfig.getComments().put(fieldName, annotation.comment());
@@ -122,13 +124,36 @@ public static void init(ModConfigImpl, ?> modConfig) {
if (annotation.readOnly()) {
continue;
}
+
+ BiConsumer onChange;
+ String onChangeMethodName = annotation.onChange();
+ if (!onChangeMethodName.isEmpty()) {
+ Method onChangeMethod;
+ try {
+ onChangeMethod = modConfig.getConfigsClass().getDeclaredMethod(onChangeMethodName, field.getType(), field.getType());
+ } catch (ReflectiveOperationException e) {
+ throw new AssertionError(e);
+ }
+ onChangeMethod.setAccessible(true);
+ onChange = (oldValue, newValue) -> {
+ try {
+ onChangeMethod.invoke(null, oldValue, newValue);
+ } catch (ReflectiveOperationException e) {
+ throw new AssertionError(e);
+ }
+ };
+ } else {
+ onChange = (oldValue, newValue) -> {};
+ }
+ modConfig.getOnChangeCallbacks().put(fieldName, onChange);
+
Class> type = field.getType();
if (Collection.class.isAssignableFrom(type)) {
- initCollection(modConfig, field, annotation);
+ initCollection(modConfig, field, annotation, onChange);
} else if (Map.class.isAssignableFrom(type)) {
- initMap(modConfig, field, annotation);
+ initMap(modConfig, field, annotation, onChange);
} else {
- initObject(modConfig, field, annotation);
+ initObject(modConfig, field, annotation, onChange);
}
}
@@ -141,7 +166,7 @@ public static void init(ModConfigImpl, ?> modConfig) {
}
}
- private static void initCollection(ModConfigImpl, ?> modConfig, Field field, Config annotation) {
+ private static void initCollection(ModConfigImpl, ?> modConfig, Field field, Config annotation, BiConsumer onChange) {
String fieldName = field.getName();
Type[] types = ((ParameterizedType) field.getGenericType()).getActualTypeArguments();
Config.Adder adder = annotation.adder();
@@ -157,7 +182,7 @@ private static void initCollection(ModConfigImpl, ?> modConfig, Field field, C
}
modConfig.getAdders().put(fieldName, value -> {
try {
- add.invoke(field.get(null), value);
+ onChange(modConfig, field, () -> add.invoke(field.get(null), value), onChange);
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
@@ -173,7 +198,7 @@ private static void initCollection(ModConfigImpl, ?> modConfig, Field field, C
adderMethod.setAccessible(true);
modConfig.getAdders().put(fieldName, value -> {
try {
- adderMethod.invoke(null, value);
+ onChange(modConfig, field, () -> adderMethod.invoke(null, value), onChange);
} catch (ReflectiveOperationException e) {
if (e.getCause() instanceof CommandSyntaxException commandSyntaxException) {
throw commandSyntaxException;
@@ -195,7 +220,7 @@ private static void initCollection(ModConfigImpl, ?> modConfig, Field field, C
}
modConfig.getRemovers().put(fieldName, value -> {
try {
- remove.invoke(field.get(null), value);
+ onChange(modConfig, field, () -> remove.invoke(field.get(null), value), onChange);
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
@@ -211,7 +236,7 @@ private static void initCollection(ModConfigImpl, ?> modConfig, Field field, C
removerMethod.setAccessible(true);
modConfig.getRemovers().put(fieldName, value -> {
try {
- removerMethod.invoke(null, value);
+ onChange(modConfig, field, () -> removerMethod.invoke(null, value), onChange);
} catch (ReflectiveOperationException e) {
if (e.getCause() instanceof CommandSyntaxException commandSyntaxException) {
throw commandSyntaxException;
@@ -222,7 +247,7 @@ private static void initCollection(ModConfigImpl, ?> modConfig, Field field, C
}
}
- private static void initMap(ModConfigImpl, ?> modConfig, Field field, Config annotation) {
+ private static void initMap(ModConfigImpl, ?> modConfig, Field field, Config annotation, BiConsumer onChange) {
String fieldName = field.getName();
Type[] types = ((ParameterizedType) field.getGenericType()).getActualTypeArguments();
Config.Adder adder = annotation.adder();
@@ -240,7 +265,7 @@ private static void initMap(ModConfigImpl, ?> modConfig, Field field, Config a
adderMethod.setAccessible(true);
modConfig.getAdders().put(fieldName, key -> {
try {
- adderMethod.invoke(null, key);
+ onChange(modConfig, field, () -> adderMethod.invoke(null, key), onChange);
} catch (ReflectiveOperationException e) {
if (e.getCause() instanceof CommandSyntaxException commandSyntaxException) {
throw commandSyntaxException;
@@ -262,7 +287,7 @@ private static void initMap(ModConfigImpl, ?> modConfig, Field field, Config a
}
modConfig.getPutters().put(fieldName, (key, value) -> {
try {
- put.invoke(field.get(null), key, value);
+ onChange(modConfig, field, () -> put.invoke(field.get(null), key, value), onChange);
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
@@ -279,7 +304,7 @@ private static void initMap(ModConfigImpl, ?> modConfig, Field field, Config a
putterMethod.setAccessible(true);
modConfig.getPutters().put(fieldName, (key, value) -> {
try {
- putterMethod.invoke(null, key, value);
+ onChange(modConfig, field, () -> putterMethod.invoke(null, key, value), onChange);
} catch (ReflectiveOperationException e) {
if (e.getCause() instanceof CommandSyntaxException commandSyntaxException) {
throw commandSyntaxException;
@@ -301,7 +326,7 @@ private static void initMap(ModConfigImpl, ?> modConfig, Field field, Config a
}
modConfig.getRemovers().put(fieldName, key -> {
try {
- remove.invoke(field.get(null), key);
+ onChange(modConfig, field, () -> remove.invoke(field.get(null), key), onChange);
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
@@ -317,7 +342,7 @@ private static void initMap(ModConfigImpl, ?> modConfig, Field field, Config a
removerMethod.setAccessible(true);
modConfig.getRemovers().put(fieldName, key -> {
try {
- removerMethod.invoke(null, key);
+ onChange(modConfig, field, () -> removerMethod.invoke(null, key), onChange);
} catch (ReflectiveOperationException e) {
if (e.getCause() instanceof CommandSyntaxException commandSyntaxException) {
throw commandSyntaxException;
@@ -328,7 +353,7 @@ private static void initMap(ModConfigImpl, ?> modConfig, Field field, Config a
}
}
- private static void initObject(ModConfigImpl, ?> modConfig, Field field, Config annotation) {
+ private static void initObject(ModConfigImpl, ?> modConfig, Field field, Config annotation, BiConsumer onChange) {
String fieldName = field.getName();
Config.Setter setter = annotation.setter();
String setterMethodName = setter.value();
@@ -337,7 +362,7 @@ private static void initObject(ModConfigImpl, ?> modConfig, Field field, Confi
} else if (setterMethodName.isEmpty()) {
modConfig.getSetters().put(fieldName, value -> {
try {
- field.set(null, value);
+ onChange(modConfig, field, () -> field.set(null, value), onChange);
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
@@ -353,7 +378,7 @@ private static void initObject(ModConfigImpl, ?> modConfig, Field field, Confi
setterMethod.setAccessible(true);
modConfig.getSetters().put(fieldName, value -> {
try {
- setterMethod.invoke(null, value);
+ onChange(modConfig, field, () -> setterMethod.invoke(null, value), onChange);
} catch (ReflectiveOperationException e) {
if (e.getCause() instanceof CommandSyntaxException commandSyntaxException) {
throw commandSyntaxException;
@@ -363,4 +388,11 @@ private static void initObject(ModConfigImpl, ?> modConfig, Field field, Confi
});
}
}
+
+ static void onChange(ModConfigImpl, ?> modConfig, Field field, CheckedRunnable updater, BiConsumer onChange) throws ReflectiveOperationException {
+ Object oldValue = modConfig.deepCopy(field.get(null), field.getGenericType());
+ updater.run();
+ Object newValue = modConfig.deepCopy(field.get(null), field.getGenericType());
+ onChange.accept(oldValue, newValue);
+ }
}
diff --git a/common/src/main/java/dev/xpple/betterconfig/impl/ModConfigImpl.java b/common/src/main/java/dev/xpple/betterconfig/impl/ModConfigImpl.java
index 03389fc..b7e4e4d 100644
--- a/common/src/main/java/dev/xpple/betterconfig/impl/ModConfigImpl.java
+++ b/common/src/main/java/dev/xpple/betterconfig/impl/ModConfigImpl.java
@@ -26,6 +26,7 @@
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
+import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -46,14 +47,15 @@ public class ModConfigImpl implements ModConfig {
.build();
private final Map configs = new HashMap<>();
+ private final Map annotations = new HashMap<>();
private final Map defaults = new HashMap<>();
private final Map comments = new HashMap<>();
+ private final Map> conditions = new HashMap<>();
private final Map> setters = new HashMap<>();
private final Map> adders = new HashMap<>();
private final Map> putters = new HashMap<>();
private final Map> removers = new HashMap<>();
- private final Map> conditions = new HashMap<>();
- private final Map annotations = new HashMap<>();
+ private final Map> onChangeCallbacks = new HashMap<>();
private final String modId;
private final Class> configsClass;
@@ -80,7 +82,7 @@ public Class> getConfigsClass() {
return this.configsClass;
}
- public Gson getGson() {
+ Gson getGson() {
return this.gson;
}
@@ -88,7 +90,11 @@ public Map getConfigs() {
return this.configs;
}
- public Map getDefaults() {
+ public Map getAnnotations() {
+ return this.annotations;
+ }
+
+ Map getDefaults() {
return this.defaults;
}
@@ -96,6 +102,10 @@ public Map getComments() {
return this.comments;
}
+ public Map> getConditions() {
+ return this.conditions;
+ }
+
public Map> getSetters() {
return this.setters;
}
@@ -112,12 +122,8 @@ public Map> getRemovers(
return this.removers;
}
- public Map> getConditions() {
- return this.conditions;
- }
-
- public Map getAnnotations() {
- return this.annotations;
+ Map> getOnChangeCallbacks() {
+ return this.onChangeCallbacks;
}
@SuppressWarnings("unchecked")
@@ -160,7 +166,7 @@ public void reset(String config) {
throw new IllegalArgumentException();
}
try {
- field.set(null, this.gson.fromJson(this.gson.toJsonTree(this.defaults.get(config)), field.getGenericType()));
+ BetterConfigInternals.onChange(this, field, () -> field.set(null, this.deepCopy(this.defaults.get(config), field.getGenericType())), this.onChangeCallbacks.get(config));
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
@@ -232,6 +238,10 @@ public Type[] getParameterTypes(String config) {
return ((ParameterizedType) field.getGenericType()).getActualTypeArguments();
}
+ Object deepCopy(Object object, Type type) {
+ return this.gson.fromJson(this.gson.toJsonTree(object), type);
+ }
+
@Override
public boolean save() {
try (BufferedWriter writer = Files.newBufferedWriter(this.getConfigsPath())) {
diff --git a/common/src/main/java/dev/xpple/betterconfig/util/CheckedRunnable.java b/common/src/main/java/dev/xpple/betterconfig/util/CheckedRunnable.java
new file mode 100644
index 0000000..430621f
--- /dev/null
+++ b/common/src/main/java/dev/xpple/betterconfig/util/CheckedRunnable.java
@@ -0,0 +1,6 @@
+package dev.xpple.betterconfig.util;
+
+@FunctionalInterface
+public interface CheckedRunnable {
+ void run() throws E;
+}
diff --git a/fabric/src/testmod/java/dev/xpple/betterconfig/Configs.java b/fabric/src/testmod/java/dev/xpple/betterconfig/Configs.java
index 6f8e7e1..1c843b6 100644
--- a/fabric/src/testmod/java/dev/xpple/betterconfig/Configs.java
+++ b/fabric/src/testmod/java/dev/xpple/betterconfig/Configs.java
@@ -86,4 +86,10 @@ private static void privateSetter(String string) {
@Config
public static StructureType> exampleConvertedArgumentType = StructureType.WOODLAND_MANSION;
+
+ @Config(onChange = "onChange")
+ public static List exampleOnChange = new ArrayList<>(List.of("xpple, earthcomputer"));
+ private static void onChange(List oldValue, List newValue) {
+ System.out.println("Old: " + oldValue + ", new: " + newValue);
+ }
}
diff --git a/paper/src/testplugin/java/dev/xpple/betterconfig/Configs.java b/paper/src/testplugin/java/dev/xpple/betterconfig/Configs.java
index b860580..fc9c7f0 100644
--- a/paper/src/testplugin/java/dev/xpple/betterconfig/Configs.java
+++ b/paper/src/testplugin/java/dev/xpple/betterconfig/Configs.java
@@ -83,4 +83,10 @@ private static void privateSetter(String string) {
@Config
public static Structure exampleCustomArgumentType = Structure.MANSION;
+
+ @Config(onChange = "onChange")
+ public static List exampleOnChange = new ArrayList<>(List.of("xpple, earthcomputer"));
+ private static void onChange(List oldValue, List newValue) {
+ System.out.println("Old: " + oldValue + ", new: " + newValue);
+ }
}
From ec35dcf1ff77505f184a9f6c23584875fb5c6eb1 Mon Sep 17 00:00:00 2001
From: xpple
Date: Thu, 12 Sep 2024 16:52:27 +0200
Subject: [PATCH 2/3] Add fabric-resource-loader-v0
---
fabric/build.gradle | 1 +
fabric/src/main/resources/fabric.mod.json | 1 +
2 files changed, 2 insertions(+)
diff --git a/fabric/build.gradle b/fabric/build.gradle
index 0d0cc69..2cc75ca 100644
--- a/fabric/build.gradle
+++ b/fabric/build.gradle
@@ -77,6 +77,7 @@ dependencies {
}
modImplementation "net.fabricmc:fabric-loader:${project.fabric_loader_version}"
+ modImplementation fabricApi.module("fabric-resource-loader-v0", project.fabric_api_version)
modImplementation fabricApi.module("fabric-command-api-v2", project.fabric_api_version)
}
diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json
index 60e9b8c..676e712 100644
--- a/fabric/src/main/resources/fabric.mod.json
+++ b/fabric/src/main/resources/fabric.mod.json
@@ -33,6 +33,7 @@
"fabricloader": ">=${fabric_loader_version}",
"minecraft": "${minecraft_version}.x",
"java": ">=21",
+ "fabric-resource-loader-v0": "*",
"fabric-command-api-v2": "*"
},
"custom": {
From 984f6a656d2d2d3a5d444c0326a9b70e06f600bc Mon Sep 17 00:00:00 2001
From: xpple
Date: Thu, 12 Sep 2024 17:36:41 +0200
Subject: [PATCH 3/3] Minor refactor
---
.../impl/BetterConfigInternals.java | 137 +++++++++---------
1 file changed, 72 insertions(+), 65 deletions(-)
diff --git a/common/src/main/java/dev/xpple/betterconfig/impl/BetterConfigInternals.java b/common/src/main/java/dev/xpple/betterconfig/impl/BetterConfigInternals.java
index feb0ec8..83ddd07 100644
--- a/common/src/main/java/dev/xpple/betterconfig/impl/BetterConfigInternals.java
+++ b/common/src/main/java/dev/xpple/betterconfig/impl/BetterConfigInternals.java
@@ -76,76 +76,13 @@ public static void init(ModConfigImpl, ?> modConfig) {
}
}
- if (annotation.condition().isEmpty()) {
- modConfig.getConditions().put(fieldName, source -> true);
- } else {
- Method predicateMethod;
- boolean hasParameter = false;
- try {
- predicateMethod = modConfig.getConfigsClass().getDeclaredMethod(annotation.condition());
- } catch (ReflectiveOperationException e) {
- hasParameter = true;
- try {
- Class> commandSourceClass = Platform.current.getCommandSourceClass();
- predicateMethod = modConfig.getConfigsClass().getDeclaredMethod(annotation.condition(), commandSourceClass);
- } catch (ReflectiveOperationException e1) {
- throw new AssertionError(e1);
- }
- }
- if (predicateMethod.getReturnType() != boolean.class) {
- throw new AssertionError("Condition method '" + annotation.condition() + "' does not return boolean");
- }
- if (!Modifier.isStatic(predicateMethod.getModifiers())) {
- throw new AssertionError("Condition method '" + annotation.condition() + "' is not static");
- }
- predicateMethod.setAccessible(true);
-
- Method predicateMethod_f = predicateMethod;
-
- if (hasParameter) {
- modConfig.getConditions().put(fieldName, source -> {
- try {
- return (Boolean) predicateMethod_f.invoke(null, source);
- } catch (ReflectiveOperationException e) {
- throw new AssertionError(e);
- }
- });
- } else {
- modConfig.getConditions().put(fieldName, source -> {
- try {
- return (Boolean) predicateMethod_f.invoke(null);
- } catch (ReflectiveOperationException e) {
- throw new AssertionError(e);
- }
- });
- }
- }
+ initCondition(modConfig, annotation.condition(), fieldName);
if (annotation.readOnly()) {
continue;
}
- BiConsumer onChange;
- String onChangeMethodName = annotation.onChange();
- if (!onChangeMethodName.isEmpty()) {
- Method onChangeMethod;
- try {
- onChangeMethod = modConfig.getConfigsClass().getDeclaredMethod(onChangeMethodName, field.getType(), field.getType());
- } catch (ReflectiveOperationException e) {
- throw new AssertionError(e);
- }
- onChangeMethod.setAccessible(true);
- onChange = (oldValue, newValue) -> {
- try {
- onChangeMethod.invoke(null, oldValue, newValue);
- } catch (ReflectiveOperationException e) {
- throw new AssertionError(e);
- }
- };
- } else {
- onChange = (oldValue, newValue) -> {};
- }
- modConfig.getOnChangeCallbacks().put(fieldName, onChange);
+ BiConsumer onChange = initOnChange(modConfig, field, annotation.onChange());
Class> type = field.getType();
if (Collection.class.isAssignableFrom(type)) {
@@ -166,6 +103,76 @@ public static void init(ModConfigImpl, ?> modConfig) {
}
}
+ private static void initCondition(ModConfigImpl, ?> modConfig, String condition, String fieldName) {
+ if (condition.isEmpty()) {
+ modConfig.getConditions().put(fieldName, source -> true);
+ return;
+ }
+ Method predicateMethod;
+ boolean hasParameter = false;
+ try {
+ predicateMethod = modConfig.getConfigsClass().getDeclaredMethod(condition);
+ } catch (ReflectiveOperationException e) {
+ hasParameter = true;
+ try {
+ Class> commandSourceClass = Platform.current.getCommandSourceClass();
+ predicateMethod = modConfig.getConfigsClass().getDeclaredMethod(condition, commandSourceClass);
+ } catch (ReflectiveOperationException e1) {
+ throw new AssertionError(e1);
+ }
+ }
+ if (predicateMethod.getReturnType() != boolean.class) {
+ throw new AssertionError("Condition method '" + condition + "' does not return boolean");
+ }
+ if (!Modifier.isStatic(predicateMethod.getModifiers())) {
+ throw new AssertionError("Condition method '" + condition + "' is not static");
+ }
+ predicateMethod.setAccessible(true);
+
+ Method predicateMethod_f = predicateMethod;
+
+ if (hasParameter) {
+ modConfig.getConditions().put(fieldName, source -> {
+ try {
+ return (Boolean) predicateMethod_f.invoke(null, source);
+ } catch (ReflectiveOperationException e) {
+ throw new AssertionError(e);
+ }
+ });
+ } else {
+ modConfig.getConditions().put(fieldName, source -> {
+ try {
+ return (Boolean) predicateMethod_f.invoke(null);
+ } catch (ReflectiveOperationException e) {
+ throw new AssertionError(e);
+ }
+ });
+ }
+ }
+
+ private static BiConsumer initOnChange(ModConfigImpl, ?> modConfig, Field field, String onChangeMethodName) {
+ if (onChangeMethodName.isEmpty()) {
+ return (oldValue, newValue) -> {};
+ }
+ Method onChangeMethod;
+ try {
+ onChangeMethod = modConfig.getConfigsClass().getDeclaredMethod(onChangeMethodName, field.getType(), field.getType());
+ } catch (ReflectiveOperationException e) {
+ throw new AssertionError(e);
+ }
+ onChangeMethod.setAccessible(true);
+ BiConsumer onChange = (oldValue, newValue) -> {
+ try {
+ onChangeMethod.invoke(null, oldValue, newValue);
+ } catch (ReflectiveOperationException e) {
+ throw new AssertionError(e);
+ }
+ };
+
+ modConfig.getOnChangeCallbacks().put(field.getName(), onChange);
+ return onChange;
+ }
+
private static void initCollection(ModConfigImpl, ?> modConfig, Field field, Config annotation, BiConsumer onChange) {
String fieldName = field.getName();
Type[] types = ((ParameterizedType) field.getGenericType()).getActualTypeArguments();