diff options
author | Joffrey BION <joffrey.bion@gmail.com> | 2019-05-16 23:48:38 +0200 |
---|---|---|
committer | Joffrey BION <joffrey.bion@gmail.com> | 2019-05-16 23:48:38 +0200 |
commit | 2382a452456e4bdef4584e1046925e372624cb79 (patch) | |
tree | 0e49b2e5d81facb55fb8b08228abeb218a27d466 /backend/src/main/kotlin/org/luxons | |
parent | Remove GRADLE_METADATA feature to avoid breaking frontend build (diff) | |
download | seven-wonders-2382a452456e4bdef4584e1046925e372624cb79.tar.gz seven-wonders-2382a452456e4bdef4584e1046925e372624cb79.tar.bz2 seven-wonders-2382a452456e4bdef4584e1046925e372624cb79.zip |
Rationalize module names
Diffstat (limited to 'backend/src/main/kotlin/org/luxons')
25 files changed, 0 insertions, 1011 deletions
diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/SevenWonders.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/SevenWonders.kt deleted file mode 100644 index 04f03956..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/SevenWonders.kt +++ /dev/null @@ -1,13 +0,0 @@ -package org.luxons.sevenwonders - -import org.hildan.livedoc.spring.boot.starter.EnableJSONDoc -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.boot.runApplication - -@SpringBootApplication -@EnableJSONDoc -class SevenWonders - -fun main(args: Array<String>) { - runApplication<SevenWonders>(*args) -} diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/actions/ChooseNameAction.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/actions/ChooseNameAction.kt deleted file mode 100644 index ab444780..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/actions/ChooseNameAction.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.luxons.sevenwonders.actions - -import org.hildan.livedoc.core.annotations.types.ApiType -import org.hildan.livedoc.core.annotations.types.ApiTypeProperty -import org.luxons.sevenwonders.doc.Documentation -import javax.validation.constraints.Size - -/** - * The action to choose the player's name. This is the first action that should be called. - */ -@ApiType(group = Documentation.GROUP_ACTIONS) -class ChooseNameAction( - /** - * The display name of the player. May contain spaces and special characters. - */ - @Size(min = 2, max = 20) - @ApiTypeProperty(required = true) - val playerName: String -) diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/actions/CreateGameAction.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/actions/CreateGameAction.kt deleted file mode 100644 index c10f9c34..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/actions/CreateGameAction.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.luxons.sevenwonders.actions - -import org.hildan.livedoc.core.annotations.types.ApiType -import org.hildan.livedoc.core.annotations.types.ApiTypeProperty -import org.luxons.sevenwonders.doc.Documentation -import javax.validation.constraints.Size - -/** - * The action to create a game. - */ -@ApiType(group = Documentation.GROUP_ACTIONS) -class CreateGameAction( - /** - * The name of the game to create. - */ - @Size(min = 2, max = 30) - @ApiTypeProperty(required = true) - val gameName: String -) diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/actions/JoinGameAction.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/actions/JoinGameAction.kt deleted file mode 100644 index 002309b3..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/actions/JoinGameAction.kt +++ /dev/null @@ -1,17 +0,0 @@ -package org.luxons.sevenwonders.actions - -import org.hildan.livedoc.core.annotations.types.ApiType -import org.hildan.livedoc.core.annotations.types.ApiTypeProperty -import org.luxons.sevenwonders.doc.Documentation - -/** - * The action to join a game. - */ -@ApiType(group = Documentation.GROUP_ACTIONS) -class JoinGameAction( - /** - * The ID of the game to join. - */ - @ApiTypeProperty(required = true) - val gameId: Long -) diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/actions/PrepareMoveAction.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/actions/PrepareMoveAction.kt deleted file mode 100644 index 6b39c486..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/actions/PrepareMoveAction.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.luxons.sevenwonders.actions - -import org.hildan.livedoc.core.annotations.types.ApiType -import org.hildan.livedoc.core.annotations.types.ApiTypeProperty -import org.luxons.sevenwonders.doc.Documentation -import org.luxons.sevenwonders.game.api.PlayerMove - -/** - * The action to prepare the next move during a game. - */ -@ApiType(group = Documentation.GROUP_ACTIONS) -class PrepareMoveAction( - /** - * The move to prepare. - */ - @ApiTypeProperty(required = true) - val move: PlayerMove -) diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/actions/ReorderPlayersAction.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/actions/ReorderPlayersAction.kt deleted file mode 100644 index 79a32137..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/actions/ReorderPlayersAction.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.luxons.sevenwonders.actions - -import org.hildan.livedoc.core.annotations.types.ApiType -import org.hildan.livedoc.core.annotations.types.ApiTypeProperty -import org.luxons.sevenwonders.doc.Documentation - -/** - * The action to update the order of the players around the table. Can only be called in the lobby by the owner of the - * game. - */ -@ApiType(group = Documentation.GROUP_ACTIONS) -class ReorderPlayersAction( - /** - * The list of usernames of the players, in the new order. - */ - @ApiTypeProperty(required = true) - val orderedPlayers: List<String> -) diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/actions/UpdateSettingsAction.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/actions/UpdateSettingsAction.kt deleted file mode 100644 index d13e5b45..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/actions/UpdateSettingsAction.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.luxons.sevenwonders.actions - -import org.hildan.livedoc.core.annotations.types.ApiType -import org.hildan.livedoc.core.annotations.types.ApiTypeProperty -import org.luxons.sevenwonders.doc.Documentation -import org.luxons.sevenwonders.game.api.CustomizableSettings - -/** - * The action to update the settings of the game. Can only be called in the lobby by the owner of the game. - */ -@ApiType(group = Documentation.GROUP_ACTIONS) -class UpdateSettingsAction( - /** - * The new values for the settings. - */ - @ApiTypeProperty(required = true) - val settings: CustomizableSettings -) diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/api/LobbyDTO.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/api/LobbyDTO.kt deleted file mode 100644 index b4445f32..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/api/LobbyDTO.kt +++ /dev/null @@ -1,17 +0,0 @@ -package org.luxons.sevenwonders.api - -import org.luxons.sevenwonders.lobby.Lobby -import org.luxons.sevenwonders.lobby.State - -data class LobbyDTO( - val id: Long, - val name: String, - val owner: String, - val players: List<PlayerDTO>, - val state: State -) - -fun Lobby.toDTO(currentUser: String): LobbyDTO { - val players = getPlayers().map { it.toDTO(currentUser) } - return LobbyDTO(id, name, owner.username, players, state) -} diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/api/PlayerDTO.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/api/PlayerDTO.kt deleted file mode 100644 index 54c69122..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/api/PlayerDTO.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.luxons.sevenwonders.api - -import org.luxons.sevenwonders.lobby.Player - -data class PlayerDTO( - val username: String, - val displayName: String, - val index: Int, - val isGameOwner: Boolean, - val isUser: Boolean -) - -fun Player.toDTO(currentUser: String) = - PlayerDTO(username, displayName, index, isGameOwner, username === currentUser) diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/config/AnonymousUsersHandshakeHandler.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/config/AnonymousUsersHandshakeHandler.kt deleted file mode 100644 index db707d1b..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/config/AnonymousUsersHandshakeHandler.kt +++ /dev/null @@ -1,27 +0,0 @@ -package org.luxons.sevenwonders.config - -import org.springframework.http.server.ServerHttpRequest -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken -import org.springframework.web.socket.WebSocketHandler -import org.springframework.web.socket.server.support.DefaultHandshakeHandler -import java.security.Principal - -/** - * Generates [Principal] objects for anonymous users in the form "playerX", where X is an auto-incremented number. - */ -internal class AnonymousUsersHandshakeHandler : DefaultHandshakeHandler() { - - private var playerId = 0 - - override fun determineUser( - request: ServerHttpRequest, - wsHandler: WebSocketHandler, - attributes: Map<String, Any> - ): Principal? { - var p = super.determineUser(request, wsHandler, attributes) - if (p == null) { - p = UsernamePasswordAuthenticationToken("player" + playerId++, null) - } - return p - } -} diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/config/TopicSubscriptionInterceptor.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/config/TopicSubscriptionInterceptor.kt deleted file mode 100644 index f4c55c2c..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/config/TopicSubscriptionInterceptor.kt +++ /dev/null @@ -1,38 +0,0 @@ -package org.luxons.sevenwonders.config - -import org.luxons.sevenwonders.validation.DestinationAccessValidator -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.messaging.Message -import org.springframework.messaging.MessageChannel -import org.springframework.messaging.simp.stomp.StompCommand -import org.springframework.messaging.simp.stomp.StompHeaderAccessor -import org.springframework.messaging.support.ChannelInterceptor -import org.springframework.stereotype.Component - -@Component -class TopicSubscriptionInterceptor @Autowired constructor( - private val destinationAccessValidator: DestinationAccessValidator -) : ChannelInterceptor { - - override fun preSend(message: Message<*>, channel: MessageChannel): Message<*>? { - val headerAccessor = StompHeaderAccessor.wrap(message) - if (StompCommand.SUBSCRIBE == headerAccessor.command) { - val username = headerAccessor.user!!.name - val destination = headerAccessor.destination!! - if (!destinationAccessValidator.hasAccess(username, destination)) { - sendForbiddenSubscriptionError(username, destination) - return null - } - } - return message - } - - private fun sendForbiddenSubscriptionError(username: String, destination: String?) { - logger.error(String.format("Player '%s' is not allowed to access %s", username, destination)) - } - - companion object { - private val logger = LoggerFactory.getLogger(TopicSubscriptionInterceptor::class.java) - } -} diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/config/WebSecurityConfig.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/config/WebSecurityConfig.kt deleted file mode 100644 index 06b2bc90..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/config/WebSecurityConfig.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.luxons.sevenwonders.config - -import org.springframework.context.annotation.Configuration -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter - -@Configuration -class WebSecurityConfig : WebSecurityConfigurerAdapter() { - - // this disables default authentication settings - override fun configure(httpSecurity: HttpSecurity) = Unit -} diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/config/WebSocketConfig.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/config/WebSocketConfig.kt deleted file mode 100644 index 743e3d1a..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/config/WebSocketConfig.kt +++ /dev/null @@ -1,44 +0,0 @@ -package org.luxons.sevenwonders.config - -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.messaging.simp.config.ChannelRegistration -import org.springframework.messaging.simp.config.MessageBrokerRegistry -import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker -import org.springframework.web.socket.config.annotation.StompEndpointRegistry -import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer -import org.springframework.web.socket.server.support.DefaultHandshakeHandler - -const val SEVEN_WONDERS_WS_ENDPOINT = "/seven-wonders-websocket" - -@Configuration -@EnableWebSocketMessageBroker -class WebSocketConfig @Autowired constructor(private val topicSubscriptionInterceptor: TopicSubscriptionInterceptor) : - WebSocketMessageBrokerConfigurer { - - override fun configureMessageBroker(config: MessageBrokerRegistry) { - // prefixes for all subscriptions - config.enableSimpleBroker("/queue", "/topic") - config.setUserDestinationPrefix("/user") - - // /app for normal calls, /topic for subscription events - config.setApplicationDestinationPrefixes("/app", "/topic") - } - - override fun registerStompEndpoints(registry: StompEndpointRegistry) { - registry.addEndpoint(SEVEN_WONDERS_WS_ENDPOINT) - .setHandshakeHandler(handshakeHandler()) - .setAllowedOrigins("http://localhost:3000") // to allow frontend server proxy requests in dev mode - .withSockJS() - } - - @Bean - fun handshakeHandler(): DefaultHandshakeHandler { - return AnonymousUsersHandshakeHandler() - } - - override fun configureClientInboundChannel(registration: ChannelRegistration) { - registration.interceptors(topicSubscriptionInterceptor) - } -} diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/controllers/GameBrowserController.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/controllers/GameBrowserController.kt deleted file mode 100644 index f856365f..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/controllers/GameBrowserController.kt +++ /dev/null @@ -1,112 +0,0 @@ -package org.luxons.sevenwonders.controllers - -import org.hildan.livedoc.core.annotations.Api -import org.luxons.sevenwonders.actions.CreateGameAction -import org.luxons.sevenwonders.actions.JoinGameAction -import org.luxons.sevenwonders.api.LobbyDTO -import org.luxons.sevenwonders.api.toDTO -import org.luxons.sevenwonders.errors.ApiMisuseException -import org.luxons.sevenwonders.lobby.Lobby -import org.luxons.sevenwonders.repositories.LobbyRepository -import org.luxons.sevenwonders.repositories.PlayerRepository -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.messaging.handler.annotation.MessageMapping -import org.springframework.messaging.simp.SimpMessagingTemplate -import org.springframework.messaging.simp.annotation.SendToUser -import org.springframework.messaging.simp.annotation.SubscribeMapping -import org.springframework.stereotype.Controller -import org.springframework.validation.annotation.Validated -import java.security.Principal - -/** - * This is the place where the player looks for a game. - */ -@Api(name = "GameBrowser") -@Controller -class GameBrowserController @Autowired constructor( - private val lobbyController: LobbyController, - private val lobbyRepository: LobbyRepository, - 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. - * - * @param principal the connected user's information - * - * @return the current list of [Lobby]s - */ - @SubscribeMapping("/games") // prefix /topic not shown - fun listGames(principal: Principal): Collection<LobbyDTO> { - logger.info("Player '{}' subscribed to /topic/games", principal.name) - return lobbyRepository.list().map { it.toDTO(principal.name) } - } - - /** - * Creates a new [Lobby]. - * - * @param action the action to create the game - * @param principal the connected user's information - * - * @return the newly created [Lobby] - */ - @MessageMapping("/lobby/create") - @SendToUser("/queue/lobby/joined") - 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) - - logger.info( - "Game '{}' ({}) created by {} ({})", lobby.name, lobby.id, gameOwner.displayName, gameOwner.username - ) - - // notify everyone that a new game exists - val lobbyDto = lobby.toDTO(principal.name) - template.convertAndSend("/topic/games", listOf(lobbyDto)) - return lobbyDto - } - - /** - * Joins an existing [Lobby]. - * - * @param action the action to join the game - * @param principal the connected user's information - * - * @return the [Lobby] that has just been joined - */ - @MessageMapping("/lobby/join") - @SendToUser("/queue/lobby/joined") - fun joinGame(@Validated action: JoinGameAction, principal: Principal): LobbyDTO { - checkThatUserIsNotInAGame(principal, "cannot join another game") - - val lobby = lobbyRepository.find(action.gameId) - val newPlayer = playerRepository.find(principal.name) - lobby.addPlayer(newPlayer) - - logger.info( - "Player '{}' ({}) joined game {}", newPlayer.displayName, newPlayer.username, lobby.name - ) - val lobbyDTO = lobby.toDTO(principal.name) - lobbyController.sendLobbyUpdateToPlayers(lobbyDTO) - return lobbyDTO - } - - private fun checkThatUserIsNotInAGame(principal: Principal, impossibleActionDescription: String) { - val player = playerRepository.find(principal.name) - if (player.isInLobby || player.isInGame) { - throw UserAlreadyInGameException(player.lobby.name, impossibleActionDescription) - } - } - - internal class UserAlreadyInGameException(gameName: String, impossibleActionDescription: String) : - ApiMisuseException("Client already in game '$gameName', $impossibleActionDescription") - - companion object { - private val logger = LoggerFactory.getLogger(GameBrowserController::class.java) - } -} diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/controllers/GameController.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/controllers/GameController.kt deleted file mode 100644 index b37c9c7c..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/controllers/GameController.kt +++ /dev/null @@ -1,102 +0,0 @@ -package org.luxons.sevenwonders.controllers - -import org.hildan.livedoc.core.annotations.Api -import org.luxons.sevenwonders.actions.PrepareMoveAction -import org.luxons.sevenwonders.api.PlayerDTO -import org.luxons.sevenwonders.api.toDTO -import org.luxons.sevenwonders.game.Game -import org.luxons.sevenwonders.game.api.Table -import org.luxons.sevenwonders.game.cards.CardBack -import org.luxons.sevenwonders.lobby.Player -import org.luxons.sevenwonders.repositories.PlayerRepository -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.messaging.handler.annotation.MessageMapping -import org.springframework.messaging.simp.SimpMessagingTemplate -import org.springframework.stereotype.Controller -import java.security.Principal - -/** - * This API is for in-game events management. - */ -@Api(name = "Game") -@Controller -class GameController @Autowired constructor( - private val template: SimpMessagingTemplate, - private val playerRepository: PlayerRepository -) { - private val Principal.player - get() = playerRepository.find(name) - - /** - * Notifies the game that the player is ready to receive his hand. - * - * @param principal - * the connected user's information - */ - @MessageMapping("/game/sayReady") - fun ready(principal: Principal) { - val player = principal.player - player.isReady = true - val game = player.game - logger.info("Game {}: player {} is ready for the next turn", game.id, player) - - val lobby = player.lobby - val players = lobby.getPlayers() - - sendPlayerReady(game.id, player) - - val allReady = players.all { it.isReady } - if (allReady) { - logger.info("Game {}: all players ready, sending turn info", game.id) - players.forEach { it.isReady = false } - sendTurnInfo(players, game) - } - } - - private fun sendTurnInfo(players: List<Player>, game: Game) { - for (turnInfo in game.getCurrentTurnInfo()) { - val player = players[turnInfo.playerIndex] - template.convertAndSendToUser(player.username, "/queue/game/turn", turnInfo) - } - } - - private fun sendPlayerReady(gameId: Long, player: Player) = - template.convertAndSend("/topic/game/$gameId/playerReady", "\"${player.username}\"") - - /** - * Prepares the player's next move. When all players have prepared their moves, all moves are executed. - * - * @param action - * the action to prepare the move - * @param principal - * the connected user's information - */ - @MessageMapping("/game/prepareMove") - fun prepareMove(action: PrepareMoveAction, principal: Principal) { - val player = principal.player - val game = player.game - val preparedCardBack = game.prepareMove(player.index, action.move) - val preparedCard = PreparedCard(player.toDTO(principal.name), preparedCardBack) - logger.info("Game {}: player {} prepared move {}", game.id, principal.name, action.move) - sendPreparedCard(game.id, preparedCard) - - if (game.allPlayersPreparedTheirMove()) { - logger.info("Game {}: all players have prepared their move, executing turn...", game.id) - val table = game.playTurn() - sendPlayedMoves(game.id, table) - } - } - - private fun sendPlayedMoves(gameId: Long, table: Table) = - template.convertAndSend("/topic/game/$gameId/tableUpdates", table) - - private fun sendPreparedCard(gameId: Long, preparedCard: PreparedCard) = - template.convertAndSend("/topic/game/$gameId/prepared", preparedCard) - - companion object { - private val logger = LoggerFactory.getLogger(GameController::class.java) - } -} - -class PreparedCard(val player: PlayerDTO, val cardBack: CardBack) diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/controllers/HomeController.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/controllers/HomeController.kt deleted file mode 100644 index bd672000..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/controllers/HomeController.kt +++ /dev/null @@ -1,46 +0,0 @@ -package org.luxons.sevenwonders.controllers - -import org.hildan.livedoc.core.annotations.Api -import org.luxons.sevenwonders.actions.ChooseNameAction -import org.luxons.sevenwonders.api.PlayerDTO -import org.luxons.sevenwonders.api.toDTO -import org.luxons.sevenwonders.repositories.PlayerRepository -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.messaging.handler.annotation.MessageMapping -import org.springframework.messaging.simp.annotation.SendToUser -import org.springframework.stereotype.Controller -import org.springframework.validation.annotation.Validated -import java.security.Principal - -/** - * Handles actions in the homepage of the game. - */ -@Api(name = "Home") -@Controller -class HomeController @Autowired constructor( - private val playerRepository: PlayerRepository -) { - - /** - * Creates/updates the player's name (for the user's session). - * - * @param action the action to choose the name of the player - * @param principal the connected user's information - * - * @return the created [PlayerDTO] object - */ - @MessageMapping("/chooseName") - @SendToUser("/queue/nameChoice") - fun chooseName(@Validated action: ChooseNameAction, principal: Principal): PlayerDTO { - val username = principal.name - val player = playerRepository.createOrUpdate(username, action.playerName) - - logger.info("Player '{}' chose the name '{}'", username, player.displayName) - return player.toDTO(username) - } - - companion object { - private val logger = LoggerFactory.getLogger(HomeController::class.java) - } -} diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/controllers/LobbyController.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/controllers/LobbyController.kt deleted file mode 100644 index 4e4120a9..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/controllers/LobbyController.kt +++ /dev/null @@ -1,107 +0,0 @@ -package org.luxons.sevenwonders.controllers - -import org.hildan.livedoc.core.annotations.Api -import org.luxons.sevenwonders.actions.ReorderPlayersAction -import org.luxons.sevenwonders.actions.UpdateSettingsAction -import org.luxons.sevenwonders.api.LobbyDTO -import org.luxons.sevenwonders.api.toDTO -import org.luxons.sevenwonders.lobby.Player -import org.luxons.sevenwonders.repositories.LobbyRepository -import org.luxons.sevenwonders.repositories.PlayerRepository -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.messaging.handler.annotation.MessageMapping -import org.springframework.messaging.simp.SimpMessagingTemplate -import org.springframework.stereotype.Controller -import org.springframework.validation.annotation.Validated -import java.security.Principal - -/** - * Handles actions in the game's lobby. The lobby is the place where players gather before a game. - */ -@Api(name = "Lobby") -@Controller -class LobbyController @Autowired constructor( - private val lobbyRepository: LobbyRepository, - private val playerRepository: PlayerRepository, - private val template: SimpMessagingTemplate -) { - private val Principal.player: Player - get() = playerRepository.find(name) - - /** - * Leaves the current lobby. - * - * @param principal - * the connected user's information - */ - @MessageMapping("/lobby/leave") - fun leave(principal: Principal) { - val lobby = principal.player.lobby - val player = lobby.removePlayer(principal.name) - if (lobby.getPlayers().isEmpty()) { - lobbyRepository.remove(lobby.id) - } - - logger.info("Player {} left game '{}'", player, lobby.name) - sendLobbyUpdateToPlayers(lobby.toDTO(principal.name)) - } - - /** - * Reorders the players in the current lobby. This can only be done by the lobby's owner. - * - * @param action - * the action to reorder the players - * @param principal - * the connected user's information - */ - @MessageMapping("/lobby/reorderPlayers") - fun reorderPlayers(@Validated action: ReorderPlayersAction, principal: Principal) { - val lobby = principal.player.ownedLobby - lobby.reorderPlayers(action.orderedPlayers) - - logger.info("Players in game '{}' reordered to {}", lobby.name, action.orderedPlayers) - sendLobbyUpdateToPlayers(lobby.toDTO(principal.name)) - } - - /** - * Updates the game settings. This can only be done by the lobby's owner. - * - * @param action - * the action to update the settings - * @param principal - * the connected user's information - */ - @MessageMapping("/lobby/updateSettings") - fun updateSettings(@Validated action: UpdateSettingsAction, principal: Principal) { - val lobby = principal.player.ownedLobby - lobby.settings = action.settings - - logger.info("Updated settings of game '{}'", lobby.name) - sendLobbyUpdateToPlayers(lobby.toDTO(principal.name)) - } - - internal fun sendLobbyUpdateToPlayers(lobby: LobbyDTO) { - template.convertAndSend("/topic/lobby/" + lobby.id + "/updated", lobby) - template.convertAndSend("/topic/games", listOf(lobby)) - } - - /** - * Starts the game. - * - * @param principal - * the connected user's information - */ - @MessageMapping("/lobby/startGame") - fun startGame(principal: Principal) { - val lobby = principal.player.ownedLobby - val game = lobby.startGame() - - logger.info("Game {} successfully started", game.id) - template.convertAndSend("/topic/lobby/" + lobby.id + "/started", "") - } - - companion object { - private val logger = LoggerFactory.getLogger(LobbyController::class.java) - } -} diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/doc/Documentation.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/doc/Documentation.kt deleted file mode 100644 index 3b04356a..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/doc/Documentation.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.luxons.sevenwonders.doc - -object Documentation { - - const val GROUP_ACTIONS = "Actions" -} diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/errors/ErrorDTO.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/errors/ErrorDTO.kt deleted file mode 100644 index c3eae0b5..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/errors/ErrorDTO.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.luxons.sevenwonders.errors - -import org.springframework.validation.FieldError -import org.springframework.validation.ObjectError - -enum class ErrorType { - VALIDATION, CLIENT, SERVER -} - -data class ErrorDTO( - val code: String, - val message: String, - val type: ErrorType, - val details: List<ValidationErrorDTO> = emptyList() -) - -data class ValidationErrorDTO( - val path: String, - val message: String, - val rejectedValue: Any? = null -) - -fun ObjectError.toDTO() = (this as? FieldError)?.fieldError() ?: objectError() - -fun FieldError.fieldError(): ValidationErrorDTO = - ValidationErrorDTO("$objectName.$field", "Invalid value for field '$field': $defaultMessage", rejectedValue) - -fun ObjectError.objectError(): ValidationErrorDTO = - ValidationErrorDTO(objectName, "Invalid value for object '$objectName': $defaultMessage") diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/errors/ExceptionHandler.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/errors/ExceptionHandler.kt deleted file mode 100644 index 76d01f5f..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/errors/ExceptionHandler.kt +++ /dev/null @@ -1,44 +0,0 @@ -package org.luxons.sevenwonders.errors - -import org.slf4j.LoggerFactory -import org.springframework.messaging.converter.MessageConversionException -import org.springframework.messaging.handler.annotation.MessageExceptionHandler -import org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException -import org.springframework.messaging.simp.annotation.SendToUser -import org.springframework.web.bind.annotation.ControllerAdvice - -open class ApiMisuseException(message: String) : RuntimeException(message) - -@ControllerAdvice -@SendToUser("/queue/errors") -class ExceptionHandler { - - @MessageExceptionHandler - fun handleValidationError(exception: MethodArgumentNotValidException): ErrorDTO { - logger.error("Invalid input", exception) - val validationErrors = exception.bindingResult?.allErrors?.map { it.toDTO() } ?: emptyList() - return ErrorDTO("INVALID_DATA", "Invalid input data", ErrorType.VALIDATION, validationErrors) - } - - @MessageExceptionHandler - fun handleConversionError(exception: MessageConversionException): ErrorDTO { - logger.error("Error interpreting the message", exception) - return ErrorDTO("INVALID_MESSAGE_FORMAT", "Invalid input format", ErrorType.VALIDATION) - } - - @MessageExceptionHandler - fun handleApiError(exception: ApiMisuseException): ErrorDTO { - logger.error("Invalid API input", exception) - return ErrorDTO(exception.javaClass.simpleName, exception.message!!, ErrorType.CLIENT) - } - - @MessageExceptionHandler - fun handleUnexpectedInternalError(exception: Throwable): ErrorDTO { - logger.error("Uncaught exception thrown during message handling", exception) - return ErrorDTO(exception.javaClass.simpleName, exception.localizedMessage ?: "", ErrorType.SERVER) - } - - companion object { - private val logger = LoggerFactory.getLogger(ExceptionHandler::class.java) - } -} diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/lobby/Lobby.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/lobby/Lobby.kt deleted file mode 100644 index 08249193..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/lobby/Lobby.kt +++ /dev/null @@ -1,111 +0,0 @@ -package org.luxons.sevenwonders.lobby - -import org.luxons.sevenwonders.game.Game -import org.luxons.sevenwonders.game.api.CustomizableSettings -import org.luxons.sevenwonders.game.data.GameDefinition - -enum class State { - LOBBY, PLAYING -} - -class Lobby( - val id: Long, - val name: String, - var owner: Player, - private val gameDefinition: GameDefinition -) { - private val players: MutableList<Player> = ArrayList(gameDefinition.maxPlayers) - - var settings: CustomizableSettings = CustomizableSettings() - - var state = State.LOBBY - private set - - init { - addPlayer(owner) - } - - fun getPlayers(): List<Player> = players - - @Synchronized - fun addPlayer(player: Player) { - if (hasStarted()) { - throw GameAlreadyStartedException(name) - } - if (maxPlayersReached()) { - throw PlayerOverflowException(gameDefinition.maxPlayers) - } - if (playerNameAlreadyUsed(player.displayName)) { - throw PlayerNameAlreadyUsedException(player.displayName, name) - } - player.join(this) - players.add(player) - } - - 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 } - - @Synchronized - fun startGame(): Game { - if (!hasEnoughPlayers()) { - throw PlayerUnderflowException(gameDefinition.minPlayers) - } - state = State.PLAYING - val game = gameDefinition.initGame(id, settings, players.size) - players.forEachIndexed { index, player -> player.join(game, index) } - return game - } - - private fun hasEnoughPlayers(): Boolean = players.size >= gameDefinition.minPlayers - - @Synchronized - fun reorderPlayers(orderedUsernames: List<String>) { - val usernames = players.map { it.username } - if (orderedUsernames.toSet() != usernames.toSet()) { - throw PlayerListMismatchException(orderedUsernames) - } - players.sortBy { orderedUsernames.indexOf(it.username) } - } - - private fun find(username: String): Player = - players.firstOrNull { it.username == username } ?: throw UnknownPlayerException(username) - - @Synchronized - fun isOwner(username: String?): Boolean = owner.username == username - - @Synchronized - fun containsUser(username: String): Boolean = players.any { it.username == username } - - @Synchronized - fun removePlayer(username: String): Player { - val player = find(username) - players.remove(player) - player.leave() - - if (player == owner && !players.isEmpty()) { - owner = players[0] - } - return player - } - - internal class GameAlreadyStartedException(name: String) : - IllegalStateException("Game '$name' has already started") - - internal class PlayerOverflowException(max: Int) : - IllegalStateException("Maximum $max players allowed") - - internal class PlayerUnderflowException(min: Int) : - IllegalStateException("Minimum $min players required to start a game") - - internal class PlayerNameAlreadyUsedException(displayName: String, gameName: String) : - IllegalArgumentException("Name '$displayName' is already used by a player in game '$gameName'") - - internal class UnknownPlayerException(username: String) : - IllegalArgumentException("Unknown player '$username'") - - internal class PlayerListMismatchException(usernames: List<String>) : - IllegalArgumentException("Newly ordered usernames $usernames don't match the current player list") -} diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/lobby/Player.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/lobby/Player.kt deleted file mode 100644 index d6e9b344..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/lobby/Player.kt +++ /dev/null @@ -1,61 +0,0 @@ -package org.luxons.sevenwonders.lobby - -import org.luxons.sevenwonders.errors.ApiMisuseException -import org.luxons.sevenwonders.game.Game - -class Player( - val username: String, - var displayName: String -) { - var index: Int = -1 - - var isReady: Boolean = false - - val isGameOwner: Boolean - get() = _lobby?.isOwner(username) ?: false - - val isInLobby: Boolean - get() = _lobby != null - - val isInGame: Boolean - get() = _game != null - - private var _lobby: Lobby? = null - - val lobby: Lobby - get() = _lobby ?: throw PlayerNotInLobbyException(username) - - val ownedLobby: Lobby - get() = if (isGameOwner) lobby else throw PlayerIsNotOwnerException(username) - - private var _game: Game? = null - - val game: Game - get() = _game ?: throw PlayerNotInGameException(username) - - fun join(lobby: Lobby) { - _lobby = lobby - } - - fun join(game: Game, index: Int) { - _game = game - this.index = index - } - - fun leave() { - _lobby = null - _game = null - index = -1 - } - - override fun toString(): String = "'$displayName' ($username)" -} - -internal class PlayerNotInLobbyException(username: String) : - ApiMisuseException("User $username is not in a lobby, create or join a game first") - -internal class PlayerIsNotOwnerException(username: String) : - ApiMisuseException("User $username does not own the lobby he's in") - -internal class PlayerNotInGameException(username: String) : - ApiMisuseException("User $username is not in a game, start a game first") diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/repositories/LobbyRepository.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/repositories/LobbyRepository.kt deleted file mode 100644 index 768aa659..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/repositories/LobbyRepository.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.luxons.sevenwonders.repositories - -import org.luxons.sevenwonders.game.data.GameDefinition -import org.luxons.sevenwonders.lobby.Lobby -import org.luxons.sevenwonders.lobby.Player -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.stereotype.Repository -import java.util.HashMap - -@Repository -class LobbyRepository @Autowired constructor() { - - private val lobbies = HashMap<Long, Lobby>() - - private var lastGameId: Long = 0 - - fun list(): Collection<Lobby> = lobbies.values - - fun create(gameName: String, owner: Player): Lobby { - val id = lastGameId++ - val lobby = Lobby(id, gameName, owner, GameDefinition.load()) - lobbies[id] = lobby - return lobby - } - - fun find(lobbyId: Long): Lobby = lobbies[lobbyId] ?: throw LobbyNotFoundException(lobbyId) - - fun remove(lobbyId: Long): Lobby = lobbies.remove(lobbyId) ?: throw LobbyNotFoundException(lobbyId) -} - -internal class LobbyNotFoundException(id: Long) : RuntimeException("Lobby not found for id '$id'") diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/repositories/PlayerRepository.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/repositories/PlayerRepository.kt deleted file mode 100644 index 4d552eaa..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/repositories/PlayerRepository.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.luxons.sevenwonders.repositories - -import org.luxons.sevenwonders.errors.ApiMisuseException -import org.luxons.sevenwonders.lobby.Player -import org.springframework.stereotype.Repository -import java.util.HashMap - -@Repository -class PlayerRepository { - - private val players = HashMap<String, Player>() - - operator fun contains(username: String): Boolean = players.containsKey(username) - - fun createOrUpdate(username: String, displayName: String): Player { - return if (players.containsKey(username)) { - update(username, displayName) - } else { - create(username, displayName) - } - } - - private fun create(username: String, displayName: String): Player { - val player = Player(username, displayName) - players[username] = player - return player - } - - private fun update(username: String, displayName: String): Player { - val player = find(username) - player.displayName = displayName - return player - } - - fun find(username: String): Player = players[username] ?: throw PlayerNotFoundException(username) - - fun remove(username: String): Player = players.remove(username) ?: throw PlayerNotFoundException(username) -} - -internal class PlayerNotFoundException(username: String) : - ApiMisuseException("Player '$username' doesn't exist") diff --git a/backend/src/main/kotlin/org/luxons/sevenwonders/validation/DestinationAccessValidator.kt b/backend/src/main/kotlin/org/luxons/sevenwonders/validation/DestinationAccessValidator.kt deleted file mode 100644 index 5f704357..00000000 --- a/backend/src/main/kotlin/org/luxons/sevenwonders/validation/DestinationAccessValidator.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.luxons.sevenwonders.validation - -import org.luxons.sevenwonders.repositories.LobbyRepository -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.stereotype.Component -import java.util.regex.Pattern - -@Component -class DestinationAccessValidator @Autowired constructor(private val lobbyRepository: LobbyRepository) { - - fun hasAccess(username: String?, destination: String): Boolean { - return when { - username == null -> false // unnamed user cannot belong to anything - hasForbiddenGameReference(username, destination) -> false - hasForbiddenLobbyReference(username, destination) -> false - else -> true - } - } - - private fun hasForbiddenGameReference(username: String, destination: String): Boolean { - val gameMatcher = gameDestination.matcher(destination) - if (!gameMatcher.matches()) { - return false // no game reference is always OK - } - val gameId = gameMatcher.group("id").toLong() - return !isUserInLobby(username, gameId) - } - - private fun hasForbiddenLobbyReference(username: String, destination: String): Boolean { - val lobbyMatcher = lobbyDestination.matcher(destination) - if (!lobbyMatcher.matches()) { - return false // no lobby reference is always OK - } - val lobbyId = lobbyMatcher.group("id").toLong() - return !isUserInLobby(username, lobbyId) - } - - private fun isUserInLobby(username: String, lobbyId: Long): Boolean = - lobbyRepository.find(lobbyId).containsUser(username) - - companion object { - - private val lobbyDestination = Pattern.compile(".*?/lobby/(?<id>\\d+?)(/.*)?") - - private val gameDestination = Pattern.compile(".*?/game/(?<id>\\d+?)(/.*)?") - } -} |