In-depth serialization improvement, fixes Barbarian Camps revealed by Honor not showing immediately in multiplayer

* Fix Barbarian Camp Spawned notification not revealing the camp on the map in multiplayer

* Fix lastSeenImprovement not being cloned

* Use HashMapVector2 in BarbarianManager

* Fix value not having its class written out for proper deserializing

* Refactor: various code improvements
This commit is contained in:
Timo T 2022-05-08 12:35:41 +02:00 committed by GitHub
parent 569b51cb27
commit 86d5011da1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 206 additions and 94 deletions

View File

@ -6,10 +6,10 @@ import android.net.Uri
import android.os.Build
import androidx.annotation.GuardedBy
import androidx.annotation.RequiresApi
import com.unciv.json.json
import com.unciv.logic.CustomSaveLocationHelper
import com.unciv.logic.GameInfo
import com.unciv.logic.GameSaver
import com.unciv.logic.GameSaver.json
// The Storage Access Framework is available from API 19 and up:
// https://developer.android.com/guide/topics/providers/document-provider

View File

@ -1,22 +0,0 @@
package com.unciv
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.utils.Json
import com.unciv.logic.UncivShowableException
class JsonParser {
private val json = Json().apply { ignoreUnknownFields = true }
fun <T> getFromJson(tClass: Class<T>, filePath: String): T = getFromJson(tClass, Gdx.files.internal(filePath))
fun <T> getFromJson(tClass: Class<T>, file: FileHandle): T {
try {
val jsonText = file.readString(Charsets.UTF_8.name())
return json.fromJson(tClass, jsonText)
} catch (exception:Exception){
throw Exception("Could not parse json of file ${file.name()}", exception)
}
}
}

View File

@ -0,0 +1,24 @@
package com.unciv.json
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.utils.Json
import java.util.HashMap
/**
* @see NonStringKeyMapSerializer
*/
class HashMapVector2<T> : HashMap<Vector2, T>() {
companion object {
init {
@Suppress("UNCHECKED_CAST") // kotlin can't tell that HashMapVector2 is also a MutableMap within generics
val mapClass = HashMapVector2::class.java as Class<MutableMap<Vector2, Any>>
val serializer = NonStringKeyMapSerializer(
mapClass,
Vector2::class.java,
{ HashMapVector2<Any>() }
)
jsonSerializers.add(Pair(mapClass, serializer))
}
}
}

View File

@ -0,0 +1,53 @@
package com.unciv.json
import com.badlogic.gdx.utils.Json
import com.badlogic.gdx.utils.Json.Serializer
import com.badlogic.gdx.utils.JsonValue
/**
* A [Serializer] for gdx's [Json] that serializes a map that does not have [String] as its key class.
*
* Exists solely because [Json] always serializes any map by converting its key to [String], so when you load it again,
* all your keys are [String], messing up value retrieval.
*
* To work around that, we have to use a custom serializer. A custom serializer in Json is only added for a specific class
* and only checks for direct equality, and since we can't just do `HashMap<Any, *>::class.java`, only `HashMap::class.java`,
* we have to create a completely new class and use that class as [mapClass] here.
*
* @param MT Must be a type that extends [MutableMap]
* @param KT Must be the key type of [MT]
*/
class NonStringKeyMapSerializer<MT: MutableMap<KT, Any>, KT>(
private val mapClass: Class<MT>,
private val keyClass: Class<KT>,
private val mutableMapFactory: () -> MT
) : Serializer<MT> {
override fun write(json: Json, toWrite: MT, knownType: Class<*>) {
json.writeObjectStart()
json.writeType(mapClass)
json.writeArrayStart("entries")
for ((key, value) in toWrite) {
json.writeArrayStart()
json.writeValue(key)
json.writeValue(value, null)
json.writeArrayEnd()
}
json.writeArrayEnd()
json.writeObjectEnd()
}
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<Any>(null, entry.child.next)
result[key!!] = value!! as Any
entry = entry.next
}
return result
}
}

View File

