mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-16 10:49:17 +07:00
Notifications remove backward compatibility (#10228)
* Remove backwards compatibility code from Notification serializer * Notification constructor simplified * One measly typo
This commit is contained in:
@ -1,6 +1,5 @@
|
||||
package com.unciv.logic.civilization
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Json
|
||||
@ -32,15 +31,13 @@ open class Notification() : IsPartOfGameInfoSerialization {
|
||||
|
||||
constructor(
|
||||
text: String,
|
||||
notificationIcons: Array<out String>,
|
||||
notificationIcons: Array<out String>, // `out` needed so we can pass a vararg directly
|
||||
actions: Iterable<NotificationAction>?,
|
||||
category: NotificationCategory = NotificationCategory.General
|
||||
) : this() {
|
||||
this.category = category
|
||||
this.text = text
|
||||
if (notificationIcons.isNotEmpty()) {
|
||||
this.icons = notificationIcons.toCollection(ArrayList())
|
||||
}
|
||||
notificationIcons.toCollection(this.icons)
|
||||
actions?.toCollection(this.actions)
|
||||
}
|
||||
|
||||
@ -98,25 +95,23 @@ open class Notification() : IsPartOfGameInfoSerialization {
|
||||
/**
|
||||
* Custom [Gdx.Json][Json] serializer/deserializer for one [Notification].
|
||||
*
|
||||
* Migration roadmap:
|
||||
*
|
||||
* 1.) Change internal structures but write old json format
|
||||
* 2.) Wait for good distribution in multiplayer user base
|
||||
* 3.) Switch to writing new format
|
||||
* 4.) Wait for Versions prior to Step 3 to fade out, keep switch for quick revert
|
||||
* 5.) Remove Switch, old format routines and this comment
|
||||
*
|
||||
* Caveats:
|
||||
*
|
||||
* * New format can express Notifications the old can't.
|
||||
* In that case, in Phase 1, reduce to first action and throw away the rest.
|
||||
* Example of the serialized format:
|
||||
* ```json
|
||||
* "notifications":[
|
||||
* {
|
||||
* "category":"Production",
|
||||
* "text":"[Nobel Foundation] has been built in [Stockholm]",
|
||||
* "icons":["BuildingIcons/Nobel Foundation"],
|
||||
* "actions":[
|
||||
* {"LocationAction":{"location":{"x":9,"y":3}}},
|
||||
* {"CivilopediaAction":{"link":"Wonder/Nobel Foundation"}},
|
||||
* {"CityAction":{"city":{"x":9,"y":3}}}
|
||||
* ]
|
||||
* }
|
||||
* ]
|
||||
* ```
|
||||
*/
|
||||
class Serializer : Json.Serializer<Notification> {
|
||||
companion object {
|
||||
/** The switch that starts Phase III and dies with Phase V
|
||||
* @see Serializer */
|
||||
private const val compatibilityMode = false
|
||||
}
|
||||
|
||||
override fun write(json: Json, notification: Notification, knownType: Class<*>?) {
|
||||
json.writeObjectStart()
|
||||
@ -127,13 +122,12 @@ open class Notification() : IsPartOfGameInfoSerialization {
|
||||
if (notification.icons.isNotEmpty())
|
||||
json.writeValue("icons", notification.icons, null, String::class.java)
|
||||
|
||||
if (compatibilityMode) writeOldFormatAction(json, notification)
|
||||
else writeNewFormatActions(json, notification)
|
||||
writeActions(json, notification)
|
||||
|
||||
json.writeObjectEnd()
|
||||
}
|
||||
|
||||
private fun writeNewFormatActions(json: Json, notification: Notification) {
|
||||
private fun writeActions(json: Json, notification: Notification) {
|
||||
if (notification.actions.isEmpty()) return
|
||||
json.writeArrayStart("actions")
|
||||
for (action in notification.actions) {
|
||||
@ -146,35 +140,14 @@ open class Notification() : IsPartOfGameInfoSerialization {
|
||||
json.writeArrayEnd()
|
||||
}
|
||||
|
||||
private fun writeOldFormatAction(json: Json, notification: Notification) {
|
||||
if (notification.actions.isEmpty()) return
|
||||
val firstAction = notification.actions.first()
|
||||
if (firstAction !is LocationAction) {
|
||||
json.writeValue("action", firstAction, null)
|
||||
return
|
||||
}
|
||||
val locations = notification.actions.filterIsInstance<LocationAction>()
|
||||
.map { it.location }.toTypedArray()
|
||||
json.writeObjectStart("action")
|
||||
json.writeValue("class", "com.unciv.logic.civilization.LocationAction")
|
||||
json.writeValue("locations", locations, Array<Vector2>::class.java, Vector2::class.java)
|
||||
json.writeObjectEnd()
|
||||
}
|
||||
|
||||
override fun read(json: Json, jsonData: JsonValue, type: Class<*>?) = Notification().apply {
|
||||
// Cannot be distinguished 100% certain by field names but if neither action / actions exist then both formats are compatible
|
||||
json.readField(this, "category", jsonData)
|
||||
json.readField(this, "text", jsonData)
|
||||
readOldFormatAction(json, jsonData)
|
||||
readNewFormatActions(json, jsonData)
|
||||
readActions(json, jsonData)
|
||||
json.readField(this, "icons", jsonData)
|
||||
}
|
||||
|
||||
private fun Notification.readNewFormatActions(json: Json, jsonData: JsonValue) {
|
||||
// New format looks like this: "notifications":[
|
||||
// {"category":"Cities","text":"[Stockholm] has expanded its borders!","icons":["StatIcons/Culture"],"actions":[{"LocationAction":{"location":{"x":7,"y":1}}},{"LocationAction":{"location":{"x":9,"y":3}}}]},
|
||||
// {"category":"Production","text":"[Nobel Foundation] has been built in [Stockholm]","icons":["BuildingIcons/Nobel Foundation"],"actions":[{"LocationAction":{"location":{"x":9,"y":3}}}]}
|
||||
// ]
|
||||
private fun Notification.readActions(json: Json, jsonData: JsonValue) {
|
||||
if (!jsonData.hasChild("actions")) return
|
||||
var entry = jsonData.get("actions").child
|
||||
while (entry != null) {
|
||||
@ -182,26 +155,5 @@ open class Notification() : IsPartOfGameInfoSerialization {
|
||||
entry = entry.next
|
||||
}
|
||||
}
|
||||
|
||||
private fun Notification.readOldFormatAction(json: Json, jsonData: JsonValue) {
|
||||
// Old format looks like: "notifications":[
|
||||
// {"text":"[Stockholm] has expanded its borders!","icons":["StatIcons/Culture"],"action":{"class":"com.unciv.logic.civilization.LocationAction","locations":[{"x":7,"y":1},{"x":9,"y":3}]},"category":"Cities"},
|
||||
// {"text":"[Nobel Foundation] has been built in [Stockholm]","icons":["BuildingIcons/Nobel Foundation"],"action":{"class":"com.unciv.logic.civilization.LocationAction","locations":[{"x":9,"y":3}]},"category":"Production"}
|
||||
// ]
|
||||
val actionData = jsonData.get("action") ?: return
|
||||
val actionClass = actionData.getString("class")
|
||||
when (actionClass.substring(actionClass.lastIndexOf('.') + 1)) {
|
||||
"LocationAction" -> actions += getOldFormatLocations(json, actionData)
|
||||
"TechAction" -> actions += json.readValue(TechAction::class.java, actionData)
|
||||
"CityAction" -> actions += json.readValue(CityAction::class.java, actionData)
|
||||
"DiplomacyAction" -> actions += json.readValue(DiplomacyAction::class.java, actionData)
|
||||
"MayaLongCountAction" -> actions += MayaLongCountAction()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOldFormatLocations(json: Json, actionData: JsonValue): Sequence<LocationAction> {
|
||||
val locations = json.readValue("locations", Array<Vector2>::class.java, actionData)
|
||||
return locations.asSequence().map { LocationAction(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ class OverviewAction(
|
||||
internal class NotificationActionsDeserializer {
|
||||
/* This exists as trick to leverage readFields for Json deserialization.
|
||||
// The serializer writes each NotificationAction as json object (within the actions array),
|
||||
// containing the class simpleName as subfield name, which carries any (on none) subclass-
|
||||
// containing the class simpleName as subfield name, which carries any (or none) subclass-
|
||||
// specific data as its object value. So, reading this from json data will fill just one of the
|
||||
// fields, and the listOfNotNull will output that field only.
|
||||
// Even though we know there's only one result, no need to first() since it's no advantage to the caller.
|
||||
|
Reference in New Issue
Block a user