it is literally working

This commit is contained in:
Adam 2022-01-04 15:09:50 +01:00
parent 841f07c847
commit a0c330384a
17 changed files with 499 additions and 63 deletions

View file

@ -42,6 +42,14 @@ is provided.
* https://jbrandhorst.com/post/go-protobuf-tips/
##### wasm
kt -> wasm -> lua?
kt-flatbuffers -> wasm -> lua?
https://www.npmjs.com/package/wasm2lua?activeTab=readme
##### flatbuffers
https://github.com/dvidelabs/flatcc
@ -56,6 +64,3 @@ https://github.com/dvidelabs/flatcc
https://www.zash.se/lua-cbor.html
https://code.zash.se/lua-cbor/archive/68b3a36d0816.tar.gz

View file

@ -2,6 +2,13 @@
# yarn lockfile v1
"@babel/runtime@^7.9.2":
version "7.16.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.5.tgz#7f3e34bf8bdbbadf03fbb7b1ea0d929569c9487a"
integrity sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==
dependencies:
regenerator-runtime "^0.13.4"
"@discoveryjs/json-ext@^0.5.0":
version "0.5.6"
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f"
@ -81,6 +88,11 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83"
integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==
"@types/geojson@*", "@types/geojson@7946.0.8":
version "7946.0.8"
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca"
integrity sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==
"@types/glob@5 - 7":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
@ -101,6 +113,13 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
"@types/leaflet@^1.7.7":
version "1.7.7"
resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.7.7.tgz#3c9f629126e03405ed68cc1aa395e32df3312376"
integrity sha512-5/nNqTviSqUwPxbS3PlHl8ODjfnlpOXSFtUAi7dexnisO8E3pfyotChRY/hlIYGvbBiBcKNd6V18xA8O4OpHiQ==
dependencies:
"@types/geojson" "*"
"@types/minimatch@*":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
@ -949,6 +968,14 @@ dukat@0.5.8-rc.3:
google-protobuf "3.12.2"
typescript "3.9.5"
dukat@0.5.8-rc.4:
version "0.5.8-rc.4"
resolved "https://registry.yarnpkg.com/dukat/-/dukat-0.5.8-rc.4.tgz#90384dcb50b14c26f0e99dae92b2dea44f5fce21"
integrity sha512-ZnMt6DGBjlVgK2uQamXfd7uP/AxH7RqI0BL9GLrrJb2gKdDxvJChWy+M9AQEaL+7/6TmxzJxFOsRiInY9oGWTA==
dependencies:
google-protobuf "3.12.2"
typescript "3.9.5"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@ -1280,6 +1307,11 @@ function-bind@^1.1.1:
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
geojson@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/geojson/-/geojson-0.5.0.tgz#3cd6c96399be65b56ee55596116fe9191ce701c0"
integrity sha1-PNbJY5m+ZbVu5VWWEW/pGRznAcA=
get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
@ -1845,6 +1877,11 @@ kvision-assets@^5.0.7:
resolved "https://registry.yarnpkg.com/kvision-assets/-/kvision-assets-5.0.7.tgz#e6dc3e28a2d244b4c89318b060d3e7e337c15495"
integrity sha512-szoL8ixFRTivLkbnmkWFjGJMk3bsUW/loUS5QfWThPwFnyFEw8ht3eBUrJ0V51Rn/WaJbVoPvY4SfIaiNu13bg==
leaflet@^1.7.1:
version "1.7.1"
resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.7.1.tgz#10d684916edfe1bf41d688a3b97127c0322a2a19"
integrity sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw==
loader-runner@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384"
@ -2430,6 +2467,23 @@ rechoir@^0.7.0:
dependencies:
resolve "^1.9.0"
redux-thunk@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714"
integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==
redux@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104"
integrity sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==
dependencies:
"@babel/runtime" "^7.9.2"
regenerator-runtime@^0.13.4:
version "0.13.9"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
regexp.prototype.flags@^1.2.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26"

View file

