Add player list to game lobby
This commit is contained in:
parent
38a36aaa13
commit
3d2fb445ed
@ -16,6 +16,10 @@ public class Destinations {
|
|||||||
|
|
||||||
public static final String OPPONENT_REPORTS = topicDestination("opponentReports");
|
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 PLAYER_REPORTS = topicDestination("playerReports");
|
||||||
|
|
||||||
public static final String PRACTICE_GAME = topicDestination("practiceGame");
|
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 SESSION_USERNAME = topicDestination("sessionUsername");
|
||||||
|
|
||||||
public static final String USER_JOINED = topicDestination("userJoined");
|
|
||||||
|
|
||||||
private static String topicDestination(String suffix) {
|
private static String topicDestination(String suffix) {
|
||||||
return "/topic/" + suffix;
|
return "/topic/" + suffix;
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,17 @@ package com.charego.lingo.client.multiplayer;
|
|||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
|
import com.charego.lingo.common.LobbyData;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
|
||||||
import org.springframework.core.task.TaskExecutor;
|
import org.springframework.core.task.TaskExecutor;
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
import org.springframework.messaging.simp.stomp.StompFrameHandler;
|
import org.springframework.messaging.simp.stomp.StompFrameHandler;
|
||||||
import org.springframework.messaging.simp.stomp.StompHeaders;
|
import org.springframework.messaging.simp.stomp.StompHeaders;
|
||||||
import org.springframework.messaging.simp.stomp.StompSession.Subscription;
|
import org.springframework.messaging.simp.stomp.StompSession.Subscription;
|
||||||
@ -142,9 +140,9 @@ public class MultiplayerPresenter implements FxmlController {
|
|||||||
username = UUID.randomUUID().toString().substring(0, 8);
|
username = UUID.randomUUID().toString().substring(0, 8);
|
||||||
stompTemplate.getSession().send("/app/setUsername", username);
|
stompTemplate.getSession().send("/app/setUsername", username);
|
||||||
|
|
||||||
Collection<Game> games = restTemplate.exchange("/games", HttpMethod.GET, null, new GameList()).getBody();
|
LobbyData lobbyData = restTemplate.getForObject("/data", LobbyData.class);
|
||||||
boolean joinedGame = false;
|
boolean joinedGame = false;
|
||||||
for (Game game : games) {
|
for (Game game : lobbyData.getGames()) {
|
||||||
if (game.getPlayerTwo() == null) {
|
if (game.getPlayerTwo() == null) {
|
||||||
log.debug("Joining game...");
|
log.debug("Joining game...");
|
||||||
stompTemplate.getSession().send("/app/joinGame", game.getId());
|
stompTemplate.getSession().send("/app/joinGame", game.getId());
|
||||||
@ -440,8 +438,4 @@ public class MultiplayerPresenter implements FxmlController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class GameList extends ParameterizedTypeReference<Collection<Game>> {
|
|
||||||
// intentionally left empty
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
35
common/src/main/java/com/charego/lingo/common/LobbyData.java
Normal file
35
common/src/main/java/com/charego/lingo/common/LobbyData.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package com.charego.lingo.common;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class LobbyData {
|
||||||
|
|
||||||
|
private Collection<Game> games;
|
||||||
|
|
||||||
|
private Collection<Player> players;
|
||||||
|
|
||||||
|
public LobbyData() {
|
||||||
|
// Empty constructor required for serialization
|
||||||
|
}
|
||||||
|
|
||||||
|
public LobbyData(Collection<Game> games, Collection<Player> players) {
|
||||||
|
this.games = games;
|
||||||
|
this.players = players;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Game> getGames() {
|
||||||
|
return games;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Player> getPlayers() {
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGames(Collection<Game> games) {
|
||||||
|
this.games = games;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlayers(Collection<Player> players) {
|
||||||
|
this.players = players;
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,6 @@ package com.charego.lingo.server;
|
|||||||
|
|
||||||
import static org.springframework.messaging.simp.SimpMessageHeaderAccessor.SESSION_ID_HEADER;
|
import static org.springframework.messaging.simp.SimpMessageHeaderAccessor.SESSION_ID_HEADER;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -11,6 +10,8 @@ import java.util.TreeMap;
|
|||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
|
import com.charego.lingo.api.Destinations;
|
||||||
|
import com.charego.lingo.common.*;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
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
|
@RestController
|
||||||
public class LingoController {
|
public class LingoController {
|
||||||
|
|
||||||
@ -63,9 +56,9 @@ public class LingoController {
|
|||||||
return new ChatMessage(player.getUsername(), message);
|
return new ChatMessage(player.getUsername(), message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping("/games")
|
@RequestMapping("/data")
|
||||||
public Collection<Game> getGames() {
|
public LobbyData getLobbyData() {
|
||||||
return gameById.values();
|
return new LobbyData(gameById.values(), sessionManager.getPlayers());
|
||||||
}
|
}
|
||||||
|
|
||||||
@MessageMapping("/guess")
|
@MessageMapping("/guess")
|
||||||
@ -179,14 +172,13 @@ public class LingoController {
|
|||||||
final String username = player.getUsername();
|
final String username = player.getUsername();
|
||||||
usernames.remove(username);
|
usernames.remove(username);
|
||||||
final Game game = gameByPlayer.remove(player);
|
final Game game = gameByPlayer.remove(player);
|
||||||
if (game == null) {
|
if (game != null) {
|
||||||
if (username != null) {
|
|
||||||
log.info("{} left", username);
|
|
||||||
send(Destinations.CHAT, new ChatMessage(null, username + " left"));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
leaveGame(game, player);
|
leaveGame(game, player);
|
||||||
}
|
}
|
||||||
|
if (username != null) {
|
||||||
|
log.info("{} left", username);
|
||||||
|
send(Destinations.PLAYER_LEFT, username);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MessageMapping("/leaveGame")
|
@MessageMapping("/leaveGame")
|
||||||
@ -256,7 +248,7 @@ public class LingoController {
|
|||||||
player.setUsername(username);
|
player.setUsername(username);
|
||||||
log.info("{} --> {}", sessionId, username);
|
log.info("{} --> {}", sessionId, username);
|
||||||
sendToPlayer(player, Destinations.SESSION_USERNAME, new SetUsernameMessage(true, username, null));
|
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 {
|
} else {
|
||||||
log.warn("{} -/> {} : Username taken", sessionId, username);
|
log.warn("{} -/> {} : Username taken", sessionId, username);
|
||||||
final SetUsernameMessage response = new SetUsernameMessage(false, null, "Username taken");
|
final SetUsernameMessage response = new SetUsernameMessage(false, null, "Username taken");
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.charego.lingo.server;
|
package com.charego.lingo.server;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -44,6 +45,12 @@ public class SessionManager implements ApplicationListener<AbstractSubProtocolEv
|
|||||||
return playerBySession.size();
|
return playerBySession.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<Player> getPlayers() {
|
||||||
|
Collection<Player> allPlayers = new HashSet<>(playerBySession.values());
|
||||||
|
allPlayers.removeIf(p -> p.getUsername() == null);
|
||||||
|
return allPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApplicationEvent(AbstractSubProtocolEvent event) {
|
public void onApplicationEvent(AbstractSubProtocolEvent event) {
|
||||||
if (event instanceof SessionConnectedEvent) {
|
if (event instanceof SessionConnectedEvent) {
|
||||||
|
@ -10,6 +10,7 @@ var sessionId = null;
|
|||||||
var vm = new Vue({
|
var vm = new Vue({
|
||||||
el: '#vue-app',
|
el: '#vue-app',
|
||||||
data: {
|
data: {
|
||||||
|
players: [],
|
||||||
games: [],
|
games: [],
|
||||||
messages: [],
|
messages: [],
|
||||||
username: null,
|
username: null,
|
||||||
@ -197,6 +198,16 @@ var vm = new Vue({
|
|||||||
}
|
}
|
||||||
this.games.splice(indexToRemove, 1);
|
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) {
|
onCanvasKeydown: function(e) {
|
||||||
if (e.key === 'Backspace') {
|
if (e.key === 'Backspace') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -362,7 +373,7 @@ function main() {
|
|||||||
}
|
}
|
||||||
if (usernameSubscription === null) {
|
if (usernameSubscription === null) {
|
||||||
usernameSubscription = client.subscribe(usernameTopic, usernameHandler);
|
usernameSubscription = client.subscribe(usernameTopic, usernameHandler);
|
||||||
client.subscribe('/topic/userJoined', onUserJoined);
|
client.subscribe('/topic/playerJoined', onPlayerJoined);
|
||||||
}
|
}
|
||||||
client.publish({destination: '/app/setUsername', body: usernameValue})
|
client.publish({destination: '/app/setUsername', body: usernameValue})
|
||||||
}
|
}
|
||||||
@ -398,7 +409,9 @@ function start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load initial data
|
// 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++) {
|
for (var i = 0; i < games.length; i++) {
|
||||||
var game = games[i];
|
var game = games[i];
|
||||||
vm.games.push({
|
vm.games.push({
|
||||||
@ -409,6 +422,12 @@ function start() {
|
|||||||
started: game.playerTwo !== null
|
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
|
// Subscribe to updates
|
||||||
@ -417,6 +436,7 @@ function start() {
|
|||||||
client.subscribe('/topic/gameHosted', onGameHosted);
|
client.subscribe('/topic/gameHosted', onGameHosted);
|
||||||
client.subscribe('/topic/gameJoined', onGameJoined);
|
client.subscribe('/topic/gameJoined', onGameJoined);
|
||||||
client.subscribe('/topic/gameLeft', onGameLeft);
|
client.subscribe('/topic/gameLeft', onGameLeft);
|
||||||
|
client.subscribe('/topic/playerLeft', onPlayerLeft);
|
||||||
client.subscribe('/user/topic/opponentJoined', onOpponentJoined);
|
client.subscribe('/user/topic/opponentJoined', onOpponentJoined);
|
||||||
client.subscribe('/user/topic/opponentReports', onOpponentReport);
|
client.subscribe('/user/topic/opponentReports', onOpponentReport);
|
||||||
client.subscribe('/user/topic/playerReports', onPlayerReport);
|
client.subscribe('/user/topic/playerReports', onPlayerReport);
|
||||||
@ -511,9 +531,7 @@ function onGameJoined(message) {
|
|||||||
var playerOne = game.playerOne.username;
|
var playerOne = game.playerOne.username;
|
||||||
var playerTwo = game.playerTwo.username;
|
var playerTwo = game.playerTwo.username;
|
||||||
|
|
||||||
var chatMessage = playerTwo + ' joined ' + playerOne + "'s game"
|
console.log(playerTwo + ' joined ' + playerOne + "'s game");
|
||||||
console.log(chatMessage);
|
|
||||||
addChatAnnouncement(chatMessage);
|
|
||||||
|
|
||||||
var vueGame = null;
|
var vueGame = null;
|
||||||
for (var i = 0; i < vm.games.length; i++) {
|
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 report = JSON.parse(message.body);
|
||||||
var username = report[0];
|
var username = report[0];
|
||||||
var numUsers = report[1];
|
var numUsers = report[1];
|
||||||
@ -627,9 +645,18 @@ function onUserJoined(message) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addChatAnnouncement(username + ' joined');
|
addChatAnnouncement(username + ' joined');
|
||||||
|
vm.players.push({
|
||||||
|
username: username
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onPlayerLeft(message) {
|
||||||
|
var username = message.body;
|
||||||
|
addChatAnnouncement(username + ' left');
|
||||||
|
vm.removePlayer(username);
|
||||||
|
}
|
||||||
|
|
||||||
function canShowNotification() {
|
function canShowNotification() {
|
||||||
if (document.hidden === 'undefined' || document.hidden === false) {
|
if (document.hidden === 'undefined' || document.hidden === false) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -40,6 +40,18 @@
|
|||||||
<button v-show="inGame" @click="leaveGame" type="button" class="leave button">Leave game</button>
|
<button v-show="inGame" @click="leaveGame" type="button" class="leave button">Leave game</button>
|
||||||
<button v-show="!inGame" @click="hostGame5" type="button" class="create button">Create 5-letter game</button>
|
<button v-show="!inGame" @click="hostGame5" type="button" class="create button">Create 5-letter game</button>
|
||||||
<button v-show="!inGame" @click="hostGame6" type="button" class="create button">Create 6-letter game</button>
|
<button v-show="!inGame" @click="hostGame6" type="button" class="create button">Create 6-letter game</button>
|
||||||
|
<div class="panel">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Players</h3>
|
||||||
|
</div>
|
||||||
|
<div class="list-group">
|
||||||
|
<div v-if="players.length === 0" class="list-group-item">There are no players</div>
|
||||||
|
<template v-for="player in players">
|
||||||
|
<div class="list-group-item user" v-if="player.username === username">{{ player.username }}</div>
|
||||||
|
<div class="list-group-item" v-else>{{ player.username }}</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-bind:class="{ primary: inStartedGame }" class="game column">
|
<div v-bind:class="{ primary: inStartedGame }" class="game column">
|
||||||
|
@ -125,6 +125,11 @@ button:hover:disabled {
|
|||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-group-item.user {
|
||||||
|
background-color: steelblue;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
button.list-group-item {
|
button.list-group-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user