summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/luxons/sevenwonders/actions/PrepareCardAction.java16
-rw-r--r--src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java123
-rw-r--r--src/main/java/org/luxons/sevenwonders/errors/ApiMisuseException.java8
-rw-r--r--src/main/java/org/luxons/sevenwonders/errors/ErrorFactory.java52
-rw-r--r--src/main/java/org/luxons/sevenwonders/errors/ExceptionHandler.java65
-rw-r--r--src/main/java/org/luxons/sevenwonders/errors/UserInputException.java9
-rw-r--r--src/main/java/org/luxons/sevenwonders/game/Game.java4
-rw-r--r--src/main/java/org/luxons/sevenwonders/repositories/GameRepository.java41
-rw-r--r--src/main/java/org/luxons/sevenwonders/repositories/LobbyRepository.java58
9 files changed, 242 insertions, 134 deletions
diff --git a/src/main/java/org/luxons/sevenwonders/actions/PrepareCardAction.java b/src/main/java/org/luxons/sevenwonders/actions/PrepareCardAction.java
new file mode 100644
index 00000000..8fd809a0
--- /dev/null
+++ b/src/main/java/org/luxons/sevenwonders/actions/PrepareCardAction.java
@@ -0,0 +1,16 @@
+package org.luxons.sevenwonders.actions;
+
+import org.luxons.sevenwonders.game.Move;
+
+public class PrepareCardAction {
+
+ private Move move;
+
+ public Move getMove() {
+ return move;
+ }
+
+ public void setMove(Move move) {
+ this.move = move;
+ }
+}
diff --git a/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java b/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java
index e15935d6..22e994dd 100644
--- a/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java
+++ b/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java
@@ -1,25 +1,21 @@
package org.luxons.sevenwonders.controllers;
import java.security.Principal;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import org.luxons.sevenwonders.actions.JoinOrCreateGameAction;
import org.luxons.sevenwonders.actions.StartGameAction;
-import org.luxons.sevenwonders.errors.ErrorFactory;
-import org.luxons.sevenwonders.errors.UIError;
-import org.luxons.sevenwonders.errors.UniqueIdAlreadyUsedException;
+import org.luxons.sevenwonders.errors.ApiMisuseException;
import org.luxons.sevenwonders.game.Game;
import org.luxons.sevenwonders.game.Lobby;
import org.luxons.sevenwonders.game.Player;
import org.luxons.sevenwonders.game.api.PlayerTurnInfo;
-import org.luxons.sevenwonders.game.data.GameDefinitionLoader;
+import org.luxons.sevenwonders.repositories.GameRepository;
+import org.luxons.sevenwonders.repositories.LobbyRepository;
import org.luxons.sevenwonders.session.SessionAttributes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
@@ -34,50 +30,32 @@ public class LobbyController {
private static final Logger logger = LoggerFactory.getLogger(LobbyController.class);
- private final GameDefinitionLoader gameDefinitionLoader;
-
private final SimpMessagingTemplate template;
- private final ErrorFactory errorFactory;
-
- private long lastGameId = 0;
-
- private Map<String, Lobby> lobbies = new HashMap<>();
+ private final LobbyRepository lobbyRepository;
- private Map<String, Game> games = new HashMap<>();
+ private final GameRepository gameRepository;
@Autowired
- public LobbyController(GameDefinitionLoader gameDefinitionLoader, SimpMessagingTemplate template,
- ErrorFactory errorFactory) {
- this.gameDefinitionLoader = gameDefinitionLoader;
+ public LobbyController(SimpMessagingTemplate template, LobbyRepository lobbyRepository,
+ GameRepository gameRepository) {
this.template = template;
- this.errorFactory = errorFactory;
- }
-
- @MessageExceptionHandler
- @SendToUser("/queue/errors")
- public UIError handleException(Throwable exception) {
- logger.error("An error occured during message handling", exception);
- return errorFactory.createError(exception);
+ this.lobbyRepository = lobbyRepository;
+ this.gameRepository = gameRepository;
}
@MessageMapping("/create-game")
@SendTo("/topic/games")
public Lobby createGame(SimpMessageHeaderAccessor headerAccessor, @Validated JoinOrCreateGameAction action,
Principal principal) {
- Lobby lobby = (Lobby)headerAccessor.getSessionAttributes().get(SessionAttributes.ATTR_LOBBY);
- if (lobby != null) {
- logger.warn("Client already in game '{}', cannot create a new game", lobby.getName());
- return lobby;
- }
-
- Player player = createPlayer(action.getPlayerName(), principal);
- lobby = createGame(action.getGameName(), player);
+ checkThatUserIsNotInAGame(headerAccessor, "cannot create another game");
+ Player gameOwner = new Player(action.getPlayerName(), principal.getName());
+ Lobby lobby = lobbyRepository.create(action.getGameName(), gameOwner);
headerAccessor.getSessionAttributes().put(SessionAttributes.ATTR_LOBBY, lobby);
- logger.info("Game '{}' (id={}) created by {} ({})", lobby.getName(), lobby.getId(), player.getDisplayName(),
- player.getUserName());
+ logger.info("Game '{}' (id={}) created by {} ({})", lobby.getName(), lobby.getId(), gameOwner.getDisplayName(),
+ gameOwner.getUserName());
return lobby;
}
@@ -85,44 +63,32 @@ public class LobbyController {
@SendToUser("/queue/join-game")
public Lobby joinGame(SimpMessageHeaderAccessor headerAccessor, @Validated JoinOrCreateGameAction action,
Principal principal) {
- Lobby lobby = (Lobby)headerAccessor.getSessionAttributes().get(SessionAttributes.ATTR_LOBBY);
- if (lobby != null) {
- logger.warn("Client already in game '{}', cannot join a different game", lobby.getName());
- return lobby;
- }
-
- lobby = lobbies.get(action.getGameName());
- if (lobby == null) {
- throw new GameNotFoundException(action.getGameName());
- }
+ checkThatUserIsNotInAGame(headerAccessor, "cannot join another game");
- Player newPlayer = createPlayer(action.getPlayerName(), principal);
+ Lobby lobby = lobbyRepository.find(action.getGameName());
+ Player newPlayer = new Player(action.getPlayerName(), principal.getName());
lobby.addPlayer(newPlayer);
-
headerAccessor.getSessionAttributes().put(SessionAttributes.ATTR_LOBBY, lobby);
- logger.warn("Player {} joined game {}", action.getPlayerName(), action.getGameName());
-
+ logger.info("Player {} joined game {}", action.getPlayerName(), action.getGameName());
return lobby;
}
- @MessageMapping("/start-game")
- public void startGame(SimpMessageHeaderAccessor headerAccessor, @Validated StartGameAction action,
- Principal principal) {
+ private void checkThatUserIsNotInAGame(SimpMessageHeaderAccessor headerAccessor,
+ String impossibleActionDescription) {
Lobby lobby = (Lobby)headerAccessor.getSessionAttributes().get(SessionAttributes.ATTR_LOBBY);
- if (lobby == null) {
- logger.error("User {} is not in a lobby", principal.getName());
- template.convertAndSendToUser(principal.getName(), "/queue/errors", "No game to start");
- return;
- }
-
- if (!lobby.isOwner(principal.getName())) {
- logger.error("User {} is not the owner of the game '{}'", principal.getName(), lobby.getName());
- template.convertAndSendToUser(principal.getName(), "/queue/errors", "Only the owner can start the game");
- return;
+ if (lobby != null) {
+ throw new UserAlreadyInGameException(lobby.getName(), impossibleActionDescription);
}
+ }
+ @MessageMapping("/start-game")
+ public void startGame(SimpMessageHeaderAccessor headerAccessor, @Validated StartGameAction action,
+ Principal principal) {
+ Lobby lobby = getOwnedLobby(headerAccessor, principal);
Game game = lobby.startGame(action.getSettings());
+ gameRepository.add(game);
+
logger.info("Game {} successfully started", game.getId());
List<PlayerTurnInfo> playerTurnInfos = game.startTurn();
@@ -133,31 +99,26 @@ public class LobbyController {
}
}
- private Player createPlayer(String name, Principal principal) {
- return new Player(name, principal.getName());
- }
-
- private Lobby createGame(String name, Player owner) {
- if (lobbies.containsKey(name)) {
- throw new GameNameAlreadyUsedException(name);
+ private Lobby getOwnedLobby(SimpMessageHeaderAccessor headerAccessor, Principal principal) {
+ Lobby lobby = (Lobby)headerAccessor.getSessionAttributes().get(SessionAttributes.ATTR_LOBBY);
+ if (lobby == null) {
+ throw new UserOwnsNoLobbyException("User " + principal.getName() + " is not in a lobby");
+ }
+ if (!lobby.isOwner(principal.getName())) {
+ throw new UserOwnsNoLobbyException("User " + principal.getName() + " does not own the lobby he's in");
}
- long id = lastGameId++;
- Lobby lobby = new Lobby(id, name, owner, gameDefinitionLoader.getGameDefinition());
- lobbies.put(name, lobby);
return lobby;
}
- private class GameNotFoundException extends RuntimeException {
-
- public GameNotFoundException(String name) {
- super(name);
+ private class UserOwnsNoLobbyException extends ApiMisuseException {
+ UserOwnsNoLobbyException(String message) {
+ super(message);
}
}
- private class GameNameAlreadyUsedException extends UniqueIdAlreadyUsedException {
-
- public GameNameAlreadyUsedException(String name) {
- super(name);
+ private class UserAlreadyInGameException extends ApiMisuseException {
+ UserAlreadyInGameException(String gameName, String impossibleActionDescription) {
+ super("Client already in game '" + gameName + "', " + impossibleActionDescription);
}
}
}
diff --git a/src/main/java/org/luxons/sevenwonders/errors/ApiMisuseException.java b/src/main/java/org/luxons/sevenwonders/errors/ApiMisuseException.java
new file mode 100644
index 00000000..0d7d1a82
--- /dev/null
+++ b/src/main/java/org/luxons/sevenwonders/errors/ApiMisuseException.java
@@ -0,0 +1,8 @@
+package org.luxons.sevenwonders.errors;
+
+public class ApiMisuseException extends RuntimeException {
+
+ public ApiMisuseException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/luxons/sevenwonders/errors/ErrorFactory.java b/src/main/java/org/luxons/sevenwonders/errors/ErrorFactory.java
deleted file mode 100644
index 2d3dcd0b..00000000
--- a/src/main/java/org/luxons/sevenwonders/errors/ErrorFactory.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.luxons.sevenwonders.errors;
-
-import java.util.List;
-import java.util.Locale;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.MessageSource;
-import org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException;
-import org.springframework.stereotype.Component;
-import org.springframework.validation.ObjectError;
-
-@Component
-public class ErrorFactory {
-
- private static final String ERROR_CODE_VALIDATION = "VALIDATION_ERROR";
-
- private static final String ERROR_MSG_VALIDATION = "Input invalid";
-
- private final MessageSource messageSource;
-
- @Autowired
- public ErrorFactory(MessageSource messageSource) {
- this.messageSource = messageSource;
- }
-
- public UIError createError(Throwable exception) {
- if (exception instanceof UserInputException) {
- return createUserError((UserInputException)exception);
- } else if (exception instanceof MethodArgumentNotValidException) {
- return createValidationError((MethodArgumentNotValidException)exception);
- } else {
- return createInternalError(exception);
- }
- }
-
- private UIError createUserError(UserInputException exception) {
- String messageKey = exception.getMessageResourceKey();
- String message = messageSource.getMessage(messageKey, null, messageKey, Locale.US);
- return new UIError(messageKey, message, ErrorType.USER);
- }
-
- private UIError createInternalError(Throwable exception) {
- return new UIError(exception.getClass().getSimpleName(), exception.getMessage(), ErrorType.INTERNAL);
- }
-
- private UIError createValidationError(MethodArgumentNotValidException exception) {
- List<ObjectError> errors = exception.getBindingResult().getAllErrors();
- UIError uiError = new UIError(ERROR_CODE_VALIDATION, ERROR_MSG_VALIDATION, ErrorType.VALIDATION);
- uiError.setValidationErrors(errors);
- return uiError;
- }
-}
diff --git a/src/main/java/org/luxons/sevenwonders/errors/ExceptionHandler.java b/src/main/java/org/luxons/sevenwonders/errors/ExceptionHandler.java
new file mode 100644
index 00000000..77bda927
--- /dev/null
+++ b/src/main/java/org/luxons/sevenwonders/errors/ExceptionHandler.java
@@ -0,0 +1,65 @@
+package org.luxons.sevenwonders.errors;
+
+import java.util.List;
+import java.util.Locale;
+
+import org.luxons.sevenwonders.controllers.GameController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.MessageSource;
+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.validation.ObjectError;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+
+@ControllerAdvice
+public class ExceptionHandler {
+
+ private static final Logger logger = LoggerFactory.getLogger(GameController.class);
+
+ private static final String ERROR_CODE_VALIDATION = "VALIDATION_ERROR";
+
+ private static final String ERROR_MSG_VALIDATION = "Input invalid";
+
+ private final MessageSource messageSource;
+
+ @Autowired
+ public ExceptionHandler(MessageSource messageSource) {
+ this.messageSource = messageSource;
+ }
+
+ @MessageExceptionHandler
+ @SendToUser("/queue/errors")
+ private UIError handleValidationError(MethodArgumentNotValidException exception) {
+ logger.error("Invalid input", exception);
+ List<ObjectError> errors = exception.getBindingResult().getAllErrors();
+ UIError uiError = new UIError(ERROR_CODE_VALIDATION, ERROR_MSG_VALIDATION, ErrorType.VALIDATION);
+ uiError.setValidationErrors(errors);
+ return uiError;
+ }
+
+ @MessageExceptionHandler
+ @SendToUser("/queue/errors")
+ private UIError handleGenericUserError(UserInputException exception) {
+ logger.error("Incorrect user input: " + exception.getMessage());
+ String messageKey = exception.getMessageResourceKey();
+ String message = messageSource.getMessage(messageKey, exception.getParams(), messageKey, Locale.US);
+ return new UIError(messageKey, message, ErrorType.USER);
+ }
+
+ @MessageExceptionHandler
+ @SendToUser("/queue/errors")
+ private UIError handleApiError(ApiMisuseException exception) {
+ logger.error("Invalid API input", exception);
+ return new UIError(exception.getClass().getSimpleName(), exception.getMessage(), ErrorType.INTERNAL);
+ }
+
+ @MessageExceptionHandler
+ @SendToUser("/queue/errors")
+ private UIError handleUnexpectedInternalError(Throwable exception) {
+ logger.error("Uncaught exception thrown during message handling", exception);
+ return new UIError(exception.getClass().getSimpleName(), exception.getMessage(), ErrorType.INTERNAL);
+ }
+}
diff --git a/src/main/java/org/luxons/sevenwonders/errors/UserInputException.java b/src/main/java/org/luxons/sevenwonders/errors/UserInputException.java
index 11f7f8f2..a86448ae 100644
--- a/src/main/java/org/luxons/sevenwonders/errors/UserInputException.java
+++ b/src/main/java/org/luxons/sevenwonders/errors/UserInputException.java
@@ -4,11 +4,18 @@ public class UserInputException extends RuntimeException {
private final String messageResourceKey;
- public UserInputException(String messageResourceKey) {
+ private final Object[] params;
+
+ public UserInputException(String messageResourceKey, Object... params) {
this.messageResourceKey = messageResourceKey;
+ this.params = params;
}
public String getMessageResourceKey() {
return messageResourceKey;
}
+
+ public Object[] getParams() {
+ return params;
+ }
}
diff --git a/src/main/java/org/luxons/sevenwonders/game/Game.java b/src/main/java/org/luxons/sevenwonders/game/Game.java
index 2a2fab27..6006ef47 100644
--- a/src/main/java/org/luxons/sevenwonders/game/Game.java
+++ b/src/main/java/org/luxons/sevenwonders/game/Game.java
@@ -44,6 +44,10 @@ public class Game {
return id;
}
+ public List<Player> getPlayers() {
+ return table.getPlayers();
+ }
+
private void startNewAge() {
currentAge++;
hands = decks.deal(currentAge, table.getNbPlayers());
diff --git a/src/main/java/org/luxons/sevenwonders/repositories/GameRepository.java b/src/main/java/org/luxons/sevenwonders/repositories/GameRepository.java
new file mode 100644
index 00000000..14aaf3ac
--- /dev/null
+++ b/src/main/java/org/luxons/sevenwonders/repositories/GameRepository.java
@@ -0,0 +1,41 @@
+package org.luxons.sevenwonders.repositories;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.luxons.sevenwonders.errors.ApiMisuseException;
+import org.luxons.sevenwonders.game.Game;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class GameRepository {
+
+ private Map<Long, Game> games = new HashMap<>();
+
+ public void add(Game game) {
+ if (games.containsKey(game.getId())) {
+ throw new GameAlreadyExistsException(game.getId());
+ }
+ games.put(game.getId(), game);
+ }
+
+ public Game find(long gameId) {
+ Game game = games.get(gameId);
+ if (game == null) {
+ throw new GameNotFoundException(gameId);
+ }
+ return game;
+ }
+
+ private class GameNotFoundException extends ApiMisuseException {
+ GameNotFoundException(long id) {
+ super("Game " + id + " doesn't exist");
+ }
+ }
+
+ private class GameAlreadyExistsException extends ApiMisuseException {
+ GameAlreadyExistsException(long id) {
+ super("Game " + id + " already exists");
+ }
+ }
+}
diff --git a/src/main/java/org/luxons/sevenwonders/repositories/LobbyRepository.java b/src/main/java/org/luxons/sevenwonders/repositories/LobbyRepository.java
new file mode 100644
index 00000000..e4f049f5
--- /dev/null
+++ b/src/main/java/org/luxons/sevenwonders/repositories/LobbyRepository.java
@@ -0,0 +1,58 @@
+package org.luxons.sevenwonders.repositories;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.luxons.sevenwonders.errors.UserInputException;
+import org.luxons.sevenwonders.game.Lobby;
+import org.luxons.sevenwonders.game.Player;
+import org.luxons.sevenwonders.game.data.GameDefinitionLoader;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class LobbyRepository {
+
+ private final GameDefinitionLoader gameDefinitionLoader;
+
+ private Map<String, Lobby> lobbies = new HashMap<>();
+
+ private long lastGameId = 0;
+
+ @Autowired
+ public LobbyRepository(GameDefinitionLoader gameDefinitionLoader) {
+ this.gameDefinitionLoader = gameDefinitionLoader;
+ }
+
+ public Lobby create(String gameName, Player owner) {
+ if (lobbies.containsKey(gameName)) {
+ throw new GameNameAlreadyUsedException(gameName);
+ }
+ long id = lastGameId++;
+ Lobby lobby = new Lobby(id, gameName, owner, gameDefinitionLoader.getGameDefinition());
+ lobbies.put(gameName, lobby);
+ return lobby;
+ }
+
+ public Lobby find(String gameName) {
+ Lobby lobby = lobbies.get(gameName);
+ if (lobby == null) {
+ throw new LobbyNotFoundException(gameName);
+ }
+ return lobby;
+ }
+
+ private class LobbyNotFoundException extends RuntimeException {
+
+ public LobbyNotFoundException(String name) {
+ super("Lobby not found for game '" + name + "'");
+ }
+ }
+
+ private class GameNameAlreadyUsedException extends UserInputException {
+
+ public GameNameAlreadyUsedException(String name) {
+ super("Game name '" + name + "' already exists");
+ }
+ }
+}
bgstack15