diff options
5 files changed, 55 insertions, 58 deletions
diff --git a/sw-client/build.gradle.kts b/sw-client/build.gradle.kts index 1f4996bc..0c21cd36 100644 --- a/sw-client/build.gradle.kts +++ b/sw-client/build.gradle.kts @@ -3,7 +3,7 @@ plugins { id("org.jlleitschuh.gradle.ktlint") } -val krossbowVersion = "0.4.1" +val krossbowVersion = "0.10.2" kotlin { jvm() @@ -15,7 +15,7 @@ kotlin { dependencies { api(project(":sw-common-model")) implementation(kotlin("stdlib-common")) - implementation("org.hildan.krossbow:krossbow-client-metadata:$krossbowVersion") + api("org.hildan.krossbow:krossbow-stomp-kxserialization:$krossbowVersion") } } val commonTest by getting { @@ -27,7 +27,7 @@ kotlin { val jvmMain by getting { dependencies { implementation(kotlin("stdlib-jdk8")) - implementation("org.hildan.krossbow:krossbow-client-jvm:$krossbowVersion") + api("org.hildan.krossbow:krossbow-stomp-kxserialization-jvm:$krossbowVersion") } } val jvmTest by getting { @@ -39,8 +39,8 @@ kotlin { val jsMain by getting { dependencies { implementation(kotlin("stdlib-js")) - implementation("org.hildan.krossbow:krossbow-client-js:$krossbowVersion") - implementation(npm("webstomp-client")) // required by krossbow + api("org.hildan.krossbow:krossbow-stomp-kxserialization-js:$krossbowVersion") + implementation(npm("text-encoding", "0.7.0")) // required by krossbow, because required by kotlinx-io } } val jsTest by getting { 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 691a3f0a..4acbccd9 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 @@ -1,10 +1,16 @@ package org.luxons.sevenwonders.client -import kotlinx.coroutines.channels.ReceiveChannel -import kotlinx.coroutines.channels.map -import org.hildan.krossbow.client.KrossbowClient -import org.hildan.krossbow.client.KrossbowSession -import org.hildan.krossbow.client.KrossbowSubscription +import kotlinx.serialization.ImplicitReflectionSerializer +import kotlinx.serialization.list +import kotlinx.serialization.serializer +import org.hildan.krossbow.stomp.StompClient +import org.hildan.krossbow.stomp.StompSubscription +import org.hildan.krossbow.stomp.conversions.kxserialization.StompSessionWithKxSerialization +import org.hildan.krossbow.stomp.conversions.kxserialization.convertAndSend +import org.hildan.krossbow.stomp.conversions.kxserialization.subscribe +import org.hildan.krossbow.stomp.conversions.kxserialization.withJsonConversions +import org.hildan.krossbow.stomp.sendEmptyMsg +import org.hildan.krossbow.stomp.subscribeEmptyMsg import org.luxons.sevenwonders.model.CustomizableSettings import org.luxons.sevenwonders.model.GameState import org.luxons.sevenwonders.model.PlayerTurnInfo @@ -21,50 +27,41 @@ import org.luxons.sevenwonders.model.cards.PreparedCard class SevenWondersClient { - private val stompClient = KrossbowClient() + private val stompClient = StompClient() - suspend fun connect(serverUrl: String): SevenWondersSession { - val session = stompClient.connect("$serverUrl$SEVEN_WONDERS_WS_ENDPOINT") + suspend fun connect(serverHost: String): SevenWondersSession { + val session = stompClient.connect("ws://$serverHost$SEVEN_WONDERS_WS_ENDPOINT").withJsonConversions() return SevenWondersSession(session) } } -suspend inline fun <reified T : Any, reified U : Any> KrossbowSession.request( +@OptIn(ImplicitReflectionSerializer::class) +suspend inline fun <reified T : Any, reified U : Any> StompSessionWithKxSerialization.request( sendDestination: String, receiveDestination: String, payload: T? = null ): U { val sub = subscribe<U>(receiveDestination) - send(sendDestination, payload) + convertAndSend(sendDestination, payload) val msg = sub.messages.receive() sub.unsubscribe() - return msg.payload + return msg } -data class SevenWondersSubscription<T : Any>( - val messages: ReceiveChannel<T>, - val unsubscribe: suspend () -> Unit -) - -private fun <T : Any> KrossbowSubscription<T>.ignoreHeaders() = SevenWondersSubscription( - messages = messages.map { it.payload }, - unsubscribe = { unsubscribe() } -) - -class SevenWondersSession(private val stompSession: KrossbowSession) { +class SevenWondersSession(private val stompSession: StompSessionWithKxSerialization) { suspend fun disconnect() = stompSession.disconnect() - suspend fun watchErrors(): KrossbowSubscription<ErrorDTO> = - stompSession.subscribe("/user/queue/errors") + suspend fun watchErrors(): StompSubscription<ErrorDTO> = + stompSession.subscribe("/user/queue/errors", ErrorDTO.serializer()) suspend fun chooseName(displayName: String): PlayerDTO { val action = ChooseNameAction(displayName) return stompSession.request("/app/chooseName", "/user/queue/nameChoice", action) } - suspend fun watchGames(): SevenWondersSubscription<Array<LobbyDTO>> = - stompSession.subscribe<Array<LobbyDTO>>("/topic/games").ignoreHeaders() + suspend fun watchGames(): StompSubscription<List<LobbyDTO>> = + stompSession.subscribe("/topic/games", LobbyDTO.serializer().list) suspend fun createGame(gameName: String): LobbyDTO { val action = CreateGameAction(gameName) @@ -77,40 +74,40 @@ class SevenWondersSession(private val stompSession: KrossbowSession) { } suspend fun leaveGame() { - stompSession.send("/app/lobby/leave") + stompSession.sendEmptyMsg("/app/lobby/leave") } suspend fun reorderPlayers(players: List<String>) { - stompSession.send("/app/lobby/reorderPlayers", ReorderPlayersAction(players)) + stompSession.convertAndSend("/app/lobby/reorderPlayers", ReorderPlayersAction(players), ReorderPlayersAction.serializer()) } suspend fun updateSettings(settings: CustomizableSettings) { - stompSession.send("/app/lobby/reorderPlayers", UpdateSettingsAction(settings)) + stompSession.convertAndSend("/app/lobby/reorderPlayers", UpdateSettingsAction(settings), UpdateSettingsAction.serializer()) } - suspend fun watchLobbyUpdates(gameId: Long): SevenWondersSubscription<LobbyDTO> = - stompSession.subscribe<LobbyDTO>("/topic/lobby/$gameId/updated").ignoreHeaders() + suspend fun watchLobbyUpdates(gameId: Long): StompSubscription<LobbyDTO> = + stompSession.subscribe("/topic/lobby/$gameId/updated", LobbyDTO.serializer()) - suspend fun watchGameStart(gameId: Long): SevenWondersSubscription<Unit> = - stompSession.subscribe<Unit>("/topic/lobby/$gameId/started").ignoreHeaders() + suspend fun watchGameStart(gameId: Long): StompSubscription<Unit> = + stompSession.subscribeEmptyMsg("/topic/lobby/$gameId/started") suspend fun startGame() { - stompSession.send("/app/lobby/startGame") + stompSession.sendEmptyMsg("/app/lobby/startGame") } - suspend fun watchPlayerReady(gameId: Long): SevenWondersSubscription<String> = - stompSession.subscribe<String>("/topic/game/$gameId/playerReady").ignoreHeaders() + suspend fun watchPlayerReady(gameId: Long): StompSubscription<String> = + stompSession.subscribe("/topic/game/$gameId/playerReady", String.serializer()) - suspend fun watchTableUpdates(gameId: Long): SevenWondersSubscription<GameState> = - stompSession.subscribe<GameState>("/topic/game/$gameId/tableUpdates").ignoreHeaders() + suspend fun watchTableUpdates(gameId: Long): StompSubscription<GameState> = + stompSession.subscribe("/topic/game/$gameId/tableUpdates", GameState.serializer()) - suspend fun watchPreparedCards(gameId: Long): SevenWondersSubscription<PreparedCard> = - stompSession.subscribe<PreparedCard>("/topic/game/$gameId/prepared").ignoreHeaders() + suspend fun watchPreparedCards(gameId: Long): StompSubscription<PreparedCard> = + stompSession.subscribe("/topic/game/$gameId/prepared", PreparedCard.serializer()) - suspend fun watchTurns(): SevenWondersSubscription<PlayerTurnInfo> = - stompSession.subscribe<PlayerTurnInfo>("/user/queue/game/turn").ignoreHeaders() + suspend fun watchTurns(): StompSubscription<PlayerTurnInfo> = + stompSession.subscribe("/user/queue/game/turn", PlayerTurnInfo.serializer()) suspend fun sayReady() { - stompSession.send("/app/game/sayReady") + stompSession.sendEmptyMsg("/app/game/sayReady") } } diff --git a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/config/WebSocketConfig.kt b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/config/WebSocketConfig.kt index 72312b70..8bff83da 100644 --- a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/config/WebSocketConfig.kt +++ b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/config/WebSocketConfig.kt @@ -30,7 +30,6 @@ class WebSocketConfig @Autowired constructor(private val topicSubscriptionInterc registry.addEndpoint(SEVEN_WONDERS_WS_ENDPOINT) .setHandshakeHandler(handshakeHandler()) .setAllowedOrigins("*") // to allow any client to use the API - .withSockJS() } @Bean 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 7b996680..ab947609 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 @@ -67,7 +67,7 @@ class SevenWondersTest { session2.joinGame(lobby.id) val outsiderSession = newPlayer("Outsider") - val (started) = outsiderSession.watchGameStart(lobby.id) + val started = outsiderSession.watchGameStart(lobby.id).messages ownerSession.startGame() val nothing = withTimeoutOrNull(30) { started.receive() } @@ -94,7 +94,7 @@ class SevenWondersTest { fun createGame_seenByConnectedPlayers() { runBlocking { val otherSession = newPlayer("OtherPlayer") - val (games) = otherSession.watchGames() + val games = otherSession.watchGames().messages var receivedLobbies = withTimeout(500) { games.receive() } assertNotNull(receivedLobbies) @@ -126,18 +126,18 @@ class SevenWondersTest { val session3 = newPlayer("Player3") session3.joinGame(lobby.id) - val (gameStart1) = session1.watchGameStart(lobby.id) - val (gameStart2) = session2.watchGameStart(lobby.id) - val (gameStart3) = session3.watchGameStart(lobby.id) + val gameStart1 = session1.watchGameStart(lobby.id).messages + val gameStart2 = session2.watchGameStart(lobby.id).messages + val gameStart3 = session3.watchGameStart(lobby.id).messages session1.startGame() withTimeout(500) { gameStart1.receive() } withTimeout(500) { gameStart2.receive() } withTimeout(500) { gameStart3.receive() } - val (turns1) = session1.watchTurns() - val (turns2) = session2.watchTurns() - val (turns3) = session3.watchTurns() + val turns1 = session1.watchTurns().messages + val turns2 = session2.watchTurns().messages + val turns3 = session3.watchTurns().messages session1.sayReady() session2.sayReady() session3.sayReady() 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 837c4db6..728a7d2f 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 @@ -13,7 +13,8 @@ typealias SwSagaContext = SagaContext<SwState, RAction, WrapperAction> suspend fun SwSagaContext.rootSaga() { val action = next<RequestChooseName>() - val session = SevenWondersClient().connect("http://localhost:8000") + val session = SevenWondersClient().connect("localhost:8000") + console.info("Connected to Seven Wonders web socket API") coroutineScope { launch { gameBrowserSaga(session) } |