From 5af219449a1a9b04cc122dceb21c61a7e23d5626 Mon Sep 17 00:00:00 2001 From: Joffrey Bion Date: Tue, 24 Mar 2020 15:27:12 +0100 Subject: Get joinability/startability info from server with reason --- .../org/luxons/sevenwonders/model/api/Api.kt | 9 +++++++- .../luxons/sevenwonders/server/api/Converters.kt | 8 +++++--- .../server/controllers/GameBrowserController.kt | 24 +++++++++------------- .../server/controllers/LobbyController.kt | 6 +++--- .../org/luxons/sevenwonders/server/lobby/Lobby.kt | 23 +++++++++++++++++++-- .../ui/components/gameBrowser/GameList.kt | 4 ++-- .../sevenwonders/ui/components/lobby/Lobby.kt | 3 ++- 7 files changed, 51 insertions(+), 26 deletions(-) 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 503141c4..5816edaf 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,7 +15,14 @@ data class LobbyDTO( val owner: String, val players: List, val state: State, - val canBeStarted: Boolean + val joinAction: Actionability, + val startAction: Actionability +) + +@Serializable +data class Actionability( + val canDo: Boolean, + val tooltip: String ) @Serializable 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 390dc839..5fb73b23 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,9 +5,11 @@ import org.luxons.sevenwonders.model.api.PlayerDTO import org.luxons.sevenwonders.server.lobby.Lobby import org.luxons.sevenwonders.server.lobby.Player -fun Lobby.toDTO(currentUser: String): LobbyDTO { - val players = getPlayers().map { it.toDTO(currentUser) } - return LobbyDTO(id, name, owner.username, players, state, canBeStarted()) +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 Player.toDTO(currentUser: String) = 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 e2f0458e..aa98f121 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 @@ -30,7 +30,6 @@ class GameBrowserController @Autowired constructor( private val playerRepository: PlayerRepository, private val template: SimpMessagingTemplate ) { - /** * Gets the created or updated games. The list of existing games is received on this topic at once upon * subscription, and then each time the list changes. @@ -42,7 +41,8 @@ class GameBrowserController @Autowired constructor( @SubscribeMapping("/games") // prefix /topic not shown fun listGames(principal: Principal): Collection { logger.info("Player '{}' subscribed to /topic/games", principal.name) - return lobbyRepository.list().map { it.toDTO(principal.name) } + val player = playerRepository.find(principal.name) + return lobbyRepository.list().map { it.toDTO(player) } } /** @@ -58,15 +58,13 @@ class GameBrowserController @Autowired constructor( fun createGame(@Validated action: CreateGameAction, principal: Principal): LobbyDTO { checkThatUserIsNotInAGame(principal, "cannot create another game") - val gameOwner = playerRepository.find(principal.name) - val lobby = lobbyRepository.create(action.gameName, gameOwner) + val player = playerRepository.find(principal.name) + val lobby = lobbyRepository.create(action.gameName, owner = player) - logger.info( - "Game '{}' ({}) created by {} ({})", lobby.name, lobby.id, gameOwner.displayName, gameOwner.username - ) + logger.info("Game '{}' ({}) created by {} ({})", lobby.name, lobby.id, player.displayName, player.username) // notify everyone that a new game exists - val lobbyDto = lobby.toDTO(principal.name) + val lobbyDto = lobby.toDTO(player) template.convertAndSend("/topic/games", listOf(lobbyDto)) return lobbyDto } @@ -85,13 +83,11 @@ class GameBrowserController @Autowired constructor( checkThatUserIsNotInAGame(principal, "cannot join another game") val lobby = lobbyRepository.find(action.gameId) - val newPlayer = playerRepository.find(principal.name) - lobby.addPlayer(newPlayer) + val player = playerRepository.find(principal.name) + lobby.addPlayer(player) - logger.info( - "Player '{}' ({}) joined game {}", newPlayer.displayName, newPlayer.username, lobby.name - ) - val lobbyDTO = lobby.toDTO(principal.name) + logger.info("Player '{}' ({}) joined game {}", player.displayName, player.username, lobby.name) + val lobbyDTO = lobby.toDTO(player) lobbyController.sendLobbyUpdateToPlayers(lobbyDTO) return lobbyDTO } 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 19834ef1..477a6739 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 @@ -44,7 +44,7 @@ class LobbyController @Autowired constructor( } logger.info("Player {} left game '{}'", player, lobby.name) - sendLobbyUpdateToPlayers(lobby.toDTO(principal.name)) + sendLobbyUpdateToPlayers(lobby.toDTO(principal.player)) } /** @@ -61,7 +61,7 @@ class LobbyController @Autowired constructor( lobby.reorderPlayers(action.orderedPlayers) logger.info("Players in game '{}' reordered to {}", lobby.name, action.orderedPlayers) - sendLobbyUpdateToPlayers(lobby.toDTO(principal.name)) + sendLobbyUpdateToPlayers(lobby.toDTO(principal.player)) } /** @@ -78,7 +78,7 @@ class LobbyController @Autowired constructor( lobby.settings = action.settings logger.info("Updated settings of game '{}'", lobby.name) - sendLobbyUpdateToPlayers(lobby.toDTO(principal.name)) + sendLobbyUpdateToPlayers(lobby.toDTO(principal.player)) } internal fun sendLobbyUpdateToPlayers(lobby: LobbyDTO) { 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 599d889f..5368bedf 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,6 +3,7 @@ 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( @@ -40,11 +41,24 @@ 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 - private fun playerNameAlreadyUsed(name: String?): Boolean = players.any { it.displayName == name } + private fun playerNameAlreadyUsed(name: String): Boolean = players.any { it.displayName == name } @Synchronized fun startGame(): Game { @@ -57,7 +71,12 @@ class Lobby( return game } - fun canBeStarted(): Boolean = hasEnoughPlayers() + 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 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 375ac96f..10d5c868 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 @@ -108,9 +108,9 @@ private fun RBuilder.playerCount(nPlayers: Int) { private fun RBuilder.joinButton(lobby: LobbyDTO, joinGame: (Long) -> Unit) { bpButton( minimal = true, - title = "Join Game", + title = lobby.joinAction.tooltip, icon = "arrow-right", - disabled = lobby.state != State.LOBBY, + disabled = !lobby.joinAction.canDo, onClick = { joinGame(lobby.id) } ) } 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 62f1c524..668a41c2 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 @@ -40,7 +40,8 @@ class LobbyPresenter(props: LobbyProps) : RComponent(props) large = true, intent = Intent.PRIMARY, icon = "play", - disabled = !currentGame.canBeStarted, + title = currentGame.startAction.tooltip, + disabled = !currentGame.startAction.canDo, onClick = { props.startGame() } ) { + "START" -- cgit