1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-22 17:37:38 +00:00

Replace Forge Config port with a NYI version

While it is a really nice library, it ends up being a bit overkill for
our needs - we don't need config syncing or anything. By NIHing our own,
we can drop one dependency and ease the updating burden a little.

Closes #1296
This commit is contained in:
Jonathan Coates
2023-03-14 22:47:34 +00:00
parent a74089d8ae
commit e96ac35d67
16 changed files with 828 additions and 215 deletions

View File

@@ -5,8 +5,6 @@
*/
package dan200.computercraft.data;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import com.google.common.base.Splitter;
import com.google.gson.JsonObject;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
@@ -17,6 +15,7 @@ import dan200.computercraft.core.metrics.Metrics;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.metrics.basic.Aggregate;
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
import dan200.computercraft.shared.config.ConfigFile;
import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.data.CachedOutput;
@@ -24,7 +23,6 @@ import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.common.ForgeConfigSpec;
import java.util.HashMap;
import java.util.Map;
@@ -211,58 +209,58 @@ public final class LanguageProvider implements DataProvider {
add("gui.computercraft.pocket_computer_overlay", "Pocket computer open. Press ESC to close.");
// Config options
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.computerSpaceLimit, "Computer space limit (bytes)");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.floppySpaceLimit, "Floppy Disk space limit (bytes)");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.maximumFilesOpen, "Maximum files open per computer");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.disableLua51Features, "Disable Lua 5.1 features");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.defaultComputerSettings, "Default Computer settings");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.logComputerErrors, "Log computer errors");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.commandRequireCreative, "Command computers require creative");
addConfigEntry(ConfigSpec.computerSpaceLimit, "Computer space limit (bytes)");
addConfigEntry(ConfigSpec.floppySpaceLimit, "Floppy Disk space limit (bytes)");
addConfigEntry(ConfigSpec.maximumFilesOpen, "Maximum files open per computer");
addConfigEntry(ConfigSpec.disableLua51Features, "Disable Lua 5.1 features");
addConfigEntry(ConfigSpec.defaultComputerSettings, "Default Computer settings");
addConfigEntry(ConfigSpec.logComputerErrors, "Log computer errors");
addConfigEntry(ConfigSpec.commandRequireCreative, "Command computers require creative");
addConfigGroup(ConfigSpec.serverSpec, "execution", "Execution");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.computerThreads, "Computer threads");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.maxMainGlobalTime, "Server tick global time limit");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.maxMainComputerTime, "Server tick computer time limit");
addConfigEntry(ConfigSpec.computerThreads, "Computer threads");
addConfigEntry(ConfigSpec.maxMainGlobalTime, "Server tick global time limit");
addConfigEntry(ConfigSpec.maxMainComputerTime, "Server tick computer time limit");
addConfigGroup(ConfigSpec.serverSpec, "http", "HTTP");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpEnabled, "Enable the HTTP API");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpWebsocketEnabled, "Enable websockets");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpRules, "Allow/deny rules");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpMaxRequests, "Maximum concurrent requests");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpMaxWebsockets, "Maximum concurrent websockets");
addConfigEntry(ConfigSpec.httpEnabled, "Enable the HTTP API");
addConfigEntry(ConfigSpec.httpWebsocketEnabled, "Enable websockets");
addConfigEntry(ConfigSpec.httpRules, "Allow/deny rules");
addConfigEntry(ConfigSpec.httpMaxRequests, "Maximum concurrent requests");
addConfigEntry(ConfigSpec.httpMaxWebsockets, "Maximum concurrent websockets");
addConfigGroup(ConfigSpec.serverSpec, "http.bandwidth", "Bandwidth");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpDownloadBandwidth, "Global download limit");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpUploadBandwidth, "Global upload limit");
addConfigEntry(ConfigSpec.httpDownloadBandwidth, "Global download limit");
addConfigEntry(ConfigSpec.httpUploadBandwidth, "Global upload limit");
addConfigGroup(ConfigSpec.serverSpec, "peripheral", "Peripherals");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.commandBlockEnabled, "Enable command block peripheral");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.modemRange, "Modem range (default)");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.modemHighAltitudeRange, "Modem range (high-altitude)");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.modemRangeDuringStorm, "Modem range (bad weather)");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.modemHighAltitudeRangeDuringStorm, "Modem range (high-altitude, bad weather)");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.maxNotesPerTick, "Maximum notes that a computer can play at once");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.monitorBandwidth, "Monitor bandwidth");
addConfigEntry(ConfigSpec.commandBlockEnabled, "Enable command block peripheral");
addConfigEntry(ConfigSpec.modemRange, "Modem range (default)");
addConfigEntry(ConfigSpec.modemHighAltitudeRange, "Modem range (high-altitude)");
addConfigEntry(ConfigSpec.modemRangeDuringStorm, "Modem range (bad weather)");
addConfigEntry(ConfigSpec.modemHighAltitudeRangeDuringStorm, "Modem range (high-altitude, bad weather)");
addConfigEntry(ConfigSpec.maxNotesPerTick, "Maximum notes that a computer can play at once");
addConfigEntry(ConfigSpec.monitorBandwidth, "Monitor bandwidth");
addConfigGroup(ConfigSpec.serverSpec, "turtle", "Turtles");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.turtlesNeedFuel, "Enable fuel");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.turtleFuelLimit, "Turtle fuel limit");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.advancedTurtleFuelLimit, "Advanced Turtle fuel limit");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.turtlesCanPush, "Turtles can push entities");
addConfigEntry(ConfigSpec.turtlesNeedFuel, "Enable fuel");
addConfigEntry(ConfigSpec.turtleFuelLimit, "Turtle fuel limit");
addConfigEntry(ConfigSpec.advancedTurtleFuelLimit, "Advanced Turtle fuel limit");
addConfigEntry(ConfigSpec.turtlesCanPush, "Turtles can push entities");
addConfigGroup(ConfigSpec.serverSpec, "term_sizes", "Terminal sizes");
addConfigGroup(ConfigSpec.serverSpec, "term_sizes.computer", "Computer");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.computerTermWidth, "Terminal width");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.computerTermHeight, "Terminal height");
addConfigEntry(ConfigSpec.computerTermWidth, "Terminal width");
addConfigEntry(ConfigSpec.computerTermHeight, "Terminal height");
addConfigGroup(ConfigSpec.serverSpec, "term_sizes.pocket_computer", "Pocket Computer");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.pocketTermWidth, "Terminal width");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.pocketTermHeight, "Terminal height");
addConfigEntry(ConfigSpec.pocketTermWidth, "Terminal width");
addConfigEntry(ConfigSpec.pocketTermHeight, "Terminal height");
addConfigGroup(ConfigSpec.serverSpec, "term_sizes.monitor", "Monitor");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.monitorWidth, "Max monitor width");
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.monitorHeight, "Max monitor height");
addConfigEntry(ConfigSpec.monitorWidth, "Max monitor width");
addConfigEntry(ConfigSpec.monitorHeight, "Max monitor height");
addConfigValue(ConfigSpec.clientSpec, ConfigSpec.monitorRenderer, "Monitor renderer");
addConfigValue(ConfigSpec.clientSpec, ConfigSpec.monitorDistance, "Monitor distance");
addConfigValue(ConfigSpec.clientSpec, ConfigSpec.uploadNagDelay, "Upload nag delay");
addConfigEntry(ConfigSpec.monitorRenderer, "Monitor renderer");
addConfigEntry(ConfigSpec.monitorDistance, "Monitor distance");
addConfigEntry(ConfigSpec.uploadNagDelay, "Upload nag delay");
}
private Stream<String> getExpectedKeys() {
@@ -276,23 +274,11 @@ public final class LanguageProvider implements DataProvider {
turtleUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
pocketUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
Metric.metrics().values().stream().map(x -> AggregatedMetric.TRANSLATION_PREFIX + x.name() + ".name"),
getConfigKeys(ConfigSpec.serverSpec),
getConfigKeys(ConfigSpec.clientSpec)
getConfigEntries(ConfigSpec.serverSpec).map(ConfigFile.Entry::translationKey),
getConfigEntries(ConfigSpec.clientSpec).map(ConfigFile.Entry::translationKey)
).flatMap(x -> x);
}
private Stream<String> getConfigKeys(UnmodifiableConfig spec) {
return spec.entrySet().stream().flatMap(x -> {
if (x.getValue() instanceof ForgeConfigSpec.ValueSpec valueSpec) {
return Stream.of(valueSpec.getTranslationKey());
} else if (x.getValue() instanceof UnmodifiableConfig config) {
return getConfigKeys(config);
} else {
return Stream.empty();
}
});
}
private void add(String id, String text) {
Objects.requireNonNull(id, "id cannot be null");
Objects.requireNonNull(text, "text cannot be null");
@@ -309,15 +295,26 @@ public final class LanguageProvider implements DataProvider {
add(AggregatedMetric.TRANSLATION_PREFIX + metric.name() + ".name", text);
}
private void addConfigGroup(ForgeConfigSpec spec, String path, String text) {
var pathList = Splitter.on('.').splitToList(path);
add(spec.getLevelTranslationKey(pathList), text);
add(spec.getLevelTranslationKey(pathList) + ".tooltip", spec.getLevelComment(pathList));
private void addConfigGroup(ConfigFile spec, String path, String text) {
var entry = spec.getEntry(path);
if (!(entry instanceof ConfigFile.Group)) throw new IllegalArgumentException("Cannot find group " + path);
addConfigEntry(entry, text);
}
private void addConfigValue(ForgeConfigSpec spec, ForgeConfigSpec.ConfigValue<?> value, String text) {
ForgeConfigSpec.ValueSpec valueSpec = spec.getSpec().get(value.getPath());
add(valueSpec.getTranslationKey(), text);
add(valueSpec.getTranslationKey() + ".tooltip", valueSpec.getComment());
private void addConfigEntry(ConfigFile.Entry value, String text) {
add(value.translationKey(), text);
add(value.translationKey() + ".tooltip", value.comment());
}
private static Stream<ConfigFile.Entry> getConfigEntries(ConfigFile spec) {
return spec.entries().flatMap(LanguageProvider::getConfigEntries);
}
private static Stream<ConfigFile.Entry> getConfigEntries(ConfigFile.Entry entry) {
if (entry instanceof ConfigFile.Value<?>) return Stream.of(entry);
if (entry instanceof ConfigFile.Group group) {
return Stream.concat(Stream.of(entry), group.children().flatMap(LanguageProvider::getConfigEntries));
}
throw new IllegalStateException("Invalid config entry " + entry);
}
}

View File

@@ -0,0 +1,147 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.config;
import com.google.common.base.Splitter;
import javax.annotation.Nullable;
import javax.annotation.OverridingMethodsMustInvokeSuper;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* A config file which the user can modify.
*/
public interface ConfigFile {
String TRANSLATION_PREFIX = "gui.computercraft.config.";
Splitter SPLITTER = Splitter.on('.');
/**
* An entry in the config file, either a {@link Value} or {@linkplain Group group of other entries}.
*/
sealed interface Entry permits Group, Value {
/**
* Get the translation key of this config entry.
*
* @return This entry's translation key.
*/
String translationKey();
/**
* Get the comment about this config entry.
*
* @return The comment for this config entry.
*/
String comment();
}
/**
* A configurable value.
*
* @param <T> The type of the stored value.
*/
non-sealed interface Value<T> extends Entry, Supplier<T> {
}
/**
* A group of config entries.
*/
non-sealed interface Group extends Entry {
/**
* Get all entries in this group.
*
* @return All child entries.
*/
Stream<Entry> children();
}
/**
* Get a list of all config keys in this file.
*
* @return All config keys.
*/
Stream<Entry> entries();
@Nullable
Entry getEntry(String path);
/**
* A builder which can be used to generate a config object.
*/
abstract class Builder {
protected final Deque<String> groupStack = new ArrayDeque<>();
protected String getTranslation(String name) {
var key = new StringBuilder(TRANSLATION_PREFIX);
for (var group : groupStack) key.append(group).append('.');
key.append(name);
return key.toString();
}
/**
* Add a comment to the next config object (either a {@linkplain #push(String) group} or a {@linkplain
* #define(String, boolean) property}).
*
* @param comment The comment.
* @return The current object, for chaining.
*/
public abstract Builder comment(String comment);
/**
* Push a new config group.
*
* @param name The name of the group.
*/
@OverridingMethodsMustInvokeSuper
public void push(String name) {
groupStack.addLast(name);
}
/**
* Pop a config group.
*/
@OverridingMethodsMustInvokeSuper
public void pop() {
groupStack.removeLast();
}
/**
* Mark the next config property as requiring a world restart.
*
* @return The current object, for chaining.
*/
public abstract Builder worldRestart();
public abstract <T> ConfigFile.Value<T> define(String path, T defaultValue);
/**
* A boolean-specific override of the above {@link #define(String, Object)} method.
*
* @param path The path to the value we're defining.
* @param defaultValue The default value.
* @return The accessor for this config option.
*/
public abstract ConfigFile.Value<Boolean> define(String path, boolean defaultValue);
public abstract ConfigFile.Value<Integer> defineInRange(String path, int defaultValue, int min, int max);
public abstract <T> ConfigFile.Value<List<? extends T>> defineList(String path, List<? extends T> defaultValue, Predicate<Object> elementValidator);
public abstract <V extends Enum<V>> ConfigFile.Value<V> defineEnum(String path, V defaultValue);
/**
* Finalise this config file.
*
* @param onChange The function to run on change.
* @return The built config file.
*/
public abstract ConfigFile build(Runnable onChange);
}
}

View File

@@ -6,79 +6,75 @@
package dan200.computercraft.shared.config;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.core.CoreConfig;
import dan200.computercraft.core.Logging;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.apis.http.options.Action;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.common.ForgeConfigSpec.ConfigValue;
import net.minecraftforge.fml.config.ModConfig;
import dan200.computercraft.shared.platform.PlatformHelper;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.filter.MarkerFilter;
import java.util.*;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
public final class ConfigSpec {
private static final int MODEM_MAX_RANGE = 100000;
private static final String TRANSLATION_PREFIX = "gui.computercraft.config.";
public static final ConfigFile serverSpec;
public static final ForgeConfigSpec serverSpec;
public static final ConfigFile.Value<Integer> computerSpaceLimit;
public static final ConfigFile.Value<Integer> floppySpaceLimit;
public static final ConfigFile.Value<Integer> maximumFilesOpen;
public static final ConfigFile.Value<Boolean> disableLua51Features;
public static final ConfigFile.Value<String> defaultComputerSettings;
public static final ConfigFile.Value<Boolean> logComputerErrors;
public static final ConfigFile.Value<Boolean> commandRequireCreative;
public static final ConfigValue<Integer> computerSpaceLimit;
public static final ConfigValue<Integer> floppySpaceLimit;
public static final ConfigValue<Integer> maximumFilesOpen;
public static final ConfigValue<Boolean> disableLua51Features;
public static final ConfigValue<String> defaultComputerSettings;
public static final ConfigValue<Boolean> logComputerErrors;
public static final ConfigValue<Boolean> commandRequireCreative;
public static final ConfigFile.Value<Integer> computerThreads;
public static final ConfigFile.Value<Integer> maxMainGlobalTime;
public static final ConfigFile.Value<Integer> maxMainComputerTime;
public static final ConfigValue<Integer> computerThreads;
public static final ConfigValue<Integer> maxMainGlobalTime;
public static final ConfigValue<Integer> maxMainComputerTime;
public static final ConfigFile.Value<Boolean> httpEnabled;
public static final ConfigFile.Value<Boolean> httpWebsocketEnabled;
public static final ConfigFile.Value<List<? extends UnmodifiableConfig>> httpRules;
public static final ConfigValue<Boolean> httpEnabled;
public static final ConfigValue<Boolean> httpWebsocketEnabled;
public static final ConfigValue<List<? extends UnmodifiableConfig>> httpRules;
public static final ConfigFile.Value<Integer> httpMaxRequests;
public static final ConfigFile.Value<Integer> httpMaxWebsockets;
public static final ConfigValue<Integer> httpMaxRequests;
public static final ConfigValue<Integer> httpMaxWebsockets;
public static final ConfigFile.Value<Integer> httpDownloadBandwidth;
public static final ConfigFile.Value<Integer> httpUploadBandwidth;
public static final ConfigValue<Integer> httpDownloadBandwidth;
public static final ConfigValue<Integer> httpUploadBandwidth;
public static final ConfigFile.Value<Boolean> commandBlockEnabled;
public static final ConfigFile.Value<Integer> modemRange;
public static final ConfigFile.Value<Integer> modemHighAltitudeRange;
public static final ConfigFile.Value<Integer> modemRangeDuringStorm;
public static final ConfigFile.Value<Integer> modemHighAltitudeRangeDuringStorm;
public static final ConfigFile.Value<Integer> maxNotesPerTick;
public static final ConfigFile.Value<Integer> monitorBandwidth;
public static final ConfigValue<Boolean> commandBlockEnabled;
public static final ConfigValue<Integer> modemRange;
public static final ConfigValue<Integer> modemHighAltitudeRange;
public static final ConfigValue<Integer> modemRangeDuringStorm;
public static final ConfigValue<Integer> modemHighAltitudeRangeDuringStorm;
public static final ConfigValue<Integer> maxNotesPerTick;
public static final ConfigValue<Integer> monitorBandwidth;
public static final ConfigFile.Value<Boolean> turtlesNeedFuel;
public static final ConfigFile.Value<Integer> turtleFuelLimit;
public static final ConfigFile.Value<Integer> advancedTurtleFuelLimit;
public static final ConfigFile.Value<Boolean> turtlesCanPush;
public static final ConfigValue<Boolean> turtlesNeedFuel;
public static final ConfigValue<Integer> turtleFuelLimit;
public static final ConfigValue<Integer> advancedTurtleFuelLimit;
public static final ConfigValue<Boolean> turtlesCanPush;
public static final ConfigFile.Value<Integer> computerTermWidth;
public static final ConfigFile.Value<Integer> computerTermHeight;
public static final ConfigValue<Integer> computerTermWidth;
public static final ConfigValue<Integer> computerTermHeight;
public static final ConfigFile.Value<Integer> pocketTermWidth;
public static final ConfigFile.Value<Integer> pocketTermHeight;
public static final ConfigValue<Integer> pocketTermWidth;
public static final ConfigValue<Integer> pocketTermHeight;
public static final ConfigFile.Value<Integer> monitorWidth;
public static final ConfigFile.Value<Integer> monitorHeight;
public static final ConfigValue<Integer> monitorWidth;
public static final ConfigValue<Integer> monitorHeight;
public static final ConfigFile clientSpec;
public static final ForgeConfigSpec clientSpec;
public static final ConfigValue<MonitorRenderer> monitorRenderer;
public static final ConfigValue<Integer> monitorDistance;
public static final ConfigValue<Integer> uploadNagDelay;
public static final ConfigFile.Value<MonitorRenderer> monitorRenderer;
public static final ConfigFile.Value<Integer> monitorDistance;
public static final ConfigFile.Value<Integer> uploadNagDelay;
private static MarkerFilter logFilter = MarkerFilter.createFilter(Logging.COMPUTER_ERROR.getName(), Filter.Result.ACCEPT, Filter.Result.NEUTRAL);
@@ -88,7 +84,7 @@ public final class ConfigSpec {
static {
LoggerContext.getContext().addFilter(logFilter);
var builder = new TranslatingBuilder();
var builder = PlatformHelper.get().createConfigBuilder();
{ // General computers
computerSpaceLimit = builder
@@ -312,9 +308,9 @@ public final class ConfigSpec {
builder.pop();
}
serverSpec = builder.build();
serverSpec = builder.build(ConfigSpec::syncServer);
var clientBuilder = new TranslatingBuilder();
var clientBuilder = PlatformHelper.get().createConfigBuilder();
monitorRenderer = clientBuilder
.comment("""
The renderer to use for monitors. Generally this should be kept at "best" - if
@@ -330,12 +326,10 @@ public final class ConfigSpec {
.comment("The delay in seconds after which we'll notify about unhandled imports. Set to 0 to disable.")
.defineInRange("upload_nag_delay", Config.uploadNagDelay, 0, 60);
clientSpec = clientBuilder.build();
clientSpec = clientBuilder.build(ConfigSpec::syncClient);
}
private static void syncServer() {
if (!serverSpec.isLoaded()) return;
public static void syncServer() {
// General
Config.computerSpaceLimit = computerSpaceLimit.get();
Config.floppySpaceLimit = floppySpaceLimit.get();
@@ -395,85 +389,9 @@ public final class ConfigSpec {
Config.monitorHeight = monitorHeight.get();
}
private static void syncClient() {
if (!clientSpec.isLoaded()) return;
public static void syncClient() {
Config.monitorRenderer = monitorRenderer.get();
Config.monitorDistance = monitorDistance.get();
Config.uploadNagDelay = uploadNagDelay.get();
}
public static void sync(ModConfig config) {
if (!config.getModId().equals(ComputerCraftAPI.MOD_ID)) return;
if (config.getType() == ModConfig.Type.SERVER) syncServer();
if (config.getType() == ModConfig.Type.CLIENT) syncClient();
}
/**
* A {@link ForgeConfigSpec.Builder} which adds translation keys to every entry.
*/
private static class TranslatingBuilder {
private final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
private final Deque<String> groupStack = new ArrayDeque<>();
private void translation(String name) {
var key = new StringBuilder(TRANSLATION_PREFIX);
for (var group : groupStack) key.append(group).append('.');
key.append(name);
builder.translation(key.toString());
}
public TranslatingBuilder comment(String comment) {
builder.comment(comment);
return this;
}
public TranslatingBuilder push(String name) {
translation(name);
builder.push(name);
groupStack.addLast(name);
return this;
}
public TranslatingBuilder pop() {
builder.pop();
groupStack.removeLast();
return this;
}
public ForgeConfigSpec build() {
return builder.build();
}
public TranslatingBuilder worldRestart() {
builder.worldRestart();
return this;
}
public <T> ConfigValue<T> define(String path, T defaultValue) {
translation(path);
return builder.define(path, defaultValue);
}
public ConfigValue<Boolean> define(String path, boolean defaultValue) {
translation(path);
return builder.define(path, defaultValue);
}
public ConfigValue<Integer> defineInRange(String path, int defaultValue, int min, int max) {
translation(path);
return builder.defineInRange(path, defaultValue, min, max);
}
public <T> ConfigValue<List<? extends T>> defineList(String path, List<? extends T> defaultValue, Predicate<Object> elementValidator) {
translation(path);
return builder.defineList(path, defaultValue, elementValidator);
}
public <V extends Enum<V>> ConfigValue<V> defineEnum(String path, V defaultValue) {
translation(path);
return builder.defineEnum(path, defaultValue);
}
}
}

View File

@@ -10,6 +10,7 @@ import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType;
import dan200.computercraft.api.network.wired.WiredElement;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.config.ConfigFile;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.container.ContainerData;
@@ -69,6 +70,13 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
return (PlatformHelper) dan200.computercraft.impl.PlatformHelper.get();
}
/**
* Create a new config builder.
*
* @return The newly created config builder.
*/
ConfigFile.Builder createConfigBuilder();
/**
* Wrap a Minecraft registry in our own abstraction layer.
*

View File

@@ -0,0 +1,54 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.util;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
/**
* A key-value map, where the key is a list of values.
*
* @param <K> The type of keys in this trie.
* @param <V> The values in this map.
*/
public class Trie<K, V> {
private @Nullable V current;
private @Nullable Map<K, Trie<K, V>> children;
public Trie<K, V> getChild(Iterable<K> key) {
var self = this;
for (var keyElement : key) {
if (self.children == null) self.children = new HashMap<>(1);
self = self.children.computeIfAbsent(keyElement, x -> new Trie<>());
}
return self;
}
public @Nullable V getValue(Iterable<K> key) {
return getChild(key).current;
}
public void setValue(Iterable<K> key, V value) {
getChild(key).current = value;
}
public Stream<V> children() {
return children == null
? Stream.empty()
: children.values().stream().map(x -> x.current).filter(Objects::nonNull);
}
public Stream<V> stream() {
return Stream.concat(
current == null ? Stream.empty() : Stream.of(current),
children == null ? Stream.empty() : children.values().stream().flatMap(Trie::stream)
);
}
}

View File

@@ -13,6 +13,7 @@ import dan200.computercraft.api.network.wired.WiredElement;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.impl.AbstractComputerCraftAPI;
import dan200.computercraft.impl.ComputerCraftAPIService;
import dan200.computercraft.shared.config.ConfigFile;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.container.ContainerData;
@@ -59,6 +60,11 @@ import java.util.function.Predicate;
@AutoService({ PlatformHelper.class, dan200.computercraft.impl.PlatformHelper.class, ComputerCraftAPIService.class })
public class TestPlatformHelper extends AbstractComputerCraftAPI implements PlatformHelper {
@Override
public ConfigFile.Builder createConfigBuilder() {
throw new UnsupportedOperationException("Cannot create config file inside tests");
}
@Override
public <T> RegistryWrappers.RegistryWrapper<T> wrap(ResourceKey<Registry<T>> registry) {
throw new UnsupportedOperationException("Cannot query registry inside tests");