fix polymorphic serialization, and update the names of some schema objects

This commit is contained in:
Adam 2022-01-09 12:37:26 +01:00
parent e5feabf7eb
commit 2208c91652
11 changed files with 109 additions and 55 deletions

View file

@ -1,6 +1,6 @@
// Generated by TypeScriptGenerator - do not edit this file manually // Generated by TypeScriptGenerator - do not edit this file manually
type ObjectName = "LuaPlayer" | "LuaEntity" | "LuaSurface" | "FactorioMapChunk" | "LuaTile" | "ConsoleChatMessage"; type ObjectName = "LuaPlayer" | "LuaEntity" | "LuaSurface" | "MapChunk" | "LuaTile" | "ConsoleChatMessage";
interface FactorioObjectData { interface FactorioObjectData {
objectName: ObjectName; objectName: ObjectName;
@ -45,16 +45,16 @@ interface SurfaceData extends FactorioObjectData {
objectName: ObjectName; objectName: ObjectName;
} }
interface FactorioMapTile extends FactorioObjectData { interface MapTile extends FactorioObjectData {
objectName: ObjectName; objectName: ObjectName;
position: PositionData; position: PositionData;
prototypeName: string; prototypeName: string;
surfaceIndex: int; surfaceIndex: int;
} }
interface FactorioMapChunk extends FactorioObjectData { interface MapChunk extends FactorioObjectData {
objectName: ObjectName; objectName: ObjectName;
tiles: FactorioMapTile[]; tiles: MapTile[];
} }
interface ConsoleChatMessage extends FactorioObjectData { interface ConsoleChatMessage extends FactorioObjectData {

View file

@ -83,7 +83,7 @@ export namespace Converters {
} }
} }
export function convertTile(tile: LuaTile): FactorioMapTile { export function convertTile(tile: LuaTile): MapTile {
return { return {
objectName: tile.object_name, objectName: tile.object_name,

View file

@ -70,12 +70,12 @@ export function handleChunkUpdate(
} }
) ?? [] ) ?? []
let convertedTiles: FactorioMapTile[] = tiles.map((tile: LuaTile) => { let convertedTiles: MapTile[] = tiles.map((tile: LuaTile) => {
return Converters.convertTile(tile) return Converters.convertTile(tile)
}) })
let mapChunk: FactorioMapChunk = { let mapChunk: MapChunk = {
objectName: "FactorioMapChunk", objectName: "MapChunk",
tiles: convertedTiles, tiles: convertedTiles,
} }

View file

@ -1,17 +1,17 @@
package dev.adamko.kafkatorio.events.schema package dev.adamko.kafkatorio.events.schema
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
@Serializable @Serializable
@SerialName("CONFIG")
data class FactorioConfigurationUpdate( data class FactorioConfigurationUpdate(
override val modVersion: String, override val modVersion: String,
val factorioData: FactorioGameDataUpdate, val factorioData: FactorioGameDataUpdate,
val allMods: List<FactorioModInfo>, val allMods: List<FactorioModInfo>,
val prototypes: List<FactorioPrototype>, val prototypes: List<FactorioPrototype>,
) : KafkatorioPacket() { ) : KafkatorioPacket() {
@Transient
override val packetType = PacketType.CONFIG override val packetType = PacketType.CONFIG
} }

View file

@ -1,11 +1,20 @@
package dev.adamko.kafkatorio.events.schema package dev.adamko.kafkatorio.events.schema
import dev.adamko.kafkatorio.events.schema.FactorioObjectData.ObjectName
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.modules.SerializersModule
@Serializable @Serializable
@SerialName("EVENT")
data class FactorioEvent<out T : FactorioObjectData>( data class FactorioEvent<out T : FactorioObjectData>(
override val modVersion: String, override val modVersion: String,
/** the initial Factorio event (`defines.events`) trigger */ /** the initial Factorio event (`defines.events`) trigger */
@ -14,6 +23,40 @@ data class FactorioEvent<out T : FactorioObjectData>(
val tick: UInt, val tick: UInt,
val data: T, val data: T,
) : KafkatorioPacket() { ) : KafkatorioPacket() {
@Transient
override val packetType: PacketType = PacketType.EVENT override val packetType: PacketType = PacketType.EVENT
companion object {
val kxsModule = SerializersModule {
contextual(FactorioEvent::class) { types -> serializer(types[0]) }
}
}
}
object FactorioEventSerializer : JsonContentPolymorphicSerializer<FactorioEvent<*>>(
FactorioEvent::class
) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out FactorioEvent<*>> {
val objectName: ObjectName? = element
.jsonObject[FactorioEvent<*>::data.name]
?.jsonObject
?.get(FactorioObjectData.discriminatorKey)
?.jsonPrimitive
?.contentOrNull
?.let(ObjectName::valueOf)
val sub = when (objectName) {
ObjectName.LuaPlayer -> PlayerData.serializer()
ObjectName.LuaEntity -> EntityData.serializer()
ObjectName.LuaSurface -> SurfaceData.serializer()
ObjectName.MapChunk -> MapChunk.serializer()
ObjectName.LuaTile -> MapTile.serializer()
ObjectName.ConsoleChatMessage -> ConsoleChatMessage.serializer()
null -> throw IllegalStateException("unknown type $objectName")
}
return FactorioEvent.serializer(sub)
}
} }

View file

@ -2,7 +2,6 @@ package dev.adamko.kafkatorio.events.schema
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.json.JsonClassDiscriminator import kotlinx.serialization.json.JsonClassDiscriminator
@Serializable @Serializable
@ -33,7 +32,7 @@ sealed class FactorioObjectData {
LuaPlayer, LuaPlayer,
LuaEntity, LuaEntity,
LuaSurface, LuaSurface,
FactorioMapChunk, MapChunk,
LuaTile, LuaTile,
ConsoleChatMessage, ConsoleChatMessage,
} }
@ -57,7 +56,6 @@ data class PlayerData(
val chatColour: Colour, val chatColour: Colour,
val lastOnline: UInt, val lastOnline: UInt,
) : FactorioObjectData() { ) : FactorioObjectData() {
@Transient
override val objectName = ObjectName.LuaPlayer override val objectName = ObjectName.LuaPlayer
} }
@ -74,7 +72,6 @@ data class EntityData(
val unitNumber: UInt? = null, val unitNumber: UInt? = null,
val playerIndex: UInt? = null, val playerIndex: UInt? = null,
) : FactorioObjectData() { ) : FactorioObjectData() {
@Transient
override val objectName = ObjectName.LuaEntity override val objectName = ObjectName.LuaEntity
} }
@ -85,36 +82,33 @@ data class SurfaceData(
val index: UInt, val index: UInt,
val name: String, val name: String,
) : FactorioObjectData() { ) : FactorioObjectData() {
@Transient
override val objectName = ObjectName.LuaSurface override val objectName = ObjectName.LuaSurface
} }
/** Quasi-object. This isn't a Factorio Lua type, but a helpful collection of [FactorioMapTile]s */ /** Quasi-object. This isn't a Factorio Lua type, but a helpful collection of [MapTile]s */
@Serializable @Serializable
@SerialName("FactorioMapChunk") @SerialName("MapChunk")
data class FactorioMapChunk( data class MapChunk(
val tiles: List<FactorioMapTile>, val tiles: List<MapTile>,
) : FactorioObjectData() { ) : FactorioObjectData() {
@Transient override val objectName = ObjectName.MapChunk
override val objectName = ObjectName.FactorioMapChunk
} }
@Serializable @Serializable
@SerialName("LuaTile") @SerialName("LuaTile")
data class FactorioMapTile( data class MapTile(
val prototypeName: String, val prototypeName: String,
val position: PositionData, val position: PositionData,
val surfaceIndex: Int, val surfaceIndex: Int,
) : FactorioObjectData() { ) : FactorioObjectData() {
@Transient
override val objectName = ObjectName.LuaTile override val objectName = ObjectName.LuaTile
} }
@Serializable @Serializable
@SerialName("ConsoleChatMessage")
data class ConsoleChatMessage( data class ConsoleChatMessage(
val authorPlayerIndex: UInt?, val authorPlayerIndex: UInt?,
val content: String, val content: String,
) : FactorioObjectData() { ) : FactorioObjectData() {
@Transient
override val objectName = ObjectName.ConsoleChatMessage override val objectName = ObjectName.ConsoleChatMessage
} }

