mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-30 13:13:00 +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:
@@ -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.WiredModemFullBlockEntity;
|
||||
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.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.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
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.resource.IdentifiableResourceReloadListener;
|
||||
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
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.Executor;
|
||||
|
||||
public class ComputerCraft {
|
||||
private static final LevelResource SERVERCONFIG = new LevelResource("serverconfig");
|
||||
|
||||
public static void init() {
|
||||
NetworkHandler.init();
|
||||
FabricRegistryBuilder.createSimple(TurtleUpgradeSerialiser.REGISTRY_ID).buildAndRegister();
|
||||
@@ -81,8 +83,14 @@ public class ComputerCraft {
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, context, environment) -> CommandComputerCraft.register(dispatcher));
|
||||
|
||||
// Register hooks
|
||||
ServerLifecycleEvents.SERVER_STARTING.register(CommonHooks::onServerStarting);
|
||||
ServerLifecycleEvents.SERVER_STOPPED.register(s -> CommonHooks.onServerStopped());
|
||||
ServerLifecycleEvents.SERVER_STARTING.register(server -> {
|
||||
((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));
|
||||
|
||||
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)));
|
||||
|
||||
// Config loading
|
||||
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);
|
||||
((FabricConfigFile) ConfigSpec.clientSpec).load(FabricLoader.getInstance().getConfigDir().resolve(ComputerCraftAPI.MOD_ID + "-client.toml"));
|
||||
|
||||
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.PeripheralLookup;
|
||||
import dan200.computercraft.mixin.ArgumentTypeInfosAccessor;
|
||||
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;
|
||||
@@ -81,6 +82,11 @@ import java.util.function.*;
|
||||
|
||||
@AutoService(dan200.computercraft.impl.PlatformHelper.class)
|
||||
public class PlatformHelperImpl implements PlatformHelper {
|
||||
@Override
|
||||
public ConfigFile.Builder createConfigBuilder() {
|
||||
return new FabricConfigFile.Builder();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> Registry<T> getRegistry(ResourceKey<Registry<T>> id) {
|
||||
var registry = (Registry<T>) BuiltInRegistries.REGISTRY.get(id.location());
|
||||
|
||||
Reference in New Issue
Block a user