diff options
author | Victor Chabbert <chabbertvi@eisti.eu> | 2016-12-19 14:32:12 +0100 |
---|---|---|
committer | Victor Chabbert <chabbertvi@eisti.eu> | 2016-12-19 14:32:12 +0100 |
commit | 5fa35946cdc26506cac1b15ce7e816678a809c59 (patch) | |
tree | 5c67cd934c679ee9373bee735612a00cb9308c65 /src/main/java | |
parent | Working error saga (diff) | |
parent | Add react css librairies (diff) | |
download | seven-wonders-5fa35946cdc26506cac1b15ce7e816678a809c59.tar.gz seven-wonders-5fa35946cdc26506cac1b15ce7e816678a809c59.tar.bz2 seven-wonders-5fa35946cdc26506cac1b15ce7e816678a809c59.zip |
Merge branch 'master' into feature/websockets-sagas
Diffstat (limited to 'src/main/java')
13 files changed, 249 insertions, 33 deletions
diff --git a/src/main/java/org/luxons/sevenwonders/actions/StartGameAction.java b/src/main/java/org/luxons/sevenwonders/actions/StartGameAction.java new file mode 100644 index 00000000..632b80ef --- /dev/null +++ b/src/main/java/org/luxons/sevenwonders/actions/StartGameAction.java @@ -0,0 +1,19 @@ +package org.luxons.sevenwonders.actions; + +import javax.validation.constraints.NotNull; + +import org.luxons.sevenwonders.game.Settings; + +public class StartGameAction { + + @NotNull + private Settings settings; + + public Settings getSettings() { + return settings; + } + + public void setSettings(Settings settings) { + this.settings = settings; + } +} diff --git a/src/main/java/org/luxons/sevenwonders/config/WebSocketConfig.java b/src/main/java/org/luxons/sevenwonders/config/WebSocketConfig.java index 81c90c9b..83629d6a 100644 --- a/src/main/java/org/luxons/sevenwonders/config/WebSocketConfig.java +++ b/src/main/java/org/luxons/sevenwonders/config/WebSocketConfig.java @@ -24,7 +24,10 @@ public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { - registry.addEndpoint("/seven-wonders-websocket").setHandshakeHandler(handshakeHandler()).withSockJS(); + registry.addEndpoint("/seven-wonders-websocket") + .setHandshakeHandler(handshakeHandler()) + .setAllowedOrigins("http://localhost:3000") // to allow frontend server proxy requests in dev mode + .withSockJS(); } @Bean diff --git a/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java b/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java index ea262c4e..e15935d6 100644 --- a/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java +++ b/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java @@ -2,13 +2,20 @@ 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.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.session.SessionAttributes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -16,6 +23,7 @@ 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; +import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.messaging.simp.annotation.SendToUser; import org.springframework.stereotype.Controller; import org.springframework.validation.annotation.Validated; @@ -26,10 +34,12 @@ public class LobbyController { private static final Logger logger = LoggerFactory.getLogger(LobbyController.class); - public static final String ATTR_LOBBY = "lobby"; - private final GameDefinitionLoader gameDefinitionLoader; + private final SimpMessagingTemplate template; + + private final ErrorFactory errorFactory; + private long lastGameId = 0; private Map<String, Lobby> lobbies = new HashMap<>(); @@ -37,22 +47,25 @@ public class LobbyController { private Map<String, Game> games = new HashMap<>(); @Autowired - public LobbyController(GameDefinitionLoader gameDefinitionLoader) { + public LobbyController(GameDefinitionLoader gameDefinitionLoader, SimpMessagingTemplate template, + ErrorFactory errorFactory) { this.gameDefinitionLoader = gameDefinitionLoader; + this.template = template; + this.errorFactory = errorFactory; } @MessageExceptionHandler @SendToUser("/queue/errors") - public String handleException(Throwable exception) { + public UIError handleException(Throwable exception) { logger.error("An error occured during message handling", exception); - return exception.getClass().getSimpleName() + ": " + exception.getMessage(); + return errorFactory.createError(exception); } @MessageMapping("/create-game") @SendTo("/topic/games") public Lobby createGame(SimpMessageHeaderAccessor headerAccessor, @Validated JoinOrCreateGameAction action, Principal principal) { - Lobby lobby = (Lobby)headerAccessor.getSessionAttributes().get(ATTR_LOBBY); + 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; @@ -61,7 +74,7 @@ public class LobbyController { Player player = createPlayer(action.getPlayerName(), principal); lobby = createGame(action.getGameName(), player); - headerAccessor.getSessionAttributes().put(ATTR_LOBBY, lobby); + headerAccessor.getSessionAttributes().put(SessionAttributes.ATTR_LOBBY, lobby); logger.info("Game '{}' (id={}) created by {} ({})", lobby.getName(), lobby.getId(), player.getDisplayName(), player.getUserName()); @@ -72,7 +85,7 @@ public class LobbyController { @SendToUser("/queue/join-game") public Lobby joinGame(SimpMessageHeaderAccessor headerAccessor, @Validated JoinOrCreateGameAction action, Principal principal) { - Lobby lobby = (Lobby)headerAccessor.getSessionAttributes().get(ATTR_LOBBY); + 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; @@ -86,17 +99,42 @@ public class LobbyController { Player newPlayer = createPlayer(action.getPlayerName(), principal); lobby.addPlayer(newPlayer); - headerAccessor.getSessionAttributes().put(ATTR_LOBBY, lobby); + headerAccessor.getSessionAttributes().put(SessionAttributes.ATTR_LOBBY, lobby); logger.warn("Player {} joined game {}", action.getPlayerName(), action.getGameName()); return lobby; } + @MessageMapping("/start-game") + public void startGame(SimpMessageHeaderAccessor headerAccessor, @Validated StartGameAction action, + Principal principal) { + 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; + } + + Game game = lobby.startGame(action.getSettings()); + logger.info("Game {} successfully started", game.getId()); + + List<PlayerTurnInfo> playerTurnInfos = game.startTurn(); + for (PlayerTurnInfo playerTurnInfo : playerTurnInfos) { + Player player = playerTurnInfo.getTable().getPlayers().get(playerTurnInfo.getPlayerIndex()); + String userName = player.getUserName(); + template.convertAndSendToUser(userName, "/queue/game/turn", playerTurnInfo); + } + } + private Player createPlayer(String name, Principal principal) { - Player player = new Player(name); - player.setUserName(principal.getName()); - return player; + return new Player(name, principal.getName()); } private Lobby createGame(String name, Player owner) { @@ -114,7 +152,6 @@ public class LobbyController { public GameNotFoundException(String name) { super(name); } - } private class GameNameAlreadyUsedException extends UniqueIdAlreadyUsedException { @@ -123,5 +160,4 @@ public class LobbyController { super(name); } } - } diff --git a/src/main/java/org/luxons/sevenwonders/errors/ErrorFactory.java b/src/main/java/org/luxons/sevenwonders/errors/ErrorFactory.java new file mode 100644 index 00000000..2d3dcd0b --- /dev/null +++ b/src/main/java/org/luxons/sevenwonders/errors/ErrorFactory.java @@ -0,0 +1,52 @@ +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/ErrorType.java b/src/main/java/org/luxons/sevenwonders/errors/ErrorType.java new file mode 100644 index 00000000..71df185f --- /dev/null +++ b/src/main/java/org/luxons/sevenwonders/errors/ErrorType.java @@ -0,0 +1,7 @@ +package org.luxons.sevenwonders.errors; + +public enum ErrorType { + USER, + VALIDATION, + INTERNAL +} diff --git a/src/main/java/org/luxons/sevenwonders/errors/UIError.java b/src/main/java/org/luxons/sevenwonders/errors/UIError.java new file mode 100644 index 00000000..67802f22 --- /dev/null +++ b/src/main/java/org/luxons/sevenwonders/errors/UIError.java @@ -0,0 +1,42 @@ +package org.luxons.sevenwonders.errors; + +import java.util.List; + +import org.springframework.validation.ObjectError; + +public class UIError { + + private final String code; + + private final String message; + + private final ErrorType type; + + private List<ObjectError> validationErrors; + + public UIError(String code, String message, ErrorType type) { + this.code = code; + this.message = message; + this.type = type; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } + + public ErrorType getType() { + return type; + } + + public List<ObjectError> getValidationErrors() { + return validationErrors; + } + + public void setValidationErrors(List<ObjectError> validationErrors) { + this.validationErrors = validationErrors; + } +} diff --git a/src/main/java/org/luxons/sevenwonders/controllers/UniqueIdAlreadyUsedException.java b/src/main/java/org/luxons/sevenwonders/errors/UniqueIdAlreadyUsedException.java index 56c22655..b1b00453 100644 --- a/src/main/java/org/luxons/sevenwonders/controllers/UniqueIdAlreadyUsedException.java +++ b/src/main/java/org/luxons/sevenwonders/errors/UniqueIdAlreadyUsedException.java @@ -1,4 +1,4 @@ -package org.luxons.sevenwonders.controllers; +package org.luxons.sevenwonders.errors; public class UniqueIdAlreadyUsedException extends RuntimeException { diff --git a/src/main/java/org/luxons/sevenwonders/errors/UserInputException.java b/src/main/java/org/luxons/sevenwonders/errors/UserInputException.java new file mode 100644 index 00000000..11f7f8f2 --- /dev/null +++ b/src/main/java/org/luxons/sevenwonders/errors/UserInputException.java @@ -0,0 +1,14 @@ +package org.luxons.sevenwonders.errors; + +public class UserInputException extends RuntimeException { + + private final String messageResourceKey; + + public UserInputException(String messageResourceKey) { + this.messageResourceKey = messageResourceKey; + } + + public String getMessageResourceKey() { + return messageResourceKey; + } +} diff --git a/src/main/java/org/luxons/sevenwonders/game/Game.java b/src/main/java/org/luxons/sevenwonders/game/Game.java index f4dac7ef..2a2fab27 100644 --- a/src/main/java/org/luxons/sevenwonders/game/Game.java +++ b/src/main/java/org/luxons/sevenwonders/game/Game.java @@ -37,22 +37,23 @@ public class Game { this.discardedCards = new ArrayList<>(); this.hands = new HashMap<>(); this.preparedMoves = new HashMap<>(); + startNewAge(); } public long getId() { return id; } - public int startNewAge() { + private void startNewAge() { currentAge++; hands = decks.deal(currentAge, table.getNbPlayers()); - return currentAge; } public List<PlayerTurnInfo> startTurn() { return hands.entrySet() .stream() .map(e -> table.createPlayerTurnInfo(e.getKey(), e.getValue())) + .peek(ptu -> ptu.setCurrentAge(currentAge)) .collect(Collectors.toList()); } @@ -69,10 +70,27 @@ public class Game { return preparedMoves.size() == table.getPlayers().size(); } - public void playTurn() { + public List<Move> playTurn() { + List<Move> playedMoves = mapToList(preparedMoves); + // cards need to be all placed first as some effects depend on just-played cards placePreparedCards(); playPreparedCards(); + preparedMoves.clear(); + + return playedMoves; + } + + private static List<Move> mapToList(Map<Integer, Move> movesPerPlayer) { + List<Move> moves = new ArrayList<>(movesPerPlayer.size()); + for (int p = 0; p < movesPerPlayer.size(); p++) { + Move move = movesPerPlayer.get(p); + if (move == null) { + throw new MissingPreparedMoveException(p); + } + moves.add(move); + } + return moves; } private void placePreparedCards() { @@ -85,10 +103,10 @@ public class Game { // TODO pre-upgrade the level of wonder without effect break; case DISCARD: + discardedCards.add(decks.getCard(move.getCardName())); break; } }); - } private void playPreparedCards() { @@ -108,6 +126,13 @@ public class Game { } - public class InvalidMoveException extends RuntimeException { + private static class MissingPreparedMoveException extends RuntimeException { + + public MissingPreparedMoveException(int playerIndex) { + super("Player " + playerIndex + " is not ready to play"); + } + } + + private static class InvalidMoveException extends RuntimeException { } } diff --git a/src/main/java/org/luxons/sevenwonders/game/Lobby.java b/src/main/java/org/luxons/sevenwonders/game/Lobby.java index 7256936c..16d4e8a3 100644 --- a/src/main/java/org/luxons/sevenwonders/game/Lobby.java +++ b/src/main/java/org/luxons/sevenwonders/game/Lobby.java @@ -3,7 +3,7 @@ package org.luxons.sevenwonders.game; import java.util.ArrayList; import java.util.List; -import org.luxons.sevenwonders.controllers.UniqueIdAlreadyUsedException; +import org.luxons.sevenwonders.errors.UniqueIdAlreadyUsedException; import org.luxons.sevenwonders.game.data.GameDefinition; public class Lobby { @@ -12,6 +12,8 @@ public class Lobby { private final String name; + private final Player owner; + private final List<Player> players; private final GameDefinition gameDefinition; @@ -21,6 +23,7 @@ public class Lobby { public Lobby(long id, String name, Player owner, GameDefinition gameDefinition) { this.id = id; this.name = name; + this.owner = owner; this.gameDefinition = gameDefinition; this.players = new ArrayList<>(gameDefinition.getMinPlayers()); players.add(owner); @@ -79,6 +82,10 @@ public class Lobby { return "Lobby{" + "id=" + id + ", name='" + name + '\'' + ", state=" + state + '}'; } + public boolean isOwner(String userName) { + return owner.getUserName().equals(userName); + } + public class GameAlreadyStartedException extends IllegalStateException { } diff --git a/src/main/java/org/luxons/sevenwonders/game/Player.java b/src/main/java/org/luxons/sevenwonders/game/Player.java index fd2f254b..66ce566d 100644 --- a/src/main/java/org/luxons/sevenwonders/game/Player.java +++ b/src/main/java/org/luxons/sevenwonders/game/Player.java @@ -2,30 +2,23 @@ package org.luxons.sevenwonders.game; public class Player { - private String displayName; + private final String displayName; - private String userName; + private final String userName; - public Player(String displayName) { + public Player(String displayName, String userName) { this.displayName = displayName; + this.userName = userName; } public String getDisplayName() { return displayName; } - public void setDisplayName(String displayName) { - this.displayName = displayName; - } - public String getUserName() { return userName; } - public void setUserName(String userName) { - this.userName = userName; - } - @Override public String toString() { return "Player{" + "name='" + displayName + '\'' + ", userName='" + userName + '\'' + '}'; diff --git a/src/main/java/org/luxons/sevenwonders/game/api/PlayerTurnInfo.java b/src/main/java/org/luxons/sevenwonders/game/api/PlayerTurnInfo.java index ad9e6da9..b7090ff9 100644 --- a/src/main/java/org/luxons/sevenwonders/game/api/PlayerTurnInfo.java +++ b/src/main/java/org/luxons/sevenwonders/game/api/PlayerTurnInfo.java @@ -8,6 +8,8 @@ public class PlayerTurnInfo { private final Table table; + private int currentAge; + private List<HandCard> hand; private String message; @@ -25,6 +27,14 @@ public class PlayerTurnInfo { return table; } + public int getCurrentAge() { + return currentAge; + } + + public void setCurrentAge(int currentAge) { + this.currentAge = currentAge; + } + public List<HandCard> getHand() { return hand; } diff --git a/src/main/java/org/luxons/sevenwonders/session/SessionAttributes.java b/src/main/java/org/luxons/sevenwonders/session/SessionAttributes.java new file mode 100644 index 00000000..8c69d8c3 --- /dev/null +++ b/src/main/java/org/luxons/sevenwonders/session/SessionAttributes.java @@ -0,0 +1,8 @@ +package org.luxons.sevenwonders.session; + +public class SessionAttributes { + + public static final String ATTR_LOBBY = "lobby"; + + public static final String ATTR_GAME = "game"; +} |