Create GameStateRepository
Use GameState.apply() to build Projection Create GameEventHandler Add PlayerWinEvent
This commit is contained in:
@@ -1,15 +1,14 @@
|
|||||||
package eventDemo.app.command
|
package eventDemo.app.command
|
||||||
|
|
||||||
import eventDemo.app.GameState
|
|
||||||
import eventDemo.app.command.command.GameCommand
|
import eventDemo.app.command.command.GameCommand
|
||||||
import eventDemo.app.command.command.ICantPlayCommand
|
import eventDemo.app.command.command.ICantPlayCommand
|
||||||
import eventDemo.app.command.command.IWantToJoinTheGameCommand
|
import eventDemo.app.command.command.IWantToJoinTheGameCommand
|
||||||
import eventDemo.app.command.command.IWantToPlayCardCommand
|
import eventDemo.app.command.command.IWantToPlayCardCommand
|
||||||
import eventDemo.app.command.command.IamReadyToPlayCommand
|
import eventDemo.app.command.command.IamReadyToPlayCommand
|
||||||
import eventDemo.app.entity.Player
|
import eventDemo.app.entity.Player
|
||||||
import eventDemo.app.event.GameEventStream
|
import eventDemo.app.event.GameEventHandler
|
||||||
import eventDemo.app.event.buildStateFromEventStream
|
|
||||||
import eventDemo.app.event.event.GameEvent
|
import eventDemo.app.event.event.GameEvent
|
||||||
|
import eventDemo.app.event.projection.GameStateRepository
|
||||||
import eventDemo.app.notification.ErrorNotification
|
import eventDemo.app.notification.ErrorNotification
|
||||||
import eventDemo.shared.toFrame
|
import eventDemo.shared.toFrame
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
@@ -24,7 +23,8 @@ import kotlinx.coroutines.channels.trySendBlocking
|
|||||||
* This action can be executing an action and produce a new [GameEvent] after verification.
|
* This action can be executing an action and produce a new [GameEvent] after verification.
|
||||||
*/
|
*/
|
||||||
class GameCommandHandler(
|
class GameCommandHandler(
|
||||||
private val eventStream: GameEventStream,
|
private val eventHandler: GameEventHandler,
|
||||||
|
private val gameStateRepository: GameStateRepository,
|
||||||
) {
|
) {
|
||||||
private val logger = KotlinLogging.logger { }
|
private val logger = KotlinLogging.logger { }
|
||||||
|
|
||||||
@@ -58,16 +58,14 @@ class GameCommandHandler(
|
|||||||
nack()
|
nack()
|
||||||
}
|
}
|
||||||
|
|
||||||
val gameState = command.buildGameState()
|
val gameState = gameStateRepository.get(command.payload.gameId)
|
||||||
|
|
||||||
when (command) {
|
when (command) {
|
||||||
is IWantToPlayCardCommand -> command.run(gameState, playerErrorNotifier, eventStream)
|
is IWantToPlayCardCommand -> command.run(gameState, playerErrorNotifier, eventHandler)
|
||||||
is IamReadyToPlayCommand -> command.run(gameState, playerErrorNotifier, eventStream)
|
is IamReadyToPlayCommand -> command.run(gameState, playerErrorNotifier, eventHandler)
|
||||||
is IWantToJoinTheGameCommand -> command.run(gameState, playerErrorNotifier, eventStream)
|
is IWantToJoinTheGameCommand -> command.run(gameState, playerErrorNotifier, eventHandler)
|
||||||
is ICantPlayCommand -> command.run(gameState, playerErrorNotifier, eventStream)
|
is ICantPlayCommand -> command.run(gameState, playerErrorNotifier, eventHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun GameCommand.buildGameState(): GameState = payload.gameId.buildStateFromEventStream(eventStream)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +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.GameEventHandler
|
||||||
import eventDemo.app.event.event.PlayerHavePassEvent
|
import eventDemo.app.event.event.PlayerHavePassEvent
|
||||||
|
import eventDemo.app.event.projection.GameState
|
||||||
import eventDemo.libs.command.CommandId
|
import eventDemo.libs.command.CommandId
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@@ -26,13 +26,13 @@ data class ICantPlayCommand(
|
|||||||
fun run(
|
fun run(
|
||||||
state: GameState,
|
state: GameState,
|
||||||
playerErrorNotifier: (String) -> Unit,
|
playerErrorNotifier: (String) -> Unit,
|
||||||
eventStream: GameEventStream,
|
eventHandler: GameEventHandler,
|
||||||
) {
|
) {
|
||||||
val playableCards = state.playableCards(payload.player)
|
val playableCards = state.playableCards(payload.player)
|
||||||
if (playableCards.isEmpty()) {
|
if (playableCards.isEmpty()) {
|
||||||
val takenCard = state.deck.stack.first()
|
val takenCard = state.deck.stack.first()
|
||||||
|
|
||||||
eventStream.publish(
|
eventHandler.handle(
|
||||||
PlayerHavePassEvent(
|
PlayerHavePassEvent(
|
||||||
gameId = payload.gameId,
|
gameId = payload.gameId,
|
||||||
player = payload.player,
|
player = payload.player,
|
||||||
|
|||||||
@@ -1,10 +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.GameEventHandler
|
||||||
import eventDemo.app.event.event.NewPlayerEvent
|
import eventDemo.app.event.event.NewPlayerEvent
|
||||||
|
import eventDemo.app.event.projection.GameState
|
||||||
import eventDemo.libs.command.CommandId
|
import eventDemo.libs.command.CommandId
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
@@ -27,11 +27,11 @@ data class IWantToJoinTheGameCommand(
|
|||||||
fun run(
|
fun run(
|
||||||
state: GameState,
|
state: GameState,
|
||||||
playerErrorNotifier: (String) -> Unit,
|
playerErrorNotifier: (String) -> Unit,
|
||||||
eventStream: GameEventStream,
|
eventHandler: GameEventHandler,
|
||||||
) {
|
) {
|
||||||
val logger = KotlinLogging.logger {}
|
val logger = KotlinLogging.logger {}
|
||||||
if (!state.isStarted) {
|
if (!state.isStarted) {
|
||||||
eventStream.publish(
|
eventHandler.handle(
|
||||||
NewPlayerEvent(
|
NewPlayerEvent(
|
||||||
payload.gameId,
|
payload.gameId,
|
||||||
payload.player,
|
payload.player,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package eventDemo.app.command.command
|
package eventDemo.app.command.command
|
||||||
|
|
||||||
import eventDemo.app.GameState
|
|
||||||
import eventDemo.app.entity.Card
|
import eventDemo.app.entity.Card
|
||||||
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.GameEventHandler
|
||||||
import eventDemo.app.event.event.CardIsPlayedEvent
|
import eventDemo.app.event.event.CardIsPlayedEvent
|
||||||
|
import eventDemo.app.event.projection.GameState
|
||||||
import eventDemo.libs.command.CommandId
|
import eventDemo.libs.command.CommandId
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ data class IWantToPlayCardCommand(
|
|||||||
fun run(
|
fun run(
|
||||||
state: GameState,
|
state: GameState,
|
||||||
playerErrorNotifier: (String) -> Unit,
|
playerErrorNotifier: (String) -> Unit,
|
||||||
eventStream: GameEventStream,
|
eventHandler: GameEventHandler,
|
||||||
) {
|
) {
|
||||||
if (!state.isStarted) {
|
if (!state.isStarted) {
|
||||||
playerErrorNotifier("The game is Not started")
|
playerErrorNotifier("The game is Not started")
|
||||||
@@ -36,7 +36,7 @@ data class IWantToPlayCardCommand(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state.canBePlayThisCard(payload.player, payload.card)) {
|
if (state.canBePlayThisCard(payload.player, payload.card)) {
|
||||||
eventStream.publish(
|
eventHandler.handle(
|
||||||
CardIsPlayedEvent(
|
CardIsPlayedEvent(
|
||||||
payload.gameId,
|
payload.gameId,
|
||||||
payload.card,
|
payload.card,
|
||||||
|
|||||||
@@ -1,10 +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.GameEventHandler
|
||||||
import eventDemo.app.event.event.PlayerReadyEvent
|
import eventDemo.app.event.event.PlayerReadyEvent
|
||||||
|
import eventDemo.app.event.projection.GameState
|
||||||
import eventDemo.libs.command.CommandId
|
import eventDemo.libs.command.CommandId
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ data class IamReadyToPlayCommand(
|
|||||||
fun run(
|
fun run(
|
||||||
state: GameState,
|
state: GameState,
|
||||||
playerErrorNotifier: (String) -> Unit,
|
playerErrorNotifier: (String) -> Unit,
|
||||||
eventStream: GameEventStream,
|
eventHandler: GameEventHandler,
|
||||||
) {
|
) {
|
||||||
val playerExist: Boolean = state.players.contains(payload.player)
|
val playerExist: Boolean = state.players.contains(payload.player)
|
||||||
val playerIsAlreadyReady: Boolean = state.readyPlayers.contains(payload.player)
|
val playerIsAlreadyReady: Boolean = state.readyPlayers.contains(payload.player)
|
||||||
@@ -36,7 +36,7 @@ data class IamReadyToPlayCommand(
|
|||||||
} else if (playerIsAlreadyReady) {
|
} else if (playerIsAlreadyReady) {
|
||||||
playerErrorNotifier("You are already ready")
|
playerErrorNotifier("You are already ready")
|
||||||
} else {
|
} else {
|
||||||
eventStream.publish(
|
eventHandler.handle(
|
||||||
PlayerReadyEvent(
|
PlayerReadyEvent(
|
||||||
payload.gameId,
|
payload.gameId,
|
||||||
payload.player,
|
payload.player,
|
||||||
|
|||||||
@@ -47,6 +47,11 @@ data class Deck(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun playerHasNoCardLeft(): List<Player.PlayerId> =
|
||||||
|
playersHands
|
||||||
|
.filter { (playerId, hand) -> hand.isEmpty() }
|
||||||
|
.map { (playerId, hand) -> playerId }
|
||||||
|
|
||||||
private fun take(n: Int): Pair<Deck, List<Card>> {
|
private fun take(n: Int): Pair<Deck, List<Card>> {
|
||||||
val takenCards = stack.take(n)
|
val takenCards = stack.take(n)
|
||||||
val newStack = stack.filterNot { takenCards.contains(it) }.toStack()
|
val newStack = stack.filterNot { takenCards.contains(it) }.toStack()
|
||||||
|
|||||||
27
src/main/kotlin/eventDemo/app/event/GameEventHandler.kt
Normal file
27
src/main/kotlin/eventDemo/app/event/GameEventHandler.kt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package eventDemo.app.event
|
||||||
|
|
||||||
|
import eventDemo.app.event.event.GameEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A stream to publish and read the played card event.
|
||||||
|
*/
|
||||||
|
class GameEventHandler(
|
||||||
|
private val eventBus: GameEventBus,
|
||||||
|
private val eventStream: GameEventStream,
|
||||||
|
) {
|
||||||
|
private val projectionsBuilders: MutableList<(GameEvent) -> Unit> = mutableListOf()
|
||||||
|
|
||||||
|
fun registerProjectionBuilder(builder: GameProjectionBuilder) {
|
||||||
|
projectionsBuilders.add(builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handle(vararg events: GameEvent) {
|
||||||
|
events.forEach { event ->
|
||||||
|
eventStream.publish(event)
|
||||||
|
projectionsBuilders.forEach { it(event) }
|
||||||
|
eventBus.publish(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias GameProjectionBuilder = (GameEvent) -> Unit
|
||||||
@@ -8,11 +8,9 @@ import eventDemo.libs.event.EventStream
|
|||||||
* A stream to publish and read the played card event.
|
* A stream to publish and read the played card event.
|
||||||
*/
|
*/
|
||||||
class GameEventStream(
|
class GameEventStream(
|
||||||
private val eventBus: GameEventBus,
|
|
||||||
private val eventStream: EventStream<GameEvent, GameId>,
|
private val eventStream: EventStream<GameEvent, GameId>,
|
||||||
) : EventStream<GameEvent, GameId> by eventStream {
|
) : EventStream<GameEvent, GameId> by eventStream {
|
||||||
override fun publish(event: GameEvent) {
|
override fun publish(event: GameEvent) {
|
||||||
eventStream.publish(event)
|
eventStream.publish(event)
|
||||||
eventBus.publish(event)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,14 +17,15 @@ data class GameStartedEvent(
|
|||||||
fun new(
|
fun new(
|
||||||
id: GameId,
|
id: GameId,
|
||||||
players: Set<Player>,
|
players: Set<Player>,
|
||||||
|
shuffleIsDisabled: Boolean = isDisabled,
|
||||||
): GameStartedEvent =
|
): GameStartedEvent =
|
||||||
GameStartedEvent(
|
GameStartedEvent(
|
||||||
gameId = id,
|
gameId = id,
|
||||||
firstPlayer = if (isDisabled) players.first() else players.random(),
|
firstPlayer = if (shuffleIsDisabled) players.first() else players.random(),
|
||||||
deck =
|
deck =
|
||||||
Deck
|
Deck
|
||||||
.newWithoutPlayers()
|
.newWithoutPlayers()
|
||||||
.let { if (isDisabled) it else it.shuffle() }
|
.let { if (shuffleIsDisabled) it else it.shuffle() }
|
||||||
.initHands(players)
|
.initHands(players)
|
||||||
.placeFirstCardOnDiscard(),
|
.placeFirstCardOnDiscard(),
|
||||||
)
|
)
|
||||||
|
|||||||
12
src/main/kotlin/eventDemo/app/event/event/PlayerWinEvent.kt
Normal file
12
src/main/kotlin/eventDemo/app/event/event/PlayerWinEvent.kt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package eventDemo.app.event.event
|
||||||
|
|
||||||
|
import eventDemo.app.entity.GameId
|
||||||
|
import eventDemo.app.entity.Player
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This [GameEvent] is sent when a player is ready.
|
||||||
|
*/
|
||||||
|
data class PlayerWinEvent(
|
||||||
|
override val gameId: GameId,
|
||||||
|
val player: Player,
|
||||||
|
) : GameEvent
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package eventDemo.app
|
package eventDemo.app.event.projection
|
||||||
|
|
||||||
import eventDemo.app.entity.Card
|
import eventDemo.app.entity.Card
|
||||||
import eventDemo.app.entity.Deck
|
import eventDemo.app.entity.Deck
|
||||||
@@ -17,6 +17,7 @@ data class GameState(
|
|||||||
val readyPlayers: Set<Player> = emptySet(),
|
val readyPlayers: Set<Player> = emptySet(),
|
||||||
val deck: Deck = Deck(players),
|
val deck: Deck = Deck(players),
|
||||||
val isStarted: Boolean = false,
|
val isStarted: Boolean = false,
|
||||||
|
val playerWins: Set<Player> = emptySet(),
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class LastCard(
|
data class LastCard(
|
||||||
@@ -92,6 +93,11 @@ data class GameState(
|
|||||||
?.filter { canBePlayThisCard(player, it) }
|
?.filter { canBePlayThisCard(player, it) }
|
||||||
?: emptyList()
|
?: emptyList()
|
||||||
|
|
||||||
|
fun playerHasNoCardLeft(): List<Player> =
|
||||||
|
deck.playerHasNoCardLeft().map { playerId ->
|
||||||
|
players.find { it.id == playerId } ?: error("inconsistency detected between players")
|
||||||
|
}
|
||||||
|
|
||||||
fun canBePlayThisCard(
|
fun canBePlayThisCard(
|
||||||
player: Player,
|
player: Player,
|
||||||
card: Card,
|
card: Card,
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package eventDemo.app.event
|
package eventDemo.app.event.projection
|
||||||
|
|
||||||
import eventDemo.app.GameState
|
|
||||||
import eventDemo.app.entity.Card
|
import eventDemo.app.entity.Card
|
||||||
import eventDemo.app.entity.GameId
|
import eventDemo.app.entity.GameId
|
||||||
|
import eventDemo.app.event.GameEventStream
|
||||||
import eventDemo.app.event.event.CardIsPlayedEvent
|
import eventDemo.app.event.event.CardIsPlayedEvent
|
||||||
import eventDemo.app.event.event.GameEvent
|
import eventDemo.app.event.event.GameEvent
|
||||||
import eventDemo.app.event.event.GameStartedEvent
|
import eventDemo.app.event.event.GameStartedEvent
|
||||||
@@ -10,19 +10,33 @@ import eventDemo.app.event.event.NewPlayerEvent
|
|||||||
import eventDemo.app.event.event.PlayerChoseColorEvent
|
import eventDemo.app.event.event.PlayerChoseColorEvent
|
||||||
import eventDemo.app.event.event.PlayerHavePassEvent
|
import eventDemo.app.event.event.PlayerHavePassEvent
|
||||||
import eventDemo.app.event.event.PlayerReadyEvent
|
import eventDemo.app.event.event.PlayerReadyEvent
|
||||||
|
import eventDemo.app.event.event.PlayerWinEvent
|
||||||
|
|
||||||
fun GameId.buildStateFromEventStream(eventStream: GameEventStream): GameState =
|
fun GameId.buildStateFromEventStream(eventStream: GameEventStream): GameState =
|
||||||
buildStateFromEvents(
|
buildStateFromEvents(
|
||||||
eventStream.readAll(this),
|
eventStream.readAll(this),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the state to the specific event
|
||||||
|
*/
|
||||||
fun GameEvent.buildStateFromEventStreamTo(eventStream: GameEventStream): GameState =
|
fun GameEvent.buildStateFromEventStreamTo(eventStream: GameEventStream): GameState =
|
||||||
gameId.buildStateFromEvents(
|
gameId.buildStateFromEvents(
|
||||||
eventStream.readAll(gameId).takeWhile { it != this } + this,
|
eventStream.readAll(gameId).takeWhile { it != this } + this,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun GameId.buildStateFromEvents(events: List<GameEvent>): GameState =
|
private fun GameId.buildStateFromEvents(events: List<GameEvent>): GameState =
|
||||||
events.fold(GameState(this)) { state: GameState, event: GameEvent ->
|
events.fold(GameState(this)) { state, event ->
|
||||||
|
state.apply(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<GameEvent>.buildStateFromEvents(): GameState =
|
||||||
|
fold(GameState(this.first().gameId)) { state, event ->
|
||||||
|
state.apply(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun GameState.apply(event: GameEvent): GameState =
|
||||||
|
let { state ->
|
||||||
when (event) {
|
when (event) {
|
||||||
is CardIsPlayedEvent -> {
|
is CardIsPlayedEvent -> {
|
||||||
val direction =
|
val direction =
|
||||||
@@ -83,5 +97,11 @@ private fun GameId.buildStateFromEvents(events: List<GameEvent>): GameState =
|
|||||||
isStarted = true,
|
isStarted = true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is PlayerWinEvent -> {
|
||||||
|
copy(
|
||||||
|
playerWins = playerWins + event.player,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package eventDemo.app.event.projection
|
||||||
|
|
||||||
|
import eventDemo.app.entity.GameId
|
||||||
|
import eventDemo.app.event.GameEventHandler
|
||||||
|
import eventDemo.app.event.GameEventStream
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
class GameStateRepository(
|
||||||
|
private val eventStream: GameEventStream,
|
||||||
|
eventHandler: GameEventHandler,
|
||||||
|
) {
|
||||||
|
private val projections: ConcurrentHashMap<GameId, GameState> = ConcurrentHashMap()
|
||||||
|
|
||||||
|
init {
|
||||||
|
eventHandler.registerProjectionBuilder { event ->
|
||||||
|
val projection = projections[event.gameId]
|
||||||
|
if (projection == null) {
|
||||||
|
event.gameId
|
||||||
|
.buildStateFromEventStream(eventStream)
|
||||||
|
.update()
|
||||||
|
} else {
|
||||||
|
projection
|
||||||
|
.apply(event)
|
||||||
|
.let { projections.put(it.gameId, it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get(gameId: GameId): GameState = gameId.buildStateFromEventStream(eventStream)
|
||||||
|
|
||||||
|
private fun GameState.update() {
|
||||||
|
projections[gameId] = this
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ package eventDemo.app.eventListener
|
|||||||
import eventDemo.app.entity.Player
|
import eventDemo.app.entity.Player
|
||||||
import eventDemo.app.event.GameEventBus
|
import eventDemo.app.event.GameEventBus
|
||||||
import eventDemo.app.event.GameEventStream
|
import eventDemo.app.event.GameEventStream
|
||||||
import eventDemo.app.event.buildStateFromEventStreamTo
|
|
||||||
import eventDemo.app.event.event.CardIsPlayedEvent
|
import eventDemo.app.event.event.CardIsPlayedEvent
|
||||||
import eventDemo.app.event.event.GameEvent
|
import eventDemo.app.event.event.GameEvent
|
||||||
import eventDemo.app.event.event.GameStartedEvent
|
import eventDemo.app.event.event.GameStartedEvent
|
||||||
@@ -11,11 +10,14 @@ import eventDemo.app.event.event.NewPlayerEvent
|
|||||||
import eventDemo.app.event.event.PlayerChoseColorEvent
|
import eventDemo.app.event.event.PlayerChoseColorEvent
|
||||||
import eventDemo.app.event.event.PlayerHavePassEvent
|
import eventDemo.app.event.event.PlayerHavePassEvent
|
||||||
import eventDemo.app.event.event.PlayerReadyEvent
|
import eventDemo.app.event.event.PlayerReadyEvent
|
||||||
|
import eventDemo.app.event.event.PlayerWinEvent
|
||||||
|
import eventDemo.app.event.projection.buildStateFromEventStreamTo
|
||||||
import eventDemo.app.notification.PlayerAsJoinTheGameNotification
|
import eventDemo.app.notification.PlayerAsJoinTheGameNotification
|
||||||
import eventDemo.app.notification.PlayerAsPlayACardNotification
|
import eventDemo.app.notification.PlayerAsPlayACardNotification
|
||||||
import eventDemo.app.notification.PlayerHavePassNotification
|
import eventDemo.app.notification.PlayerHavePassNotification
|
||||||
import eventDemo.app.notification.PlayerWasChoseTheCardColorNotification
|
import eventDemo.app.notification.PlayerWasChoseTheCardColorNotification
|
||||||
import eventDemo.app.notification.PlayerWasReadyNotification
|
import eventDemo.app.notification.PlayerWasReadyNotification
|
||||||
|
import eventDemo.app.notification.PlayerWinNotification
|
||||||
import eventDemo.app.notification.TheGameWasStartedNotification
|
import eventDemo.app.notification.TheGameWasStartedNotification
|
||||||
import eventDemo.app.notification.WelcomeToTheGameNotification
|
import eventDemo.app.notification.WelcomeToTheGameNotification
|
||||||
import eventDemo.app.notification.YourNewCardNotification
|
import eventDemo.app.notification.YourNewCardNotification
|
||||||
@@ -102,7 +104,14 @@ class GameEventPlayerNotificationListener(
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is PlayerWinEvent -> {
|
||||||
|
PlayerWinNotification(
|
||||||
|
player = event.player,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (notification == null) {
|
if (notification == null) {
|
||||||
logger.atInfo {
|
logger.atInfo {
|
||||||
message = "Notification Ignore: $event"
|
message = "Notification Ignore: $event"
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
package eventDemo.app.eventListener
|
package eventDemo.app.eventListener
|
||||||
|
|
||||||
import eventDemo.app.event.GameEventBus
|
import eventDemo.app.event.GameEventBus
|
||||||
|
import eventDemo.app.event.GameEventHandler
|
||||||
import eventDemo.app.event.GameEventStream
|
import eventDemo.app.event.GameEventStream
|
||||||
import eventDemo.app.event.buildStateFromEventStreamTo
|
|
||||||
import eventDemo.app.event.event.GameEvent
|
import eventDemo.app.event.event.GameEvent
|
||||||
import eventDemo.app.event.event.GameStartedEvent
|
import eventDemo.app.event.event.GameStartedEvent
|
||||||
|
import eventDemo.app.event.event.PlayerWinEvent
|
||||||
|
import eventDemo.app.event.projection.GameState
|
||||||
|
import eventDemo.app.event.projection.buildStateFromEventStreamTo
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
|
||||||
class GameEventReactionListener(
|
class GameEventReactionListener(
|
||||||
private val eventBus: GameEventBus,
|
private val eventBus: GameEventBus,
|
||||||
|
private val eventHandler: GameEventHandler,
|
||||||
private val eventStream: GameEventStream,
|
private val eventStream: GameEventStream,
|
||||||
private val priority: Int = DEFAULT_PRIORITY,
|
private val priority: Int = DEFAULT_PRIORITY,
|
||||||
) {
|
) {
|
||||||
@@ -21,6 +25,15 @@ class GameEventReactionListener(
|
|||||||
fun init() {
|
fun init() {
|
||||||
eventBus.subscribe(priority) { event: GameEvent ->
|
eventBus.subscribe(priority) { event: GameEvent ->
|
||||||
val state = event.buildStateFromEventStreamTo(eventStream)
|
val state = event.buildStateFromEventStreamTo(eventStream)
|
||||||
|
sendStartGameEvent(state, event)
|
||||||
|
sendWinnerEvent(state, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendStartGameEvent(
|
||||||
|
state: GameState,
|
||||||
|
event: GameEvent,
|
||||||
|
) {
|
||||||
if (state.isReady && !state.isStarted) {
|
if (state.isReady && !state.isStarted) {
|
||||||
val reactionEvent =
|
val reactionEvent =
|
||||||
GameStartedEvent.new(
|
GameStartedEvent.new(
|
||||||
@@ -35,8 +48,30 @@ class GameEventReactionListener(
|
|||||||
"reactionEvent" to reactionEvent,
|
"reactionEvent" to reactionEvent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
eventHandler.handle(reactionEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendWinnerEvent(
|
||||||
|
state: GameState,
|
||||||
|
event: GameEvent,
|
||||||
|
) {
|
||||||
|
val winner = state.playerHasNoCardLeft().firstOrNull()
|
||||||
|
if (winner != null) {
|
||||||
|
val reactionEvent =
|
||||||
|
PlayerWinEvent(
|
||||||
|
state.gameId,
|
||||||
|
winner,
|
||||||
|
)
|
||||||
|
logger.atInfo {
|
||||||
|
message = "Event Send on reaction of: $event"
|
||||||
|
payload =
|
||||||
|
mapOf(
|
||||||
|
"event" to event,
|
||||||
|
"reactionEvent" to reactionEvent,
|
||||||
|
)
|
||||||
|
}
|
||||||
eventStream.publish(reactionEvent)
|
eventStream.publish(reactionEvent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package eventDemo.app.notification
|
||||||
|
|
||||||
|
import eventDemo.app.entity.Player
|
||||||
|
import eventDemo.shared.UUIDSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PlayerWinNotification(
|
||||||
|
@Serializable(with = UUIDSerializer::class)
|
||||||
|
override val id: UUID = UUID.randomUUID(),
|
||||||
|
val player: Player,
|
||||||
|
) : Notification
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
package eventDemo.app.query
|
package eventDemo.app.query
|
||||||
|
|
||||||
import eventDemo.app.entity.GameId
|
import eventDemo.app.entity.GameId
|
||||||
import eventDemo.app.event.GameEventStream
|
import eventDemo.app.event.projection.GameStateRepository
|
||||||
import eventDemo.app.event.buildStateFromEventStream
|
|
||||||
import eventDemo.app.event.event.CardIsPlayedEvent
|
|
||||||
import eventDemo.libs.event.readLastOf
|
|
||||||
import eventDemo.shared.GameIdSerializer
|
import eventDemo.shared.GameIdSerializer
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.resources.Resource
|
import io.ktor.resources.Resource
|
||||||
@@ -37,19 +34,21 @@ class Game(
|
|||||||
/**
|
/**
|
||||||
* API routes to read the game state.
|
* API routes to read the game state.
|
||||||
*/
|
*/
|
||||||
fun Route.readTheGameState(eventStream: GameEventStream) {
|
fun Route.readTheGameState(gameStateRepository: GameStateRepository) {
|
||||||
authenticate {
|
authenticate {
|
||||||
// Read the last played card on the game.
|
// Read the last played card on the game.
|
||||||
get<Game.Card> { body ->
|
get<Game.Card> { body ->
|
||||||
eventStream
|
gameStateRepository
|
||||||
.readLastOf<CardIsPlayedEvent, _, _>(body.game.id)
|
.get(body.game.id)
|
||||||
?.let { call.respond(it.card) }
|
.lastCard
|
||||||
|
?.card
|
||||||
|
?.let { call.respond(it) }
|
||||||
?: call.response.status(HttpStatusCode.BadRequest)
|
?: call.response.status(HttpStatusCode.BadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the last played card on the game.
|
// Read the last played card on the game.
|
||||||
get<Game.State> { body ->
|
get<Game.State> { body ->
|
||||||
val state = body.game.id.buildStateFromEventStream(eventStream)
|
val state = gameStateRepository.get(body.game.id)
|
||||||
call.respond(state)
|
call.respond(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package eventDemo.configuration
|
|||||||
|
|
||||||
import eventDemo.app.command.GameCommandHandler
|
import eventDemo.app.command.GameCommandHandler
|
||||||
import eventDemo.app.event.GameEventBus
|
import eventDemo.app.event.GameEventBus
|
||||||
|
import eventDemo.app.event.GameEventHandler
|
||||||
import eventDemo.app.event.GameEventStream
|
import eventDemo.app.event.GameEventStream
|
||||||
|
import eventDemo.app.event.projection.GameStateRepository
|
||||||
import eventDemo.app.eventListener.GameEventPlayerNotificationListener
|
import eventDemo.app.eventListener.GameEventPlayerNotificationListener
|
||||||
import eventDemo.libs.event.EventBusInMemory
|
import eventDemo.libs.event.EventBusInMemory
|
||||||
import eventDemo.libs.event.EventStreamInMemory
|
import eventDemo.libs.event.EventStreamInMemory
|
||||||
@@ -26,10 +28,16 @@ val appKoinModule =
|
|||||||
GameEventBus(EventBusInMemory())
|
GameEventBus(EventBusInMemory())
|
||||||
}
|
}
|
||||||
single {
|
single {
|
||||||
GameEventStream(get(), EventStreamInMemory())
|
GameEventStream(EventStreamInMemory())
|
||||||
}
|
}
|
||||||
single {
|
single {
|
||||||
GameCommandHandler(get())
|
GameStateRepository(get(), get())
|
||||||
|
}
|
||||||
|
single {
|
||||||
|
GameEventHandler(get(), get())
|
||||||
|
}
|
||||||
|
single {
|
||||||
|
GameCommandHandler(get(), get())
|
||||||
}
|
}
|
||||||
|
|
||||||
singleOf(::GameEventPlayerNotificationListener)
|
singleOf(::GameEventPlayerNotificationListener)
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ import io.ktor.server.application.Application
|
|||||||
import org.koin.ktor.ext.get
|
import org.koin.ktor.ext.get
|
||||||
|
|
||||||
fun Application.configureGameListener() {
|
fun Application.configureGameListener() {
|
||||||
GameEventReactionListener(get(), get())
|
GameEventReactionListener(get(), get(), get())
|
||||||
.init()
|
.init()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package eventDemo.app.event.projection
|
||||||
|
|
||||||
|
import eventDemo.app.entity.Card
|
||||||
|
import eventDemo.app.entity.GameId
|
||||||
|
import eventDemo.app.entity.Player
|
||||||
|
import eventDemo.app.event.event.CardIsPlayedEvent
|
||||||
|
import eventDemo.app.event.event.GameStartedEvent
|
||||||
|
import eventDemo.app.event.event.NewPlayerEvent
|
||||||
|
import eventDemo.app.event.event.PlayerReadyEvent
|
||||||
|
import eventDemo.app.event.event.disableShuffleDeck
|
||||||
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.matchers.equals.shouldBeEqual
|
||||||
|
import kotlin.test.assertIs
|
||||||
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
|
class GameStateBuilderTest :
|
||||||
|
FunSpec({
|
||||||
|
test("apply") {
|
||||||
|
disableShuffleDeck()
|
||||||
|
val gameId = GameId()
|
||||||
|
val player1 = Player(name = "Nikola")
|
||||||
|
val player2 = Player(name = "Einstein")
|
||||||
|
|
||||||
|
GameState(gameId)
|
||||||
|
.run {
|
||||||
|
val event = NewPlayerEvent(gameId, player1)
|
||||||
|
apply(event).also { state ->
|
||||||
|
state.gameId shouldBeEqual gameId
|
||||||
|
state.isReady shouldBeEqual false
|
||||||
|
state.isStarted shouldBeEqual false
|
||||||
|
}
|
||||||
|
}.run {
|
||||||
|
val event = NewPlayerEvent(gameId, player2)
|
||||||
|
apply(event).also { state ->
|
||||||
|
state.gameId shouldBeEqual gameId
|
||||||
|
state.players shouldBeEqual setOf(player1, player2)
|
||||||
|
}
|
||||||
|
}.run {
|
||||||
|
val event = PlayerReadyEvent(gameId, player1)
|
||||||
|
apply(event).also { state ->
|
||||||
|
state.gameId shouldBeEqual gameId
|
||||||
|
state.readyPlayers shouldBeEqual setOf(player1)
|
||||||
|
}
|
||||||
|
}.run {
|
||||||
|
val event = PlayerReadyEvent(gameId, player2)
|
||||||
|
apply(event).also { state ->
|
||||||
|
state.gameId shouldBeEqual gameId
|
||||||
|
state.readyPlayers shouldBeEqual setOf(player1, player2)
|
||||||
|
state.isReady shouldBeEqual true
|
||||||
|
state.isStarted shouldBeEqual false
|
||||||
|
}
|
||||||
|
}.run {
|
||||||
|
val event =
|
||||||
|
GameStartedEvent.new(
|
||||||
|
gameId,
|
||||||
|
setOf(player1, player2),
|
||||||
|
shuffleIsDisabled = true,
|
||||||
|
)
|
||||||
|
apply(event).also { state ->
|
||||||
|
state.gameId shouldBeEqual gameId
|
||||||
|
state.isStarted shouldBeEqual true
|
||||||
|
assertIs<Card.NumericCard>(state.deck.stack.first()).let {
|
||||||
|
it.number shouldBeEqual 6
|
||||||
|
it.color shouldBeEqual Card.Color.Red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.run {
|
||||||
|
val playedCard = playableCards(player1)[0]
|
||||||
|
val event = CardIsPlayedEvent(gameId, playedCard, player1)
|
||||||
|
apply(event).also { state ->
|
||||||
|
state.gameId shouldBeEqual gameId
|
||||||
|
assertNotNull(state.lastCard).card shouldBeEqual playedCard
|
||||||
|
assertIs<Card.NumericCard>(playedCard).let {
|
||||||
|
it.number shouldBeEqual 0
|
||||||
|
it.color shouldBeEqual Card.Color.Red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.run {
|
||||||
|
val playedCard = playableCards(player2)[0]
|
||||||
|
val event = CardIsPlayedEvent(gameId, playedCard, player2)
|
||||||
|
apply(event).also { state ->
|
||||||
|
state.gameId shouldBeEqual gameId
|
||||||
|
assertNotNull(state.lastCard).card shouldBeEqual playedCard
|
||||||
|
assertIs<Card.NumericCard>(playedCard).let {
|
||||||
|
it.number shouldBeEqual 7
|
||||||
|
it.color shouldBeEqual Card.Color.Red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
package eventDemo.app.query
|
package eventDemo.app.query
|
||||||
|
|
||||||
import eventDemo.app.GameState
|
|
||||||
import eventDemo.app.entity.Card
|
import eventDemo.app.entity.Card
|
||||||
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.GameEventHandler
|
||||||
import eventDemo.app.event.event.CardIsPlayedEvent
|
import eventDemo.app.event.event.CardIsPlayedEvent
|
||||||
|
import eventDemo.app.event.event.GameStartedEvent
|
||||||
|
import eventDemo.app.event.event.NewPlayerEvent
|
||||||
|
import eventDemo.app.event.event.PlayerReadyEvent
|
||||||
|
import eventDemo.app.event.projection.GameState
|
||||||
|
import eventDemo.app.event.projection.GameStateRepository
|
||||||
import eventDemo.configuration.configure
|
import eventDemo.configuration.configure
|
||||||
import eventDemo.configuration.makeJwt
|
import eventDemo.configuration.makeJwt
|
||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
@@ -20,9 +24,13 @@ import io.ktor.client.statement.bodyAsText
|
|||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.server.testing.testApplication
|
import io.ktor.server.testing.testApplication
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.koin.core.context.stopKoin
|
import org.koin.core.context.stopKoin
|
||||||
import org.koin.ktor.ext.inject
|
import org.koin.ktor.ext.inject
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertIs
|
||||||
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
class GameStateRouteTest :
|
class GameStateRouteTest :
|
||||||
FunSpec({
|
FunSpec({
|
||||||
@@ -51,30 +59,56 @@ class GameStateRouteTest :
|
|||||||
|
|
||||||
test("/game/{id}/card/last") {
|
test("/game/{id}/card/last") {
|
||||||
testApplication {
|
testApplication {
|
||||||
val id = GameId()
|
val gameId = GameId()
|
||||||
val card: Card = Card.NumericCard(1, Card.Color.Blue)
|
val player1 = Player(name = "Nikola")
|
||||||
val player = Player(name = "Nikola")
|
val player2 = Player(name = "Einstein")
|
||||||
|
var lastPlayedCard: Card? = null
|
||||||
|
|
||||||
application {
|
application {
|
||||||
stopKoin()
|
stopKoin()
|
||||||
configure()
|
configure()
|
||||||
|
|
||||||
val eventStream by inject<GameEventStream>()
|
val eventHandler by inject<GameEventHandler>()
|
||||||
eventStream.publish(
|
val stateRepo by inject<GameStateRepository>()
|
||||||
CardIsPlayedEvent(id, Card.NumericCard(2, Card.Color.Yellow), player),
|
runBlocking {
|
||||||
CardIsPlayedEvent(id, card, player),
|
eventHandler.handle(
|
||||||
// Other game
|
NewPlayerEvent(gameId, player1),
|
||||||
CardIsPlayedEvent(GameId(), Card.NumericCard(2, Card.Color.Yellow), player),
|
NewPlayerEvent(gameId, player2),
|
||||||
|
PlayerReadyEvent(gameId, player1),
|
||||||
|
PlayerReadyEvent(gameId, player2),
|
||||||
|
GameStartedEvent.new(
|
||||||
|
gameId,
|
||||||
|
setOf(player1, player2),
|
||||||
|
shuffleIsDisabled = true,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
delay(100)
|
||||||
|
lastPlayedCard = stateRepo.get(gameId).playableCards(player1).first()
|
||||||
|
assertNotNull(lastPlayedCard)
|
||||||
|
.let { assertIs<Card.NumericCard>(lastPlayedCard) }
|
||||||
|
.let {
|
||||||
|
it.number shouldBeEqual 0
|
||||||
|
it.color shouldBeEqual Card.Color.Red
|
||||||
|
}
|
||||||
|
delay(100)
|
||||||
|
eventHandler.handle(
|
||||||
|
CardIsPlayedEvent(
|
||||||
|
gameId,
|
||||||
|
assertNotNull(lastPlayedCard),
|
||||||
|
player1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
delay(100)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
httpClient()
|
httpClient()
|
||||||
.get("/game/$id/card/last") {
|
.get("/game/$gameId/card/last") {
|
||||||
withAuth(player)
|
withAuth(player1)
|
||||||
accept(ContentType.Application.Json)
|
accept(ContentType.Application.Json)
|
||||||
}.apply {
|
}.apply {
|
||||||
assertEquals(HttpStatusCode.OK, status, message = bodyAsText())
|
assertEquals(HttpStatusCode.OK, status, message = bodyAsText())
|
||||||
assertEquals(card, call.body<Card>())
|
assertEquals(assertNotNull(lastPlayedCard), call.body<Card>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package eventDemo.app.query
|
package eventDemo.app.query
|
||||||
|
|
||||||
import eventDemo.app.GameState
|
|
||||||
import eventDemo.app.command.GameCommandHandler
|
import eventDemo.app.command.GameCommandHandler
|
||||||
import eventDemo.app.command.command.IWantToJoinTheGameCommand
|
import eventDemo.app.command.command.IWantToJoinTheGameCommand
|
||||||
import eventDemo.app.command.command.IWantToPlayCardCommand
|
import eventDemo.app.command.command.IWantToPlayCardCommand
|
||||||
@@ -8,8 +7,9 @@ import eventDemo.app.command.command.IamReadyToPlayCommand
|
|||||||
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.GameEventStream
|
||||||
import eventDemo.app.event.buildStateFromEventStream
|
|
||||||
import eventDemo.app.event.event.disableShuffleDeck
|
import eventDemo.app.event.event.disableShuffleDeck
|
||||||
|
import eventDemo.app.event.projection.GameState
|
||||||
|
import eventDemo.app.event.projection.buildStateFromEventStream
|
||||||
import eventDemo.app.eventListener.GameEventPlayerNotificationListener
|
import eventDemo.app.eventListener.GameEventPlayerNotificationListener
|
||||||
import eventDemo.app.eventListener.GameEventReactionListener
|
import eventDemo.app.eventListener.GameEventReactionListener
|
||||||
import eventDemo.app.notification.PlayerAsJoinTheGameNotification
|
import eventDemo.app.notification.PlayerAsJoinTheGameNotification
|
||||||
@@ -52,7 +52,7 @@ class GameStateTest :
|
|||||||
val commandHandler by inject<GameCommandHandler>()
|
val commandHandler by inject<GameCommandHandler>()
|
||||||
val playerNotificationListener by inject<GameEventPlayerNotificationListener>()
|
val playerNotificationListener by inject<GameEventPlayerNotificationListener>()
|
||||||
val eventStream by inject<GameEventStream>()
|
val eventStream by inject<GameEventStream>()
|
||||||
GameEventReactionListener(get(), get()).init()
|
GameEventReactionListener(get(), get(), get()).init()
|
||||||
playerNotificationListener.startListening(channelOut1, player1)
|
playerNotificationListener.startListening(channelOut1, player1)
|
||||||
playerNotificationListener.startListening(channelOut2, player2)
|
playerNotificationListener.startListening(channelOut2, player2)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user