summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Chabbert <chabbertvi@eisti.eu>2016-12-18 09:30:04 +0100
committerVictor Chabbert <chabbertvi@eisti.eu>2016-12-18 09:30:04 +0100
commit88827a0633c6e359659e7aa79862672ac255df27 (patch)
treed0a8c29179f49208aca92f6daf1ca02446b8cf81
parentAdd redux and react-redux librairies with initial setup (diff)
parentImprove LobbyController errors and logs (diff)
downloadseven-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.gradle1
-rw-r--r--src/main/java/org/luxons/sevenwonders/actions/JoinGameAction.java24
-rw-r--r--src/main/java/org/luxons/sevenwonders/actions/JoinOrCreateGameAction.java31
-rw-r--r--src/main/java/org/luxons/sevenwonders/config/AnonymousUsersHandshakeHandler.java25
-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.java106
-rw-r--r--src/main/java/org/luxons/sevenwonders/controllers/UniqueIdAlreadyUsedException.java15
-rw-r--r--src/main/java/org/luxons/sevenwonders/game/Lobby.java32
-rw-r--r--src/main/java/org/luxons/sevenwonders/game/Player.java29
-rw-r--r--src/main/resources/static/app.js49
-rw-r--r--src/main/resources/static/index.html8
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
bgstack15