mirror of
https://github.com/yairm210/Unciv.git
synced 2025-02-11 11:28:03 +07:00
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:
parent
569b51cb27
commit
86d5011da1
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
24
core/src/com/unciv/json/HashMapVector2.kt
Normal file
24
core/src/com/unciv/json/HashMapVector2.kt
Normal 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
53
core/src/com/unciv/json/NonStringKeyMapSerializer.kt
Normal file
53
core/src/com/unciv/json/NonStringKeyMapSerializer.kt
Normal 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
|
||||
}
|
||||
}
|
31
core/src/com/unciv/json/UncivJson.kt
Normal file
31
core/src/com/unciv/json/UncivJson.kt
Normal 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)
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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().
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
""
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user