summaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-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
8 files changed, 213 insertions, 60 deletions
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 + '\'' + '}';
}
}
bgstack15