diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/App.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/App.kt index aab0664..f21d8ad 100644 --- a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/App.kt +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/App.kt @@ -1,9 +1,10 @@ package dev.adamko.kafkatorio.webmap import dev.adamko.kafkatorio.schema.packets.EventServerPacket -import dev.adamko.kafkatorio.webmap.state.FactorioGameState -import dev.adamko.kafkatorio.webmap.state.FactorioUpdate -import dev.adamko.kafkatorio.webmap.state.createFactorioReduxStore +import dev.adamko.kafkatorio.webmap.layout.headerNav +import dev.adamko.kafkatorio.webmap.layout.homePage +import dev.adamko.kafkatorio.webmap.layout.serverPage +import dev.adamko.kafkatorio.webmap.services.WebsocketService import io.kvision.Application import io.kvision.BootstrapCssModule import io.kvision.BootstrapIconsModule @@ -12,17 +13,21 @@ 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.maps.externals.leaflet.layer.tile.GridLayer +import io.kvision.html.header +import io.kvision.html.main import io.kvision.module import io.kvision.panel.root import io.kvision.redux.ReduxStore +import io.kvision.redux.createReduxStore import io.kvision.require +import io.kvision.routing.Routing import io.kvision.startApplication -import kotlin.coroutines.CoroutineContext +import io.kvision.state.bind import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.flow.filterIsInstance @@ -33,54 +38,78 @@ import kotlinx.coroutines.flow.onEach val rootJob = Job() -class App( - reduxStore: ReduxStore, - wsService: WebsocketService, -) : Application(), CoroutineScope { +class App : Application() { - override val coroutineContext: CoroutineContext = CoroutineName("WebMapApp") + private val coroutineScope: CoroutineScope = CoroutineScope( + Dispatchers.Default + + SupervisorJob(rootJob) + + CoroutineName("WebMapApp") + ) private var appState: MutableMap = mutableMapOf() - var reduxStore: ReduxStore by appState - var wsService: WebsocketService by appState + var reduxStore: ReduxStore by appState + private set +// var wsService: WebsocketService by appState +// private set init { - this.reduxStore = reduxStore - this.wsService = wsService + this.reduxStore = createReduxStore(SiteState::plus, SiteState()) +// this.wsService = wsService require("./css/kafkatorio.css") } - private val gameState: FactorioGameState - get() = reduxStore.getState() - - private val kvMaps: Maps - get() = gameState.map.kvMap +// private val gameState: FactorioGameState +// get() = reduxStore.getState() +// private val kvMaps: Maps +// get() = gameState.map.kvMap override fun start(state: Map) { + Routing.init(useHash = false) + this.appState = state.toMutableMap() + val siteRouting = SiteRouting(reduxStore) + val websocketService = WebsocketService(reduxStore) + + siteRouting.init() + root("kvapp") { + + header().bind(siteRouting.siteStateStore) { state -> + headerNav(state) + } + div("Kafkatorio Web Map") - add(kvMaps) - } - kvMaps.leafletMap { - invalidateSize(true) - eachLayer({ - if (it is GridLayer<*>) { - it.redraw() + main().bind(siteRouting.siteStateStore) { state -> + when (state.view) { + SiteView.HOME -> homePage(state) + SiteView.SERVER -> serverPage(state) } - }) + } + +// div("Kafkatorio Web Map") +// add(kvMaps) } - wsService.packetsFlow +// kvMaps.leafletMap { +// invalidateSize(true) +// eachLayer({ +// if (it is GridLayer<*>) { +// it.redraw() +// } +// }) +// } + + websocketService.packetsFlow .filterIsInstance() .onEach { tileSavedEvent -> // println("[packetsFlow] triggering tile refresh ${tileSavedEvent.filename.value}") - gameState.map.refreshUpdatedTilePng(tileSavedEvent.filename) - }.launchIn(this) + reduxStore.dispatch(SiteAction.EventServerUpdate(tileSavedEvent)) +// reduxStore.map.refreshUpdatedTilePng(tileSavedEvent.filename) + }.launchIn(coroutineScope) } @@ -96,14 +125,13 @@ class App( fun main() { startApplication( { - val reduxStore = createFactorioReduxStore() - val wsService = WebsocketService( -// "ws://localhost:9073/ws", // TODO load from config - reduxStore = reduxStore, - ) +// val reduxStore = createFactorioReduxStore() +// val wsService = WebsocketService( +// reduxStore = reduxStore, +// ) App( - reduxStore = reduxStore, - wsService = wsService, +// reduxStore = reduxStore, +// wsService = wsService, ) }, module.hot, diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/ApplicationProperties.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/config/ApplicationProperties.kt similarity index 100% rename from modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/ApplicationProperties.kt rename to modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/config/ApplicationProperties.kt diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/json.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/config/json.kt similarity index 82% rename from modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/json.kt rename to modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/config/json.kt index 3dd2fba..1b9be5b 100644 --- a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/json.kt +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/config/json.kt @@ -1,4 +1,4 @@ -package dev.adamko.kafkatorio.webmap +package dev.adamko.kafkatorio.webmap.config import dev.adamko.kafkatorio.library.jsonMapperKafkatorio import kotlinx.serialization.json.Json diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/layout/headerNav.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/layout/headerNav.kt new file mode 100644 index 0000000..6ce1bc1 --- /dev/null +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/layout/headerNav.kt @@ -0,0 +1,16 @@ +package dev.adamko.kafkatorio.webmap.layout + +import dev.adamko.kafkatorio.webmap.SiteState +import dev.adamko.kafkatorio.webmap.SiteView +import io.kvision.core.Container +import io.kvision.html.div +import io.kvision.html.link +import io.kvision.html.nav + +fun Container.headerNav(state: SiteState) { + nav(className = "navbar") { + div(className = "container") { + link("home", "#${SiteView.HOME.pathMatcher}") + } + } +} diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/layout/homePage.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/layout/homePage.kt new file mode 100644 index 0000000..a2fd4b8 --- /dev/null +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/layout/homePage.kt @@ -0,0 +1,30 @@ +package dev.adamko.kafkatorio.webmap.layout + +import dev.adamko.kafkatorio.webmap.SiteState +import io.kvision.core.Container +import io.kvision.html.div +import io.kvision.html.h5 +import io.kvision.html.link + + +fun Container.homePage(state: SiteState) { + div(className = "home-page") { + servers(state) + } +} + + +private fun Container.servers(state: SiteState) { + div(className = "servers") { + h5("Servers") + if (state.serverIds != null) { + state.serverIds.forEach { serverId -> + div { + link("server", "/servers/$serverId") + } + } + } else { + div("no servers available") + } + } +} diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/layout/serverPage.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/layout/serverPage.kt new file mode 100644 index 0000000..2c41863 --- /dev/null +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/layout/serverPage.kt @@ -0,0 +1,26 @@ +package dev.adamko.kafkatorio.webmap.layout + +import dev.adamko.kafkatorio.webmap.SiteState +import io.kvision.core.Container +import io.kvision.html.div +import io.kvision.maps.externals.leaflet.layer.tile.GridLayer + + +fun Container.serverPage(state: SiteState) { + val factorioGameState = state.factorioGameState + div(className = "server-page") { + if (factorioGameState != null) { + + add(factorioGameState.map.kvMap) + + factorioGameState.map.kvMap.leafletMap { + invalidateSize(true) + eachLayer({ + if (it is GridLayer<*>) { + it.redraw() + } + }) + } + } + } +} diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/map.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/map.kt index adc7782..621f7ac 100644 --- a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/map.kt +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/map.kt @@ -1,5 +1,6 @@ package dev.adamko.kafkatorio.webmap +import dev.adamko.kafkatorio.schema.common.FactorioServerId import dev.adamko.kafkatorio.schema.common.ServerMapTileLayer import dev.adamko.kafkatorio.schema.common.ServerMapTilePngFilename import dev.adamko.kafkatorio.webmap.externals.TileOnLoadFn @@ -31,7 +32,9 @@ import org.w3c.fetch.RequestInit import org.w3c.fetch.RequestMode -class FactorioMap { +class FactorioMap( + private val serverId: FactorioServerId, +) { val kvMap: Maps = Maps { width = 800.px @@ -155,16 +158,18 @@ class FactorioMap { return baseTileLayer } + private fun tileLayerUrlTemplate(layer: ServerMapTileLayer): String { + return "/kafkatorio/data/servers/${serverId.id}/map/layers/${layer.dir}/s1/z{z}/x{x}/y{y}.png" + } + companion object { const val DYNAMIC_RELOAD_ATT = "dynamic-reload" - private val serverName: String - get() = window.location.pathname - .substringAfter("kafkatorio/data/servers/") - .substringBefore("/") +// private val serverName: String? +// get() = window.location.pathname +// .substringAfter("servers") +// .substringBefore("/") +// .ifBlank { null } - private fun tileLayerUrlTemplate(layer: ServerMapTileLayer): String { - return "/kafkatorio/data/servers/$serverName/map/layers/${layer.dir}/s1/z{z}/x{x}/y{y}.png" - } } } diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/routing.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/routing.kt new file mode 100644 index 0000000..f1e7043 --- /dev/null +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/routing.kt @@ -0,0 +1,161 @@ +package dev.adamko.kafkatorio.webmap + +import dev.adamko.kafkatorio.schema.common.FactorioServerId +import dev.adamko.kafkatorio.schema.common.Tick +import dev.adamko.kafkatorio.schema.packets.EventServerPacket +import dev.adamko.kafkatorio.schema.packets.PlayerUpdate +import dev.adamko.kafkatorio.webmap.services.EventsServerClient +import dev.adamko.kafkatorio.webmap.state.FactorioGameState +import io.kvision.navigo.Match +import io.kvision.navigo.Navigo +import io.kvision.redux.RAction +import io.kvision.redux.ReduxStore +import io.kvision.routing.routing +import kotlin.js.RegExp +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch + + +class SiteRouting( + val siteStateStore: ReduxStore +) : CoroutineScope by CoroutineScope(Dispatchers.Default + SupervisorJob()) { + + fun init() { + routing + .on(SiteView.HOME) { homepageView() } + .on(SiteView.SERVER) { match -> + serverView(match) + } + .resolve() + } + + private fun homepageView() { + siteStateStore.dispatch(SiteAction.HomePage) + launch { + val serverIds = EventsServerClient.serverIds() + siteStateStore.dispatch(SiteAction.ServerIdsLoaded(serverIds)) + } + } + + private fun serverView(match: Match) { + val serverId = match.data[0] as? String ?: error("invalid server id") + siteStateStore.dispatch(SiteAction.ServerPage(FactorioServerId(serverId))) + } +} + + +data class SiteState( + val view: SiteView = SiteView.HOME, + + val serverIds: List? = null, + + val factorioGameState: FactorioGameState? = null, +) { + + operator fun plus(action: SiteAction): SiteState = when (action) { + is SiteAction.HomePage -> copy(view = SiteView.HOME) + + is SiteAction.ServerPage -> copy( + view = SiteView.SERVER, + factorioGameState = FactorioGameState(action.serverId), + ) + + is SiteAction.ServerIdsLoaded -> copy(serverIds = action.serverIds) + + is SiteAction.FactorioUpdate -> copy( + factorioGameState = factorioGameState?.let { it + action } + ) + + is SiteAction.EventServerUpdate -> copy( + + ) + } +} + + +sealed interface SiteAction : RAction { + object HomePage : SiteAction + data class ServerPage(val serverId: FactorioServerId) : SiteAction + + + data class ServerIdsLoaded(val serverIds: List) : SiteAction + + + /** Updates from `events-server` */ + sealed class FactorioUpdate : SiteAction { + data class Player(val tick: Tick, val data: PlayerUpdate) : FactorioUpdate() + } + + data class EventServerUpdate(val packet: EventServerPacket) : SiteAction +} + + +sealed interface SiteView { + val pathMatcher: Any + + sealed interface StaticPath : SiteView { + override val pathMatcher: String + } + + sealed interface DynamicPath : SiteView { + override val pathMatcher: RegExp + } + + + object HOME : StaticPath { + override val pathMatcher: String = "/" + } + + object SERVER : DynamicPath { + override val pathMatcher: RegExp = RegExp("^servers/${FactorioServerId.validIdRegex}") + } +} + +//val SiteView.urlMatcher: String +// get() = when (this) { +// SiteView.HOME -> "/" +// SiteView.SERVER -> "servers/${FactorioServerId.validIdRegex}" +// } + +private fun Navigo.on(view: SiteView, handler: (Match) -> Unit): Navigo { + return when (view) { + is SiteView.StaticPath -> on(view.pathMatcher, { match: Match -> handler(match) }) + is SiteView.DynamicPath -> on(view.pathMatcher, { match: Match -> handler(match) }) + } +} + +//private fun Navigo.on( +// view: SiteView, +// before: BeforeHook? = null, +// after: AfterHook? = null, +// leave: LeaveHook? = null, +// already: AlreadyHook? = null, +// handler: (Match) -> Unit, +//): Navigo { +// +// val hooks: RouteHooks? = +// if (before != null || after != null || leave != null || already != null) { +// jso { +// if (before != null) this@jso.before = before +// if (after != null) this@jso.after = after +// if (leave != null) this@jso.leave = leave +// if (already != null) this@jso.already = already +// } +// } else { +// null +// } +// +// return when (view) { +// is SiteView.StaticPath -> when (hooks) { +// null -> on(view.pathMatcher, { match: Match -> handler(match) }) +// else -> on(view.pathMatcher, { match: Match -> handler(match) }, hooks) +// } +// +// is SiteView.DynamicPath -> when (hooks) { +// null -> on(view.pathMatcher, { match: Match -> handler(match) }) +// else -> on(view.pathMatcher, { match: Match -> handler(match) }, hooks) +// } +// } +//} diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/services/EventsServerClient.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/services/EventsServerClient.kt new file mode 100644 index 0000000..bc61491 --- /dev/null +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/services/EventsServerClient.kt @@ -0,0 +1,25 @@ +package dev.adamko.kafkatorio.webmap.services + +import dev.adamko.kafkatorio.schema.common.FactorioServerId +import io.kvision.rest.RestClient +import io.kvision.rest.call +import kotlinx.browser.window +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.await + +object EventsServerClient : + CoroutineScope by CoroutineScope(Dispatchers.Default + SupervisorJob()) { + + private val restClient = RestClient() + + + /** Fetch all possible server IDs. */ + suspend fun serverIds(): List = + restClient + .call>(window.location.origin + "/kafkatorio/data/servers") + .await() + + +} diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/websockets.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/services/WebsocketService.kt similarity index 84% rename from modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/websockets.kt rename to modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/services/WebsocketService.kt index b48f91f..a111a6c 100644 --- a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/websockets.kt +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/services/WebsocketService.kt @@ -1,4 +1,4 @@ -package dev.adamko.kafkatorio.webmap +package dev.adamko.kafkatorio.webmap.services import dev.adamko.kafkatorio.schema.packets.ConfigurationUpdate import dev.adamko.kafkatorio.schema.packets.ConsoleChatUpdate @@ -12,8 +12,11 @@ import dev.adamko.kafkatorio.schema.packets.MapChunkTileUpdate import dev.adamko.kafkatorio.schema.packets.PlayerUpdate import dev.adamko.kafkatorio.schema.packets.PrototypesUpdate import dev.adamko.kafkatorio.schema.packets.SurfaceUpdate -import dev.adamko.kafkatorio.webmap.state.FactorioGameState -import dev.adamko.kafkatorio.webmap.state.FactorioUpdate +import dev.adamko.kafkatorio.webmap.SiteAction +import dev.adamko.kafkatorio.webmap.SiteState +import dev.adamko.kafkatorio.webmap.config.ApplicationProperties +import dev.adamko.kafkatorio.webmap.config.jsonMapper +import dev.adamko.kafkatorio.webmap.rootJob import io.kvision.redux.ReduxStore import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineName @@ -29,14 +32,13 @@ import org.w3c.dom.events.Event class WebsocketService( - wsUrl: String = Props.websocketServerUrl, - private val reduxStore: ReduxStore, + private val siteStateStore: ReduxStore ) : CoroutineScope { override val coroutineContext: CoroutineContext = CoroutineName("WebsocketService") + Job(rootJob) - private val ws = WebSocket(wsUrl) + private val ws = WebSocket(ApplicationProperties.websocketServerUrl) // private val ws = WebSocket("ws://localhost:12080/ws/foo") private val _packetsFlow = MutableSharedFlow() @@ -78,7 +80,9 @@ class WebsocketService( is EventServerPacket.Kafkatorio -> when (val packetData = packet.packet.data) { is PlayerUpdate -> - reduxStore.dispatch(FactorioUpdate.Player(packet.packet.tick, packetData)) + siteStateStore.dispatch( + SiteAction.FactorioUpdate.Player(packet.packet.tick, packetData) + ) is ConfigurationUpdate, is ConsoleChatUpdate, diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/converters.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/services/converters.kt similarity index 87% rename from modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/converters.kt rename to modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/services/converters.kt index b47e73b..9b4753a 100644 --- a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/converters.kt +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/services/converters.kt @@ -1,4 +1,4 @@ -package dev.adamko.kafkatorio.webmap +package dev.adamko.kafkatorio.webmap.services import dev.adamko.kafkatorio.schema.common.MapEntityPosition import dev.adamko.kafkatorio.schema.packets.PlayerUpdate diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/state/PlayerProperties.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/state/PlayerProperties.kt new file mode 100644 index 0000000..94e25d2 --- /dev/null +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/state/PlayerProperties.kt @@ -0,0 +1,82 @@ +package dev.adamko.kafkatorio.webmap.state + + +import dev.adamko.kafkatorio.schema.common.Colour +import dev.adamko.kafkatorio.schema.common.MapEntityPosition +import dev.adamko.kafkatorio.schema.common.SurfaceIndex +import dev.adamko.kafkatorio.schema.common.toHexString +import dev.adamko.kafkatorio.schema.packets.PlayerUpdate +import dev.adamko.kafkatorio.webmap.services.latLng +import io.kvision.maps.externals.leaflet.geo.LatLng + +sealed interface PlayerProperties { + + val position: MapEntityPosition? + val surfaceIndex: SurfaceIndex? + val colour: Colour? + val name: String? + val latLng: LatLng? + val isConnected: Boolean + val isShowOnMap: Boolean + + + data class Partial( + override val position: MapEntityPosition? = null, + override val surfaceIndex: SurfaceIndex? = null, + override val colour: Colour? = null, + override val name: String? = null, + override val isConnected: Boolean = false, + override val isShowOnMap: Boolean = false, + ) : PlayerProperties { + override val latLng: LatLng? = null + } + + + data class Complete( + override val position: MapEntityPosition, + override val surfaceIndex: SurfaceIndex, + override val colour: Colour, + override val name: String, + override val isConnected: Boolean = false, + override val isShowOnMap: Boolean = false, + ) : PlayerProperties { + override val latLng: LatLng = position.latLng() + val hexColour: String = colour.toHexString(false) + } + + + fun update(update: PlayerUpdate): PlayerProperties { + + val position = update.position ?: this.position + val surfaceIndex = update.surfaceIndex ?: this.surfaceIndex + val colour = update.colour ?: this.colour + val name = update.name ?: this.name + val isConnected = update.isConnected + val isShowOnMap = update.isShowOnMap + + return when { + position != null + && surfaceIndex != null + && colour != null + && name != null -> + Complete( + position = position, + surfaceIndex = surfaceIndex, + colour = colour, + name = name, + isConnected = isConnected, + isShowOnMap = isShowOnMap, + ) + + else -> + Partial( + position = position, + surfaceIndex = surfaceIndex, + colour = colour, + name = name, + isConnected = isConnected, + isShowOnMap = isShowOnMap, + ) + } + } +} diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/state/playerState.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/state/playerState.kt index c659521..f627ffb 100644 --- a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/state/playerState.kt +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/state/playerState.kt @@ -1,22 +1,17 @@ package dev.adamko.kafkatorio.webmap.state -import dev.adamko.kafkatorio.schema.common.Colour -import dev.adamko.kafkatorio.schema.common.MapEntityPosition import dev.adamko.kafkatorio.schema.common.PlayerIndex -import dev.adamko.kafkatorio.schema.common.SurfaceIndex import dev.adamko.kafkatorio.schema.common.Tick -import dev.adamko.kafkatorio.schema.common.toHexString import dev.adamko.kafkatorio.schema.packets.PlayerUpdate -import dev.adamko.kafkatorio.webmap.latLng +import dev.adamko.kafkatorio.webmap.SiteAction import io.kvision.maps.Maps -import io.kvision.maps.externals.leaflet.geo.LatLng import io.kvision.maps.externals.leaflet.layer.vector.CircleMarker import kotlinx.js.jso fun handlePlayerUpdate( gameState: FactorioGameState, - update: FactorioUpdate.Player, + update: SiteAction.FactorioUpdate.Player, ): FactorioGameState { val previous = gameState.players[update.data.key.index] @@ -105,76 +100,3 @@ data class PlayerState( } } } - - -sealed interface PlayerProperties { - - val position: MapEntityPosition? - val surfaceIndex: SurfaceIndex? - val colour: Colour? - val name: String? - val latLng: LatLng? - val isConnected: Boolean - val isShowOnMap: Boolean - - - data class Partial( - override val position: MapEntityPosition? = null, - override val surfaceIndex: SurfaceIndex? = null, - override val colour: Colour? = null, - override val name: String? = null, - override val isConnected: Boolean = false, - override val isShowOnMap: Boolean = false, - ) : PlayerProperties { - override val latLng: LatLng? = null - } - - - data class Complete( - override val position: MapEntityPosition, - override val surfaceIndex: SurfaceIndex, - override val colour: Colour, - override val name: String, - override val isConnected: Boolean = false, - override val isShowOnMap: Boolean = false, - ) : PlayerProperties { - override val latLng: LatLng = position.latLng() - val hexColour: String = colour.toHexString(false) - } - - - fun update(update: PlayerUpdate): PlayerProperties { - - val position = update.position ?: this.position - val surfaceIndex = update.surfaceIndex ?: this.surfaceIndex - val colour = update.colour ?: this.colour - val name = update.name ?: this.name - val isConnected = update.isConnected - val isShowOnMap = update.isShowOnMap - - return when { - position != null - && surfaceIndex != null - && colour != null - && name != null -> - Complete( - position = position, - surfaceIndex = surfaceIndex, - colour = colour, - name = name, - isConnected = isConnected, - isShowOnMap = isShowOnMap, - ) - - else -> - Partial( - position = position, - surfaceIndex = surfaceIndex, - colour = colour, - name = name, - isConnected = isConnected, - isShowOnMap = isShowOnMap, - ) - } - } -} diff --git a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/state/state.kt b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/state/state.kt index 1e71ba4..69d0f6b 100644 --- a/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/state/state.kt +++ b/modules/web-map/src/main/kotlin/dev/adamko/kafkatorio/webmap/state/state.kt @@ -1,41 +1,33 @@ package dev.adamko.kafkatorio.webmap.state +import dev.adamko.kafkatorio.schema.common.FactorioServerId import dev.adamko.kafkatorio.schema.common.PlayerIndex -import dev.adamko.kafkatorio.schema.common.Tick -import dev.adamko.kafkatorio.schema.packets.PlayerUpdate import dev.adamko.kafkatorio.webmap.FactorioMap +import dev.adamko.kafkatorio.webmap.SiteAction import io.kvision.maps.externals.leaflet.layer.LayerGroup -import io.kvision.redux.ReduxStore -import io.kvision.redux.createReduxStore -import redux.RAction -fun createFactorioReduxStore(): ReduxStore = - createReduxStore( - ::factorioGameReducer, - FactorioGameState( - map = FactorioMap(), - players = emptyMap(), - ) - ) +//fun createFactorioReduxStore(): ReduxStore = +// createReduxStore( +// ::factorioGameReducer, +// FactorioGameState( +// map = FactorioMap(), +// players = emptyMap(), +// ) +// ) data class FactorioGameState( - val map: FactorioMap, - val players: Map, + val serverId: FactorioServerId, + val map: FactorioMap = FactorioMap(serverId), + val players: Map = emptyMap(), ) { val playerIconsLayer: LayerGroup get() = map.playerIconsLayer + + operator fun plus(action: SiteAction.FactorioUpdate): FactorioGameState = + when (action) { + is SiteAction.FactorioUpdate.Player -> handlePlayerUpdate(this, action) + } } - - -sealed class FactorioUpdate : RAction { - data class Player(val tick: Tick, val data: PlayerUpdate) : FactorioUpdate() -} - - -fun factorioGameReducer(gameState: FactorioGameState, update: FactorioUpdate): FactorioGameState = - when (update) { - is FactorioUpdate.Player -> handlePlayerUpdate(gameState, update) - }