extract action command

This commit is contained in:
2025-03-05 00:31:24 +01:00
parent bc35131bfc
commit a4e83f7357
4 changed files with 83 additions and 64 deletions

View File

@@ -2,14 +2,12 @@ package eventDemo.app.command
import eventDemo.app.GameState import eventDemo.app.GameState
import eventDemo.app.command.command.GameCommand import eventDemo.app.command.command.GameCommand
import eventDemo.app.command.command.IWantToPlayCardCommand
import eventDemo.app.command.command.IamReadyToPlayCommand import eventDemo.app.command.command.IamReadyToPlayCommand
import eventDemo.app.command.command.IwantToPlayCardCommand
import eventDemo.app.entity.Player import eventDemo.app.entity.Player
import eventDemo.app.event.GameEventStream import eventDemo.app.event.GameEventStream
import eventDemo.app.event.buildStateFromEventStream import eventDemo.app.event.buildStateFromEventStream
import eventDemo.app.event.event.CardIsPlayedEvent
import eventDemo.app.event.event.GameEvent import eventDemo.app.event.event.GameEvent
import eventDemo.app.event.event.PlayerReadyEvent
import io.ktor.websocket.Frame import io.ktor.websocket.Frame
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -29,7 +27,7 @@ class GameCommandHandler(
outgoing: SendChannel<Frame>, outgoing: SendChannel<Frame>,
) { ) {
private val commandStream = GameCommandStream(incoming, outgoing) private val commandStream = GameCommandStream(incoming, outgoing)
private val playerNotifier = outgoing private val playerNotifier: (String) -> Unit = { runBlocking { outgoing.send(Frame.Text(it)) } }
/** /**
* Init the handler * Init the handler
@@ -41,48 +39,20 @@ class GameCommandHandler(
nack() nack()
} }
val state = command.buildState() val gameState = command.buildGameState()
when (command) { when (command) {
is IwantToPlayCardCommand -> { is IWantToPlayCardCommand -> {
// Check the command can be executed command.run(gameState, playerNotifier, eventStream)
if (state.commandCardCanBeExecuted(command)) {
eventStream.publish(
CardIsPlayedEvent(
command.payload.gameId,
command.payload.card,
command.payload.player,
),
)
} else {
runBlocking {
playerNotifier.send(Frame.Text("Command cannot be executed"))
}
}
} }
is IamReadyToPlayCommand -> { is IamReadyToPlayCommand -> {
if (state.playerIsAlreadyReady(command.payload.player)) { command.run(gameState, playerNotifier, eventStream)
nack()
} else {
PlayerReadyEvent(
command.payload.gameId,
command.payload.player,
)
}
} }
} }
} }
} }
} }
private fun GameState.playerIsAlreadyReady(player: Player): Boolean = readyPlayers.contains(player) private fun GameCommand.buildGameState(): GameState = payload.gameId.buildStateFromEventStream(eventStream)
private fun GameState.commandCardCanBeExecuted(command: IwantToPlayCardCommand): Boolean =
canBePlayThisCard(
command.payload.player,
command.payload.card,
)
private fun GameCommand.buildState(): GameState = payload.gameId.buildStateFromEventStream(eventStream)
} }

View File

@@ -0,0 +1,54 @@
package eventDemo.app.command.command
import eventDemo.app.GameState
import eventDemo.app.entity.Card
import eventDemo.app.entity.GameId
import eventDemo.app.entity.Player
import eventDemo.app.event.GameEventStream
import eventDemo.app.event.event.CardIsPlayedEvent
import eventDemo.libs.command.CommandId
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* A command to perform an action to play a new card
*/
@Serializable
@SerialName("PlayCard")
data class IWantToPlayCardCommand(
override val payload: Payload,
) : GameCommand {
override val name: String = "PlayCard"
override val id: CommandId = CommandId()
@Serializable
data class Payload(
override val gameId: GameId,
override val player: Player,
val card: Card,
) : GameCommand.Payload
fun run(
state: GameState,
playerNotifier: (String) -> Unit,
eventStream: GameEventStream,
) {
val commandCardCanBeExecuted: Boolean =
state.canBePlayThisCard(
payload.player,
payload.card,
)
if (commandCardCanBeExecuted) {
eventStream.publish(
CardIsPlayedEvent(
payload.gameId,
payload.card,
payload.player,
),
)
} else {
playerNotifier("Command cannot be executed")
}
}
}

View File

@@ -1,7 +1,10 @@
package eventDemo.app.command.command package eventDemo.app.command.command
import eventDemo.app.GameState
import eventDemo.app.entity.GameId import eventDemo.app.entity.GameId
import eventDemo.app.entity.Player import eventDemo.app.entity.Player
import eventDemo.app.event.GameEventStream
import eventDemo.app.event.event.PlayerReadyEvent
import eventDemo.libs.command.CommandId import eventDemo.libs.command.CommandId
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -22,4 +25,23 @@ data class IamReadyToPlayCommand(
override val gameId: GameId, override val gameId: GameId,
override val player: Player, override val player: Player,
) : GameCommand.Payload ) : GameCommand.Payload
fun run(
state: GameState,
playerNotifier: (String) -> Unit,
eventStream: GameEventStream,
) {
val playerIsAlreadyReady: Boolean = state.readyPlayers.contains(payload.player)
if (playerIsAlreadyReady) {
playerNotifier("You are already ready")
} else {
eventStream.publish(
PlayerReadyEvent(
payload.gameId,
payload.player,
),
)
}
}
} }

View File

@@ -1,27 +0,0 @@
package eventDemo.app.command.command
import eventDemo.app.entity.Card
import eventDemo.app.entity.GameId
import eventDemo.app.entity.Player
import eventDemo.libs.command.CommandId
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* A command to perform an action to play a new card
*/
@Serializable
@SerialName("PlayCard")
data class IwantToPlayCardCommand(
override val payload: Payload,
) : GameCommand {
override val name: String = "PlayCard"
override val id: CommandId = CommandId()
@Serializable
data class Payload(
override val gameId: GameId,
override val player: Player,
val card: Card,
) : GameCommand.Payload
}