web-map tidy up, and add diagram to README
This commit is contained in:
parent
3976c3cc1b
commit
de419aeae8
9 changed files with 60 additions and 49 deletions
38
README.md
38
README.md
|
|
@ -10,8 +10,42 @@ and WebSocket APIs.
|
|||
|
||||
Development is ongoing. At present the only application is a
|
||||
[live-view web map](https://factorio.adamko.dev), that shows the current status of a Factorio
|
||||
server, and any connected players. Other possibilities are exposing metrics, and inter-server
|
||||
communication.
|
||||
server, and any connected players. Other usage possibilities include exposing live metrics, and
|
||||
inter-server communication.
|
||||
|
||||
### Overview
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
|
||||
Factorio multiplayer server->>events-mod: in-game events
|
||||
Note over events-mod: converts events to packets
|
||||
events-mod->>logs processor: packets logged to console
|
||||
logs processor->>Kafka: publishes packets<br/>(Requires API key!)
|
||||
|
||||
Kafka->>events-server:
|
||||
|
||||
Note over events-server: Packets are processed
|
||||
|
||||
events-server->>web-map: Web map tiles (REST API)
|
||||
events-server-)web-map: WebSocket updates<br/>(e.g. player movement)
|
||||
```
|
||||
|
||||
1. The Kafkatorio game mod, [`events-mod`](./modules/events-mod), collects event data, de-bouncing
|
||||
and grouping events, and converts them to JSON packets
|
||||
2. `events-mod` prints the packets to the Factorio game server logs
|
||||
3. The Factorio logs are forwarded to a Kafka cluster, either using
|
||||
* [syslog](https://docs.docker.com/config/containers/logging/syslog/) (using Docker logging
|
||||
plugin)
|
||||
* or [Kafka Pipe](./modules/infra-kafka-pipe) (a script that reads the server logs and forwards
|
||||
them to a Kafka topic)
|
||||
4. The packets are consumed by [`events-server`](./modules/events-server), which processes the
|
||||
packets using Kafka Streams, creating and hosting map tiles.
|
||||
|
||||
`events-server` serves the map tiles via a REST API, and produces WebSocket messages regarding
|
||||
updates (for example, when a player's location has changed).
|
||||
5. The `web-map` uses Leaflet/JS to display the map, using the tiles and WebSocket messages from
|
||||
`events-server`
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
|||
|
|
@ -50,12 +50,10 @@ object App : Application() {
|
|||
|
||||
var siteStateStore: ReduxStore<SiteState, SiteAction> by appState
|
||||
private set
|
||||
// var wsService: WebsocketService by appState
|
||||
// private set
|
||||
|
||||
init {
|
||||
this.siteStateStore = createReduxStore(SiteState::plus, SiteState())
|
||||
// this.wsService = wsService
|
||||
|
||||
require("./css/kafkatorio.css")
|
||||
|
||||
WebsocketService.packetsFlow
|
||||
|
|
@ -78,9 +76,6 @@ object App : Application() {
|
|||
|
||||
this.appState = state.toMutableMap()
|
||||
|
||||
// val siteRouting = SiteRouting(reduxStore)
|
||||
// val websocketService = WebsocketService(siteStateStore)
|
||||
|
||||
SiteRouting.init()
|
||||
|
||||
root("kvapp") {
|
||||
|
|
@ -99,9 +94,6 @@ object App : Application() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// div("Kafkatorio Web Map")
|
||||
// add(kvMaps)
|
||||
}
|
||||
|
||||
// kvMaps.leafletMap {
|
||||
|
|
@ -124,20 +116,10 @@ object App : Application() {
|
|||
|
||||
}
|
||||
|
||||
|
||||
fun main() {
|
||||
startApplication(
|
||||
{
|
||||
// val reduxStore = createFactorioReduxStore()
|
||||
// val wsService = WebsocketService(
|
||||
// reduxStore = reduxStore,
|
||||
// )
|
||||
// App(
|
||||
// reduxStore = reduxStore,
|
||||
// wsService = wsService,
|
||||
// )
|
||||
|
||||
App
|
||||
},
|
||||
{ App },
|
||||
module.hot,
|
||||
BootstrapModule,
|
||||
// BootstrapCssModule,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import kotlinx.browser.window
|
|||
|
||||
|
||||
object ApplicationProperties {
|
||||
|
||||
/** Determine the WebSocket URL based on the window location URL. */
|
||||
@Suppress("HttpUrlsUsage")
|
||||
val websocketServerUrl: String
|
||||
get() = window.location.origin
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@ 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
|
||||
import io.kvision.navbar.nav
|
||||
import io.kvision.navbar.navLink
|
||||
import io.kvision.navbar.navbar
|
||||
|
||||
|
||||
fun Container.headerNav(state: SiteState) {
|
||||
nav(className = "navbar") {
|
||||
div(className = "container") {
|
||||
link("home", SiteView.HOME.path)
|
||||
navbar {
|
||||
nav {
|
||||
navLink("Home", SiteView.HOME.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ private fun Container.servers(state: SiteState) {
|
|||
// }
|
||||
// }
|
||||
|
||||
button(text = "$serverId") {
|
||||
button(text = "$serverId", className = "btn btn-link") {
|
||||
style = ButtonStyle.PRIMARY
|
||||
size = ButtonSize.SMALL
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import io.kvision.maps.externals.leaflet.layer.tile.GridLayer
|
|||
fun Container.serverPage(state: SiteState) {
|
||||
val factorioGameState = state.factorioGameState
|
||||
|
||||
div(className = "server-page") {
|
||||
div(className = "server-page container-fluid") {
|
||||
if (factorioGameState != null) {
|
||||
|
||||
add(factorioGameState.map.kvMap)
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ class FactorioMap(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private fun createServerTileLayer(
|
||||
layer: ServerMapTileLayer
|
||||
): TileLayer<TileLayer.TileLayerOptions> {
|
||||
|
|
@ -160,11 +161,11 @@ 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"
|
||||
}
|
||||
private fun tileLayerUrlTemplate(layer: ServerMapTileLayer): String =
|
||||
"/kafkatorio/data/servers/${serverId.id}/map/layers/${layer.dir}/s1/z{z}/x{x}/y{y}.png"
|
||||
|
||||
companion object {
|
||||
/** Add this attribute to `<img>` tiles that need to be dynamically refreshed. */
|
||||
const val DYNAMIC_RELOAD_ATT = "dynamic-reload"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import dev.adamko.kafkatorio.webmap.rootJob
|
|||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
|
@ -33,14 +34,13 @@ import org.w3c.dom.events.Event
|
|||
object WebsocketService : CoroutineScope {
|
||||
|
||||
override val coroutineContext: CoroutineContext =
|
||||
CoroutineName("WebsocketService") + Job(rootJob)
|
||||
Dispatchers.Default + CoroutineName("WebsocketService") + Job(rootJob)
|
||||
|
||||
private val ws = WebSocket(ApplicationProperties.websocketServerUrl)
|
||||
// private val ws = WebSocket("ws://localhost:12080/ws/foo")
|
||||
|
||||
private val _packetsFlow = MutableSharedFlow<EventServerPacket>()
|
||||
private val packetsMutableFlow = MutableSharedFlow<EventServerPacket>()
|
||||
val packetsFlow: SharedFlow<EventServerPacket>
|
||||
get() = _packetsFlow.asSharedFlow()
|
||||
get() = packetsMutableFlow.asSharedFlow()
|
||||
|
||||
init {
|
||||
ws.onmessage = ::handleMessageEvent
|
||||
|
|
@ -69,7 +69,7 @@ object WebsocketService : CoroutineScope {
|
|||
|
||||
if (packet != null) {
|
||||
|
||||
launch { _packetsFlow.emit(packet) }
|
||||
launch { packetsMutableFlow.emit(packet) }
|
||||
|
||||
when (packet) {
|
||||
is EventServerPacket.ChunkTileSaved -> {}
|
||||
|
|
|
|||
|
|
@ -7,16 +7,7 @@
|
|||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
/*html {*/
|
||||
/* background: #2B2B2BFF;*/
|
||||
/*}*/
|
||||
|
||||
/*body {*/
|
||||
/* background: #2B2B2BFF;*/
|
||||
/*}*/
|
||||
|
||||
.leaflet-container {
|
||||
/* make the Leaflet map background transparent, so it matches the site theme */
|
||||
background-color: rgba(255, 0, 0, 0.0) !important;
|
||||
/*background: rgb(34, 34, 34) !important;*/
|
||||
/*background: var(--bs-body-bg-rgb) !important;*/
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue