From 3d2fb445edf172676a1f43cd27ee373538b1b318 Mon Sep 17 00:00:00 2001 From: Charles Gould Date: Mon, 18 May 2020 00:21:23 -0500 Subject: [PATCH] Add player list to game lobby --- .../com/charego/lingo/api/Destinations.java | 6 ++- .../multiplayer/MultiplayerPresenter.java | 12 ++---- .../com/charego/lingo/common/LobbyData.java | 35 +++++++++++++++++ .../charego/lingo/server/LingoController.java | 30 ++++++-------- .../charego/lingo/server/SessionManager.java | 7 ++++ server/src/main/resources/static/client.js | 39 ++++++++++++++++--- server/src/main/resources/static/index.html | 12 ++++++ server/src/main/resources/static/style.css | 5 +++ 8 files changed, 110 insertions(+), 36 deletions(-) create mode 100644 common/src/main/java/com/charego/lingo/common/LobbyData.java diff --git a/api/src/main/java/com/charego/lingo/api/Destinations.java b/api/src/main/java/com/charego/lingo/api/Destinations.java index f8f9686..e766284 100644 --- a/api/src/main/java/com/charego/lingo/api/Destinations.java +++ b/api/src/main/java/com/charego/lingo/api/Destinations.java @@ -16,6 +16,10 @@ public class Destinations { public static final String OPPONENT_REPORTS = topicDestination("opponentReports"); + public static final String PLAYER_JOINED = topicDestination("playerJoined"); + + public static final String PLAYER_LEFT = topicDestination("playerLeft"); + public static final String PLAYER_REPORTS = topicDestination("playerReports"); public static final String PRACTICE_GAME = topicDestination("practiceGame"); @@ -26,8 +30,6 @@ public class Destinations { public static final String SESSION_USERNAME = topicDestination("sessionUsername"); - public static final String USER_JOINED = topicDestination("userJoined"); - private static String topicDestination(String suffix) { return "/topic/" + suffix; } diff --git a/client/src/main/java/com/charego/lingo/client/multiplayer/MultiplayerPresenter.java b/client/src/main/java/com/charego/lingo/client/multiplayer/MultiplayerPresenter.java index f09f018..9dff359 100644 --- a/client/src/main/java/com/charego/lingo/client/multiplayer/MultiplayerPresenter.java +++ b/client/src/main/java/com/charego/lingo/client/multiplayer/MultiplayerPresenter.java @@ -2,19 +2,17 @@ package com.charego.lingo.client.multiplayer; import java.lang.reflect.Type; import java.util.Arrays; -import java.util.Collection; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; import javax.annotation.PostConstruct; +import com.charego.lingo.common.LobbyData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.task.TaskExecutor; -import org.springframework.http.HttpMethod; import org.springframework.messaging.simp.stomp.StompFrameHandler; import org.springframework.messaging.simp.stomp.StompHeaders; import org.springframework.messaging.simp.stomp.StompSession.Subscription; @@ -142,9 +140,9 @@ public class MultiplayerPresenter implements FxmlController { username = UUID.randomUUID().toString().substring(0, 8); stompTemplate.getSession().send("/app/setUsername", username); - Collection games = restTemplate.exchange("/games", HttpMethod.GET, null, new GameList()).getBody(); + LobbyData lobbyData = restTemplate.getForObject("/data", LobbyData.class); boolean joinedGame = false; - for (Game game : games) { + for (Game game : lobbyData.getGames()) { if (game.getPlayerTwo() == null) { log.debug("Joining game..."); stompTemplate.getSession().send("/app/joinGame", game.getId()); @@ -440,8 +438,4 @@ public class MultiplayerPresenter implements FxmlController { } } - private class GameList extends ParameterizedTypeReference> { - // intentionally left empty - } - } diff --git a/common/src/main/java/com/charego/lingo/common/LobbyData.java b/common/src/main/java/com/charego/lingo/common/LobbyData.java new file mode 100644 index 0000000..06783f6 --- /dev/null +++ b/common/src/main/java/com/charego/lingo/common/LobbyData.java @@ -0,0 +1,35 @@ +package com.charego.lingo.common; + +import java.util.Collection; + +public class LobbyData { + + private Collection games; + + private Collection players; + + public LobbyData() { + // Empty constructor required for serialization + } + + public LobbyData(Collection games, Collection players) { + this.games = games; + this.players = players; + } + + public Collection getGames() { + return games; + } + + public Collection getPlayers() { + return players; + } + + public void setGames(Collection games) { + this.games = games; + } + + public void setPlayers(Collection players) { + this.players = players; + } +} diff --git a/server/src/main/java/com/charego/lingo/server/LingoController.java b/server/src/main/java/com/charego/lingo/server/LingoController.java index c5075c6..31d3076 100644 --- a/server/src/main/java/com/charego/lingo/server/LingoController.java +++ b/server/src/main/java/com/charego/lingo/server/LingoController.java @@ -2,7 +2,6 @@ package com.charego.lingo.server; import static org.springframework.messaging.simp.SimpMessageHeaderAccessor.SESSION_ID_HEADER; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -11,6 +10,8 @@ import java.util.TreeMap; import javax.annotation.PostConstruct; +import com.charego.lingo.api.Destinations; +import com.charego.lingo.common.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -24,14 +25,6 @@ import org.springframework.messaging.simp.annotation.SubscribeMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.charego.lingo.api.Destinations; -import com.charego.lingo.common.ChatMessage; -import com.charego.lingo.common.Game; -import com.charego.lingo.common.GameLeftMessage; -import com.charego.lingo.common.Player; -import com.charego.lingo.common.Report; -import com.charego.lingo.common.SetUsernameMessage; - @RestController public class LingoController { @@ -63,9 +56,9 @@ public class LingoController { return new ChatMessage(player.getUsername(), message); } - @RequestMapping("/games") - public Collection getGames() { - return gameById.values(); + @RequestMapping("/data") + public LobbyData getLobbyData() { + return new LobbyData(gameById.values(), sessionManager.getPlayers()); } @MessageMapping("/guess") @@ -179,14 +172,13 @@ public class LingoController { final String username = player.getUsername(); usernames.remove(username); final Game game = gameByPlayer.remove(player); - if (game == null) { - if (username != null) { - log.info("{} left", username); - send(Destinations.CHAT, new ChatMessage(null, username + " left")); - } - } else { + if (game != null) { leaveGame(game, player); } + if (username != null) { + log.info("{} left", username); + send(Destinations.PLAYER_LEFT, username); + } } @MessageMapping("/leaveGame") @@ -256,7 +248,7 @@ public class LingoController { player.setUsername(username); log.info("{} --> {}", sessionId, username); sendToPlayer(player, Destinations.SESSION_USERNAME, new SetUsernameMessage(true, username, null)); - send(Destinations.USER_JOINED, new Object[] { username, sessionManager.getPlayerCount() }); + send(Destinations.PLAYER_JOINED, new Object[] { username, sessionManager.getPlayerCount() }); } else { log.warn("{} -/> {} : Username taken", sessionId, username); final SetUsernameMessage response = new SetUsernameMessage(false, null, "Username taken"); diff --git a/server/src/main/java/com/charego/lingo/server/SessionManager.java b/server/src/main/java/com/charego/lingo/server/SessionManager.java index bbe01a7..00bed80 100644 --- a/server/src/main/java/com/charego/lingo/server/SessionManager.java +++ b/server/src/main/java/com/charego/lingo/server/SessionManager.java @@ -1,5 +1,6 @@ package com.charego.lingo.server; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -44,6 +45,12 @@ public class SessionManager implements ApplicationListener getPlayers() { + Collection allPlayers = new HashSet<>(playerBySession.values()); + allPlayers.removeIf(p -> p.getUsername() == null); + return allPlayers; + } + @Override public void onApplicationEvent(AbstractSubProtocolEvent event) { if (event instanceof SessionConnectedEvent) { diff --git a/server/src/main/resources/static/client.js b/server/src/main/resources/static/client.js index e5960c4..5d28036 100644 --- a/server/src/main/resources/static/client.js +++ b/server/src/main/resources/static/client.js @@ -10,6 +10,7 @@ var sessionId = null; var vm = new Vue({ el: '#vue-app', data: { + players: [], games: [], messages: [], username: null, @@ -197,6 +198,16 @@ var vm = new Vue({ } this.games.splice(indexToRemove, 1); }, + removePlayer: function(name) { + var indexToRemove = null; + for (var i = 0; i < this.players.length; i++) { + if (this.players[i].username === name) { + indexToRemove = i; + break; + } + } + this.players.splice(indexToRemove, 1); + }, onCanvasKeydown: function(e) { if (e.key === 'Backspace') { e.preventDefault(); @@ -362,7 +373,7 @@ function main() { } if (usernameSubscription === null) { usernameSubscription = client.subscribe(usernameTopic, usernameHandler); - client.subscribe('/topic/userJoined', onUserJoined); + client.subscribe('/topic/playerJoined', onPlayerJoined); } client.publish({destination: '/app/setUsername', body: usernameValue}) } @@ -398,7 +409,9 @@ function start() { } // Load initial data - doHttpGet('/games', function(games) { + doHttpGet('/data', function(data) { + var players = data.players; + var games = data.games; for (var i = 0; i < games.length; i++) { var game = games[i]; vm.games.push({ @@ -409,6 +422,12 @@ function start() { started: game.playerTwo !== null }); } + for (var i = 0; i < players.length; i++) { + var player = players[i]; + vm.players.push({ + username: player.username + }); + } }); // Subscribe to updates @@ -417,6 +436,7 @@ function start() { client.subscribe('/topic/gameHosted', onGameHosted); client.subscribe('/topic/gameJoined', onGameJoined); client.subscribe('/topic/gameLeft', onGameLeft); + client.subscribe('/topic/playerLeft', onPlayerLeft); client.subscribe('/user/topic/opponentJoined', onOpponentJoined); client.subscribe('/user/topic/opponentReports', onOpponentReport); client.subscribe('/user/topic/playerReports', onPlayerReport); @@ -511,9 +531,7 @@ function onGameJoined(message) { var playerOne = game.playerOne.username; var playerTwo = game.playerTwo.username; - var chatMessage = playerTwo + ' joined ' + playerOne + "'s game" - console.log(chatMessage); - addChatAnnouncement(chatMessage); + console.log(playerTwo + ' joined ' + playerOne + "'s game"); var vueGame = null; for (var i = 0; i < vm.games.length; i++) { @@ -614,7 +632,7 @@ function onPlayerReport(message) { } } -function onUserJoined(message) { +function onPlayerJoined(message) { var report = JSON.parse(message.body); var username = report[0]; var numUsers = report[1]; @@ -627,9 +645,18 @@ function onUserJoined(message) { } } else { addChatAnnouncement(username + ' joined'); + vm.players.push({ + username: username + }); } } +function onPlayerLeft(message) { + var username = message.body; + addChatAnnouncement(username + ' left'); + vm.removePlayer(username); +} + function canShowNotification() { if (document.hidden === 'undefined' || document.hidden === false) { return false; diff --git a/server/src/main/resources/static/index.html b/server/src/main/resources/static/index.html index df823c6..a2bcda2 100644 --- a/server/src/main/resources/static/index.html +++ b/server/src/main/resources/static/index.html @@ -40,6 +40,18 @@ +
+
+

Players

+
+
+
There are no players
+ +
+
diff --git a/server/src/main/resources/static/style.css b/server/src/main/resources/static/style.css index b47072c..3f65f53 100644 --- a/server/src/main/resources/static/style.css +++ b/server/src/main/resources/static/style.css @@ -125,6 +125,11 @@ button:hover:disabled { border: 1px solid #ddd; } +.list-group-item.user { + background-color: steelblue; + color: white; +} + button.list-group-item { width: 100%; text-align: left;