Add server & shared module and remove the serveur from the client app.

This commit is contained in:
Thomas Andres Gomez 2024-11-29 18:19:54 +01:00
parent fa87f05be6
commit 3419afbe59
47 changed files with 233 additions and 530 deletions

View file

@ -0,0 +1,7 @@
package com.pixelized.server.lwa
import com.pixelized.server.lwa.server.LocalServer
fun main() {
LocalServer().create().start()
}

View file

@ -0,0 +1,17 @@
package com.pixelized.server.lwa.extention
import com.pixelized.server.lwa.protocol.Message
import io.ktor.websocket.Frame
import io.ktor.websocket.readText
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToJsonElement
fun Json.decodeFromFrame(frame: Frame.Text): Message {
val json = frame.readText()
return decodeFromString<Message>(json)
}
fun Json.encodeToFrame(message: Message): Frame {
val json = encodeToJsonElement(message)
return Frame.Text(text = json.toString())
}

View file

@ -0,0 +1,101 @@
package com.pixelized.server.lwa.server
import com.pixelized.server.lwa.SERVER_PORT
import com.pixelized.server.lwa.extention.decodeFromFrame
import io.ktor.server.application.install
import io.ktor.server.engine.EmbeddedServer
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import io.ktor.server.netty.NettyApplicationEngine
import io.ktor.server.routing.routing
import io.ktor.server.websocket.DefaultWebSocketServerSession
import io.ktor.server.websocket.WebSockets
import io.ktor.server.websocket.pingPeriod
import io.ktor.server.websocket.timeout
import io.ktor.server.websocket.webSocket
import io.ktor.websocket.Frame
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.Json
import kotlin.time.Duration.Companion.seconds
// https://ktor.io/docs/server-websockets.html#handle-multiple-session
typealias Server = EmbeddedServer<NettyApplicationEngine, NettyApplicationEngine.Configuration>
class LocalServer {
private var server: Server? = null
private val scope = CoroutineScope(Dispatchers.IO)
private var networkJob: Job? = null
private val outgoingMessageBuffer = MutableSharedFlow<Frame>()
fun create(): LocalServer {
server = build {
println("Server launched")
val job = launch {
// send local message to the clients
outgoingMessageBuffer.collect { frame ->
send(frame)
}
}
runCatching {
// watching for clients incoming message
incoming.consumeEach { frame ->
if (frame is Frame.Text) {
val message = Json.decodeFromFrame(frame = frame)
println(message)
// broadcast to clients the message
outgoingMessageBuffer.emit(frame)
}
}
}.onFailure { exception ->
println("WebSocket exception: ${exception.localizedMessage}")
}.also {
job.cancel()
}
}
return this
}
fun start() {
runBlocking {
try {
server?.start(wait = true)
} catch (exception: Exception) {
// TODO
println("WebSocket exception: ${exception.localizedMessage}")
} finally {
println("Server close")
}
}
}
private fun build(
port: Int = SERVER_PORT,
handler: suspend DefaultWebSocketServerSession.() -> Unit
): EmbeddedServer<NettyApplicationEngine, NettyApplicationEngine.Configuration> {
return embeddedServer(
factory = Netty,
port = port,
module = {
install(WebSockets) {
pingPeriod = 15.seconds
timeout = 15.seconds
maxFrameSize = Long.MAX_VALUE
masking = false
}
routing {
webSocket(
path = "/ws",
handler = handler,
)
}
},
)
}
}