Merge remote-tracking branch 'origin/master'
# Conflicts: # src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/Client.java # src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/helpers/Protocol.java # src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/ClientHandler.java # src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/JServerProtocolParser.java
This commit is contained in:
commit
d98ee434c8
@ -256,6 +256,10 @@ Stand 15:00 Uhr:
|
||||
Versuch meine commits wieder richtig zuorden zu können indem ich die user.name und user.email
|
||||
Variablen von git
|
||||
auf meinem Rechner neu configuriere.
|
||||
Stand 17:30 Uhr:
|
||||
Ich habe einen neuen Branch "BudaLobbySebastian" erstellt, worauf ich schaue wie und wo eigentlich eine "Lobby"
|
||||
lebt. LISTL wurde auch implementiert aber noch nicht getestet.
|
||||
|
||||
|
||||
ToDo:
|
||||
Spiellogik: - Send() methode von Passenger mit Client-Server verknüpfen(Seraina)
|
||||
|
||||
@ -5,14 +5,13 @@ import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.ClientPinger;
|
||||
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.multiplayer.server.JServerProtocolParser;
|
||||
import java.net.Socket;
|
||||
import java.io.*;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Objects;
|
||||
import java.util.Scanner;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@ -31,6 +30,7 @@ public class Client {
|
||||
*/
|
||||
int position = Integer.MAX_VALUE;
|
||||
|
||||
|
||||
public Client(Socket socket) {
|
||||
try {
|
||||
this.socket = socket;
|
||||
@ -120,6 +120,7 @@ public class Client {
|
||||
try {
|
||||
chatMsg = in.readLine(); //todo: maybe if
|
||||
if (chatMsg != null) {
|
||||
//LOGGER.debug("chatMSG recieved from Server: " + chatMsg);
|
||||
parse(chatMsg);
|
||||
} else { System.out.println("chatMsg is null"); throw new IOException();}
|
||||
} catch (IOException e) {
|
||||
@ -208,6 +209,7 @@ public class Client {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public Socket getSocket() {
|
||||
return socket;
|
||||
}
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
package ch.unibas.dmi.dbis.cs108.multiplayer.client;
|
||||
|
||||
public class Client_01 {
|
||||
public static void main(String[] args) {
|
||||
Client.main(args);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package ch.unibas.dmi.dbis.cs108.multiplayer.client;
|
||||
|
||||
public class Client_02 {
|
||||
public static void main(String[] args) {
|
||||
Client.main(args);
|
||||
}
|
||||
}
|
||||
@ -39,6 +39,10 @@ public class JClientProtocolParser {
|
||||
case Protocol.printToClientConsole:
|
||||
System.out.println(msg.substring(6));
|
||||
break;
|
||||
case Protocol.printToClientChat:
|
||||
//todo: handle chat separately from console.
|
||||
System.out.println(msg.substring(6));
|
||||
break;
|
||||
case Protocol.serverConfirmQuit:
|
||||
c.disconnectFromServer();
|
||||
break;
|
||||
@ -54,6 +58,7 @@ public class JClientProtocolParser {
|
||||
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:
|
||||
System.out.println("Received unknown command");
|
||||
}
|
||||
|
||||
@ -11,8 +11,8 @@ public class MessageFormatter {
|
||||
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
|
||||
|
||||
/**
|
||||
* Takes a given Message and reformats it to where the JServerProtocolParser.parse() method can
|
||||
* handle it (see Protocol.java). May need to be redesigned once the games uses a GUI
|
||||
* Takes a given client input and reformats it to where the JServerProtocolParser.parse() method can
|
||||
* handle it (see Protocol.java). May need to be redesigned once the game uses a GUI.
|
||||
*
|
||||
* @param msg the Messaged to be reformatted
|
||||
* @return the reformatted message in the form HEADR$msg
|
||||
@ -29,6 +29,14 @@ public class MessageFormatter {
|
||||
}
|
||||
switch (header) {
|
||||
case "/c":
|
||||
stringBuilder.append(Protocol.chatMsgToLobby + "$");
|
||||
try {
|
||||
s = msg.substring(3);
|
||||
} catch (Exception e) {
|
||||
System.out.println("You didn't even write a chat line, you silly billy!");
|
||||
}
|
||||
break;
|
||||
case "/b":
|
||||
stringBuilder.append(Protocol.chatMsgToAll + "$");
|
||||
try {
|
||||
s = msg.substring(3);
|
||||
@ -37,7 +45,7 @@ public class MessageFormatter {
|
||||
}
|
||||
break;
|
||||
case "/q":
|
||||
stringBuilder.append(Protocol.clientQuitRequest + "$");
|
||||
stringBuilder.append(Protocol.clientQuitRequest);
|
||||
s = "";
|
||||
break;
|
||||
case "/n":
|
||||
@ -49,15 +57,30 @@ public class MessageFormatter {
|
||||
}
|
||||
break;
|
||||
case "/g":
|
||||
//CRTGM command
|
||||
stringBuilder.append(Protocol.createNewGame + "$");
|
||||
s = ""; //command has no parameters
|
||||
//TODO add LOGGER msg. Find out if .info or .debug.
|
||||
stringBuilder.append(Protocol.createNewLobby);
|
||||
break;
|
||||
case "/l":
|
||||
//LISTL command
|
||||
stringBuilder.append(Protocol.listLobbies + "$");
|
||||
s = ""; //Command has no parameters
|
||||
stringBuilder.append(Protocol.listLobbies);
|
||||
break;
|
||||
case "/p":
|
||||
stringBuilder.append(Protocol.listPlayersInLobby);
|
||||
break;
|
||||
case "/j":
|
||||
stringBuilder.append(Protocol.joinLobby + "$");
|
||||
try {
|
||||
s = msg.substring(3);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
break;
|
||||
case "/w":
|
||||
stringBuilder.append(Protocol.whisper + "$");
|
||||
try {
|
||||
s = msg.substring(3);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
break;
|
||||
case "/e":
|
||||
stringBuilder.append(Protocol.leaveLobby);
|
||||
break;
|
||||
case "/v":
|
||||
try {
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package ch.unibas.dmi.dbis.cs108.multiplayer.helpers;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.multiplayer.server.Lobby;
|
||||
|
||||
/**
|
||||
* This class is where the Protocol commands are saved as strings. The idea is that every class that
|
||||
* uses protocol messages does not directly use e.g. "CHATA" in the code but rather uses
|
||||
@ -41,6 +43,15 @@ public class Protocol {
|
||||
*/
|
||||
public static final String chatMsgToAll = "CHATA";
|
||||
|
||||
/**
|
||||
* When the server receives this, it broadcasts a chat message to all clients in the same Lobby.
|
||||
* The message has to be given in the protocol message after {@code CHATL$}, for example the protocol message {@code
|
||||
* CHATL$Hello everybody!}, if sent from the user named Poirot, will print {@code Poirot: Hello
|
||||
* everybody!} to the chat console of every client in the lobby (note the absence / presence of spaces).
|
||||
* If the client is not in a lobby, the chat message will be sent to everyone not in a lobby.
|
||||
*/
|
||||
public static final String chatMsgToLobby = "CHATL";
|
||||
|
||||
/**
|
||||
* The message sent by the client on login to set their name. For example, {@code LOGIN$Poirot}
|
||||
* will use the clientHandler.setUsernameOnLogin() method to set this client's username to Poirot,
|
||||
@ -75,23 +86,41 @@ public class Protocol {
|
||||
public static final String clientQuitRequest = "QUITR";
|
||||
|
||||
/**
|
||||
* TODO: enable for client
|
||||
* TODO: add sever response
|
||||
* Client sends this message when he wants to create a new game.
|
||||
* Client sends this message when they want to create a new game.
|
||||
* Client issues this command in {@link ch.unibas.dmi.dbis.cs108.multiplayer.client.MessageFormatter}
|
||||
* using "/g".
|
||||
* First a lobby {@link ch.unibas.dmi.dbis.cs108.sebaschi.Lobby} is created of which the requesting client is the admin of.
|
||||
* First a lobby {@link Lobby} is created of which the requesting client is the admin of.
|
||||
*/
|
||||
public static final String createNewGame = "CRTGM";
|
||||
public static final String createNewLobby = "CRTLB";
|
||||
|
||||
/**
|
||||
* TODO: implement in {@link ch.unibas.dmi.dbis.cs108.multiplayer.client.MessageFormatter}
|
||||
* TODO: imlement in {@link ch.unibas.dmi.dbis.cs108.multiplayer.server.JServerProtocolParser}
|
||||
* TODO: add the Servers reaction, i.e. sending a list of lobbies.
|
||||
* Represents a clients' request for a list of lobbies
|
||||
*/
|
||||
public static final String listLobbies = "LISTL";
|
||||
|
||||
/**
|
||||
* Represents a clients' request for a list of all players within the lobby.
|
||||
*/
|
||||
public static final String listPlayersInLobby = "LISTP";
|
||||
|
||||
/**
|
||||
* Client requests to join the Lobby with the given number, for example,
|
||||
* {@code JOINL$2} means the client wants to join lobby 2.
|
||||
* todo: document handling when lobby is already full
|
||||
*/
|
||||
public static final String joinLobby = "JOINL";
|
||||
|
||||
/**
|
||||
* Client requests to leave whatever lobby they're in.
|
||||
*/
|
||||
public static final String leaveLobby = "LEAVL";
|
||||
|
||||
/**
|
||||
* Whisper chat. Syntax: {@code WHISP$recipient's username$message}
|
||||
*/
|
||||
public static final String whisper ="WHISP";
|
||||
|
||||
|
||||
/**
|
||||
* A Client decides to start the game.
|
||||
*/
|
||||
@ -114,11 +143,19 @@ public class Protocol {
|
||||
public static final String pingFromServer = "SPING";
|
||||
|
||||
/**
|
||||
* prints out incoming chat messages / announcements into the user's console. any string that
|
||||
* prints out incoming announcements into the user's console. any string that
|
||||
* follows CONSM$ is printed as is, so the message that follows already has to be formatted the
|
||||
* way it should be shown to the client.
|
||||
*/
|
||||
public static final String printToClientConsole = "CONSM";
|
||||
|
||||
/**
|
||||
* prints out incoming chat messages into the user's chat. any string that
|
||||
* follows CHATM$ is printed as is, so the message that follows already has to be formatted the
|
||||
* way it should be shown to the client.
|
||||
*/
|
||||
public static final String printToClientConsole = "CHATM";
|
||||
public static final String printToClientChat = "CHATM";
|
||||
|
||||
|
||||
/**
|
||||
* Server confirms the client's quit request, meaning that the client can now close its
|
||||
@ -138,6 +175,11 @@ public class Protocol {
|
||||
*/
|
||||
public static final String serverRequestsHumanVote = "HVOTR";
|
||||
|
||||
/**
|
||||
* todo: doc
|
||||
*/
|
||||
public static final String serverDeliversLobbyList = "LLIST"; //todo: do we need this?
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -30,6 +30,8 @@ Implemented:
|
||||
* SPING Ping from server to client
|
||||
* PINGB Pingback from client to server.
|
||||
* QUITC Confirms to the client that they are being disconnected from the server.
|
||||
* LLIST$LobbyIDAndAdmin
|
||||
Response to LISTL. Parameter is a string.
|
||||
|
||||
Future / planned:
|
||||
* MSGRS "Message received": Paramaters: a string detailing to the client that and what the server received as command.
|
||||
|
||||
@ -6,6 +6,7 @@ import ch.unibas.dmi.dbis.cs108.gamelogic.TrainOverflow;
|
||||
import ch.unibas.dmi.dbis.cs108.gamelogic.VoteHandler;
|
||||
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol;
|
||||
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.ServerPinger;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
@ -16,6 +17,7 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class ClientHandler implements Runnable {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger();
|
||||
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
|
||||
|
||||
@ -25,7 +27,6 @@ public class ClientHandler implements Runnable {
|
||||
private Socket socket;
|
||||
private InetAddress ip;
|
||||
|
||||
|
||||
/**
|
||||
* notes if the client has formally logged in yet. If connecting through the normal Client class,
|
||||
* the client is logged in automatically, if connecting though some external application, the
|
||||
@ -42,6 +43,7 @@ public class ClientHandler implements Runnable {
|
||||
|
||||
/**
|
||||
* Implements the login logic in client-server architecture.
|
||||
*
|
||||
* @param ip the ip of the client, used for re-connection.
|
||||
* @param socket the socket on which to make the connection.
|
||||
*/
|
||||
@ -50,7 +52,7 @@ public class ClientHandler implements Runnable {
|
||||
this.ip = ip;
|
||||
this.socket = socket;
|
||||
this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
|
||||
this.in = new BufferedReader(new InputStreamReader((socket.getInputStream())));
|
||||
this.in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
this.loggedIn = false;
|
||||
this.clientUserName = nameDuplicateChecker.checkName("U.N. Owen");
|
||||
connectedClients.add(this);
|
||||
@ -77,16 +79,13 @@ public class ClientHandler implements Runnable {
|
||||
|
||||
/**
|
||||
* Needed to fill a train with client TODO: how do lobbies fit here?
|
||||
*
|
||||
* @return the HashSet of Connected Clients
|
||||
*/
|
||||
public static HashSet<ClientHandler> getConnectedClients() {
|
||||
return connectedClients;
|
||||
}
|
||||
|
||||
public static HashSet<ClientHandler> getLobby() {
|
||||
return lobby;
|
||||
}
|
||||
|
||||
public static HashSet<ClientHandler> getGhostClients() {
|
||||
return ghostClients;
|
||||
}
|
||||
@ -119,42 +118,70 @@ public class ClientHandler implements Runnable {
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOGGER.debug(this.getClientUserName() + " reached end of run()");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Lets the client change their username, if the username is already taken, a similar
|
||||
* option is chosen.
|
||||
*
|
||||
* Lets the client change their username, if the username is already taken, a similar option is
|
||||
* chosen.
|
||||
* @param newName The desired new name to replace the old one with.
|
||||
*/
|
||||
public void changeUsername(String newName) {
|
||||
String helper = this.getClientUserName();
|
||||
this.clientUserName = nameDuplicateChecker.checkName(newName);
|
||||
broadcastAnnouncement(helper + " has changed their nickname to " + clientUserName);
|
||||
broadcastAnnouncementToAll(helper + " has changed their nickname to " + clientUserName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client's username on login, if the username is already taken, a similar
|
||||
* option is chosen. Functionally, the only difference between this method and changeUsername
|
||||
* is that it doesn't print out the name change.
|
||||
* Sets the client's username on login, if the username is already taken, a similar option is
|
||||
* chosen. Functionally, the only difference between this method and changeUsername is that it
|
||||
* doesn't print out the name change.
|
||||
*
|
||||
* @param name The desired name.
|
||||
*/
|
||||
public void setUsernameOnLogin(String name) {
|
||||
this.clientUserName = nameDuplicateChecker.checkName(name);
|
||||
broadcastAnnouncement( clientUserName + " has joined the Server");
|
||||
broadcastAnnouncementToAll(clientUserName + " has joined the Server");
|
||||
|
||||
/* The following lines could be un-commented to provide Lobby information on login
|
||||
sendAnnouncementToClient("Welcome, " + clientUserName + "!");
|
||||
sendAnnouncementToClient("Here are the currently open Lobbies:");
|
||||
listLobbies();
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a chat Message to all active clients in the form "Username: @msg"
|
||||
*
|
||||
* Returns the Lobby this ClientHandler is in. If this ClientHandler is not in a Lobby,
|
||||
* it returns null.
|
||||
*/
|
||||
public Lobby getLobby() {
|
||||
try {
|
||||
return Lobby.getLobbyFromID(Lobby.clientIsInLobby(this));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a chat Message to all clients in the same lobby in the form "Username: @msg"
|
||||
* If this client isn't in a lobby, it instead sends the message to everyone not in a lobby
|
||||
* @param msg the Message to be broadcast
|
||||
*/
|
||||
public void broadcastChatMessage(String msg) {
|
||||
for (ClientHandler client : connectedClients) {
|
||||
client.sendMsgToClient(Protocol.printToClientConsole + "$" + clientUserName + ": " + msg);
|
||||
public void broadcastChatMessageToLobby(String msg) {
|
||||
Lobby l = getLobby();
|
||||
if (l != null) {
|
||||
for (ClientHandler client : l.getLobbyClients()) {
|
||||
client.sendMsgToClient(Protocol.printToClientChat + "$" + clientUserName + ": " + msg);
|
||||
}
|
||||
} else {
|
||||
//send msg to all clients who are not in a lobby.
|
||||
for (ClientHandler client: connectedClients) {
|
||||
if (Lobby.clientIsInLobby(client) == -1) {
|
||||
client.sendMsgToClient(Protocol.printToClientChat + "$" + clientUserName + ": " + msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,26 +197,71 @@ public class ClientHandler implements Runnable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a non-chat Message to all active clients. This can be used for server
|
||||
* messages / announcements rather than chat messages. The message will be printed to the user
|
||||
* exactly as it is given to this method. Unlike broadcastChatMessage, it will also be printed
|
||||
* onto the server console.
|
||||
*
|
||||
* Broadcasts a chat Message to all clients across all lobbies & clients who are not in a lobby
|
||||
* in the form "Username: @msg"
|
||||
* @param msg the Message to be broadcast
|
||||
*/
|
||||
public void broadcastAnnouncement(String msg) {
|
||||
public void broadcastChatMessageToAll(String msg) {
|
||||
for (ClientHandler client : connectedClients) {
|
||||
client.sendMsgToClient(Protocol.printToClientChat + "$" + clientUserName + ": " + msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a non-chat Message to all active clients. This can be used for server messages /
|
||||
* announcements rather than chat messages. The message will be printed to the user exactly as it
|
||||
* is given to this method. Unlike eg. broadcastChatMessageToLobby, it will also be printed onto the server
|
||||
* console.
|
||||
* @param msg the Message to be broadcast. Does not have to be protocol-formatted, this method will take care of that.
|
||||
*/
|
||||
public static void broadcastAnnouncementToAll(String msg) {
|
||||
System.out.println(msg);
|
||||
for (ClientHandler client : connectedClients) {
|
||||
client.sendMsgToClient(Protocol.printToClientConsole + "$" + msg);
|
||||
}
|
||||
}
|
||||
|
||||
/** Sends a given message to client. The message has to already be protocol-formatted. ALL
|
||||
/**
|
||||
* Broadcasts a non-chat Message to all clients in the same lobby. This can be used for server messages /
|
||||
* announcements rather than chat messages. The message will be printed to the user exactly as it
|
||||
* is given to this method. The announcement will not be printed on the server console.
|
||||
* If this clienthandler is not in a lobby, it will instead broadcast to all clients.
|
||||
*
|
||||
* @param msg the Message to be broadcast. Does not have to be protocol-formatted, this method will take care of that.
|
||||
*/
|
||||
public void broadcastAnnouncementToLobby(String msg) {
|
||||
Lobby l = getLobby();
|
||||
if (l != null) {
|
||||
//System.out.println(msg); we can-comment this if you want lobby-announcements to print on the server console as well.
|
||||
for (ClientHandler client : l.getLobbyClients()) {
|
||||
client.sendMsgToClient(Protocol.printToClientConsole + "$" + msg);
|
||||
}
|
||||
} else {
|
||||
LOGGER.debug("Could not send announcements; probably client isn't in a lobby."
|
||||
+ "Will broadcast across all lobbies now.");
|
||||
broadcastAnnouncementToAll(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message only to the specified user, as well as sending a confirmation to the user who sent the message
|
||||
* that it has been sent. Syntax:
|
||||
* @param target MUST NOT BE NULL!
|
||||
*/
|
||||
public void whisper(String msg, ClientHandler target) {
|
||||
target.sendMsgToClient(Protocol.printToClientChat + "$" + this.getClientUserName() + " whispers: " + msg);
|
||||
sendMsgToClient(Protocol.printToClientChat + "$You whispered to " + target.getClientUserName() + ": " + msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a given message to this client. The message has to already be protocol-formatted. ALL
|
||||
* communication with the client has to happen via this method!
|
||||
*
|
||||
* @param msg the given message. Should already be protocol-formatted.
|
||||
*/
|
||||
public void sendMsgToClient(String msg) {
|
||||
try {
|
||||
//if(!msg.equals("SPING"))LOGGER.debug("Message sent to client: " + msg);
|
||||
out.write(msg);
|
||||
out.newLine();
|
||||
out.flush();
|
||||
@ -239,15 +311,26 @@ public class ClientHandler implements Runnable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes & disconnects the client. To be used if a severe connection loss is detected (i.e. if trying to
|
||||
* send / receive a message throws an exception, not just if ping-pong detects a connection loss).
|
||||
* This is very similar to removeClientOnLogout(), however removeClientOnLogout() should only be used for
|
||||
* regular quitting, since removeClientOnLogout() cannot handle a client with connection loss.
|
||||
* Sends an announcement to just this client. Essentially the same as broadcastAnnouncementToAll except
|
||||
* it only sends an announcement to just this client instead of everyone.
|
||||
* Can be used for private non-chat messages (e.g. "You are now a ghost").
|
||||
*/
|
||||
public void sendAnnouncementToClient(String msg) {
|
||||
sendMsgToClient(Protocol.printToClientConsole + "$" + msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes & disconnects the client. To be used if a severe connection loss is detected (i.e. if
|
||||
* trying to send / receive a message throws an exception, not just if ping-pong detects a
|
||||
* connection loss). This is very similar to removeClientOnLogout(), however
|
||||
* removeClientOnLogout() should only be used for regular quitting, since removeClientOnLogout()
|
||||
* cannot handle a client with connection loss.
|
||||
*/
|
||||
public void removeClientOnConnectionLoss() {
|
||||
connectedClients.remove(this);
|
||||
disconnectClient();
|
||||
broadcastAnnouncement(getClientUserName() + " has left the server due to a connection loss.");
|
||||
leaveLobby();
|
||||
broadcastAnnouncementToAll(getClientUserName() + " has left the server due to a connection loss.");
|
||||
disconnectedClients.add(this);
|
||||
}
|
||||
|
||||
@ -257,14 +340,108 @@ public class ClientHandler implements Runnable {
|
||||
* removeClientOnConnectionLoss() if the client has to be removed due to a connection loss.
|
||||
*/
|
||||
public void removeClientOnLogout() {
|
||||
broadcastAnnouncement(getClientUserName() + " has left the server.");
|
||||
sendMsgToClient(Protocol.serverConfirmQuit); //todo: protocol
|
||||
broadcastAnnouncementToAll(getClientUserName() + " has left the server.");
|
||||
sendMsgToClient(Protocol.serverConfirmQuit);
|
||||
connectedClients.remove(this);
|
||||
leaveLobby();
|
||||
disconnectClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the client's socket, in, and out.
|
||||
* Invoked by Protocol.createNewLobby. Creates a new lobby with the ClientHandler as admin and
|
||||
* adds the lobby to the server data.
|
||||
*/
|
||||
public void createNewLobby() {
|
||||
if (Lobby.clientIsInLobby(this) == -1) {
|
||||
Lobby newGame = new Lobby(this);
|
||||
} else {
|
||||
sendAnnouncementToClient("You are already in lobby nr. " + Lobby.clientIsInLobby(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The client wants to join the lobby with the index i.
|
||||
* //todo: needs more doc.
|
||||
* @param i
|
||||
*/
|
||||
public void joinLobby(int i) {
|
||||
Lobby l = Lobby.getLobbyFromID(i);
|
||||
if (l != null) {
|
||||
l.addPlayer(this);
|
||||
} else {
|
||||
sendAnnouncementToClient("Invalid Lobby nr.");
|
||||
sendAnnouncementToClient("use LISTL to list lobbies");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If the client is in a Lobby, they leave it. Otherwise, this method does nothing.
|
||||
*/
|
||||
public void leaveLobby() {
|
||||
Lobby l = Lobby.getLobbyFromID(Lobby.clientIsInLobby(this));
|
||||
if (l != null) {
|
||||
l.removePlayer(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lists all lobbies and their members, along with players outside lobbies
|
||||
* to this clientHandler's client as an announcement.
|
||||
*/
|
||||
public void listLobbies() {
|
||||
if (Lobby.lobbies.isEmpty()) {
|
||||
sendAnnouncementToClient("No open Lobbies.");
|
||||
} else {
|
||||
for (Lobby l : Lobby.lobbies) {
|
||||
sendAnnouncementToClient("Lobby nr. " + l.getLobbyID() + ":");
|
||||
for (ClientHandler c : l.getLobbyClients()) {
|
||||
if (c.equals(l.getAdmin())) {
|
||||
sendAnnouncementToClient(" -" + c.getClientUserName() + " (admin)");
|
||||
} else {
|
||||
sendAnnouncementToClient(" -" + c.getClientUserName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean helper = false; //used to print "Clients not in lobbies" only once, if needed.
|
||||
for (ClientHandler c: connectedClients) {
|
||||
if (Lobby.clientIsInLobby(c) == -1) {
|
||||
if (!helper) {
|
||||
helper = true;
|
||||
sendAnnouncementToClient("Clients not in lobbies:");
|
||||
}
|
||||
sendAnnouncementToClient(" -" + c.getClientUserName());
|
||||
}
|
||||
}
|
||||
if (!helper) {
|
||||
sendAnnouncementToClient("No clients outside of lobbies");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all players in the client's lobby. If the client is not in a Lobby, it will say
|
||||
* "You are not in a lobby."
|
||||
*/
|
||||
public void listPlayersInLobby() {
|
||||
Lobby l = getLobby();
|
||||
if (l != null) {
|
||||
sendAnnouncementToClient("Players in lobby nr. " + l.getLobbyID() + ":");
|
||||
for (ClientHandler c : l.getLobbyClients()) {
|
||||
if (c.equals(l.getAdmin())) {
|
||||
sendAnnouncementToClient(" -" + c.getClientUserName() + " (admin)");
|
||||
} else {
|
||||
sendAnnouncementToClient(" -" + c.getClientUserName());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sendAnnouncementToClient("You are not in a lobby.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the client's socket, in, and out. and removes from global list of clients.
|
||||
*/
|
||||
public void disconnectClient() {
|
||||
socket = this.getSocket();
|
||||
|
||||
@ -16,11 +16,6 @@ public class JServerProtocolParser {
|
||||
public static final Logger LOGGER = LogManager.getLogger();
|
||||
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
|
||||
|
||||
/**
|
||||
* jsdcjkhcsdjksdacjkn
|
||||
*/
|
||||
public static final String CHATA = "CHATA";
|
||||
|
||||
|
||||
/**
|
||||
* Used by the server (i.e. ClientHandler{@link ClientHandler}) to parse an incoming protocol
|
||||
@ -43,7 +38,29 @@ public class JServerProtocolParser {
|
||||
}
|
||||
switch (header) {
|
||||
case Protocol.chatMsgToAll:
|
||||
h.broadcastChatMessage(msg.substring(6));
|
||||
h.broadcastChatMessageToAll(msg.substring(6));
|
||||
break;
|
||||
case Protocol.chatMsgToLobby:
|
||||
h.broadcastChatMessageToLobby(msg.substring(6));
|
||||
break;
|
||||
case Protocol.whisper:
|
||||
//find ClientHandler
|
||||
try {
|
||||
ClientHandler target = null;
|
||||
String targetName = msg.substring(6, msg.indexOf("$", 6));
|
||||
String chatMsg = msg.substring(msg.indexOf("$", 6)+1);
|
||||
System.out.println(targetName);
|
||||
System.out.println(chatMsg);
|
||||
for (ClientHandler c : ClientHandler.getConnectedClients()) {
|
||||
if (c.getClientUserName().equals(targetName)) {
|
||||
target = c;
|
||||
}
|
||||
}
|
||||
assert target != null;
|
||||
h.whisper(chatMsg, target);
|
||||
} catch (Exception ignored) {
|
||||
h.sendAnnouncementToClient("Something went wrong.");
|
||||
}
|
||||
break;
|
||||
case Protocol.clientLogin:
|
||||
h.setLoggedIn(true);
|
||||
@ -65,15 +82,26 @@ public class JServerProtocolParser {
|
||||
case Protocol.clientQuitRequest:
|
||||
h.removeClientOnLogout();
|
||||
break;
|
||||
case Protocol.createNewGame:
|
||||
// TODO add h.openLobby(h) method
|
||||
LOGGER.debug(Protocol.createNewGame
|
||||
+ " command reached in JServerProtocolParser. Command issued by: "
|
||||
+ h.getClientUserName());
|
||||
case Protocol.joinLobby:
|
||||
try {
|
||||
int i = Integer.parseInt(msg.substring(6, 7));
|
||||
h.joinLobby(i);
|
||||
} catch (Exception e) {
|
||||
h.sendMsgToClient(Protocol.printToClientConsole
|
||||
+ "$Invalid input. Please use JOINL$1 to join Lobby 1, for example.");
|
||||
}
|
||||
break;
|
||||
case Protocol.createNewLobby:
|
||||
h.createNewLobby();
|
||||
break;
|
||||
case Protocol.listLobbies:
|
||||
//TODO: add action
|
||||
LOGGER.debug(Protocol.listLobbies + " command received from: " + h.getClientUserName());
|
||||
h.listLobbies();
|
||||
break;
|
||||
case Protocol.listPlayersInLobby:
|
||||
h.listPlayersInLobby();
|
||||
break;
|
||||
case Protocol.leaveLobby:
|
||||
h.leaveLobby();
|
||||
break;
|
||||
case Protocol.votedFor:
|
||||
LOGGER.debug("Made it here");
|
||||
|
||||
@ -0,0 +1,170 @@
|
||||
package ch.unibas.dmi.dbis.cs108.multiplayer.server;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
|
||||
|
||||
import java.util.HashSet;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Use: If a client sends a CRTLB command the server should create a lobby with the client as admin.
|
||||
* In this state, up to 5 other clients (so 6 in total) are able to join this lobby. Once the admin
|
||||
* feels like it, they can start a game.
|
||||
*/
|
||||
public class Lobby {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger();
|
||||
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
|
||||
public static HashSet<Lobby> lobbies = new HashSet<>();
|
||||
|
||||
private static final int MAX_NO_OF_CLIENTS = 6;
|
||||
|
||||
|
||||
/**
|
||||
* The Person who created the game and can configure it and decide to start once enough lobbyClients
|
||||
* have entered the lobby.
|
||||
*/
|
||||
private final ClientHandler admin;
|
||||
|
||||
/**
|
||||
* Everyone who's in the lobby.
|
||||
*/
|
||||
private HashSet<ClientHandler> lobbyClients = new HashSet<>(6);
|
||||
|
||||
private final int lobbyID;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor. Sets the admin to who created the lobby. Adds the admin to the list of lobbyClients.
|
||||
* Increases the number of lobbyClients from 0 to 1.
|
||||
*
|
||||
* @param admin the Client who called CRTGM
|
||||
*/
|
||||
public Lobby(ClientHandler admin) {
|
||||
this.admin = admin;
|
||||
this.lobbyClients.add(admin);
|
||||
lobbies.add(this);
|
||||
int helper = 1;
|
||||
while (getLobbyFromID(helper) != null) {
|
||||
helper++;
|
||||
}
|
||||
this.lobbyID = helper;
|
||||
ClientHandler.broadcastAnnouncementToAll("New Lobby created by " + admin.getClientUserName() +
|
||||
". This lobby's ID: " + this.lobbyID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @return the admin of the lobby.
|
||||
*/
|
||||
public ClientHandler getAdmin() {
|
||||
return this.admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* getter for the lobby ID
|
||||
* @return lobbyID as set in constructor.
|
||||
*/
|
||||
public int getLobbyID() {
|
||||
return this.lobbyID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list containing lobbyClients currently in the lobby
|
||||
*
|
||||
* @return list of lobbyClients
|
||||
*/
|
||||
public HashSet<ClientHandler> getLobbyClients() {
|
||||
return this.lobbyClients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lobby with the desired LobbyID.
|
||||
* For example, getLobbyFromID(5) returns the lobby whose LobbyID is 5.
|
||||
* If no such lobby exists, it returns null.
|
||||
*/
|
||||
public static Lobby getLobbyFromID(int i) {
|
||||
for (Lobby l: lobbies) {
|
||||
if (l.getLobbyID() == i) {
|
||||
return l;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the lobby that the client is in. If the client is not in any
|
||||
* lobby, it returns -1.
|
||||
*/
|
||||
public static int clientIsInLobby(ClientHandler h) {
|
||||
for (Lobby l: lobbies) {
|
||||
for (ClientHandler clientHandler: l.getLobbyClients()) {
|
||||
if (h.equals(clientHandler)) {
|
||||
return l.getLobbyID();
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a player to the lobby. Returns true if successful.
|
||||
* TODO: add an appropriate response. Currently hardcoded.
|
||||
* @param client who wants to join the lobby.
|
||||
*/
|
||||
public synchronized boolean addPlayer(ClientHandler client) {
|
||||
if (lobbyClients.size() < MAX_NO_OF_CLIENTS) {
|
||||
if (clientIsInLobby(client) == -1) {
|
||||
lobbyClients.add(client);
|
||||
ClientHandler.broadcastAnnouncementToAll(client.getClientUserName() + " has joined lobby " + this.getLobbyID());
|
||||
//LOGGER.debug(client.getClientUserName() + " has been added to Lobby with ID: " + lobbyID
|
||||
// + ". Current number of lobbyClients in this lobby: " + lobbyClients.size());
|
||||
return true;
|
||||
} else {
|
||||
client.sendAnnouncementToClient("You are already in lobby nr. " + clientIsInLobby(client));
|
||||
}
|
||||
} else {
|
||||
client.sendAnnouncementToClient("This lobby is full. Please try joining a different lobby or create a new lobby");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does what is says on the box. Needs to be called when a client disconnects for some reason and
|
||||
* cannot reconnect. I.E. when the server closes the connection to that client, the client should
|
||||
* be removed from the list.
|
||||
*
|
||||
* @param player that is to be removed
|
||||
* @return true if a player was found and removed. Used for debugging.
|
||||
*/
|
||||
public synchronized boolean removePlayer(ClientHandler player) {
|
||||
//if the player who leaves the lobby is the admin, the lobby is closed.
|
||||
if (player.equals(getAdmin())) {
|
||||
ClientHandler.broadcastAnnouncementToAll(player.getClientUserName() + " has closed lobby nr. " + this.getLobbyID());
|
||||
closeLobby();
|
||||
} else if (this.getLobbyClients().remove(player)){
|
||||
ClientHandler.broadcastAnnouncementToAll(player.getClientUserName() + " has left lobby nr. " + this.getLobbyID());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the lobby.
|
||||
*
|
||||
*/
|
||||
public void closeLobby() {
|
||||
lobbies.remove(this);
|
||||
ClientHandler.broadcastAnnouncementToAll("Lobby nr. " + this.getLobbyID() + " has been closed.");
|
||||
/*
|
||||
Todo: theoretically, this is enough to close a lobby.
|
||||
ClientHandlers dont have to manually be removed from the lobby
|
||||
since if the lobby is removed from the lobbies
|
||||
hash set then e.g. clientIsInLobby will not be able to tell that these clients
|
||||
are theoretically still in this lobby. However, in the future we might implement
|
||||
removing the clients anyways.
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package ch.unibas.dmi.dbis.cs108.multiplayer.server;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
@ -32,7 +33,7 @@ public class Server {
|
||||
Socket socket = serverSocket.accept();
|
||||
ClientHandler nextClient = new ClientHandler(socket, socket.getInetAddress());
|
||||
Thread th = new Thread(nextClient);
|
||||
connectedClients.add(nextClient);
|
||||
connectedClients.add(nextClient); // will leave be for now
|
||||
th.start();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
package ch.unibas.dmi.dbis.cs108.sebaschi;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
|
||||
import ch.unibas.dmi.dbis.cs108.gamelogic.Game;
|
||||
import ch.unibas.dmi.dbis.cs108.multiplayer.client.Client;
|
||||
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
|
||||
import java.net.Socket;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* This Class Represents an Object containing different Maps, Lists and Sets wherein a server object
|
||||
* can find all needed data. An instance of this object can also be passed to other class-objects if
|
||||
* they need the same data. This Class is used to query for information in collections.
|
||||
*/
|
||||
public class CentralServerData {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger();
|
||||
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
|
||||
|
||||
private Set<ClientHandler> clientsOnServer;
|
||||
private Set<Game> activeGames;
|
||||
private Set<Game> gamesOpenToJoin;
|
||||
|
||||
private Map<ClientHandler, Socket> clientSocketMap;
|
||||
private Map<Socket, ClientHandler> socketClientMap;
|
||||
private Map<Game, ClientHandler> gameClientMap;
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
package ch.unibas.dmi.dbis.cs108.sebaschi;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
|
||||
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Ghost;
|
||||
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Passenger;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.util.Set;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Class that shall contain all non-game logik information relevant to a game session needed for
|
||||
* client-server and client-client communication.
|
||||
*/
|
||||
public class GameSessionData {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger();
|
||||
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
|
||||
|
||||
CentralServerData globalData;
|
||||
|
||||
Set<Passenger> passengers;
|
||||
Set<BufferedOutputStream> clientOutputStreams;
|
||||
Set<Ghost> ghosts;
|
||||
}
|
||||
@ -1,96 +0,0 @@
|
||||
package ch.unibas.dmi.dbis.cs108.sebaschi;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
|
||||
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol;
|
||||
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Use: If a client sends a CRTGM command the server should create a lobby with the client as admin.
|
||||
* In this state, up to 5 other clients (so 6 in total) are able to join this lobby. Once the admin
|
||||
* feels like it, he can start a game.
|
||||
* TODO: is all data in here or should GameSessionData be used to collect all relevant data?
|
||||
*/
|
||||
public class Lobby {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger();
|
||||
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
|
||||
|
||||
private static final int MAX_NO_OF_CLIENTS = 6;
|
||||
public static int lobbies;
|
||||
|
||||
//TODO
|
||||
CentralServerData serverData;
|
||||
|
||||
/**
|
||||
* The Person who created the game and can configure it and decide to start once enough players
|
||||
* have entered the lobby.
|
||||
*/
|
||||
private final ClientHandler admin;
|
||||
|
||||
/**
|
||||
* Everyone who's in the lobby.
|
||||
*/
|
||||
private List<ClientHandler> players = new ArrayList<>(6);
|
||||
|
||||
|
||||
private int numberOfPlayersInLobby;
|
||||
//TODO maybe it makes sense to have LobbyID Class?
|
||||
private final int lobbyID = lobbies++;
|
||||
|
||||
|
||||
static {
|
||||
lobbies = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor. Sets the admin to who created the lobby. Adds the admin to the list of players.
|
||||
* Increases the number of players from 0 to 1.
|
||||
*
|
||||
* @param admin the Client who called CRTGM
|
||||
*/
|
||||
public Lobby(ClientHandler admin) {
|
||||
this.admin = admin;
|
||||
this.players.add(admin);
|
||||
this.numberOfPlayersInLobby = 1;
|
||||
lobbies++;
|
||||
LOGGER.debug("New Lobby created by " + admin.getClientUserName() + ". This lobby's ID: "
|
||||
+ this.lobbyID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @return the admin of the lobby.
|
||||
*/
|
||||
public ClientHandler getAdmin() {
|
||||
return this.admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a player to the lobby.
|
||||
* TODO: ad an appropriate response. Currently hardcoded.
|
||||
* TODO: Does this method need to implemented somewhere else, e.g. in the ClientHandler?
|
||||
* @param player who wants to join the lobby.
|
||||
*/
|
||||
public void addPlayer(ClientHandler player) {
|
||||
if (players.size() <= MAX_NO_OF_CLIENTS) {
|
||||
players.add(player);
|
||||
numberOfPlayersInLobby++;
|
||||
LOGGER.debug(player.getClientUserName() + " has been added to Lobby with ID: " + lobbyID
|
||||
+ ". Current number of players in this lobby: " + players.size());
|
||||
} else {
|
||||
LOGGER.debug(
|
||||
player.getClientUserName() + " could not be added to lobby. No. of players in lobby: "
|
||||
+ numberOfPlayersInLobby);
|
||||
//TODO: does this have to be formatted in any way to conform to protocol?
|
||||
player.sendMsgToClient(Protocol.printToClientConsole +
|
||||
"$The lobby is full. Please try joining a different lobby or create a new game");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
package ch.unibas.dmi.dbis.cs108.sebaschi;
|
||||
|
||||
/**
|
||||
* Implement the login sequenz when a client wnats to connect to the server.
|
||||
*/
|
||||
public class LoginClient {
|
||||
//TODO implement if needed
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
package ch.unibas.dmi.dbis.cs108.sebaschi;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class is just everyone on the server, which games are open to join, who is in session. I.E.
|
||||
* the context of the game just after joining the server, before starting a game and entering into a
|
||||
* lobby.
|
||||
*/
|
||||
public class ServerLobby {
|
||||
|
||||
private static List<Lobby> openLobbies;
|
||||
private static List<ClientHandler> allClients;
|
||||
|
||||
static {
|
||||
openLobbies = new ArrayList<>();
|
||||
allClients = new ArrayList<>();
|
||||
}
|
||||
|
||||
public static List<Lobby> getOpenLobbies() {
|
||||
return openLobbies;
|
||||
}
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
package ch.unibas.dmi.dbis.cs108.sebaschi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Session {
|
||||
List<Lobby> Lobbies = new ArrayList<>();
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user