diff --git a/modules/events-mod/src/main/typescript/generated/kafkatorio-schema/object-data.d.ts b/modules/events-mod/src/main/typescript/generated/kafkatorio-schema/object-data.d.ts index 27d0907..30d82f0 100644 --- a/modules/events-mod/src/main/typescript/generated/kafkatorio-schema/object-data.d.ts +++ b/modules/events-mod/src/main/typescript/generated/kafkatorio-schema/object-data.d.ts @@ -1,6 +1,6 @@ // 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 { objectName: ObjectName; @@ -45,16 +45,16 @@ interface SurfaceData extends FactorioObjectData { objectName: ObjectName; } -interface FactorioMapTile extends FactorioObjectData { +interface MapTile extends FactorioObjectData { objectName: ObjectName; position: PositionData; prototypeName: string; surfaceIndex: int; } -interface FactorioMapChunk extends FactorioObjectData { +interface MapChunk extends FactorioObjectData { objectName: ObjectName; - tiles: FactorioMapTile[]; + tiles: MapTile[]; } interface ConsoleChatMessage extends FactorioObjectData { diff --git a/modules/events-mod/src/main/typescript/main/events/converters.ts b/modules/events-mod/src/main/typescript/main/events/converters.ts index 104df2a..0bb3b53 100644 --- a/modules/events-mod/src/main/typescript/main/events/converters.ts +++ b/modules/events-mod/src/main/typescript/main/events/converters.ts @@ -83,7 +83,7 @@ export namespace Converters { } } - export function convertTile(tile: LuaTile): FactorioMapTile { + export function convertTile(tile: LuaTile): MapTile { return { objectName: tile.object_name, diff --git a/modules/events-mod/src/main/typescript/main/events/handlers.ts b/modules/events-mod/src/main/typescript/main/events/handlers.ts index 2700139..f8e085f 100644 --- a/modules/events-mod/src/main/typescript/main/events/handlers.ts +++ b/modules/events-mod/src/main/typescript/main/events/handlers.ts @@ -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) }) - let mapChunk: FactorioMapChunk = { - objectName: "FactorioMapChunk", + let mapChunk: MapChunk = { + objectName: "MapChunk", tiles: convertedTiles, } diff --git a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioConfiguration.kt b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioConfiguration.kt index ad8844f..ee45182 100644 --- a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioConfiguration.kt +++ b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioConfiguration.kt @@ -1,17 +1,17 @@ package dev.adamko.kafkatorio.events.schema +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient @Serializable +@SerialName("CONFIG") data class FactorioConfigurationUpdate( override val modVersion: String, val factorioData: FactorioGameDataUpdate, val allMods: List, val prototypes: List, ) : KafkatorioPacket() { - @Transient override val packetType = PacketType.CONFIG } diff --git a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioEvents.kt b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioEvents.kt index aaaf2a1..badb888 100644 --- a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioEvents.kt +++ b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioEvents.kt @@ -1,11 +1,20 @@ 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.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 +@SerialName("EVENT") data class FactorioEvent( override val modVersion: String, /** the initial Factorio event (`defines.events`) trigger */ @@ -14,6 +23,40 @@ data class FactorioEvent( val tick: UInt, val data: T, ) : KafkatorioPacket() { - @Transient override val packetType: PacketType = PacketType.EVENT + + companion object { + val kxsModule = SerializersModule { + contextual(FactorioEvent::class) { types -> serializer(types[0]) } + } + } + +} + + +object FactorioEventSerializer : JsonContentPolymorphicSerializer>( + FactorioEvent::class +) { + + override fun selectDeserializer(element: JsonElement): DeserializationStrategy> { + + 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) + } } diff --git a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioLuaObjects.kt b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioLuaObjects.kt index 7d5d749..f1ab819 100644 --- a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioLuaObjects.kt +++ b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioLuaObjects.kt @@ -2,7 +2,6 @@ package dev.adamko.kafkatorio.events.schema import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient import kotlinx.serialization.json.JsonClassDiscriminator @Serializable @@ -33,7 +32,7 @@ sealed class FactorioObjectData { LuaPlayer, LuaEntity, LuaSurface, - FactorioMapChunk, + MapChunk, LuaTile, ConsoleChatMessage, } @@ -57,7 +56,6 @@ data class PlayerData( val chatColour: Colour, val lastOnline: UInt, ) : FactorioObjectData() { - @Transient override val objectName = ObjectName.LuaPlayer } @@ -74,7 +72,6 @@ data class EntityData( val unitNumber: UInt? = null, val playerIndex: UInt? = null, ) : FactorioObjectData() { - @Transient override val objectName = ObjectName.LuaEntity } @@ -85,36 +82,33 @@ data class SurfaceData( val index: UInt, val name: String, ) : FactorioObjectData() { - @Transient 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 -@SerialName("FactorioMapChunk") -data class FactorioMapChunk( - val tiles: List, +@SerialName("MapChunk") +data class MapChunk( + val tiles: List, ) : FactorioObjectData() { - @Transient - override val objectName = ObjectName.FactorioMapChunk + override val objectName = ObjectName.MapChunk } @Serializable @SerialName("LuaTile") -data class FactorioMapTile( +data class MapTile( val prototypeName: String, val position: PositionData, val surfaceIndex: Int, ) : FactorioObjectData() { - @Transient override val objectName = ObjectName.LuaTile } @Serializable +@SerialName("ConsoleChatMessage") data class ConsoleChatMessage( val authorPlayerIndex: UInt?, val content: String, ) : FactorioObjectData() { - @Transient override val objectName = ObjectName.ConsoleChatMessage } diff --git a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/json.kt b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/json.kt index d99ec7e..78f26d5 100644 --- a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/json.kt +++ b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/json.kt @@ -9,7 +9,6 @@ import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonTransformingSerializer import kotlinx.serialization.json.jsonArray 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. */ @@ -27,8 +26,10 @@ internal class ListAsObjectSerializer(dataSerializer: KSerializer) : ) } -internal val jsonMapper = Json { +val jsonMapperKafkatorio = Json { prettyPrint = true prettyPrintIndent = " " - serializersModule = SerializersModule { } + serializersModule = FactorioEvent.kxsModule +// serializersModule = KafkatorioPacket.kxsModule + FactorioEvent.kxsModule +// serializersModule = SerializersModule { } } diff --git a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/kafkatorioPacket.kt b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/kafkatorioPacket.kt index fa6d789..702542f 100644 --- a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/kafkatorioPacket.kt +++ b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/kafkatorioPacket.kt @@ -1,29 +1,45 @@ package dev.adamko.kafkatorio.events.schema; +import kotlinx.serialization.DeserializationStrategy 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 -@JsonClassDiscriminator("packetType") +@Serializable(with = KafkatorioPacketSerializer::class) sealed class KafkatorioPacket { /** Schema versioning */ abstract val modVersion: String abstract val packetType: PacketType enum class PacketType { - EVENT, CONFIG, + EVENT, + CONFIG, } } -//@Serializable -//@JsonClassDiscriminator(KafkatorioPacketData.discriminatorKey) -//sealed class KafkatorioPacketData( -// @SerialName(discriminatorKey) -// val dataType: PacketDataType -//) { -// companion object { -// const val discriminatorKey: String = "dataType" -// } -//} +object KafkatorioPacketSerializer : JsonContentPolymorphicSerializer( + KafkatorioPacket::class +) { + private const val key = "packetType" + + override fun selectDeserializer(element: JsonElement): DeserializationStrategy { + + 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' ") + } + } +} diff --git a/modules/events-schema/src/jvmTest/kotlin/dev/adamko/kafkatorio/events/schema/FactorioEventTest.kt b/modules/events-schema/src/jvmTest/kotlin/dev/adamko/kafkatorio/events/schema/FactorioEventTest.kt index f5db169..04625ed 100644 --- a/modules/events-schema/src/jvmTest/kotlin/dev/adamko/kafkatorio/events/schema/FactorioEventTest.kt +++ b/modules/events-schema/src/jvmTest/kotlin/dev/adamko/kafkatorio/events/schema/FactorioEventTest.kt @@ -45,7 +45,7 @@ class FactorioEventTest : BehaviorSpec({ """.trimIndent() Then("parse") { - val actual: KafkatorioPacket = jsonMapper.decodeFromString(KafkatorioPacket.serializer(), json) + val actual: KafkatorioPacket = jsonMapperKafkatorio.decodeFromString(json) val expected = FactorioEvent( data = PlayerData( @@ -99,7 +99,7 @@ class FactorioEventTest : BehaviorSpec({ } """.trimIndent() Then("parse") { - val actual: KafkatorioPacket = jsonMapper.decodeFromString(json) + val actual: KafkatorioPacket = jsonMapperKafkatorio.decodeFromString(json) val expected = FactorioEvent( data = SurfaceData( @@ -144,7 +144,7 @@ class FactorioEventTest : BehaviorSpec({ } """.trimIndent() Then("parse") { - val actual: KafkatorioPacket = jsonMapper.decodeFromString(json) + val actual: KafkatorioPacket = jsonMapperKafkatorio.decodeFromString(json) val expected = FactorioEvent( data = EntityData( diff --git a/modules/events-server/src/main/kotlin/dev/adamko/kafkatorio/processor/serdes.kt b/modules/events-server/src/main/kotlin/dev/adamko/kafkatorio/processor/serdes.kt index 3138db1..88c3666 100644 --- a/modules/events-server/src/main/kotlin/dev/adamko/kafkatorio/processor/serdes.kt +++ b/modules/events-server/src/main/kotlin/dev/adamko/kafkatorio/processor/serdes.kt @@ -1,10 +1,10 @@ package dev.adamko.kafkatorio.processor import dev.adamko.kafkatorio.events.schema.KafkatorioPacket +import dev.adamko.kafkatorio.events.schema.jsonMapperKafkatorio import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import kotlinx.serialization.modules.SerializersModule import org.apache.kafka.common.serialization.Deserializer import org.apache.kafka.common.serialization.Serde import org.apache.kafka.common.serialization.Serializer @@ -26,7 +26,7 @@ object KafkatorioPacketSerde : Serde { val jsonMapper = Json { prettyPrint = true prettyPrintIndent = " " - serializersModule = SerializersModule { } + serializersModule = jsonMapperKafkatorio.serializersModule } object KXS : ConfigurableKotlinxSerialization({ @@ -34,5 +34,5 @@ object KXS : ConfigurableKotlinxSerialization({ prettyPrint = true prettyPrintIndent = " " asConfigurable().withStandardMappings().done() - serializersModule = SerializersModule { } + serializersModule = jsonMapperKafkatorio.serializersModule }) diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/websockets.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/websockets.kt index 0b05406..ca59674 100644 --- a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/websockets.kt +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/websockets.kt @@ -4,8 +4,8 @@ import dev.adamko.kafkatorio.events.schema.ConsoleChatMessage import dev.adamko.kafkatorio.events.schema.EntityData import dev.adamko.kafkatorio.events.schema.FactorioConfigurationUpdate import dev.adamko.kafkatorio.events.schema.FactorioEvent -import dev.adamko.kafkatorio.events.schema.FactorioMapChunk -import dev.adamko.kafkatorio.events.schema.FactorioMapTile +import dev.adamko.kafkatorio.events.schema.MapChunk +import dev.adamko.kafkatorio.events.schema.MapTile import dev.adamko.kafkatorio.events.schema.KafkatorioPacket import dev.adamko.kafkatorio.events.schema.PlayerData import dev.adamko.kafkatorio.events.schema.SurfaceData @@ -42,10 +42,10 @@ class WebsocketService( is PlayerData -> reduxStore.dispatch(FactorioUpdate.PlayerUpdate(event, eventData)) is ConsoleChatMessage -> {} - is EntityData -> {} - is FactorioMapChunk -> {} - is FactorioMapTile -> {} - is SurfaceData -> {} + is EntityData -> {} + is MapChunk -> {} + is MapTile -> {} + is SurfaceData -> {} } } is FactorioConfigurationUpdate -> {}