From 7c24b0b6afc9ca22affe88f118de9d41cf7d337b Mon Sep 17 00:00:00 2001 From: Timo T Date: Sun, 8 May 2022 20:21:25 +0200 Subject: [PATCH] Fix old barbarian camps not being properly loaded after 86d5011 (#6724) * Fix old barbarian camps not being properly loaded after 86d5011 * Fix first load resulting in error --- core/src/com/unciv/json/HashMapVector2.kt | 12 ++++-- .../unciv/json/NonStringKeyMapSerializer.kt | 36 ++++++++++++++---- core/src/com/unciv/json/UncivJson.kt | 10 ++--- .../com/unciv/logic/BackwardCompatibility.kt | 37 +++++++++++++++++++ core/src/com/unciv/logic/GameInfo.kt | 2 + 5 files changed, 80 insertions(+), 17 deletions(-) diff --git a/core/src/com/unciv/json/HashMapVector2.kt b/core/src/com/unciv/json/HashMapVector2.kt index 956eddd6a4..86ea139d0f 100644 --- a/core/src/com/unciv/json/HashMapVector2.kt +++ b/core/src/com/unciv/json/HashMapVector2.kt @@ -9,15 +9,19 @@ import java.util.HashMap */ class HashMapVector2 : HashMap() { companion object { - init { + fun createSerializer(): NonStringKeyMapSerializer, Vector2> { @Suppress("UNCHECKED_CAST") // kotlin can't tell that HashMapVector2 is also a MutableMap within generics val mapClass = HashMapVector2::class.java as Class> - val serializer = NonStringKeyMapSerializer( + return NonStringKeyMapSerializer( mapClass, Vector2::class.java, - { HashMapVector2() } + { HashMapVector2() } ) - jsonSerializers.add(Pair(mapClass, serializer)) + } + + fun getSerializerClass(): Class> { + @Suppress("UNCHECKED_CAST") // kotlin can't tell that HashMapVector2 is also a MutableMap within generics + return HashMapVector2::class.java as Class> } } } diff --git a/core/src/com/unciv/json/NonStringKeyMapSerializer.kt b/core/src/com/unciv/json/NonStringKeyMapSerializer.kt index 4b6d33bd75..40de06f216 100644 --- a/core/src/com/unciv/json/NonStringKeyMapSerializer.kt +++ b/core/src/com/unciv/json/NonStringKeyMapSerializer.kt @@ -40,14 +40,36 @@ class NonStringKeyMapSerializer, KT>( override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): MT { val result = mutableMapFactory() val entries = jsonData.get("entries") - var entry = entries!!.child - while (entry != null) { - val key = json.readValue(keyClass, entry.child) - val value = json.readValue(null, entry.child.next) - result[key!!] = value!! as Any - - entry = entry.next + if (entries == null) { + readOldFormat(jsonData, json, result) + } else { + readNewFormat(entries!!, json, result) } return result } + + @Deprecated("This is only here temporarily until all users migrate the old properties to the new ones") + private fun readOldFormat(jsonData: JsonValue, json: Json, result: MT) { + val map = result as MutableMap + var child: JsonValue? = jsonData.child + while (child != null) { + if (child.name == "class") { + child = child.next + continue + } + map[child.name] = json.readValue(null, child) + child = child.next + } + } + + private fun readNewFormat(entries: JsonValue, json: Json, result: MT) { + var entry = entries.child + while (entry != null) { + val key = json.readValue(keyClass, entry.child) + val value = json.readValue(null, entry.child.next) + result[key!!] = value!! + + entry = entry.next + } + } } \ No newline at end of file diff --git a/core/src/com/unciv/json/UncivJson.kt b/core/src/com/unciv/json/UncivJson.kt index 1e6ac5a8d3..7cf5034e31 100644 --- a/core/src/com/unciv/json/UncivJson.kt +++ b/core/src/com/unciv/json/UncivJson.kt @@ -2,21 +2,19 @@ package com.unciv.json import com.badlogic.gdx.Gdx import com.badlogic.gdx.files.FileHandle +import com.badlogic.gdx.math.Vector2 import com.badlogic.gdx.utils.Json import com.badlogic.gdx.utils.Json.Serializer -internal val jsonSerializers = ArrayList, Serializer<*>>>() /** - * [Json] is not thread-safe. + * [Json] is not thread-safe. Use a new one for each parse. */ fun json() = Json().apply { setIgnoreDeprecated(true) ignoreUnknownFields = true - for ((clazz, serializer) in jsonSerializers) { - @Suppress("UNCHECKED_CAST") // we used * to accept all types, so kotlin can't know if the class & serializer parameters are actually the same - setSerializer(clazz as Class, serializer as Serializer) - } + + setSerializer(HashMapVector2.getSerializerClass(), HashMapVector2.createSerializer()) } fun Json.fromJsonFile(tClass: Class, filePath: String): T = fromJsonFile(tClass, Gdx.files.internal(filePath)) diff --git a/core/src/com/unciv/logic/BackwardCompatibility.kt b/core/src/com/unciv/logic/BackwardCompatibility.kt index 4b5fe98ead..e1dfa09af6 100644 --- a/core/src/com/unciv/logic/BackwardCompatibility.kt +++ b/core/src/com/unciv/logic/BackwardCompatibility.kt @@ -1,6 +1,9 @@ package com.unciv.logic import com.badlogic.gdx.math.Vector2 +import com.badlogic.gdx.utils.JsonValue +import com.unciv.json.HashMapVector2 +import com.unciv.json.json import com.unciv.logic.city.CityConstructions import com.unciv.logic.city.PerpetualConstruction import com.unciv.logic.civilization.CivilizationInfo @@ -159,4 +162,38 @@ object BackwardCompatibility { lastSeenImprovement.putAll(lastSeenImprovementSaved.mapKeys { Vector2().fromString(it.key) }) lastSeenImprovementSaved.clear() } + + /** + * Fixes barbarian manager camps not being correctly serialized. Previously we had a [HashMap]. We need to fix that each time an old save is loaded. + * + * When removing this, also remove [com.unciv.json.NonStringKeyMapSerializer.readOldFormat] + */ + @Suppress("DEPRECATION") + fun BarbarianManager.migrateBarbarianCamps() { + if (isOldFormat(this)) { + val newFormat = HashMapVector2() + @Suppress("UNCHECKED_CAST") // The old format is deserialized to a map + for ((key, value) in camps as MutableMap) { + val newKey = Vector2().fromString(key) + val newValue = json().readValue(Encampment::class.java, value) + newFormat[newKey] = newValue + } + + camps.clear() + camps.putAll(newFormat) + } + } + + private fun isOldFormat(manager: BarbarianManager): Boolean { + val keys = manager.camps.keys as Set + val iterator = keys.iterator() + while (iterator.hasNext()) { + val key = iterator.next() + if (key is String) { + return true + } + } + return false + } } diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index cc82da0345..b5caf3fa8e 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -3,6 +3,7 @@ package com.unciv.logic import com.unciv.Constants import com.unciv.UncivGame import com.unciv.logic.BackwardCompatibility.guaranteeUnitPromotions +import com.unciv.logic.BackwardCompatibility.migrateBarbarianCamps import com.unciv.logic.BackwardCompatibility.migrateSeenImprovements import com.unciv.logic.BackwardCompatibility.removeMissingModReferences import com.unciv.logic.automation.NextTurnAutomation @@ -398,6 +399,7 @@ class GameInfo { } // [TEMPORARY] Convert old saves to remove json workaround for (civInfo in civilizations) civInfo.migrateSeenImprovements() + barbarians.migrateBarbarianCamps() ruleSet = RulesetCache.getComplexRuleset(gameParameters)