diff options
17 files changed, 155 insertions, 141 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 d95c32f5..b97413c3 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 @@ -2,8 +2,8 @@ package org.luxons.sevenwonders.client import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy -import kotlinx.serialization.builtins.serializer import kotlinx.serialization.builtins.list +import kotlinx.serialization.builtins.serializer import org.hildan.krossbow.stomp.StompClient import org.hildan.krossbow.stomp.StompSubscription import org.hildan.krossbow.stomp.conversions.kxserialization.StompSessionWithKxSerialization @@ -12,11 +12,10 @@ 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.PlayerMove import org.luxons.sevenwonders.model.PlayerTurnInfo +import org.luxons.sevenwonders.model.api.ConnectedPlayer import org.luxons.sevenwonders.model.api.LobbyDTO -import org.luxons.sevenwonders.model.api.PlayerDTO import org.luxons.sevenwonders.model.api.SEVEN_WONDERS_WS_ENDPOINT import org.luxons.sevenwonders.model.api.actions.ChooseNameAction import org.luxons.sevenwonders.model.api.actions.CreateGameAction @@ -58,12 +57,12 @@ class SevenWondersSession(private val stompSession: StompSessionWithKxSerializat suspend fun watchErrors(): StompSubscription<ErrorDTO> = stompSession.subscribe("/user/queue/errors", ErrorDTO.serializer()) - suspend fun chooseName(displayName: String): PlayerDTO = stompSession.request( + suspend fun chooseName(displayName: String): ConnectedPlayer = stompSession.request( sendDestination = "/app/chooseName", receiveDestination = "/user/queue/nameChoice", payload = ChooseNameAction(displayName), serializer = ChooseNameAction.serializer(), - deserializer = PlayerDTO.serializer() + deserializer = ConnectedPlayer.serializer() ) suspend fun watchGames(): StompSubscription<List<LobbyDTO>> = diff --git a/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Api.kt b/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Api.kt index b648b72b..946c93c7 100644 --- a/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Api.kt +++ b/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Api.kt @@ -15,9 +15,33 @@ data class LobbyDTO( val owner: String, val players: List<PlayerDTO>, val state: State, - val joinAction: Actionability, - val startAction: Actionability -) + val hasEnoughPlayers: Boolean, + val maxPlayersReached: Boolean +) { + fun joinability(userDisplayName: String): Actionability = when { + state != State.LOBBY -> Actionability(false, "Cannot join: the game has already started") + maxPlayersReached -> Actionability( + false, + "Cannot join: the game is full" + ) + playerNameAlreadyUsed(userDisplayName) -> Actionability( + false, + "Cannot join: already a player named '$userDisplayName' in this game" + ) + else -> Actionability(true, "Join game") + } + + fun startability(username: String): Actionability = when { + !hasEnoughPlayers -> Actionability( + false, + "Cannot start the game, more players needed" + ) + owner != username -> Actionability(false, "Cannot start the game: only the owner can") + else -> Actionability(true, "Start game") + } + + private fun playerNameAlreadyUsed(name: String): Boolean = players.any { it.displayName == name } +} @Serializable data class Actionability( @@ -26,11 +50,16 @@ data class Actionability( ) @Serializable +data class ConnectedPlayer( + val username: String, + val displayName: String +) + +@Serializable data class PlayerDTO( val username: String, val displayName: String, val index: Int, val isGameOwner: Boolean, - val isMe: Boolean, val isReady: Boolean ) diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/GameTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/GameTest.kt index 935d66e5..ad3c28d4 100644 --- a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/GameTest.kt +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/GameTest.kt @@ -50,7 +50,7 @@ class GameTest { assertEquals(nbPlayers, turnInfos.size) turnInfos.forEach { assertEquals(ageToCheck, it.currentAge) - assertEquals(handSize, it.hand.size) + assertEquals(handSize, it.hand?.size) } val moveExpectations = turnInfos.mapNotNull { it.firstAvailableMove() } @@ -67,20 +67,20 @@ class GameTest { private fun PlayerTurnInfo.firstAvailableMove(): MoveExpectation? = when (action) { Action.PLAY, Action.PLAY_2, Action.PLAY_LAST -> createPlayCardMove(this) Action.PICK_NEIGHBOR_GUILD -> createPickGuildMove(this) - Action.WAIT -> null + Action.WAIT, Action.SAY_READY -> null } private fun createPlayCardMove(turnInfo: PlayerTurnInfo): MoveExpectation { val wonderBuildability = turnInfo.wonderBuildability if (wonderBuildability.isBuildable) { val transactions = wonderBuildability.cheapestTransactions.first() - return planMove(turnInfo, MoveType.UPGRADE_WONDER, turnInfo.hand.first(), transactions) + return planMove(turnInfo, MoveType.UPGRADE_WONDER, turnInfo.hand!!.first(), transactions) } - val playableCard = turnInfo.hand.firstOrNull { it.playability.isPlayable } + val playableCard = turnInfo.hand!!.firstOrNull { it.playability.isPlayable } return if (playableCard != null) { planMove(turnInfo, MoveType.PLAY, playableCard, playableCard.playability.cheapestTransactions.first()) } else { - planMove(turnInfo, MoveType.DISCARD, turnInfo.hand.first(), + planMove(turnInfo, MoveType.DISCARD, turnInfo.hand!!.first(), noTransactions() ) } diff --git a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/api/Converters.kt b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/api/Converters.kt index a2390a7d..7c3ccfbb 100644 --- a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/api/Converters.kt +++ b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/api/Converters.kt @@ -5,12 +5,15 @@ import org.luxons.sevenwonders.model.api.PlayerDTO import org.luxons.sevenwonders.server.lobby.Lobby import org.luxons.sevenwonders.server.lobby.Player -fun Lobby.toDTO(currentPlayer: Player): LobbyDTO { - val players = getPlayers().map { it.toDTO(currentPlayer.username) } - val joinability = joinability(currentPlayer.displayName) - val startability = startability(currentPlayer.username) - return LobbyDTO(id, name, owner.username, players, state, joinability, startability) -} +fun Lobby.toDTO(): LobbyDTO = LobbyDTO( + id = id, + name = name, + owner = owner.username, + players = getPlayers().map { it.toDTO() }, + state = state, + hasEnoughPlayers = hasEnoughPlayers(), + maxPlayersReached = maxPlayersReached() +) + +fun Player.toDTO() = PlayerDTO(username, displayName, index, isGameOwner, isReady) -fun Player.toDTO(currentUser: String) = - PlayerDTO(username, displayName, index, isGameOwner, username === currentUser, isReady) diff --git a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserController.kt b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserController.kt index 0bd1de79..3de0de97 100644 --- a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserController.kt +++ b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserController.kt @@ -42,7 +42,7 @@ class GameBrowserController @Autowired constructor( fun listGames(principal: Principal): Collection<LobbyDTO> { logger.info("Player '{}' subscribed to /topic/games", principal.name) val player = playerRepository.find(principal.name) - return lobbyRepository.list().map { it.toDTO(player) } + return lobbyRepository.list().map { it.toDTO() } } /** @@ -64,7 +64,7 @@ class GameBrowserController @Autowired constructor( logger.info("Game '{}' ({}) created by {} ({})", lobby.name, lobby.id, player.displayName, player.username) // notify everyone that a new game exists - val lobbyDto = lobby.toDTO(player) + val lobbyDto = lobby.toDTO() template.convertAndSend("/topic/games", listOf(lobbyDto)) return lobbyDto } @@ -87,8 +87,8 @@ class GameBrowserController @Autowired constructor( lobby.addPlayer(player) logger.info("Player '{}' ({}) joined game {}", player.displayName, player.username, lobby.name) - val lobbyDTO = lobby.toDTO(player) - lobbyController.sendLobbyUpdateToPlayers(lobby, player) + val lobbyDTO = lobby.toDTO() + lobbyController.sendLobbyUpdateToPlayers(lobby) return lobbyDTO } diff --git a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameController.kt b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameController.kt index 0db7a21c..803cca78 100644 --- a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameController.kt +++ b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameController.kt @@ -66,7 +66,7 @@ class GameController @Autowired constructor( val player = principal.player val game = player.game val preparedCardBack = game.prepareMove(player.index, action.move) - val preparedCard = PreparedCard(player.toDTO(principal.name), preparedCardBack) + val preparedCard = PreparedCard(player.toDTO(), preparedCardBack) logger.info("Game {}: player {} prepared move {}", game.id, principal.name, action.move) sendPreparedCard(game.id, preparedCard) diff --git a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/HomeController.kt b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/HomeController.kt index a0d53f8b..c2619f8c 100644 --- a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/HomeController.kt +++ b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/HomeController.kt @@ -1,9 +1,9 @@ package org.luxons.sevenwonders.server.controllers import org.hildan.livedoc.core.annotations.Api +import org.luxons.sevenwonders.model.api.ConnectedPlayer import org.luxons.sevenwonders.model.api.PlayerDTO import org.luxons.sevenwonders.model.api.actions.ChooseNameAction -import org.luxons.sevenwonders.server.api.toDTO import org.luxons.sevenwonders.server.repositories.PlayerRepository import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired @@ -32,12 +32,12 @@ class HomeController @Autowired constructor( */ @MessageMapping("/chooseName") @SendToUser("/queue/nameChoice") - fun chooseName(@Validated action: ChooseNameAction, principal: Principal): PlayerDTO { + fun chooseName(@Validated action: ChooseNameAction, principal: Principal): ConnectedPlayer { val username = principal.name val player = playerRepository.createOrUpdate(username, action.playerName) logger.info("Player '{}' chose the name '{}'", username, player.displayName) - return player.toDTO(username) + return ConnectedPlayer(username, player.displayName) } companion object { diff --git a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/LobbyController.kt b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/LobbyController.kt index b74913cb..04fb2228 100644 --- a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/LobbyController.kt +++ b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/LobbyController.kt @@ -45,7 +45,7 @@ class LobbyController @Autowired constructor( } logger.info("Player {} left game '{}'", player, lobby.name) - sendLobbyUpdateToPlayers(lobby, player) + sendLobbyUpdateToPlayers(lobby) } /** @@ -62,7 +62,7 @@ class LobbyController @Autowired constructor( lobby.reorderPlayers(action.orderedPlayers) logger.info("Players in game '{}' reordered to {}", lobby.name, action.orderedPlayers) - sendLobbyUpdateToPlayers(lobby, principal.player) + sendLobbyUpdateToPlayers(lobby) } /** @@ -75,19 +75,18 @@ class LobbyController @Autowired constructor( */ @MessageMapping("/lobby/updateSettings") fun updateSettings(@Validated action: UpdateSettingsAction, principal: Principal) { - val player = principal.player - val lobby = player.ownedLobby + val lobby = principal.player.ownedLobby lobby.settings = action.settings logger.info("Updated settings of game '{}'", lobby.name) - sendLobbyUpdateToPlayers(lobby, player) + sendLobbyUpdateToPlayers(lobby) } - internal fun sendLobbyUpdateToPlayers(lobby: Lobby, player: Player) { + internal fun sendLobbyUpdateToPlayers(lobby: Lobby) { lobby.getPlayers().forEach { - template.convertAndSendToUser(it.username, "/queue/lobby/updated", lobby.toDTO(it)) + template.convertAndSendToUser(it.username, "/queue/lobby/updated", lobby.toDTO()) } - template.convertAndSend("/topic/games", listOf(lobby.toDTO(player))) + template.convertAndSend("/topic/games", listOf(lobby.toDTO())) } /** diff --git a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/lobby/Lobby.kt b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/lobby/Lobby.kt index 5368bedf..eadc1742 100644 --- a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/lobby/Lobby.kt +++ b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/lobby/Lobby.kt @@ -3,7 +3,6 @@ package org.luxons.sevenwonders.server.lobby import org.luxons.sevenwonders.engine.Game import org.luxons.sevenwonders.engine.data.GameDefinition import org.luxons.sevenwonders.model.CustomizableSettings -import org.luxons.sevenwonders.model.api.Actionability import org.luxons.sevenwonders.model.api.State class Lobby( @@ -14,8 +13,7 @@ class Lobby( ) { private val players: MutableList<Player> = ArrayList(gameDefinition.maxPlayers) - var settings: CustomizableSettings = - CustomizableSettings() + var settings: CustomizableSettings = CustomizableSettings() var state = State.LOBBY private set @@ -41,22 +39,9 @@ class Lobby( players.add(player) } - fun joinability(userDisplayName: String): Actionability = when { - hasStarted() -> Actionability(false, "Cannot join: the game has already started") - maxPlayersReached() -> Actionability( - false, - "Cannot join: the game is full (${gameDefinition.maxPlayers} players max)" - ) - playerNameAlreadyUsed(userDisplayName) -> Actionability( - false, - "Cannot join: already a player named '$userDisplayName' in this game" - ) - else -> Actionability(true, "Join game") - } - private fun hasStarted(): Boolean = state != State.LOBBY - private fun maxPlayersReached(): Boolean = players.size >= gameDefinition.maxPlayers + fun maxPlayersReached(): Boolean = players.size >= gameDefinition.maxPlayers private fun playerNameAlreadyUsed(name: String): Boolean = players.any { it.displayName == name } @@ -71,14 +56,7 @@ class Lobby( return game } - fun startability(username: String): Actionability = when { - !hasEnoughPlayers() -> Actionability(false, "Cannot start: min ${gameDefinition.minPlayers} players required" - ) - owner.username != username -> Actionability(false, "Cannot start the game: only the owner can") - else -> Actionability(true, "Start game") - } - - private fun hasEnoughPlayers(): Boolean = players.size >= gameDefinition.minPlayers + fun hasEnoughPlayers(): Boolean = players.size >= gameDefinition.minPlayers @Synchronized fun reorderPlayers(orderedUsernames: List<String>) { diff --git a/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserControllerTest.kt b/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserControllerTest.kt index 804b5f6e..88e04c14 100644 --- a/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserControllerTest.kt +++ b/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserControllerTest.kt @@ -52,7 +52,7 @@ class GameBrowserControllerTest { assertFalse(games.isEmpty()) val lobby = games.iterator().next() assertEquals(lobby, createdLobby) - assertEquals(player.toDTO(principal.name), lobby.players[0]) + assertEquals(player.toDTO(), lobby.players[0]) } @Test @@ -90,7 +90,7 @@ class GameBrowserControllerTest { val createGameAction = CreateGameAction("Test Game") val createdLobby = gameBrowserController.createGame(createGameAction, ownerPrincipal) - assertEquals(owner.toDTO(ownerPrincipal.name), createdLobby.players[0]) + assertEquals(owner.toDTO(), createdLobby.players[0]) val joiner = playerRepository.createOrUpdate("testjoiner", "Test User Joiner") val joinerPrincipal = TestPrincipal("testjoiner") @@ -98,8 +98,8 @@ class GameBrowserControllerTest { val joinedLobby = gameBrowserController.joinGame(joinGameAction, joinerPrincipal) - assertEquals(owner.toDTO(joinerPrincipal.name), joinedLobby.players[0]) - assertEquals(joiner.toDTO(joinerPrincipal.name), joinedLobby.players[1]) + assertEquals(owner.toDTO(), joinedLobby.players[0]) + assertEquals(joiner.toDTO(), joinedLobby.players[1]) } @Test diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/GameBrowser.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/GameBrowser.kt index fc7e12d5..2f860ca7 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/GameBrowser.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/GameBrowser.kt @@ -6,5 +6,5 @@ import react.dom.* fun RBuilder.gameBrowser() = div { h1 { +"Games" } createGameForm {} - gameList {} + gameList() } diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/GameList.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/GameList.kt index 19cd961f..47c17da1 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/GameList.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/GameList.kt @@ -15,6 +15,7 @@ import kotlinx.css.flexDirection import kotlinx.css.verticalAlign import kotlinx.html.classes import kotlinx.html.title +import org.luxons.sevenwonders.model.api.ConnectedPlayer import org.luxons.sevenwonders.model.api.LobbyDTO import org.luxons.sevenwonders.model.api.State import org.luxons.sevenwonders.ui.redux.RequestJoinGame @@ -30,6 +31,7 @@ import styled.styledSpan import styled.styledTr interface GameListStateProps : RProps { + var connectedPlayer: ConnectedPlayer var games: List<LobbyDTO> } @@ -56,69 +58,73 @@ class GameListPresenter(props: GameListProps) : RComponent<GameListProps, RState } } } -} - -private fun RBuilder.gameListHeaderRow() = tr { - th { +"Name" } - th { +"Status" } - th { +"Nb Players" } - th { +"Join" } -} -private fun RBuilder.gameListItemRow(lobby: LobbyDTO, joinGame: (Long) -> Unit) = styledTr { - css { - verticalAlign = VerticalAlign.middle + private fun RBuilder.gameListHeaderRow() = tr { + th { +"Name" } + th { +"Status" } + th { +"Nb Players" } + th { +"Join" } } - attrs { - key = lobby.id.toString() - } - td { +lobby.name } - td { gameStatus(lobby.state) } - td { playerCount(lobby.players.size) } - td { joinButton(lobby, joinGame) } -} -private fun RBuilder.gameStatus(state: State) { - val intent = when(state) { - State.LOBBY -> Intent.SUCCESS - State.PLAYING -> Intent.WARNING - State.FINISHED -> Intent.DANGER - } - bpTag(minimal = true, intent = intent) { - +state.toString() - } -} - -private fun RBuilder.playerCount(nPlayers: Int) { - styledDiv { + private fun RBuilder.gameListItemRow(lobby: LobbyDTO, joinGame: (Long) -> Unit) = styledTr { css { - display = Display.flex - flexDirection = FlexDirection.row - alignItems = Align.center + verticalAlign = VerticalAlign.middle } attrs { - title = "Number of players" + key = lobby.id.toString() + } + td { +lobby.name } + td { gameStatus(lobby.state) } + td { playerCount(lobby.players.size) } + td { joinButton(lobby) } + } + + private fun RBuilder.gameStatus(state: State) { + val intent = when(state) { + State.LOBBY -> Intent.SUCCESS + State.PLAYING -> Intent.WARNING + State.FINISHED -> Intent.DANGER } - bpIcon(name = "people", title = null) - styledSpan { - +nPlayers.toString() + bpTag(minimal = true, intent = intent) { + +state.toString() } } -} -private fun RBuilder.joinButton(lobby: LobbyDTO, joinGame: (Long) -> Unit) { - bpButton( - minimal = true, - title = lobby.joinAction.tooltip, - icon = "arrow-right", - disabled = !lobby.joinAction.canDo, - onClick = { joinGame(lobby.id) } - ) + private fun RBuilder.playerCount(nPlayers: Int) { + styledDiv { + css { + display = Display.flex + flexDirection = FlexDirection.row + alignItems = Align.center + } + attrs { + title = "Number of players" + } + bpIcon(name = "people", title = null) + styledSpan { + +nPlayers.toString() + } + } + } + + private fun RBuilder.joinButton(lobby: LobbyDTO) { + val joinability = lobby.joinability(props.connectedPlayer.displayName) + bpButton( + minimal = true, + title = joinability.tooltip, + icon = "arrow-right", + disabled = !joinability.canDo, + onClick = { props.joinGame(lobby.id) } + ) + } } -val gameList = connectStateAndDispatch<GameListStateProps, GameListDispatchProps, GameListProps>( +fun RBuilder.gameList() = gameList {} + +private val gameList = connectStateAndDispatch<GameListStateProps, GameListDispatchProps, GameListProps>( clazz = GameListPresenter::class, mapStateToProps = { state, _ -> + connectedPlayer = state.connectedPlayer ?: error("there should be a connected player") games = state.games }, mapDispatchToProps = { dispatch, _ -> diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/PlayerInfo.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/PlayerInfo.kt index 222d4329..b939dfe1 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/PlayerInfo.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/PlayerInfo.kt @@ -1,6 +1,6 @@ package org.luxons.sevenwonders.ui.components.gameBrowser -import org.luxons.sevenwonders.model.api.PlayerDTO +import org.luxons.sevenwonders.model.api.ConnectedPlayer import org.luxons.sevenwonders.ui.redux.connectState import react.RBuilder import react.RComponent @@ -9,7 +9,7 @@ import react.RState import react.dom.* interface PlayerInfoProps : RProps { - var currentPlayer: PlayerDTO? + var connectedPlayer: ConnectedPlayer? } class PlayerInfoPresenter(props: PlayerInfoProps) : RComponent<PlayerInfoProps, RState>(props) { @@ -19,7 +19,7 @@ class PlayerInfoPresenter(props: PlayerInfoProps) : RComponent<PlayerInfoProps, b { +"Username:" } - props.currentPlayer?.let { + props.connectedPlayer?.let { + " ${it.displayName}" } } @@ -31,6 +31,6 @@ fun RBuilder.playerInfo() = playerInfo {} private val playerInfo = connectState( clazz = PlayerInfoPresenter::class, mapStateToProps = { state, _ -> - currentPlayer = state.currentPlayer + connectedPlayer = state.connectedPlayer } ) diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/Lobby.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/Lobby.kt index 45c9ca11..5b13d8b1 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/Lobby.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/Lobby.kt @@ -34,14 +34,15 @@ class LobbyPresenter(props: LobbyProps) : RComponent<LobbyProps, RState>(props) } div { h2 { +"${currentGame.name} — Lobby" } - radialPlayerList(currentGame.players) + radialPlayerList(currentGame.players, currentPlayer) if (currentPlayer.isGameOwner) { + val startability = currentGame.startability(currentPlayer.username) bpButton( large = true, intent = Intent.PRIMARY, icon = "play", - title = currentGame.startAction.tooltip, - disabled = !currentGame.startAction.canDo, + title = startability.tooltip, + disabled = !startability.canDo, onClick = { props.startGame() } ) { + "START" diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/RadialPlayerList.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/RadialPlayerList.kt index 364a0dde..ff541696 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/RadialPlayerList.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/RadialPlayerList.kt @@ -19,11 +19,11 @@ import styled.css import styled.styledDiv import styled.styledH5 -fun RBuilder.radialPlayerList(players: List<PlayerDTO>): ReactElement { +fun RBuilder.radialPlayerList(players: List<PlayerDTO>, currentPlayer: PlayerDTO): ReactElement { val playerItemBuilders = players .growTo(targetSize = 3) - .withUserFirst() - .map { p -> p.elementBuilder() } + .withUserFirst(currentPlayer) + .map { p -> p.elementBuilder(p?.username == currentPlayer.username) } val tableImgBuilder: ElementBuilder = { roundTableImg() } @@ -48,35 +48,33 @@ private fun RBuilder.roundTableImg(): ReactElement = img { } } -private fun List<PlayerDTO?>.withUserFirst(): List<PlayerDTO?> { - val nonUsersBeginning = takeWhile { !it.isMe } +private fun List<PlayerDTO?>.withUserFirst(me: PlayerDTO): List<PlayerDTO?> { + val nonUsersBeginning = takeWhile { it?.username != me.username } val userToEnd = subList(nonUsersBeginning.size, size) return userToEnd + nonUsersBeginning } -private val PlayerDTO?.isMe: Boolean get() = this?.isMe ?: false - private fun <T> List<T>.growTo(targetSize: Int): List<T?> { if (size >= targetSize) return this return this + List(targetSize - size) { null } } -private fun PlayerDTO?.elementBuilder(): ElementBuilder { +private fun PlayerDTO?.elementBuilder(isMe: Boolean): ElementBuilder { if (this == null) { return { playerPlaceholder() } } else { - return { playerItem(this@elementBuilder) } + return { playerItem(this@elementBuilder, isMe) } } } -private fun RBuilder.playerItem(player: PlayerDTO): ReactElement = styledDiv { +private fun RBuilder.playerItem(player: PlayerDTO, isMe: Boolean): ReactElement = styledDiv { css { display = Display.flex flexDirection = FlexDirection.column alignItems = Align.center } val title = if (player.isGameOwner) "Game owner" else null - userIcon(isMe = player.isMe, isOwner = player.isGameOwner, title = title) + userIcon(isMe = isMe, isOwner = player.isGameOwner, title = title) styledH5 { css { margin = "0" diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt index 63fc26eb..4947fa9b 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt @@ -1,12 +1,12 @@ package org.luxons.sevenwonders.ui.redux import org.luxons.sevenwonders.model.PlayerTurnInfo +import org.luxons.sevenwonders.model.api.ConnectedPlayer import org.luxons.sevenwonders.model.api.LobbyDTO -import org.luxons.sevenwonders.model.api.PlayerDTO import org.luxons.sevenwonders.model.cards.PreparedCard import redux.RAction -data class SetCurrentPlayerAction(val player: PlayerDTO): RAction +data class SetCurrentPlayerAction(val player: ConnectedPlayer): RAction data class UpdateGameListAction(val games: List<LobbyDTO>): RAction diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt index f2a20cef..18d34d78 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt @@ -1,25 +1,26 @@ package org.luxons.sevenwonders.ui.redux import org.luxons.sevenwonders.model.PlayerTurnInfo +import org.luxons.sevenwonders.model.api.ConnectedPlayer import org.luxons.sevenwonders.model.api.LobbyDTO import org.luxons.sevenwonders.model.api.PlayerDTO import org.luxons.sevenwonders.model.api.State import redux.RAction data class SwState( - val currentPlayer: PlayerDTO? = null, + val connectedPlayer: ConnectedPlayer? = null, // they must be by ID to support updates to a sublist val gamesById: Map<Long, LobbyDTO> = emptyMap(), - val currentPlayerUsername: String? = null, val currentLobby: LobbyDTO? = null, val currentTurnInfo: PlayerTurnInfo? = null ) { + val currentPlayer: PlayerDTO? = currentLobby?.players?.first { it.username == connectedPlayer?.username } val games: List<LobbyDTO> = gamesById.values.toList() } fun rootReducer(state: SwState, action: RAction): SwState = state.copy( gamesById = gamesReducer(state.gamesById, action), - currentPlayer = currentPlayerReducer(state.currentPlayer, action), + connectedPlayer = currentPlayerReducer(state.connectedPlayer, action), currentLobby = currentLobbyReducer(state.currentLobby, action), currentTurnInfo = currentTurnInfoReducer(state.currentTurnInfo, action) ) @@ -29,7 +30,7 @@ private fun gamesReducer(games: Map<Long, LobbyDTO>, action: RAction): Map<Long, else -> games } -private fun currentPlayerReducer(currentPlayer: PlayerDTO?, action: RAction): PlayerDTO? = when (action) { +private fun currentPlayerReducer(currentPlayer: ConnectedPlayer?, action: RAction): ConnectedPlayer? = when (action) { is SetCurrentPlayerAction -> action.player else -> currentPlayer } |