summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/luxons/sevenwonders/actions/JoinOrCreateGameAction.java (renamed from src/main/java/org/luxons/sevenwonders/actions/JoinGameAction.java)12
-rw-r--r--src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java98
-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.java19
-rw-r--r--src/main/resources/static/app.js49
-rw-r--r--src/main/resources/static/index.html8
7 files changed, 174 insertions, 59 deletions
diff --git a/src/main/java/org/luxons/sevenwonders/actions/JoinGameAction.java b/src/main/java/org/luxons/sevenwonders/actions/JoinOrCreateGameAction.java
index fc2e11d2..cf5596da 100644
--- a/src/main/java/org/luxons/sevenwonders/actions/JoinGameAction.java
+++ b/src/main/java/org/luxons/sevenwonders/actions/JoinOrCreateGameAction.java
@@ -3,22 +3,22 @@ package org.luxons.sevenwonders.actions;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
-public class JoinGameAction {
+public class JoinOrCreateGameAction {
@NotNull
@Size(min=2, max=30)
- private String gameId;
+ private String gameName;
@NotNull
@Size(min=2, max=20)
private String playerName;
- public String getGameId() {
- return gameId;
+ public String getGameName() {
+ return gameName;
}
- public void setGameId(String gameId) {
- this.gameId = gameId;
+ public void setGameName(String gameName) {
+ this.gameName = gameName;
}
public String getPlayerName() {
diff --git a/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java b/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java
index 312214d6..ea262c4e 100644
--- a/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java
+++ b/src/main/java/org/luxons/sevenwonders/controllers/LobbyController.java
@@ -4,7 +4,7 @@ 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;
@@ -12,9 +12,11 @@ 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;
@@ -24,6 +26,8 @@ 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;
@@ -37,39 +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, Principal principal) {
- long newId = lastGameId++;
- String id = String.valueOf(newId);
-
- Lobby lobby = new Lobby(newId, gameDefinitionLoader.getGameDefinition());
- lobbies.put(id, lobby);
- logger.info("Game {} created by {}", id, principal.getName());
- return id;
+ 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);
+
+ headerAccessor.getSessionAttributes().put(ATTR_LOBBY, lobby);
+
+ 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, @Validated JoinGameAction action,
+ @SendToUser("/queue/join-game")
+ public Lobby joinGame(SimpMessageHeaderAccessor headerAccessor, @Validated JoinOrCreateGameAction action,
Principal principal) {
- Player player = (Player)headerAccessor.getSessionAttributes().get("player");
- Lobby lobby = (Lobby)headerAccessor.getSessionAttributes().get("lobby");
- if (player != null && lobby != null) {
- logger.warn("Client has already joined game {} under the name {}", lobby.getId(), player.getName());
- return player;
+ 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;
+ }
+
+ lobby = lobbies.get(action.getGameName());
+ if (lobby == null) {
+ throw new GameNotFoundException(action.getGameName());
}
- lobby = lobbies.get(action.getGameId());
- Player newPlayer = new Player(action.getPlayerName());
- newPlayer.setUserName(principal.getName());
+ 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.getGameId());
+ logger.warn("Player {} joined game {}", action.getPlayerName(), action.getGameName());
- return newPlayer;
+ 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);
+ }
+
+ }
+
+ 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 eafabf4a..fd2f254b 100644
--- a/src/main/java/org/luxons/sevenwonders/game/Player.java
+++ b/src/main/java/org/luxons/sevenwonders/game/Player.java
@@ -2,20 +2,20 @@ package org.luxons.sevenwonders.game;
public class Player {
- private String name;
+ private String displayName;
private String userName;
- public Player(String name) {
- this.name = name;
+ public Player(String displayName) {
+ this.displayName = displayName;
}
- public String getName() {
- return name;
+ public String getDisplayName() {
+ return displayName;
}
- public void setName(String name) {
- this.name = name;
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
}
public String getUserName() {
@@ -25,4 +25,9 @@ public class Player {
public void setUserName(String userName) {
this.userName = userName;
}
+
+ @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