diff options
author | Victor Chabbert <chabbertvi@eisti.eu> | 2016-12-18 09:30:04 +0100 |
---|---|---|
committer | Victor Chabbert <chabbertvi@eisti.eu> | 2016-12-18 09:30:04 +0100 |
commit | 88827a0633c6e359659e7aa79862672ac255df27 (patch) | |
tree | d0a8c29179f49208aca92f6daf1ca02446b8cf81 | |
parent | Add redux and react-redux librairies with initial setup (diff) | |
parent | Improve LobbyController errors and logs (diff) | |
download | seven-wonders-88827a0633c6e359659e7aa79862672ac255df27.tar.gz seven-wonders-88827a0633c6e359659e7aa79862672ac255df27.tar.bz2 seven-wonders-88827a0633c6e359659e7aa79862672ac255df27.zip |
Merge branch 'master' of github.com:luxons/seven-wonders
-rw-r--r-- | build.gradle | 1 | ||||
-rw-r--r-- | src/main/java/org/luxons/sevenwonders/actions/JoinGameAction.java | 24 | ||||
-rw-r--r-- | src/main/java/org/luxons/sevenwonders/actions/JoinOrCreateGameAction.java | 31 | ||||
-rw-r--r-- | src/main/java/org/luxons/sevenwonders/config/AnonymousUsersHandshakeHandler.java | 25 | ||||
-rw-r--r-- | src/main/java/org/luxons/sevenwonders/config/WebSocketConfig.java (renamed from src/main/java/org/luxons/sevenwonders/WebSocketConfig.java) | 11 | ||||
-rw-r--r-- | src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java | 106 | ||||
-rw-r--r-- | src/main/java/org/luxons/sevenwonders/controllers/UniqueIdAlreadyUsedException.java | 15 | ||||
-rw-r--r-- | src/main/java/org/luxons/sevenwonders/game/Lobby.java | 32 | ||||
-rw-r--r-- | src/main/java/org/luxons/sevenwonders/game/Player.java | 29 | ||||
-rw-r--r-- | src/main/resources/static/app.js | 49 | ||||
-rw-r--r-- | src/main/resources/static/index.html | 8 |
11 files changed, 250 insertions, 81 deletions
diff --git a/build.gradle b/build.gradle index bb64b9a0..d72cfc4f 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,7 @@ repositories { dependencies { compile 'com.google.code.gson:gson:2.8.0' + compile 'ch.qos.logback:logback-classic:1.0.13' compile 'org.springframework.boot:spring-boot-starter-websocket' compile 'org.webjars:webjars-locator' diff --git a/src/main/java/org/luxons/sevenwonders/actions/JoinGameAction.java b/src/main/java/org/luxons/sevenwonders/actions/JoinGameAction.java deleted file mode 100644 index a9a84d78..00000000 --- a/src/main/java/org/luxons/sevenwonders/actions/JoinGameAction.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.luxons.sevenwonders.actions; - -public class JoinGameAction { - - private String gameId; - - private String playerName; - - public String getGameId() { - return gameId; - } - - public void setGameId(String gameId) { - this.gameId = gameId; - } - - public String getPlayerName() { - return playerName; - } - - public void setPlayerName(String playerName) { - this.playerName = playerName; - } -} diff --git a/src/main/java/org/luxons/sevenwonders/actions/JoinOrCreateGameAction.java b/src/main/java/org/luxons/sevenwonders/actions/JoinOrCreateGameAction.java new file mode 100644 index 00000000..cf5596da --- /dev/null +++ b/src/main/java/org/luxons/sevenwonders/actions/JoinOrCreateGameAction.java @@ -0,0 +1,31 @@ +package org.luxons.sevenwonders.actions; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +public class JoinOrCreateGameAction { + + @NotNull + @Size(min=2, max=30) + private String gameName; + + @NotNull + @Size(min=2, max=20) + private String playerName; + + public String getGameName() { + return gameName; + } + + public void setGameName(String gameName) { + this.gameName = gameName; + } + + public String getPlayerName() { + return playerName; + } + + public void setPlayerName(String playerName) { + this.playerName = playerName; + } +} diff --git a/src/main/java/org/luxons/sevenwonders/config/AnonymousUsersHandshakeHandler.java b/src/main/java/org/luxons/sevenwonders/config/AnonymousUsersHandshakeHandler.java new file mode 100644 index 00000000..1132197d --- /dev/null +++ b/src/main/java/org/luxons/sevenwonders/config/AnonymousUsersHandshakeHandler.java @@ -0,0 +1,25 @@ +package org.luxons.sevenwonders.config; + +import java.security.Principal; +import java.util.Map; + +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.server.support.DefaultHandshakeHandler; + +import sun.security.acl.PrincipalImpl; + +class AnonymousUsersHandshakeHandler extends DefaultHandshakeHandler { + + private int playerId = 0; + + @Override + public Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, + Map<String, Object> attributes) { + Principal p = request.getPrincipal(); + if (p == null) { + p = new PrincipalImpl("player" + playerId++); + } + return p; + } +} diff --git a/src/main/java/org/luxons/sevenwonders/WebSocketConfig.java b/src/main/java/org/luxons/sevenwonders/config/WebSocketConfig.java index b0613e79..81c90c9b 100644 --- a/src/main/java/org/luxons/sevenwonders/WebSocketConfig.java +++ b/src/main/java/org/luxons/sevenwonders/config/WebSocketConfig.java @@ -1,10 +1,12 @@ -package org.luxons.sevenwonders; +package org.luxons.sevenwonders.config; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.server.support.DefaultHandshakeHandler; @Configuration @EnableWebSocketMessageBroker @@ -22,7 +24,12 @@ public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { - registry.addEndpoint("/seven-wonders-websocket").withSockJS(); + registry.addEndpoint("/seven-wonders-websocket").setHandshakeHandler(handshakeHandler()).withSockJS(); + } + + @Bean + public DefaultHandshakeHandler handshakeHandler() { + return new AnonymousUsersHandshakeHandler(); } }
\ No newline at end of file diff --git a/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java b/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java index 705e6ef2..ea262c4e 100644 --- a/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java +++ b/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java @@ -1,23 +1,33 @@ package org.luxons.sevenwonders.controllers; +import java.security.Principal; import java.util.HashMap; import java.util.Map; -import org.luxons.sevenwonders.actions.JoinGameAction; +import org.luxons.sevenwonders.actions.JoinOrCreateGameAction; import org.luxons.sevenwonders.game.Game; import org.luxons.sevenwonders.game.Lobby; import org.luxons.sevenwonders.game.Player; import org.luxons.sevenwonders.game.data.GameDefinitionLoader; +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; +import org.springframework.messaging.simp.annotation.SendToUser; import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; @Controller @MessageMapping("/lobby") public class LobbyController { + private static final Logger logger = LoggerFactory.getLogger(LobbyController.class); + + public static final String ATTR_LOBBY = "lobby"; + private final GameDefinitionLoader gameDefinitionLoader; private long lastGameId = 0; @@ -31,41 +41,87 @@ public class LobbyController { this.gameDefinitionLoader = gameDefinitionLoader; } + @MessageExceptionHandler + @SendToUser("/queue/errors") + public String handleException(Throwable exception) { + logger.error("An error occured during message handling", exception); + return exception.getClass().getSimpleName() + ": " + exception.getMessage(); + } + @MessageMapping("/create-game") @SendTo("/topic/games") - public String createGame(SimpMessageHeaderAccessor headerAccessor) throws Exception { - System.out.println("Received message: " + headerAccessor.getSessionId()); - Thread.sleep(1000); // simulated delay + public Lobby createGame(SimpMessageHeaderAccessor headerAccessor, @Validated JoinOrCreateGameAction action, + Principal principal) { + Lobby lobby = (Lobby)headerAccessor.getSessionAttributes().get(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); - long newId = lastGameId++; - String id = String.valueOf(newId); - System.out.println("Creating game " + id); + headerAccessor.getSessionAttributes().put(ATTR_LOBBY, lobby); - Lobby lobby = new Lobby(newId, gameDefinitionLoader.getGameDefinition()); - lobbies.put(id, lobby); - return id; + logger.info("Game '{}' (id={}) created by {} ({})", lobby.getName(), lobby.getId(), player.getDisplayName(), + player.getUserName()); + return lobby; } @MessageMapping("/join-game") - @SendTo("/topic/players") - public Player joinGame(SimpMessageHeaderAccessor headerAccessor, JoinGameAction joinAction) throws Exception { - Thread.sleep(1000); // simulated delay - - Player player = (Player)headerAccessor.getSessionAttributes().get("player"); - Lobby lobby = (Lobby)headerAccessor.getSessionAttributes().get("lobby"); - if (player != null && lobby != null) { - System.out.println("Client has already joined game " + lobby.getId() + "under the name " + player.getName()); - return player; + @SendToUser("/queue/join-game") + public Lobby joinGame(SimpMessageHeaderAccessor headerAccessor, @Validated JoinOrCreateGameAction action, + Principal principal) { + Lobby lobby = (Lobby)headerAccessor.getSessionAttributes().get(ATTR_LOBBY); + if (lobby != null) { + logger.warn("Client already in game '{}', cannot join a different game", lobby.getName()); + return lobby; } - System.out.println("Player " + joinAction.getPlayerName() + " joined game " + joinAction.getGameId()); - lobby = lobbies.get(joinAction.getGameId()); - Player newPlayer = new Player(joinAction.getPlayerName()); + lobby = lobbies.get(action.getGameName()); + if (lobby == null) { + throw new GameNotFoundException(action.getGameName()); + } + + Player newPlayer = createPlayer(action.getPlayerName(), principal); lobby.addPlayer(newPlayer); - headerAccessor.getSessionAttributes().put("player", newPlayer); - headerAccessor.getSessionAttributes().put("lobby", lobby); + headerAccessor.getSessionAttributes().put(ATTR_LOBBY, lobby); + + logger.warn("Player {} joined game {}", action.getPlayerName(), action.getGameName()); + + return lobby; + } + + private Player createPlayer(String name, Principal principal) { + Player player = new Player(name); + player.setUserName(principal.getName()); + return player; + } + + private Lobby createGame(String name, Player owner) { + if (lobbies.containsKey(name)) { + throw new GameNameAlreadyUsedException(name); + } + 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); + } + + } - return newPlayer; + private class GameNameAlreadyUsedException extends UniqueIdAlreadyUsedException { + + public GameNameAlreadyUsedException(String name) { + super(name); + } } + } diff --git a/src/main/java/org/luxons/sevenwonders/controllers/UniqueIdAlreadyUsedException.java b/src/main/java/org/luxons/sevenwonders/controllers/UniqueIdAlreadyUsedException.java new file mode 100644 index 00000000..56c22655 --- /dev/null +++ b/src/main/java/org/luxons/sevenwonders/controllers/UniqueIdAlreadyUsedException.java @@ -0,0 +1,15 @@ +package org.luxons.sevenwonders.controllers; + +public class UniqueIdAlreadyUsedException extends RuntimeException { + + private String id; + + public UniqueIdAlreadyUsedException(String id) { + super("'" + id + "'"); + this.id = id; + } + + public String getUsedId() { + return id; + } +} diff --git a/src/main/java/org/luxons/sevenwonders/game/Lobby.java b/src/main/java/org/luxons/sevenwonders/game/Lobby.java index 24055c75..7256936c 100644 --- a/src/main/java/org/luxons/sevenwonders/game/Lobby.java +++ b/src/main/java/org/luxons/sevenwonders/game/Lobby.java @@ -3,28 +3,37 @@ package org.luxons.sevenwonders.game; import java.util.ArrayList; import java.util.List; +import org.luxons.sevenwonders.controllers.UniqueIdAlreadyUsedException; import org.luxons.sevenwonders.game.data.GameDefinition; public class Lobby { private final long id; + private final String name; + private final List<Player> players; private final GameDefinition gameDefinition; private State state = State.LOBBY; - public Lobby(long id, GameDefinition gameDefinition) { + public Lobby(long id, String name, Player owner, GameDefinition gameDefinition) { this.id = id; + this.name = name; this.gameDefinition = gameDefinition; - this.players = new ArrayList<>(3); + this.players = new ArrayList<>(gameDefinition.getMinPlayers()); + players.add(owner); } public long getId() { return id; } + public String getName() { + return name; + } + public synchronized int addPlayer(Player player) throws GameAlreadyStartedException, PlayerOverflowException { if (hasStarted()) { throw new GameAlreadyStartedException(); @@ -32,6 +41,9 @@ public class Lobby { if (maxPlayersReached()) { throw new PlayerOverflowException(); } + if (playerNameAlreadyUsed(player.getDisplayName())) { + throw new PlayerNameAlreadyUsedException(player.getDisplayName()); + } int playerId = players.size(); players.add(player); return playerId; @@ -45,6 +57,10 @@ public class Lobby { return players.size() >= gameDefinition.getMaxPlayers(); } + private boolean playerNameAlreadyUsed(String name) { + return players.stream().anyMatch(p -> p.getDisplayName().equals(name)); + } + public synchronized Game startGame(Settings settings) throws PlayerUnderflowException { if (!hasEnoughPlayers()) { throw new PlayerUnderflowException(); @@ -58,6 +74,11 @@ public class Lobby { return players.size() >= gameDefinition.getMinPlayers(); } + @Override + public String toString() { + return "Lobby{" + "id=" + id + ", name='" + name + '\'' + ", state=" + state + '}'; + } + public class GameAlreadyStartedException extends IllegalStateException { } @@ -66,4 +87,11 @@ public class Lobby { public class PlayerUnderflowException extends IllegalStateException { } + + public class PlayerNameAlreadyUsedException extends UniqueIdAlreadyUsedException { + + public PlayerNameAlreadyUsedException(String name) { + super(name); + } + } } diff --git a/src/main/java/org/luxons/sevenwonders/game/Player.java b/src/main/java/org/luxons/sevenwonders/game/Player.java index ac7067b4..fd2f254b 100644 --- a/src/main/java/org/luxons/sevenwonders/game/Player.java +++ b/src/main/java/org/luxons/sevenwonders/game/Player.java @@ -2,17 +2,32 @@ package org.luxons.sevenwonders.game; public class Player { - private String name; + private String displayName; - public Player(String name) { - this.name = name; + private String userName; + + public Player(String displayName) { + this.displayName = displayName; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getUserName() { + return userName; } - public String getName() { - return name; + public void setUserName(String userName) { + this.userName = userName; } - public void setName(String name) { - this.name = name; + @Override + public String toString() { + return "Player{" + "name='" + displayName + '\'' + ", userName='" + userName + '\'' + '}'; } } diff --git a/src/main/resources/static/app.js b/src/main/resources/static/app.js index dfdcf934..b1881357 100644 --- a/src/main/resources/static/app.js +++ b/src/main/resources/static/app.js @@ -18,14 +18,20 @@ function connect() { setConnected(true); console.log('Connected: ' + frame); - stompClient.subscribe('/topic/games', function (gameId) { - console.log("Received new game: " + gameId); - addNewGame(gameId.body); + stompClient.subscribe('/user/queue/errors', function (msg) { + console.error(msg.body); }); - stompClient.subscribe('/topic/players', function (player) { - console.log("Received new player: " + player); - addNewPlayer(JSON.parse(player.body)); + stompClient.subscribe('/topic/games', function (msg) { + var game = JSON.parse(msg.body); + console.log("Received new game: " + game); + addNewGame(game); + }); + + stompClient.subscribe('/user/queue/join-game', function (msg) { + var game = JSON.parse(msg.body); + console.log("Joined game: " + game); + addNewPlayer(game); }); }); } @@ -38,20 +44,26 @@ function disconnect() { console.log("Disconnected"); } -function sendCreateGame() { - stompClient.send("/app/lobby/create-game", {}, ""); +function sendCreateGame(gameName, playerName) { + stompClient.send("/app/lobby/create-game", {}, JSON.stringify({ + 'gameName': gameName, + 'playerName': playerName + })); } -function sendJoinGame(gameId) { - stompClient.send("/app/lobby/join-game", {}, - JSON.stringify({'gameId': gameId, 'playerName': $("#player-name-field").val()})); +function sendJoinGame(gameName, playerName) { + stompClient.send("/app/lobby/join-game", {}, JSON.stringify({ + 'gameName': gameName, + 'playerName': playerName + })); } -function addNewGame(gameId) { - console.log(gameId); - $("#game-list-content").append('<tr><td>' + gameId + '</td><td><button id="join-' + gameId + '" type="submit">Join</button></td></tr>'); - $("#join-" + gameId).click(function () { - sendJoinGame(gameId); +function addNewGame(game) { + console.log(game); + $("#game-list-content").append('<tr><td>' + game.name + '</td><td><button id="join-' + game.id + + '" type="submit">Join</button></td></tr>'); + $("#join-" + game.id).click(function () { + sendJoinGame(game.name, $("#player-name-field").val()); }); } @@ -70,9 +82,6 @@ $(function () { disconnect(); }); $("#create-game").click(function () { - sendCreateGame(); - }); - $("#join-game").click(function () { - sendJoinGame(); + sendCreateGame($("#game-name-field").val(), $("#player-name-field").val()); }); });
\ No newline at end of file diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 6131e912..d5ec178d 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -52,7 +52,13 @@ </tbody> </table> -<button id="create-game" class="btn btn-default" type="submit">Create Game</button> +<form class="form-inline"> + <div class="form-group"> + <label for="game-name-field">Game name</label> + <input id="game-name-field"> + <button id="create-game" class="btn btn-default" type="submit">Create</button> + </div> +</form> </body> </html>
\ No newline at end of file |