webmap
- tidy up player layer - no need for smaller web tiles, make the large ones render as pixelated - make tile server and websocket server URL not-hardcoded - tidy up dynamic tile refreshing
This commit is contained in:
parent
e9a6f99fb8
commit
f151027464
9 changed files with 312 additions and 135 deletions
|
|
@ -1,10 +1,13 @@
|
||||||
|
import dev.adamko.kafkatorio.gradle.taskProvider
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
|
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
|
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension
|
||||||
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
// dev.adamko.kafkatorio.lang.`kotlin-js`
|
dev.adamko.kafkatorio.lang.`kotlin-js`
|
||||||
dev.adamko.kafkatorio.lang.`kotlin-multiplatform`
|
// dev.adamko.kafkatorio.lang.`kotlin-multiplatform`
|
||||||
kotlin("plugin.serialization")
|
kotlin("plugin.serialization")
|
||||||
id("io.kvision")
|
id("io.kvision")
|
||||||
}
|
}
|
||||||
|
|
@ -19,13 +22,42 @@ project.ext.set("io.kvision.plugin.enableBackendTasks", false)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
kotlin {
|
rootProject.extensions.configure<NodeJsRootExtension> {
|
||||||
js(IR) {
|
// https://github.com/rjaros/kvision/issues/410
|
||||||
browser()
|
versions.webpackCli.version = "4.10.0"
|
||||||
binaries.executable()
|
versions.webpackDevServer.version = "4.9.2"
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.extensions.configure<YarnRootExtension> {
|
||||||
|
resolution("http-proxy-middleware", "^2.0.6")
|
||||||
|
}
|
||||||
|
|
||||||
|
//rootProject.afterEvaluate {
|
||||||
|
// extensions.configure<NodeJsRootExtension> {
|
||||||
|
// // https://github.com/rjaros/kvision/issues/410
|
||||||
|
// versions.webpackCli.version = "4.10.0"
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
rootProject.extensions.configure<NodeJsRootExtension> {
|
||||||
|
// https://github.com/rjaros/kvision/issues/410
|
||||||
|
versions.webpackCli.version = "4.10.0"
|
||||||
|
versions.webpackDevServer.version = "4.9.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
js("frontend", IR) {
|
rootProject.extensions.configure<YarnRootExtension> {
|
||||||
|
resolution("http-proxy-middleware", "^2.0.6")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
js(IR) {
|
||||||
|
// browser()
|
||||||
|
// binaries.executable()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// js("frontend", IR) {
|
||||||
// nodejs()
|
// nodejs()
|
||||||
browser {
|
browser {
|
||||||
runTask {
|
runTask {
|
||||||
|
|
@ -35,13 +67,20 @@ kotlin {
|
||||||
open = false,
|
open = false,
|
||||||
port = 3000,
|
port = 3000,
|
||||||
proxy = mutableMapOf(
|
proxy = mutableMapOf(
|
||||||
"/kv/*" to "http://localhost:8080",
|
"/tiles" to mapOf(
|
||||||
"/kvws/*" to mapOf(
|
"target" to "http://localhost:12080",
|
||||||
"target" to "ws://localhost:8080",
|
"secure" to false,
|
||||||
|
"changeOrigin" to true,
|
||||||
|
),
|
||||||
|
"/ws/foo" to mapOf(
|
||||||
|
"target" to "ws://localhost:12080",
|
||||||
|
// "secure" to false,
|
||||||
"ws" to true,
|
"ws" to true,
|
||||||
)
|
// "changeOrigin" to true,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
static = mutableListOf("$buildDir/processedResources/frontend/main")
|
// static = mutableListOf("$buildDir/processedResources/frontend/main")
|
||||||
|
static = mutableListOf("$buildDir/processedResources/js/main")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
webpackTask {
|
webpackTask {
|
||||||
|
|
@ -54,11 +93,14 @@ kotlin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binaries.executable()
|
binaries.executable()
|
||||||
|
// compilations["main"].packageJson {
|
||||||
|
// this.devDependencies["webpack-dev-server"] = "4.9.1"
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
jvm("backend") {
|
// jvm("backend") {
|
||||||
|
//
|
||||||
}
|
// }
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
|
||||||
|
|
@ -69,7 +111,8 @@ kotlin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val commonMain by getting {
|
// val commonMain by getting {
|
||||||
|
main {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(dependencies.platform(libs.kotlinx.coroutines.bom))
|
implementation(dependencies.platform(libs.kotlinx.coroutines.bom))
|
||||||
implementation(dependencies.platform(libs.kotlin.jsWrappers.bom))
|
implementation(dependencies.platform(libs.kotlin.jsWrappers.bom))
|
||||||
|
|
@ -79,13 +122,13 @@ kotlin {
|
||||||
implementation(projects.modules.eventsSchema)
|
implementation(projects.modules.eventsSchema)
|
||||||
|
|
||||||
implementation(libs.kotlinx.html)
|
implementation(libs.kotlinx.html)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
val jsMain by getting {
|
// val jsMain by getting {
|
||||||
dependsOn(commonMain)
|
// dependsOn(commonMain)
|
||||||
|
//
|
||||||
dependencies {
|
// dependencies {
|
||||||
|
|
||||||
implementation(projects.modules.eventsSchema)
|
implementation(projects.modules.eventsSchema)
|
||||||
|
|
||||||
|
|
@ -108,36 +151,52 @@ kotlin {
|
||||||
|
|
||||||
implementation(libs.kotlinx.coroutines.core)
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
|
|
||||||
|
implementation(devNpm("http-proxy-middleware", "^2.0.6"))
|
||||||
|
implementation(dependencies.platform(devNpm("http-proxy-middleware", "^2.0.6")))
|
||||||
|
|
||||||
// implementation(dependencies.platform(npm("follow-redirects", "^1.14.8")))
|
// implementation(dependencies.platform(npm("follow-redirects", "^1.14.8")))
|
||||||
// implementation(dependencies.platform(npm("nanoid", "^3.1.31")))
|
// implementation(dependencies.platform(npm("nanoid", "^3.1.31")))
|
||||||
// implementation(dependencies.platform(npm("minimist", "^1.2.6")))
|
// implementation(dependencies.platform(npm("minimist", "^1.2.6")))
|
||||||
// implementation(dependencies.platform(npm("async", "^2.6.4")))
|
// implementation(dependencies.platform(npm("async", "^2.6.4")))
|
||||||
// implementation(dependencies.platform(npm("node-forge", "^1.3.0")))
|
// implementation(dependencies.platform(npm("node-forge", "^1.3.0")))
|
||||||
|
// implementation(dependencies.platform(npm("socket.io", "^4.5.1")))
|
||||||
|
// implementation(dependencies.platform(devNpm("http-proxy-middleware", "^3.0.0-beta.0")))
|
||||||
|
// implementation(dependencies.platform(devNpm("webpack-dev-server", "4.9.1")))
|
||||||
}
|
}
|
||||||
|
|
||||||
// val webDir = file("src/main/web")
|
// val webDir = file("src/main/web")
|
||||||
resources.srcDir("src/main/web")
|
resources.srcDir("src/main/web")
|
||||||
resources.srcDir("src/main/resources")
|
|
||||||
|
|
||||||
kotlin.srcDir("src/main/kotlin")
|
|
||||||
}
|
|
||||||
|
|
||||||
val frontendMain by getting {
|
|
||||||
dependsOn(jsMain)
|
|
||||||
// resources.srcDir("src/main/web")
|
|
||||||
// resources.srcDir("src/main/resources")
|
// resources.srcDir("src/main/resources")
|
||||||
|
|
||||||
|
// kotlin.srcDir("src/main/kotlin")
|
||||||
}
|
}
|
||||||
|
|
||||||
val jsTest by getting {
|
// val frontendMain by getting {
|
||||||
|
// dependsOn(jsMain)
|
||||||
|
//// resources.srcDir("src/main/web")
|
||||||
|
//// resources.srcDir("src/main/resources")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// val jsTest by getting {
|
||||||
|
// dependencies {
|
||||||
|
// implementation(kotlin("test-js"))
|
||||||
|
//
|
||||||
|
// kvision("kvision-testutils")
|
||||||
|
//
|
||||||
|
//// implementation(npm("karma", "^6.3.16"))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// kotlin.srcDir("src/test/kotlin")
|
||||||
|
// }
|
||||||
|
|
||||||
|
test {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("test-js"))
|
implementation(kotlin("test-js"))
|
||||||
|
|
||||||
kvision("kvision-testutils")
|
kvision("kvision-testutils")
|
||||||
|
|
||||||
// implementation(npm("karma", "^6.3.16"))
|
// implementation(npm("karma", "^6.3.16"))
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin.srcDir("src/test/kotlin")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,17 +211,42 @@ fun KotlinDependencyHandler.kvision(
|
||||||
implementation("io.kvision:$module:${version.get()}", configure)
|
implementation("io.kvision:$module:${version.get()}", configure)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://youtrack.jetbrains.com/issue/KT-42420
|
////// https://youtrack.jetbrains.com/issue/KT-42420
|
||||||
//afterEvaluate {
|
//afterEvaluate {
|
||||||
// yarn {
|
// yarn {
|
||||||
// resolution("mocha", "9.2.2")
|
//// resolution("mocha", "9.2.2")
|
||||||
// resolution("follow-redirects", "1.14.8")
|
//// resolution("follow-redirects", "1.14.8")
|
||||||
// resolution("nanoid", "3.1.31")
|
//// resolution("nanoid", "3.1.31")
|
||||||
// resolution("minimist", "1.2.6")
|
//// resolution("minimist", "1.2.6")
|
||||||
// resolution("async", "2.6.4")
|
//// resolution("async", "2.6.4")
|
||||||
// resolution("node-forge", "1.3.0")
|
//// resolution("node-forge", "1.3.0")
|
||||||
|
//// resolution("socket.io", "^4.5.1")
|
||||||
|
//// resolution("http-proxy-middleware", "^3.0.0-beta.0")
|
||||||
|
// resolution("webpack-dev-server", "4.9.1")
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
evaluationDependsOn(rootProject.path)
|
|
||||||
evaluationDependsOn(projects.modules.eventsSchema.dependencyProject.path)
|
//evaluationDependsOn(rootProject.path)
|
||||||
|
//evaluationDependsOn(projects.modules.eventsSchema.dependencyProject.path)
|
||||||
|
|
||||||
|
|
||||||
|
// stop warnings when building
|
||||||
|
tasks.configureEach {
|
||||||
|
when (name) {
|
||||||
|
"productionExecutableCompileSync" ->
|
||||||
|
dependsOn(taskProvider("compileProductionExecutableKotlinJs"))
|
||||||
|
"testTestDevelopmentExecutableCompileSync" ->
|
||||||
|
dependsOn(taskProvider("compileTestDevelopmentExecutableKotlinJs"))
|
||||||
|
// "jsTestTestDevelopmentExecutableCompileSync" ->
|
||||||
|
// dependsOn(taskProvider("compileTestDevelopmentExecutableKotlinFrontend"))
|
||||||
|
// "jsProductionExecutableCompileSync" ->
|
||||||
|
// dependsOn(taskProvider("compileProductionExecutableKotlinFrontend"))
|
||||||
|
"browserDevelopmentRun" ->
|
||||||
|
dependsOn(taskProvider("browserDevelopmentWebpack"))
|
||||||
|
"browserDevelopmentWebpack" ->
|
||||||
|
dependsOn(taskProvider("assemble"))
|
||||||
|
"browserProductionWebpack" ->
|
||||||
|
dependsOn(taskProvider("developmentExecutableCompileSync"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,7 @@ class App(
|
||||||
wsService: WebsocketService,
|
wsService: WebsocketService,
|
||||||
) : Application(), CoroutineScope {
|
) : Application(), CoroutineScope {
|
||||||
|
|
||||||
override val coroutineContext: CoroutineContext =
|
override val coroutineContext: CoroutineContext = CoroutineName("WebMapApp")
|
||||||
CoroutineName("WebMapApp") + Job(rootJob)
|
|
||||||
|
|
||||||
private var appState: MutableMap<String, Any> = mutableMapOf()
|
private var appState: MutableMap<String, Any> = mutableMapOf()
|
||||||
|
|
||||||
|
|
@ -62,7 +61,7 @@ class App(
|
||||||
override fun start(state: Map<String, Any>) {
|
override fun start(state: Map<String, Any>) {
|
||||||
this.appState = state.toMutableMap()
|
this.appState = state.toMutableMap()
|
||||||
|
|
||||||
val root = root("kvapp") {
|
root("kvapp") {
|
||||||
div("Kafkatorio Web Map")
|
div("Kafkatorio Web Map")
|
||||||
add(kvMaps)
|
add(kvMaps)
|
||||||
}
|
}
|
||||||
|
|
@ -74,18 +73,16 @@ class App(
|
||||||
it.redraw()
|
it.redraw()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// this.asDynamic()._fadeAnimated = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val doc = root.getElement()?.ownerDocument
|
|
||||||
val window = doc!!.defaultView!!
|
|
||||||
|
|
||||||
wsService.packetsFlow
|
wsService.packetsFlow
|
||||||
.filterIsInstance<EventServerPacket.ChunkTileSaved>()
|
.filterIsInstance<EventServerPacket.ChunkTileSaved>()
|
||||||
.onEach {
|
.onEach {
|
||||||
println("[packetsFlow] triggering tile refresh ${it.filename.value}")
|
println("[packetsFlow] triggering tile refresh ${it.filename.value}")
|
||||||
gameState.map.refreshUpdatedTilePng(doc, window, it.filename)
|
gameState.map.refreshUpdatedTilePng(
|
||||||
|
// doc, window,
|
||||||
|
it.filename
|
||||||
|
)
|
||||||
}.launchIn(this)
|
}.launchIn(this)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -104,8 +101,8 @@ fun main() {
|
||||||
{
|
{
|
||||||
val reduxStore = createFactorioReduxStore()
|
val reduxStore = createFactorioReduxStore()
|
||||||
val wsService = WebsocketService(
|
val wsService = WebsocketService(
|
||||||
"ws://localhost:9073/ws",
|
// "ws://localhost:9073/ws", // TODO load from config
|
||||||
reduxStore,
|
reduxStore = reduxStore,
|
||||||
)
|
)
|
||||||
App(
|
App(
|
||||||
reduxStore = reduxStore,
|
reduxStore = reduxStore,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package dev.adamko.kafkatorio.webmap
|
||||||
|
|
||||||
|
import kotlinx.browser.window
|
||||||
|
|
||||||
|
object Props {
|
||||||
|
@Suppress("HttpUrlsUsage")
|
||||||
|
val websocketServerUrl: String
|
||||||
|
get() = window.location.href
|
||||||
|
.replace("http://", "ws://")
|
||||||
|
.replace("https://", "wss://") +
|
||||||
|
"ws/foo"
|
||||||
|
}
|
||||||
33
modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/externals/leaflet.kt
vendored
Normal file
33
modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/externals/leaflet.kt
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
package dev.adamko.kafkatorio.webmap.externals
|
||||||
|
|
||||||
|
import io.kvision.maps.externals.leaflet.DoneCallback
|
||||||
|
import io.kvision.maps.externals.leaflet.geo.LatLng
|
||||||
|
import io.kvision.maps.externals.leaflet.layer.tile.TileLayer
|
||||||
|
import io.kvision.maps.externals.leaflet.layer.vector.Polyline
|
||||||
|
import org.w3c.dom.HTMLElement
|
||||||
|
|
||||||
|
|
||||||
|
inline var TileLayer<TileLayer.TileLayerOptions>.tileOnLoad: TileOnLoadFn?
|
||||||
|
get() = asDynamic()._tileOnLoad as TileOnLoadFn
|
||||||
|
set(value) {
|
||||||
|
asDynamic()._tileOnLoad = value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typealias TileOnLoadFn = (done: DoneCallback, tile: HTMLElement) -> Unit
|
||||||
|
|
||||||
|
|
||||||
|
sealed interface PolylinePathPoints {
|
||||||
|
value class D1(val points: Array<LatLng>) : PolylinePathPoints
|
||||||
|
value class D2(val points: Array<Array<LatLng>>) : PolylinePathPoints
|
||||||
|
value class D3(val points: Array<Array<Array<LatLng>>>) : PolylinePathPoints
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun Polyline<*>.getLatLngs2(): PolylinePathPoints? {
|
||||||
|
return when (val points = this.getLatLngs()) {
|
||||||
|
is Array<LatLng> -> PolylinePathPoints.D1(points as Array<LatLng>)
|
||||||
|
is Array<Array<LatLng>> -> PolylinePathPoints.D2(points as Array<Array<LatLng>>)
|
||||||
|
is Array<Array<Array<LatLng>>> -> PolylinePathPoints.D3(points as Array<Array<Array<LatLng>>>)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package dev.adamko.kafkatorio.webmap
|
package dev.adamko.kafkatorio.webmap
|
||||||
|
|
||||||
import dev.adamko.kafkatorio.schema.common.TilePngFilename
|
import dev.adamko.kafkatorio.schema.common.TilePngFilename
|
||||||
|
import dev.adamko.kafkatorio.webmap.externals.TileOnLoadFn
|
||||||
|
import dev.adamko.kafkatorio.webmap.externals.tileOnLoad
|
||||||
import io.kvision.maps.Maps
|
import io.kvision.maps.Maps
|
||||||
import io.kvision.maps.Maps.Companion.L
|
import io.kvision.maps.Maps.Companion.L
|
||||||
import io.kvision.maps.externals.leaflet.DoneCallback
|
import io.kvision.maps.externals.leaflet.DoneCallback
|
||||||
|
|
@ -11,15 +13,15 @@ import io.kvision.maps.externals.leaflet.geo.CRS
|
||||||
import io.kvision.maps.externals.leaflet.geo.LatLng
|
import io.kvision.maps.externals.leaflet.geo.LatLng
|
||||||
import io.kvision.maps.externals.leaflet.layer.LayerGroup
|
import io.kvision.maps.externals.leaflet.layer.LayerGroup
|
||||||
import io.kvision.maps.externals.leaflet.layer.tile.TileLayer
|
import io.kvision.maps.externals.leaflet.layer.tile.TileLayer
|
||||||
import io.kvision.utils.obj
|
|
||||||
import io.kvision.utils.px
|
import io.kvision.utils.px
|
||||||
|
import kotlinx.browser.document
|
||||||
|
import kotlinx.browser.window
|
||||||
import kotlinx.coroutines.await
|
import kotlinx.coroutines.await
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.w3c.dom.Document
|
import kotlinx.js.jso
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
import org.w3c.dom.Image
|
import org.w3c.dom.Image
|
||||||
import org.w3c.dom.Window
|
|
||||||
import org.w3c.dom.asList
|
import org.w3c.dom.asList
|
||||||
import org.w3c.fetch.NO_CORS
|
import org.w3c.fetch.NO_CORS
|
||||||
import org.w3c.fetch.RELOAD
|
import org.w3c.fetch.RELOAD
|
||||||
|
|
@ -29,7 +31,7 @@ import org.w3c.fetch.RequestMode
|
||||||
|
|
||||||
|
|
||||||
class FactorioMap(
|
class FactorioMap(
|
||||||
private val tileUrlTemplate: String = """http://localhost:9073/tiles/s1/z{z}/x{x}/y{y}.png""",
|
private val tileUrlTemplate: String = """/tiles/s1/z{z}/x{x}/y{y}.png""",
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val kvMap: Maps = Maps {
|
val kvMap: Maps = Maps {
|
||||||
|
|
@ -38,21 +40,24 @@ class FactorioMap(
|
||||||
margin = 10.px
|
margin = 10.px
|
||||||
}
|
}
|
||||||
|
|
||||||
val factorioTerrainLayer: TileLayer<*> = buildFactorioTerrainLayer()
|
private val factorioTerrainLayer: TileLayer<*> = buildFactorioTerrainLayer()
|
||||||
|
|
||||||
val playerIconsLayer: LayerGroup = LayerGroup()
|
val playerIconsLayer: LayerGroup = LayerGroup()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val baseLayers: Control.LayersObject = obj<Control.LayersObject> {}
|
|
||||||
baseLayers["Terrain"] = factorioTerrainLayer
|
|
||||||
|
|
||||||
val overlays: Control.LayersObject = obj<Control.LayersObject> {}
|
val baseLayers: Control.LayersObject = jso {
|
||||||
overlays["Players"] = playerIconsLayer
|
set("Terrain", factorioTerrainLayer)
|
||||||
|
}
|
||||||
|
|
||||||
|
val overlays: Control.LayersObject = jso {
|
||||||
|
set("Players", playerIconsLayer)
|
||||||
|
}
|
||||||
|
|
||||||
val layersControl = Layers(
|
val layersControl = Layers(
|
||||||
baseLayers = baseLayers,
|
baseLayers = baseLayers,
|
||||||
overlays = overlays,
|
overlays = overlays,
|
||||||
options = obj<Layers.LayersOptions> {
|
options = jso {
|
||||||
sortLayers = true
|
sortLayers = true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -78,21 +83,19 @@ class FactorioMap(
|
||||||
|
|
||||||
|
|
||||||
suspend fun refreshUpdatedTilePng(
|
suspend fun refreshUpdatedTilePng(
|
||||||
document: Document,
|
|
||||||
window: Window,
|
|
||||||
tilePngFilename: TilePngFilename,
|
tilePngFilename: TilePngFilename,
|
||||||
): Unit = coroutineScope {
|
): Unit = coroutineScope {
|
||||||
launch {
|
document
|
||||||
document
|
.querySelectorAll("img.leaflet-tile-loaded")
|
||||||
.querySelectorAll("img.leaflet-tile-loaded")
|
.asList()
|
||||||
.asList()
|
.filterIsInstance<Image>()
|
||||||
.filterIsInstance<Image>()
|
.filter { img ->
|
||||||
.filter { img ->
|
tilePngFilename.value in img.src
|
||||||
tilePngFilename.value in img.src
|
}
|
||||||
}
|
.forEach { img ->
|
||||||
.forEach { img ->
|
val imgSrc = img.src
|
||||||
val imgSrc = img.src //.substringBeforeLast('?')
|
|
||||||
|
|
||||||
|
launch {
|
||||||
window.fetch(
|
window.fetch(
|
||||||
imgSrc,
|
imgSrc,
|
||||||
RequestInit(
|
RequestInit(
|
||||||
|
|
@ -100,24 +103,14 @@ class FactorioMap(
|
||||||
mode = RequestMode.NO_CORS,
|
mode = RequestMode.NO_CORS,
|
||||||
)
|
)
|
||||||
).then {
|
).then {
|
||||||
println("[refreshUpdatedTilePng] updating $imgSrc")
|
window.requestAnimationFrame {
|
||||||
img.setAttribute(DYNAMIC_RELOAD_ATT, "true")
|
println("[refreshUpdatedTilePng] updating $imgSrc")
|
||||||
img.src = imgSrc
|
img.setAttribute(DYNAMIC_RELOAD_ATT, "true")
|
||||||
|
img.src = imgSrc
|
||||||
|
}
|
||||||
}.await()
|
}.await()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//// println("fetching $imgSrc")
|
|
||||||
// val newImg = Image()
|
|
||||||
// newImg.onload = {
|
|
||||||
//// window.requestAnimationFrame {
|
|
||||||
//// println("image loaded ${newImg!!.src}")
|
|
||||||
// img.setAttribute(DYNAMIC_RELOAD_ATT, "true")
|
|
||||||
// img.src = newImg.src
|
|
||||||
// Unit
|
|
||||||
//// }
|
|
||||||
// }
|
|
||||||
// newImg.src = imgSrc //+ "?t=${currentTimeMillis()}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildFactorioTerrainLayer(): TileLayer<TileLayer.TileLayerOptions> {
|
private fun buildFactorioTerrainLayer(): TileLayer<TileLayer.TileLayerOptions> {
|
||||||
|
|
@ -132,7 +125,7 @@ class FactorioMap(
|
||||||
minZoom = -2
|
minZoom = -2
|
||||||
maxZoom = 6
|
maxZoom = 6
|
||||||
|
|
||||||
maxNativeZoom = 3
|
maxNativeZoom = -1
|
||||||
minNativeZoom = -1
|
minNativeZoom = -1
|
||||||
|
|
||||||
noWrap = true
|
noWrap = true
|
||||||
|
|
@ -140,13 +133,14 @@ class FactorioMap(
|
||||||
updateWhenZooming = true
|
updateWhenZooming = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentTileOnLoad: (DoneCallback, HTMLElement) -> Unit =
|
val currentTileOnLoad: TileOnLoadFn? = baseTileLayer.tileOnLoad
|
||||||
baseTileLayer.asDynamic()._tileOnLoad as (DoneCallback, HTMLElement) -> Unit
|
|
||||||
baseTileLayer.asDynamic()._tileOnLoad = { done: DoneCallback, tile: HTMLElement ->
|
if (currentTileOnLoad != null) {
|
||||||
if (tile.hasAttribute(DYNAMIC_RELOAD_ATT)) {
|
baseTileLayer.tileOnLoad = { done: DoneCallback, tile: HTMLElement ->
|
||||||
|
if (!tile.hasAttribute(DYNAMIC_RELOAD_ATT)) {
|
||||||
|
currentTileOnLoad(done, tile)
|
||||||
|
}
|
||||||
tile.removeAttribute(DYNAMIC_RELOAD_ATT)
|
tile.removeAttribute(DYNAMIC_RELOAD_ATT)
|
||||||
} else {
|
|
||||||
currentTileOnLoad(done, tile)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,8 @@ import dev.adamko.kafkatorio.schema.packets.PlayerUpdate
|
||||||
import dev.adamko.kafkatorio.webmap.latLng
|
import dev.adamko.kafkatorio.webmap.latLng
|
||||||
import io.kvision.maps.Maps
|
import io.kvision.maps.Maps
|
||||||
import io.kvision.maps.externals.leaflet.geo.LatLng
|
import io.kvision.maps.externals.leaflet.geo.LatLng
|
||||||
import io.kvision.maps.externals.leaflet.layer.overlay.Tooltip
|
|
||||||
import io.kvision.maps.externals.leaflet.layer.vector.CircleMarker
|
import io.kvision.maps.externals.leaflet.layer.vector.CircleMarker
|
||||||
import io.kvision.utils.obj
|
import kotlinx.js.jso
|
||||||
|
|
||||||
|
|
||||||
fun handlePlayerUpdate(
|
fun handlePlayerUpdate(
|
||||||
|
|
@ -100,7 +99,7 @@ data class PlayerState(
|
||||||
className = "player-marker"
|
className = "player-marker"
|
||||||
// interactive = false
|
// interactive = false
|
||||||
}.also { circle ->
|
}.also { circle ->
|
||||||
circle.bindTooltip("", obj<Tooltip.TooltipOptions> {
|
circle.bindTooltip("", jso {
|
||||||
// interactive = false
|
// interactive = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,11 @@ import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.w3c.dom.MessageEvent
|
import org.w3c.dom.MessageEvent
|
||||||
import org.w3c.dom.WebSocket
|
import org.w3c.dom.WebSocket
|
||||||
|
import org.w3c.dom.events.Event
|
||||||
|
|
||||||
|
|
||||||
class WebsocketService(
|
class WebsocketService(
|
||||||
wsUrl: String,
|
wsUrl: String = Props.websocketServerUrl,
|
||||||
private val reduxStore: ReduxStore<FactorioGameState, FactorioUpdate>,
|
private val reduxStore: ReduxStore<FactorioGameState, FactorioUpdate>,
|
||||||
) : CoroutineScope {
|
) : CoroutineScope {
|
||||||
|
|
||||||
|
|
@ -33,6 +34,7 @@ class WebsocketService(
|
||||||
CoroutineName("WebsocketService") + Job(rootJob)
|
CoroutineName("WebsocketService") + Job(rootJob)
|
||||||
|
|
||||||
private val ws = WebSocket(wsUrl)
|
private val ws = WebSocket(wsUrl)
|
||||||
|
// private val ws = WebSocket("ws://localhost:12080/ws/foo")
|
||||||
|
|
||||||
private val _packetsFlow = MutableSharedFlow<EventServerPacket>()
|
private val _packetsFlow = MutableSharedFlow<EventServerPacket>()
|
||||||
val packetsFlow: SharedFlow<EventServerPacket>
|
val packetsFlow: SharedFlow<EventServerPacket>
|
||||||
|
|
@ -40,6 +42,9 @@ class WebsocketService(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
ws.onmessage = ::handleMessageEvent
|
ws.onmessage = ::handleMessageEvent
|
||||||
|
ws.onerror = ::handleError
|
||||||
|
|
||||||
|
println("[WebsocketService] init ${ws.url}, ${ws.protocol}")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleMessageEvent(msg: MessageEvent) {
|
private fun handleMessageEvent(msg: MessageEvent) {
|
||||||
|
|
@ -52,35 +57,46 @@ class WebsocketService(
|
||||||
&& data.endsWith("}")
|
&& data.endsWith("}")
|
||||||
) {
|
) {
|
||||||
|
|
||||||
println(data.replace('\n', ' '))
|
println("[WebsocketService.handleMessageEvent] ${data.replace('\n', ' ')}")
|
||||||
|
|
||||||
val packet = jsonMapper.decodeFromString(EventServerPacket.serializer(), data)
|
val packet: EventServerPacket? = runCatching {
|
||||||
|
jsonMapper.decodeFromString(EventServerPacket.serializer(), data)
|
||||||
launch {
|
}.getOrElse {
|
||||||
_packetsFlow.emit(packet)
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
when (packet) {
|
if (packet != null) {
|
||||||
is EventServerPacket.ChunkTileSaved -> {}
|
|
||||||
|
|
||||||
is EventServerPacket.Kafkatorio ->
|
launch { _packetsFlow.emit(packet) }
|
||||||
when (val packetData = packet.packet.data) {
|
|
||||||
is PlayerUpdate -> reduxStore.dispatch(
|
when (packet) {
|
||||||
FactorioUpdate.Player(packet.packet.tick, packetData)
|
is EventServerPacket.ChunkTileSaved -> {}
|
||||||
)
|
|
||||||
is ConfigurationUpdate,
|
is EventServerPacket.Kafkatorio ->
|
||||||
is ConsoleChatUpdate,
|
when (val packetData = packet.packet.data) {
|
||||||
is ConsoleCommandUpdate,
|
is PlayerUpdate -> reduxStore.dispatch(
|
||||||
is PrototypesUpdate,
|
FactorioUpdate.Player(packet.packet.tick, packetData)
|
||||||
is SurfaceUpdate,
|
)
|
||||||
is EntityUpdate,
|
is ConfigurationUpdate,
|
||||||
is MapChunkUpdate -> {
|
is ConsoleChatUpdate,
|
||||||
// to be continued...
|
is ConsoleCommandUpdate,
|
||||||
|
is PrototypesUpdate,
|
||||||
|
is SurfaceUpdate,
|
||||||
|
is EntityUpdate,
|
||||||
|
is MapChunkUpdate -> {
|
||||||
|
// to be continued...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
println("[WebsocketService.handleMessageEvent] unknown json message ${msg.data}")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println(data)
|
println("[WebsocketService.handleMessageEvent] non-json message ${msg.data}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleError(error: Event) {
|
||||||
|
println("ws error: ${JSON.stringify(error)}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
/* https://github.com/Leaflet/Leaflet/issues/3575 */
|
|
||||||
.leaflet-tile {
|
.leaflet-tile {
|
||||||
width: 257px !important;
|
/* https://github.com/Leaflet/Leaflet/issues/3575 */
|
||||||
height: 257px !important;
|
/*width: 257px !important;*/
|
||||||
box-shadow: 0 0 1px rgba(0, 0, 0, 0.05);
|
/*height: 257px !important;*/
|
||||||
|
/*box-shadow: 0 0 1px rgba(0, 0, 0, 0.05);*/
|
||||||
|
|
||||||
|
image-rendering: pixelated;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,55 @@
|
||||||
config.resolve.modules.push("../../processedResources/js/main");
|
config.resolve.modules.push("../../processedResources/js/main");
|
||||||
|
|
||||||
if (config.devServer) {
|
if (config.devServer) {
|
||||||
config.devServer.hot = true;
|
config.devServer.hot = true;
|
||||||
config.devtool = 'eval-cheap-source-map';
|
config.devtool = 'eval-cheap-source-map';
|
||||||
|
config.devServer.host = "0.0.0.0"
|
||||||
} else {
|
} else {
|
||||||
config.devtool = undefined;
|
config.devtool = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable bundle size warning
|
// disable bundle size warning
|
||||||
config.performance = {
|
config.performance = {
|
||||||
assetFilter: function (assetFilename) {
|
assetFilter: function (assetFilename) {
|
||||||
return !assetFilename.endsWith('.js');
|
return !assetFilename.endsWith('.js');
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// // dev server
|
||||||
|
// config.devServer = {
|
||||||
|
// open: false,
|
||||||
|
// // devMiddleware: {
|
||||||
|
// // index: false, // specify to enable root proxying
|
||||||
|
// // },
|
||||||
|
// port: 3000,
|
||||||
|
// proxy: {
|
||||||
|
// // context: () => true,
|
||||||
|
// // target: "http://localhost:12080",
|
||||||
|
// // secure: false,
|
||||||
|
// // ws: true,
|
||||||
|
// // changeOrigin: true,
|
||||||
|
//
|
||||||
|
// // "/tiles": {
|
||||||
|
// // target: "http://localhost:12080",
|
||||||
|
// // // "pathRewrite": { "^/tiles": "" },
|
||||||
|
// // // "logger": "console",
|
||||||
|
// // // "logLevel": "debug",
|
||||||
|
// // secure: false,
|
||||||
|
// // changeOrigin: true,
|
||||||
|
// // },
|
||||||
|
// // "/ws": {
|
||||||
|
// // target: "http://localhost:12080",
|
||||||
|
// // // pathRewrite: { "^/ws": "" },
|
||||||
|
// // // "logLevel": "debug",
|
||||||
|
// // // "logger": "console",
|
||||||
|
// // secure: false,
|
||||||
|
// // ws: true,
|
||||||
|
// // changeOrigin: true,
|
||||||
|
// // },
|
||||||
|
// },
|
||||||
|
// "static": [
|
||||||
|
// // "../../processedResources/js/main",
|
||||||
|
// "D:\\Users\\Adam\\Projects\\games\\kafkatorio\\modules\\web-map\\build\\processedResources\\frontend\\main",
|
||||||
|
// ],
|
||||||
|
// };
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue