summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoffrey Bion <joffrey.bion@booking.com>2020-03-26 20:50:25 +0100
committerJoffrey Bion <joffrey.bion@booking.com>2020-03-27 10:59:39 +0100
commite600544531764f67a5917483526d8d1941454640 (patch)
tree186f23e11b4300cdc53393ff7891592280bbc961
parentUse plain redux compose when devtools not present (diff)
downloadseven-wonders-e600544531764f67a5917483526d8d1941454640.tar.gz
seven-wonders-e600544531764f67a5917483526d8d1941454640.tar.bz2
seven-wonders-e600544531764f67a5917483526d8d1941454640.zip
Rework sagas and router to sub/unsubscribe properly
-rw-r--r--sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt7
-rw-r--r--sw-server/src/test/kotlin/org/luxons/sevenwonders/server/SevenWondersTest.kt17
-rw-r--r--sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/Application.kt19
-rw-r--r--sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameBrowserSagas.kt3
-rw-r--r--sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/LobbySagas.kt40
-rw-r--r--sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt26
-rw-r--r--sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/router/Router.kt38
7 files changed, 89 insertions, 61 deletions
diff --git a/sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt b/sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt
index 4acbccd9..c4293c2a 100644
--- a/sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt
+++ b/sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt
@@ -88,8 +88,11 @@ class SevenWondersSession(private val stompSession: StompSessionWithKxSerializat
suspend fun watchLobbyUpdates(gameId: Long): StompSubscription<LobbyDTO> =
stompSession.subscribe("/topic/lobby/$gameId/updated", LobbyDTO.serializer())
- suspend fun watchGameStart(gameId: Long): StompSubscription<Unit> =
- stompSession.subscribeEmptyMsg("/topic/lobby/$gameId/started")
+ suspend fun awaitGameStart(gameId: Long) {
+ val gameStartSubscription = stompSession.subscribeEmptyMsg("/topic/lobby/$gameId/started")
+ gameStartSubscription.messages.receive()
+ gameStartSubscription.unsubscribe()
+ }
suspend fun startGame() {
stompSession.sendEmptyMsg("/app/lobby/startGame")
diff --git a/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/SevenWondersTest.kt b/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/SevenWondersTest.kt
index ab947609..e436d054 100644
--- a/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/SevenWondersTest.kt
+++ b/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/SevenWondersTest.kt
@@ -1,5 +1,6 @@
package org.luxons.sevenwonders.server
+import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import kotlinx.coroutines.withTimeoutOrNull
@@ -67,10 +68,10 @@ class SevenWondersTest {
session2.joinGame(lobby.id)
val outsiderSession = newPlayer("Outsider")
- val started = outsiderSession.watchGameStart(lobby.id).messages
+ val started = launch { outsiderSession.awaitGameStart(lobby.id) }
ownerSession.startGame()
- val nothing = withTimeoutOrNull(30) { started.receive() }
+ val nothing = withTimeoutOrNull(30) { started.join() }
assertNull(nothing)
disconnect(ownerSession, session1, session2, outsiderSession)
}
@@ -126,14 +127,14 @@ class SevenWondersTest {
val session3 = newPlayer("Player3")
session3.joinGame(lobby.id)
- val gameStart1 = session1.watchGameStart(lobby.id).messages
- val gameStart2 = session2.watchGameStart(lobby.id).messages
- val gameStart3 = session3.watchGameStart(lobby.id).messages
+ val gameStart1 = launch { session1.awaitGameStart(lobby.id) }
+ val gameStart2 = launch { session2.awaitGameStart(lobby.id) }
+ val gameStart3 = launch { session3.awaitGameStart(lobby.id) }
session1.startGame()
- withTimeout(500) { gameStart1.receive() }
- withTimeout(500) { gameStart2.receive() }
- withTimeout(500) { gameStart3.receive() }
+ withTimeout(500) { gameStart1.join() }
+ withTimeout(500) { gameStart2.join() }
+ withTimeout(500) { gameStart3.join() }
val turns1 = session1.watchTurns().messages
val turns2 = session2.watchTurns().messages
diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/Application.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/Application.kt
index d692c256..b1244b5c 100644
--- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/Application.kt
+++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/Application.kt
@@ -2,26 +2,21 @@ package org.luxons.sevenwonders.ui.components
import org.luxons.sevenwonders.ui.components.game.gameScene
import org.luxons.sevenwonders.ui.components.gameBrowser.gameBrowser
+import org.luxons.sevenwonders.ui.components.home.home
+import org.luxons.sevenwonders.ui.components.lobby.lobby
+import org.luxons.sevenwonders.ui.router.Route
import react.RBuilder
import react.router.dom.hashRouter
import react.router.dom.redirect
import react.router.dom.route
import react.router.dom.switch
-import org.luxons.sevenwonders.ui.components.home.home
-import org.luxons.sevenwonders.ui.components.lobby.lobby
-import react.RProps
-
-interface IdProps : RProps {
- val id: Long
-}
-
fun RBuilder.application() = hashRouter {
switch {
- route("/games") { gameBrowser() }
- route("/game") { gameScene() }
- route("/lobby") { lobby() }
- route("/", exact = true) { home() }
+ route(Route.GAME_BROWSER.path) { gameBrowser() }
+ route(Route.GAME.path) { gameScene() }
+ route(Route.LOBBY.path) { lobby() }
+ route(Route.HOME.path, exact = true) { home() }
redirect(from = "*", to = "/")
}
}
diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameBrowserSagas.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameBrowserSagas.kt
index c5e3bdb8..38c3e60a 100644
--- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameBrowserSagas.kt
+++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameBrowserSagas.kt
@@ -10,6 +10,8 @@ import org.luxons.sevenwonders.ui.redux.EnterLobbyAction
import org.luxons.sevenwonders.ui.redux.RequestCreateGameAction
import org.luxons.sevenwonders.ui.redux.RequestJoinGameAction
import org.luxons.sevenwonders.ui.redux.UpdateGameListAction
+import org.luxons.sevenwonders.ui.router.Navigate
+import org.luxons.sevenwonders.ui.router.Route
suspend fun SwSagaContext.gameBrowserSaga(session: SevenWondersSession) {
GameBrowserSaga(session, this).run()
@@ -26,6 +28,7 @@ private class GameBrowserSaga(
val lobby = awaitCreateOrJoinGame()
gamesSubscription.unsubscribe()
sagaContext.dispatch(EnterLobbyAction(lobby))
+ sagaContext.dispatch(Navigate(Route.LOBBY))
}
}
diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/LobbySagas.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/LobbySagas.kt
index b2d83416..143fecd8 100644
--- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/LobbySagas.kt
+++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/LobbySagas.kt
@@ -2,38 +2,40 @@ package org.luxons.sevenwonders.ui.redux.sagas
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
+import org.hildan.krossbow.stomp.StompSubscription
import org.luxons.sevenwonders.client.SevenWondersSession
+import org.luxons.sevenwonders.model.api.LobbyDTO
import org.luxons.sevenwonders.ui.redux.EnterGameAction
import org.luxons.sevenwonders.ui.redux.RequestStartGameAction
import org.luxons.sevenwonders.ui.redux.UpdateLobbyAction
-import org.luxons.sevenwonders.ui.router.Router
+import org.luxons.sevenwonders.ui.router.Navigate
+import org.luxons.sevenwonders.ui.router.Route
-suspend fun SwSagaContext.lobbySaga(session: SevenWondersSession, lobbyId: Long) {
+suspend fun SwSagaContext.lobbySaga(session: SevenWondersSession) {
+ val lobbyId = getState().currentLobbyId ?: error("Lobby saga run without a current lobby")
coroutineScope {
- launch { watchLobbyUpdates(session, lobbyId) }
- launch { handleGameStart(session, lobbyId) }
- launch { watchStartGame(session) }
+ val lobbyUpdatesSubscription = session.watchLobbyUpdates(lobbyId)
+ launch { watchLobbyUpdates(lobbyUpdatesSubscription) }
+ val startGameJob = launch { awaitStartGame(session) }
+
+ awaitGameStart(session, lobbyId)
+
+ lobbyUpdatesSubscription.unsubscribe()
+ startGameJob.cancel()
+ dispatch(Navigate(Route.GAME))
}
}
-private suspend fun SwSagaContext.watchLobbyUpdates(session: SevenWondersSession, lobbyId: Long) {
- val lobbyUpdates = session.watchLobbyUpdates(lobbyId)
- for (lobby in lobbyUpdates.messages) {
- dispatch(UpdateLobbyAction(lobby))
- }
+private suspend fun SwSagaContext.watchLobbyUpdates(lobbyUpdatesSubscription: StompSubscription<LobbyDTO>) {
+ dispatchAll(lobbyUpdatesSubscription.messages) { UpdateLobbyAction(it) }
}
-private suspend fun SwSagaContext.handleGameStart(session: SevenWondersSession, lobbyId: Long) {
- val gameStartSubscription = session.watchGameStart(lobbyId)
- gameStartSubscription.messages.receive()
+private suspend fun SwSagaContext.awaitGameStart(session: SevenWondersSession, lobbyId: Long) {
+ session.awaitGameStart(lobbyId)
dispatch(EnterGameAction(lobbyId))
-
- coroutineScope {
- launch { gameSaga(session) }
- Router.game()
- }
}
-private suspend fun SwSagaContext.watchStartGame(session: SevenWondersSession) = onEach<RequestStartGameAction> {
+private suspend fun SwSagaContext.awaitStartGame(session: SevenWondersSession) {
+ next<RequestStartGameAction>()
session.startGame()
}
diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt
index ded0bc89..a0cd6f3c 100644
--- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt
+++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt
@@ -1,16 +1,19 @@
package org.luxons.sevenwonders.ui.redux.sagas
+import kotlinx.coroutines.coroutineScope
import org.luxons.sevenwonders.client.SevenWondersClient
+import org.luxons.sevenwonders.client.SevenWondersSession
import org.luxons.sevenwonders.ui.redux.RequestChooseName
import org.luxons.sevenwonders.ui.redux.SetCurrentPlayerAction
import org.luxons.sevenwonders.ui.redux.SwState
-import org.luxons.sevenwonders.ui.router.Router
+import org.luxons.sevenwonders.ui.router.Route
+import org.luxons.sevenwonders.ui.router.routerSaga
import redux.RAction
import redux.WrapperAction
typealias SwSagaContext = SagaContext<SwState, RAction, WrapperAction>
-suspend fun SwSagaContext.rootSaga() {
+suspend fun SwSagaContext.rootSaga() = coroutineScope {
val action = next<RequestChooseName>()
val session = SevenWondersClient().connect("localhost:8000")
console.info("Connected to Seven Wonders web socket API")
@@ -18,8 +21,19 @@ suspend fun SwSagaContext.rootSaga() {
val player = session.chooseName(action.playerName)
dispatch(SetCurrentPlayerAction(player))
- Router.games()
- gameBrowserSaga(session)
- Router.lobby()
- lobbySaga(session)
+ routerSaga(Route.GAME_BROWSER) {
+ when (it) {
+ Route.HOME -> homeSaga(session)
+ Route.LOBBY -> lobbySaga(session)
+ Route.GAME_BROWSER -> gameBrowserSaga(session)
+ Route.GAME -> gameSaga(session)
+ }
+ }
+}
+
+private suspend fun SwSagaContext.homeSaga(session: SevenWondersSession): SevenWondersSession {
+ val action = next<RequestChooseName>()
+ val player = session.chooseName(action.playerName)
+ dispatch(SetCurrentPlayerAction(player))
+ return session
}
diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/router/Router.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/router/Router.kt
index 09789ee1..19e8bd94 100644
--- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/router/Router.kt
+++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/router/Router.kt
@@ -1,22 +1,32 @@
package org.luxons.sevenwonders.ui.router
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+import org.luxons.sevenwonders.ui.redux.sagas.SwSagaContext
+import redux.RAction
import kotlin.browser.window
-object Router {
-
- fun games() {
- push("/games")
- }
-
- fun game() {
- push("/game")
- }
+enum class Route(val path: String) {
+ HOME("/"),
+ GAME_BROWSER("/games"),
+ LOBBY("/lobby"),
+ GAME("/game"),
+}
- fun lobby() {
- push("/lobby")
- }
+data class Navigate(val route: Route): RAction
- private fun push(path: String) {
- window.location.hash = path
+suspend fun SwSagaContext.routerSaga(
+ startRoute: Route,
+ runRouteSaga: suspend SwSagaContext.(Route) -> Unit
+) {
+ coroutineScope {
+ window.location.hash = startRoute.path
+ var currentSaga: Job = launch { runRouteSaga(startRoute) }
+ onEach<Navigate> {
+ currentSaga.cancel()
+ window.location.hash = it.route.path
+ currentSaga = launch { runRouteSaga(it.route) }
+ }
}
}
bgstack15