JSON Fabrication

- Add rudimentary JSON parsing to load a config on fabric
- Save the config file after a config command changes
This commit is contained in:
Jozufozu 2024-05-09 12:09:19 -07:00
parent 18676599a2
commit b95201f001
3 changed files with 149 additions and 10 deletions

View File

@ -1,15 +1,48 @@
package com.jozufozu.flywheel.impl;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.nio.file.Path;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.backend.Backend;
import com.jozufozu.flywheel.api.backend.BackendManager;
// TODO: Fabric config
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.ResourceLocationException;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
public class FabricFlwConfig implements FlwConfig {
public static final FabricFlwConfig INSTANCE = new FabricFlwConfig();
public static final Path PATH = FabricLoader.getInstance()
.getConfigDir()
.resolve("flywheel.json");
public static final FabricFlwConfig INSTANCE = new FabricFlwConfig(PATH.toFile());
protected static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
public static final int WORKER_THREADS_DEFAULT = -1;
public static final int WORKER_THREADS_MAX = Runtime.getRuntime()
.availableProcessors();
public static final boolean LIMIT_UPDATES_DEFAULT = true;
public Backend backend = BackendManager.getDefaultBackend();
public boolean limitUpdates = true;
public int workerThreads = -1;
public boolean limitUpdates = LIMIT_UPDATES_DEFAULT;
public int workerThreads = WORKER_THREADS_DEFAULT;
private final File file;
public FabricFlwConfig(File file) {
this.file = file;
}
@Override
public Backend backend() {
@ -25,4 +58,106 @@ public class FabricFlwConfig implements FlwConfig {
public int workerThreads() {
return workerThreads;
}
public void load() {
if (file.exists()) {
try (FileReader reader = new FileReader(file)) {
fromJson(JsonParser.parseReader(reader));
} catch (Exception e) {
Flywheel.LOGGER.error("Could not load config from file '{}'", file.getAbsolutePath(), e);
}
}
// In case we found an error in the config file, immediately save to fix it.
save();
}
public void save() {
try (FileWriter writer = new FileWriter(file)) {
GSON.toJson(toJson(), writer);
} catch (Exception e) {
Flywheel.LOGGER.error("Could not save config to file '{}'", file.getAbsolutePath(), e);
}
}
public void fromJson(JsonElement json) {
if (!(json instanceof JsonObject object)) {
backend = BackendManager.getDefaultBackend();
limitUpdates = LIMIT_UPDATES_DEFAULT;
workerThreads = WORKER_THREADS_DEFAULT;
return;
}
readBackend(object);
readLimitUpdates(object);
readWorkerThreads(object);
}
private void readBackend(JsonObject object) {
var backendJson = object.get("backend");
String err = null;
if (backendJson instanceof JsonPrimitive primitive && primitive.isString()) {
var backendString = primitive.getAsString();
try {
this.backend = Backend.REGISTRY.getOrThrow(new ResourceLocation(backendString));
return;
} catch (IllegalArgumentException e) {
err = "backend ID '" + backendString + "' is not registered";
} catch (ResourceLocationException e) {
err = "backend '" + backendString + "' is not a valid resource location";
} catch (Exception e) {
// Something else went wrong? This should be dead code.
err = "backend '" + backendString + "' is invalid";
}
} else if (backendJson != null) {
err = "backend must be a string";
}
// Don't log an error if the field is missing.
if (err != null) {
Flywheel.LOGGER.warn(err);
}
backend = BackendManager.getDefaultBackend();
}
private void readLimitUpdates(JsonObject object) {
var limitUpdatesJson = object.get("limitUpdates");
if (limitUpdatesJson instanceof JsonPrimitive primitive && primitive.isBoolean()) {
limitUpdates = primitive.getAsBoolean();
return;
} else if (limitUpdatesJson != null) {
Flywheel.LOGGER.warn("limitUpdates must be a boolean");
}
limitUpdates = LIMIT_UPDATES_DEFAULT;
}
private void readWorkerThreads(JsonObject object) {
var workerThreadsJson = object.get("workerThreads");
if (workerThreadsJson instanceof JsonPrimitive primitive && primitive.isNumber()) {
int configuredValue = primitive.getAsInt();
int clamped = Mth.clamp(configuredValue, WORKER_THREADS_DEFAULT, WORKER_THREADS_MAX);
if (clamped != configuredValue) {
Flywheel.LOGGER.warn("workerThreads value of {} is out of range, clamping to {}", configuredValue, clamped);
}
workerThreads = clamped;
return;
} else if (workerThreadsJson != null) {
Flywheel.LOGGER.warn("workerThreads must be an integer");
}
workerThreads = WORKER_THREADS_DEFAULT;
}
public JsonObject toJson() {
JsonObject object = new JsonObject();
object.addProperty("limitUpdates", limitUpdates);
object.addProperty("workerThreads", workerThreads);
object.addProperty("backend", Backend.REGISTRY.getIdOrThrow(backend).toString());
return object;
}
}

View File

@ -25,8 +25,6 @@ public final class FlwCommands {
}
public static void registerClientCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandBuildContext buildContext) {
FabricFlwConfig config = FabricFlwConfig.INSTANCE;
LiteralArgumentBuilder<FabricClientCommandSource> command = ClientCommandManager.literal("flywheel");
command.then(ClientCommandManager.literal("backend")
@ -40,7 +38,8 @@ public final class FlwCommands {
.then(ClientCommandManager.argument("id", BackendArgument.INSTANCE)
.executes(context -> {
Backend requestedBackend = context.getArgument("id", Backend.class);
config.backend = requestedBackend;
FabricFlwConfig.INSTANCE.backend = requestedBackend;
FabricFlwConfig.INSTANCE.save();
// Reload renderers so we can report the actual backend.
Minecraft.getInstance().levelRenderer.allChanged();
@ -60,7 +59,7 @@ public final class FlwCommands {
command.then(ClientCommandManager.literal("limitUpdates")
.executes(context -> {
if (config.limitUpdates) {
if (FabricFlwConfig.INSTANCE.limitUpdates) {
context.getSource().sendFeedback(Component.translatable("command.flywheel.limit_updates.get.on"));
} else {
context.getSource().sendFeedback(Component.translatable("command.flywheel.limit_updates.get.off"));
@ -69,14 +68,16 @@ public final class FlwCommands {
})
.then(ClientCommandManager.literal("on")
.executes(context -> {
config.limitUpdates = true;
FabricFlwConfig.INSTANCE.limitUpdates = true;
FabricFlwConfig.INSTANCE.save();
context.getSource().sendFeedback(Component.translatable("command.flywheel.limit_updates.set.on"));
Minecraft.getInstance().levelRenderer.allChanged();
return Command.SINGLE_SUCCESS;
}))
.then(ClientCommandManager.literal("off")
.executes(context -> {
config.limitUpdates = false;
FabricFlwConfig.INSTANCE.limitUpdates = false;
FabricFlwConfig.INSTANCE.save();
context.getSource().sendFeedback(Component.translatable("command.flywheel.limit_updates.set.off"));
Minecraft.getInstance().levelRenderer.allChanged();
return Command.SINGLE_SUCCESS;

View File

@ -44,6 +44,9 @@ public final class FlywheelFabric implements ClientModInitializer {
// FIXME: Registries cannot be frozen this early.
FlywheelInit.freezeRegistries();
// Have to load the config after we freeze registries,
// so we can find third party backends.
FabricFlwConfig.INSTANCE.load();
}
private static void setupImpl() {