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:
Seraina 2022-04-14 08:36:01 +02:00
commit d98ee434c8
18 changed files with 540 additions and 264 deletions

View File

@ -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)

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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");
}

View File

@ -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 {

View File

@ -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?

View File

@ -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.

View File

@ -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();

View File

@ -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");

View File

@ -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.
*/
}
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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");
}
}
}

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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<>();
}