mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-09 07:18:57 +07:00
Added support for new historyranking serialization - saved ~10% of save file size (#12079)
* Added support for new historyranking serialization - saved ~10% of save file size!
* Convert RankingType strings to chars
* Join to string using ranking char instead of generic delimiter
* Keep writing old format for now :)
* Handle negative rankings
* Fix test
* Minor perf
* Revert "Minor perf"
This reverts commit 470b3e9286
.
This commit is contained in:
@ -20,32 +20,58 @@ class CivRankingHistory : HashMap<Int, Map<RankingType, Int>>(), IsPartOfGameInf
|
||||
|
||||
fun recordRankingStats(civilization: Civilization) {
|
||||
this[civilization.gameInfo.turns] =
|
||||
RankingType.values().associateWith { civilization.getStatForRanking(it) }
|
||||
RankingType.entries.associateWith { civilization.getStatForRanking(it) }
|
||||
}
|
||||
|
||||
/** Implement Json.Serializable
|
||||
* - Output looked like this: `statsHistory:{0:{S:50,G:120,...},1:{S:55,G:80,...}}`
|
||||
* (but now we have turned off simplifed json, so it's properly quoted)
|
||||
* - New format looks like this: `statsHistory:{0:"S50G120,...",1:"S55G80,..."}`
|
||||
*/
|
||||
override fun write(json: Json) {
|
||||
for ((turn, rankings) in this) {
|
||||
|
||||
// Old format - deprecated 4.12.18
|
||||
json.writeObjectStart(turn.toString())
|
||||
for ((rankingType, score) in rankings) {
|
||||
json.writeValue(rankingType.idForSerialization, score)
|
||||
json.writeValue(rankingType.idForSerialization.toString(), score)
|
||||
}
|
||||
json.writeObjectEnd()
|
||||
|
||||
// New format (disabled)
|
||||
// val rankingsString = rankings.entries
|
||||
// .joinToString("") { it.key.idForSerialization.toString() + it.value }
|
||||
// json.writeValue(turn.toString(), rankingsString)
|
||||
}
|
||||
}
|
||||
|
||||
private val nonNumber = Regex("[^\\d-]") // Rankings can be negative, so we can't just \D :(
|
||||
override fun read(json: Json, jsonData: JsonValue) {
|
||||
for (entry in jsonData) {
|
||||
val turn = entry.name.toInt()
|
||||
val rankings = mutableMapOf<RankingType, Int>()
|
||||
for (rankingEntry in entry) {
|
||||
val rankingType = RankingType.fromIdForSerialization(rankingEntry.name)
|
||||
?: continue // Silently drop unknown ranking types.
|
||||
rankings[rankingType] = rankingEntry.asInt()
|
||||
|
||||
if (entry.isString){
|
||||
// split into key-value pairs by adding a space before every non-digit, and splitting by spaces
|
||||
val pairs = entry.asString().replace(nonNumber, " $0").split(" ")
|
||||
.filter { it.isNotEmpty() } // remove empty entries
|
||||
|
||||
for (pair in pairs) {
|
||||
val rankingType = RankingType.fromIdForSerialization(pair[0]) ?: continue
|
||||
val value = pair.substring(1).toIntOrNull() ?: continue
|
||||
rankings[rankingType] = value
|
||||
}
|
||||
// New format
|
||||
} else {
|
||||
// Old format
|
||||
for (rankingEntry in entry) {
|
||||
if (rankingEntry.name.length != 1) continue
|
||||
val rankingType = RankingType.fromIdForSerialization(rankingEntry.name[0])
|
||||
?: continue // Silently drop unknown ranking types.
|
||||
rankings[rankingType] = rankingEntry.asInt()
|
||||
}
|
||||
}
|
||||
|
||||
this[turn] = rankings
|
||||
}
|
||||
}
|
||||
|
@ -7,26 +7,26 @@ import com.unciv.ui.images.ImageGetter
|
||||
enum class RankingType(
|
||||
label: String?,
|
||||
val getImage: () -> Image?,
|
||||
val idForSerialization: String
|
||||
val idForSerialization: Char
|
||||
) {
|
||||
// production, gold, happiness, and culture already have icons added when the line is `tr()`anslated
|
||||
Score({ ImageGetter.getImage("OtherIcons/Score").apply { color = Color.FIREBRICK } }, "S"),
|
||||
Population({ ImageGetter.getStatIcon("Population") }, "N"),
|
||||
CropYield("Crop Yield", { ImageGetter.getStatIcon("Food") }, "C"),
|
||||
Production("P"),
|
||||
Gold("G"),
|
||||
Territory({ ImageGetter.getImage("OtherIcons/Hexagon") }, "T"),
|
||||
Force({ ImageGetter.getImage("OtherIcons/Shield") }, "F"),
|
||||
Happiness("H"),
|
||||
Technologies({ ImageGetter.getStatIcon("Science") }, "W"),
|
||||
Culture("A")
|
||||
Score({ ImageGetter.getImage("OtherIcons/Score").apply { color = Color.FIREBRICK } }, 'S'),
|
||||
Population({ ImageGetter.getStatIcon("Population") }, 'N'),
|
||||
CropYield("Crop Yield", { ImageGetter.getStatIcon("Food") }, 'C'),
|
||||
Production('P'),
|
||||
Gold('G'),
|
||||
Territory({ ImageGetter.getImage("OtherIcons/Hexagon") }, 'T'),
|
||||
Force({ ImageGetter.getImage("OtherIcons/Shield") }, 'F'),
|
||||
Happiness('H'),
|
||||
Technologies({ ImageGetter.getStatIcon("Science") }, 'W'),
|
||||
Culture('A'),
|
||||
;
|
||||
val label = label ?: name
|
||||
constructor(getImage: () -> Image?, idForSerialization: String) : this(null, getImage, idForSerialization)
|
||||
constructor(idForSerialization: String) : this(null, { null }, idForSerialization)
|
||||
constructor(getImage: () -> Image?, idForSerialization: Char) : this(null, getImage, idForSerialization)
|
||||
constructor(idForSerialization: Char) : this(null, { null }, idForSerialization)
|
||||
|
||||
companion object {
|
||||
fun fromIdForSerialization(s: String): RankingType? =
|
||||
values().firstOrNull { it.idForSerialization == s }
|
||||
fun fromIdForSerialization(char: Char): RankingType? =
|
||||
entries.firstOrNull { it.idForSerialization == char }
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ class RankingTypeTests {
|
||||
|
||||
@Test
|
||||
fun checkIdForSerializationUniqueness() {
|
||||
val uniqueIds = HashSet<String>()
|
||||
for (rankingType in RankingType.values()) {
|
||||
val uniqueIds = HashSet<Char>()
|
||||
for (rankingType in RankingType.entries) {
|
||||
val id = rankingType.idForSerialization
|
||||
Assert.assertTrue(
|
||||
"Id $id for RankingType $rankingType is not unique",
|
||||
|
Reference in New Issue
Block a user