- tidy up groupMapChunks.kt
- add ticks to all Kafkatorio Packets - add Kafka Streams test for groupMapChunks.kt
This commit is contained in:
parent
bfdee32d61
commit
b8ba8b7c9e
22 changed files with 271 additions and 98 deletions
|
|
@ -90,9 +90,10 @@ gcp-speech = { group = "com.google.cloud", name = "google-cloud-speech"
|
||||||
|
|
||||||
|
|
||||||
## kafka ##
|
## kafka ##
|
||||||
kafka-streams = { group = "org.apache.kafka", name = "kafka-streams", version.ref = "kafka" }
|
kafka-streams = { group = "org.apache.kafka", name = "kafka-streams", version.ref = "kafka" }
|
||||||
|
kafka-streamsTestUtils = { group = "org.apache.kafka", name = "kafka-streams-test-utils", version.ref = "kafka" }
|
||||||
# kafka helpers
|
# kafka helpers
|
||||||
kotka = { group = "com.github.blueanvil", name = "kotka", version = "1.1.1" }
|
#kotka = { group = "com.github.blueanvil", name = "kotka", version = "1.1.1" }
|
||||||
|
|
||||||
jsoup = { group = "org.jsoup", name = "jsoup", version = "1.14.3" }
|
jsoup = { group = "org.jsoup", name = "jsoup", version = "1.14.3" }
|
||||||
skrapeit = { group = "it.skrape", name = "skrapeit", version = "1.2.0" }
|
skrapeit = { group = "it.skrape", name = "skrapeit", version = "1.2.0" }
|
||||||
|
|
|
||||||
|
|
@ -16,4 +16,5 @@ interface FactorioConfigurationUpdate extends KafkatorioPacket {
|
||||||
factorioData: FactorioGameDataUpdate;
|
factorioData: FactorioGameDataUpdate;
|
||||||
modVersion: string;
|
modVersion: string;
|
||||||
packetType: PacketType;
|
packetType: PacketType;
|
||||||
|
tick: uint;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ interface FactorioPrototypes extends KafkatorioPacket {
|
||||||
modVersion: string;
|
modVersion: string;
|
||||||
packetType: PacketType;
|
packetType: PacketType;
|
||||||
prototypes: FactorioPrototype[];
|
prototypes: FactorioPrototype[];
|
||||||
|
tick: uint;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MapTilePrototype extends FactorioPrototype {
|
interface MapTilePrototype extends FactorioPrototype {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ type PacketType = "EVENT" | "CONFIG" | "PROTOTYPES" | "UPDATE";
|
||||||
interface KafkatorioPacket {
|
interface KafkatorioPacket {
|
||||||
modVersion: string;
|
modVersion: string;
|
||||||
packetType: PacketType;
|
packetType: PacketType;
|
||||||
|
tick: uint;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FactorioEvent extends KafkatorioPacket {
|
interface FactorioEvent extends KafkatorioPacket {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ export function emitConfigurationUpdate(changeData: ConfigurationChangedData) {
|
||||||
modVersion: global.MOD_VERSION,
|
modVersion: global.MOD_VERSION,
|
||||||
packetType: "CONFIG",
|
packetType: "CONFIG",
|
||||||
allMods: allMods(changeData),
|
allMods: allMods(changeData),
|
||||||
factorioData: factorioData(changeData)
|
factorioData: factorioData(changeData),
|
||||||
|
tick: game.tick,
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ export function emitPrototypes() {
|
||||||
emitPacket<FactorioPrototypes>({
|
emitPacket<FactorioPrototypes>({
|
||||||
modVersion: global.MOD_VERSION,
|
modVersion: global.MOD_VERSION,
|
||||||
packetType: "PROTOTYPES",
|
packetType: "PROTOTYPES",
|
||||||
prototypes: prototypes()
|
prototypes: prototypes(),
|
||||||
|
tick: game.tick,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,6 @@ export function emitEvent<T extends FactorioObjectData>(eventData: T, tick: uint
|
||||||
packetType: "EVENT",
|
packetType: "EVENT",
|
||||||
eventType: eventType,
|
eventType: eventType,
|
||||||
modVersion: global.MOD_VERSION,
|
modVersion: global.MOD_VERSION,
|
||||||
tick: tick
|
tick: tick,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,8 +112,9 @@ interface EntityIdentifiers {
|
||||||
data class MapTileDictionary(
|
data class MapTileDictionary(
|
||||||
/** Map an X,Y coordinate a prototype name */
|
/** Map an X,Y coordinate a prototype name */
|
||||||
val tilesXY: Map<String, Map<String, PrototypeKey>>,
|
val tilesXY: Map<String, Map<String, PrototypeKey>>,
|
||||||
val protos: Map<PrototypeKey, PrototypeName>
|
val protos: Map<PrototypeKey, PrototypeName>,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun toMapTileList(): List<MapTile> = buildList {
|
fun toMapTileList(): List<MapTile> = buildList {
|
||||||
tilesXY.forEach { (xString, row) ->
|
tilesXY.forEach { (xString, row) ->
|
||||||
row.forEach { (yString, protoIndex) ->
|
row.forEach { (yString, protoIndex) ->
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import kotlinx.serialization.Serializable
|
||||||
@Serializable
|
@Serializable
|
||||||
data class FactorioConfigurationUpdate(
|
data class FactorioConfigurationUpdate(
|
||||||
override val modVersion: String,
|
override val modVersion: String,
|
||||||
|
override val tick: Tick,
|
||||||
val factorioData: FactorioGameDataUpdate,
|
val factorioData: FactorioGameDataUpdate,
|
||||||
val allMods: List<FactorioModInfo>,
|
val allMods: List<FactorioModInfo>,
|
||||||
) : KafkatorioPacket() {
|
) : KafkatorioPacket() {
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@ import kotlinx.serialization.json.jsonPrimitive
|
||||||
@Serializable
|
@Serializable
|
||||||
data class FactorioEventUpdatePacket(
|
data class FactorioEventUpdatePacket(
|
||||||
override val modVersion: String,
|
override val modVersion: String,
|
||||||
/** game time */
|
override val tick: Tick,
|
||||||
val tick: Tick,
|
|
||||||
val update: FactorioEventUpdate,
|
val update: FactorioEventUpdate,
|
||||||
) : KafkatorioPacket() {
|
) : KafkatorioPacket() {
|
||||||
@EncodeDefault
|
@EncodeDefault
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,9 @@ import kotlinx.serialization.Serializable
|
||||||
@Serializable
|
@Serializable
|
||||||
data class FactorioEvent(
|
data class FactorioEvent(
|
||||||
override val modVersion: String,
|
override val modVersion: String,
|
||||||
|
override val tick: Tick,
|
||||||
/** the initial Factorio event (`defines.events`) trigger */
|
/** the initial Factorio event (`defines.events`) trigger */
|
||||||
val eventType: String,
|
val eventType: String,
|
||||||
/** game time */
|
|
||||||
val tick: Tick,
|
|
||||||
val data: FactorioObjectData,
|
val data: FactorioObjectData,
|
||||||
) : KafkatorioPacket() {
|
) : KafkatorioPacket() {
|
||||||
@EncodeDefault
|
@EncodeDefault
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import kotlinx.serialization.json.jsonPrimitive
|
||||||
@Serializable
|
@Serializable
|
||||||
data class FactorioPrototypes(
|
data class FactorioPrototypes(
|
||||||
override val modVersion: String,
|
override val modVersion: String,
|
||||||
|
override val tick: Tick,
|
||||||
val prototypes: List<FactorioPrototype>,
|
val prototypes: List<FactorioPrototype>,
|
||||||
) : KafkatorioPacket() {
|
) : KafkatorioPacket() {
|
||||||
@EncodeDefault
|
@EncodeDefault
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import kotlinx.serialization.json.jsonPrimitive
|
||||||
sealed class KafkatorioPacket {
|
sealed class KafkatorioPacket {
|
||||||
/** Schema versioning */
|
/** Schema versioning */
|
||||||
abstract val modVersion: String
|
abstract val modVersion: String
|
||||||
|
abstract val tick: Tick
|
||||||
@EncodeDefault
|
@EncodeDefault
|
||||||
abstract val packetType: PacketType
|
abstract val packetType: PacketType
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,7 @@ class FactorioEventTest : FunSpec({
|
||||||
{
|
{
|
||||||
"modVersion": "0.2.8",
|
"modVersion": "0.2.8",
|
||||||
"packetType": "CONFIG",
|
"packetType": "CONFIG",
|
||||||
|
"tick": 534,
|
||||||
"allMods": [
|
"allMods": [
|
||||||
{
|
{
|
||||||
"modName": "kafkatorio-events",
|
"modName": "kafkatorio-events",
|
||||||
|
|
@ -310,6 +311,7 @@ class FactorioEventTest : FunSpec({
|
||||||
|
|
||||||
val expected = FactorioConfigurationUpdate(
|
val expected = FactorioConfigurationUpdate(
|
||||||
modVersion = "0.2.8",
|
modVersion = "0.2.8",
|
||||||
|
tick = Tick(534u),
|
||||||
factorioData = FactorioGameDataUpdate(null, null),
|
factorioData = FactorioGameDataUpdate(null, null),
|
||||||
allMods = listOf(
|
allMods = listOf(
|
||||||
FactorioModInfo(
|
FactorioModInfo(
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ class PrototypesTest : BehaviorSpec({
|
||||||
{
|
{
|
||||||
"modVersion":"0.2.4",
|
"modVersion":"0.2.4",
|
||||||
"packetType":"PROTOTYPES",
|
"packetType":"PROTOTYPES",
|
||||||
|
"tick": 534,
|
||||||
"prototypes":[
|
"prototypes":[
|
||||||
{
|
{
|
||||||
"prototypeObjectName":"LuaTilePrototype",
|
"prototypeObjectName":"LuaTilePrototype",
|
||||||
|
|
@ -46,6 +47,7 @@ class PrototypesTest : BehaviorSpec({
|
||||||
{
|
{
|
||||||
"modVersion":"0.3.2",
|
"modVersion":"0.3.2",
|
||||||
"packetType":"PROTOTYPES",
|
"packetType":"PROTOTYPES",
|
||||||
|
"tick": 534,
|
||||||
"prototypes":[
|
"prototypes":[
|
||||||
{
|
{
|
||||||
"prototypeObjectName":"LuaTilePrototype",
|
"prototypeObjectName":"LuaTilePrototype",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ val kafkaStateDir = layout.projectDirectory.dir("kafka-state")
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation(libs.kafka.streams)
|
implementation(libs.kafka.streams)
|
||||||
|
testImplementation(libs.kafka.streamsTestUtils)
|
||||||
|
|
||||||
implementation(projects.modules.eventsSchema)
|
implementation(projects.modules.eventsSchema)
|
||||||
|
|
||||||
|
|
@ -40,7 +41,7 @@ dependencies {
|
||||||
|
|
||||||
implementation("com.sksamuel.scrimage:scrimage-core:4.0.24")
|
implementation("com.sksamuel.scrimage:scrimage-core:4.0.24")
|
||||||
|
|
||||||
val kotkaVersion = "v0.0.14"
|
val kotkaVersion = "v0.0.15"
|
||||||
implementation("com.github.adamko-dev.kotka-streams:kotka-streams:$kotkaVersion") {
|
implementation("com.github.adamko-dev.kotka-streams:kotka-streams:$kotkaVersion") {
|
||||||
isChanging = true
|
isChanging = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,20 @@
|
||||||
package dev.adamko.kafkatorio.processor
|
package dev.adamko.kafkatorio.processor
|
||||||
|
|
||||||
import dev.adamko.kafkatorio.events.schema.ColourHex
|
import dev.adamko.kafkatorio.events.schema.ColourHex
|
||||||
import dev.adamko.kafkatorio.events.schema.FactorioEvent
|
|
||||||
import dev.adamko.kafkatorio.events.schema.FactorioEventUpdate
|
|
||||||
import dev.adamko.kafkatorio.events.schema.FactorioEventUpdatePacket
|
|
||||||
import dev.adamko.kafkatorio.events.schema.FactorioObjectData
|
|
||||||
import dev.adamko.kafkatorio.events.schema.FactorioPrototypes
|
|
||||||
import dev.adamko.kafkatorio.events.schema.KafkatorioPacket
|
|
||||||
import dev.adamko.kafkatorio.processor.config.ApplicationProperties
|
import dev.adamko.kafkatorio.processor.config.ApplicationProperties
|
||||||
import dev.adamko.kafkatorio.processor.serdes.jsonMapper
|
|
||||||
import dev.adamko.kafkatorio.processor.serdes.kxsBinary
|
import dev.adamko.kafkatorio.processor.serdes.kxsBinary
|
||||||
import dev.adamko.kafkatorio.processor.topology.FactorioServerId
|
|
||||||
import dev.adamko.kafkatorio.processor.topology.ServerMapChunkId
|
import dev.adamko.kafkatorio.processor.topology.ServerMapChunkId
|
||||||
import dev.adamko.kafkatorio.processor.topology.ServerMapChunkTiles
|
import dev.adamko.kafkatorio.processor.topology.ServerMapChunkTiles
|
||||||
import dev.adamko.kafkatorio.processor.topology.factorioServerPacketStream
|
import dev.adamko.kafkatorio.processor.topology.factorioServerPacketStream
|
||||||
import dev.adamko.kafkatorio.processor.topology.groupTilesIntoChunksWithColours
|
import dev.adamko.kafkatorio.processor.topology.groupMapChunks
|
||||||
import dev.adamko.kafkatorio.processor.topology.playerUpdatesToWsServer
|
import dev.adamko.kafkatorio.processor.topology.playerUpdatesToWsServer
|
||||||
import dev.adamko.kafkatorio.processor.topology.saveMapTiles
|
import dev.adamko.kafkatorio.processor.topology.saveMapTiles
|
||||||
import dev.adamko.kafkatorio.processor.topology.splitFactorioServerPacketStream
|
import dev.adamko.kafkatorio.processor.topology.splitFactorioServerPacketStream
|
||||||
import dev.adamko.kafkatorio.processor.topology.tileProtoColourDictionary
|
|
||||||
import dev.adamko.kotka.extensions.consumedAs
|
import dev.adamko.kotka.extensions.consumedAs
|
||||||
import dev.adamko.kotka.extensions.producedAs
|
|
||||||
import dev.adamko.kotka.extensions.tables.toStream
|
|
||||||
import dev.adamko.kotka.kxs.serde
|
import dev.adamko.kotka.kxs.serde
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineName
|
import kotlinx.coroutines.CoroutineName
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
@ -36,7 +26,6 @@ import org.apache.kafka.streams.StreamsBuilder
|
||||||
import org.apache.kafka.streams.StreamsConfig
|
import org.apache.kafka.streams.StreamsConfig
|
||||||
import org.apache.kafka.streams.Topology
|
import org.apache.kafka.streams.Topology
|
||||||
import org.apache.kafka.streams.TopologyDescription
|
import org.apache.kafka.streams.TopologyDescription
|
||||||
import org.apache.kafka.streams.kstream.KStream
|
|
||||||
import org.apache.kafka.streams.kstream.KTable
|
import org.apache.kafka.streams.kstream.KTable
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -62,54 +51,7 @@ internal class KafkatorioTopology(
|
||||||
|
|
||||||
private fun groupTilesMapChunks() {
|
private fun groupTilesMapChunks() {
|
||||||
val builder = StreamsBuilder()
|
val builder = StreamsBuilder()
|
||||||
|
val topology = groupMapChunks(builder)
|
||||||
|
|
||||||
val protosStream: KStream<FactorioServerId, FactorioPrototypes> =
|
|
||||||
builder.stream(
|
|
||||||
"kafkatorio.${KafkatorioPacket.PacketType.PROTOTYPES}.all",
|
|
||||||
consumedAs("consume.factorio-protos.all", jsonMapper.serde(), jsonMapper.serde())
|
|
||||||
)
|
|
||||||
val tileProtoColourDict = tileProtoColourDictionary(protosStream)
|
|
||||||
|
|
||||||
|
|
||||||
// val mapTilesStream: KStream<FactorioServerId, FactorioEvent> =
|
|
||||||
// builder.stream(
|
|
||||||
// "kafkatorio.${KafkatorioPacket.PacketType.EVENT}.${FactorioObjectData.ObjectName.LuaTiles}",
|
|
||||||
// consumedAs("consume.map-tiles", jsonMapper.serde(), jsonMapper.serde())
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// val mapChunksStream: KStream<FactorioServerId, FactorioEvent> =
|
|
||||||
// builder.stream(
|
|
||||||
// "kafkatorio.${KafkatorioPacket.PacketType.EVENT}.${FactorioObjectData.ObjectName.MapChunk}",
|
|
||||||
// consumedAs("consume.map-chunks", jsonMapper.serde(), jsonMapper.serde())
|
|
||||||
// )
|
|
||||||
|
|
||||||
val mapChunksUpdatesStream: KStream<FactorioServerId, FactorioEventUpdatePacket> =
|
|
||||||
builder.stream(
|
|
||||||
"kafkatorio.${KafkatorioPacket.PacketType.UPDATE}.${FactorioEventUpdate.FactorioEventUpdateType.MAP_CHUNK}",
|
|
||||||
consumedAs("consume.map-chunk-updates", jsonMapper.serde(), jsonMapper.serde())
|
|
||||||
)
|
|
||||||
|
|
||||||
val groupedMapChunkTiles: KTable<ServerMapChunkId, ServerMapChunkTiles<ColourHex>> =
|
|
||||||
groupTilesIntoChunksWithColours(
|
|
||||||
// mapTilesStream,
|
|
||||||
// mapChunksStream,
|
|
||||||
mapChunksUpdatesStream,
|
|
||||||
tileProtoColourDict,
|
|
||||||
)
|
|
||||||
|
|
||||||
groupedMapChunkTiles
|
|
||||||
.toStream("stream-grouped-map-tiles")
|
|
||||||
.to(
|
|
||||||
TOPIC_GROUPED_MAP_CHUNKS,
|
|
||||||
producedAs(
|
|
||||||
"produce.grouped-map-chunks",
|
|
||||||
kxsBinary.serde<ServerMapChunkId?>(),
|
|
||||||
kxsBinary.serde<ServerMapChunkTiles<ColourHex>?>()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val topology = builder.build()
|
|
||||||
launchTopology("groupTilesMapChunks", topology)
|
launchTopology("groupTilesMapChunks", topology)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,12 +60,14 @@ class KxsDataOutputEncoder(
|
||||||
override fun encodeLong(value: Long) = output.writeLong(value)
|
override fun encodeLong(value: Long) = output.writeLong(value)
|
||||||
override fun encodeShort(value: Short) = output.writeShort(value.toInt())
|
override fun encodeShort(value: Short) = output.writeShort(value.toInt())
|
||||||
override fun encodeString(value: String) = output.writeUTF(value)
|
override fun encodeString(value: String) = output.writeUTF(value)
|
||||||
|
// override fun encodeString(value: String) = encodeByteArray(value.toByteArray(Charsets.UTF_8))
|
||||||
|
|
||||||
override fun beginCollection(
|
override fun beginCollection(
|
||||||
descriptor: SerialDescriptor,
|
descriptor: SerialDescriptor,
|
||||||
collectionSize: Int
|
collectionSize: Int
|
||||||
): CompositeEncoder {
|
): CompositeEncoder {
|
||||||
encodeCompactSize(collectionSize)
|
encodeInt(collectionSize)
|
||||||
|
// encodeCompactSize(collectionSize)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,6 +115,7 @@ class KxsDataInputDecoder(
|
||||||
override fun decodeLong(): Long = input.readLong()
|
override fun decodeLong(): Long = input.readLong()
|
||||||
override fun decodeShort(): Short = input.readShort()
|
override fun decodeShort(): Short = input.readShort()
|
||||||
override fun decodeString(): String = input.readUTF()
|
override fun decodeString(): String = input.readUTF()
|
||||||
|
// override fun decodeString(): String = decodeByteArray().toString(Charsets.UTF_8)
|
||||||
|
|
||||||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
||||||
if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
|
if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
|
||||||
|
|
@ -125,7 +128,8 @@ class KxsDataInputDecoder(
|
||||||
override fun decodeSequentially(): Boolean = true
|
override fun decodeSequentially(): Boolean = true
|
||||||
|
|
||||||
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
|
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
|
||||||
decodeCompactSize().also { elementsCount = it }
|
decodeInt().also { elementsCount = it }
|
||||||
|
// decodeCompactSize().also { elementsCount = it }
|
||||||
|
|
||||||
override fun decodeNotNullMark(): Boolean = decodeBoolean()
|
override fun decodeNotNullMark(): Boolean = decodeBoolean()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import dev.adamko.kafkatorio.events.schema.MapChunkPosition
|
||||||
import dev.adamko.kafkatorio.events.schema.MapTilePosition
|
import dev.adamko.kafkatorio.events.schema.MapTilePosition
|
||||||
import dev.adamko.kafkatorio.events.schema.SurfaceIndex
|
import dev.adamko.kafkatorio.events.schema.SurfaceIndex
|
||||||
import dev.adamko.kafkatorio.events.schema.converters.toMapChunkPosition
|
import dev.adamko.kafkatorio.events.schema.converters.toMapChunkPosition
|
||||||
|
import dev.adamko.kotka.extensions.streams.*
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
@ -52,7 +53,7 @@ fun saveMapTiles(
|
||||||
|
|
||||||
serverMapTable
|
serverMapTable
|
||||||
.toStream()
|
.toStream()
|
||||||
.foreach { _, value ->
|
.forEach("save-chunked-tiles") { _, value ->
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
runBlocking(Dispatchers.Default) {
|
runBlocking(Dispatchers.Default) {
|
||||||
println("emitting chunkedTiles ${value.chunkId} tiles: ${value.map.size}")
|
println("emitting chunkedTiles ${value.chunkId} tiles: ${value.map.size}")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
package dev.adamko.kafkatorio.processor.topology
|
package dev.adamko.kafkatorio.processor.topology
|
||||||
|
|
||||||
import dev.adamko.kafkatorio.events.schema.ColourHex
|
import dev.adamko.kafkatorio.events.schema.ColourHex
|
||||||
|
import dev.adamko.kafkatorio.events.schema.FactorioEventUpdate
|
||||||
import dev.adamko.kafkatorio.events.schema.FactorioEventUpdatePacket
|
import dev.adamko.kafkatorio.events.schema.FactorioEventUpdatePacket
|
||||||
|
import dev.adamko.kafkatorio.events.schema.FactorioPrototypes
|
||||||
|
import dev.adamko.kafkatorio.events.schema.KafkatorioPacket
|
||||||
import dev.adamko.kafkatorio.events.schema.MapChunkPosition
|
import dev.adamko.kafkatorio.events.schema.MapChunkPosition
|
||||||
import dev.adamko.kafkatorio.events.schema.MapChunkUpdate
|
import dev.adamko.kafkatorio.events.schema.MapChunkUpdate
|
||||||
import dev.adamko.kafkatorio.events.schema.MapTile
|
import dev.adamko.kafkatorio.events.schema.MapTile
|
||||||
|
|
@ -10,16 +13,20 @@ import dev.adamko.kafkatorio.events.schema.MapTilePosition
|
||||||
import dev.adamko.kafkatorio.events.schema.MapTiles
|
import dev.adamko.kafkatorio.events.schema.MapTiles
|
||||||
import dev.adamko.kafkatorio.events.schema.SurfaceIndex
|
import dev.adamko.kafkatorio.events.schema.SurfaceIndex
|
||||||
import dev.adamko.kafkatorio.events.schema.converters.toMapChunkPosition
|
import dev.adamko.kafkatorio.events.schema.converters.toMapChunkPosition
|
||||||
|
import dev.adamko.kafkatorio.processor.KafkatorioTopology
|
||||||
|
import dev.adamko.kafkatorio.processor.serdes.jsonMapper
|
||||||
import dev.adamko.kafkatorio.processor.serdes.kxsBinary
|
import dev.adamko.kafkatorio.processor.serdes.kxsBinary
|
||||||
|
import dev.adamko.kotka.extensions.consumedAs
|
||||||
import dev.adamko.kotka.extensions.groupedAs
|
import dev.adamko.kotka.extensions.groupedAs
|
||||||
import dev.adamko.kotka.extensions.materializedAs
|
|
||||||
import dev.adamko.kotka.extensions.materializedWith
|
import dev.adamko.kotka.extensions.materializedWith
|
||||||
import dev.adamko.kotka.extensions.streams.flatMap
|
import dev.adamko.kotka.extensions.producedAs
|
||||||
import dev.adamko.kotka.extensions.streams.mapValues
|
import dev.adamko.kotka.extensions.streams.*
|
||||||
import dev.adamko.kotka.extensions.streams.reduce
|
|
||||||
import dev.adamko.kotka.extensions.tables.join
|
import dev.adamko.kotka.extensions.tables.join
|
||||||
|
import dev.adamko.kotka.extensions.tables.toStream
|
||||||
import dev.adamko.kotka.kxs.serde
|
import dev.adamko.kotka.kxs.serde
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import org.apache.kafka.streams.StreamsBuilder
|
||||||
|
import org.apache.kafka.streams.Topology
|
||||||
import org.apache.kafka.streams.kstream.KStream
|
import org.apache.kafka.streams.kstream.KStream
|
||||||
import org.apache.kafka.streams.kstream.KTable
|
import org.apache.kafka.streams.kstream.KTable
|
||||||
|
|
||||||
|
|
@ -42,7 +49,74 @@ data class ServerMapChunkTiles<Data>(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun groupTilesIntoChunksWithColours(
|
fun groupMapChunks(
|
||||||
|
builder: StreamsBuilder,
|
||||||
|
prototypesTopicName: String = "kafkatorio.${KafkatorioPacket.PacketType.PROTOTYPES}.all",
|
||||||
|
mapChunkUpdatesTopicName: String = "kafkatorio.${KafkatorioPacket.PacketType.UPDATE}.${FactorioEventUpdate.FactorioEventUpdateType.MAP_CHUNK}",
|
||||||
|
): Topology {
|
||||||
|
|
||||||
|
val protosStream: KStream<FactorioServerId, FactorioPrototypes> =
|
||||||
|
builder.stream(
|
||||||
|
prototypesTopicName,
|
||||||
|
consumedAs("consume.factorio-protos.all", jsonMapper.serde(), jsonMapper.serde())
|
||||||
|
)
|
||||||
|
val tileProtoColourDict: KTable<FactorioServerId, TileColourDict> =
|
||||||
|
tileProtoColourDictionary(protosStream)
|
||||||
|
|
||||||
|
|
||||||
|
// val mapTilesStream: KStream<FactorioServerId, FactorioEvent> =
|
||||||
|
// builder.stream(
|
||||||
|
// "kafkatorio.${KafkatorioPacket.PacketType.EVENT}.${FactorioObjectData.ObjectName.LuaTiles}",
|
||||||
|
// consumedAs("consume.map-tiles", jsonMapper.serde(), jsonMapper.serde())
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// val mapChunksStream: KStream<FactorioServerId, FactorioEvent> =
|
||||||
|
// builder.stream(
|
||||||
|
// "kafkatorio.${KafkatorioPacket.PacketType.EVENT}.${FactorioObjectData.ObjectName.MapChunk}",
|
||||||
|
// consumedAs("consume.map-chunks", jsonMapper.serde(), jsonMapper.serde())
|
||||||
|
// )
|
||||||
|
|
||||||
|
val mapChunksUpdatesStream: KStream<FactorioServerId, FactorioEventUpdatePacket> =
|
||||||
|
builder.stream(
|
||||||
|
mapChunkUpdatesTopicName,
|
||||||
|
consumedAs(
|
||||||
|
"consume.map-chunk-updates",
|
||||||
|
jsonMapper.serde<FactorioServerId>(),
|
||||||
|
jsonMapper.serde<FactorioEventUpdatePacket>(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// .repartition(
|
||||||
|
// repartitionedAs(
|
||||||
|
// "map-chunk-updates.repartition.change-serde",
|
||||||
|
// kxsBinary.serde<FactorioServerId>(),
|
||||||
|
// kxsBinary.serde<FactorioEventUpdatePacket>(),
|
||||||
|
// numberOfPartitions = 3,
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
|
||||||
|
val groupedMapChunkTiles: KTable<ServerMapChunkId, ServerMapChunkTiles<ColourHex>> =
|
||||||
|
groupTilesIntoChunksWithColours(
|
||||||
|
// mapTilesStream,
|
||||||
|
// mapChunksStream,
|
||||||
|
mapChunksUpdatesStream,
|
||||||
|
tileProtoColourDict,
|
||||||
|
)
|
||||||
|
|
||||||
|
groupedMapChunkTiles
|
||||||
|
.toStream("stream-grouped-map-tiles")
|
||||||
|
.to(
|
||||||
|
KafkatorioTopology.TOPIC_GROUPED_MAP_CHUNKS,
|
||||||
|
producedAs(
|
||||||
|
"produce.grouped-map-chunks",
|
||||||
|
kxsBinary.serde<ServerMapChunkId?>(),
|
||||||
|
kxsBinary.serde<ServerMapChunkTiles<ColourHex>?>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun groupTilesIntoChunksWithColours(
|
||||||
// mapTilePacketsStream: KStream<FactorioServerId, FactorioEvent>,
|
// mapTilePacketsStream: KStream<FactorioServerId, FactorioEvent>,
|
||||||
// mapChunksPacketsStream: KStream<FactorioServerId, FactorioEvent>,
|
// mapChunksPacketsStream: KStream<FactorioServerId, FactorioEvent>,
|
||||||
mapChunkUpdatePacketsStream: KStream<FactorioServerId, FactorioEventUpdatePacket>,
|
mapChunkUpdatePacketsStream: KStream<FactorioServerId, FactorioEventUpdatePacket>,
|
||||||
|
|
@ -107,7 +181,7 @@ fun groupTilesIntoChunksWithColours(
|
||||||
// .merge("merge-mapTilesStream-with-mapChunkTilesStream", mapChunkTilesStream)
|
// .merge("merge-mapTilesStream-with-mapChunkTilesStream", mapChunkTilesStream)
|
||||||
// .merge("merge-mapTilesStream-with-mapChunkUpdatesStream", mapChunkUpdatesStream)
|
// .merge("merge-mapTilesStream-with-mapChunkUpdatesStream", mapChunkUpdatesStream)
|
||||||
// .filter { _, value -> !value?.tiles.isNullOrEmpty() }
|
// .filter { _, value -> !value?.tiles.isNullOrEmpty() }
|
||||||
.flatMap("server-map-data.tiles.flatMapByChunk") { key: FactorioServerId, mapTiles: MapTiles ->
|
.flatMap("server-map-data.tiles.flatMapByChunk") { serverId: FactorioServerId, mapTiles: MapTiles ->
|
||||||
|
|
||||||
val standardChunkSize = ChunkSize.MAX
|
val standardChunkSize = ChunkSize.MAX
|
||||||
|
|
||||||
|
|
@ -119,10 +193,10 @@ fun groupTilesIntoChunksWithColours(
|
||||||
.map { (chunkPos, tiles) ->
|
.map { (chunkPos, tiles) ->
|
||||||
|
|
||||||
val chunkId = ServerMapChunkId(
|
val chunkId = ServerMapChunkId(
|
||||||
key,
|
serverId = serverId,
|
||||||
chunkPos,
|
chunkPosition = chunkPos,
|
||||||
mapTiles.surfaceIndex,
|
surfaceIndex = mapTiles.surfaceIndex,
|
||||||
standardChunkSize,
|
chunkSize = standardChunkSize,
|
||||||
)
|
)
|
||||||
|
|
||||||
val tilesMap = tiles.associate { it.position to TileProtoHashCode(it) }
|
val tilesMap = tiles.associate { it.position to TileProtoHashCode(it) }
|
||||||
|
|
@ -132,20 +206,24 @@ fun groupTilesIntoChunksWithColours(
|
||||||
chunkId to chunkTiles
|
chunkId to chunkTiles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// .filter("server-map-data.tiles.filter-not-empty") { _, value ->
|
.filter("server-map-data.tiles.filter-not-empty") { _, value ->
|
||||||
// value.map.isNotEmpty()
|
value.map.isNotEmpty()
|
||||||
// }
|
}
|
||||||
.groupByKey(
|
.groupByKey(
|
||||||
groupedAs(
|
groupedAs(
|
||||||
"server-map-data.tiles.group",
|
"server-map-data.tiles.group",
|
||||||
kxsBinary.serde<ServerMapChunkId>(),
|
kxsBinary.serde<ServerMapChunkId?>(),
|
||||||
kxsBinary.serde<ServerMapChunkTiles<TileProtoHashCode>>()
|
kxsBinary.serde<ServerMapChunkTiles<TileProtoHashCode>?>(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.reduce(
|
.reduce(
|
||||||
"server-map-data.tiles.reduce",
|
"server-map-data.tiles.reduce", materializedWith(
|
||||||
materializedAs("server-map-data.tiles.store", kxsBinary.serde(), kxsBinary.serde()),
|
// "server-map-data.tiles.store",
|
||||||
) { a, b -> a + b }
|
kxsBinary.serde<ServerMapChunkId>(),
|
||||||
|
kxsBinary.serde<ServerMapChunkTiles<TileProtoHashCode>>(),
|
||||||
|
),
|
||||||
|
ServerMapChunkTiles<TileProtoHashCode>::plus
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
val colourisedChunkTable: KTable<ServerMapChunkId, ServerMapChunkTiles<ColourHex>> =
|
val colourisedChunkTable: KTable<ServerMapChunkId, ServerMapChunkTiles<ColourHex>> =
|
||||||
|
|
@ -153,7 +231,10 @@ fun groupTilesIntoChunksWithColours(
|
||||||
.join(
|
.join(
|
||||||
other = tileProtoColourDict,
|
other = tileProtoColourDict,
|
||||||
name = "server-map-data.join-tiles-with-prototypes",
|
name = "server-map-data.join-tiles-with-prototypes",
|
||||||
materialized = materializedWith(kxsBinary.serde(), kxsBinary.serde()),
|
materialized = materializedWith(
|
||||||
|
kxsBinary.serde<ServerMapChunkId>(),
|
||||||
|
kxsBinary.serde<ServerMapChunkTiles<ColourHex>>(),
|
||||||
|
),
|
||||||
foreignKeyExtractor = { chunkTiles: ServerMapChunkTiles<TileProtoHashCode> -> chunkTiles.chunkId.serverId }
|
foreignKeyExtractor = { chunkTiles: ServerMapChunkTiles<TileProtoHashCode> -> chunkTiles.chunkId.serverId }
|
||||||
) { tiles: ServerMapChunkTiles<TileProtoHashCode>, colourDict: TileColourDict ->
|
) { tiles: ServerMapChunkTiles<TileProtoHashCode>, colourDict: TileColourDict ->
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ import dev.adamko.kafkatorio.events.schema.MapTile
|
||||||
import dev.adamko.kafkatorio.events.schema.MapTilePrototype
|
import dev.adamko.kafkatorio.events.schema.MapTilePrototype
|
||||||
import dev.adamko.kafkatorio.events.schema.converters.toHex
|
import dev.adamko.kafkatorio.events.schema.converters.toHex
|
||||||
import dev.adamko.kafkatorio.processor.serdes.jsonMapper
|
import dev.adamko.kafkatorio.processor.serdes.jsonMapper
|
||||||
|
import dev.adamko.kafkatorio.processor.serdes.kxsBinary
|
||||||
import dev.adamko.kotka.extensions.materializedAs
|
import dev.adamko.kotka.extensions.materializedAs
|
||||||
|
import dev.adamko.kotka.extensions.materializedWith
|
||||||
import dev.adamko.kotka.extensions.streams.filter
|
import dev.adamko.kotka.extensions.streams.filter
|
||||||
import dev.adamko.kotka.extensions.streams.mapValues
|
import dev.adamko.kotka.extensions.streams.mapValues
|
||||||
import dev.adamko.kotka.extensions.streams.toTable
|
import dev.adamko.kotka.extensions.streams.toTable
|
||||||
|
|
@ -54,10 +56,10 @@ fun tileProtoColourDictionary(
|
||||||
}
|
}
|
||||||
.toTable(
|
.toTable(
|
||||||
"server-map-data.tile-prototypes",
|
"server-map-data.tile-prototypes",
|
||||||
materializedAs(
|
materializedWith(
|
||||||
"server-map-data.tile-prototypes.store",
|
// "server-map-data.tile-prototypes.store",
|
||||||
jsonMapper.serde(),
|
kxsBinary.serde(),
|
||||||
jsonMapper.serde(),
|
kxsBinary.serde(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
package dev.adamko.kafkatorio.processor.topology
|
||||||
|
|
||||||
|
import dev.adamko.kafkatorio.events.schema.Colour
|
||||||
|
import dev.adamko.kafkatorio.events.schema.ColourHex
|
||||||
|
import dev.adamko.kafkatorio.events.schema.FactorioEventUpdatePacket
|
||||||
|
import dev.adamko.kafkatorio.events.schema.FactorioPrototypes
|
||||||
|
import dev.adamko.kafkatorio.events.schema.MapChunkPosition
|
||||||
|
import dev.adamko.kafkatorio.events.schema.MapChunkUpdate
|
||||||
|
import dev.adamko.kafkatorio.events.schema.MapTileDictionary
|
||||||
|
import dev.adamko.kafkatorio.events.schema.MapTilePosition
|
||||||
|
import dev.adamko.kafkatorio.events.schema.MapTilePrototype
|
||||||
|
import dev.adamko.kafkatorio.events.schema.PrototypeKey
|
||||||
|
import dev.adamko.kafkatorio.events.schema.PrototypeName
|
||||||
|
import dev.adamko.kafkatorio.events.schema.SurfaceIndex
|
||||||
|
import dev.adamko.kafkatorio.events.schema.Tick
|
||||||
|
import dev.adamko.kafkatorio.processor.KafkatorioTopology
|
||||||
|
import dev.adamko.kafkatorio.processor.serdes.jsonMapper
|
||||||
|
import dev.adamko.kafkatorio.processor.serdes.kxsBinary
|
||||||
|
import dev.adamko.kotka.kxs.serde
|
||||||
|
import io.kotest.assertions.withClue
|
||||||
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.matchers.collections.shouldHaveSize
|
||||||
|
import io.kotest.matchers.equality.shouldBeEqualToComparingFields
|
||||||
|
import io.kotest.matchers.nulls.shouldNotBeNull
|
||||||
|
import io.kotest.matchers.should
|
||||||
|
import org.apache.kafka.streams.StreamsBuilder
|
||||||
|
import org.apache.kafka.streams.TestInputTopic
|
||||||
|
import org.apache.kafka.streams.TestOutputTopic
|
||||||
|
import org.apache.kafka.streams.TopologyTestDriver
|
||||||
|
|
||||||
|
|
||||||
|
class GroupMapChunksTest : FunSpec({
|
||||||
|
|
||||||
|
test("basic test") {
|
||||||
|
|
||||||
|
val prototypesTopicName = "prototypes-topic"
|
||||||
|
val mapChunkUpdatesTopicName = "map-chunk-updates-topic"
|
||||||
|
|
||||||
|
val topology = groupMapChunks(
|
||||||
|
StreamsBuilder(),
|
||||||
|
prototypesTopicName,
|
||||||
|
mapChunkUpdatesTopicName,
|
||||||
|
)
|
||||||
|
|
||||||
|
val testDriver = TopologyTestDriver(topology)
|
||||||
|
|
||||||
|
val prototypesTestInputTopic: TestInputTopic<FactorioServerId, FactorioPrototypes> =
|
||||||
|
testDriver.createInputTopic(
|
||||||
|
prototypesTopicName,
|
||||||
|
jsonMapper.serde<FactorioServerId>().serializer(),
|
||||||
|
jsonMapper.serde<FactorioPrototypes>().serializer(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val serverId = FactorioServerId("test-server-id")
|
||||||
|
|
||||||
|
prototypesTestInputTopic.pipeInput(
|
||||||
|
serverId,
|
||||||
|
FactorioPrototypes(
|
||||||
|
modVersion = "1.2.3",
|
||||||
|
tick = Tick(456u),
|
||||||
|
listOf(
|
||||||
|
MapTilePrototype(
|
||||||
|
PrototypeName("fake-proto"),
|
||||||
|
layer = 1u,
|
||||||
|
mapColour = Colour(0.3f, 0.2f, 0.1f, 1.0f),
|
||||||
|
collisionMasks = listOf("fake-mask"),
|
||||||
|
order = "fake-order",
|
||||||
|
canBeMined = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val mapChunkUpdatesInputTopic: TestInputTopic<FactorioServerId, FactorioEventUpdatePacket> =
|
||||||
|
testDriver.createInputTopic(
|
||||||
|
mapChunkUpdatesTopicName,
|
||||||
|
jsonMapper.serde<FactorioServerId>().serializer(),
|
||||||
|
jsonMapper.serde<FactorioEventUpdatePacket>().serializer(),
|
||||||
|
)
|
||||||
|
mapChunkUpdatesInputTopic.pipeInput(
|
||||||
|
serverId,
|
||||||
|
FactorioEventUpdatePacket(
|
||||||
|
modVersion = "1.2.3",
|
||||||
|
tick = Tick(854u),
|
||||||
|
update = MapChunkUpdate(
|
||||||
|
MapChunkPosition(11, 22),
|
||||||
|
SurfaceIndex(1u),
|
||||||
|
tileDictionary = MapTileDictionary(
|
||||||
|
tilesXY = mapOf(
|
||||||
|
"1" to mapOf("2" to PrototypeKey("33"))
|
||||||
|
),
|
||||||
|
protos = mapOf(PrototypeKey("33") to PrototypeName("fake-proto"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val outputTopic: TestOutputTopic<ServerMapChunkId?, ServerMapChunkTiles<ColourHex>?> =
|
||||||
|
testDriver
|
||||||
|
.createOutputTopic(
|
||||||
|
KafkatorioTopology.TOPIC_GROUPED_MAP_CHUNKS,
|
||||||
|
kxsBinary.serde<ServerMapChunkId?>().deserializer(),
|
||||||
|
kxsBinary.serde<ServerMapChunkTiles<ColourHex>?>().deserializer(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val records = outputTopic.readRecordsToList()
|
||||||
|
|
||||||
|
withClue(records) {
|
||||||
|
records shouldHaveSize 1
|
||||||
|
|
||||||
|
records.map { it.key to it.value }.first() should { (key, value) ->
|
||||||
|
key.shouldNotBeNull()
|
||||||
|
key shouldBeEqualToComparingFields ServerMapChunkId(
|
||||||
|
serverId = serverId,
|
||||||
|
chunkPosition = MapChunkPosition(0, 0),
|
||||||
|
surfaceIndex = SurfaceIndex(1u),
|
||||||
|
chunkSize = ChunkSize.CHUNK_512,
|
||||||
|
)
|
||||||
|
value.shouldNotBeNull()
|
||||||
|
value shouldBeEqualToComparingFields ServerMapChunkTiles(
|
||||||
|
chunkId = key,
|
||||||
|
map = mapOf(
|
||||||
|
MapTilePosition(1, 2) to ColourHex(76u, 51u, 25u, 255u)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
Loading…
Add table
Add a link
Reference in a new issue