update web-map to include event-update changes
This commit is contained in:
parent
ebb8482201
commit
52fcfbaa93
3 changed files with 159 additions and 63 deletions
|
|
@ -1,7 +1,10 @@
|
||||||
package dev.adamko.kafkatorio.webmap
|
package dev.adamko.kafkatorio.webmap
|
||||||
|
|
||||||
import dev.adamko.kafkatorio.events.schema.PlayerData
|
import dev.adamko.kafkatorio.events.schema.MapEntityPosition
|
||||||
|
import dev.adamko.kafkatorio.events.schema.PlayerUpdate
|
||||||
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
|
||||||
|
|
||||||
fun PlayerData.latLng(): LatLng = Maps.L.latLng((position.y * -1) to position.x)
|
fun PlayerUpdate.latLng(): LatLng? = position?.latLng()
|
||||||
|
|
||||||
|
fun MapEntityPosition.latLng(): LatLng = Maps.L.latLng((this.y * -1) to this.x)
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
package dev.adamko.kafkatorio.webmap
|
package dev.adamko.kafkatorio.webmap
|
||||||
|
|
||||||
import dev.adamko.kafkatorio.events.schema.FactorioEvent
|
import dev.adamko.kafkatorio.events.schema.Colour
|
||||||
import dev.adamko.kafkatorio.events.schema.PlayerData
|
import dev.adamko.kafkatorio.events.schema.MapEntityPosition
|
||||||
|
import dev.adamko.kafkatorio.events.schema.PlayerIndex
|
||||||
|
import dev.adamko.kafkatorio.events.schema.PlayerUpdate
|
||||||
|
import dev.adamko.kafkatorio.events.schema.SurfaceIndex
|
||||||
|
import dev.adamko.kafkatorio.events.schema.Tick
|
||||||
import dev.adamko.kafkatorio.events.schema.converters.toHexString
|
import dev.adamko.kafkatorio.events.schema.converters.toHexString
|
||||||
import io.kvision.maps.externals.leaflet.layer.overlay.Tooltip
|
|
||||||
import io.kvision.maps.externals.leaflet.layer.vector.CircleMarker
|
|
||||||
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.geo.LatLng
|
||||||
|
import io.kvision.maps.externals.leaflet.layer.overlay.Tooltip
|
||||||
|
import io.kvision.maps.externals.leaflet.layer.vector.CircleMarker
|
||||||
import io.kvision.redux.ReduxStore
|
import io.kvision.redux.ReduxStore
|
||||||
import io.kvision.redux.createReduxStore
|
import io.kvision.redux.createReduxStore
|
||||||
import io.kvision.utils.obj
|
import io.kvision.utils.obj
|
||||||
|
|
@ -25,71 +30,166 @@ fun createFactorioReduxStore(): ReduxStore<FactorioGameState, FactorioUpdate> =
|
||||||
|
|
||||||
data class FactorioGameState(
|
data class FactorioGameState(
|
||||||
val kvMaps: Maps,
|
val kvMaps: Maps,
|
||||||
val players: Map<String, PlayerState>,
|
val players: Map<PlayerIndex, PlayerState>,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
sealed class FactorioUpdate : RAction {
|
sealed class FactorioUpdate : RAction {
|
||||||
data class PlayerUpdate(val event: FactorioEvent, val playerData: PlayerData) :
|
data class Player(val tick: Tick, val data: PlayerUpdate) : FactorioUpdate()
|
||||||
FactorioUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun factorioGameReducer(state: FactorioGameState, update: FactorioUpdate): FactorioGameState =
|
fun factorioGameReducer(state: FactorioGameState, update: FactorioUpdate): FactorioGameState =
|
||||||
when (update) {
|
when (update) {
|
||||||
is FactorioUpdate.PlayerUpdate -> {
|
is FactorioUpdate.Player -> {
|
||||||
|
|
||||||
val playerState = state.players.getOrElse(update.playerData.name) {
|
val playerState = state.players.getOrElse(update.data.index) {
|
||||||
val newState = PlayerState(update.playerData)
|
PlayerState(update.data.index, update.tick)
|
||||||
state.kvMaps.leafletMap {
|
|
||||||
addLayer(newState.mapMarker)
|
|
||||||
newState.mapMarker.openPopup()
|
|
||||||
// setView(update.playerData.latLng())
|
|
||||||
}
|
|
||||||
newState
|
|
||||||
}
|
}
|
||||||
|
|
||||||
playerState.update(update.playerData)
|
playerState.update(update.tick, update.data)
|
||||||
|
|
||||||
val updatedPlayersState = state.players + (update.playerData.name to playerState)
|
state.kvMaps.leafletMap {
|
||||||
|
|
||||||
|
val playerMarker = playerState.mapMarker
|
||||||
|
|
||||||
|
if (playerMarker != null) {
|
||||||
|
when (playerState.playerDetails) {
|
||||||
|
is PlayerDetailsState.Known -> if (!hasLayer(playerMarker)) addLayer(playerMarker)
|
||||||
|
is PlayerDetailsState.Unknown -> if (hasLayer(playerMarker)) removeLayer(playerMarker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val updatedPlayersState = state.players + (playerState.index to playerState)
|
||||||
state.copy(players = updatedPlayersState)
|
state.copy(players = updatedPlayersState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
data class PlayerState(
|
class PlayerState(
|
||||||
private var playerData: PlayerData,
|
val index: PlayerIndex,
|
||||||
val mapMarker: CircleMarker = createMapMarker(playerData)
|
private var lastTick: Tick,
|
||||||
) {
|
) {
|
||||||
|
var playerDetails: PlayerDetailsState = PlayerDetailsState.Unknown(index)
|
||||||
|
private set
|
||||||
|
|
||||||
fun update(playerData: PlayerData) {
|
var mapMarker: CircleMarker? = null
|
||||||
this.playerData = playerData
|
private set
|
||||||
mapMarker.setLatLng(playerData.latLng())
|
|
||||||
println("updated player ${playerData.name}")
|
fun update(tick: Tick, update: PlayerUpdate) {
|
||||||
|
|
||||||
|
if (lastTick.value > tick.value) {
|
||||||
|
println("discarding old PlayerUpdate, ${lastTick.value} > ${tick.value}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.lastTick = tick
|
||||||
|
this.playerDetails = updatePlayerDetails(update)
|
||||||
|
|
||||||
|
updateMapPosition()
|
||||||
|
|
||||||
|
println("updated PlayerState index:${playerDetails.index}, name:${playerDetails.name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
private fun updatePlayerDetails(update: PlayerUpdate): PlayerDetailsState {
|
||||||
|
|
||||||
fun createMapMarker(playerData: PlayerData): CircleMarker {
|
val position = update.position ?: playerDetails.position
|
||||||
|
val surfaceIndex = update.surfaceIndex ?: playerDetails.surfaceIndex
|
||||||
|
val colour = update.colour ?: playerDetails.colour
|
||||||
|
val name = update.name ?: playerDetails.name
|
||||||
|
|
||||||
val playerLatLng = playerData.latLng()
|
return when {
|
||||||
val playerMapTooltip = "${playerData.name} [$playerLatLng]"
|
position != null && surfaceIndex != null && colour != null && name != null ->
|
||||||
|
PlayerDetailsState.Known(
|
||||||
|
index = playerDetails.index,
|
||||||
|
position = position,
|
||||||
|
surfaceIndex = surfaceIndex,
|
||||||
|
colour = colour,
|
||||||
|
name = name,
|
||||||
|
)
|
||||||
|
|
||||||
val circle = L.circleMarker(playerLatLng) {
|
else ->
|
||||||
color = playerData.colour.toHexString(false)
|
PlayerDetailsState.Unknown(
|
||||||
fillColor = color
|
index = playerDetails.index,
|
||||||
fillOpacity = 1f
|
position = position,
|
||||||
radius = 10
|
surfaceIndex = surfaceIndex,
|
||||||
className = playerData.objectName.name
|
colour = colour,
|
||||||
// interactive = false
|
name = name,
|
||||||
}
|
)
|
||||||
|
|
||||||
circle.bindTooltip(playerMapTooltip, obj<Tooltip.TooltipOptions> {
|
|
||||||
// interactive = false
|
|
||||||
})
|
|
||||||
|
|
||||||
return circle
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun updateMapPosition() {
|
||||||
|
|
||||||
|
when (val details = playerDetails) {
|
||||||
|
is PlayerDetailsState.Unknown -> {
|
||||||
|
println("removing player ${details.index} map marker")
|
||||||
|
mapMarker?.remove()
|
||||||
|
}
|
||||||
|
is PlayerDetailsState.Known -> updateMapMarker(details)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateMapMarker(playerDetails: PlayerDetailsState.Known) {
|
||||||
|
|
||||||
|
if (mapMarker == null) {
|
||||||
|
mapMarker = L.circleMarker(playerDetails.latLng) {
|
||||||
|
fillOpacity = 1f
|
||||||
|
radius = 10
|
||||||
|
className = "player-marker"
|
||||||
|
// interactive = false
|
||||||
|
}.also { circle ->
|
||||||
|
circle.bindTooltip("", obj<Tooltip.TooltipOptions> {
|
||||||
|
// interactive = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mapMarker?.apply {
|
||||||
|
val playerColour = playerDetails.colour.toHexString(false)
|
||||||
|
options.color = playerColour
|
||||||
|
options.fillColor = playerColour
|
||||||
|
|
||||||
|
setTooltipContent("${playerDetails.name} [${playerDetails.latLng}]")
|
||||||
|
setLatLng(playerDetails.latLng)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sealed interface PlayerDetailsState {
|
||||||
|
|
||||||
|
val index: PlayerIndex
|
||||||
|
|
||||||
|
val position: MapEntityPosition?
|
||||||
|
val surfaceIndex: SurfaceIndex?
|
||||||
|
val colour: Colour?
|
||||||
|
val name: String?
|
||||||
|
val latLng: LatLng?
|
||||||
|
|
||||||
|
data class Unknown(
|
||||||
|
override val index: PlayerIndex,
|
||||||
|
|
||||||
|
override val position: MapEntityPosition? = null,
|
||||||
|
override val surfaceIndex: SurfaceIndex? = null,
|
||||||
|
override val colour: Colour? = null,
|
||||||
|
override val name: String? = null,
|
||||||
|
) : PlayerDetailsState {
|
||||||
|
override val latLng: LatLng? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Known(
|
||||||
|
override val index: PlayerIndex,
|
||||||
|
|
||||||
|
override val position: MapEntityPosition,
|
||||||
|
override val surfaceIndex: SurfaceIndex,
|
||||||
|
override val colour: Colour,
|
||||||
|
override val name: String,
|
||||||
|
) : PlayerDetailsState {
|
||||||
|
override val latLng: LatLng = position.latLng()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
package dev.adamko.kafkatorio.webmap
|
package dev.adamko.kafkatorio.webmap
|
||||||
|
|
||||||
import dev.adamko.kafkatorio.events.schema.ConsoleChatMessage
|
import dev.adamko.kafkatorio.events.schema.EntityUpdate
|
||||||
import dev.adamko.kafkatorio.events.schema.EntityData
|
|
||||||
import dev.adamko.kafkatorio.events.schema.FactorioConfigurationUpdate
|
import dev.adamko.kafkatorio.events.schema.FactorioConfigurationUpdate
|
||||||
import dev.adamko.kafkatorio.events.schema.FactorioEvent
|
import dev.adamko.kafkatorio.events.schema.FactorioEvent
|
||||||
import dev.adamko.kafkatorio.events.schema.FactorioEventUpdatePacket
|
import dev.adamko.kafkatorio.events.schema.FactorioEventUpdatePacket
|
||||||
import dev.adamko.kafkatorio.events.schema.FactorioPrototypes
|
import dev.adamko.kafkatorio.events.schema.FactorioPrototypes
|
||||||
import dev.adamko.kafkatorio.events.schema.KafkatorioPacket
|
import dev.adamko.kafkatorio.events.schema.KafkatorioPacket
|
||||||
import dev.adamko.kafkatorio.events.schema.MapChunk
|
import dev.adamko.kafkatorio.events.schema.MapChunkUpdate
|
||||||
import dev.adamko.kafkatorio.events.schema.MapTiles
|
import dev.adamko.kafkatorio.events.schema.PlayerUpdate
|
||||||
import dev.adamko.kafkatorio.events.schema.PlayerData
|
|
||||||
import dev.adamko.kafkatorio.events.schema.SurfaceData
|
|
||||||
import io.kvision.redux.ReduxStore
|
import io.kvision.redux.ReduxStore
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import org.w3c.dom.MessageEvent
|
import org.w3c.dom.MessageEvent
|
||||||
|
|
@ -39,22 +36,18 @@ class WebsocketService(
|
||||||
println(data.replace('\n', ' '))
|
println(data.replace('\n', ' '))
|
||||||
|
|
||||||
when (val event: KafkatorioPacket = jsonMapper.decodeFromString(data)) {
|
when (val event: KafkatorioPacket = jsonMapper.decodeFromString(data)) {
|
||||||
is FactorioEvent -> {
|
is FactorioEvent -> {
|
||||||
when (val eventData = event.data) {
|
}
|
||||||
is PlayerData ->
|
is FactorioEventUpdatePacket -> {
|
||||||
reduxStore.dispatch(FactorioUpdate.PlayerUpdate(event, eventData))
|
when (val eventUpdate = event.update) {
|
||||||
is ConsoleChatMessage,
|
is PlayerUpdate -> reduxStore.dispatch(FactorioUpdate.Player(event.tick, eventUpdate))
|
||||||
is EntityData,
|
is EntityUpdate,
|
||||||
is MapChunk,
|
is MapChunkUpdate -> {
|
||||||
// is MapTile,
|
|
||||||
is SurfaceData,
|
|
||||||
is MapTiles -> {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is FactorioEventUpdatePacket,
|
|
||||||
is FactorioConfigurationUpdate,
|
is FactorioConfigurationUpdate,
|
||||||
is FactorioPrototypes -> {
|
is FactorioPrototypes -> {
|
||||||
// to be continued...
|
// to be continued...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue