From d389a8fbb0ea57af141a081d05a04a58ff29f7da Mon Sep 17 00:00:00 2001 From: Seraina Date: Fri, 15 Apr 2022 23:22:38 +0200 Subject: [PATCH] Removed some ToDos that are done and started creating a Spectator class and I'm in the process of ensuring only non kicked off passengers vote --- .../dbis/cs108/gamelogic/ClientVoteData.java | 8 ++- .../unibas/dmi/dbis/cs108/gamelogic/Game.java | 8 +-- .../dmi/dbis/cs108/gamelogic/GameState.java | 4 +- .../dbis/cs108/gamelogic/GhostifyHandler.java | 23 +++++++- .../gamelogic/ServerGameInfoHandler.java | 25 +++++++-- .../dmi/dbis/cs108/gamelogic/Train.java | 2 +- .../dmi/dbis/cs108/gamelogic/VoteHandler.java | 56 ++++++++++++++----- .../gamelogic/klassenstruktur/Passenger.java | 10 +++- .../gamelogic/klassenstruktur/Spectator.java | 29 ++++++++++ .../client/JClientProtocolParser.java | 2 - .../multiplayer/server/ClientHandler.java | 9 +-- .../dbis/cs108/multiplayer/server/Lobby.java | 2 +- 12 files changed, 137 insertions(+), 41 deletions(-) create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/klassenstruktur/Spectator.java diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/ClientVoteData.java b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/ClientVoteData.java index ebf95ba..1471e20 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/ClientVoteData.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/ClientVoteData.java @@ -44,7 +44,7 @@ public class ClientVoteData { try { this.vote[position] = vote; } catch (IndexOutOfBoundsException e) { - LOGGER.info("Position is: " + position); + LOGGER.warn("Position is: " + position); } } @@ -54,6 +54,10 @@ public class ClientVoteData { * @param hasVoted the vote state value */ public void setHasVoted(int position, boolean hasVoted) { - this.hasVoted[position] = hasVoted; + try { + this.hasVoted[position] = hasVoted; + } catch (Exception e) { + LOGGER.warn("Position is:" + position); + } } } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/Game.java b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/Game.java index f412902..84f1062 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/Game.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/Game.java @@ -26,7 +26,6 @@ public class Game implements Runnable { private Lobby lobby; private String name; private static int nameCounter = 0; - //TODO: Figure out where Day/Night game state is saved maybe think about a game state class or smt. /** * Constructs a Game instance where: * @@ -35,7 +34,7 @@ public class Game implements Runnable { * @param nrOfUsers is the number of active users at the time (non NPCs) */ public Game(int nrOfPlayers, int nrOfGhosts, int nrOfUsers, Lobby lobby) - throws TrainOverflow { //ToDo: Who handles Exception how and where + throws TrainOverflow { this.gameState = new GameState(nrOfPlayers, nrOfGhosts, nrOfUsers); this.lobby = lobby; nameCounter++; @@ -63,7 +62,6 @@ public class Game implements Runnable { * currently at gameState.train[3] fills the passengerTrain moving from left to rigth in the * gameState.train array it connects clientHandlers witch the passengers in those positions * (Players) and fills the rest with NPC's - * TODO: set ghost in a random position(i), gameState.train[i] so that a lone player can also start as a Ghost maybe use Train class */ @Override public void run() { @@ -76,7 +74,7 @@ public class Game implements Runnable { LOGGER.info(gameState.toString()); - for (ClientHandler client : lobbyClients) { //TODO(Seraina): Adjust for lobbies + for (ClientHandler client : lobbyClients) { int index = order[i]; if (passengerTrain[index].getIsGhost()) { //if there is a ghost GhostPlayer ghostPlayer = new GhostPlayer(passengerTrain[index].getPosition(), @@ -104,7 +102,7 @@ public class Game implements Runnable { LOGGER.info(gameState.toString()); i = 0; - while (true) { //ToDo: was ist die Abbruchbedingung? VoteHandler muss das schicken. + while (true) { if (!isDay) { LOGGER.info("NIGHT"); voteHandler.ghostVote(gameState.getPassengerTrain(), this); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/GameState.java b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/GameState.java index c43796c..fceb845 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/GameState.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/GameState.java @@ -44,7 +44,7 @@ public class GameState { * @throws TrainOverflow if nrOfPlayers < nrOfUsers */ GameState(int nrOfPlayers, int nrOfGhosts, int nrOfUsers) - throws TrainOverflow { //ToDo: where will Exception be handled? + throws TrainOverflow { this.nrOfPlayers = nrOfPlayers; this.nrOfGhosts = nrOfGhosts; this.nrOfUsers = nrOfUsers; @@ -52,7 +52,7 @@ public class GameState { clientVoteData = new ClientVoteData(); Passenger[] passengerTrain = new Passenger[nrOfPlayers]; //Creates an array with Passengers with correlation positions (Train) for (int i = 0; i < nrOfPlayers; i++) { - if (i == train.getPositionOfGhost()) { //TODO: randomize via Train.ghostposition + if (i == train.getPositionOfGhost()) { LOGGER.info("OG position: " + train.getOrderOfTrain()[i]); Ghost g = new Ghost(); g.setPosition(train.getOrderOfTrain()[i]); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/GhostifyHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/GhostifyHandler.java index d0539b0..3dec67c 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/GhostifyHandler.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/GhostifyHandler.java @@ -5,6 +5,7 @@ import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Ghost; import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.GhostNPC; import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.GhostPlayer; import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Passenger; +import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Spectator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -18,7 +19,7 @@ public class GhostifyHandler { * @param p Passenger to be ghostified */ - public Ghost ghost(Passenger p, Game game) { //TODO: Adjust for not only players but also npcs + public static Ghost ghost(Passenger p, Game game) { LOGGER.debug("Passenger Position " + p.getPosition()); Ghost g; if (p.getIsPlayer()) { @@ -41,4 +42,24 @@ public class GhostifyHandler { LOGGER.info("Passenger at position " + p.getPosition() + " has been ghostified"); return g; } + + /** + * Handles a Kick Off event. If the passenger to be kicked off is a Player, the Player will be converted + * into a Spectator to watch the game. + * @param passenger to be kicked off the train + * @param game the game the train and passenger are in + * @return either a new spectator or the oly passenger now with kickeckedOff true + */ + public static Passenger kickOff (Passenger passenger, Game game) { + if (passenger.getIsPlayer()) { + Spectator spectator = new Spectator(passenger.getPosition(), passenger.getName()); + spectator.setClientHandler(passenger.getClientHandler()); + spectator.setPlayer(true); + game.gameState.addNewPassenger(spectator, passenger.getPosition()); + return spectator; + } else { + passenger.setKickedOff(true); + return passenger; + } + } } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/ServerGameInfoHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/ServerGameInfoHandler.java index 9189bdf..1406475 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/ServerGameInfoHandler.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/ServerGameInfoHandler.java @@ -14,7 +14,6 @@ import org.apache.logging.log4j.Logger; * Handles all communications Server to Client concerning game state or game state related requests * - Needs a possibility to only send to Ghosts * - and only humans - * TODO: Figure out what format the messages have, consider protocol add what is needed dont forget about parsing */ public class ServerGameInfoHandler { @@ -24,15 +23,17 @@ public class ServerGameInfoHandler { /** * Gets a string msg from somewhere and formats it into protocol messages * @param msg the message to be formatted + * @param passenger the passenger getting the message + * @param game the game in wich the passenger lives * @return a message in a protocol format */ - public static String format(String msg, Passenger p, Game game) { + public static String format(String msg, Passenger passenger, Game game) { switch (msg) { case ClientGameInfoHandler.ghostVoteRequest: - msg = Protocol.serverRequestsGhostVote + "$" + p.getPosition() +"$" + game.gameState.toString(); + msg = Protocol.serverRequestsGhostVote + "$" + passenger.getPosition() +"$" + game.gameState.toString(); break; case ClientGameInfoHandler.humanVoteRequest: - msg = Protocol.serverRequestsHumanVote + "$" + p.getPosition() +"$"+ game.gameState.humanToString(); + msg = Protocol.serverRequestsHumanVote + "$" + passenger.getPosition() +"$"+ game.gameState.humanToString(); break; default: msg = Protocol.printToClientConsole + "$"+ msg; @@ -41,6 +42,18 @@ public class ServerGameInfoHandler { return msg; } + /** + * //TODO(Seraina): implementation + * Formartiert Nachrichten die für einen Spectator gedacht sind. + * @param msg the message to be formatted + * @param passenger the passenger getting the message + * @param game the game in wich the passenger lives + * @return a message in a protocol format + */ + public static String spectatorFormat(String msg, Passenger passenger, Game game) { + return msg; + } + /** * Chooses for an NPC what they want to say, so they don't sound the same all the time * @return a String saying that sm heard sm noise @@ -50,8 +63,8 @@ public class ServerGameInfoHandler { String b = "noise"; String c = "I heard smt strange tonight"; String d = "Me, noise!"; - String e = "Uuuuh, spoky noises"; - int number = (int)(Math.random()*4); + String e = "Uuuuh, spoky sounds"; + int number = (int)(Math.random()*5); switch (number) { case 0: return a; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/Train.java b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/Train.java index b7125be..10212b5 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/Train.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/Train.java @@ -22,7 +22,7 @@ public class Train { Train(int nrOfPlayers, int nrOfUsers) throws TrainOverflow { if (nrOfPlayers < nrOfUsers) { - throw new TrainOverflow(); //ToDo: What kind of Exception must be thrown here and who handles it how? + throw new TrainOverflow(); } int[] userTrain = new int[nrOfPlayers]; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/VoteHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/VoteHandler.java index be7ab99..d19ae97 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/VoteHandler.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/VoteHandler.java @@ -15,7 +15,7 @@ import org.apache.logging.log4j.Logger; * *

(All messages going to Clients are handled via ServerGameInfoHandler) * - *

TODO: Think about if the timer needs to be implemented here or in the Game class + *

*/ public class VoteHandler { public static final Logger LOGGER = LogManager.getLogger(VoteHandler.class); @@ -57,7 +57,7 @@ public class VoteHandler { LOGGER.warn("Thread " + Thread.currentThread() + " was interrupted"); } - int currentMax = voteEvaluation(passengers, votesForPlayers, game.getGameState().getClientVoteData(), game); + int currentMax = ghostVoteEvaluation(passengers, votesForPlayers, game.getGameState().getClientVoteData(), game); LOGGER.debug("Most votes: " + currentMax + " vote"); @@ -71,11 +71,11 @@ public class VoteHandler { } } LOGGER.info("Most votes for: " + ghostPosition); - GhostifyHandler gh = new GhostifyHandler(); - Ghost g = gh.ghost(passengers[ghostPosition], game); + + Ghost g = GhostifyHandler.ghost(passengers[ghostPosition], game); passengers[ghostPosition] = g; passengers[ghostPosition].send( - ClientGameInfoHandler.youGotGhostyfied, game); // TODO: ServerGameInfoHandler might deal with this one + ClientGameInfoHandler.youGotGhostyfied, game); try { // waits 20 seconds before votes get collected Thread.sleep(10); } catch (InterruptedException e) { @@ -127,7 +127,6 @@ public class VoteHandler { int[] votesForPlayers = new int[6]; // Walk through entire train, ask humans to vote and ghosts to wait - // TODO: Messages in for-loop should probably be handled by ServerGameInfoHandler for (Passenger passenger : passengers) { if (passenger.getIsGhost()) { passenger.send(ClientGameInfoHandler.itsDayTime, game); @@ -142,7 +141,7 @@ public class VoteHandler { LOGGER.warn("Thread " + Thread.currentThread() + " was interrupted"); } - int currentMax = voteEvaluation(passengers, votesForPlayers, game.getGameState().getClientVoteData(), game); + int currentMax = humanVoteEvaluation(passengers, votesForPlayers, game.getGameState().getClientVoteData(), game); // deal with voting results int voteIndex = 0; @@ -156,12 +155,12 @@ public class VoteHandler { .getIsGhost()) { // if player with most votes is human, notify everyone about it for (Passenger passenger : passengers) { passenger.send( - ClientGameInfoHandler.humansVotedFor + voteIndex + ClientGameInfoHandler.isAHuman, game); // TODO: ServerGameInfoHandler might be better to use here + ClientGameInfoHandler.humansVotedFor + voteIndex + ClientGameInfoHandler.isAHuman, game); } } if (passengers[voteIndex].getIsGhost()) { // if player is a ghost if (passengers[voteIndex].getIsOG()) { // if ghost is OG --> end game, humans win - System.out.println(ClientGameInfoHandler.gameOverHumansWin); // TODO: correctly handle end of game + System.out.println(ClientGameInfoHandler.gameOverHumansWin); return ClientGameInfoHandler.gameOverHumansWin; } else { /* Special case: if ghost is not OG and if only one human is left (--> last human didn't vote for OG ghost), @@ -179,7 +178,7 @@ public class VoteHandler { } // Usual case: there is more than one human left and a normal ghost has been voted for --> // kick this ghost off - passengers[voteIndex].setKickedOff(true); + passengers[voteIndex] = GhostifyHandler.kickOff(passengers[voteIndex], game); for (Passenger passenger : passengers) { passenger.send("Player " + voteIndex + ClientGameInfoHandler.gotKickedOff, game); } @@ -220,7 +219,7 @@ public class VoteHandler { } /** - * Collecting the votes - distribute them among the vote counters for all players. Note: each voting collects + * Collecting the votes of Ghosts- distribute them among the vote counters for all players. Note: each voting collects * votes for all players even though some might not be concerned (i.e. ghosts during ghost vote). Those players * will then get 0 votes so it dosen't matter. Returns the max amount of votes a player received. * @param passengers train passengers @@ -228,10 +227,41 @@ public class VoteHandler { * @param data deals with Client votes * @param game current game instance */ - int voteEvaluation(Passenger[] passengers, int[] votesForPlayers, ClientVoteData data, Game game) { + int ghostVoteEvaluation(Passenger[] passengers, int[] votesForPlayers, ClientVoteData data, Game game) { for (Passenger passenger : passengers) { passenger.getVoteFromGameState(data, game); - if (passenger.getHasVoted()) { + if (passenger.getHasVoted() && passenger.getIsGhost() && !passenger.getKickedOff()) { + for (int i = 0; i < votesForPlayers.length; i++) { + if (passenger.getVote() == i) { + votesForPlayers[i]++; + } + } + } + } + /* count the votes - determine which player has the most votes by going through the + votesForPlayers array */ + int currentMax = 0; + for (int votesForPlayer : votesForPlayers) { + if (votesForPlayer > currentMax) { + currentMax = votesForPlayer; + } + } + return currentMax; + } + + /** + * Collecting the votes of Humans - distribute them among the vote counters for all players. Note: each voting collects + * votes for all players even though some might not be concerned (i.e. ghosts during ghost vote). Those players + * will then get 0 votes so it dosen't matter. Returns the max amount of votes a player received. + * @param passengers train passengers + * @param votesForPlayers array collecting the votes each player received during a voting + * @param data deals with Client votes + * @param game current game instance + */ + int humanVoteEvaluation(Passenger[] passengers, int[] votesForPlayers, ClientVoteData data, Game game) { + for (Passenger passenger : passengers) { + passenger.getVoteFromGameState(data, game); + if (passenger.getHasVoted() && !passenger.getIsGhost() && !passenger.getKickedOff()) { for (int i = 0; i < votesForPlayers.length; i++) { if (passenger.getVote() == i) { votesForPlayers[i]++; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/klassenstruktur/Passenger.java b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/klassenstruktur/Passenger.java index 8e4508f..03b0524 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/klassenstruktur/Passenger.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/klassenstruktur/Passenger.java @@ -17,7 +17,7 @@ public class Passenger { protected boolean isGhost; //boolean regarding if the player is a ghost. Could probably be removed since ghost is a subclass but I'm keeping it in. protected boolean isOG = false; //true if the player is the original ghost, false by default. protected boolean isPlayer; //same here - protected boolean kickedOff; //true if the player has been voted off. + protected boolean kickedOff; //true if the player has been voted off protected ClientHandler clientHandler;//the socket for the client associated with this Passenger, for NPCs, this can be null. protected boolean hasVoted; //true if the player gave his vote during voting time protected int vote; //saves the number of the player this passenger voted for during voting (0-5) @@ -67,6 +67,14 @@ public class Passenger { isOG = true; } + public void setPlayer(boolean player) { + isPlayer = player; + } + + public void setClientHandler(ClientHandler clientHandler) { + this.clientHandler = clientHandler; + } + public int getPosition() { return position; } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/klassenstruktur/Spectator.java b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/klassenstruktur/Spectator.java new file mode 100644 index 0000000..e48ec4e --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/gamelogic/klassenstruktur/Spectator.java @@ -0,0 +1,29 @@ +package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur; + +import ch.unibas.dmi.dbis.cs108.BudaLogConfig; +import ch.unibas.dmi.dbis.cs108.gamelogic.Game; +import ch.unibas.dmi.dbis.cs108.gamelogic.ServerGameInfoHandler; +import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * A class for all Players that have been kicked off, where they can observe everything + */ +public class Spectator extends Passenger{ + public static final Logger LOGGER = LogManager.getLogger(Spectator.class); + public static final BudaLogConfig l = new BudaLogConfig(LOGGER); + + public Spectator(int position, String name) { + this.position = position; + this.name = name; + isGhost = false; + isPlayer = true; + kickedOff = true; + } + + @Override + public void send(String msg, Game game) { + clientHandler.sendMsgToClient(ServerGameInfoHandler.format(msg, this, game)); + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java index 63077ae..35d60e3 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java @@ -50,13 +50,11 @@ public class JClientProtocolParser { LOGGER.debug("Ghost received Vote request"); System.out.println("Ghost Vote:"); c.positionSetter(msg.substring(6)); - //TODO(Seraina): How can be enforced, that clients won't vote otherwise? Trigger a methode here that listens to input break; case Protocol.serverRequestsHumanVote: LOGGER.debug("Human received Vote request"); System.out.println("Human Vote:"); c.positionSetter(msg.substring(6)); - //TODO(Seraina): How can be enforced, that clients won't vote otherwise? Trigger a methode here that listens to input break; case Protocol.serverDeliversLobbyList: default: diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/ClientHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/ClientHandler.java index 9a69e26..5d09cbb 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/ClientHandler.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/ClientHandler.java @@ -77,11 +77,6 @@ public class ClientHandler implements Runnable { return socket; } - /** - * Needed to fill a train with client TODO: how do lobbies fit here? - * - * @return the HashSet of Connected Clients - */ public static HashSet getConnectedClients() { return connectedClients; } @@ -300,7 +295,7 @@ public class ClientHandler implements Runnable { if(vote != Integer.MAX_VALUE) { //gets MAX_VALUE when the vote wasn't valid getLobby().getGame().getGameState().getClientVoteData().setVote(position,vote); LOGGER.debug("Player vote: " + vote); - getLobby().getGame().getGameState().getClientVoteData().setHasVoted(position,true); //TODO: move clientVoteData to gamestate + getLobby().getGame().getGameState().getClientVoteData().setHasVoted(position,true); } } @@ -490,7 +485,7 @@ public class ClientHandler implements Runnable { } catch (Exception e) { sendAnnouncementToClient(" - No running Games"); } - sendAnnouncementToClient("Finished Games"); + sendAnnouncementToClient("Finished Games:"); try { for (Game finishedGame : Lobby.finishedGames) { sendAnnouncementToClient(" - " + finishedGame.getName() + ", Lobby" + finishedGame.getLobby().getLobbyID()); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/Lobby.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/Lobby.java index 815564a..1fb87d4 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/Lobby.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/Lobby.java @@ -159,7 +159,7 @@ public class Lobby { */ public synchronized boolean addPlayer(ClientHandler client) { if (lobbyClients.size() < MAX_NO_OF_CLIENTS) { - //todo: check that game hasn't started yet + //todo: check that game hasn't started yet (handled in cleintHandler) if (clientIsInLobby(client) == -1) { lobbyClients.add(client); ClientHandler.broadcastAnnouncementToAll(client.getClientUserName() + " has joined lobby " + this.getLobbyID());