@ -13,7 +13,8 @@ camel-kafkaConnectors = "0.11.0"
http4k = "4.17.6.0"
kafka = "3.0.0"
kotlinx-serialization = "1.3.2"
kvision = "5.6.1"
#kvision = "5.6.1"
kvision = "5.6.2-SNAPSHOT"
log4j2 = "2.14.1"
qosLogback = "1.2.5"
slf4j = "1.7.30"
@ -50,8 +51,12 @@ kotlinx-serialization-bom = { group = "org.jetbrains.kotlinx", name = "kotlinx-
kotlinx-serialization-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core" }
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json" }
kotlinx-serialization-yaml = { group = "com.charleskorn.kaml", name = "kaml", version = "0.37.0" }
kotlinx-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.6.0" }
kotlinx-html = { group = "org.jetbrains.kotlinx", name = "kotlinx-html", version = "0.7.3" }
kotlinx-nodejs = { group = "org.jetbrains.kotlinx", name = "kotlinx-nodejs", version = "0.0.7" }
## http4k ##
http4k-bom = { group = "org.http4k", name = "http4k-bom", version.ref = "http4k" }

View file

@ -11,6 +11,13 @@ interface FactorioEvent<T extends FactorioObjectData> {
tick: uint;
}
interface Colour {
alpha: float;
blue: float;
green: float;
red: float;
}
interface PositionData {
x: double;
y: double;
@ -19,6 +26,9 @@ interface PositionData {
interface PlayerData extends FactorioObjectData {
associated_characters_unit_numbers: uint[];
character_unit_number: uint | null;
chat_colour: Colour;
colour: Colour;
last_online: uint;
name: string;
object_name: string;
position: PositionData;

View file

@ -10,6 +10,9 @@ export namespace Serdes {
character_unit_number: player.character?.unit_number ?? null,
associated_characters_unit_numbers: charIds,
position: positionTableToTable(player.position),
colour: mapColour(player.color),
chat_colour: mapColour(player.chat_color),
last_online: player.last_online
}
}
@ -62,4 +65,14 @@ export namespace Serdes {
}
}
export function mapColour(color: Color): Colour {
color = (color as ColorTable)
return {
red: color.r ?? 0,
green: color.g ?? 0,
blue: color.b ?? 0,
alpha: color.a ?? 0,
}
}
}

View file

@ -30,9 +30,9 @@ kotlin {
val commonMain by getting {
dependencies {
implementation(project.dependencies.enforcedPlatform(libs.kotlin.bom))
implementation(project.dependencies.platform(libs.kotlin.bom))
implementation(project.dependencies.enforcedPlatform(libs.kotlinx.serialization.bom))
implementation(project.dependencies.platform(libs.kotlinx.serialization.bom))
implementation(libs.kotlinx.serialization.core)
implementation(libs.kotlinx.serialization.json)
@ -44,7 +44,7 @@ kotlin {
implementation(kotlin("test"))
implementation(project.dependencies.enforcedPlatform(libs.kotest.bom))
implementation(project.dependencies.platform(libs.kotest.bom))
implementation("io.kotest:kotest-assertions-core")
implementation("io.kotest:kotest-property")
implementation("io.kotest:kotest-assertions-json")

View file

@ -11,13 +11,18 @@ import kotlinx.serialization.json.JsonClassDiscriminator
@Serializable
data class FactorioEvent<out T : FactorioObjectData>(
/** the initial Factorio event ({defines.events}) trigger */
@SerialName("event_type") val eventType: String,
@SerialName("event_type")
val eventType: String,
/** Schema versioning */
@SerialName("mod_version") val modVersion: String,
@SerialName("factorio_version") val factorioVersion: String,
@SerialName("mod_version")
val modVersion: String,
@SerialName("factorio_version")
val factorioVersion: String,
/** game time */
@SerialName("tick") val tick: UInt,
@SerialName("data") val data: T
@SerialName("tick")
val tick: UInt,
@SerialName("data")
val data: T
)
@ -53,10 +58,20 @@ sealed class FactorioObjectData {
@SerialName("LuaPlayer")
data class PlayerData(
@Serializable(with = ListAsObjectSerializer::class)
@SerialName("associated_characters_unit_numbers") val associatedCharactersUnitNumbers: List<UInt>,
@SerialName("character_unit_number") val characterUnitNumber: UInt?,
@SerialName("name") val name: String,
@SerialName("position") val position: PositionData,
@SerialName("associated_characters_unit_numbers")
val associatedCharactersUnitNumbers: List<UInt>,
@SerialName("character_unit_number")
val characterUnitNumber: UInt?,
@SerialName("name")
val name: String,
@SerialName("position")
val position: PositionData,
@SerialName("colour")
val colour: Colour,
@SerialName("chat_colour")
val chatColour: Colour,
@SerialName("last_online")
val lastOnline: UInt,
) : FactorioObjectData() {
@Transient
override val objectName: String = "LuaPlayer"
@ -65,16 +80,25 @@ data class PlayerData(
@Serializable
@SerialName("LuaEntity")
data class EntityData(
@SerialName("active") val active: Boolean,
@SerialName("health") val health: Double?,
@SerialName("health_ratio") val healthRatio: Double,
@SerialName("name") val name: String,
@SerialName("position") val position: PositionData,
@SerialName("surface_index") val surfaceIndex: Int,
@SerialName("type") val type: String,
@SerialName("unit_number") val unitNumber: UInt? = null,
@SerialName("active")
val active: Boolean,
@SerialName("health")
val health: Double?,
@SerialName("health_ratio")
val healthRatio: Double,
@SerialName("name")
val name: String,
@SerialName("position")
val position: PositionData,
@SerialName("surface_index")
val surfaceIndex: Int,
@SerialName("type")
val type: String,
@SerialName("unit_number")
val unitNumber: UInt? = null,
@SerialName("player_index") val playerIndex: UInt? = null,
@SerialName("player_index")
val playerIndex: UInt? = null,
) : FactorioObjectData() {
@Transient
override val objectName: String = "LuaEntity"
@ -83,9 +107,12 @@ data class EntityData(
@Serializable
@SerialName("LuaSurface")
data class SurfaceData(
@SerialName("daytime") val daytime: Double,
@SerialName("index") val index: UInt,
@SerialName("name") val name: String,
@SerialName("daytime")
val daytime: Double,
@SerialName("index")
val index: UInt,
@SerialName("name")
val name: String,
) : FactorioObjectData() {
@Transient
override val objectName: String = "LuaSurface"
@ -93,6 +120,32 @@ data class SurfaceData(
@Serializable
data class PositionData(
@SerialName("x") val x: Double,
@SerialName("y") val y: Double,
val x: Double,
val y: Double,
)
/**
* Red, green, blue and alpha values, all in range `[0, 1]` or all in range `[0, 255]` if any
* value is > 1.
*
* All values here are optional. Color channels default to 0, the alpha channel defaults to 1.
*/
@Serializable
data class Colour(
val red: Float = 0f,
val green: Float = 0f,
val blue: Float = 0f,
val alpha: Float = 1f,
) {
/** True if any value is greater than 1, so the values are hexadecimal. */
fun isDecimal(): Boolean {
return red > 1f
|| green > 1f
|| blue > 1f
|| alpha > 1f
}
/** True if all values are between `[0..1]`. */
fun isPercentage(): Boolean = !isDecimal()
}

View file

@ -21,7 +21,8 @@ class FactorioEventTest : BehaviorSpec({
"position": {
"x": -3.5703125,
"y": 29.75
}
},
"last_online": 123
},
"event_type": "on_player_joined_game",
"mod_version": "0.0.4",
@ -41,7 +42,10 @@ class FactorioEventTest : BehaviorSpec({
position = PositionData(
x = -3.5703125,
y = 29.75
)
),
colour = Colour(),
chatColour = Colour(),
lastOnline = 123u
),
eventType = "on_player_joined_game",
modVersion = "0.0.4",

View file

@ -1,9 +1,9 @@
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
plugins {
id("dev.adamko.kafkatorio.lang.kotlin-js")
kotlin("plugin.serialization")
// kotlin("js") // version kotlinVersion
id("io.kvision") version "5.6.1"
}
@ -13,7 +13,7 @@ kotlin {
browser {
runTask {
outputFileName = "main.bundle.js"
sourceMaps = false
sourceMaps = true
devServer = KotlinWebpackConfig.DevServer(
open = false,
port = 3000,
@ -37,31 +37,54 @@ kotlin {
}
sourceSets {
val kvisionVersion: String = libs.versions.kvision.get()
all {
languageSettings.optIn("kotlin.RequiresOptIn")
languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi")
}
main {
dependencies {
implementation(projects.modules.eventsSchema)
implementation("org.jetbrains.kotlinx:kotlinx-nodejs:0.0.7")
implementation("org.jetbrains.kotlinx:kotlinx-html:0.7.3")
implementation("io.kvision:kvision:$kvisionVersion")
implementation("io.kvision:kvision-bootstrap:$kvisionVersion")
implementation("io.kvision:kvision-bootstrap-css:$kvisionVersion")
implementation("io.kvision:kvision-bootstrap-icons:$kvisionVersion")
implementation("io.kvision:kvision-fontawesome:$kvisionVersion")
implementation("io.kvision:kvision-state:$kvisionVersion")
implementation("io.kvision:kvision-chart:$kvisionVersion")
kvision("kvision")
kvision("kvision-bootstrap")
kvision("kvision-bootstrap-css")
kvision("kvision-bootstrap-icons")
kvision("kvision-fontawesome")
kvision("kvision-state")
kvision("kvision-chart")
kvision("kvision-maps")
kvision("kvision-redux")
kvision("kvision-state-flow")
implementation(libs.kotlinx.coroutines)
}
val webDir = file("src/main/web")
resources.srcDir(webDir)
}
test {
dependencies {
implementation(kotlin("test-js"))
implementation("io.kvision:kvision-testutils:$kvisionVersion")
kvision("kvision-testutils")
}
}
}
}
fun KotlinDependencyHandler.kvision(
module: String,
version: Provider<String> = libs.versions.kvision,
configure: ExternalModuleDependency.() -> Unit = {
isChanging = true
}
) {
implementation("io.kvision:$module:${version.get()}", configure)
}

View file

@ -1,28 +1,67 @@
package dev.adamko.kafkatorio.webmap
import io.kvision.Application
import io.kvision.CoreModule
import io.kvision.BootstrapModule
import io.kvision.BootstrapCssModule
import io.kvision.BootstrapIconsModule
import io.kvision.FontAwesomeModule
import io.kvision.BootstrapModule
import io.kvision.ChartModule
import io.kvision.CoreModule
import io.kvision.FontAwesomeModule
import io.kvision.html.div
import io.kvision.maps.Maps
import io.kvision.module
import io.kvision.panel.root
import io.kvision.redux.ReduxStore
import io.kvision.startApplication
class App : Application() {
override fun start() {
class App(
reduxStore: ReduxStore<FactorioGameState, FactorioUpdate>,
wsService: WebsocketService
) : Application() {
private var appState: MutableMap<String, Any> = mutableMapOf()
var reduxStore: ReduxStore<FactorioGameState, FactorioUpdate> by appState
var wsService: WebsocketService by appState
init {
this.reduxStore = reduxStore
this.wsService = wsService
}
private val kvMaps: Maps
get() = reduxStore.getState().kvMaps
override fun start(state: Map<String, Any>) {
this.appState = state.toMutableMap()
root("kvapp") {
div("Hello world")
div("Kafkatorio Web Map")
add(kvMaps)
}
}
override fun dispose(): Map<String, Any> {
return appState
}
}
fun main() {
startApplication(
::App,
{
val reduxStore = createFactorioReduxStore()
val wsService = WebsocketService(
"ws://localhost:9073/ws",
reduxStore,
)
App(
reduxStore = reduxStore,
wsService = wsService
)
},
module.hot,
BootstrapModule,
BootstrapCssModule,

View file

@ -0,0 +1,36 @@
package dev.adamko.kafkatorio.webmap
import dev.adamko.kafkatorio.events.schema.Colour
import dev.adamko.kafkatorio.events.schema.PlayerData
import externals.leaflet.geo.LatLng
import io.kvision.maps.Maps
import kotlin.math.roundToInt
fun Colour.toHexString(includeAlpha: Boolean = false): String {
val hexConverter: Float.() -> String = if (isDecimal()) {
Float::decimalToHex
} else {
Float::percentageToHex
}
val length = if (includeAlpha) 9 else 7
return buildString(length) {
append('#')
append(red.hexConverter())
append(green.hexConverter())
append(blue.hexConverter())
if (includeAlpha) {
append(alpha.hexConverter())
}
}
}
fun Float.decimalToHex(): String =
roundToInt().toString(16).padStart(2, ' ')
fun Float.percentageToHex(): String =
times(255).roundToInt().toString(16).padStart(2, ' ')
fun PlayerData.latLng(): LatLng = Maps.L.latLng((position.y * -1) to position.x)

View file

@ -0,0 +1,7 @@
package dev.adamko.kafkatorio.webmap
import kotlinx.serialization.json.Json
val jsonMapper = Json {
prettyPrint = true
}

View file

@ -0,0 +1,37 @@
package dev.adamko.kafkatorio.webmap
import externals.leaflet.geo.CRS
import externals.leaflet.geo.LatLng
import io.kvision.maps.DefaultTileLayers
import io.kvision.maps.Maps
import io.kvision.utils.px
//
//val polyline = Maps.L.polyline(
// listOf(
// LatLng(55, 2),
// LatLng(65, 2),
// LatLng(65, 20),
// LatLng(55, 20),
// LatLng(55, 2),
// )
//) {
// noClip = true
//}
fun createFactorioMap() = Maps {
width = 800.px
height = 800.px
margin = 10.px
configureLeafletMap {
setView(LatLng(0, 0), 13)
// DefaultTileLayers.OpenStreetMap.addTo(this)
with(options) {
crs = CRS.Simple
}
}
}

View file

@ -0,0 +1,92 @@
package dev.adamko.kafkatorio.webmap
import dev.adamko.kafkatorio.events.schema.FactorioEvent
import dev.adamko.kafkatorio.events.schema.PlayerData
import externals.leaflet.geo.LatLng
import externals.leaflet.layer.overlay.Tooltip
import externals.leaflet.layer.vector.CircleMarker
import io.kvision.maps.Maps
import io.kvision.maps.Maps.Companion.L
import io.kvision.redux.ReduxStore
import io.kvision.redux.createReduxStore
import io.kvision.utils.obj
import redux.RAction
fun createFactorioReduxStore(): ReduxStore<FactorioGameState, FactorioUpdate> =
createReduxStore(
::factorioGameReducer,
FactorioGameState(
kvMaps = createFactorioMap(),
players = emptyMap()
)
)
data class FactorioGameState(
val kvMaps: Maps,
val players: Map<String, PlayerState>,
)
sealed class FactorioUpdate : RAction {
data class PlayerUpdate(val event: FactorioEvent<*>, val playerData: PlayerData) :
FactorioUpdate()
}
fun factorioGameReducer(state: FactorioGameState, update: FactorioUpdate): FactorioGameState =
when (update) {
is FactorioUpdate.PlayerUpdate -> {
val playerState = state.players.getOrElse(update.playerData.name) {
val newState = PlayerState(update.playerData)
state.kvMaps.leafletMap {
addLayer(newState.mapMarker)
newState.mapMarker.openPopup()
// setView(update.playerData.latLng())
}
newState
}
playerState.update(update.playerData)
val updatedPlayersState = state.players + (update.playerData.name to playerState)
state.copy(players = updatedPlayersState)
}
}
data class PlayerState(
private var playerData: PlayerData,
val mapMarker: CircleMarker = createMapMarker(playerData)
) {
fun update(playerData: PlayerData) {
this.playerData = playerData
mapMarker.setLatLng(playerData.latLng())
println("updated player ${playerData.name}")
}
companion object {
fun createMapMarker(playerData: PlayerData): CircleMarker {
val playerLatLng = playerData.latLng()
val playerMapTooltip = "${playerData.name} [$playerLatLng]"
val circle = L.circleMarker(playerLatLng) {
color = playerData.colour.toHexString(false)
fillColor = color
fillOpacity = 1f
radius = 10
className = playerData.objectName
// interactive = false
}
circle.bindTooltip(playerMapTooltip, obj<Tooltip.TooltipOptions> {
// interactive = false
})
return circle
}
}
}

View file

@ -0,0 +1,46 @@
package dev.adamko.kafkatorio.webmap
import dev.adamko.kafkatorio.events.schema.FactorioEvent
import dev.adamko.kafkatorio.events.schema.FactorioObjectData
import dev.adamko.kafkatorio.events.schema.PlayerData
import io.kvision.redux.ReduxStore
import kotlinx.serialization.decodeFromString
import org.w3c.dom.MessageEvent
import org.w3c.dom.WebSocket
class WebsocketService(
private val wsUrl: String,
private val reduxStore: ReduxStore<FactorioGameState, FactorioUpdate>,
) {
private val ws = WebSocket(wsUrl)
init {
ws.onmessage = ::handleMessageEvent
}
private fun handleMessageEvent(msg: MessageEvent) {
val data = msg.data as? String
if (
data != null
&& data.trim().startsWith("{")
&& data.trim().endsWith("}")
) {
println(data.replace('\n', ' '))
val event: FactorioEvent<FactorioObjectData> = jsonMapper.decodeFromString(data)
when (val eventData = event.data) {
is PlayerData -> {
reduxStore.dispatch(FactorioUpdate.PlayerUpdate(event, eventData))
}
else -> {}
}
} else {
println(data)
}
}
}

View file

@ -1,9 +1,10 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>KVision Template</title>
<title>Kafkatorio Web Map</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.1.0/dist/superhero/bootstrap.min.css" integrity="sha256-T8h7oW5tHp4MhFhmRoDI8xjnB2ld89qSgc3s9g8T0uU=" crossorigin="anonymous">
<script type="text/javascript" src="main.bundle.js"></script>
</head>
<body>

View file

@ -21,3 +21,14 @@ enableFeaturePreview("VERSION_CATALOGS")
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
apply(from = "./buildSrc/repositories.settings.gradle.kts")
@Suppress("UnstableApiUsage") // centralized repositories is incubating
dependencyResolutionManagement {
repositories {
mavenLocal {
content {
includeGroup("io.kvision")
}
}
}
}