@ -0,0 +1,31 @@
package com.unciv.json
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.utils.Json
import com.badlogic.gdx.utils.Json.Serializer
internal val jsonSerializers = ArrayList<Pair<Class<*>, Serializer<*>>>()
/**
* [Json] is not thread-safe.
*/
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<Any>, serializer as Serializer<Any>)
}
}
fun <T> Json.fromJsonFile(tClass: Class<T>, filePath: String): T = fromJsonFile(tClass, Gdx.files.internal(filePath))
fun <T> Json.fromJsonFile(tClass: Class<T>, file: FileHandle): T {
try {
val jsonText = file.readString(Charsets.UTF_8.name())
return fromJson(tClass, jsonText)
} catch (exception:Exception){
throw Exception("Could not parse json of file ${file.name()}", exception)
}
}

View File

@ -1,7 +1,9 @@
package com.unciv.logic
import com.badlogic.gdx.math.Vector2
import com.unciv.logic.city.CityConstructions
import com.unciv.logic.city.PerpetualConstruction
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.TechManager
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
import com.unciv.logic.civilization.diplomacy.DiplomacyManager
@ -147,4 +149,14 @@ object BackwardCompatibility {
maxXPfromBarbarians = 30
}
}
/** Removes the workaround previously used for storing a map that does not have a [String] key
* @see com.unciv.json.NonStringKeyMapSerializer
*/
@Suppress("DEPRECATION")
fun CivilizationInfo.migrateSeenImprovements() {
if (lastSeenImprovementSaved.isEmpty()) return;
lastSeenImprovement.putAll(lastSeenImprovementSaved.mapKeys { Vector2().fromString(it.key) })
lastSeenImprovementSaved.clear()
}
}

View File

