diff options
Diffstat (limited to 'src/main/java')
-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 |
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 + '\'' + '}'; } } |