mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-11 07:47:41 +00:00
[1.20.x] Add ByteValue, ShortValue and FloatValue to ForgeConfigSpec, cleanup code (#9902)
This commit is contained in:
1 parent
73b0088cb4
commit
8e16765438
2 files changed
+217
-196
No files matched your search
+1
-1
@@ -70,7 +70,7 @@ dependencyResolutionManagement {
|
||||
bundle('terminalconsoleappender', ['terminalconsoleappender', 'jline-reader', 'jline-terminal', 'jline-terminal-jna'])
|
||||
|
||||
// The core of our configuration system, it has many flaws, but it works for the most part
|
||||
version('night-config', '3.6.4')
|
||||
version('night-config', '3.6.7')
|
||||
library('night-config-toml', 'com.electronwill.night-config', 'toml').versionRef('night-config')
|
||||
library('night-config-core', 'com.electronwill.night-config', 'core').versionRef('night-config')
|
||||
bundle('night-config', ['night-config-toml', 'night-config-core'])
|
||||
|
||||
@@ -38,18 +38,16 @@ import com.electronwill.nightconfig.core.UnmodifiableConfig;
|
||||
import com.electronwill.nightconfig.core.file.FileConfig;
|
||||
import com.electronwill.nightconfig.core.utils.UnmodifiableConfigWrapper;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/*
|
||||
/**
|
||||
* Like {@link com.electronwill.nightconfig.core.ConfigSpec} except in builder format, and extended to accept comments, language keys,
|
||||
* and other things Forge configs would find useful.
|
||||
*/
|
||||
public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfig> implements IConfigSpec<ForgeConfigSpec>//TODO: Remove extends and pipe everything through getSpec/getValues?
|
||||
{
|
||||
//TODO: Remove extends and pipe everything through getSpec/getValues?
|
||||
public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfig> implements IConfigSpec<ForgeConfigSpec> {
|
||||
private final Map<List<String>, String> levelComments;
|
||||
private final Map<List<String>, String> levelTranslationKeys;
|
||||
|
||||
@@ -60,6 +58,9 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final Pattern WINDOWS_NEWLINE = Pattern.compile("\r\n");
|
||||
private static final Joiner LINE_JOINER = Joiner.on("\n");
|
||||
private static final Joiner DOT_JOINER = Joiner.on(".");
|
||||
private static final Splitter DOT_SPLITTER = Splitter.on('.');
|
||||
|
||||
private ForgeConfigSpec(UnmodifiableConfig storage, UnmodifiableConfig values, Map<List<String>, String> levelComments, Map<List<String>, String> levelTranslationKeys) {
|
||||
super(storage);
|
||||
@@ -87,8 +88,8 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
(action, path, incorrectValue, correctedValue) ->
|
||||
LOGGER.debug(Logging.CORE, "The comment on key {} does not match the spec. This may create a backup.", DOT_JOINER.join( path )));
|
||||
|
||||
if (config instanceof FileConfig) {
|
||||
((FileConfig) config).save();
|
||||
if (config instanceof FileConfig fileConfig) {
|
||||
fileConfig.save();
|
||||
}
|
||||
}
|
||||
this.afterReload();
|
||||
@@ -99,6 +100,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
setConfig(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCorrecting() {
|
||||
return isCorrecting;
|
||||
}
|
||||
@@ -115,6 +117,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
return this.values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterReload() {
|
||||
this.resetCaches(getValues().valueMap().values());
|
||||
}
|
||||
@@ -129,19 +132,20 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
});
|
||||
}
|
||||
|
||||
public void save()
|
||||
{
|
||||
Preconditions.checkNotNull(childConfig, "Cannot save config value without assigned Config object present");
|
||||
if (childConfig instanceof FileConfig) {
|
||||
((FileConfig)childConfig).save();
|
||||
public void save() {
|
||||
Objects.requireNonNull(childConfig, "Cannot save config value without assigned Config object present");
|
||||
if (childConfig instanceof FileConfig fileConfig) {
|
||||
fileConfig.save();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isCorrect(CommentedConfig config) {
|
||||
LinkedList<String> parentPath = new LinkedList<>();
|
||||
return correct(this.config, config, parentPath, Collections.unmodifiableList( parentPath ), (a, b, c, d) -> {}, null, true) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int correct(CommentedConfig config) {
|
||||
return correct(config, (action, path, incorrectValue, correctedValue) -> {}, null);
|
||||
}
|
||||
@@ -162,15 +166,13 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
return ret;
|
||||
}
|
||||
|
||||
private int correct(UnmodifiableConfig spec, CommentedConfig config, LinkedList<String> parentPath, List<String> parentPathUnmodifiable, CorrectionListener listener, CorrectionListener commentListener, boolean dryRun)
|
||||
{
|
||||
private int correct(UnmodifiableConfig spec, CommentedConfig config, LinkedList<String> parentPath, List<String> parentPathUnmodifiable, CorrectionListener listener, CorrectionListener commentListener, boolean dryRun) {
|
||||
int count = 0;
|
||||
|
||||
Map<String, Object> specMap = spec.valueMap();
|
||||
Map<String, Object> configMap = config.valueMap();
|
||||
|
||||
for (Map.Entry<String, Object> specEntry : specMap.entrySet())
|
||||
{
|
||||
for (Map.Entry<String, Object> specEntry : specMap.entrySet()) {
|
||||
final String key = specEntry.getKey();
|
||||
final Object specValue = specEntry.getValue();
|
||||
final Object configValue = configMap.get(key);
|
||||
@@ -178,20 +180,14 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
|
||||
parentPath.addLast(key);
|
||||
|
||||
if (specValue instanceof Config)
|
||||
{
|
||||
if (configValue instanceof CommentedConfig)
|
||||
{
|
||||
if (specValue instanceof Config) {
|
||||
if (configValue instanceof CommentedConfig) {
|
||||
count += correct((Config)specValue, (CommentedConfig)configValue, parentPath, parentPathUnmodifiable, listener, commentListener, dryRun);
|
||||
if (count > 0 && dryRun)
|
||||
return count;
|
||||
}
|
||||
else if (dryRun)
|
||||
{
|
||||
} else if (dryRun) {
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
CommentedConfig newValue = config.createSubConfig();
|
||||
configMap.put(key, newValue);
|
||||
listener.onCorrect(action, parentPathUnmodifiable, configValue, newValue);
|
||||
@@ -199,10 +195,9 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
count += correct((Config)specValue, newValue, parentPath, parentPathUnmodifiable, listener, commentListener, dryRun);
|
||||
}
|
||||
|
||||
String newComment = levelComments.get(parentPath);
|
||||
@Nullable String newComment = levelComments.get(parentPath);
|
||||
String oldComment = config.getComment(key);
|
||||
if (!stringsMatchIgnoringNewlines(oldComment, newComment))
|
||||
{
|
||||
if (!stringsMatchIgnoringNewlines(oldComment, newComment)) {
|
||||
if(commentListener != null)
|
||||
commentListener.onCorrect(action, parentPathUnmodifiable, oldComment, newComment);
|
||||
|
||||
@@ -211,12 +206,9 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
|
||||
config.setComment(key, newComment);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ValueSpec valueSpec = (ValueSpec)specValue;
|
||||
if (!valueSpec.test(configValue))
|
||||
{
|
||||
if (!valueSpec.test(configValue)) {
|
||||
if (dryRun)
|
||||
return 1;
|
||||
|
||||
@@ -226,8 +218,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
count++;
|
||||
}
|
||||
String oldComment = config.getComment(key);
|
||||
if (!stringsMatchIgnoringNewlines(oldComment, valueSpec.getComment()))
|
||||
{
|
||||
if (!stringsMatchIgnoringNewlines(oldComment, valueSpec.getComment())) {
|
||||
if (commentListener != null)
|
||||
commentListener.onCorrect(action, parentPathUnmodifiable, oldComment, valueSpec.getComment());
|
||||
|
||||
@@ -242,11 +233,9 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
}
|
||||
|
||||
// Second step: removes the unspecified values
|
||||
for (Iterator<Map.Entry<String, Object>> ittr = configMap.entrySet().iterator(); ittr.hasNext();)
|
||||
{
|
||||
for (Iterator<Map.Entry<String, Object>> ittr = configMap.entrySet().iterator(); ittr.hasNext();) {
|
||||
Map.Entry<String, Object> entry = ittr.next();
|
||||
if (!specMap.containsKey(entry.getKey()))
|
||||
{
|
||||
if (!specMap.containsKey(entry.getKey())) {
|
||||
if (dryRun)
|
||||
return 1;
|
||||
|
||||
@@ -260,23 +249,19 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
return count;
|
||||
}
|
||||
|
||||
private boolean stringsMatchIgnoringNewlines(@Nullable Object obj1, @Nullable Object obj2)
|
||||
{
|
||||
if (obj1 instanceof String string1 && obj2 instanceof String string2)
|
||||
{
|
||||
if (!string1.isEmpty() && !string2.isEmpty())
|
||||
{
|
||||
private boolean stringsMatchIgnoringNewlines(@Nullable String string1, @Nullable String string2) {
|
||||
if (string1 != null && string2 != null) {
|
||||
if (!string1.isEmpty() && !string2.isEmpty()) {
|
||||
return WINDOWS_NEWLINE.matcher(string1).replaceAll("\n")
|
||||
.equals(WINDOWS_NEWLINE.matcher(string2).replaceAll("\n"));
|
||||
|
||||
}
|
||||
}
|
||||
// Fallback for when we're not given Strings, or one of them is empty
|
||||
return Objects.equals(obj1, obj2);
|
||||
return Objects.equals(string1, string2);
|
||||
}
|
||||
|
||||
public static class Builder
|
||||
{
|
||||
public static class Builder {
|
||||
private final Config storage = Config.of(LinkedHashMap::new, InMemoryFormat.withUniversalSupport()); // Use LinkedHashMap for consistent ordering
|
||||
private BuilderContext context = new BuilderContext();
|
||||
private final Map<List<String>, String> levelComments = new HashMap<>();
|
||||
@@ -284,7 +269,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
private final List<String> currentPath = new ArrayList<>();
|
||||
private final List<ConfigValue<?>> values = new ArrayList<>();
|
||||
|
||||
//Object
|
||||
//region Objects
|
||||
public <T> ConfigValue<T> define(String path, T defaultValue) {
|
||||
return define(split(path), defaultValue);
|
||||
}
|
||||
@@ -310,8 +295,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
}
|
||||
public <T> ConfigValue<T> define(List<String> path, ValueSpec value, Supplier<T> defaultSupplier) { // This is the root where everything at the end of the day ends up.
|
||||
if (!currentPath.isEmpty()) {
|
||||
List<String> tmp = new ArrayList<>(currentPath.size() + path.size());
|
||||
tmp.addAll(currentPath);
|
||||
List<String> tmp = new ArrayList<>(currentPath);
|
||||
tmp.addAll(path);
|
||||
path = tmp;
|
||||
}
|
||||
@@ -331,7 +315,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
public <V extends Comparable<? super V>> ConfigValue<V> defineInRange(List<String> path, Supplier<V> defaultSupplier, V min, V max, Class<V> clazz) {
|
||||
Range<V> range = new Range<>(clazz, min, max);
|
||||
context.setRange(range);
|
||||
comment("Range: " + range.toString());
|
||||
comment("Range: " + range);
|
||||
if (min.compareTo(max) > 0)
|
||||
throw new IllegalArgumentException("Range min most be less then max.");
|
||||
return define(path, defaultSupplier, range);
|
||||
@@ -348,6 +332,9 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
public <T> ConfigValue<T> defineInList(List<String> path, Supplier<T> defaultSupplier, Collection<? extends T> acceptableValues) {
|
||||
return define(path, defaultSupplier, acceptableValues::contains);
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region Collections
|
||||
public <T> ConfigValue<List<? extends T>> defineList(String path, List<? extends T> defaultValue, Predicate<Object> elementValidator) {
|
||||
return defineList(split(path), defaultValue, elementValidator);
|
||||
}
|
||||
@@ -362,17 +349,17 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
return define(path, new ValueSpec(defaultSupplier, x -> x instanceof List && ((List<?>) x).stream().allMatch( elementValidator ), context, path) {
|
||||
@Override
|
||||
public Object correct(Object value) {
|
||||
if (value == null || !(value instanceof List) || ((List<?>)value).isEmpty()) {
|
||||
if (!(value instanceof List<?> list) || list.isEmpty()) {
|
||||
LOGGER.debug(Logging.CORE, "List on key {} is deemed to need correction. It is null, not a list, or an empty list. Modders, consider defineListAllowEmpty?", path.get(path.size() - 1));
|
||||
return getDefault();
|
||||
}
|
||||
List<?> list = new ArrayList<>((List<?>) value);
|
||||
list.removeIf(elementValidator.negate());
|
||||
if (list.isEmpty()) {
|
||||
final List<?> copy = new ArrayList<>(list);
|
||||
copy.removeIf(elementValidator.negate());
|
||||
if (copy.isEmpty()) {
|
||||
LOGGER.debug(Logging.CORE, "List on key {} is deemed to need correction. It failed validation.", path.get(path.size() - 1));
|
||||
return getDefault();
|
||||
}
|
||||
return list;
|
||||
return copy;
|
||||
}
|
||||
}, defaultSupplier);
|
||||
}
|
||||
@@ -390,22 +377,23 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
return define(path, new ValueSpec(defaultSupplier, x -> x instanceof List && ((List<?>) x).stream().allMatch( elementValidator ), context, path) {
|
||||
@Override
|
||||
public Object correct(Object value) {
|
||||
if (value == null || !(value instanceof List)) {
|
||||
if (!(value instanceof List<?> list)) {
|
||||
LOGGER.debug(Logging.CORE, "List on key {} is deemed to need correction, as it is null or not a list.", path.get(path.size() - 1));
|
||||
return getDefault();
|
||||
}
|
||||
List<?> list = new ArrayList<>((List<?>) value);
|
||||
list.removeIf(elementValidator.negate());
|
||||
if (list.isEmpty()) {
|
||||
final List<?> copy = new ArrayList<>(list);
|
||||
copy.removeIf(elementValidator.negate());
|
||||
if (copy.isEmpty()) {
|
||||
LOGGER.debug(Logging.CORE, "List on key {} is deemed to need correction. It failed validation.", path.get(path.size() - 1));
|
||||
return getDefault();
|
||||
}
|
||||
return list;
|
||||
return copy;
|
||||
}
|
||||
}, defaultSupplier);
|
||||
}
|
||||
//endregion
|
||||
|
||||
//Enum
|
||||
//region Enums
|
||||
public <V extends Enum<V>> EnumValue<V> defineEnum(String path, V defaultValue) {
|
||||
return defineEnum(split(path), defaultValue);
|
||||
}
|
||||
@@ -428,7 +416,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V extends Enum<V>> EnumValue<V> defineEnum(List<String> path, V defaultValue, V... acceptableValues) {
|
||||
return defineEnum(path, defaultValue, (Collection<V>) Arrays.asList(acceptableValues));
|
||||
return defineEnum(path, defaultValue, Arrays.asList(acceptableValues));
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V extends Enum<V>> EnumValue<V> defineEnum(List<String> path, V defaultValue, EnumGetMethod converter, V... acceptableValues) {
|
||||
@@ -445,12 +433,12 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
}
|
||||
public <V extends Enum<V>> EnumValue<V> defineEnum(List<String> path, V defaultValue, EnumGetMethod converter, Collection<V> acceptableValues) {
|
||||
return defineEnum(path, defaultValue, converter, obj -> {
|
||||
if (obj instanceof Enum) {
|
||||
return acceptableValues.contains(obj);
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (obj instanceof Enum) {
|
||||
return acceptableValues.contains(obj);
|
||||
}
|
||||
try {
|
||||
return acceptableValues.contains(converter.get(obj, defaultValue.getDeclaringClass()));
|
||||
} catch (IllegalArgumentException | ClassCastException e) {
|
||||
@@ -483,32 +471,49 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
context.setClazz(clazz);
|
||||
V[] allowedValues = clazz.getEnumConstants();
|
||||
comment("Allowed Values: " + Arrays.stream(allowedValues).filter(validator).map(Enum::name).collect(Collectors.joining(", ")));
|
||||
return new EnumValue<V>(this, define(path, new ValueSpec(defaultSupplier, validator, context, path), defaultSupplier).getPath(), defaultSupplier, converter, clazz);
|
||||
return new EnumValue<>(this, define(path, new ValueSpec(defaultSupplier, validator, context, path), defaultSupplier).getPath(), defaultSupplier, converter, clazz);
|
||||
}
|
||||
//endregion
|
||||
|
||||
//boolean
|
||||
//region booleans
|
||||
public BooleanValue define(String path, boolean defaultValue) {
|
||||
return define(split(path), defaultValue);
|
||||
}
|
||||
public BooleanValue define(List<String> path, boolean defaultValue) {
|
||||
return define(path, (Supplier<Boolean>)() -> defaultValue);
|
||||
return define(path, () -> defaultValue);
|
||||
}
|
||||
public BooleanValue define(String path, Supplier<Boolean> defaultSupplier) {
|
||||
return define(split(path), defaultSupplier);
|
||||
}
|
||||
public BooleanValue define(List<String> path, Supplier<Boolean> defaultSupplier) {
|
||||
return new BooleanValue(this, define(path, defaultSupplier, o -> {
|
||||
if (o instanceof String) return ((String)o).equalsIgnoreCase("true") || ((String)o).equalsIgnoreCase("false");
|
||||
if (o instanceof String s) return s.equalsIgnoreCase("true") || s.equalsIgnoreCase("false");
|
||||
return o instanceof Boolean;
|
||||
}, Boolean.class).getPath(), defaultSupplier);
|
||||
}
|
||||
//endregion
|
||||
|
||||
//Double
|
||||
//region floats
|
||||
public FloatValue defineInRange(String path, float defaultValue, float min, float max) {
|
||||
return defineInRange(split(path), defaultValue, min, max);
|
||||
}
|
||||
public FloatValue defineInRange(List<String> path, float defaultValue, float min, float max) {
|
||||
return defineInRange(path, () -> defaultValue, min, max);
|
||||
}
|
||||
public FloatValue defineInRange(String path, Supplier<Float> defaultSupplier, float min, float max) {
|
||||
return defineInRange(split(path), defaultSupplier, min, max);
|
||||
}
|
||||
public FloatValue defineInRange(List<String> path, Supplier<Float> defaultSupplier, float min, float max) {
|
||||
return new FloatValue(this, defineInRange(path, defaultSupplier, min, max, Float.class).getPath(), defaultSupplier);
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region doubles
|
||||
public DoubleValue defineInRange(String path, double defaultValue, double min, double max) {
|
||||
return defineInRange(split(path), defaultValue, min, max);
|
||||
}
|
||||
public DoubleValue defineInRange(List<String> path, double defaultValue, double min, double max) {
|
||||
return defineInRange(path, (Supplier<Double>)() -> defaultValue, min, max);
|
||||
return defineInRange(path, () -> defaultValue, min, max);
|
||||
}
|
||||
public DoubleValue defineInRange(String path, Supplier<Double> defaultSupplier, double min, double max) {
|
||||
return defineInRange(split(path), defaultSupplier, min, max);
|
||||
@@ -516,13 +521,44 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
public DoubleValue defineInRange(List<String> path, Supplier<Double> defaultSupplier, double min, double max) {
|
||||
return new DoubleValue(this, defineInRange(path, defaultSupplier, min, max, Double.class).getPath(), defaultSupplier);
|
||||
}
|
||||
//endregion
|
||||
|
||||
//Ints
|
||||
//region bytes
|
||||
public ByteValue defineInRange(String path, byte defaultValue, byte min, byte max) {
|
||||
return defineInRange(split(path), defaultValue, min, max);
|
||||
}
|
||||
public ByteValue defineInRange(List<String> path, byte defaultValue, byte min, byte max) {
|
||||
return defineInRange(path, () -> defaultValue, min, max);
|
||||
}
|
||||
public ByteValue defineInRange(String path, Supplier<Byte> defaultSupplier, byte min, byte max) {
|
||||
return defineInRange(split(path), defaultSupplier, min, max);
|
||||
}
|
||||
public ByteValue defineInRange(List<String> path, Supplier<Byte> defaultSupplier, byte min, byte max) {
|
||||
return new ByteValue(this, defineInRange(path, defaultSupplier, min, max, Byte.class).getPath(), defaultSupplier);
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region shorts
|
||||
public ShortValue defineInRange(String path, short defaultValue, short min, short max) {
|
||||
return defineInRange(split(path), defaultValue, min, max);
|
||||
}
|
||||
public ShortValue defineInRange(List<String> path, short defaultValue, short min, short max) {
|
||||
return defineInRange(path, () -> defaultValue, min, max);
|
||||
}
|
||||
public ShortValue defineInRange(String path, Supplier<Short> defaultSupplier, short min, short max) {
|
||||
return defineInRange(split(path), defaultSupplier, min, max);
|
||||
}
|
||||
public ShortValue defineInRange(List<String> path, Supplier<Short> defaultSupplier, short min, short max) {
|
||||
return new ShortValue(this, defineInRange(path, defaultSupplier, min, max, Short.class).getPath(), defaultSupplier);
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region ints
|
||||
public IntValue defineInRange(String path, int defaultValue, int min, int max) {
|
||||
return defineInRange(split(path), defaultValue, min, max);
|
||||
}
|
||||
public IntValue defineInRange(List<String> path, int defaultValue, int min, int max) {
|
||||
return defineInRange(path, (Supplier<Integer>)() -> defaultValue, min, max);
|
||||
return defineInRange(path, () -> defaultValue, min, max);
|
||||
}
|
||||
public IntValue defineInRange(String path, Supplier<Integer> defaultSupplier, int min, int max) {
|
||||
return defineInRange(split(path), defaultSupplier, min, max);
|
||||
@@ -530,13 +566,14 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
public IntValue defineInRange(List<String> path, Supplier<Integer> defaultSupplier, int min, int max) {
|
||||
return new IntValue(this, defineInRange(path, defaultSupplier, min, max, Integer.class).getPath(), defaultSupplier);
|
||||
}
|
||||
//endregion
|
||||
|
||||
//Longs
|
||||
//region longs
|
||||
public LongValue defineInRange(String path, long defaultValue, long min, long max) {
|
||||
return defineInRange(split(path), defaultValue, min, max);
|
||||
}
|
||||
public LongValue defineInRange(List<String> path, long defaultValue, long min, long max) {
|
||||
return defineInRange(path, (Supplier<Long>)() -> defaultValue, min, max);
|
||||
return defineInRange(path, () -> defaultValue, min, max);
|
||||
}
|
||||
public LongValue defineInRange(String path, Supplier<Long> defaultSupplier, long min, long max) {
|
||||
return defineInRange(split(path), defaultSupplier, min, max);
|
||||
@@ -544,18 +581,17 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
public LongValue defineInRange(List<String> path, Supplier<Long> defaultSupplier, long min, long max) {
|
||||
return new LongValue(this, defineInRange(path, defaultSupplier, min, max, Long.class).getPath(), defaultSupplier);
|
||||
}
|
||||
//endregion
|
||||
|
||||
public Builder comment(String comment)
|
||||
{
|
||||
public Builder comment(String comment) {
|
||||
context.addComment(comment);
|
||||
return this;
|
||||
}
|
||||
public Builder comment(String... comment)
|
||||
{
|
||||
public Builder comment(String... comment) {
|
||||
// Iterate list first, to throw meaningful errors
|
||||
// Don't add any comments until we make sure there is no nulls
|
||||
for (int i = 0; i < comment.length; i++)
|
||||
Preconditions.checkNotNull(comment[i], "Comment string at " + i + " is null.");
|
||||
Objects.requireNonNull(comment[i], "Comment string at " + i + " is null.");
|
||||
|
||||
for (String s : comment)
|
||||
context.addComment(s);
|
||||
@@ -563,14 +599,12 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder translation(String translationKey)
|
||||
{
|
||||
public Builder translation(String translationKey) {
|
||||
context.setTranslationKey(translationKey);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder worldRestart()
|
||||
{
|
||||
public Builder worldRestart() {
|
||||
context.worldRestart();
|
||||
return this;
|
||||
}
|
||||
@@ -586,7 +620,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
context.clearComment(); // Set to empty
|
||||
}
|
||||
if (context.getTranslationKey() != null) {
|
||||
levelTranslationKeys.put(new ArrayList<String>(currentPath), context.getTranslationKey());
|
||||
levelTranslationKeys.put(new ArrayList<>(currentPath), context.getTranslationKey());
|
||||
context.setTranslationKey(null);
|
||||
}
|
||||
context.ensureEmpty();
|
||||
@@ -610,8 +644,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
return Pair.of(o, this.build());
|
||||
}
|
||||
|
||||
public ForgeConfigSpec build()
|
||||
{
|
||||
public ForgeConfigSpec build() {
|
||||
context.ensureEmpty();
|
||||
Config valueCfg = Config.of(Config.getDefaultMapCreator(true, true), InMemoryFormat.withSupport(ConfigValue.class::isAssignableFrom));
|
||||
values.forEach(v -> valueCfg.set(v.getPath(), v));
|
||||
@@ -626,29 +659,24 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
}
|
||||
}
|
||||
|
||||
private static class BuilderContext
|
||||
{
|
||||
private static class BuilderContext {
|
||||
private final List<String> comment = new LinkedList<>();
|
||||
private String langKey;
|
||||
private Range<?> range;
|
||||
private boolean worldRestart = false;
|
||||
private Class<?> clazz;
|
||||
|
||||
public void addComment(String value)
|
||||
{
|
||||
// Don't use `validate` because it throws IllegalStateException, not NullPointerException
|
||||
Preconditions.checkNotNull(value, "Passed in null value for comment");
|
||||
public void addComment(String value) {
|
||||
Objects.requireNonNull(value, "Passed in null value for comment");
|
||||
|
||||
comment.add(value);
|
||||
}
|
||||
|
||||
public void clearComment() { comment.clear(); }
|
||||
public boolean hasComment() { return this.comment.size() > 0; }
|
||||
public boolean hasComment() { return !this.comment.isEmpty(); }
|
||||
public String buildComment() { return buildComment(List.of("unknown", "unknown")); }
|
||||
public String buildComment(final List<String> path)
|
||||
{
|
||||
if (comment.stream().allMatch(String::isBlank))
|
||||
{
|
||||
public String buildComment(final List<String> path) {
|
||||
if (comment.stream().allMatch(String::isBlank)) {
|
||||
if (FMLEnvironment.production)
|
||||
LOGGER.warn(Logging.CORE, "Detected a comment that is all whitespace for config option {}, which causes obscure bugs in Forge's config system and will cause a crash in the future. Please report this to the mod author.",
|
||||
DOT_JOINER.join(path));
|
||||
@@ -662,8 +690,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
}
|
||||
public void setTranslationKey(String value) { this.langKey = value; }
|
||||
public String getTranslationKey() { return this.langKey; }
|
||||
public <V extends Comparable<? super V>> void setRange(Range<V> value)
|
||||
{
|
||||
public <V extends Comparable<? super V>> void setRange(Range<V> value) {
|
||||
this.range = value;
|
||||
this.setClazz(value.getClazz());
|
||||
}
|
||||
@@ -674,35 +701,30 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
public void setClazz(Class<?> clazz) { this.clazz = clazz; }
|
||||
public Class<?> getClazz(){ return this.clazz; }
|
||||
|
||||
public void ensureEmpty()
|
||||
{
|
||||
public void ensureEmpty() {
|
||||
validate(hasComment(), "Non-empty comment when empty expected");
|
||||
validate(langKey, "Non-null translation key when null expected");
|
||||
validate(range, "Non-null range when null expected");
|
||||
validate(worldRestart, "Dangeling world restart value set to true");
|
||||
}
|
||||
|
||||
private void validate(Object value, String message)
|
||||
{
|
||||
private void validate(Object value, String message) {
|
||||
if (value != null)
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
private void validate(boolean value, String message)
|
||||
{
|
||||
private void validate(boolean value, String message) {
|
||||
if (value)
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class Range<V extends Comparable<? super V>> implements Predicate<Object>
|
||||
{
|
||||
public static class Range<V extends Comparable<? super V>> implements Predicate<Object> {
|
||||
private final Class<? extends V> clazz;
|
||||
private final V min;
|
||||
private final V max;
|
||||
|
||||
private Range(Class<V> clazz, V min, V max)
|
||||
{
|
||||
private Range(Class<V> clazz, V min, V max) {
|
||||
this.clazz = clazz;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
@@ -712,20 +734,16 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
public V getMin() { return min; }
|
||||
public V getMax() { return max; }
|
||||
|
||||
private boolean isNumber(Object other)
|
||||
{
|
||||
private boolean isNumber(Object other) {
|
||||
return Number.class.isAssignableFrom(clazz) && other instanceof Number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Object t)
|
||||
{
|
||||
if (isNumber(t))
|
||||
{
|
||||
public boolean test(Object t) {
|
||||
if (isNumber(t)) {
|
||||
Number n = (Number) t;
|
||||
boolean result = ((Number)min).doubleValue() <= n.doubleValue() && n.doubleValue() <= ((Number)max).doubleValue();
|
||||
if(!result)
|
||||
{
|
||||
if(!result) {
|
||||
LOGGER.debug(Logging.CORE, "Range value {} is not within its bounds {}-{}", n.doubleValue(), ((Number)min).doubleValue(), ((Number)max).doubleValue());
|
||||
}
|
||||
return result;
|
||||
@@ -734,17 +752,14 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
V c = clazz.cast(t);
|
||||
|
||||
boolean result = c.compareTo(min) >= 0 && c.compareTo(max) <= 0;
|
||||
if(!result)
|
||||
{
|
||||
if(!result) {
|
||||
LOGGER.debug(Logging.CORE, "Range value {} is not within its bounds {}-{}", c, min, max);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Object correct(Object value, Object def)
|
||||
{
|
||||
if (isNumber(value))
|
||||
{
|
||||
public Object correct(Object value, Object def) {
|
||||
if (isNumber(value)) {
|
||||
Number n = (Number) value;
|
||||
return n.doubleValue() < ((Number)min).doubleValue() ? min : n.doubleValue() > ((Number)max).doubleValue() ? max : value;
|
||||
}
|
||||
@@ -754,8 +769,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
public String toString() {
|
||||
if (clazz == Integer.class) {
|
||||
if (max.equals(Integer.MAX_VALUE)) {
|
||||
return "> " + min;
|
||||
@@ -767,8 +781,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
}
|
||||
}
|
||||
|
||||
public static class ValueSpec
|
||||
{
|
||||
public static class ValueSpec {
|
||||
private final String comment;
|
||||
private final String langKey;
|
||||
private final Range<?> range;
|
||||
@@ -777,8 +790,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
private final Supplier<?> supplier;
|
||||
private final Predicate<Object> validator;
|
||||
|
||||
private ValueSpec(Supplier<?> supplier, Predicate<Object> validator, BuilderContext context, List<String> path)
|
||||
{
|
||||
private ValueSpec(Supplier<?> supplier, Predicate<Object> validator, BuilderContext context, List<String> path) {
|
||||
Objects.requireNonNull(supplier, "Default supplier can not be null");
|
||||
Objects.requireNonNull(validator, "Validator can not be null");
|
||||
|
||||
@@ -803,9 +815,8 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
public Object getDefault() { return supplier.get(); }
|
||||
}
|
||||
|
||||
public static class ConfigValue<T> implements Supplier<T>
|
||||
{
|
||||
private static boolean USE_CACHES = true;
|
||||
public static class ConfigValue<T> implements Supplier<T> {
|
||||
private static final boolean USE_CACHES = true;
|
||||
|
||||
private final Builder parent;
|
||||
private final List<String> path;
|
||||
@@ -823,8 +834,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
this.parent.values.add(this);
|
||||
}
|
||||
|
||||
public List<String> getPath()
|
||||
{
|
||||
public List<String> getPath() {
|
||||
return new ArrayList<>(path);
|
||||
}
|
||||
|
||||
@@ -837,19 +847,19 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
* @throws IllegalStateException if the associated config has not yet been loaded
|
||||
*/
|
||||
@Override
|
||||
public T get()
|
||||
{
|
||||
Preconditions.checkNotNull(spec, "Cannot get config value before spec is built");
|
||||
public T get() {
|
||||
Objects.requireNonNull(spec, "Cannot get config value before spec is built");
|
||||
// TODO: Remove this dev-time check so this errors out on both production and dev
|
||||
// This is dev-time-only in 1.19.x, to avoid breaking already published mods while forcing devs to fix their errors
|
||||
if (!FMLEnvironment.production)
|
||||
{
|
||||
if (!FMLEnvironment.production) {
|
||||
// When the above if-check is removed, change message to "Cannot get config value before config is loaded"
|
||||
Preconditions.checkState(spec.childConfig != null, """
|
||||
if (spec.childConfig == null) {
|
||||
throw new IllegalStateException("""
|
||||
Cannot get config value before config is loaded.
|
||||
This error is currently only thrown in the development environment, to avoid breaking published mods.
|
||||
In a future version, this will also throw in the production environment.
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
||||
if (spec.childConfig == null)
|
||||
@@ -863,35 +873,30 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
return cachedValue;
|
||||
}
|
||||
|
||||
protected T getRaw(Config config, List<String> path, Supplier<T> defaultSupplier)
|
||||
{
|
||||
protected T getRaw(Config config, List<String> path, Supplier<T> defaultSupplier) {
|
||||
return config.getOrElse(path, defaultSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the default value for the configuration setting}
|
||||
*/
|
||||
public T getDefault()
|
||||
{
|
||||
public T getDefault() {
|
||||
return defaultSupplier.get();
|
||||
}
|
||||
|
||||
public Builder next()
|
||||
{
|
||||
public Builder next() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void save()
|
||||
{
|
||||
Preconditions.checkNotNull(spec, "Cannot save config value before spec is built");
|
||||
Preconditions.checkNotNull(spec.childConfig, "Cannot save config value without assigned Config object present");
|
||||
public void save() {
|
||||
Objects.requireNonNull(spec, "Cannot save config value before spec is built");
|
||||
Objects.requireNonNull(spec.childConfig, "Cannot save config value without assigned Config object present");
|
||||
spec.save();
|
||||
}
|
||||
|
||||
public void set(T value)
|
||||
{
|
||||
Preconditions.checkNotNull(spec, "Cannot set config value before spec is built");
|
||||
Preconditions.checkNotNull(spec.childConfig, "Cannot set config value without assigned Config object present");
|
||||
public void set(T value) {
|
||||
Objects.requireNonNull(spec, "Cannot set config value before spec is built");
|
||||
Objects.requireNonNull(spec.childConfig, "Cannot set config value without assigned Config object present");
|
||||
spec.childConfig.set(path, value);
|
||||
this.cachedValue = value;
|
||||
}
|
||||
@@ -901,81 +906,97 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
|
||||
}
|
||||
}
|
||||
|
||||
public static class BooleanValue extends ConfigValue<Boolean>
|
||||
{
|
||||
BooleanValue(Builder parent, List<String> path, Supplier<Boolean> defaultSupplier)
|
||||
{
|
||||
public static class BooleanValue extends ConfigValue<Boolean> {
|
||||
BooleanValue(Builder parent, List<String> path, Supplier<Boolean> defaultSupplier) {
|
||||
super(parent, path, defaultSupplier);
|
||||
}
|
||||
}
|
||||
|
||||
public static class IntValue extends ConfigValue<Integer>
|
||||
{
|
||||
IntValue(Builder parent, List<String> path, Supplier<Integer> defaultSupplier)
|
||||
{
|
||||
public static class ByteValue extends ConfigValue<Byte> {
|
||||
ByteValue(Builder parent, List<String> path, Supplier<Byte> defaultSupplier) {
|
||||
super(parent, path, defaultSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer getRaw(Config config, List<String> path, Supplier<Integer> defaultSupplier)
|
||||
{
|
||||
return config.getIntOrElse(path, () -> defaultSupplier.get());
|
||||
protected Byte getRaw(Config config, List<String> path, Supplier<Byte> defaultSupplier) {
|
||||
return config.getByteOrElse(path, defaultSupplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
public static class LongValue extends ConfigValue<Long>
|
||||
{
|
||||
LongValue(Builder parent, List<String> path, Supplier<Long> defaultSupplier)
|
||||
{
|
||||
public static class ShortValue extends ConfigValue<Short> {
|
||||
ShortValue(Builder parent, List<String> path, Supplier<Short> defaultSupplier) {
|
||||
super(parent, path, defaultSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getRaw(Config config, List<String> path, Supplier<Long> defaultSupplier)
|
||||
{
|
||||
return config.getLongOrElse(path, () -> defaultSupplier.get());
|
||||
protected Short getRaw(Config config, List<String> path, Supplier<Short> defaultSupplier) {
|
||||
return config.getShortOrElse(path, defaultSupplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
public static class DoubleValue extends ConfigValue<Double>
|
||||
{
|
||||
DoubleValue(Builder parent, List<String> path, Supplier<Double> defaultSupplier)
|
||||
{
|
||||
public static class IntValue extends ConfigValue<Integer> {
|
||||
IntValue(Builder parent, List<String> path, Supplier<Integer> defaultSupplier) {
|
||||
super(parent, path, defaultSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Double getRaw(Config config, List<String> path, Supplier<Double> defaultSupplier)
|
||||
{
|
||||
Number n = config.<Number>get(path);
|
||||
protected Integer getRaw(Config config, List<String> path, Supplier<Integer> defaultSupplier) {
|
||||
return config.getIntOrElse(path, defaultSupplier::get);
|
||||
}
|
||||
}
|
||||
|
||||
public static class LongValue extends ConfigValue<Long> {
|
||||
LongValue(Builder parent, List<String> path, Supplier<Long> defaultSupplier) {
|
||||
super(parent, path, defaultSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getRaw(Config config, List<String> path, Supplier<Long> defaultSupplier) {
|
||||
return config.getLongOrElse(path, defaultSupplier::get);
|
||||
}
|
||||
}
|
||||
|
||||
public static class FloatValue extends ConfigValue<Float> {
|
||||
FloatValue(Builder parent, List<String> path, Supplier<Float> defaultSupplier) {
|
||||
super(parent, path, defaultSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Float getRaw(Config config, List<String> path, Supplier<Float> defaultSupplier) {
|
||||
Number n = config.get(path);
|
||||
return n == null ? defaultSupplier.get() : n.floatValue();
|
||||
}
|
||||
}
|
||||
|
||||
public static class DoubleValue extends ConfigValue<Double> {
|
||||
DoubleValue(Builder parent, List<String> path, Supplier<Double> defaultSupplier) {
|
||||
super(parent, path, defaultSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Double getRaw(Config config, List<String> path, Supplier<Double> defaultSupplier) {
|
||||
Number n = config.get(path);
|
||||
return n == null ? defaultSupplier.get() : n.doubleValue();
|
||||
}
|
||||
}
|
||||
|
||||
public static class EnumValue<T extends Enum<T>> extends ConfigValue<T>
|
||||
{
|
||||
public static class EnumValue<T extends Enum<T>> extends ConfigValue<T> {
|
||||
private final EnumGetMethod converter;
|
||||
private final Class<T> clazz;
|
||||
|
||||
EnumValue(Builder parent, List<String> path, Supplier<T> defaultSupplier, EnumGetMethod converter, Class<T> clazz)
|
||||
{
|
||||
EnumValue(Builder parent, List<String> path, Supplier<T> defaultSupplier, EnumGetMethod converter, Class<T> clazz) {
|
||||
super(parent, path, defaultSupplier);
|
||||
this.converter = converter;
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T getRaw(Config config, List<String> path, Supplier<T> defaultSupplier)
|
||||
{
|
||||
protected T getRaw(Config config, List<String> path, Supplier<T> defaultSupplier) {
|
||||
return config.getEnumOrElse(path, clazz, converter, defaultSupplier);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Joiner LINE_JOINER = Joiner.on("\n");
|
||||
private static final Joiner DOT_JOINER = Joiner.on(".");
|
||||
private static final Splitter DOT_SPLITTER = Splitter.on(".");
|
||||
private static List<String> split(String path)
|
||||
{
|
||||
return Lists.newArrayList(DOT_SPLITTER.split(path));
|
||||
private static List<String> split(String path) {
|
||||
return DOT_SPLITTER.splitToStream(path).toList();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user