workinggggg!!!!!!!
- fix map tiles proto colours KTable - test and tidy up Colour/ColourHex - reset event-server application
This commit is contained in:
parent
bdb2d9577f
commit
5a59b3bd6d
13 changed files with 211 additions and 97 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
package dev.adamko.kafkatorio.events.schema.converters
|
package dev.adamko.kafkatorio.events.schema.converters
|
||||||
|
|
||||||
import dev.adamko.kafkatorio.events.schema.Colour
|
import dev.adamko.kafkatorio.events.schema.Colour
|
||||||
|
import dev.adamko.kafkatorio.events.schema.ColourHex
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -15,39 +16,12 @@ fun Colour.isHexadecimal(): Boolean {
|
||||||
/** True if all values are between `[0..1]`. */
|
/** True if all values are between `[0..1]`. */
|
||||||
fun Colour.isPercentage(): Boolean = !isHexadecimal()
|
fun Colour.isPercentage(): Boolean = !isHexadecimal()
|
||||||
|
|
||||||
fun Colour.toHexadecimal() = if (isPercentage()) {
|
|
||||||
Colour(
|
|
||||||
(red * 255).coerceIn(0f..255f),
|
|
||||||
(green * 255).coerceIn(0f..255f),
|
|
||||||
(blue * 255).coerceIn(0f..255f),
|
|
||||||
(alpha * 255).coerceIn(0f..255f),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Colour(
|
|
||||||
red.coerceIn(0f..255f),
|
|
||||||
green.coerceIn(0f..255f),
|
|
||||||
blue.coerceIn(0f..255f),
|
|
||||||
alpha.coerceIn(0f..255f),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Colour.toPercentage() = if (isPercentage()) {
|
|
||||||
this
|
|
||||||
} else {
|
|
||||||
Colour(
|
|
||||||
(red / 255).coerceIn(0f..1f),
|
|
||||||
(green / 255).coerceIn(0f..1f),
|
|
||||||
(blue / 255).coerceIn(0f..1f),
|
|
||||||
(alpha / 255).coerceIn(0f..1f),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Colour.toHexString(includeAlpha: Boolean = false): String {
|
fun Colour.toHexString(includeAlpha: Boolean = false): String {
|
||||||
|
|
||||||
val hexConverter: Float.() -> String = if (isHexadecimal()) {
|
val hexConverter: Float.() -> String = when {
|
||||||
Float::decimalToHex
|
isHexadecimal() -> Float::hexadecimalToString
|
||||||
} else {
|
else -> Float::percentageToHexadecimalString
|
||||||
Float::percentageToHex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val length = if (includeAlpha) 9 else 7
|
val length = if (includeAlpha) 9 else 7
|
||||||
|
|
@ -62,8 +36,51 @@ fun Colour.toHexString(includeAlpha: Boolean = false): String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Float.decimalToHex(): String =
|
fun Float.hexadecimalToString(): String =
|
||||||
roundToInt().toString(16).padStart(2, ' ')
|
roundToInt().toString(16).padStart(2, ' ')
|
||||||
|
|
||||||
fun Float.percentageToHex(): String =
|
fun Float.percentageToHexadecimalString(): String =
|
||||||
times(255).roundToInt().toString(16).padStart(2, ' ')
|
times(255).roundToInt().toString(16).padStart(2, ' ')
|
||||||
|
|
||||||
|
|
||||||
|
private val ubyteMaxFloat = UByte.MAX_VALUE.toFloat()
|
||||||
|
private val ubyteMinFloat = UByte.MIN_VALUE.toFloat()
|
||||||
|
private val ubyteRangeFloat = ubyteMinFloat..ubyteMaxFloat
|
||||||
|
|
||||||
|
|
||||||
|
private fun Float.toColourHex() = (this * ubyteMaxFloat)
|
||||||
|
.coerceIn(ubyteRangeFloat)
|
||||||
|
.toUInt()
|
||||||
|
.toUByte()
|
||||||
|
|
||||||
|
|
||||||
|
private fun Float.coerceToColourHex() = coerceIn(ubyteRangeFloat)
|
||||||
|
.toUInt()
|
||||||
|
.toUByte()
|
||||||
|
|
||||||
|
|
||||||
|
fun Colour.toHex(): ColourHex {
|
||||||
|
|
||||||
|
val converter: Float.() -> UByte = when {
|
||||||
|
isPercentage() -> Float::toColourHex
|
||||||
|
else -> Float::coerceToColourHex
|
||||||
|
}
|
||||||
|
|
||||||
|
return ColourHex(
|
||||||
|
red.converter(),
|
||||||
|
green.converter(),
|
||||||
|
blue.converter(),
|
||||||
|
alpha.converter(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun UByte.toPercentileFloat() = (this.toFloat() / ubyteMaxFloat).coerceIn(0f..1f)
|
||||||
|
|
||||||
|
fun ColourHex.toPercentile(): Colour {
|
||||||
|
return Colour(
|
||||||
|
red.toPercentileFloat(),
|
||||||
|
green.toPercentileFloat(),
|
||||||
|
blue.toPercentileFloat(),
|
||||||
|
alpha.toPercentileFloat(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package dev.adamko.kafkatorio.events.schema.converters
|
package dev.adamko.kafkatorio.events.schema.converters
|
||||||
|
|
||||||
import dev.adamko.kafkatorio.events.schema.MAP_CHUNK_SIZE
|
import dev.adamko.kafkatorio.events.schema.MAP_CHUNK_SIZE
|
||||||
import dev.adamko.kafkatorio.events.schema.MapBoundingBox
|
|
||||||
import dev.adamko.kafkatorio.events.schema.MapChunkPosition
|
import dev.adamko.kafkatorio.events.schema.MapChunkPosition
|
||||||
import dev.adamko.kafkatorio.events.schema.MapEntityPosition
|
import dev.adamko.kafkatorio.events.schema.MapEntityPosition
|
||||||
import dev.adamko.kafkatorio.events.schema.MapTilePosition
|
import dev.adamko.kafkatorio.events.schema.MapTilePosition
|
||||||
|
|
@ -17,7 +16,7 @@ operator fun MapTilePosition.plus(addend: Int) =
|
||||||
MapTilePosition(x + addend, y + addend)
|
MapTilePosition(x + addend, y + addend)
|
||||||
|
|
||||||
|
|
||||||
fun MapTilePosition.toMapChunkPosition( chunkSize: Int = MAP_CHUNK_SIZE) =
|
fun MapTilePosition.toMapChunkPosition(chunkSize: Int = MAP_CHUNK_SIZE) =
|
||||||
MapChunkPosition(
|
MapChunkPosition(
|
||||||
floor(x.toDouble() / chunkSize.toDouble()).toInt(),
|
floor(x.toDouble() / chunkSize.toDouble()).toInt(),
|
||||||
floor(y.toDouble() / chunkSize.toDouble()).toInt(),
|
floor(y.toDouble() / chunkSize.toDouble()).toInt(),
|
||||||
|
|
@ -27,7 +26,7 @@ fun MapTilePosition.toMapChunkPosition( chunkSize: Int = MAP_CHUNK_SIZE) =
|
||||||
fun MapEntityPosition.toMapTilePosition() =
|
fun MapEntityPosition.toMapTilePosition() =
|
||||||
MapChunkPosition(floor(x).toInt(), floor(y).toInt())
|
MapChunkPosition(floor(x).toInt(), floor(y).toInt())
|
||||||
|
|
||||||
fun MapEntityPosition.toMapChunkPosition( chunkSize: Int = MAP_CHUNK_SIZE) =
|
fun MapEntityPosition.toMapChunkPosition(chunkSize: Int = MAP_CHUNK_SIZE) =
|
||||||
MapChunkPosition(
|
MapChunkPosition(
|
||||||
floor(x / chunkSize.toDouble()).toInt(),
|
floor(x / chunkSize.toDouble()).toInt(),
|
||||||
floor(y / chunkSize.toDouble()).toInt(),
|
floor(y / chunkSize.toDouble()).toInt(),
|
||||||
|
|
@ -44,8 +43,8 @@ operator fun MapChunkPosition.minus(subtrahend: Int) =
|
||||||
MapChunkPosition(x - subtrahend, y - subtrahend)
|
MapChunkPosition(x - subtrahend, y - subtrahend)
|
||||||
|
|
||||||
|
|
||||||
fun MapChunkPosition.leftTopTile( chunkSize: Int = MAP_CHUNK_SIZE): MapTilePosition =
|
fun MapChunkPosition.leftTopTile(chunkSize: Int = MAP_CHUNK_SIZE): MapTilePosition =
|
||||||
MapTilePosition(
|
MapTilePosition(
|
||||||
x * chunkSize,
|
x * chunkSize,
|
||||||
y * chunkSize,
|
y * chunkSize,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package dev.adamko.kafkatorio.events.schema
|
package dev.adamko.kafkatorio.events.schema
|
||||||
|
|
||||||
import dev.adamko.kafkatorio.events.schema.converters.toHexadecimal
|
|
||||||
import kotlinx.serialization.EncodeDefault
|
import kotlinx.serialization.EncodeDefault
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
@ -69,20 +68,10 @@ data class Colour(
|
||||||
val blue: Float = 0f,
|
val blue: Float = 0f,
|
||||||
@EncodeDefault
|
@EncodeDefault
|
||||||
val alpha: Float = 1f,
|
val alpha: Float = 1f,
|
||||||
) {
|
)
|
||||||
|
|
||||||
fun toHex() = toHexadecimal().let {
|
|
||||||
ColourHex(
|
|
||||||
it.red.toUInt().toUByte(),
|
|
||||||
it.green.toUInt().toUByte(),
|
|
||||||
it.blue.toUInt().toUByte(),
|
|
||||||
it.alpha.toUInt().toUByte(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Size-efficient version of [Colour] */
|
/** Size-efficient version of [Colour] (`4*4` bytes vs `4*1` bytes) */
|
||||||
@Serializable
|
@Serializable
|
||||||
data class ColourHex(
|
data class ColourHex(
|
||||||
@EncodeDefault
|
@EncodeDefault
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
package dev.adamko.kafkatorio.events.schema
|
||||||
|
|
||||||
|
import dev.adamko.kafkatorio.events.schema.converters.toHex
|
||||||
|
import dev.adamko.kafkatorio.events.schema.converters.toPercentile
|
||||||
|
import io.kotest.assertions.withClue
|
||||||
|
import io.kotest.core.spec.style.BehaviorSpec
|
||||||
|
import io.kotest.matchers.floats.shouldBeWithinPercentageOf
|
||||||
|
import io.kotest.matchers.should
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import io.kotest.property.checkAll
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
|
||||||
|
class ColourHexTest : BehaviorSpec({
|
||||||
|
|
||||||
|
Given("colour json") {
|
||||||
|
|
||||||
|
//Language=JSON
|
||||||
|
val colourJson = """
|
||||||
|
{
|
||||||
|
"red": 0.1,
|
||||||
|
"green": 0.4,
|
||||||
|
"blue": 0.7,
|
||||||
|
"alpha": 0.6
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
val colour: Colour = jsonMapperKafkatorio.decodeFromString(colourJson)
|
||||||
|
|
||||||
|
Then("expect colour is decoded") {
|
||||||
|
colour shouldBe Colour(
|
||||||
|
0.1f,
|
||||||
|
0.4f,
|
||||||
|
0.7f,
|
||||||
|
0.6f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Then("expect Colour can be converted to ColourHex and back") {
|
||||||
|
val colourHex = colour.toHex()
|
||||||
|
colourHex shouldBe ColourHex(
|
||||||
|
25u,
|
||||||
|
102u,
|
||||||
|
178u,
|
||||||
|
153u,
|
||||||
|
)
|
||||||
|
|
||||||
|
colourHex.toPercentile() should { back ->
|
||||||
|
withClue(back) {
|
||||||
|
|
||||||
|
val tolerance = 2.6 // 2.6%, because 255/100 = 2.55
|
||||||
|
|
||||||
|
withClue("red") { back.red.shouldBeWithinPercentageOf(0.1f, tolerance) }
|
||||||
|
withClue("green") { back.green.shouldBeWithinPercentageOf(0.4f, tolerance) }
|
||||||
|
withClue("blue") { back.blue.shouldBeWithinPercentageOf(0.7f, tolerance) }
|
||||||
|
withClue("alpha") { back.alpha.shouldBeWithinPercentageOf(0.6f, tolerance) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Given("any colour json") {
|
||||||
|
Then("expect can always convert") {
|
||||||
|
|
||||||
|
checkAll<UByte, UByte, UByte, UByte> { r, g, b, a ->
|
||||||
|
|
||||||
|
//Language=JSON
|
||||||
|
val colourJson = """
|
||||||
|
{
|
||||||
|
"red": ${r.toFloat() / UByte.MAX_VALUE.toFloat()},
|
||||||
|
"green": ${g.toFloat() / UByte.MAX_VALUE.toFloat()},
|
||||||
|
"blue": ${b.toFloat() / UByte.MAX_VALUE.toFloat()},
|
||||||
|
"alpha": ${a.toFloat() / UByte.MAX_VALUE.toFloat()}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
val colour: Colour = jsonMapperKafkatorio.decodeFromString(colourJson)
|
||||||
|
val colourHex = colour.toHex()
|
||||||
|
colourHex shouldBe ColourHex(r, g, b, a)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
import org.jetbrains.kotlin.util.parseSpaceSeparatedArgs
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
dev.adamko.kafkatorio.lang.`kotlin-jvm`
|
dev.adamko.kafkatorio.lang.`kotlin-jvm`
|
||||||
|
|
@ -64,3 +65,19 @@ tasks.withType<KotlinCompile> {
|
||||||
"-opt-in=kotlinx.coroutines.FlowPreview",
|
"-opt-in=kotlinx.coroutines.FlowPreview",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val kafkatorioEventsServerKafkaForceReset by tasks.registering(Exec::class) {
|
||||||
|
group = project.name
|
||||||
|
|
||||||
|
val cmd = """
|
||||||
|
/kafka/bin/kafka-streams-application-reset.sh --application-id kafkatorio-events-processor --input-topics factorio-server-log --force
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
logging.captureStandardOutput(LogLevel.LIFECYCLE)
|
||||||
|
|
||||||
|
commandLine = parseSpaceSeparatedArgs(""" docker exec -d kafka $cmd """)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.run.configure {
|
||||||
|
dependsOn(kafkatorioEventsServerKafkaForceReset)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@ import org.apache.kafka.clients.admin.TopicListing
|
||||||
|
|
||||||
private val logger = KotlinLogging.logger {}
|
private val logger = KotlinLogging.logger {}
|
||||||
|
|
||||||
fun main() {
|
//fun main() {
|
||||||
CreateTopics.create()
|
// KafkaAdmin.createKafkatorioTopics()
|
||||||
}
|
//}
|
||||||
|
|
||||||
object CreateTopics {
|
object KafkaAdmin {
|
||||||
|
|
||||||
private val kafkaAdmin: Admin =
|
private val kafkaAdmin: Admin =
|
||||||
Admin.create(
|
Admin.create(
|
||||||
|
|
@ -26,7 +26,7 @@ object CreateTopics {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
fun create() {
|
fun createKafkatorioTopics() {
|
||||||
|
|
||||||
val currentTopics = currentTopics().map { it.name() }
|
val currentTopics = currentTopics().map { it.name() }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ class KafkatorioTopology(
|
||||||
private val builder: StreamsBuilder = StreamsBuilder(),
|
private val builder: StreamsBuilder = StreamsBuilder(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val sourceTopic = "factorio-server-log"
|
const val sourceTopic = "factorio-server-log"
|
||||||
}
|
}
|
||||||
|
|
@ -23,11 +24,8 @@ class KafkatorioTopology(
|
||||||
|
|
||||||
fun build() {
|
fun build() {
|
||||||
|
|
||||||
|
|
||||||
val packets = factorioServerPacketStream(builder)
|
val packets = factorioServerPacketStream(builder)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
splitFactorioServerPacketStream(packets)
|
splitFactorioServerPacketStream(packets)
|
||||||
|
|
||||||
playerUpdatesToWsServer(websocketServer, builder)
|
playerUpdatesToWsServer(websocketServer, builder)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package dev.adamko.kafkatorio.processor
|
package dev.adamko.kafkatorio.processor
|
||||||
|
|
||||||
|
|
||||||
import dev.adamko.kafkatorio.admin.CreateTopics
|
import dev.adamko.kafkatorio.admin.KafkaAdmin
|
||||||
import dev.adamko.kafkatorio.processor.config.ApplicationProperties
|
import dev.adamko.kafkatorio.processor.config.ApplicationProperties
|
||||||
import dev.adamko.kafkatorio.processor.tileserver.WebMapTileServer
|
import dev.adamko.kafkatorio.processor.tileserver.WebMapTileServer
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
@ -9,11 +9,13 @@ import org.http4k.server.PolyHandler
|
||||||
import org.http4k.server.Undertow
|
import org.http4k.server.Undertow
|
||||||
import org.http4k.server.asServer
|
import org.http4k.server.asServer
|
||||||
|
|
||||||
|
|
||||||
val appProps = ApplicationProperties()
|
val appProps = ApplicationProperties()
|
||||||
|
|
||||||
|
|
||||||
fun main(): Unit = runBlocking {
|
fun main(): Unit = runBlocking {
|
||||||
|
|
||||||
CreateTopics.create()
|
KafkaAdmin.createKafkatorioTopics()
|
||||||
|
|
||||||
val wsServer = WebsocketServer()
|
val wsServer = WebsocketServer()
|
||||||
val tileServer = WebMapTileServer()
|
val tileServer = WebMapTileServer()
|
||||||
|
|
|
||||||
|
|
@ -76,11 +76,6 @@ fun groupTilesIntoChunksWithColours(
|
||||||
|
|
||||||
val zoomLevel = ZoomLevel.ZOOM_0
|
val zoomLevel = ZoomLevel.ZOOM_0
|
||||||
|
|
||||||
//
|
|
||||||
// ZoomLevel
|
|
||||||
// .values
|
|
||||||
// .mapNotNull { zoomLevel ->
|
|
||||||
|
|
||||||
mapTileData
|
mapTileData
|
||||||
?.tiles
|
?.tiles
|
||||||
?.groupBy { tile ->
|
?.groupBy { tile ->
|
||||||
|
|
@ -101,7 +96,7 @@ fun groupTilesIntoChunksWithColours(
|
||||||
|
|
||||||
chunkId to chunkTiles
|
chunkId to chunkTiles
|
||||||
} ?: listOf()
|
} ?: listOf()
|
||||||
// }.flatten()
|
|
||||||
}
|
}
|
||||||
.groupByKey(
|
.groupByKey(
|
||||||
groupedAs("server-map-data.tiles.group", kxsBinary.serde(), kxsBinary.serde())
|
groupedAs("server-map-data.tiles.group", kxsBinary.serde(), kxsBinary.serde())
|
||||||
|
|
@ -121,15 +116,20 @@ fun groupTilesIntoChunksWithColours(
|
||||||
foreignKeyExtractor = { chunkTiles: ServerMapChunkTiles<TileProtoHashCode> -> chunkTiles.chunkId.serverId }
|
foreignKeyExtractor = { chunkTiles: ServerMapChunkTiles<TileProtoHashCode> -> chunkTiles.chunkId.serverId }
|
||||||
) { tiles: ServerMapChunkTiles<TileProtoHashCode>, colourDict: TileColourDict ->
|
) { tiles: ServerMapChunkTiles<TileProtoHashCode>, colourDict: TileColourDict ->
|
||||||
|
|
||||||
ServerMapChunkTiles(
|
val missingProtos = mutableSetOf<TileProtoHashCode>()
|
||||||
tiles.chunkId,
|
|
||||||
tiles.map.mapValues { (_, code) ->
|
val tileColours = tiles.map.mapValues { (_, code) ->
|
||||||
colourDict.map.getOrElse(code) {
|
colourDict.map.getOrElse(code) {
|
||||||
println("missing prototype $code")
|
missingProtos.add(code)
|
||||||
ColourHex.TRANSPARENT
|
ColourHex.TRANSPARENT
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
|
if (missingProtos.isNotEmpty()) {
|
||||||
|
println("missing ${missingProtos.size} tile prototypes: ${missingProtos.joinToString { "${it.code}" }}")
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerMapChunkTiles(tiles.chunkId, tileColours)
|
||||||
}
|
}
|
||||||
|
|
||||||
return colourisedChunkTable
|
return colourisedChunkTable
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ fun factorioServerPacketStream(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun splitFactorioServerPacketStream(
|
fun splitFactorioServerPacketStream(
|
||||||
factorioServerPacketStream: KStream<FactorioServerId, KafkatorioPacket>
|
factorioServerPacketStream: KStream<FactorioServerId, KafkatorioPacket>
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package dev.adamko.kafkatorio.processor.topology
|
||||||
|
|
||||||
import com.sksamuel.scrimage.ImmutableImage
|
import com.sksamuel.scrimage.ImmutableImage
|
||||||
import com.sksamuel.scrimage.ScaleMethod
|
import com.sksamuel.scrimage.ScaleMethod
|
||||||
|
import com.sksamuel.scrimage.color.RGBColor
|
||||||
import com.sksamuel.scrimage.nio.PngWriter
|
import com.sksamuel.scrimage.nio.PngWriter
|
||||||
import dev.adamko.kafkatorio.events.schema.ColourHex
|
import dev.adamko.kafkatorio.events.schema.ColourHex
|
||||||
import dev.adamko.kafkatorio.events.schema.MapChunkPosition
|
import dev.adamko.kafkatorio.events.schema.MapChunkPosition
|
||||||
|
|
@ -20,7 +21,6 @@ import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.conflate
|
import kotlinx.coroutines.flow.conflate
|
||||||
import kotlinx.coroutines.flow.debounce
|
import kotlinx.coroutines.flow.debounce
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
@ -63,7 +63,7 @@ fun saveMapTiles(
|
||||||
chunkedTiles
|
chunkedTiles
|
||||||
.conflate()
|
.conflate()
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.debounce(5.seconds)
|
.debounce(1.seconds)
|
||||||
.map {
|
.map {
|
||||||
println("createMapTileImage ${it.chunkId}")
|
println("createMapTileImage ${it.chunkId}")
|
||||||
createMapTileImage(it)
|
createMapTileImage(it)
|
||||||
|
|
@ -164,6 +164,9 @@ private fun createMapTileImage(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val TRANSPARENT_AWT = ColourHex.TRANSPARENT.toRgbColor().awt()
|
||||||
|
|
||||||
|
|
||||||
private fun createMapTileImage(
|
private fun createMapTileImage(
|
||||||
chunkPosition: MapChunkPosition,
|
chunkPosition: MapChunkPosition,
|
||||||
chunkColours: Map<MapTilePosition, ColourHex>,
|
chunkColours: Map<MapTilePosition, ColourHex>,
|
||||||
|
|
@ -173,7 +176,7 @@ private fun createMapTileImage(
|
||||||
val chunkImage = ImmutableImage.filled(
|
val chunkImage = ImmutableImage.filled(
|
||||||
z.chunkSize,
|
z.chunkSize,
|
||||||
z.chunkSize,
|
z.chunkSize,
|
||||||
ColourHex.TRANSPARENT.toRgbColor().awt(),
|
TRANSPARENT_AWT,
|
||||||
BufferedImage.TYPE_INT_ARGB
|
BufferedImage.TYPE_INT_ARGB
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -196,3 +199,13 @@ private fun createMapTileImage(
|
||||||
|
|
||||||
return chunkImage.scaleTo(WEB_MAP_TILE_SIZE_PX, WEB_MAP_TILE_SIZE_PX, ScaleMethod.Progressive)
|
return chunkImage.scaleTo(WEB_MAP_TILE_SIZE_PX, WEB_MAP_TILE_SIZE_PX, ScaleMethod.Progressive)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun ColourHex.toRgbColor(): RGBColor {
|
||||||
|
return RGBColor(
|
||||||
|
red.toInt(),
|
||||||
|
green.toInt(),
|
||||||
|
blue.toInt(),
|
||||||
|
alpha.toInt(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
package dev.adamko.kafkatorio.processor.topology
|
package dev.adamko.kafkatorio.processor.topology
|
||||||
|
|
||||||
import com.sksamuel.scrimage.color.RGBColor
|
|
||||||
import dev.adamko.kafkatorio.events.schema.Colour
|
|
||||||
import dev.adamko.kafkatorio.events.schema.ColourHex
|
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
@ -24,16 +21,6 @@ value class FactorioServerId(val id: String) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun ColourHex.toRgbColor(): RGBColor {
|
|
||||||
return RGBColor(
|
|
||||||
red.toInt(),
|
|
||||||
green.toInt(),
|
|
||||||
blue.toInt(),
|
|
||||||
alpha.toInt(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum class ZoomLevel(
|
enum class ZoomLevel(
|
||||||
val level: Int
|
val level: Int
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,10 @@ import dev.adamko.kafkatorio.events.schema.FactorioPrototypes
|
||||||
import dev.adamko.kafkatorio.events.schema.KafkatorioPacket
|
import dev.adamko.kafkatorio.events.schema.KafkatorioPacket
|
||||||
import dev.adamko.kafkatorio.events.schema.MapTile
|
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.processor.serdes.jsonMapper
|
import dev.adamko.kafkatorio.processor.serdes.jsonMapper
|
||||||
import dev.adamko.kotka.extensions.materializedAs
|
import dev.adamko.kotka.extensions.materializedAs
|
||||||
|
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
|
||||||
import dev.adamko.kotka.kxs.serde
|
import dev.adamko.kotka.kxs.serde
|
||||||
|
|
@ -22,9 +24,10 @@ value class PrototypeName(val name: String) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Use the hashcode of Prototypes names as keys - more efficient storage */
|
||||||
@JvmInline
|
@JvmInline
|
||||||
@Serializable
|
@Serializable
|
||||||
value class TileProtoHashCode(val code: Int) {
|
value class TileProtoHashCode private constructor(val code: Int) {
|
||||||
constructor(prototype: MapTilePrototype) : this(prototype.name.hashCode())
|
constructor(prototype: MapTilePrototype) : this(prototype.name.hashCode())
|
||||||
constructor(tile: MapTile) : this(tile.prototypeName.hashCode())
|
constructor(tile: MapTile) : this(tile.prototypeName.hashCode())
|
||||||
}
|
}
|
||||||
|
|
@ -32,19 +35,17 @@ value class TileProtoHashCode(val code: Int) {
|
||||||
|
|
||||||
@JvmInline
|
@JvmInline
|
||||||
@Serializable
|
@Serializable
|
||||||
value class TileColourDict(val map: Map<TileProtoHashCode, ColourHex>) {
|
value class TileColourDict(val map: Map<TileProtoHashCode, ColourHex>)
|
||||||
operator fun plus(other: TileColourDict) = TileColourDict(this.map + other.map)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Aggregate [KafkatorioPacket]s into a single [FactorioServerMap] (per [serverId][FactorioServerId]). */
|
/** Get the latest map-tile colours per server. */
|
||||||
fun tileProtoColourDictionary(
|
fun tileProtoColourDictionary(
|
||||||
factorioServerPacketStream: KStream<FactorioServerId, KafkatorioPacket>
|
factorioServerPacketStream: KStream<FactorioServerId, KafkatorioPacket>
|
||||||
): KTable<FactorioServerId, TileColourDict> {
|
): KTable<FactorioServerId, TileColourDict> {
|
||||||
|
|
||||||
// get all the prototypes
|
// get all the prototypes
|
||||||
return factorioServerPacketStream
|
return factorioServerPacketStream
|
||||||
.mapValues("server-map-data.tile-prototypes.mapValues") { _, packet ->
|
.mapValues("server-map-data.tile-prototypes.mapValues") { _, packet: KafkatorioPacket ->
|
||||||
val map = when (packet) {
|
val map = when (packet) {
|
||||||
is FactorioPrototypes -> packet
|
is FactorioPrototypes -> packet
|
||||||
.prototypes
|
.prototypes
|
||||||
|
|
@ -53,13 +54,17 @@ fun tileProtoColourDictionary(
|
||||||
else -> mapOf()
|
else -> mapOf()
|
||||||
}
|
}
|
||||||
TileColourDict(map)
|
TileColourDict(map)
|
||||||
|
}.filter("server-map-data.tile-prototypes.filterMapTileProtos") { _, dict: TileColourDict ->
|
||||||
|
dict.map.isNotEmpty()
|
||||||
|
}.peek { serverId, dict: TileColourDict ->
|
||||||
|
println("server $serverId has TileColourDict[${dict.map.size}]: ${dict.map.entries.joinToString()}")
|
||||||
}
|
}
|
||||||
.toTable(
|
.toTable(
|
||||||
"server-map-data.tile-prototypes",
|
"server-map-data.tile-prototypes",
|
||||||
materializedAs(
|
materializedAs(
|
||||||
"server-map-data.tile-prototypes.store",
|
"server-map-data.tile-prototypes.store",
|
||||||
jsonMapper.serde(),
|
jsonMapper.serde(),
|
||||||
jsonMapper.serde()
|
jsonMapper.serde(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue