aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorAaron <51387595+AzureAaron@users.noreply.github.com>2025-06-26 18:41:54 -0400
committerAaron <51387595+AzureAaron@users.noreply.github.com>2025-06-26 18:41:54 -0400
commita3cb1c28fb7d8cd8c22f528aae7d6ca4ba523513 (patch)
tree0f4ac251d4741e3a2dee4c28a2a4e4a641e3d50a /src/main/java
parent7c9e4500ccabf576757171f6cdcf755d292fa92a (diff)
downloadSkyblocker-a3cb1c28fb7d8cd8c22f528aae7d6ca4ba523513.tar.gz
Skyblocker-a3cb1c28fb7d8cd8c22f528aae7d6ca4ba523513.tar.bz2
Skyblocker-a3cb1c28fb7d8cd8c22f528aae7d6ca4ba523513.zip
Use custom vanilla GSON config serialization
The mysterious config wipe errors were the result of malformed json, normal GSON is extensively tested and is highly unlikely to produce that itself but YACL uses an intermediary parser that has had similar problems with this stuff in the past. By circumventing that library, this issue can hopefully be fixed.
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java25
-rw-r--r--src/main/java/de/hysky/skyblocker/config/serialization/CodecTypeAdapter.java (renamed from src/main/java/de/hysky/skyblocker/config/CodecTypeAdapter.java)2
-rw-r--r--src/main/java/de/hysky/skyblocker/config/serialization/ItemTypeAdapter.java29
-rw-r--r--src/main/java/de/hysky/skyblocker/config/serialization/VanillaGsonConfigSerializer.java84
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/accessors/ConfigClassHandlerImplAccessor.java13
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/CodecUtils.java2
6 files changed, 147 insertions, 8 deletions
diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java
index 33c92959..6449e770 100644
--- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java
+++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java
@@ -4,12 +4,15 @@ import com.google.gson.FieldNamingPolicy;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.config.categories.*;
+import de.hysky.skyblocker.config.serialization.CodecTypeAdapter;
+import de.hysky.skyblocker.config.serialization.ItemTypeAdapter;
+import de.hysky.skyblocker.config.serialization.VanillaGsonConfigSerializer;
import de.hysky.skyblocker.debug.Debug;
import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor;
+import de.hysky.skyblocker.utils.CodecUtils;
import de.hysky.skyblocker.utils.scheduler.Scheduler;
import dev.isxander.yacl3.api.YetAnotherConfigLib;
import dev.isxander.yacl3.config.v2.api.ConfigClassHandler;
-import dev.isxander.yacl3.config.v2.api.serializer.GsonConfigSerializerBuilder;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
@@ -20,9 +23,13 @@ import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.ButtonWidget;
+import net.minecraft.item.Item;
+import net.minecraft.text.Style;
import net.minecraft.text.Text;
+import net.minecraft.text.TextCodecs;
import net.minecraft.util.Identifier;
+import java.awt.Color;
import java.lang.StackWalker.Option;
import java.nio.file.Path;
import java.util.function.Consumer;
@@ -33,13 +40,17 @@ public class SkyblockerConfigManager {
public static final int CONFIG_VERSION = 4;
private static final Path CONFIG_FILE = FabricLoader.getInstance().getConfigDir().resolve("skyblocker.json");
private static final ConfigClassHandler<SkyblockerConfig> HANDLER = ConfigClassHandler.createBuilder(SkyblockerConfig.class)
- .serializer(config -> GsonConfigSerializerBuilder.create(config)
- .setPath(CONFIG_FILE)
- .setJson5(false)
- .appendGsonBuilder(builder -> builder
+ .serializer(config -> new VanillaGsonConfigSerializer<>(config, CONFIG_FILE, builder -> builder
.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
- .registerTypeHierarchyAdapter(Identifier.class, new CodecTypeAdapter<>(Identifier.CODEC)))
- .build())
+ .setPrettyPrinting()
+ .serializeNulls()
+ .registerTypeHierarchyAdapter(Color.class, new CodecTypeAdapter<>(CodecUtils.COLOR_CODEC))
+ .registerTypeHierarchyAdapter(Text.class, new CodecTypeAdapter<>(TextCodecs.CODEC))
+ .registerTypeHierarchyAdapter(Style.class, new CodecTypeAdapter<>(Style.Codecs.CODEC))
+ .registerTypeHierarchyAdapter(Identifier.class, new CodecTypeAdapter<>(Identifier.CODEC))
+ .registerTypeHierarchyAdapter(Item.class, new ItemTypeAdapter())
+ )
+ )
.build();
public static SkyblockerConfig get() {
diff --git a/src/main/java/de/hysky/skyblocker/config/CodecTypeAdapter.java b/src/main/java/de/hysky/skyblocker/config/serialization/CodecTypeAdapter.java
index 27544703..26b04017 100644
--- a/src/main/java/de/hysky/skyblocker/config/CodecTypeAdapter.java
+++ b/src/main/java/de/hysky/skyblocker/config/serialization/CodecTypeAdapter.java
@@ -1,4 +1,4 @@
-package de.hysky.skyblocker.config;
+package de.hysky.skyblocker.config.serialization;
import java.lang.reflect.Type;
diff --git a/src/main/java/de/hysky/skyblocker/config/serialization/ItemTypeAdapter.java b/src/main/java/de/hysky/skyblocker/config/serialization/ItemTypeAdapter.java
new file mode 100644
index 00000000..d854b1b5
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/config/serialization/ItemTypeAdapter.java
@@ -0,0 +1,29 @@
+package de.hysky.skyblocker.config.serialization;
+
+import java.lang.reflect.Type;
+import java.util.Locale;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+
+import net.minecraft.item.Item;
+import net.minecraft.registry.Registries;
+import net.minecraft.util.Identifier;
+
+public class ItemTypeAdapter implements JsonSerializer<Item>, JsonDeserializer<Item> {
+
+ @Override
+ public Item deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ return Registries.ITEM.get(Identifier.of(json.getAsString().toLowerCase(Locale.ENGLISH)));
+ }
+
+ @Override
+ public JsonElement serialize(Item src, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonPrimitive(Registries.ITEM.getId(src).toString());
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/config/serialization/VanillaGsonConfigSerializer.java b/src/main/java/de/hysky/skyblocker/config/serialization/VanillaGsonConfigSerializer.java
new file mode 100644
index 00000000..a9640fd3
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/config/serialization/VanillaGsonConfigSerializer.java
@@ -0,0 +1,84 @@
+package de.hysky.skyblocker.config.serialization;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.Map;
+import java.util.function.UnaryOperator;
+
+import org.slf4j.Logger;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonParser;
+import com.mojang.logging.LogUtils;
+
+import de.hysky.skyblocker.mixins.accessors.ConfigClassHandlerImplAccessor;
+import dev.isxander.yacl3.config.v2.api.ConfigClassHandler;
+import dev.isxander.yacl3.config.v2.api.ConfigField;
+import dev.isxander.yacl3.config.v2.api.ConfigSerializer;
+import dev.isxander.yacl3.config.v2.api.FieldAccess;
+
+public class VanillaGsonConfigSerializer<T> extends ConfigSerializer<T> {
+ private static final Logger LOGGER = LogUtils.getLogger();
+ private final Path path;
+ private final Gson gson;
+
+ public VanillaGsonConfigSerializer(ConfigClassHandler<T> config, Path path, UnaryOperator<GsonBuilder> builderUpdater) {
+ super(config);
+ this.path = path;
+ this.gson = builderUpdater.apply(new GsonBuilder()).create();
+ }
+
+ @Override
+ public void save() {
+ T instance = this.config.instance();
+
+ try {
+ String json = this.gson.toJson(instance);
+
+ Files.createDirectories(path.getParent());
+ Files.writeString(this.path, json, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
+ LOGGER.info("[Skyblocker Config] Successfully saved config file to {}.", this.path);
+ } catch (Exception e) {
+ LOGGER.error(LogUtils.FATAL_MARKER, "[Skyblocker Config] Failed to save the config file to: {}!!!", this.path, e);
+ }
+ }
+
+ @Override
+ public LoadResult loadSafely(Map<ConfigField<?>, FieldAccess<?>> bufferAccessMap) {
+ try {
+ if (!Files.exists(this.path)) {
+ ((ConfigClassHandlerImplAccessor) this.config).setInstance(createNewConfigInstance());
+ save();
+ return LoadResult.NO_CHANGE;
+ }
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Config] Failed to create default config file!", e);
+ }
+
+ try {
+ String config = Files.readString(this.path);
+ T instance = this.gson.fromJson(JsonParser.parseString(config), this.config.configClass());
+
+ //Set the instance
+ ((ConfigClassHandlerImplAccessor) this.config).setInstance(instance);
+ LOGGER.info("[Skyblocker Config] Successfully loaded the config file from {}.", this.path);
+ } catch (Exception e) {
+ LOGGER.error(LogUtils.FATAL_MARKER, "[Skyblocker Config] Failed to load the config!", e);
+ }
+
+ //We use no change to avoid the instance being set by YACL to a fresh one since we just did that ourselves.
+ return LoadResult.NO_CHANGE;
+ }
+
+ private T createNewConfigInstance() {
+ try {
+ return (T) this.config.configClass().getDeclaredConstructor().newInstance();
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Config] Failed to create new config instance!", e);
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/mixins/accessors/ConfigClassHandlerImplAccessor.java b/src/main/java/de/hysky/skyblocker/mixins/accessors/ConfigClassHandlerImplAccessor.java
new file mode 100644
index 00000000..cc9c2727
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/mixins/accessors/ConfigClassHandlerImplAccessor.java
@@ -0,0 +1,13 @@
+package de.hysky.skyblocker.mixins.accessors;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import dev.isxander.yacl3.config.v2.impl.ConfigClassHandlerImpl;
+
+@Mixin(ConfigClassHandlerImpl.class)
+public interface ConfigClassHandlerImplAccessor {
+
+ @Accessor
+ void setInstance(Object instance);
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/CodecUtils.java b/src/main/java/de/hysky/skyblocker/utils/CodecUtils.java
index 649f2dc4..2c01a3ec 100644
--- a/src/main/java/de/hysky/skyblocker/utils/CodecUtils.java
+++ b/src/main/java/de/hysky/skyblocker/utils/CodecUtils.java
@@ -4,10 +4,12 @@ import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import it.unimi.dsi.fastutil.objects.*;
+import java.awt.Color;
import java.util.*;
import java.util.function.Function;
public final class CodecUtils {
+ public static final Codec<Color> COLOR_CODEC = Codec.INT.xmap(argb -> new Color(argb, true), color -> color.getRGB());
private CodecUtils() {
throw new IllegalStateException("Uhhhh no? like just no. What are you trying to do? D- Do you think this will be useful to instantiate this? Like it's private, so you went through the effort of putting an accessor actually i'm not sure you can accessor a constructor. can you? so if not did you really put an access widener for that? like really? honestly this is just sad. Plus there aren't even any method in here that requires an instance. There's only static methods. like bruh. you know what i'm done typing shit for you to read, bye i'm leaving *voice lowers as I leave* I swear those modders think they can access all they want sheesh *comes back instantly* AND I SWEAR IF YOU INJECT SO THIS ERROR CANNOT BE THROWN I WILL SEND YOU TO HELL'S FREEZER");