aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/build.gradle.kts1
-rw-r--r--common/src/main/java/dev/isxander/yacl3/config/v2/api/GsonConfigSerializerBuilder.java2
-rw-r--r--common/src/main/java/dev/isxander/yacl3/config/v2/api/SimpleOptionFactory.java5
-rw-r--r--common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/MasterTickBoxImpl.java14
-rw-r--r--common/src/main/java/dev/isxander/yacl3/config/v2/impl/serializer/GsonConfigSerializer.java109
5 files changed, 81 insertions, 50 deletions
diff --git a/common/build.gradle.kts b/common/build.gradle.kts
index 7d5ece7..ba6dd04 100644
--- a/common/build.gradle.kts
+++ b/common/build.gradle.kts
@@ -28,6 +28,7 @@ dependencies {
modImplementation(libs.fabric.loader)
implementation(libs.bundles.twelvemonkeys.imageio)
+ implementation(libs.bundles.quilt.parsers)
}
java {
diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/api/GsonConfigSerializerBuilder.java b/common/src/main/java/dev/isxander/yacl3/config/v2/api/GsonConfigSerializerBuilder.java
index 8d8d6c7..e871b7c 100644
--- a/common/src/main/java/dev/isxander/yacl3/config/v2/api/GsonConfigSerializerBuilder.java
+++ b/common/src/main/java/dev/isxander/yacl3/config/v2/api/GsonConfigSerializerBuilder.java
@@ -64,5 +64,7 @@ public interface GsonConfigSerializerBuilder<T> {
*/
GsonConfigSerializerBuilder<T> appendGsonBuilder(UnaryOperator<GsonBuilder> gsonBuilder);
+ GsonConfigSerializerBuilder<T> setJson5(boolean json5);
+
ConfigSerializer<T> build();
}
diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/api/SimpleOptionFactory.java b/common/src/main/java/dev/isxander/yacl3/config/v2/api/SimpleOptionFactory.java
index 5c6894e..67b1d87 100644
--- a/common/src/main/java/dev/isxander/yacl3/config/v2/api/SimpleOptionFactory.java
+++ b/common/src/main/java/dev/isxander/yacl3/config/v2/api/SimpleOptionFactory.java
@@ -26,6 +26,7 @@ public abstract class SimpleOptionFactory<A extends Annotation, T> implements Op
.controller(opt -> this.createController(annotation, field, storage, opt))
.available(this.available(annotation, field, storage))
.flags(this.flags(annotation, field, storage))
+ .listener((opt, v) -> this.listener(annotation, field, storage, opt, v))
.build();
postInit(annotation, field, storage, option);
@@ -70,6 +71,10 @@ public abstract class SimpleOptionFactory<A extends Annotation, T> implements Op
return Set.of();
}
+ protected void listener(A annotation, ConfigField<T> field, OptionStorage storage, Option<T> option, T value) {
+
+ }
+
protected void postInit(A annotation, ConfigField<T> field, OptionStorage storage, Option<T> option) {
}
diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/MasterTickBoxImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/MasterTickBoxImpl.java
index 65433a3..fa2b7b5 100644
--- a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/MasterTickBoxImpl.java
+++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/MasterTickBoxImpl.java
@@ -15,13 +15,11 @@ public class MasterTickBoxImpl extends SimpleOptionFactory<MasterTickBox, Boolea
}
@Override
- protected void postInit(MasterTickBox annotation, ConfigField<Boolean> field, OptionStorage storage, Option<Boolean> option) {
- option.addListener((opt, val) -> {
- for (String child : annotation.value()) {
- storage.scheduleOptionOperation(child, childOpt -> {
- childOpt.setAvailable(annotation.invert() != val);
- });
- }
- });
+ protected void listener(MasterTickBox annotation, ConfigField<Boolean> field, OptionStorage storage, Option<Boolean> option, Boolean value) {
+ for (String child : annotation.value()) {
+ storage.scheduleOptionOperation(child, childOpt -> {
+ childOpt.setAvailable(annotation.invert() != value);
+ });
+ }
}
}
diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/serializer/GsonConfigSerializer.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/serializer/GsonConfigSerializer.java
index 712a459..c59d20b 100644
--- a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/serializer/GsonConfigSerializer.java
+++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/serializer/GsonConfigSerializer.java
@@ -6,52 +6,71 @@ import dev.isxander.yacl3.impl.utils.YACLConstants;
import dev.isxander.yacl3.platform.YACLPlatform;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
+import org.quiltmc.parsers.json.JsonReader;
+import org.quiltmc.parsers.json.JsonWriter;
+import org.quiltmc.parsers.json.gson.GsonReader;
+import org.quiltmc.parsers.json.gson.GsonWriter;
import java.awt.*;
+import java.io.IOException;
+import java.io.StringWriter;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
public class GsonConfigSerializer<T> extends ConfigSerializer<T> {
private final Gson gson;
private final Path path;
+ private final boolean json5;
- private GsonConfigSerializer(ConfigClassHandler<T> config, Path path, Gson gson) {
+ private GsonConfigSerializer(ConfigClassHandler<T> config, Path path, Gson gson, boolean json5) {
super(config);
this.gson = gson;
this.path = path;
+ this.json5 = json5;
}
@Override
public void serialize() {
- JsonObject root = new JsonObject();
+ YACLConstants.LOGGER.info("Serializing {} to '{}'", config.configClass(), path);
- for (ConfigField<?> field : config.fields()) {
- SerialField serial = field.serial().orElse(null);
- if (serial == null) {
- continue;
- }
+ try (StringWriter stringWriter = new StringWriter()) {
+ JsonWriter jsonWriter = json5 ? JsonWriter.json5(stringWriter) : JsonWriter.json(stringWriter);
+ GsonWriter gsonWriter = new GsonWriter(jsonWriter);
+ jsonWriter.beginObject();
+ for (ConfigField<?> field : config.fields()) {
+ SerialField serial = field.serial().orElse(null);
+ if (serial == null) {
+ continue;
+ }
- if (YACLPlatform.isDevelopmentEnv() && serial.comment().isPresent()) {
- YACLConstants.LOGGER.error("Config field '{}' has a comment, but comments are not supported by Gson. Please remove the comment or switch to a different serializer. This log will not be shown in production.", serial.serialName());
- }
+ if (!json5 && serial.comment().isPresent() && YACLPlatform.isDevelopmentEnv()) {
+ YACLConstants.LOGGER.warn("Found comment in config field '{}', but json5 is not enabled. Enable it with `.setJson5(true)` on the `GsonConfigSerializerBuilder`. Comments will not be serialized. This warning is only visible in development environments.", serial.serialName());
+ }
+ jsonWriter.comment(serial.comment().orElse(null));
- try {
- root.add(serial.serialName(), gson.toJsonTree(field.access().get()));
- } catch (Exception e) {
- YACLConstants.LOGGER.error("Failed to serialize config field '{}'.", serial.serialName(), e);
+ jsonWriter.name(serial.serialName());
+ try {
+ gson.toJson(field.access().get(), field.access().type(), gsonWriter);
+ } catch (Exception e) {
+ YACLConstants.LOGGER.error("Failed to serialize config field '{}'.", serial.serialName(), e);
+ jsonWriter.nullValue();
+ }
}
- }
+ jsonWriter.endObject();
+ jsonWriter.flush();
- YACLConstants.LOGGER.info("Serializing {} to '{}'", config.configClass(), path);
- try {
- Files.writeString(path, gson.toJson(root), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
- } catch (Exception e) {
+ Files.createDirectories(path.getParent());
+ Files.writeString(path, stringWriter.toString(), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
+ } catch (IOException e) {
YACLConstants.LOGGER.error("Failed to serialize config class '{}'.", config.configClass().getSimpleName(), e);
}
}
@@ -66,38 +85,37 @@ public class GsonConfigSerializer<T> extends ConfigSerializer<T> {
YACLConstants.LOGGER.info("Deserializing {} from '{}'", config.configClass().getSimpleName(), path);
- String json;
- try {
- json = Files.readString(path);
- } catch (Exception e) {
- throw new IllegalStateException("Failed to read '%s' for deserialization.".formatted(path), e);
- }
+ try (JsonReader jsonReader = json5 ? JsonReader.json5(path) : JsonReader.json(path)) {
+ GsonReader gsonReader = new GsonReader(jsonReader);
- Map<String, JsonElement> root = gson.fromJson(json, JsonObject.class).asMap();
- List<String> unconsumedKeys = new ArrayList<>(root.keySet());
+ Map<String, ConfigField<?>> fieldMap = Arrays.stream(config.fields())
+ .filter(field -> field.serial().isPresent())
+ .collect(Collectors.toMap(f -> f.serial().orElseThrow().serialName(), Function.identity()));
- for (ConfigField<?> field : config.fields()) {
- SerialField serial = field.serial().orElse(null);
- if (serial == null) {
- continue;
- }
+ jsonReader.beginObject();
+
+ while (jsonReader.hasNext()) {
+ String name = jsonReader.nextName();
+ ConfigField<?> field = fieldMap.get(name);
+ if (field == null) {
+ YACLConstants.LOGGER.warn("Found unknown config field '{}' in '{}'.", name, path);
+ jsonReader.skipValue();
+ continue;
+ }
- if (root.containsKey(serial.serialName())) {
try {
- field.access().set(gson.fromJson(root.get(serial.serialName()), field.access().type()));
+ field.access().set(gson.fromJson(gsonReader, field.access().type()));
} catch (Exception e) {
- YACLConstants.LOGGER.error("Failed to deserialize config field '{}'.", serial.serialName(), e);
+ YACLConstants.LOGGER.error("Failed to deserialize config field '{}'.", name, e);
+ jsonReader.skipValue();
}
- } else {
- YACLConstants.LOGGER.warn("Config field '{}' was not found in the config file. Skipping.", serial.serialName());
}
- unconsumedKeys.remove(serial.serialName());
+ jsonReader.endObject();
+ } catch (IOException e) {
+ YACLConstants.LOGGER.error("Failed to deserialize config class '{}'.", config.configClass().getSimpleName(), e);
}
- if (!unconsumedKeys.isEmpty()) {
- YACLConstants.LOGGER.warn("The following keys were not consumed by the config class: {}", String.join(", ", unconsumedKeys));
- }
}
public static class ColorTypeAdapter implements JsonSerializer<Color>, JsonDeserializer<Color> {
@@ -115,6 +133,7 @@ public class GsonConfigSerializer<T> extends ConfigSerializer<T> {
public static class Builder<T> implements GsonConfigSerializerBuilder<T> {
private final ConfigClassHandler<T> config;
private Path path;
+ private boolean json5;
private UnaryOperator<GsonBuilder> gsonBuilder = builder -> builder
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.serializeNulls()
@@ -152,8 +171,14 @@ public class GsonConfigSerializer<T> extends ConfigSerializer<T> {
}
@Override
+ public Builder<T> setJson5(boolean json5) {
+ this.json5 = json5;
+ return this;
+ }
+
+ @Override
public GsonConfigSerializer<T> build() {
- return new GsonConfigSerializer<>(config, path, gsonBuilder.apply(new GsonBuilder()).create());
+ return new GsonConfigSerializer<>(config, path, gsonBuilder.apply(new GsonBuilder()).create(), json5);
}
}
}