diff options
Diffstat (limited to 'sw-engine/src/main/kotlin/org/luxons')
19 files changed, 153 insertions, 262 deletions
diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Requirements.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Requirements.kt index b78a5057..1d03aa76 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Requirements.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Requirements.kt @@ -1,11 +1,13 @@ package org.luxons.sevenwonders.engine.cards +import kotlinx.serialization.Serializable import org.luxons.sevenwonders.engine.Player import org.luxons.sevenwonders.engine.boards.Board import org.luxons.sevenwonders.engine.resources.* import org.luxons.sevenwonders.model.resources.ResourceTransactions import org.luxons.sevenwonders.model.resources.bestPrice +@Serializable data class Requirements internal constructor( val gold: Int = 0, val resources: Resources = emptyResources(), diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/GameDefinition.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/GameDefinition.kt index 07c101e6..64d6be3b 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/GameDefinition.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/GameDefinition.kt @@ -1,34 +1,20 @@ package org.luxons.sevenwonders.engine.data -import com.github.salomonbrys.kotson.typeToken -import com.google.gson.Gson -import com.google.gson.GsonBuilder +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json import org.luxons.sevenwonders.engine.Game import org.luxons.sevenwonders.engine.boards.Board import org.luxons.sevenwonders.engine.data.definitions.DecksDefinition import org.luxons.sevenwonders.engine.data.definitions.WonderDefinition -import org.luxons.sevenwonders.engine.data.serializers.NumericEffectSerializer -import org.luxons.sevenwonders.engine.data.serializers.ProductionIncreaseSerializer -import org.luxons.sevenwonders.engine.data.serializers.ProductionSerializer -import org.luxons.sevenwonders.engine.data.serializers.ResourceTypeSerializer -import org.luxons.sevenwonders.engine.data.serializers.ResourceTypesSerializer -import org.luxons.sevenwonders.engine.data.serializers.ResourcesSerializer -import org.luxons.sevenwonders.engine.data.serializers.ScienceProgressSerializer -import org.luxons.sevenwonders.engine.effects.GoldIncrease -import org.luxons.sevenwonders.engine.effects.MilitaryReinforcements -import org.luxons.sevenwonders.engine.effects.ProductionIncrease -import org.luxons.sevenwonders.engine.effects.RawPointsIncrease -import org.luxons.sevenwonders.engine.effects.ScienceProgress -import org.luxons.sevenwonders.engine.resources.Production -import org.luxons.sevenwonders.engine.resources.Resources import org.luxons.sevenwonders.model.Age import org.luxons.sevenwonders.model.Settings -import org.luxons.sevenwonders.model.resources.ResourceType import org.luxons.sevenwonders.model.wonders.AssignedWonder import org.luxons.sevenwonders.model.wonders.PreGameWonder internal const val LAST_AGE: Age = 3 +@Serializable internal data class GlobalRules( val minPlayers: Int, val maxPlayers: Int, @@ -63,36 +49,18 @@ class GameDefinition internal constructor( companion object { fun load(): GameDefinition { - val gson: Gson = createGson() - val rules = loadJson("global_rules.json", GlobalRules::class.java, gson) - val wonders = loadJson("wonders.json", Array<WonderDefinition>::class.java, gson) - val decksDefinition = loadJson("cards.json", DecksDefinition::class.java, gson) + val rules = loadJson<GlobalRules>("global_rules.json") + val wonders = loadJson<Array<WonderDefinition>>("wonders.json") + val decksDefinition = loadJson<DecksDefinition>("cards.json") return GameDefinition(rules, wonders.toList(), decksDefinition) } } } -private fun <T> loadJson(filename: String, clazz: Class<T>, gson: Gson): T { +private inline fun <reified T> loadJson(filename: String): T { val packageAsPath = GameDefinition::class.java.`package`.name.replace('.', '/') val resourcePath = "/$packageAsPath/$filename" val resource = GameDefinition::class.java.getResource(resourcePath) val json = resource.readText() - return gson.fromJson(json, clazz) + return Json.decodeFromString(json) } - -private fun createGson(): Gson { - return GsonBuilder().disableHtmlEscaping() - .registerTypeAdapter<Resources>(ResourcesSerializer()) - .registerTypeAdapter<ResourceType>(ResourceTypeSerializer()) - .registerTypeAdapter<List<ResourceType>>(ResourceTypesSerializer()) - .registerTypeAdapter<Production>(ProductionSerializer()) - .registerTypeAdapter<ProductionIncrease>(ProductionIncreaseSerializer()) - .registerTypeAdapter<MilitaryReinforcements>(NumericEffectSerializer()) - .registerTypeAdapter<RawPointsIncrease>(NumericEffectSerializer()) - .registerTypeAdapter<GoldIncrease>(NumericEffectSerializer()) - .registerTypeAdapter<ScienceProgress>(ScienceProgressSerializer()) - .create() -} - -private inline fun <reified T : Any> GsonBuilder.registerTypeAdapter(typeAdapter: Any): GsonBuilder = - this.registerTypeAdapter(typeToken<T>(), typeAdapter) diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/CardDefinition.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/CardDefinition.kt index cc42641a..0b9fbb7d 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/CardDefinition.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/CardDefinition.kt @@ -1,21 +1,23 @@ package org.luxons.sevenwonders.engine.data.definitions +import kotlinx.serialization.Serializable import org.luxons.sevenwonders.engine.cards.Card import org.luxons.sevenwonders.engine.cards.Requirements import org.luxons.sevenwonders.model.cards.CardBack import org.luxons.sevenwonders.model.cards.Color +@Serializable internal class CardDefinition( private val name: String, private val color: Color, - private val requirements: Requirements?, private val effect: EffectsDefinition, - private val chainParent: String?, - private val chainChildren: List<String>?, + private val requirements: Requirements? = null, + private val chainParent: String? = null, + private val chainChildren: List<String>? = null, + private val countPerNbPlayer: Map<Int, Int>? = null, private val image: String, - private val countPerNbPlayer: Map<Int, Int>, ) { - fun create(back: CardBack, nbPlayers: Int): List<Card> = List(countPerNbPlayer[nbPlayers] ?: 0) { create(back) } + fun create(back: CardBack, nbPlayers: Int): List<Card> = List(countPerNbPlayer!![nbPlayers] ?: 0) { create(back) } fun create(back: CardBack): Card { val reqs = requirements ?: Requirements() diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/DecksDefinition.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/DecksDefinition.kt index f3dc42f4..ed2b1214 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/DecksDefinition.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/DecksDefinition.kt @@ -1,17 +1,20 @@ package org.luxons.sevenwonders.engine.data.definitions +import kotlinx.serialization.Serializable import org.luxons.sevenwonders.engine.cards.Card import org.luxons.sevenwonders.engine.cards.Decks import org.luxons.sevenwonders.model.cards.CardBack import kotlin.random.Random +@Serializable internal class DeckDefinition( val cards: List<CardDefinition>, val backImage: String, ) { - fun create(nbPlayers: Int): List<Card> = cards.flatMap { it.create(CardBack(backImage), nbPlayers) } + fun prepareFor(nbPlayers: Int): List<Card> = cards.flatMap { it.create(CardBack(backImage), nbPlayers) } } +@Serializable internal class DecksDefinition( private val age1: DeckDefinition, private val age2: DeckDefinition, @@ -20,9 +23,9 @@ internal class DecksDefinition( ) { fun prepareDecks(nbPlayers: Int, random: Random) = Decks( mapOf( - 1 to age1.create(nbPlayers).shuffled(random), - 2 to age2.create(nbPlayers).shuffled(random), - 3 to (age3.create(nbPlayers) + pickGuildCards(nbPlayers, random)).shuffled(random), + 1 to age1.prepareFor(nbPlayers).shuffled(random), + 2 to age2.prepareFor(nbPlayers).shuffled(random), + 3 to (age3.prepareFor(nbPlayers) + pickGuildCards(nbPlayers, random)).shuffled(random), ), ) diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/EffectsDefinition.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/EffectsDefinition.kt index 86829eca..efd92c90 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/EffectsDefinition.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/EffectsDefinition.kt @@ -1,5 +1,6 @@ package org.luxons.sevenwonders.engine.data.definitions +import kotlinx.serialization.Serializable import org.luxons.sevenwonders.engine.effects.BonusPerBoardElement import org.luxons.sevenwonders.engine.effects.Discount import org.luxons.sevenwonders.engine.effects.Effect @@ -11,24 +12,25 @@ import org.luxons.sevenwonders.engine.effects.ScienceProgress import org.luxons.sevenwonders.engine.effects.SpecialAbility import org.luxons.sevenwonders.engine.effects.SpecialAbilityActivation +@Serializable internal class EffectsDefinition( - private val gold: GoldIncrease? = null, - private val military: MilitaryReinforcements? = null, + private val gold: Int? = null, + private val military: Int? = null, private val science: ScienceProgress? = null, private val discount: Discount? = null, private val perBoardElement: BonusPerBoardElement? = null, private val production: ProductionIncrease? = null, - private val points: RawPointsIncrease? = null, + private val points: Int? = null, private val action: SpecialAbility? = null, ) { fun create(): List<Effect> = mutableListOf<Effect>().apply { - gold?.let { add(it) } - military?.let { add(it) } + gold?.let { add(GoldIncrease(it)) } + military?.let { add(MilitaryReinforcements(it)) } science?.let { add(it) } discount?.let { add(it) } perBoardElement?.let { add(it) } production?.let { add(it) } - points?.let { add(it) } + points?.let { add(RawPointsIncrease(it)) } action?.let { add(SpecialAbilityActivation(it)) } } } diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/WonderDefinition.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/WonderDefinition.kt index 2a59f6dc..3d9956bc 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/WonderDefinition.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/definitions/WonderDefinition.kt @@ -1,12 +1,15 @@ package org.luxons.sevenwonders.engine.data.definitions +import kotlinx.serialization.Serializable import org.luxons.sevenwonders.engine.cards.Requirements +import org.luxons.sevenwonders.engine.data.serializers.ResourceTypeSerializer import org.luxons.sevenwonders.engine.wonders.Wonder import org.luxons.sevenwonders.engine.wonders.WonderStage import org.luxons.sevenwonders.model.resources.ResourceType import org.luxons.sevenwonders.model.wonders.WonderName import org.luxons.sevenwonders.model.wonders.WonderSide +@Serializable internal class WonderDefinition( val name: WonderName, val sides: Map<WonderSide, WonderSideDefinition>, @@ -14,7 +17,9 @@ internal class WonderDefinition( fun create(wonderSide: WonderSide): Wonder = sides[wonderSide]!!.createWonder(name) } +@Serializable internal class WonderSideDefinition( + @Serializable(with = ResourceTypeSerializer::class) private val initialResource: ResourceType, private val stages: List<WonderStageDefinition>, val image: String, @@ -22,6 +27,7 @@ internal class WonderSideDefinition( fun createWonder(name: String): Wonder = Wonder(name, initialResource, stages.map { it.create() }, image) } +@Serializable internal class WonderStageDefinition( private val requirements: Requirements, private val effects: EffectsDefinition, diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/NumericEffectSerializer.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/NumericEffectSerializer.kt deleted file mode 100644 index a149e8a0..00000000 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/NumericEffectSerializer.kt +++ /dev/null @@ -1,38 +0,0 @@ -package org.luxons.sevenwonders.engine.data.serializers - -import com.google.gson.JsonDeserializationContext -import com.google.gson.JsonDeserializer -import com.google.gson.JsonElement -import com.google.gson.JsonParseException -import com.google.gson.JsonPrimitive -import com.google.gson.JsonSerializationContext -import com.google.gson.JsonSerializer -import org.luxons.sevenwonders.engine.effects.Effect -import org.luxons.sevenwonders.engine.effects.GoldIncrease -import org.luxons.sevenwonders.engine.effects.MilitaryReinforcements -import org.luxons.sevenwonders.engine.effects.RawPointsIncrease -import java.lang.reflect.Type - -internal class NumericEffectSerializer : JsonSerializer<Effect>, JsonDeserializer<Effect> { - - override fun serialize(effect: Effect, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { - val value: Int = when (effect) { - is MilitaryReinforcements -> effect.count - is GoldIncrease -> effect.amount - is RawPointsIncrease -> effect.points - else -> throw IllegalArgumentException("Unknown numeric effect " + effect.javaClass.name) - } - return JsonPrimitive(value) - } - - @Throws(JsonParseException::class) - override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Effect { - val value = json.asInt - return when (typeOfT) { - MilitaryReinforcements::class.java -> MilitaryReinforcements(value) - GoldIncrease::class.java -> GoldIncrease(value) - RawPointsIncrease::class.java -> RawPointsIncrease(value) - else -> throw IllegalArgumentException("Unknown numeric effet " + typeOfT.typeName) - } - } -} diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ProductionIncreaseSerializer.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ProductionIncreaseSerializer.kt deleted file mode 100644 index dc340578..00000000 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ProductionIncreaseSerializer.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.luxons.sevenwonders.engine.data.serializers - -import com.google.gson.JsonDeserializationContext -import com.google.gson.JsonDeserializer -import com.google.gson.JsonElement -import com.google.gson.JsonParseException -import com.google.gson.JsonPrimitive -import com.google.gson.JsonSerializationContext -import com.google.gson.JsonSerializer -import org.luxons.sevenwonders.engine.effects.ProductionIncrease -import org.luxons.sevenwonders.engine.resources.Production -import java.lang.reflect.Type - -internal class ProductionIncreaseSerializer : JsonSerializer<ProductionIncrease>, JsonDeserializer<ProductionIncrease> { - - override fun serialize( - productionIncrease: ProductionIncrease, - typeOfSrc: Type, - context: JsonSerializationContext, - ): JsonElement { - val production = productionIncrease.production - val json = context.serialize(production) - return if (!json.isJsonNull && !productionIncrease.isSellable) JsonPrimitive("(${json.asString})") else json - } - - @Throws(JsonParseException::class) - override fun deserialize( - json: JsonElement, - typeOfT: Type, - context: JsonDeserializationContext, - ): ProductionIncrease { - var prodJson = json - - var resourcesStr = prodJson.asString - val isSellable = !resourcesStr.startsWith("(") - if (!isSellable) { - resourcesStr = unwrapBrackets(resourcesStr) - prodJson = JsonPrimitive(resourcesStr) - } - val production = context.deserialize<Production>(prodJson, Production::class.java) - return ProductionIncrease(production, isSellable) - } - - private fun unwrapBrackets(str: String): String { - return str.substring(1, str.length - 1) - } -} diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ProductionSerializer.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ProductionSerializer.kt index c98f20a8..48efaedb 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ProductionSerializer.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ProductionSerializer.kt @@ -1,53 +1,47 @@ package org.luxons.sevenwonders.engine.data.serializers -import com.google.gson.JsonDeserializationContext -import com.google.gson.JsonDeserializer -import com.google.gson.JsonElement -import com.google.gson.JsonNull -import com.google.gson.JsonParseException -import com.google.gson.JsonSerializationContext -import com.google.gson.JsonSerializer +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder import org.luxons.sevenwonders.engine.resources.Production -import org.luxons.sevenwonders.engine.resources.Resources import org.luxons.sevenwonders.model.resources.ResourceType -import java.lang.reflect.Type -internal class ProductionSerializer : JsonSerializer<Production>, JsonDeserializer<Production> { +internal object ProductionSerializer : KSerializer<Production> { - override fun serialize(production: Production, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { - val fixedResources = production.getFixedResources() - val choices = production.getAlternativeResources() - return when { - fixedResources.isEmpty() -> serializeAsChoice(choices, context) - choices.isEmpty() -> serializeAsResources(fixedResources, context) + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Production", PrimitiveKind.STRING) + + @OptIn(ExperimentalSerializationApi::class) + override fun serialize(encoder: Encoder, value: Production) { + val fixedResources = value.getFixedResources() + val choices = value.getAlternativeResources() + when { + fixedResources.isEmpty() -> choices.toChoiceString()?.let { encoder.encodeString(it) } ?: encoder.encodeNull() + choices.isEmpty() -> encoder.encodeSerializableValue(ResourcesSerializer, fixedResources) else -> throw IllegalArgumentException("Cannot serialize a production with mixed fixed resources and choices") } } - private fun serializeAsChoice(choices: List<Set<ResourceType>>, context: JsonSerializationContext): JsonElement { - if (choices.isEmpty()) { - return JsonNull.INSTANCE + private fun List<Set<ResourceType>>.toChoiceString(): String? { + if (isEmpty()) { + return null } - if (choices.size > 1) { + if (size > 1) { throw IllegalArgumentException("Cannot serialize a production with more than one choice") } - val str = choices[0].map { it.symbol }.joinToString("/") - return context.serialize(str) - } - - private fun serializeAsResources(fixedResources: Resources, context: JsonSerializationContext): JsonElement { - return context.serialize(fixedResources) + return this[0].map { it.symbol }.joinToString("/") } - @Throws(JsonParseException::class) - override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Production { - val resourcesStr = json.asString + override fun deserialize(decoder: Decoder): Production { + val resourcesStr = decoder.decodeString() val production = Production() if (resourcesStr.contains("/")) { production.addChoice(*createChoice(resourcesStr)) } else { - val fixedResources = context.deserialize<Resources>(json, Resources::class.java) - production.addAll(fixedResources) + production.addAll(ResourcesSerializer.deserializeString(resourcesStr)) } return production } diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ResourceTypeSerializer.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ResourceTypeSerializer.kt index b308fd24..ccbb9560 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ResourceTypeSerializer.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ResourceTypeSerializer.kt @@ -1,26 +1,20 @@ package org.luxons.sevenwonders.engine.data.serializers -import com.google.gson.JsonDeserializationContext -import com.google.gson.JsonDeserializer -import com.google.gson.JsonElement -import com.google.gson.JsonParseException -import com.google.gson.JsonPrimitive -import com.google.gson.JsonSerializationContext -import com.google.gson.JsonSerializer +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder import org.luxons.sevenwonders.model.resources.ResourceType -import java.lang.reflect.Type -internal class ResourceTypeSerializer : JsonSerializer<ResourceType>, JsonDeserializer<ResourceType> { +internal object ResourceTypeSerializer : KSerializer<ResourceType> { - override fun serialize(type: ResourceType, typeOfSrc: Type, context: JsonSerializationContext): JsonElement = - JsonPrimitive(type.symbol) + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ResourceType", PrimitiveKind.STRING) - @Throws(JsonParseException::class) - override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ResourceType { - val str = json.asString - if (str.isEmpty()) { - throw IllegalArgumentException("Empty string is not a valid resource level") - } - return ResourceType.fromSymbol(str[0]) + override fun serialize(encoder: Encoder, value: ResourceType) { + encoder.encodeString(value.symbol.toString()) } + + override fun deserialize(decoder: Decoder): ResourceType = ResourceType.fromSymbol(decoder.decodeString()) } diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ResourceTypesSerializer.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ResourceTypesSerializer.kt index ebbee8fd..8bd35f52 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ResourceTypesSerializer.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ResourceTypesSerializer.kt @@ -1,30 +1,21 @@ package org.luxons.sevenwonders.engine.data.serializers -import com.google.gson.JsonDeserializationContext -import com.google.gson.JsonDeserializer -import com.google.gson.JsonElement -import com.google.gson.JsonParseException -import com.google.gson.JsonPrimitive -import com.google.gson.JsonSerializationContext -import com.google.gson.JsonSerializer +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder import org.luxons.sevenwonders.model.resources.ResourceType -import java.lang.reflect.Type -internal class ResourceTypesSerializer : JsonSerializer<List<ResourceType>>, JsonDeserializer<List<ResourceType>> { +internal object ResourceTypesSerializer : KSerializer<List<ResourceType>> { - override fun serialize( - resources: List<ResourceType>, - typeOfSrc: Type, - context: JsonSerializationContext, - ): JsonElement { - val s = resources.map { it.symbol }.joinToString("") - return JsonPrimitive(s) + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ResourceTypeList", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: List<ResourceType>) { + encoder.encodeString(value.map { it.symbol }.joinToString("")) } - @Throws(JsonParseException::class) - override fun deserialize( - json: JsonElement, - typeOfT: Type, - context: JsonDeserializationContext, - ): List<ResourceType> = json.asString.map { ResourceType.fromSymbol(it) } + override fun deserialize(decoder: Decoder): List<ResourceType> = + decoder.decodeString().map { ResourceType.fromSymbol(it) } } diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ResourcesSerializer.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ResourcesSerializer.kt index 4226411d..cf31c380 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ResourcesSerializer.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ResourcesSerializer.kt @@ -1,26 +1,27 @@ package org.luxons.sevenwonders.engine.data.serializers -import com.google.gson.JsonDeserializationContext -import com.google.gson.JsonDeserializer -import com.google.gson.JsonElement -import com.google.gson.JsonNull -import com.google.gson.JsonParseException -import com.google.gson.JsonPrimitive -import com.google.gson.JsonSerializationContext -import com.google.gson.JsonSerializer +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder import org.luxons.sevenwonders.engine.resources.Resources import org.luxons.sevenwonders.engine.resources.toResources import org.luxons.sevenwonders.model.resources.ResourceType -import java.lang.reflect.Type -internal class ResourcesSerializer : JsonSerializer<Resources>, JsonDeserializer<Resources> { +internal object ResourcesSerializer : KSerializer<Resources> { - override fun serialize(resources: Resources, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { - val s = resources.toList().map { it.symbol }.joinToString("") - return if (s.isEmpty()) JsonNull.INSTANCE else JsonPrimitive(s) + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Resources", PrimitiveKind.STRING) + + @OptIn(ExperimentalSerializationApi::class) + override fun serialize(encoder: Encoder, value: Resources) { + val s = value.toList().map { it.symbol }.joinToString("") + if (s.isEmpty()) encoder.encodeNull() else encoder.encodeString(s) } - @Throws(JsonParseException::class) - override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Resources = - json.asString.map { ResourceType.fromSymbol(it) to 1 }.toResources() + override fun deserialize(decoder: Decoder): Resources = deserializeString(decoder.decodeString()) + + fun deserializeString(symbols: String): Resources = symbols.map { ResourceType.fromSymbol(it) to 1 }.toResources() } diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ScienceProgressSerializer.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ScienceProgressSerializer.kt index 1193c94b..b4493c70 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ScienceProgressSerializer.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/data/serializers/ScienceProgressSerializer.kt @@ -1,55 +1,48 @@ package org.luxons.sevenwonders.engine.data.serializers -import com.google.gson.JsonDeserializationContext -import com.google.gson.JsonDeserializer -import com.google.gson.JsonElement -import com.google.gson.JsonNull -import com.google.gson.JsonParseException -import com.google.gson.JsonPrimitive -import com.google.gson.JsonSerializationContext -import com.google.gson.JsonSerializer +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.serializer import org.luxons.sevenwonders.engine.boards.Science import org.luxons.sevenwonders.engine.boards.ScienceType import org.luxons.sevenwonders.engine.effects.ScienceProgress -import java.lang.reflect.Type -internal class ScienceProgressSerializer : JsonSerializer<ScienceProgress>, JsonDeserializer<ScienceProgress> { +internal object ScienceProgressSerializer : KSerializer<ScienceProgress> { - override fun serialize( - scienceProgress: ScienceProgress, - typeOfSrc: Type, - context: JsonSerializationContext, - ): JsonElement { - val science = scienceProgress.science + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ScienceProgress", PrimitiveKind.STRING) - if (science.size() > 1) { + @OptIn(ExperimentalSerializationApi::class) + override fun serialize(encoder: Encoder, value: ScienceProgress) { + if (value.science.size() > 1) { throw UnsupportedOperationException("Cannot serialize science containing more than one element") } - + if (value.science.jokers == 1) { + encoder.encodeString("any") + return + } for (type in ScienceType.values()) { - val quantity = science.getQuantity(type) + val quantity = value.science.getQuantity(type) if (quantity == 1) { - return context.serialize(type) + encoder.encodeSerializableValue(serializer(), type) + return } } - - return if (science.jokers == 1) JsonPrimitive("any") else JsonNull.INSTANCE + encoder.encodeNull() } - @Throws(JsonParseException::class) - override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ScienceProgress { - val s = json.asString + override fun deserialize(decoder: Decoder): ScienceProgress { + val s = decoder.decodeString() val science = Science() if ("any" == s) { science.addJoker(1) } else { - science.add(deserializeScienceType(json, context), 1) + science.add(ScienceType.valueOf(s), 1) } return ScienceProgress(science) } - - private fun deserializeScienceType(json: JsonElement, context: JsonDeserializationContext): ScienceType { - return context.deserialize<ScienceType>(json, ScienceType::class.java) - ?: throw IllegalArgumentException("Invalid science level " + json.asString) - } } diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/BonusPerBoardElement.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/BonusPerBoardElement.kt index 41dbd937..6f1cee51 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/BonusPerBoardElement.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/BonusPerBoardElement.kt @@ -1,5 +1,6 @@ package org.luxons.sevenwonders.engine.effects +import kotlinx.serialization.Serializable import org.luxons.sevenwonders.engine.Player import org.luxons.sevenwonders.engine.boards.Board import org.luxons.sevenwonders.model.boards.RelativeBoardPosition @@ -11,6 +12,7 @@ enum class BoardElementType { DEFEAT_TOKEN, } +@Serializable internal data class BonusPerBoardElement( val boards: List<RelativeBoardPosition>, val type: BoardElementType, diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/Discount.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/Discount.kt index 83a45e28..f74f79c8 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/Discount.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/Discount.kt @@ -1,10 +1,14 @@ package org.luxons.sevenwonders.engine.effects +import kotlinx.serialization.Serializable import org.luxons.sevenwonders.engine.boards.Board +import org.luxons.sevenwonders.engine.data.serializers.ResourceTypesSerializer import org.luxons.sevenwonders.model.resources.Provider import org.luxons.sevenwonders.model.resources.ResourceType +@Serializable internal data class Discount( + @Serializable(with = ResourceTypesSerializer::class) val resourceTypes: List<ResourceType> = emptyList(), val providers: List<Provider> = emptyList(), val discountedPrice: Int = 1, diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/ProductionIncrease.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/ProductionIncrease.kt index 6458ad20..006d7516 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/ProductionIncrease.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/ProductionIncrease.kt @@ -1,14 +1,19 @@ package org.luxons.sevenwonders.engine.effects +import kotlinx.serialization.Serializable import org.luxons.sevenwonders.engine.boards.Board import org.luxons.sevenwonders.engine.resources.Production -internal data class ProductionIncrease(val production: Production, val isSellable: Boolean) : InstantOwnBoardEffect() { +@Serializable +internal data class ProductionIncrease( + val resources: Production, + val isSellable: Boolean +) : InstantOwnBoardEffect() { public override fun applyTo(board: Board) { - board.production.addAll(production) + board.production.addAll(resources) if (isSellable) { - board.publicProduction.addAll(production) + board.publicProduction.addAll(resources) } } } diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/ScienceProgress.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/ScienceProgress.kt index 4ea8626f..50a670bb 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/ScienceProgress.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/effects/ScienceProgress.kt @@ -1,8 +1,11 @@ package org.luxons.sevenwonders.engine.effects +import kotlinx.serialization.Serializable import org.luxons.sevenwonders.engine.boards.Board import org.luxons.sevenwonders.engine.boards.Science +import org.luxons.sevenwonders.engine.data.serializers.ScienceProgressSerializer +@Serializable(with = ScienceProgressSerializer::class) internal class ScienceProgress(val science: Science) : InstantOwnBoardEffect() { public override fun applyTo(board: Board) = board.science.addAll(science) diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/Production.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/Production.kt index 3eb59bd5..ad11faab 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/Production.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/Production.kt @@ -1,8 +1,11 @@ package org.luxons.sevenwonders.engine.resources +import kotlinx.serialization.Serializable +import org.luxons.sevenwonders.engine.data.serializers.ProductionSerializer import org.luxons.sevenwonders.model.resources.ResourceType import java.util.EnumSet +@Serializable(with = ProductionSerializer::class) data class Production internal constructor( private val fixedResources: MutableResources = mutableResourcesOf(), // cannot be a Set because the same choices can be there multiple times diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/Resources.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/Resources.kt index b2dfc964..53556a3d 100644 --- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/Resources.kt +++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/Resources.kt @@ -1,5 +1,7 @@ package org.luxons.sevenwonders.engine.resources +import kotlinx.serialization.Serializable +import org.luxons.sevenwonders.engine.data.serializers.ResourcesSerializer import org.luxons.sevenwonders.model.resources.ResourceType fun emptyResources(): Resources = MutableResources() @@ -34,6 +36,7 @@ internal fun Map<ResourceType, Int>.toMutableResources(): MutableResources = Mut internal fun Resources.toMutableResources(): MutableResources = quantities.toMutableResources() +@Serializable(with = ResourcesSerializer::class) interface Resources { val quantities: Map<ResourceType, Int> |