@ -2,6 +2,8 @@ package com.unciv.logic
import com.badlogic.gdx.math.Vector2
import com.unciv.Constants
import com.unciv.json.HashMapVector2
import com.unciv.json.json
import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.map.TileInfo
import com.unciv.logic.map.TileMap
@ -15,7 +17,7 @@ import kotlin.math.min
import kotlin.math.pow
class BarbarianManager {
val camps = HashMap<Vector2, Encampment>()
val camps = HashMapVector2<Encampment>()
@Transient
lateinit var gameInfo: GameInfo

View File

@ -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.migrateSeenImprovements
import com.unciv.logic.BackwardCompatibility.removeMissingModReferences
import com.unciv.logic.automation.NextTurnAutomation
import com.unciv.logic.civilization.*
@ -395,6 +396,8 @@ class GameInfo {
gameParameters.baseRuleset = baseRulesetInMods
gameParameters.mods = LinkedHashSet(gameParameters.mods.filter { it != baseRulesetInMods })
}
// [TEMPORARY] Convert old saves to remove json workaround
for (civInfo in civilizations) civInfo.migrateSeenImprovements()
ruleSet = RulesetCache.getComplexRuleset(gameParameters)

View File

@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.utils.Json
import com.unciv.UncivGame
import com.unciv.json.json
import com.unciv.models.metadata.GameSettings
import com.unciv.ui.crashhandling.crashHandlingThread
import com.unciv.ui.crashhandling.postCrashHandlingRunnable
@ -21,8 +22,6 @@ object GameSaver {
* See https://developer.android.com/training/data-storage/app-specific#external-access-files */
var externalFilesDirForAndroid = ""
fun json() = Json().apply { setIgnoreDeprecated(true); ignoreUnknownFields = true } // Json() is NOT THREAD SAFE so we need to create a new one for each function
fun getSubfolder(multiplayer: Boolean = false) = if (multiplayer) multiplayerFilesFolder else saveFilesFolder
fun getSave(GameName: String, multiplayer: Boolean = false): FileHandle {

View File

@ -2,13 +2,12 @@ package com.unciv.logic
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.unciv.json.json
import com.unciv.logic.map.TileMap
import com.unciv.ui.saves.Gzip
object MapSaver {
fun json() = GameSaver.json()
const val mapsFolder = "maps"
var saveZipped = true

View File

@ -1,8 +1,15 @@
package com.unciv.logic.civilization
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.utils.Json
import com.badlogic.gdx.utils.Json.Serializer
import com.badlogic.gdx.utils.JsonValue
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.json.HashMapVector2
import com.unciv.json.json
import com.unciv.logic.BarbarianManager
import com.unciv.logic.Encampment
import com.unciv.logic.GameInfo
import com.unciv.logic.UncivShowableException
import com.unciv.logic.automation.NextTurnAutomation
@ -32,9 +39,6 @@ import com.unciv.ui.utils.toPercent
import com.unciv.ui.utils.withItem
import com.unciv.ui.victoryscreen.RankingType
import java.util.*
import kotlin.NoSuchElementException
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
@ -166,13 +170,11 @@ class CivilizationInfo {
var citiesCreated = 0
var exploredTiles = HashSet<Vector2>()
// This double construction because for some reason the game wants to load a
// map<Vector2, String> as a map<String, String> causing all sorts of type problems.
// So we let the game have its map<String, String> and remap it in setTransients,
// everyone's happy. Sort of.
@Deprecated("Only for backward compatibility, will have no values after GameInfo.setTransients",
ReplaceWith("lastSeenImprovement"))
var lastSeenImprovementSaved = HashMap<String, String>()
@Transient
var lastSeenImprovement = HashMap<Vector2, String>()
var lastSeenImprovement = HashMapVector2<String>()
// To correctly determine "game over" condition as clarified in #4707
// Nullable type meant to be deprecated and converted to non-nullable,
@ -251,7 +253,7 @@ class CivilizationInfo {
// Cloning it by-pointer is a horrific move, since the serialization would go over it ANYWAY and still lead to concurrency problems.
// Cloning it by iterating on the tilemap values may seem ridiculous, but it's a perfectly thread-safe way to go about it, unlike the other solutions.
toReturn.exploredTiles.addAll(gameInfo.tileMap.values.asSequence().map { it.position }.filter { it in exploredTiles })
toReturn.lastSeenImprovementSaved.putAll(lastSeenImprovement.mapKeys { it.key.toString() })
toReturn.lastSeenImprovement.putAll(lastSeenImprovement)
toReturn.notifications.addAll(notifications)
toReturn.citiesCreated = citiesCreated
toReturn.popupAlerts.addAll(popupAlerts)
@ -805,8 +807,6 @@ class CivilizationInfo {
}
hasLongCountDisplayUnique = hasUnique(UniqueType.MayanCalendarDisplay)
lastSeenImprovement.putAll(lastSeenImprovementSaved.mapKeys { Vector2().fromString(it.key) })
}
fun updateSightAndResources() {

View File

@ -1,6 +1,6 @@
package com.unciv.logic.multiplayer
import com.unciv.logic.GameSaver
import com.unciv.json.json
import com.unciv.ui.utils.UncivDateFormat.parseDate
import java.io.*
import java.net.HttpURLConnection
@ -63,12 +63,12 @@ object DropBox {
// instead of the path.
val response = dropboxApi("https://api.dropboxapi.com/2/files/list_folder",
"{\"path\":\"$folder\"}", "application/json")
var currentFolderListChunk = GameSaver.json().fromJson(FolderList::class.java, response)
var currentFolderListChunk = json().fromJson(FolderList::class.java, response)
folderList.addAll(currentFolderListChunk.entries)
while (currentFolderListChunk.has_more) {
val continuationResponse = dropboxApi("https://api.dropboxapi.com/2/files/list_folder/continue",
"{\"cursor\":\"${currentFolderListChunk.cursor}\"}", "application/json")
currentFolderListChunk = GameSaver.json().fromJson(FolderList::class.java, continuationResponse)
currentFolderListChunk = json().fromJson(FolderList::class.java, continuationResponse)
folderList.addAll(currentFolderListChunk.entries)
}
return folderList
@ -115,7 +115,7 @@ object DropBox {
val stream = dropboxApi("https://api.dropboxapi.com/2/files/get_metadata",
"{\"path\":\"$fileName\"}", "application/json")!!
val reader = BufferedReader(InputStreamReader(stream))
return GameSaver.json().fromJson(DropboxMetaData::class.java, reader.readText())
return json().fromJson(DropboxMetaData::class.java, reader.readText())
}
//

View File

@ -3,6 +3,7 @@ package com.unciv.logic.multiplayer
import com.badlogic.gdx.Net
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.json.json
import com.unciv.logic.GameInfo
import com.unciv.logic.GameInfoPreview
import com.unciv.logic.GameSaver
@ -86,7 +87,7 @@ class OnlineMultiplayer(var fileStorageIdentifier: String? = null) {
tryUploadGamePreview(gameInfo.asPreview())
}
val zippedGameInfo = Gzip.zip(GameSaver.json().toJson(gameInfo))
val zippedGameInfo = Gzip.zip(json().toJson(gameInfo))
fileStorage.saveFileData(gameInfo.gameId, zippedGameInfo)
}
@ -97,7 +98,7 @@ class OnlineMultiplayer(var fileStorageIdentifier: String? = null) {
* @see GameInfo.asPreview
*/
fun tryUploadGamePreview(gameInfo: GameInfoPreview) {
val zippedGameInfo = Gzip.zip(GameSaver.json().toJson(gameInfo))
val zippedGameInfo = Gzip.zip(json().toJson(gameInfo))
fileStorage.saveFileData("${gameInfo.gameId}_Preview", zippedGameInfo)
}

View File

@ -1,5 +1,6 @@
package com.unciv.logic.multiplayer
import com.unciv.json.json
import com.unciv.logic.GameInfo
import com.unciv.logic.GameInfoPreview
import com.unciv.logic.GameSaver
@ -67,7 +68,7 @@ class ServerMutex(val gameInfo: GameInfoPreview) {
}
try {
OnlineMultiplayer().fileStorage.saveFileData(fileName, Gzip.zip(GameSaver.json().toJson(LockFile())))
OnlineMultiplayer().fileStorage.saveFileData(fileName, Gzip.zip(json().toJson(LockFile())))
} catch (ex: FileStorageConflictException) {
return locked
}

View File

@ -3,8 +3,9 @@ package com.unciv.models.metadata
import com.badlogic.gdx.Application
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.unciv.JsonParser
import com.unciv.Constants
import com.unciv.json.fromJsonFile
import com.unciv.json.json
import com.unciv.logic.GameSaver
import com.unciv.ui.utils.Fonts
import java.io.File
@ -124,7 +125,7 @@ class GameSettings {
// In fact, at this point Gdx.app or Gdx.files are null but this still works.
val file = FileHandle(base + File.separator + GameSaver.settingsFileName)
return if (file.exists())
JsonParser().getFromJson(
json().fromJsonFile(
GameSettings::class.java,
file
)

View File

@ -4,7 +4,8 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.Color
import com.unciv.Constants
import com.unciv.JsonParser
import com.unciv.json.fromJsonFile
import com.unciv.json.json
import com.unciv.logic.BackwardCompatibility.updateDeprecations
import com.unciv.logic.UncivShowableException
import com.unciv.logic.map.MapParameters
@ -72,7 +73,6 @@ class ModOptions : IHasUniques {
class Ruleset {
private val jsonParser = JsonParser()
var folderLocation:FileHandle?=null
var name = ""
@ -197,7 +197,7 @@ class Ruleset {
val modOptionsFile = folderHandle.child("ModOptions.json")
if (modOptionsFile.exists()) {
try {
modOptions = jsonParser.getFromJson(ModOptions::class.java, modOptionsFile)
modOptions = json().fromJsonFile(ModOptions::class.java, modOptionsFile)
modOptions.updateDeprecations()
} catch (ex: Exception) {}
modOptions.uniqueObjects = modOptions.uniques.map { Unique(it, UniqueTarget.ModOptions) }
@ -206,7 +206,7 @@ class Ruleset {
val techFile = folderHandle.child("Techs.json")
if (techFile.exists()) {
val techColumns = jsonParser.getFromJson(Array<TechColumn>::class.java, techFile)
val techColumns = json().fromJsonFile(Array<TechColumn>::class.java, techFile)
for (techColumn in techColumns) {
for (tech in techColumn.techs) {
if (tech.cost == 0) tech.cost = techColumn.techCost
@ -217,49 +217,49 @@ class Ruleset {
}
val buildingsFile = folderHandle.child("Buildings.json")
if (buildingsFile.exists()) buildings += createHashmap(jsonParser.getFromJson(Array<Building>::class.java, buildingsFile))
if (buildingsFile.exists()) buildings += createHashmap(json().fromJsonFile(Array<Building>::class.java, buildingsFile))
for(building in buildings.values)
if(building.requiredBuildingInAllCities != null)
building.uniques.add(UniqueType.RequiresBuildingInAllCities.text.fillPlaceholders(building.requiredBuildingInAllCities!!))
val terrainsFile = folderHandle.child("Terrains.json")
if (terrainsFile.exists()) {
terrains += createHashmap(jsonParser.getFromJson(Array<Terrain>::class.java, terrainsFile))
terrains += createHashmap(json().fromJsonFile(Array<Terrain>::class.java, terrainsFile))
for (terrain in terrains.values) terrain.setTransients()
}
val resourcesFile = folderHandle.child("TileResources.json")
if (resourcesFile.exists()) tileResources += createHashmap(jsonParser.getFromJson(Array<TileResource>::class.java, resourcesFile))
if (resourcesFile.exists()) tileResources += createHashmap(json().fromJsonFile(Array<TileResource>::class.java, resourcesFile))
val improvementsFile = folderHandle.child("TileImprovements.json")
if (improvementsFile.exists()) tileImprovements += createHashmap(jsonParser.getFromJson(Array<TileImprovement>::class.java, improvementsFile))
if (improvementsFile.exists()) tileImprovements += createHashmap(json().fromJsonFile(Array<TileImprovement>::class.java, improvementsFile))
val erasFile = folderHandle.child("Eras.json")
if (erasFile.exists()) eras += createHashmap(jsonParser.getFromJson(Array<Era>::class.java, erasFile))
if (erasFile.exists()) eras += createHashmap(json().fromJsonFile(Array<Era>::class.java, erasFile))
// While `eras.values.toList()` might seem more logical, eras.values is a MutableCollection and
// therefore does not guarantee keeping the order of elements like a LinkedHashMap does.
// Using map{} sidesteps this problem
eras.map { it.value }.withIndex().forEach { it.value.eraNumber = it.index }
val unitTypesFile = folderHandle.child("UnitTypes.json")
if (unitTypesFile.exists()) unitTypes += createHashmap(jsonParser.getFromJson(Array<UnitType>::class.java, unitTypesFile))
if (unitTypesFile.exists()) unitTypes += createHashmap(json().fromJsonFile(Array<UnitType>::class.java, unitTypesFile))
val unitsFile = folderHandle.child("Units.json")
if (unitsFile.exists()) units += createHashmap(jsonParser.getFromJson(Array<BaseUnit>::class.java, unitsFile))
if (unitsFile.exists()) units += createHashmap(json().fromJsonFile(Array<BaseUnit>::class.java, unitsFile))
val promotionsFile = folderHandle.child("UnitPromotions.json")
if (promotionsFile.exists()) unitPromotions += createHashmap(jsonParser.getFromJson(Array<Promotion>::class.java, promotionsFile))
if (promotionsFile.exists()) unitPromotions += createHashmap(json().fromJsonFile(Array<Promotion>::class.java, promotionsFile))
val questsFile = folderHandle.child("Quests.json")
if (questsFile.exists()) quests += createHashmap(jsonParser.getFromJson(Array<Quest>::class.java, questsFile))
if (questsFile.exists()) quests += createHashmap(json().fromJsonFile(Array<Quest>::class.java, questsFile))
val specialistsFile = folderHandle.child("Specialists.json")
if (specialistsFile.exists()) specialists += createHashmap(jsonParser.getFromJson(Array<Specialist>::class.java, specialistsFile))
if (specialistsFile.exists()) specialists += createHashmap(json().fromJsonFile(Array<Specialist>::class.java, specialistsFile))
val policiesFile = folderHandle.child("Policies.json")
if (policiesFile.exists()) {
policyBranches += createHashmap(
jsonParser.getFromJson(Array<PolicyBranch>::class.java, policiesFile)
json().fromJsonFile(Array<PolicyBranch>::class.java, policiesFile)
)
for (branch in policyBranches.values) {
// Setup this branch
@ -289,34 +289,34 @@ class Ruleset {
val beliefsFile = folderHandle.child("Beliefs.json")
if (beliefsFile.exists())
beliefs += createHashmap(jsonParser.getFromJson(Array<Belief>::class.java, beliefsFile))
beliefs += createHashmap(json().fromJsonFile(Array<Belief>::class.java, beliefsFile))
val religionsFile = folderHandle.child("Religions.json")
if (religionsFile.exists())
religions += jsonParser.getFromJson(Array<String>::class.java, religionsFile).toList()
religions += json().fromJsonFile(Array<String>::class.java, religionsFile).toList()
val ruinRewardsFile = folderHandle.child("Ruins.json")
if (ruinRewardsFile.exists())
ruinRewards += createHashmap(jsonParser.getFromJson(Array<RuinReward>::class.java, ruinRewardsFile))
ruinRewards += createHashmap(json().fromJsonFile(Array<RuinReward>::class.java, ruinRewardsFile))
val nationsFile = folderHandle.child("Nations.json")
if (nationsFile.exists()) {
nations += createHashmap(jsonParser.getFromJson(Array<Nation>::class.java, nationsFile))
nations += createHashmap(json().fromJsonFile(Array<Nation>::class.java, nationsFile))
for (nation in nations.values) nation.setTransients()
}
val difficultiesFile = folderHandle.child("Difficulties.json")
if (difficultiesFile.exists())
difficulties += createHashmap(jsonParser.getFromJson(Array<Difficulty>::class.java, difficultiesFile))
difficulties += createHashmap(json().fromJsonFile(Array<Difficulty>::class.java, difficultiesFile))
val globalUniquesFile = folderHandle.child("GlobalUniques.json")
if (globalUniquesFile.exists()) {
globalUniques = jsonParser.getFromJson(GlobalUniques::class.java, globalUniquesFile)
globalUniques = json().fromJsonFile(GlobalUniques::class.java, globalUniquesFile)
}
val victoryTypesFiles = folderHandle.child("VictoryTypes.json")
if (victoryTypesFiles.exists()) {
victories += createHashmap(jsonParser.getFromJson(Array<Victory>::class.java, victoryTypesFiles))
victories += createHashmap(json().fromJsonFile(Array<Victory>::class.java, victoryTypesFiles))
}

View File

@ -2,8 +2,9 @@ package com.unciv.models.tilesets
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.unciv.JsonParser
import com.unciv.UncivGame
import com.unciv.json.fromJsonFile
import com.unciv.json.json
import com.unciv.models.ruleset.RulesetCache
import com.unciv.ui.images.ImageGetter
@ -49,7 +50,7 @@ object TileSetCache : HashMap<String, TileSetConfig>() {
try {
val key = TileSetAndMod(tileSetName, "")
assert(key !in allConfigs)
allConfigs[key] = JsonParser().getFromJson(TileSetConfig::class.java, configFile)
allConfigs[key] = json().fromJsonFile(TileSetConfig::class.java, configFile)
if (printOutput) {
println("TileSetConfig loaded successfully: ${configFile.name()}")
println()
@ -78,7 +79,7 @@ object TileSetCache : HashMap<String, TileSetConfig>() {
tileSetName = configFile.nameWithoutExtension().removeSuffix("Config")
val key = TileSetAndMod(tileSetName, modName)
assert(key !in allConfigs)
allConfigs[key] = JsonParser().getFromJson(TileSetConfig::class.java, configFile)
allConfigs[key] = json().fromJsonFile(TileSetConfig::class.java, configFile)
if (printOutput) {
println("TileSetConfig loaded successfully: ${configFile.path()}")
println()

View File

@ -3,7 +3,8 @@ package com.unciv.models.translations
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.utils.Array
import com.unciv.JsonParser
import com.unciv.json.fromJsonFile
import com.unciv.json.json
import com.unciv.models.metadata.BaseRuleset
import com.unciv.models.metadata.LocaleCode
import com.unciv.models.ruleset.*
@ -219,7 +220,7 @@ object TranslationFileWriter {
private fun generateTutorialsStrings(): MutableSet<String> {
val tutorialsStrings = mutableSetOf<String>()
val tutorials = JsonParser().getFromJson(LinkedHashMap<String, Array<String>>().javaClass, "jsons/Tutorials.json")
val tutorials = json().fromJsonFile(LinkedHashMap<String, Array<String>>().javaClass, "jsons/Tutorials.json")
var uniqueIndexOfNewLine = 0
for (tutorial in tutorials) {
@ -273,7 +274,6 @@ object TranslationFileWriter {
val startMillis = System.currentTimeMillis()
var uniqueIndexOfNewLine = 0
val jsonParser = JsonParser()
val listOfJSONFiles = jsonsFolder
.list { file -> file.name.endsWith(".json", true) }
.sortedBy { it.name() } // generatedStrings maintains order, so let's feed it a predictable one
@ -290,7 +290,7 @@ object TranslationFileWriter {
if (javaClass == this.javaClass)
continue // unknown JSON, let's skip it
val array = jsonParser.getFromJson(javaClass, jsonFile.path())
val array = json().fromJsonFile(javaClass, jsonFile.path())
resultStrings = mutableSetOf()
this[filename] = resultStrings

View File

@ -9,6 +9,7 @@ import com.badlogic.gdx.utils.Align
import com.badlogic.gdx.utils.Json
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.json.json
import com.unciv.models.ruleset.RulesetCache
import com.unciv.ui.images.IconTextButton
import com.unciv.ui.images.ImageGetter
@ -55,7 +56,7 @@ class CrashScreen(val exception: Throwable): BaseScreen() {
private fun tryGetSaveGame()
= try {
UncivGame.Current.gameInfo.let { gameInfo ->
Json().toJson(gameInfo).let {
json().toJson(gameInfo).let {
jsonString -> Gzip.zip(jsonString)
}
} // Taken from old CrashController().buildReport().

View File

@ -18,6 +18,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable
import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.json.json
import com.unciv.logic.GameSaver
import com.unciv.models.ruleset.Nation
import com.unciv.models.ruleset.Ruleset
@ -79,7 +80,7 @@ object ImageGetter {
fun loadModAtlases(mod: String, folder: FileHandle) {
// See #4993 - you can't .list() on a jar file, so the ImagePacker leaves us the list of actual atlases.
val controlFile = folder.child("Atlases.json")
val fileNames = (if (controlFile.exists()) GameSaver.json().fromJson(Array<String>::class.java, controlFile)
val fileNames = (if (controlFile.exists()) json().fromJson(Array<String>::class.java, controlFile)
else emptyArray()).toMutableList()
if (mod.isNotEmpty()) fileNames += "game"
for (fileName in fileNames) {

View File

@ -3,9 +3,9 @@ package com.unciv.ui.pickerscreens
import com.badlogic.gdx.Files
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.utils.Json
import com.unciv.JsonParser
import com.unciv.json.fromJsonFile
import com.unciv.json.json
import com.unciv.logic.BackwardCompatibility.updateDeprecations
import com.unciv.logic.GameSaver
import com.unciv.models.ruleset.ModOptions
import java.io.*
import java.net.HttpURLConnection
@ -234,7 +234,7 @@ object Github {
retries++ // An extra retry so the 403 is ignored in the retry count
}
} ?: continue
return GameSaver.json().fromJson(RepoSearch::class.java, inputStream.bufferedReader().readText())
return json().fromJson(RepoSearch::class.java, inputStream.bufferedReader().readText())
}
return null
}
@ -315,13 +315,13 @@ object Github {
*/
fun rewriteModOptions(repo: Repo, modFolder: FileHandle) {
val modOptionsFile = modFolder.child("jsons/ModOptions.json")
val modOptions = if (modOptionsFile.exists()) JsonParser().getFromJson(ModOptions::class.java, modOptionsFile) else ModOptions()
val modOptions = if (modOptionsFile.exists()) json().fromJsonFile(ModOptions::class.java, modOptionsFile) else ModOptions()
modOptions.modUrl = repo.html_url
modOptions.lastUpdated = repo.pushed_at
modOptions.author = repo.owner.login
modOptions.modSize = repo.size
modOptions.updateDeprecations()
Json().toJson(modOptions, modOptionsFile)
json().toJson(modOptions, modOptionsFile)
}
}

View File

@ -6,8 +6,9 @@ import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.*
import com.badlogic.gdx.utils.Align
import com.unciv.JsonParser
import com.unciv.MainMenuScreen
import com.unciv.json.fromJsonFile
import com.unciv.json.json
import com.unciv.models.ruleset.ModOptions
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetCache
@ -581,7 +582,7 @@ class ModManagementScreen(
companion object {
val modsToHideAsUrl by lazy {
val blockedModsFile = Gdx.files.internal("jsons/ManuallyBlockedMods.json")
JsonParser().getFromJson(Array<String>::class.java, blockedModsFile)
json().fromJsonFile(Array<String>::class.java, blockedModsFile)
}
}
}

View File

@ -7,6 +7,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextField
import com.badlogic.gdx.utils.Json
import com.unciv.UncivGame
import com.unciv.json.json
import com.unciv.logic.GameInfo
import com.unciv.logic.GameSaver
import com.unciv.models.translations.tr
@ -45,7 +46,7 @@ class SaveGameScreen(val gameInfo: GameInfo) : PickerScreen(disableScroll = true
copyJsonButton.onClick {
thread(name="Copy to clipboard") { // the Gzip rarely leads to ANRs
try {
val json = Json().toJson(gameInfo)
val json = json().toJson(gameInfo)
val base64Gzip = Gzip.zip(json)
Gdx.app.clipboard.contents = base64Gzip
} catch (OOM: OutOfMemoryError) {

View File

@ -1,8 +1,9 @@
package com.unciv.ui.tutorials
import com.badlogic.gdx.utils.Array
import com.unciv.JsonParser
import com.unciv.UncivGame
import com.unciv.json.fromJsonFile
import com.unciv.json.json
import com.unciv.models.Tutorial
import com.unciv.models.stats.INamed
import com.unciv.ui.civilopedia.FormattedLine
@ -15,7 +16,7 @@ class TutorialController(screen: BaseScreen) {
private var isTutorialShowing = false
var allTutorialsShowedCallback: (() -> Unit)? = null
private val tutorialRender = TutorialRender(screen)
private val tutorials = JsonParser().getFromJson(LinkedHashMap<String, Array<String>>().javaClass, "jsons/Tutorials.json")
private val tutorials = json().fromJsonFile(LinkedHashMap<String, Array<String>>().javaClass, "jsons/Tutorials.json")
fun showTutorial(tutorial: Tutorial) {
tutorialQueue.add(tutorial)

View File

@ -1,10 +1,10 @@
package com.unciv.app.desktop
import com.badlogic.gdx.Gdx
import com.unciv.json.json
import com.unciv.logic.CustomSaveLocationHelper
import com.unciv.logic.GameInfo
import com.unciv.logic.GameSaver
import com.unciv.logic.GameSaver.json
import java.awt.event.WindowEvent
import java.io.File
import java.util.concurrent.CancellationException

View File

@ -1,6 +1,7 @@
package com.unciv.testing
import com.unciv.UncivGame
import com.unciv.json.json
import com.unciv.logic.GameInfo
import com.unciv.logic.GameSaver
import com.unciv.logic.GameStarter
@ -83,7 +84,7 @@ class SerializationTests {
@Test
fun canSerializeGame() {
val json = try {
GameSaver.json().toJson(game)
json().toJson(game)
} catch (ex: Exception) {
""
}

View File

@ -1,7 +1,8 @@
package com.unciv.testing
import com.badlogic.gdx.utils.Array
import com.unciv.JsonParser
import com.unciv.json.fromJsonFile
import com.unciv.json.json
import com.unciv.models.Tutorial
import org.junit.Assert.assertTrue
import org.junit.Test
@ -16,7 +17,7 @@ class TutorialTranslationTests {
@Test
fun tutorialsFileIsSerializable() {
val map = JsonParser().getFromJson(LinkedHashMap<String, Array<String>>().javaClass, "jsons/Tutorials.json")
val map = json().fromJsonFile(LinkedHashMap<String, Array<String>>().javaClass, "jsons/Tutorials.json")
assertTrue("The number of items from Tutorials.json must match to the enum Tutorial",
map.size == tutorialCount)