View file

@ -9,7 +9,6 @@ import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonTransformingSerializer import kotlinx.serialization.json.JsonTransformingSerializer
import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.modules.SerializersModule
/** Factorio outputs lists as Json objects - this serializer converts an object back to a list. */ /** Factorio outputs lists as Json objects - this serializer converts an object back to a list. */
@ -27,8 +26,10 @@ internal class ListAsObjectSerializer<T>(dataSerializer: KSerializer<T>) :
) )
} }
internal val jsonMapper = Json { val jsonMapperKafkatorio = Json {
prettyPrint = true prettyPrint = true
prettyPrintIndent = " " prettyPrintIndent = " "
serializersModule = SerializersModule { } serializersModule = FactorioEvent.kxsModule
// serializersModule = KafkatorioPacket.kxsModule + FactorioEvent.kxsModule
// serializersModule = SerializersModule { }
} }

View file

@ -1,29 +1,45 @@
package dev.adamko.kafkatorio.events.schema; package dev.adamko.kafkatorio.events.schema;
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonClassDiscriminator import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
@Serializable @Serializable(with = KafkatorioPacketSerializer::class)
@JsonClassDiscriminator("packetType")
sealed class KafkatorioPacket { sealed class KafkatorioPacket {
/** Schema versioning */ /** Schema versioning */
abstract val modVersion: String abstract val modVersion: String
abstract val packetType: PacketType abstract val packetType: PacketType
enum class PacketType { enum class PacketType {
EVENT, CONFIG, EVENT,
CONFIG,
} }
} }
//@Serializable object KafkatorioPacketSerializer : JsonContentPolymorphicSerializer<KafkatorioPacket>(
//@JsonClassDiscriminator(KafkatorioPacketData.discriminatorKey) KafkatorioPacket::class
//sealed class KafkatorioPacketData( ) {
// @SerialName(discriminatorKey) private const val key = "packetType"
// val dataType: PacketDataType
//) { override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out KafkatorioPacket> {
// companion object {
// const val discriminatorKey: String = "dataType" val type = element
// } .jsonObject[key]
//} ?.jsonPrimitive
?.contentOrNull
?.let(KafkatorioPacket.PacketType::valueOf)
return when (type) {
KafkatorioPacket.PacketType.EVENT -> FactorioEventSerializer
KafkatorioPacket.PacketType.CONFIG -> FactorioConfigurationUpdate.serializer()
else ->
throw Exception("Unknown Packet: key '$key' ")
}
}
}

