1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-24 02:17:39 +00:00

Try to unify our config files a bit

I've tried so many rewrites of the config system over the last few
months, in an attempt to get started on #1727. All of them stink, so
this is an attempt to apply some of the cleanup.

 - Move some of the common logic into ConfigFile. This means we now
   store more information ourselves for Forge, rather than reading it
   out of the ForgeConfigSpec.

 - Don't include the Range/Allowed keys in the translation key. This was
   mostly there because of how we read comments from Forge, but it never
   made much sense.

 - Remove our separate Trie structure, and just encode the tree as part
   of the children of a Group.
This commit is contained in:
Jonathan Coates
2024-11-24 21:53:04 +00:00
parent 1963e0160f
commit ea670cc358
6 changed files with 217 additions and 301 deletions

View File

@@ -5,48 +5,32 @@
package dan200.computercraft.shared.platform;
import dan200.computercraft.shared.config.ConfigFile;
import dan200.computercraft.shared.util.Trie;
import net.minecraftforge.common.ForgeConfigSpec;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* A {@link ConfigFile} which wraps Forge's config implementation.
*/
public final class ForgeConfigFile implements ConfigFile {
public final class ForgeConfigFile extends ConfigFile {
private final ForgeConfigSpec spec;
private final Trie<String, ConfigFile.Entry> entries;
public ForgeConfigFile(ForgeConfigSpec spec, Trie<String, Entry> entries) {
private ForgeConfigFile(ForgeConfigSpec spec, Map<String, Entry> entries) {
super(entries);
this.spec = spec;
this.entries = entries;
}
public ForgeConfigSpec spec() {
return spec;
}
@Override
public Stream<Entry> entries() {
return entries.stream();
}
@Nullable
@Override
public Entry getEntry(String path) {
return entries.getValue(SPLITTER.split(path));
}
/**
* Wraps {@link ForgeConfigSpec.Builder} into our own config builder abstraction.
*/
static class Builder extends ConfigFile.Builder {
private final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
private final Trie<String, ConfigFile.Entry> entries = new Trie<>();
private void translation(String name) {
builder.translation(getTranslation(name));
@@ -54,24 +38,23 @@ public final class ForgeConfigFile implements ConfigFile {
@Override
public ConfigFile.Builder comment(String comment) {
super.comment(comment);
builder.comment(comment);
return this;
}
@Override
public void push(String name) {
super.push(name);
translation(name);
builder.push(name);
super.push(name);
}
@Override
public void pop() {
var path = new ArrayList<>(groupStack);
entries.setValue(path, new GroupImpl(path));
builder.pop();
super.pop();
builder.pop();
}
@Override
@@ -80,100 +63,63 @@ public final class ForgeConfigFile implements ConfigFile {
return this;
}
private <T> ConfigFile.Value<T> defineValue(ForgeConfigSpec.ConfigValue<T> value) {
var wrapped = new ValueImpl<>(value);
entries.setValue(value.getPath(), wrapped);
private <T> ConfigFile.Value<T> defineValue(String name, ForgeConfigSpec.ConfigValue<T> value) {
var wrapped = new ValueImpl<>(getPath(name), takeComment(), value);
groupStack.getLast().children().put(name, wrapped);
return wrapped;
}
@Override
public <T> ConfigFile.Value<T> define(String path, T defaultValue) {
translation(path);
return defineValue(builder.define(path, defaultValue));
public <T> ConfigFile.Value<T> define(String name, T defaultValue) {
translation(name);
return defineValue(name, builder.define(name, defaultValue));
}
@Override
public ConfigFile.Value<Boolean> define(String path, boolean defaultValue) {
translation(path);
return defineValue(builder.define(path, defaultValue));
public ConfigFile.Value<Boolean> define(String name, boolean defaultValue) {
translation(name);
return defineValue(name, builder.define(name, defaultValue));
}
@Override
public ConfigFile.Value<Integer> defineInRange(String path, int defaultValue, int min, int max) {
translation(path);
return defineValue(builder.defineInRange(path, defaultValue, min, max));
public ConfigFile.Value<Integer> defineInRange(String name, int defaultValue, int min, int max) {
translation(name);
return defineValue(name, builder.defineInRange(name, defaultValue, min, max));
}
@Override
public <T> ConfigFile.Value<List<? extends T>> defineList(String path, List<? extends T> defaultValue, Predicate<Object> elementValidator) {
translation(path);
return defineValue(builder.defineList(path, defaultValue, elementValidator));
public <T> ConfigFile.Value<List<? extends T>> defineList(String name, List<? extends T> defaultValue, Predicate<Object> elementValidator) {
translation(name);
return defineValue(name, builder.defineList(name, defaultValue, elementValidator));
}
@Override
public <V extends Enum<V>> ConfigFile.Value<V> defineEnum(String path, V defaultValue) {
translation(path);
return defineValue(builder.defineEnum(path, defaultValue));
public <V extends Enum<V>> ConfigFile.Value<V> defineEnum(String name, V defaultValue) {
translation(name);
return defineValue(name, builder.defineEnum(name, defaultValue));
}
@Override
public ConfigFile build(ConfigListener onChange) {
var children = groupStack.removeLast().children();
if (!groupStack.isEmpty()) throw new IllegalStateException("Mismatched config push/pop");
var spec = builder.build();
entries.stream().forEach(x -> {
if (x instanceof ValueImpl<?> value) value.owner = spec;
if (x instanceof GroupImpl value) value.owner = spec;
});
return new ForgeConfigFile(spec, entries);
return new ForgeConfigFile(spec, children);
}
}
private static final class GroupImpl implements ConfigFile.Group {
private final List<String> path;
private @Nullable ForgeConfigSpec owner;
private GroupImpl(List<String> path) {
this.path = path;
}
@Override
public String translationKey() {
if (owner == null) throw new IllegalStateException("Config has not been built yet");
return owner.getLevelTranslationKey(path);
}
@Override
public String comment() {
if (owner == null) throw new IllegalStateException("Config has not been built yet");
return owner.getLevelComment(path);
}
}
private static final class ValueImpl<T> implements ConfigFile.Value<T> {
private static final class ValueImpl<T> extends Value<T> {
private final ForgeConfigSpec.ConfigValue<T> value;
private @Nullable ForgeConfigSpec owner;
private ValueImpl(ForgeConfigSpec.ConfigValue<T> value) {
private ValueImpl(String path, String comment, ForgeConfigSpec.ConfigValue<T> value) {
super(path, comment);
this.value = value;
}
private ForgeConfigSpec.ValueSpec spec() {
if (owner == null) throw new IllegalStateException("Config has not been built yet");
return owner.getSpec().get(value.getPath());
}
@Override
public T get() {
return value.get();
}
@Override
public String translationKey() {
return spec().getTranslationKey();
}
@Override
public String comment() {
return spec().getComment();
}
}
}