add IamReadyToPlayCommand & refactoring

This commit is contained in:
2025-03-05 00:07:41 +01:00
parent 06443d7efa
commit bc35131bfc
41 changed files with 404 additions and 322 deletions

View File

@@ -1,6 +1,6 @@
package eventDemo.configuration
import eventDemo.app.GameEventReactionListener
import eventDemo.app.eventListener.GameEventReactionListener
import io.ktor.server.application.Application
import org.koin.ktor.ext.get
@@ -11,11 +11,11 @@ fun Application.configure() {
configureSerialization()
configureSockets()
configureWebSocketsGameRoute(get(), get())
configureWebSockets()
declareWebSocketsGameRoute(get(), get())
configureHttp()
configureHttpRouting()
declareHttpGameRoute()
GameEventReactionListener(get(), get())
.init()

View File

@@ -5,9 +5,13 @@ import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.plugins.autohead.AutoHeadResponse
import io.ktor.server.plugins.cors.routing.CORS
import io.ktor.server.plugins.statuspages.StatusPages
import io.ktor.server.resources.Resources
import io.ktor.server.response.respondText
fun Application.configureHttp() {
fun Application.configureHttpRouting() {
install(CORS) {
allowMethod(HttpMethod.Options)
allowMethod(HttpMethod.Put)
@@ -18,6 +22,16 @@ fun Application.configureHttp() {
allowHeader("MyCustomHeader")
anyHost() // @TODO: Don't do this in production if possible. Try to limit it.
}
install(AutoHeadResponse)
install(Resources)
install(StatusPages) {
exception<BadRequestException> { call, cause ->
call.respondText(text = "400: $cause", status = HttpStatusCode.BadRequest)
}
exception<Throwable> { call, cause ->
call.respondText(text = "500: $cause", status = HttpStatusCode.InternalServerError)
}
}
}
class BadRequestException(

View File

@@ -0,0 +1,23 @@
package eventDemo.configuration
import eventDemo.shared.UUIDSerializer
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import java.util.UUID
fun Application.configureSerialization() {
install(ContentNegotiation) {
json(
Json {
serializersModule =
SerializersModule {
contextual(UUID::class) { UUIDSerializer }
}
},
)
}
}

View File

@@ -0,0 +1,17 @@
package eventDemo.configuration
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.websocket.WebSockets
import io.ktor.server.websocket.pingPeriod
import io.ktor.server.websocket.timeout
import java.time.Duration
fun Application.configureWebSockets() {
install(WebSockets) {
pingPeriod = Duration.ofSeconds(15)
timeout = Duration.ofSeconds(15)
maxFrameSize = Long.MAX_VALUE
masking = false
}
}

View File

@@ -0,0 +1,16 @@
package eventDemo.configuration
import eventDemo.app.command.gameSocket
import eventDemo.app.event.GameEventBus
import eventDemo.app.event.GameEventStream
import io.ktor.server.application.Application
import io.ktor.server.routing.routing
fun Application.declareWebSocketsGameRoute(
eventStream: GameEventStream,
eventBus: GameEventBus,
) {
routing {
gameSocket(eventStream, eventBus)
}
}

View File

@@ -0,0 +1,12 @@
package eventDemo.configuration
import eventDemo.app.query.readTheGameState
import io.ktor.server.application.Application
import io.ktor.server.routing.routing
import org.koin.ktor.ext.get
fun Application.declareHttpGameRoute() {
routing {
readTheGameState(get())
}
}

View File

@@ -1,30 +0,0 @@
package eventDemo.configuration
import eventDemo.app.actions.readGameState
import eventDemo.app.actions.readLastPlayedCard
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.plugins.autohead.AutoHeadResponse
import io.ktor.server.plugins.statuspages.StatusPages
import io.ktor.server.resources.Resources
import io.ktor.server.response.respondText
import io.ktor.server.routing.routing
fun Application.configureHttpRouting() {
install(AutoHeadResponse)
install(Resources)
install(StatusPages) {
exception<BadRequestException> { call, cause ->
call.respondText(text = "400: $cause", status = HttpStatusCode.BadRequest)
}
exception<Throwable> { call, cause ->
call.respondText(text = "500: $cause", status = HttpStatusCode.InternalServerError)
}
}
routing {
readLastPlayedCard()
readGameState()
}
}

View File

@@ -1,83 +0,0 @@
package eventDemo.configuration
import eventDemo.app.GameId
import eventDemo.app.entity.Player.PlayerId
import eventDemo.libs.command.CommandId
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import java.util.UUID
fun Application.configureSerialization() {
install(ContentNegotiation) {
json(
Json {
serializersModule =
SerializersModule {
contextual(UUID::class) { UUIDSerializer }
}
},
)
}
}
object CommandIdSerializer : KSerializer<CommandId> {
override fun deserialize(decoder: Decoder): CommandId = CommandId(decoder.decodeString())
override fun serialize(
encoder: Encoder,
value: CommandId,
) {
encoder.encodeString(value.toString())
}
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("CommandId", PrimitiveKind.STRING)
}
object PlayerIdSerializer : KSerializer<PlayerId> {
override fun deserialize(decoder: Decoder): PlayerId = PlayerId(UUID.fromString(decoder.decodeString()))
override fun serialize(
encoder: Encoder,
value: PlayerId,
) {
encoder.encodeString(value.id.toString())
}
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("GameId", PrimitiveKind.STRING)
}
object GameIdSerializer : KSerializer<GameId> {
override fun deserialize(decoder: Decoder): GameId = GameId(UUID.fromString(decoder.decodeString()))
override fun serialize(
encoder: Encoder,
value: GameId,
) {
encoder.encodeString(value.id.toString())
}
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("GameId", PrimitiveKind.STRING)
}
object UUIDSerializer : KSerializer<UUID> {
override fun deserialize(decoder: Decoder): UUID = UUID.fromString(decoder.decodeString())
override fun serialize(
encoder: Encoder,
value: UUID,
) {
encoder.encodeString(value.toString())
}
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING)
}

View File

@@ -1,50 +0,0 @@
package eventDemo.configuration
import eventDemo.app.GameEventPlayerNotificationListener
import eventDemo.app.actions.GameCommandHandler
import eventDemo.app.entity.Player
import eventDemo.app.event.GameEventBus
import eventDemo.app.event.GameEventStream
import io.ktor.server.application.Application
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.install
import io.ktor.server.auth.authenticate
import io.ktor.server.auth.jwt.JWTPrincipal
import io.ktor.server.auth.principal
import io.ktor.server.routing.routing
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 java.time.Duration
fun Application.configureSockets() {
install(WebSockets) {
pingPeriod = Duration.ofSeconds(15)
timeout = Duration.ofSeconds(15)
maxFrameSize = Long.MAX_VALUE
masking = false
}
}
fun Application.configureWebSocketsGameRoute(
eventStream: GameEventStream,
eventBus: GameEventBus,
) {
routing {
authenticate {
webSocket("/game") {
GameCommandHandler(eventStream, incoming, outgoing).init(call.getPlayer())
GameEventPlayerNotificationListener(eventBus, outgoing).init()
}
}
}
}
fun ApplicationCall.getPlayer() =
principal<JWTPrincipal>()!!.run {
Player(
id = payload.getClaim("playerid").asString(),
name = payload.getClaim("username").asString(),
)
}