View file

@ -45,7 +45,7 @@ class FactorioEventTest : BehaviorSpec({
""".trimIndent() """.trimIndent()
Then("parse") { Then("parse") {
val actual: KafkatorioPacket = jsonMapper.decodeFromString(KafkatorioPacket.serializer(), json) val actual: KafkatorioPacket = jsonMapperKafkatorio.decodeFromString(json)
val expected = FactorioEvent( val expected = FactorioEvent(
data = PlayerData( data = PlayerData(
@ -99,7 +99,7 @@ class FactorioEventTest : BehaviorSpec({
} }
""".trimIndent() """.trimIndent()
Then("parse") { Then("parse") {
val actual: KafkatorioPacket = jsonMapper.decodeFromString(json) val actual: KafkatorioPacket = jsonMapperKafkatorio.decodeFromString(json)
val expected = FactorioEvent( val expected = FactorioEvent(
data = SurfaceData( data = SurfaceData(
@ -144,7 +144,7 @@ class FactorioEventTest : BehaviorSpec({
} }
""".trimIndent() """.trimIndent()
Then("parse") { Then("parse") {
val actual: KafkatorioPacket = jsonMapper.decodeFromString(json) val actual: KafkatorioPacket = jsonMapperKafkatorio.decodeFromString(json)
val expected = FactorioEvent( val expected = FactorioEvent(
data = EntityData( data = EntityData(

View file

@ -1,10 +1,10 @@
package dev.adamko.kafkatorio.processor package dev.adamko.kafkatorio.processor
import dev.adamko.kafkatorio.events.schema.KafkatorioPacket import dev.adamko.kafkatorio.events.schema.KafkatorioPacket
import dev.adamko.kafkatorio.events.schema.jsonMapperKafkatorio
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import org.apache.kafka.common.serialization.Deserializer import org.apache.kafka.common.serialization.Deserializer
import org.apache.kafka.common.serialization.Serde import org.apache.kafka.common.serialization.Serde
import org.apache.kafka.common.serialization.Serializer import org.apache.kafka.common.serialization.Serializer
@ -26,7 +26,7 @@ object KafkatorioPacketSerde : Serde<KafkatorioPacket> {
val jsonMapper = Json { val jsonMapper = Json {
prettyPrint = true prettyPrint = true
prettyPrintIndent = " " prettyPrintIndent = " "
serializersModule = SerializersModule { } serializersModule = jsonMapperKafkatorio.serializersModule
} }
object KXS : ConfigurableKotlinxSerialization({ object KXS : ConfigurableKotlinxSerialization({
@ -34,5 +34,5 @@ object KXS : ConfigurableKotlinxSerialization({
prettyPrint = true prettyPrint = true
prettyPrintIndent = " " prettyPrintIndent = " "
asConfigurable().withStandardMappings().done() asConfigurable().withStandardMappings().done()
serializersModule = SerializersModule { } serializersModule = jsonMapperKafkatorio.serializersModule
}) })

View file

@ -4,8 +4,8 @@ import dev.adamko.kafkatorio.events.schema.ConsoleChatMessage
import dev.adamko.kafkatorio.events.schema.EntityData import dev.adamko.kafkatorio.events.schema.EntityData
import dev.adamko.kafkatorio.events.schema.FactorioConfigurationUpdate import dev.adamko.kafkatorio.events.schema.FactorioConfigurationUpdate
import dev.adamko.kafkatorio.events.schema.FactorioEvent import dev.adamko.kafkatorio.events.schema.FactorioEvent
import dev.adamko.kafkatorio.events.schema.FactorioMapChunk import dev.adamko.kafkatorio.events.schema.MapChunk
import dev.adamko.kafkatorio.events.schema.FactorioMapTile import dev.adamko.kafkatorio.events.schema.MapTile
import dev.adamko.kafkatorio.events.schema.KafkatorioPacket import dev.adamko.kafkatorio.events.schema.KafkatorioPacket
import dev.adamko.kafkatorio.events.schema.PlayerData import dev.adamko.kafkatorio.events.schema.PlayerData
import dev.adamko.kafkatorio.events.schema.SurfaceData import dev.adamko.kafkatorio.events.schema.SurfaceData
@ -42,10 +42,10 @@ class WebsocketService(
is PlayerData -> is PlayerData ->
reduxStore.dispatch(FactorioUpdate.PlayerUpdate(event, eventData)) reduxStore.dispatch(FactorioUpdate.PlayerUpdate(event, eventData))
is ConsoleChatMessage -> {} is ConsoleChatMessage -> {}
is EntityData -> {} is EntityData -> {}
is FactorioMapChunk -> {} is MapChunk -> {}
is FactorioMapTile -> {} is MapTile -> {}
is SurfaceData -> {} is SurfaceData -> {}
} }
} }
is FactorioConfigurationUpdate -> {} is FactorioConfigurationUpdate -> {}