mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-09-29 23:40:46 +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:
parent
a74089d8ae
commit
e96ac35d67
@ -30,7 +30,6 @@ repositories {
|
|||||||
content {
|
content {
|
||||||
includeGroup("cc.tweaked")
|
includeGroup("cc.tweaked")
|
||||||
includeModule("org.squiddev", "Cobalt")
|
includeModule("org.squiddev", "Cobalt")
|
||||||
includeModule("fuzs.forgeconfigapiport", "forgeconfigapiport-fabric")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,6 @@ repositories {
|
|||||||
includeGroup("me.shedaniel.cloth")
|
includeGroup("me.shedaniel.cloth")
|
||||||
includeGroup("mezz.jei")
|
includeGroup("mezz.jei")
|
||||||
includeModule("com.terraformersmc", "modmenu")
|
includeModule("com.terraformersmc", "modmenu")
|
||||||
includeModule("fuzs.forgeconfigapiport", "forgeconfigapiport-fabric")
|
|
||||||
// Until https://github.com/SpongePowered/Mixin/pull/593 is merged
|
// Until https://github.com/SpongePowered/Mixin/pull/593 is merged
|
||||||
includeModule("org.spongepowered", "mixin")
|
includeModule("org.spongepowered", "mixin")
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ nightConfig = "3.6.5"
|
|||||||
slf4j = "1.7.36"
|
slf4j = "1.7.36"
|
||||||
|
|
||||||
# Minecraft mods
|
# Minecraft mods
|
||||||
forgeConfig = "5.0.4"
|
|
||||||
iris = "1.19.3-v1.4.6"
|
iris = "1.19.3-v1.4.6"
|
||||||
jei = "11.3.0.262"
|
jei = "11.3.0.262"
|
||||||
modmenu = "5.0.1"
|
modmenu = "5.0.1"
|
||||||
@ -85,7 +84,6 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
|||||||
# Minecraft mods
|
# Minecraft mods
|
||||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
||||||
forgeConfig = { module = "fuzs.forgeconfigapiport:forgeconfigapiport-fabric", version.ref = "forgeConfig" }
|
|
||||||
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
||||||
jei-api = { module = "mezz.jei:jei-1.19.2-common-api", version.ref = "jei" }
|
jei-api = { module = "mezz.jei:jei-1.19.2-common-api", version.ref = "jei" }
|
||||||
jei-fabric = { module = "mezz.jei:jei-1.19.2-fabric", version.ref = "jei" }
|
jei-fabric = { module = "mezz.jei:jei-1.19.2-fabric", version.ref = "jei" }
|
||||||
@ -143,10 +141,10 @@ taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
|||||||
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
||||||
|
|
||||||
# Minecraft
|
# Minecraft
|
||||||
externalMods-common = ["jei-api", "forgeConfig", "nightConfig-core", "nightConfig-toml"]
|
externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
|
||||||
externalMods-forge-compile = ["oculus", "jei-api"]
|
externalMods-forge-compile = ["oculus", "jei-api"]
|
||||||
externalMods-forge-runtime = []
|
externalMods-forge-runtime = []
|
||||||
externalMods-fabric = ["forgeConfig", "nightConfig-core", "nightConfig-toml"]
|
externalMods-fabric = ["nightConfig-core", "nightConfig-toml"]
|
||||||
externalMods-fabric-compile = ["iris", "jei-api", "rei-api", "rei-builtin"]
|
externalMods-fabric-compile = ["iris", "jei-api", "rei-api", "rei-builtin"]
|
||||||
externalMods-fabric-runtime = ["modmenu"]
|
externalMods-fabric-runtime = ["modmenu"]
|
||||||
|
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.data;
|
package dan200.computercraft.data;
|
||||||
|
|
||||||
import com.electronwill.nightconfig.core.UnmodifiableConfig;
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
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.ModRegistry;
|
||||||
import dan200.computercraft.shared.computer.metrics.basic.Aggregate;
|
import dan200.computercraft.shared.computer.metrics.basic.Aggregate;
|
||||||
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
|
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
|
||||||
|
import dan200.computercraft.shared.config.ConfigFile;
|
||||||
import dan200.computercraft.shared.config.ConfigSpec;
|
import dan200.computercraft.shared.config.ConfigSpec;
|
||||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||||
import net.minecraft.data.CachedOutput;
|
import net.minecraft.data.CachedOutput;
|
||||||
@ -24,7 +23,6 @@ import net.minecraft.data.DataProvider;
|
|||||||
import net.minecraft.data.PackOutput;
|
import net.minecraft.data.PackOutput;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraftforge.common.ForgeConfigSpec;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
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.");
|
add("gui.computercraft.pocket_computer_overlay", "Pocket computer open. Press ESC to close.");
|
||||||
|
|
||||||
// Config options
|
// Config options
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.computerSpaceLimit, "Computer space limit (bytes)");
|
addConfigEntry(ConfigSpec.computerSpaceLimit, "Computer space limit (bytes)");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.floppySpaceLimit, "Floppy Disk space limit (bytes)");
|
addConfigEntry(ConfigSpec.floppySpaceLimit, "Floppy Disk space limit (bytes)");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.maximumFilesOpen, "Maximum files open per computer");
|
addConfigEntry(ConfigSpec.maximumFilesOpen, "Maximum files open per computer");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.disableLua51Features, "Disable Lua 5.1 features");
|
addConfigEntry(ConfigSpec.disableLua51Features, "Disable Lua 5.1 features");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.defaultComputerSettings, "Default Computer settings");
|
addConfigEntry(ConfigSpec.defaultComputerSettings, "Default Computer settings");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.logComputerErrors, "Log computer errors");
|
addConfigEntry(ConfigSpec.logComputerErrors, "Log computer errors");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.commandRequireCreative, "Command computers require creative");
|
addConfigEntry(ConfigSpec.commandRequireCreative, "Command computers require creative");
|
||||||
|
|
||||||
addConfigGroup(ConfigSpec.serverSpec, "execution", "Execution");
|
addConfigGroup(ConfigSpec.serverSpec, "execution", "Execution");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.computerThreads, "Computer threads");
|
addConfigEntry(ConfigSpec.computerThreads, "Computer threads");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.maxMainGlobalTime, "Server tick global time limit");
|
addConfigEntry(ConfigSpec.maxMainGlobalTime, "Server tick global time limit");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.maxMainComputerTime, "Server tick computer time limit");
|
addConfigEntry(ConfigSpec.maxMainComputerTime, "Server tick computer time limit");
|
||||||
|
|
||||||
addConfigGroup(ConfigSpec.serverSpec, "http", "HTTP");
|
addConfigGroup(ConfigSpec.serverSpec, "http", "HTTP");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpEnabled, "Enable the HTTP API");
|
addConfigEntry(ConfigSpec.httpEnabled, "Enable the HTTP API");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpWebsocketEnabled, "Enable websockets");
|
addConfigEntry(ConfigSpec.httpWebsocketEnabled, "Enable websockets");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpRules, "Allow/deny rules");
|
addConfigEntry(ConfigSpec.httpRules, "Allow/deny rules");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpMaxRequests, "Maximum concurrent requests");
|
addConfigEntry(ConfigSpec.httpMaxRequests, "Maximum concurrent requests");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpMaxWebsockets, "Maximum concurrent websockets");
|
addConfigEntry(ConfigSpec.httpMaxWebsockets, "Maximum concurrent websockets");
|
||||||
addConfigGroup(ConfigSpec.serverSpec, "http.bandwidth", "Bandwidth");
|
addConfigGroup(ConfigSpec.serverSpec, "http.bandwidth", "Bandwidth");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpDownloadBandwidth, "Global download limit");
|
addConfigEntry(ConfigSpec.httpDownloadBandwidth, "Global download limit");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.httpUploadBandwidth, "Global upload limit");
|
addConfigEntry(ConfigSpec.httpUploadBandwidth, "Global upload limit");
|
||||||
|
|
||||||
addConfigGroup(ConfigSpec.serverSpec, "peripheral", "Peripherals");
|
addConfigGroup(ConfigSpec.serverSpec, "peripheral", "Peripherals");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.commandBlockEnabled, "Enable command block peripheral");
|
addConfigEntry(ConfigSpec.commandBlockEnabled, "Enable command block peripheral");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.modemRange, "Modem range (default)");
|
addConfigEntry(ConfigSpec.modemRange, "Modem range (default)");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.modemHighAltitudeRange, "Modem range (high-altitude)");
|
addConfigEntry(ConfigSpec.modemHighAltitudeRange, "Modem range (high-altitude)");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.modemRangeDuringStorm, "Modem range (bad weather)");
|
addConfigEntry(ConfigSpec.modemRangeDuringStorm, "Modem range (bad weather)");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.modemHighAltitudeRangeDuringStorm, "Modem range (high-altitude, bad weather)");
|
addConfigEntry(ConfigSpec.modemHighAltitudeRangeDuringStorm, "Modem range (high-altitude, bad weather)");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.maxNotesPerTick, "Maximum notes that a computer can play at once");
|
addConfigEntry(ConfigSpec.maxNotesPerTick, "Maximum notes that a computer can play at once");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.monitorBandwidth, "Monitor bandwidth");
|
addConfigEntry(ConfigSpec.monitorBandwidth, "Monitor bandwidth");
|
||||||
|
|
||||||
addConfigGroup(ConfigSpec.serverSpec, "turtle", "Turtles");
|
addConfigGroup(ConfigSpec.serverSpec, "turtle", "Turtles");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.turtlesNeedFuel, "Enable fuel");
|
addConfigEntry(ConfigSpec.turtlesNeedFuel, "Enable fuel");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.turtleFuelLimit, "Turtle fuel limit");
|
addConfigEntry(ConfigSpec.turtleFuelLimit, "Turtle fuel limit");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.advancedTurtleFuelLimit, "Advanced Turtle fuel limit");
|
addConfigEntry(ConfigSpec.advancedTurtleFuelLimit, "Advanced Turtle fuel limit");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.turtlesCanPush, "Turtles can push entities");
|
addConfigEntry(ConfigSpec.turtlesCanPush, "Turtles can push entities");
|
||||||
|
|
||||||
addConfigGroup(ConfigSpec.serverSpec, "term_sizes", "Terminal sizes");
|
addConfigGroup(ConfigSpec.serverSpec, "term_sizes", "Terminal sizes");
|
||||||
addConfigGroup(ConfigSpec.serverSpec, "term_sizes.computer", "Computer");
|
addConfigGroup(ConfigSpec.serverSpec, "term_sizes.computer", "Computer");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.computerTermWidth, "Terminal width");
|
addConfigEntry(ConfigSpec.computerTermWidth, "Terminal width");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.computerTermHeight, "Terminal height");
|
addConfigEntry(ConfigSpec.computerTermHeight, "Terminal height");
|
||||||
addConfigGroup(ConfigSpec.serverSpec, "term_sizes.pocket_computer", "Pocket Computer");
|
addConfigGroup(ConfigSpec.serverSpec, "term_sizes.pocket_computer", "Pocket Computer");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.pocketTermWidth, "Terminal width");
|
addConfigEntry(ConfigSpec.pocketTermWidth, "Terminal width");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.pocketTermHeight, "Terminal height");
|
addConfigEntry(ConfigSpec.pocketTermHeight, "Terminal height");
|
||||||
addConfigGroup(ConfigSpec.serverSpec, "term_sizes.monitor", "Monitor");
|
addConfigGroup(ConfigSpec.serverSpec, "term_sizes.monitor", "Monitor");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.monitorWidth, "Max monitor width");
|
addConfigEntry(ConfigSpec.monitorWidth, "Max monitor width");
|
||||||
addConfigValue(ConfigSpec.serverSpec, ConfigSpec.monitorHeight, "Max monitor height");
|
addConfigEntry(ConfigSpec.monitorHeight, "Max monitor height");
|
||||||
|
|
||||||
addConfigValue(ConfigSpec.clientSpec, ConfigSpec.monitorRenderer, "Monitor renderer");
|
addConfigEntry(ConfigSpec.monitorRenderer, "Monitor renderer");
|
||||||
addConfigValue(ConfigSpec.clientSpec, ConfigSpec.monitorDistance, "Monitor distance");
|
addConfigEntry(ConfigSpec.monitorDistance, "Monitor distance");
|
||||||
addConfigValue(ConfigSpec.clientSpec, ConfigSpec.uploadNagDelay, "Upload nag delay");
|
addConfigEntry(ConfigSpec.uploadNagDelay, "Upload nag delay");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Stream<String> getExpectedKeys() {
|
private Stream<String> getExpectedKeys() {
|
||||||
@ -276,23 +274,11 @@ public final class LanguageProvider implements DataProvider {
|
|||||||
turtleUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
|
turtleUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
|
||||||
pocketUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
|
pocketUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
|
||||||
Metric.metrics().values().stream().map(x -> AggregatedMetric.TRANSLATION_PREFIX + x.name() + ".name"),
|
Metric.metrics().values().stream().map(x -> AggregatedMetric.TRANSLATION_PREFIX + x.name() + ".name"),
|
||||||
getConfigKeys(ConfigSpec.serverSpec),
|
getConfigEntries(ConfigSpec.serverSpec).map(ConfigFile.Entry::translationKey),
|
||||||
getConfigKeys(ConfigSpec.clientSpec)
|
getConfigEntries(ConfigSpec.clientSpec).map(ConfigFile.Entry::translationKey)
|
||||||
).flatMap(x -> x);
|
).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) {
|
private void add(String id, String text) {
|
||||||
Objects.requireNonNull(id, "id cannot be null");
|
Objects.requireNonNull(id, "id cannot be null");
|
||||||
Objects.requireNonNull(text, "text 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);
|
add(AggregatedMetric.TRANSLATION_PREFIX + metric.name() + ".name", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addConfigGroup(ForgeConfigSpec spec, String path, String text) {
|
private void addConfigGroup(ConfigFile spec, String path, String text) {
|
||||||
var pathList = Splitter.on('.').splitToList(path);
|
var entry = spec.getEntry(path);
|
||||||
add(spec.getLevelTranslationKey(pathList), text);
|
if (!(entry instanceof ConfigFile.Group)) throw new IllegalArgumentException("Cannot find group " + path);
|
||||||
add(spec.getLevelTranslationKey(pathList) + ".tooltip", spec.getLevelComment(pathList));
|
addConfigEntry(entry, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addConfigValue(ForgeConfigSpec spec, ForgeConfigSpec.ConfigValue<?> value, String text) {
|
private void addConfigEntry(ConfigFile.Entry value, String text) {
|
||||||
ForgeConfigSpec.ValueSpec valueSpec = spec.getSpec().get(value.getPath());
|
add(value.translationKey(), text);
|
||||||
add(valueSpec.getTranslationKey(), text);
|
add(value.translationKey() + ".tooltip", value.comment());
|
||||||
add(valueSpec.getTranslationKey() + ".tooltip", valueSpec.getComment());
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -6,79 +6,75 @@
|
|||||||
package dan200.computercraft.shared.config;
|
package dan200.computercraft.shared.config;
|
||||||
|
|
||||||
import com.electronwill.nightconfig.core.UnmodifiableConfig;
|
import com.electronwill.nightconfig.core.UnmodifiableConfig;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
|
||||||
import dan200.computercraft.core.CoreConfig;
|
import dan200.computercraft.core.CoreConfig;
|
||||||
import dan200.computercraft.core.Logging;
|
import dan200.computercraft.core.Logging;
|
||||||
import dan200.computercraft.core.apis.http.NetworkUtils;
|
import dan200.computercraft.core.apis.http.NetworkUtils;
|
||||||
import dan200.computercraft.core.apis.http.options.Action;
|
import dan200.computercraft.core.apis.http.options.Action;
|
||||||
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
|
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
|
||||||
import net.minecraftforge.common.ForgeConfigSpec;
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import net.minecraftforge.common.ForgeConfigSpec.ConfigValue;
|
|
||||||
import net.minecraftforge.fml.config.ModConfig;
|
|
||||||
import org.apache.logging.log4j.core.Filter;
|
import org.apache.logging.log4j.core.Filter;
|
||||||
import org.apache.logging.log4j.core.LoggerContext;
|
import org.apache.logging.log4j.core.LoggerContext;
|
||||||
import org.apache.logging.log4j.core.filter.MarkerFilter;
|
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.concurrent.TimeUnit;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
public final class ConfigSpec {
|
public final class ConfigSpec {
|
||||||
private static final int MODEM_MAX_RANGE = 100000;
|
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 ConfigFile.Value<Integer> computerThreads;
|
||||||
public static final ConfigValue<Integer> floppySpaceLimit;
|
public static final ConfigFile.Value<Integer> maxMainGlobalTime;
|
||||||
public static final ConfigValue<Integer> maximumFilesOpen;
|
public static final ConfigFile.Value<Integer> maxMainComputerTime;
|
||||||
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 ConfigValue<Integer> computerThreads;
|
public static final ConfigFile.Value<Boolean> httpEnabled;
|
||||||
public static final ConfigValue<Integer> maxMainGlobalTime;
|
public static final ConfigFile.Value<Boolean> httpWebsocketEnabled;
|
||||||
public static final ConfigValue<Integer> maxMainComputerTime;
|
public static final ConfigFile.Value<List<? extends UnmodifiableConfig>> httpRules;
|
||||||
|
|
||||||
public static final ConfigValue<Boolean> httpEnabled;
|
public static final ConfigFile.Value<Integer> httpMaxRequests;
|
||||||
public static final ConfigValue<Boolean> httpWebsocketEnabled;
|
public static final ConfigFile.Value<Integer> httpMaxWebsockets;
|
||||||
public static final ConfigValue<List<? extends UnmodifiableConfig>> httpRules;
|
|
||||||
|
|
||||||
public static final ConfigValue<Integer> httpMaxRequests;
|
public static final ConfigFile.Value<Integer> httpDownloadBandwidth;
|
||||||
public static final ConfigValue<Integer> httpMaxWebsockets;
|
public static final ConfigFile.Value<Integer> httpUploadBandwidth;
|
||||||
|
|
||||||
public static final ConfigValue<Integer> httpDownloadBandwidth;
|
public static final ConfigFile.Value<Boolean> commandBlockEnabled;
|
||||||
public static final ConfigValue<Integer> httpUploadBandwidth;
|
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 ConfigFile.Value<Boolean> turtlesNeedFuel;
|
||||||
public static final ConfigValue<Integer> modemRange;
|
public static final ConfigFile.Value<Integer> turtleFuelLimit;
|
||||||
public static final ConfigValue<Integer> modemHighAltitudeRange;
|
public static final ConfigFile.Value<Integer> advancedTurtleFuelLimit;
|
||||||
public static final ConfigValue<Integer> modemRangeDuringStorm;
|
public static final ConfigFile.Value<Boolean> turtlesCanPush;
|
||||||
public static final ConfigValue<Integer> modemHighAltitudeRangeDuringStorm;
|
|
||||||
public static final ConfigValue<Integer> maxNotesPerTick;
|
|
||||||
public static final ConfigValue<Integer> monitorBandwidth;
|
|
||||||
|
|
||||||
public static final ConfigValue<Boolean> turtlesNeedFuel;
|
public static final ConfigFile.Value<Integer> computerTermWidth;
|
||||||
public static final ConfigValue<Integer> turtleFuelLimit;
|
public static final ConfigFile.Value<Integer> computerTermHeight;
|
||||||
public static final ConfigValue<Integer> advancedTurtleFuelLimit;
|
|
||||||
public static final ConfigValue<Boolean> turtlesCanPush;
|
|
||||||
|
|
||||||
public static final ConfigValue<Integer> computerTermWidth;
|
public static final ConfigFile.Value<Integer> pocketTermWidth;
|
||||||
public static final ConfigValue<Integer> computerTermHeight;
|
public static final ConfigFile.Value<Integer> pocketTermHeight;
|
||||||
|
|
||||||
public static final ConfigValue<Integer> pocketTermWidth;
|
public static final ConfigFile.Value<Integer> monitorWidth;
|
||||||
public static final ConfigValue<Integer> pocketTermHeight;
|
public static final ConfigFile.Value<Integer> monitorHeight;
|
||||||
|
|
||||||
public static final ConfigValue<Integer> monitorWidth;
|
public static final ConfigFile clientSpec;
|
||||||
public static final ConfigValue<Integer> monitorHeight;
|
|
||||||
|
|
||||||
public static final ForgeConfigSpec clientSpec;
|
public static final ConfigFile.Value<MonitorRenderer> monitorRenderer;
|
||||||
|
public static final ConfigFile.Value<Integer> monitorDistance;
|
||||||
public static final ConfigValue<MonitorRenderer> monitorRenderer;
|
public static final ConfigFile.Value<Integer> uploadNagDelay;
|
||||||
public static final ConfigValue<Integer> monitorDistance;
|
|
||||||
public static final ConfigValue<Integer> uploadNagDelay;
|
|
||||||
|
|
||||||
private static MarkerFilter logFilter = MarkerFilter.createFilter(Logging.COMPUTER_ERROR.getName(), Filter.Result.ACCEPT, Filter.Result.NEUTRAL);
|
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 {
|
static {
|
||||||
LoggerContext.getContext().addFilter(logFilter);
|
LoggerContext.getContext().addFilter(logFilter);
|
||||||
|
|
||||||
var builder = new TranslatingBuilder();
|
var builder = PlatformHelper.get().createConfigBuilder();
|
||||||
|
|
||||||
{ // General computers
|
{ // General computers
|
||||||
computerSpaceLimit = builder
|
computerSpaceLimit = builder
|
||||||
@ -312,9 +308,9 @@ public final class ConfigSpec {
|
|||||||
builder.pop();
|
builder.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
serverSpec = builder.build();
|
serverSpec = builder.build(ConfigSpec::syncServer);
|
||||||
|
|
||||||
var clientBuilder = new TranslatingBuilder();
|
var clientBuilder = PlatformHelper.get().createConfigBuilder();
|
||||||
monitorRenderer = clientBuilder
|
monitorRenderer = clientBuilder
|
||||||
.comment("""
|
.comment("""
|
||||||
The renderer to use for monitors. Generally this should be kept at "best" - if
|
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.")
|
.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);
|
.defineInRange("upload_nag_delay", Config.uploadNagDelay, 0, 60);
|
||||||
|
|
||||||
clientSpec = clientBuilder.build();
|
clientSpec = clientBuilder.build(ConfigSpec::syncClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void syncServer() {
|
public static void syncServer() {
|
||||||
if (!serverSpec.isLoaded()) return;
|
|
||||||
|
|
||||||
// General
|
// General
|
||||||
Config.computerSpaceLimit = computerSpaceLimit.get();
|
Config.computerSpaceLimit = computerSpaceLimit.get();
|
||||||
Config.floppySpaceLimit = floppySpaceLimit.get();
|
Config.floppySpaceLimit = floppySpaceLimit.get();
|
||||||
@ -395,85 +389,9 @@ public final class ConfigSpec {
|
|||||||
Config.monitorHeight = monitorHeight.get();
|
Config.monitorHeight = monitorHeight.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void syncClient() {
|
public static void syncClient() {
|
||||||
if (!clientSpec.isLoaded()) return;
|
|
||||||
|
|
||||||
Config.monitorRenderer = monitorRenderer.get();
|
Config.monitorRenderer = monitorRenderer.get();
|
||||||
Config.monitorDistance = monitorDistance.get();
|
Config.monitorDistance = monitorDistance.get();
|
||||||
Config.uploadNagDelay = uploadNagDelay.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import com.mojang.authlib.GameProfile;
|
|||||||
import com.mojang.brigadier.arguments.ArgumentType;
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
import dan200.computercraft.api.network.wired.WiredElement;
|
import dan200.computercraft.api.network.wired.WiredElement;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import dan200.computercraft.shared.config.ConfigFile;
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||||
import dan200.computercraft.shared.network.container.ContainerData;
|
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();
|
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.
|
* Wrap a Minecraft registry in our own abstraction layer.
|
||||||
*
|
*
|
||||||
|
@ -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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ import dan200.computercraft.api.network.wired.WiredElement;
|
|||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.impl.AbstractComputerCraftAPI;
|
import dan200.computercraft.impl.AbstractComputerCraftAPI;
|
||||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||||
|
import dan200.computercraft.shared.config.ConfigFile;
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||||
import dan200.computercraft.shared.network.container.ContainerData;
|
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 })
|
@AutoService({ PlatformHelper.class, dan200.computercraft.impl.PlatformHelper.class, ComputerCraftAPIService.class })
|
||||||
public class TestPlatformHelper extends AbstractComputerCraftAPI implements PlatformHelper {
|
public class TestPlatformHelper extends AbstractComputerCraftAPI implements PlatformHelper {
|
||||||
|
@Override
|
||||||
|
public ConfigFile.Builder createConfigBuilder() {
|
||||||
|
throw new UnsupportedOperationException("Cannot create config file inside tests");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> RegistryWrappers.RegistryWrapper<T> wrap(ResourceKey<Registry<T>> registry) {
|
public <T> RegistryWrappers.RegistryWrapper<T> wrap(ResourceKey<Registry<T>> registry) {
|
||||||
throw new UnsupportedOperationException("Cannot query registry inside tests");
|
throw new UnsupportedOperationException("Cannot query registry inside tests");
|
||||||
|
@ -49,8 +49,7 @@ dependencies {
|
|||||||
"modTestWithIris"(libs.sodium)
|
"modTestWithIris"(libs.sodium)
|
||||||
|
|
||||||
include(libs.cobalt)
|
include(libs.cobalt)
|
||||||
include(libs.netty.http) // It might be better to shadowJar this, as we don't use half of it.
|
include(libs.netty.http)
|
||||||
include(libs.forgeConfig)
|
|
||||||
include(libs.nightConfig.core)
|
include(libs.nightConfig.core)
|
||||||
include(libs.nightConfig.toml)
|
include(libs.nightConfig.toml)
|
||||||
|
|
||||||
|
@ -21,10 +21,9 @@ import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
|
|||||||
import dan200.computercraft.shared.peripheral.modem.wired.CableBlockEntity;
|
import dan200.computercraft.shared.peripheral.modem.wired.CableBlockEntity;
|
||||||
import dan200.computercraft.shared.peripheral.modem.wired.WiredModemFullBlockEntity;
|
import dan200.computercraft.shared.peripheral.modem.wired.WiredModemFullBlockEntity;
|
||||||
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemBlockEntity;
|
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemBlockEntity;
|
||||||
|
import dan200.computercraft.shared.platform.FabricConfigFile;
|
||||||
import dan200.computercraft.shared.platform.NetworkHandler;
|
import dan200.computercraft.shared.platform.NetworkHandler;
|
||||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import fuzs.forgeconfigapiport.api.config.v2.ForgeConfigRegistry;
|
|
||||||
import fuzs.forgeconfigapiport.api.config.v2.ModConfigEvents;
|
|
||||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||||
@ -35,18 +34,21 @@ import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
|
|||||||
import net.fabricmc.fabric.api.loot.v2.LootTableEvents;
|
import net.fabricmc.fabric.api.loot.v2.LootTableEvents;
|
||||||
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
|
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
|
||||||
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
|
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.packs.PackType;
|
import net.minecraft.server.packs.PackType;
|
||||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
import net.minecraft.util.profiling.ProfilerFiller;
|
import net.minecraft.util.profiling.ProfilerFiller;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraftforge.fml.config.ModConfig;
|
import net.minecraft.world.level.storage.LevelResource;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
public class ComputerCraft {
|
public class ComputerCraft {
|
||||||
|
private static final LevelResource SERVERCONFIG = new LevelResource("serverconfig");
|
||||||
|
|
||||||
public static void init() {
|
public static void init() {
|
||||||
NetworkHandler.init();
|
NetworkHandler.init();
|
||||||
FabricRegistryBuilder.createSimple(TurtleUpgradeSerialiser.REGISTRY_ID).buildAndRegister();
|
FabricRegistryBuilder.createSimple(TurtleUpgradeSerialiser.REGISTRY_ID).buildAndRegister();
|
||||||
@ -81,8 +83,14 @@ public class ComputerCraft {
|
|||||||
CommandRegistrationCallback.EVENT.register((dispatcher, context, environment) -> CommandComputerCraft.register(dispatcher));
|
CommandRegistrationCallback.EVENT.register((dispatcher, context, environment) -> CommandComputerCraft.register(dispatcher));
|
||||||
|
|
||||||
// Register hooks
|
// Register hooks
|
||||||
ServerLifecycleEvents.SERVER_STARTING.register(CommonHooks::onServerStarting);
|
ServerLifecycleEvents.SERVER_STARTING.register(server -> {
|
||||||
ServerLifecycleEvents.SERVER_STOPPED.register(s -> CommonHooks.onServerStopped());
|
((FabricConfigFile) ConfigSpec.serverSpec).load(server.getWorldPath(SERVERCONFIG).resolve(ComputerCraftAPI.MOD_ID + "-server.toml"));
|
||||||
|
CommonHooks.onServerStarting(server);
|
||||||
|
});
|
||||||
|
ServerLifecycleEvents.SERVER_STOPPED.register(s -> {
|
||||||
|
CommonHooks.onServerStopped();
|
||||||
|
((FabricConfigFile) ConfigSpec.serverSpec).unload();
|
||||||
|
});
|
||||||
ServerLifecycleEvents.SYNC_DATA_PACK_CONTENTS.register((player, joined) -> PlatformHelper.get().sendToPlayer(new UpgradesLoadedMessage(), player));
|
ServerLifecycleEvents.SYNC_DATA_PACK_CONTENTS.register((player, joined) -> PlatformHelper.get().sendToPlayer(new UpgradesLoadedMessage(), player));
|
||||||
|
|
||||||
ServerTickEvents.START_SERVER_TICK.register(CommonHooks::onServerTickStart);
|
ServerTickEvents.START_SERVER_TICK.register(CommonHooks::onServerTickStart);
|
||||||
@ -98,11 +106,7 @@ public class ComputerCraft {
|
|||||||
|
|
||||||
CommonHooks.onDatapackReload((name, listener) -> ResourceManagerHelper.get(PackType.SERVER_DATA).registerReloadListener(new ReloadListener(name, listener)));
|
CommonHooks.onDatapackReload((name, listener) -> ResourceManagerHelper.get(PackType.SERVER_DATA).registerReloadListener(new ReloadListener(name, listener)));
|
||||||
|
|
||||||
// Config loading
|
((FabricConfigFile) ConfigSpec.clientSpec).load(FabricLoader.getInstance().getConfigDir().resolve(ComputerCraftAPI.MOD_ID + "-client.toml"));
|
||||||
ForgeConfigRegistry.INSTANCE.register(ComputerCraftAPI.MOD_ID, ModConfig.Type.SERVER, ConfigSpec.serverSpec);
|
|
||||||
ForgeConfigRegistry.INSTANCE.register(ComputerCraftAPI.MOD_ID, ModConfig.Type.CLIENT, ConfigSpec.clientSpec);
|
|
||||||
ModConfigEvents.loading(ComputerCraftAPI.MOD_ID).register(ConfigSpec::sync);
|
|
||||||
ModConfigEvents.reloading(ComputerCraftAPI.MOD_ID).register(ConfigSpec::sync);
|
|
||||||
|
|
||||||
FabricDetailRegistries.FLUID_VARIANT.addProvider(FluidDetails::fill);
|
FabricDetailRegistries.FLUID_VARIANT.addProvider(FluidDetails::fill);
|
||||||
|
|
||||||
|
@ -0,0 +1,274 @@
|
|||||||
|
/*
|
||||||
|
* 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.platform;
|
||||||
|
|
||||||
|
import com.electronwill.nightconfig.core.Config;
|
||||||
|
import com.electronwill.nightconfig.core.ConfigSpec;
|
||||||
|
import com.electronwill.nightconfig.core.EnumGetMethod;
|
||||||
|
import com.electronwill.nightconfig.core.UnmodifiableConfig;
|
||||||
|
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||||
|
import com.electronwill.nightconfig.core.file.FileNotFoundAction;
|
||||||
|
import com.electronwill.nightconfig.core.file.FileWatcher;
|
||||||
|
import com.electronwill.nightconfig.core.io.WritingMode;
|
||||||
|
import com.google.errorprone.annotations.concurrent.GuardedBy;
|
||||||
|
import dan200.computercraft.shared.config.ConfigFile;
|
||||||
|
import dan200.computercraft.shared.util.Trie;
|
||||||
|
import org.apache.commons.lang3.function.TriFunction;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link ConfigFile} which sits directly on top of NightConfig.
|
||||||
|
*/
|
||||||
|
public class FabricConfigFile implements ConfigFile {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(FabricConfigFile.class);
|
||||||
|
|
||||||
|
private final ConfigSpec spec;
|
||||||
|
private final Trie<String, Entry> entries;
|
||||||
|
private final Runnable onChange;
|
||||||
|
|
||||||
|
private @Nullable CommentedFileConfig config;
|
||||||
|
|
||||||
|
public FabricConfigFile(ConfigSpec spec, Trie<String, Entry> entries, Runnable onChange) {
|
||||||
|
this.spec = spec;
|
||||||
|
this.entries = entries;
|
||||||
|
this.onChange = onChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void load(Path path) {
|
||||||
|
closeConfig();
|
||||||
|
|
||||||
|
var config = this.config = CommentedFileConfig.builder(path).sync()
|
||||||
|
.onFileNotFound(FileNotFoundAction.READ_NOTHING)
|
||||||
|
.writingMode(WritingMode.REPLACE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.createDirectories(path.getParent());
|
||||||
|
FileWatcher.defaultInstance().addWatch(config.getNioPath(), this::loadConfig);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Failed to load config from {}.", path, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadConfig()) config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void unload() {
|
||||||
|
closeConfig();
|
||||||
|
|
||||||
|
entries.stream().forEach(x -> {
|
||||||
|
if (x instanceof ValueImpl<?> value) value.unload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
|
private void closeConfig() {
|
||||||
|
if (config == null) return;
|
||||||
|
|
||||||
|
config.close();
|
||||||
|
FileWatcher.defaultInstance().removeWatch(config.getNioPath());
|
||||||
|
config = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized boolean loadConfig() {
|
||||||
|
var config = this.config;
|
||||||
|
if (config == null) return false;
|
||||||
|
|
||||||
|
LOG.info("Loading config from {}", config.getNioPath());
|
||||||
|
|
||||||
|
config.load();
|
||||||
|
|
||||||
|
entries.stream().forEach(x -> {
|
||||||
|
config.setComment(x.path, x.comment);
|
||||||
|
if (x instanceof ValueImpl<?> value) value.load(config);
|
||||||
|
});
|
||||||
|
var corrected = spec.correct(config, (action, entryPath, oldValue, newValue) -> {
|
||||||
|
LOG.warn("Incorrect key {} was corrected from {} to {}", String.join(".", entryPath), oldValue, newValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
onChange.run();
|
||||||
|
|
||||||
|
return corrected > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<ConfigFile.Entry> entries() {
|
||||||
|
return entries.children().map(x -> (ConfigFile.Entry) x);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ConfigFile.Entry getEntry(String path) {
|
||||||
|
return (ConfigFile.Entry) entries.getValue(SPLITTER.split(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Builder extends ConfigFile.Builder {
|
||||||
|
private final ConfigSpec spec = new ConfigSpec();
|
||||||
|
private final Trie<String, Entry> entries = new Trie<>();
|
||||||
|
|
||||||
|
private @Nullable String pendingComment;
|
||||||
|
|
||||||
|
private String getFullPath(String path) {
|
||||||
|
var key = new StringBuilder();
|
||||||
|
for (var group : groupStack) key.append(group).append('.');
|
||||||
|
key.append(path);
|
||||||
|
return key.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigFile.Builder comment(String comment) {
|
||||||
|
if (pendingComment != null) throw new IllegalStateException("Already have a comment");
|
||||||
|
pendingComment = comment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String takeComment() {
|
||||||
|
var comment = pendingComment;
|
||||||
|
if (comment == null) throw new IllegalStateException("No comment specified");
|
||||||
|
pendingComment = null;
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String takeComment(String suffix) {
|
||||||
|
var comment = pendingComment == null ? "" : pendingComment + "\n";
|
||||||
|
pendingComment = null;
|
||||||
|
return comment + suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void push(String name) {
|
||||||
|
var path = getFullPath(name);
|
||||||
|
var splitPath = SPLITTER.split(path);
|
||||||
|
entries.setValue(splitPath, new GroupImpl(path, takeComment(), entries.getChild(splitPath)));
|
||||||
|
|
||||||
|
super.push(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigFile.Builder worldRestart() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> Value<T> defineValue(String fullPath, String comment, T defaultValue, TriFunction<Config, String, T, T> getter) {
|
||||||
|
var value = new ValueImpl<T>(fullPath, comment, defaultValue, getter);
|
||||||
|
entries.setValue(SPLITTER.split(fullPath), value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Value<T> define(String path, T defaultValue) {
|
||||||
|
var fullPath = getFullPath(path);
|
||||||
|
spec.define(fullPath, defaultValue);
|
||||||
|
return defineValue(fullPath, takeComment(), defaultValue, Config::getOrElse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value<Boolean> define(String path, boolean defaultValue) {
|
||||||
|
var fullPath = getFullPath(path);
|
||||||
|
spec.define(fullPath, defaultValue, x -> x instanceof Boolean);
|
||||||
|
return defineValue(fullPath, takeComment(), defaultValue, UnmodifiableConfig::getOrElse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value<Integer> defineInRange(String path, int defaultValue, int min, int max) {
|
||||||
|
var fullPath = getFullPath(path);
|
||||||
|
spec.defineInRange(fullPath, defaultValue, min, max);
|
||||||
|
|
||||||
|
var suffix = max == Integer.MAX_VALUE ? "Range: > " + min : "Range: " + min + " ~ " + max;
|
||||||
|
return defineValue(fullPath, takeComment(suffix), defaultValue, UnmodifiableConfig::getIntOrElse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Value<List<? extends T>> defineList(String path, List<? extends T> defaultValue, Predicate<Object> elementValidator) {
|
||||||
|
var fullPath = getFullPath(path);
|
||||||
|
spec.defineList(fullPath, defaultValue, elementValidator);
|
||||||
|
return defineValue(fullPath, takeComment(), defaultValue, Config::getOrElse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V extends Enum<V>> Value<V> defineEnum(String path, V defaultValue) {
|
||||||
|
var fullPath = getFullPath(path);
|
||||||
|
spec.defineEnum(fullPath, defaultValue, EnumGetMethod.NAME_IGNORECASE);
|
||||||
|
|
||||||
|
var suffix = "Allowed Values: " + Arrays.stream(defaultValue.getDeclaringClass().getEnumConstants()).map(Enum::name).collect(Collectors.joining(", "));
|
||||||
|
return defineValue(path, takeComment(suffix), defaultValue, (c, p, d) -> c.getEnumOrElse(p, d, EnumGetMethod.NAME_IGNORECASE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigFile build(Runnable onChange) {
|
||||||
|
return new FabricConfigFile(spec, entries, onChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Entry {
|
||||||
|
final String path;
|
||||||
|
final String comment;
|
||||||
|
|
||||||
|
Entry(String path, String comment) {
|
||||||
|
this.path = path;
|
||||||
|
this.comment = comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String translationKey() {
|
||||||
|
return TRANSLATION_PREFIX + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String comment() {
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class GroupImpl extends Entry implements Group {
|
||||||
|
private final Trie<String, Entry> children;
|
||||||
|
|
||||||
|
private GroupImpl(String path, String comment, Trie<String, Entry> children) {
|
||||||
|
super(path, comment);
|
||||||
|
this.children = children;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<ConfigFile.Entry> children() {
|
||||||
|
return children.children().map(x -> (ConfigFile.Entry) x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ValueImpl<T> extends Entry implements Value<T> {
|
||||||
|
private @Nullable T value;
|
||||||
|
private final T defaultValue;
|
||||||
|
private final TriFunction<Config, String, T, T> get;
|
||||||
|
|
||||||
|
private ValueImpl(String path, String comment, T defaultValue, TriFunction<Config, String, T, T> get) {
|
||||||
|
super(path, comment);
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
this.get = get;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unload() {
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load(Config config) {
|
||||||
|
value = get.apply(config, path, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
var value = this.value;
|
||||||
|
if (value == null) throw new IllegalStateException("Config value " + path + " is not available");
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@ import dan200.computercraft.api.node.wired.WiredElementLookup;
|
|||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.peripheral.PeripheralLookup;
|
import dan200.computercraft.api.peripheral.PeripheralLookup;
|
||||||
import dan200.computercraft.mixin.ArgumentTypeInfosAccessor;
|
import dan200.computercraft.mixin.ArgumentTypeInfosAccessor;
|
||||||
|
import dan200.computercraft.shared.config.ConfigFile;
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||||
import dan200.computercraft.shared.network.container.ContainerData;
|
import dan200.computercraft.shared.network.container.ContainerData;
|
||||||
@ -81,6 +82,11 @@ import java.util.function.*;
|
|||||||
|
|
||||||
@AutoService(dan200.computercraft.impl.PlatformHelper.class)
|
@AutoService(dan200.computercraft.impl.PlatformHelper.class)
|
||||||
public class PlatformHelperImpl implements PlatformHelper {
|
public class PlatformHelperImpl implements PlatformHelper {
|
||||||
|
@Override
|
||||||
|
public ConfigFile.Builder createConfigBuilder() {
|
||||||
|
return new FabricConfigFile.Builder();
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static <T> Registry<T> getRegistry(ResourceKey<Registry<T>> id) {
|
private static <T> Registry<T> getRegistry(ResourceKey<Registry<T>> id) {
|
||||||
var registry = (Registry<T>) BuiltInRegistries.REGISTRY.get(id.location());
|
var registry = (Registry<T>) BuiltInRegistries.REGISTRY.get(id.location());
|
||||||
|
@ -14,6 +14,7 @@ import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
|
|||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.config.ConfigSpec;
|
import dan200.computercraft.shared.config.ConfigSpec;
|
||||||
|
import dan200.computercraft.shared.platform.ForgeConfigFile;
|
||||||
import dan200.computercraft.shared.details.FluidData;
|
import dan200.computercraft.shared.details.FluidData;
|
||||||
import dan200.computercraft.shared.peripheral.generic.methods.EnergyMethods;
|
import dan200.computercraft.shared.peripheral.generic.methods.EnergyMethods;
|
||||||
import dan200.computercraft.shared.peripheral.generic.methods.FluidMethods;
|
import dan200.computercraft.shared.peripheral.generic.methods.FluidMethods;
|
||||||
@ -38,8 +39,8 @@ public final class ComputerCraft {
|
|||||||
public ComputerCraft() {
|
public ComputerCraft() {
|
||||||
ModRegistry.register();
|
ModRegistry.register();
|
||||||
|
|
||||||
ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, ConfigSpec.serverSpec);
|
ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, ((ForgeConfigFile) ConfigSpec.serverSpec).spec());
|
||||||
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ConfigSpec.clientSpec);
|
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ((ForgeConfigFile) ConfigSpec.clientSpec).spec());
|
||||||
|
|
||||||
NetworkHandler.setup();
|
NetworkHandler.setup();
|
||||||
}
|
}
|
||||||
@ -83,11 +84,21 @@ public final class ComputerCraft {
|
|||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void sync(ModConfigEvent.Loading event) {
|
public static void sync(ModConfigEvent.Loading event) {
|
||||||
ConfigSpec.sync(event.getConfig());
|
syncConfig(event.getConfig());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void sync(ModConfigEvent.Reloading event) {
|
public static void sync(ModConfigEvent.Reloading event) {
|
||||||
ConfigSpec.sync(event.getConfig());
|
syncConfig(event.getConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void syncConfig(ModConfig config) {
|
||||||
|
if (!config.getModId().equals(ComputerCraftAPI.MOD_ID)) return;
|
||||||
|
|
||||||
|
if (config.getType() == ModConfig.Type.SERVER && ((ForgeConfigFile) ConfigSpec.serverSpec).spec().isLoaded()) {
|
||||||
|
ConfigSpec.syncServer();
|
||||||
|
} else if (config.getType() == ModConfig.Type.CLIENT) {
|
||||||
|
ConfigSpec.syncClient();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* 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.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.function.Predicate;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link ConfigFile} which wraps Forge's config implementation.
|
||||||
|
*/
|
||||||
|
public final class ForgeConfigFile implements ConfigFile {
|
||||||
|
private final ForgeConfigSpec spec;
|
||||||
|
private final Trie<String, ConfigFile.Entry> entries;
|
||||||
|
|
||||||
|
public ForgeConfigFile(ForgeConfigSpec spec, Trie<String, Entry> entries) {
|
||||||
|
this.spec = spec;
|
||||||
|
this.entries = entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ForgeConfigSpec spec() {
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<Entry> entries() {
|
||||||
|
return entries.children();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigFile.Builder comment(String comment) {
|
||||||
|
builder.comment(comment);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void push(String name) {
|
||||||
|
translation(name);
|
||||||
|
builder.push(name);
|
||||||
|
super.push(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pop() {
|
||||||
|
var path = new ArrayList<>(groupStack);
|
||||||
|
entries.setValue(path, new GroupImpl(path, entries.getChild(path)));
|
||||||
|
|
||||||
|
builder.pop();
|
||||||
|
super.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigFile.Builder worldRestart() {
|
||||||
|
builder.worldRestart();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> ConfigFile.Value<T> defineValue(ForgeConfigSpec.ConfigValue<T> value) {
|
||||||
|
var wrapped = new ValueImpl<>(value);
|
||||||
|
entries.setValue(value.getPath(), wrapped);
|
||||||
|
return wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> ConfigFile.Value<T> define(String path, T defaultValue) {
|
||||||
|
translation(path);
|
||||||
|
return defineValue(builder.define(path, defaultValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigFile.Value<Boolean> define(String path, boolean defaultValue) {
|
||||||
|
translation(path);
|
||||||
|
return defineValue(builder.define(path, 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
@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));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V extends Enum<V>> ConfigFile.Value<V> defineEnum(String path, V defaultValue) {
|
||||||
|
translation(path);
|
||||||
|
return defineValue(builder.defineEnum(path, defaultValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigFile build(Runnable onChange) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class GroupImpl implements ConfigFile.Group {
|
||||||
|
private final List<String> path;
|
||||||
|
private final Trie<String, ConfigFile.Entry> entries;
|
||||||
|
private @Nullable ForgeConfigSpec owner;
|
||||||
|
|
||||||
|
private GroupImpl(List<String> path, Trie<String, ConfigFile.Entry> entries) {
|
||||||
|
this.path = path;
|
||||||
|
this.entries = entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<Entry> children() {
|
||||||
|
return entries.children();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ValueImpl<T> implements ConfigFile.Value<T> {
|
||||||
|
private final ForgeConfigSpec.ConfigValue<T> value;
|
||||||
|
private @Nullable ForgeConfigSpec owner;
|
||||||
|
|
||||||
|
private ValueImpl(ForgeConfigSpec.ConfigValue<T> value) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ import dan200.computercraft.api.network.wired.WiredElement;
|
|||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.impl.Peripherals;
|
import dan200.computercraft.impl.Peripherals;
|
||||||
import dan200.computercraft.shared.Capabilities;
|
import dan200.computercraft.shared.Capabilities;
|
||||||
|
import dan200.computercraft.shared.config.ConfigFile;
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||||
import dan200.computercraft.shared.network.container.ContainerData;
|
import dan200.computercraft.shared.network.container.ContainerData;
|
||||||
@ -78,6 +79,11 @@ import java.util.function.*;
|
|||||||
|
|
||||||
@AutoService(dan200.computercraft.impl.PlatformHelper.class)
|
@AutoService(dan200.computercraft.impl.PlatformHelper.class)
|
||||||
public class PlatformHelperImpl implements PlatformHelper {
|
public class PlatformHelperImpl implements PlatformHelper {
|
||||||
|
@Override
|
||||||
|
public ConfigFile.Builder createConfigBuilder() {
|
||||||
|
return new ForgeConfigFile.Builder();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> ResourceLocation getRegistryKey(ResourceKey<Registry<T>> registry, T object) {
|
public <T> ResourceLocation getRegistryKey(ResourceKey<Registry<T>> registry, T object) {
|
||||||
var key = RegistryManager.ACTIVE.getRegistry(registry).getKey(object);
|
var key = RegistryManager.ACTIVE.getRegistry(registry).getKey(object);
|
||||||
|
Loading…
Reference in New Issue
Block a user