From aa66334dba2c2c086b94eab1767abd4765e006f7 Mon Sep 17 00:00:00 2001 From: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com> Date: Tue, 4 Jun 2024 04:16:22 +1000 Subject: Fix: Config resetting on wrong value (#1979) Co-authored-by: Brady --- .../utils/FeatureTogglesByDefaultAdapter.kt | 89 ---------------------- .../skyhanni/utils/ItemStackTypeAdapter.kt | 29 ------- .../java/at/hannibal2/skyhanni/utils/JsonUtils.kt | 37 --------- .../at/hannibal2/skyhanni/utils/NBTTypeAdapter.kt | 24 ------ .../java/at/hannibal2/skyhanni/utils/NEUItems.kt | 4 +- .../skyhanni/utils/json/BaseGsonBuilder.kt | 37 +++++++++ .../utils/json/FeatureTogglesByDefaultAdapter.kt | 89 ++++++++++++++++++++++ .../skyhanni/utils/json/ItemStackTypeAdapter.kt | 29 +++++++ .../at/hannibal2/skyhanni/utils/json/JsonUtils.kt | 37 +++++++++ .../skyhanni/utils/json/NBTTypeAdapter.kt | 24 ++++++ .../skyhanni/utils/json/SimpleStringTypeAdapter.kt | 29 +++++++ .../utils/json/SkippingTypeAdapterFactory.kt | 32 ++++++++ .../skyhanni/utils/json/SkyHanniTypeAdapters.kt | 79 +++++++++++++++++++ 13 files changed, 359 insertions(+), 180 deletions(-) delete mode 100644 src/main/java/at/hannibal2/skyhanni/utils/FeatureTogglesByDefaultAdapter.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/utils/ItemStackTypeAdapter.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/utils/JsonUtils.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/utils/NBTTypeAdapter.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/json/BaseGsonBuilder.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/json/FeatureTogglesByDefaultAdapter.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/json/ItemStackTypeAdapter.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/json/JsonUtils.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/json/NBTTypeAdapter.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/json/SimpleStringTypeAdapter.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/json/SkippingTypeAdapterFactory.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/json/SkyHanniTypeAdapters.kt (limited to 'src/main/java/at/hannibal2/skyhanni/utils') diff --git a/src/main/java/at/hannibal2/skyhanni/utils/FeatureTogglesByDefaultAdapter.kt b/src/main/java/at/hannibal2/skyhanni/utils/FeatureTogglesByDefaultAdapter.kt deleted file mode 100644 index f78a0c4dc..000000000 --- a/src/main/java/at/hannibal2/skyhanni/utils/FeatureTogglesByDefaultAdapter.kt +++ /dev/null @@ -1,89 +0,0 @@ -package at.hannibal2.skyhanni.utils - -import at.hannibal2.skyhanni.config.FeatureToggle -import at.hannibal2.skyhanni.utils.ReflectionUtils.getDeclaredFieldOrNull -import com.google.gson.Gson -import com.google.gson.JsonObject -import com.google.gson.JsonPrimitive -import com.google.gson.TypeAdapter -import com.google.gson.TypeAdapterFactory -import com.google.gson.internal.bind.JsonTreeReader -import com.google.gson.reflect.TypeToken -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonToken -import com.google.gson.stream.JsonWriter -import java.lang.reflect.Field -import java.lang.reflect.Type -import com.google.gson.internal.`$Gson$Types` as InternalGsonTypes - -object FeatureTogglesByDefaultAdapter : TypeAdapterFactory { - fun getType(typeToken: TypeToken, field: Field): Type { - return InternalGsonTypes.resolve(typeToken.type, typeToken.rawType, field.genericType) - } - - class Adapter( - val originalWrite: TypeAdapter, - val clazz: Class, - val gson: Gson, - val type: TypeToken, - ) : TypeAdapter() { - override fun write(out: JsonWriter, value: T) { - // Delegate the original config write, since that one is unchanged - originalWrite.write(out, value) - } - - override fun read(reader: JsonReader): T { - reader.beginObject() - // Create a default initialized instance - val obj = clazz.newInstance() - - // Overwrite the default with true (or false) for feature toggles - clazz.fields.forEach { - val featureToggle = it.getAnnotation(FeatureToggle::class.java) - val adapt = gson.getAdapter(TypeToken.get(getType(type, it))) - if (featureToggle != null) - it.set(obj, adapt.read(JsonTreeReader(JsonPrimitive(featureToggle.trueIsEnabled)))) - if (adapt is Adapter) { - it.set(obj, adapt.read(JsonTreeReader(JsonObject()))) - } - } - - // Read the actual JSON Object - while (reader.peek() != JsonToken.END_OBJECT) { - // IllegalStateException: Expected NAME but was BOOLEAN - if (reader.peek() != JsonToken.NAME) { - reader.skipValue() - continue - } - val name = reader.nextName() - val field = clazz.getDeclaredFieldOrNull(name) - if (field == null) { - println("field is in config file, but not in object file: $name") - continue - } - val fieldType = gson.getAdapter(TypeToken.get(getType(type, field))) - // Read the field data - val data = fieldType.read(reader) - // Set the field or override the feature toggle with the saved data, leaving only the unset feature toggles to deviate from their defaults - field.set(obj, data) - } - - reader.endObject() - return obj - } - } - - override fun create(gson: Gson?, type: TypeToken): TypeAdapter? { - gson!! - val t = type.rawType - - // Check if this object has any feature toggles present - if (t.fields.none { - it.isAnnotationPresent(FeatureToggle::class.java) || - gson.getAdapter(TypeToken.get(getType(type, it))) is Adapter - }) return null - - val originalWrite = gson.getDelegateAdapter(this, type) - return Adapter(originalWrite, t as Class, gson, type) - } -} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ItemStackTypeAdapter.kt b/src/main/java/at/hannibal2/skyhanni/utils/ItemStackTypeAdapter.kt deleted file mode 100644 index 602bffa2d..000000000 --- a/src/main/java/at/hannibal2/skyhanni/utils/ItemStackTypeAdapter.kt +++ /dev/null @@ -1,29 +0,0 @@ -package at.hannibal2.skyhanni.utils - -import com.google.gson.Gson -import com.google.gson.TypeAdapter -import com.google.gson.TypeAdapterFactory -import com.google.gson.reflect.TypeToken -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonWriter -import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound - -object ItemStackTypeAdapterFactory : TypeAdapterFactory { - - override fun create(gson: Gson?, type: TypeToken): TypeAdapter? { - if (type.rawType == ItemStack::class.java) { - val nbtCompoundTypeAdapter = gson!!.getAdapter(NBTTagCompound::class.java) - return object : TypeAdapter() { - override fun write(out: JsonWriter, value: ItemStack) { - nbtCompoundTypeAdapter.write(out, value.serializeNBT()) - } - - override fun read(reader: JsonReader): ItemStack { - return ItemStack.loadItemStackFromNBT(nbtCompoundTypeAdapter.read(reader)) - } - } as TypeAdapter - } - return null - } -} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/JsonUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/JsonUtils.kt deleted file mode 100644 index cc3dda044..000000000 --- a/src/main/java/at/hannibal2/skyhanni/utils/JsonUtils.kt +++ /dev/null @@ -1,37 +0,0 @@ -package at.hannibal2.skyhanni.utils - -import com.google.gson.Gson -import com.google.gson.JsonArray -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import java.io.Reader -import kotlin.reflect.jvm.javaType -import kotlin.reflect.typeOf - -inline fun Gson.fromJson(string: String): T = this.fromJson(string, typeOf().javaType) - -inline fun Gson.fromJson(jsonElement: JsonElement): T = - this.fromJson(jsonElement, typeOf().javaType) - -inline fun Gson.fromJson(reader: Reader): T = this.fromJson(reader, typeOf().javaType) - -/** - * Straight forward deep copy. This is included in gson as well, but different versions have it exposed privately instead of publicly, - * so this reimplementation is here as an always public alternative. - */ -fun JsonElement.shDeepCopy(): JsonElement { - return when (this) { - is JsonObject -> JsonObject().also { - for (entry in this.entrySet()) - it.add(entry.key, entry.value.shDeepCopy()) - } - - is JsonArray -> JsonArray().also { - for (entry in this) { - it.add(entry.shDeepCopy()) - } - } - - else -> this - } -} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NBTTypeAdapter.kt b/src/main/java/at/hannibal2/skyhanni/utils/NBTTypeAdapter.kt deleted file mode 100644 index 4e0762895..000000000 --- a/src/main/java/at/hannibal2/skyhanni/utils/NBTTypeAdapter.kt +++ /dev/null @@ -1,24 +0,0 @@ -package at.hannibal2.skyhanni.utils - -import com.google.gson.TypeAdapter -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonWriter -import net.minecraft.nbt.CompressedStreamTools -import net.minecraft.nbt.NBTTagCompound -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.util.Base64 - -object NBTTypeAdapter : TypeAdapter() { - - override fun write(out: JsonWriter, value: NBTTagCompound) { - val baos = ByteArrayOutputStream() - CompressedStreamTools.writeCompressed(value, baos) - out.value(Base64.getEncoder().encode(baos.toByteArray()).decodeToString()) - } - - override fun read(reader: JsonReader): NBTTagCompound { - val bais = ByteArrayInputStream(Base64.getDecoder().decode(reader.nextString())) - return CompressedStreamTools.readCompressed(bais) - } -} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt b/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt index b3d92e68f..297a40585 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt @@ -15,6 +15,8 @@ import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NumberUtil.isInt import at.hannibal2.skyhanni.utils.PrimitiveItemStack.Companion.makePrimitiveStack +import at.hannibal2.skyhanni.utils.json.BaseGsonBuilder +import at.hannibal2.skyhanni.utils.json.fromJson import com.google.gson.JsonObject import com.google.gson.JsonPrimitive import com.google.gson.TypeAdapter @@ -51,7 +53,7 @@ object NEUItems { private val ingredientsCache = mutableMapOf>() private val hypixelApiGson by lazy { - ConfigManager.createBaseGsonBuilder() + BaseGsonBuilder.gson() .registerTypeAdapter(HypixelApiTrophyFish::class.java, object : TypeAdapter() { override fun write(out: JsonWriter, value: HypixelApiTrophyFish) {} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/json/BaseGsonBuilder.kt b/src/main/java/at/hannibal2/skyhanni/utils/json/BaseGsonBuilder.kt new file mode 100644 index 000000000..d73edc0b0 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/json/BaseGsonBuilder.kt @@ -0,0 +1,37 @@ +package at.hannibal2.skyhanni.utils.json + +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.features.fishing.trophy.TrophyRarity +import at.hannibal2.skyhanni.utils.KotlinTypeAdapterFactory +import at.hannibal2.skyhanni.utils.LorenzRarity +import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NEUInternalName +import at.hannibal2.skyhanni.utils.SimpleTimeMark +import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker +import com.google.gson.GsonBuilder +import io.github.notenoughupdates.moulconfig.observer.PropertyTypeAdapterFactory +import net.minecraft.item.ItemStack +import java.util.UUID + +object BaseGsonBuilder { + fun gson(): GsonBuilder = GsonBuilder().setPrettyPrinting() + .excludeFieldsWithoutExposeAnnotation() + .serializeSpecialFloatingPointValues() + .registerTypeAdapterFactory(PropertyTypeAdapterFactory()) + .registerTypeAdapterFactory(KotlinTypeAdapterFactory()) + .registerTypeAdapter(UUID::class.java, SkyHanniTypeAdapters.UUID.nullSafe()) + .registerTypeAdapter(LorenzVec::class.java, SkyHanniTypeAdapters.VEC_STRING.nullSafe()) + .registerTypeAdapter(TrophyRarity::class.java, SkyHanniTypeAdapters.TROPHY_RARITY.nullSafe()) + .registerTypeAdapter(ItemStack::class.java, SkyHanniTypeAdapters.NEU_ITEMSTACK.nullSafe()) + .registerTypeAdapter(NEUInternalName::class.java, SkyHanniTypeAdapters.INTERNAL_NAME.nullSafe()) + .registerTypeAdapter(LorenzRarity::class.java, SkyHanniTypeAdapters.RARITY.nullSafe()) + .registerTypeAdapter(IslandType::class.java, SkyHanniTypeAdapters.ISLAND_TYPE.nullSafe()) + .registerTypeAdapter( + SkyHanniTracker.DefaultDisplayMode::class.java, + SkyHanniTypeAdapters.TRACKER_DISPLAY_MODE.nullSafe() + ) + .registerTypeAdapter(SimpleTimeMark::class.java, SkyHanniTypeAdapters.TIME_MARK.nullSafe()) + .enableComplexMapKeySerialization() + + fun lenientGson(): GsonBuilder = gson().registerTypeAdapterFactory(SkippingTypeAdapterFactory) +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/json/FeatureTogglesByDefaultAdapter.kt b/src/main/java/at/hannibal2/skyhanni/utils/json/FeatureTogglesByDefaultAdapter.kt new file mode 100644 index 000000000..bcc9c2edf --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/json/FeatureTogglesByDefaultAdapter.kt @@ -0,0 +1,89 @@ +package at.hannibal2.skyhanni.utils.json + +import at.hannibal2.skyhanni.config.FeatureToggle +import at.hannibal2.skyhanni.utils.ReflectionUtils.getDeclaredFieldOrNull +import com.google.gson.Gson +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import com.google.gson.TypeAdapter +import com.google.gson.TypeAdapterFactory +import com.google.gson.internal.bind.JsonTreeReader +import com.google.gson.reflect.TypeToken +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonToken +import com.google.gson.stream.JsonWriter +import java.lang.reflect.Field +import java.lang.reflect.Type +import com.google.gson.internal.`$Gson$Types` as InternalGsonTypes + +object FeatureTogglesByDefaultAdapter : TypeAdapterFactory { + fun getType(typeToken: TypeToken, field: Field): Type { + return InternalGsonTypes.resolve(typeToken.type, typeToken.rawType, field.genericType) + } + + class Adapter( + val originalWrite: TypeAdapter, + val clazz: Class, + val gson: Gson, + val type: TypeToken, + ) : TypeAdapter() { + override fun write(out: JsonWriter, value: T) { + // Delegate the original config write, since that one is unchanged + originalWrite.write(out, value) + } + + override fun read(reader: JsonReader): T { + reader.beginObject() + // Create a default initialized instance + val obj = clazz.newInstance() + + // Overwrite the default with true (or false) for feature toggles + clazz.fields.forEach { + val featureToggle = it.getAnnotation(FeatureToggle::class.java) + val adapt = gson.getAdapter(TypeToken.get(getType(type, it))) + if (featureToggle != null) + it.set(obj, adapt.read(JsonTreeReader(JsonPrimitive(featureToggle.trueIsEnabled)))) + if (adapt is Adapter) { + it.set(obj, adapt.read(JsonTreeReader(JsonObject()))) + } + } + + // Read the actual JSON Object + while (reader.peek() != JsonToken.END_OBJECT) { + // IllegalStateException: Expected NAME but was BOOLEAN + if (reader.peek() != JsonToken.NAME) { + reader.skipValue() + continue + } + val name = reader.nextName() + val field = clazz.getDeclaredFieldOrNull(name) + if (field == null) { + println("field is in config file, but not in object file: $name") + continue + } + val fieldType = gson.getAdapter(TypeToken.get(getType(type, field))) + // Read the field data + val data = fieldType.read(reader) + // Set the field or override the feature toggle with the saved data, leaving only the unset feature toggles to deviate from their defaults + field.set(obj, data) + } + + reader.endObject() + return obj + } + } + + override fun create(gson: Gson?, type: TypeToken): TypeAdapter? { + gson!! + val t = type.rawType + + // Check if this object has any feature toggles present + if (t.fields.none { + it.isAnnotationPresent(FeatureToggle::class.java) || + gson.getAdapter(TypeToken.get(getType(type, it))) is Adapter + }) return null + + val originalWrite = gson.getDelegateAdapter(this, type) + return Adapter(originalWrite, t as Class, gson, type) + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/json/ItemStackTypeAdapter.kt b/src/main/java/at/hannibal2/skyhanni/utils/json/ItemStackTypeAdapter.kt new file mode 100644 index 000000000..1940b03c0 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/json/ItemStackTypeAdapter.kt @@ -0,0 +1,29 @@ +package at.hannibal2.skyhanni.utils.json + +import com.google.gson.Gson +import com.google.gson.TypeAdapter +import com.google.gson.TypeAdapterFactory +import com.google.gson.reflect.TypeToken +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound + +object ItemStackTypeAdapterFactory : TypeAdapterFactory { + + override fun create(gson: Gson?, type: TypeToken): TypeAdapter? { + if (type.rawType == ItemStack::class.java) { + val nbtCompoundTypeAdapter = gson!!.getAdapter(NBTTagCompound::class.java) + return object : TypeAdapter() { + override fun write(out: JsonWriter, value: ItemStack) { + nbtCompoundTypeAdapter.write(out, value.serializeNBT()) + } + + override fun read(reader: JsonReader): ItemStack { + return ItemStack.loadItemStackFromNBT(nbtCompoundTypeAdapter.read(reader)) + } + } as TypeAdapter + } + return null + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/json/JsonUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/json/JsonUtils.kt new file mode 100644 index 000000000..750bd96aa --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/json/JsonUtils.kt @@ -0,0 +1,37 @@ +package at.hannibal2.skyhanni.utils.json + +import com.google.gson.Gson +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import java.io.Reader +import kotlin.reflect.jvm.javaType +import kotlin.reflect.typeOf + +inline fun Gson.fromJson(string: String): T = this.fromJson(string, typeOf().javaType) + +inline fun Gson.fromJson(jsonElement: JsonElement): T = + this.fromJson(jsonElement, typeOf().javaType) + +inline fun Gson.fromJson(reader: Reader): T = this.fromJson(reader, typeOf().javaType) + +/** + * Straight forward deep copy. This is included in gson as well, but different versions have it exposed privately instead of publicly, + * so this reimplementation is here as an always public alternative. + */ +fun JsonElement.shDeepCopy(): JsonElement { + return when (this) { + is JsonObject -> JsonObject().also { + for (entry in this.entrySet()) + it.add(entry.key, entry.value.shDeepCopy()) + } + + is JsonArray -> JsonArray().also { + for (entry in this) { + it.add(entry.shDeepCopy()) + } + } + + else -> this + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/json/NBTTypeAdapter.kt b/src/main/java/at/hannibal2/skyhanni/utils/json/NBTTypeAdapter.kt new file mode 100644 index 000000000..e2e782636 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/json/NBTTypeAdapter.kt @@ -0,0 +1,24 @@ +package at.hannibal2.skyhanni.utils.json + +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import net.minecraft.nbt.CompressedStreamTools +import net.minecraft.nbt.NBTTagCompound +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.util.Base64 + +object NBTTypeAdapter : TypeAdapter() { + + override fun write(out: JsonWriter, value: NBTTagCompound) { + val baos = ByteArrayOutputStream() + CompressedStreamTools.writeCompressed(value, baos) + out.value(Base64.getEncoder().encode(baos.toByteArray()).decodeToString()) + } + + override fun read(reader: JsonReader): NBTTagCompound { + val bais = ByteArrayInputStream(Base64.getDecoder().decode(reader.nextString())) + return CompressedStreamTools.readCompressed(bais) + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/json/SimpleStringTypeAdapter.kt b/src/main/java/at/hannibal2/skyhanni/utils/json/SimpleStringTypeAdapter.kt new file mode 100644 index 000000000..21f601932 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/json/SimpleStringTypeAdapter.kt @@ -0,0 +1,29 @@ +package at.hannibal2.skyhanni.utils.json + +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter + +class SimpleStringTypeAdapter( + val serializer: T.() -> String, + val deserializer: String.() -> T +) : TypeAdapter() { + + override fun write(writer: JsonWriter, value: T) { + writer.value(serializer(value)) + } + + override fun read(reader: JsonReader): T { + return deserializer(reader.nextString()) + } + + companion object { + + inline fun > forEnum(): SimpleStringTypeAdapter { + return SimpleStringTypeAdapter( + { name }, + { enumValueOf(this.replace(" ", "_").uppercase()) } + ) + } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/json/SkippingTypeAdapterFactory.kt b/src/main/java/at/hannibal2/skyhanni/utils/json/SkippingTypeAdapterFactory.kt new file mode 100644 index 000000000..a926976ca --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/json/SkippingTypeAdapterFactory.kt @@ -0,0 +1,32 @@ +package at.hannibal2.skyhanni.utils.json + +import at.hannibal2.skyhanni.SkyHanniMod +import com.google.gson.Gson +import com.google.gson.TypeAdapter +import com.google.gson.TypeAdapterFactory +import com.google.gson.reflect.TypeToken +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter + +object SkippingTypeAdapterFactory : TypeAdapterFactory { + + override fun create(gson: Gson, type: TypeToken): TypeAdapter { + return SafeTypeAdapter(gson.getDelegateAdapter(this, type)) + } + + private class SafeTypeAdapter(val parent: TypeAdapter) : TypeAdapter() { + override fun write(writer: JsonWriter, value: T) { + parent.write(writer, value) + } + + override fun read(reader: JsonReader): T? { + return try { + parent.read(reader) + } catch (e: Exception) { + SkyHanniMod.logger.warn("Failed to read value from JSON, skipping", e) + reader.skipValue() + null + } + } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/json/SkyHanniTypeAdapters.kt b/src/main/java/at/hannibal2/skyhanni/utils/json/SkyHanniTypeAdapters.kt new file mode 100644 index 000000000..dc50eef74 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/json/SkyHanniTypeAdapters.kt @@ -0,0 +1,79 @@ +package at.hannibal2.skyhanni.utils.json + +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.features.fishing.trophy.TrophyRarity +import at.hannibal2.skyhanni.features.garden.CropType +import at.hannibal2.skyhanni.features.garden.pests.PestType +import at.hannibal2.skyhanni.utils.LorenzRarity +import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NEUInternalName +import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName +import at.hannibal2.skyhanni.utils.NEUItems +import at.hannibal2.skyhanni.utils.SimpleTimeMark +import at.hannibal2.skyhanni.utils.SimpleTimeMark.Companion.asTimeMark +import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker +import com.google.gson.GsonBuilder +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import net.minecraft.item.ItemStack +import java.util.UUID + +object SkyHanniTypeAdapters { + val NEU_ITEMSTACK: TypeAdapter = SimpleStringTypeAdapter(NEUItems::saveNBTData, NEUItems::loadNBTData) + + val UUID: TypeAdapter = SimpleStringTypeAdapter( + { this.toString() }, + { java.util.UUID.fromString(this) } + ) + + val INTERNAL_NAME: TypeAdapter = SimpleStringTypeAdapter( + { this.asString() }, + { this.asInternalName() } + ) + + val VEC_STRING: TypeAdapter = SimpleStringTypeAdapter( + { "$x:$y:$z" }, + { LorenzVec.decodeFromString(this) } + ) + + val TROPHY_RARITY: TypeAdapter = SimpleStringTypeAdapter( + { name }, + { TrophyRarity.getByName(this) ?: error("Could not parse TrophyRarity from '$this'") } + ) + + val TIME_MARK: TypeAdapter = object : TypeAdapter() { + override fun write(out: JsonWriter, value: SimpleTimeMark) { + out.value(value.toMillis()) + } + + override fun read(reader: JsonReader): SimpleTimeMark { + return reader.nextString().toLong().asTimeMark() + } + } + + val CROP_TYPE: TypeAdapter = SimpleStringTypeAdapter( + { name }, + { CropType.getByName(this) } + ) + + val PEST_TYPE: TypeAdapter = SimpleStringTypeAdapter( + { name }, + { PestType.getByName(this) } + ) + + val TRACKER_DISPLAY_MODE = SimpleStringTypeAdapter.forEnum() + val ISLAND_TYPE = SimpleStringTypeAdapter.forEnum() + val RARITY = SimpleStringTypeAdapter.forEnum() + + inline fun GsonBuilder.registerTypeAdapter( + crossinline write: (JsonWriter, T) -> Unit, + crossinline read: (JsonReader) -> T, + ): GsonBuilder { + this.registerTypeAdapter(T::class.java, object : TypeAdapter() { + override fun write(out: JsonWriter, value: T) = write(out, value) + override fun read(reader: JsonReader) = read(reader) + }.nullSafe()) + return this + } +} -- cgit