Merge remote-tracking branch 'origin/master'

This commit is contained in:
Seraina 2022-04-08 12:42:25 +02:00
commit 33d8f779a2
8 changed files with 185 additions and 40 deletions

View File

@ -4,6 +4,7 @@ import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.ClientPinger;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol;
import java.net.Socket;
import java.io.*;
import java.net.UnknownHostException;
@ -36,7 +37,7 @@ public class Client {
systemName = "U.N. Owen";
}
if (systemName == null) systemName = "U.N. Owen";
sendMsgToServer("LOGON$" + systemName);
sendMsgToServer(Protocol.clientLogin + "$" + systemName);
clientPinger = new ClientPinger(this, this.socket);
} catch (IOException e) {

View File

@ -1,6 +1,7 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -10,7 +11,7 @@ public class MessageFormatter {
/**
* Takes a given Message and reformats it to where the JServerProtocolParser.parse() method can
* handle it (see Protocol.txt). May need to be redesigned once the games uses a GUI
* handle it (see Protocol.java). May need to be redesigned once the games uses a GUI
*
* @param msg the Messaged to be reformatted
* @return the reformatted message in the form HEADR$msg
@ -27,7 +28,7 @@ public class MessageFormatter {
}
switch (header) {
case "/c":
stringBuilder.append("CHATA$");
stringBuilder.append(Protocol.chatMsgToAll + "$");
try {
s = msg.substring(3);
} catch (Exception e) {
@ -35,15 +36,15 @@ public class MessageFormatter {
}
break;
case "/q":
stringBuilder.append("QUITS$");
stringBuilder.append(Protocol.clientQuitRequest + "$");
s = "";
break;
case "/n":
stringBuilder.append("NAMEC$");
stringBuilder.append(Protocol.nameChange + "$");
try {
s = msg.substring(3);
} catch (Exception e) {
System.out.println("Well what do you want your name to be?");
s = "U.N. Owen";
}
break;
default:

View File

@ -0,0 +1,66 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.helpers;
/**
* 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
* Protocol.chatMsgToAll. This improves legibility as well as
* allowing for quick modification of Protocol messages if needed.
* Most importantly, the Protocol commands can be documented in this
* class, and then in every other class that uses protocol messages,
* documentation for a command can be viewed by hovering over the
* specific variable, rather than having to document everywhere.
*/
public class Protocol {
//BIDIRECTIONAL COMMANDS:
/**
* Ping-back message from client to server / server to client.
* To be sent upon receiving "CPING" / "SPING".
* The other party then registers this in its ClientPinger / ServerPinger
* thread.
*/
public static final String pingBack = "PINGB";
//CLIENT TO SERVER COMMANDS:
/**
* When the server receives this, it broadcasts a chat message
* to all clients. The message has to be given in the protocol
* message after {@code CHATA$}, for example the protocol message
* {@code CHATA$Hello everybody!}, if sent from the user named Poirot,
* will print {@code Poirot: Hello everybody!} to every connected
* client's chat console (note the absence / presence of spaces).
*/
public static final String chatMsgToAll = "CHATA";
/**
* 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, and broadcast the announcement:
* {@code "Poirot has joined the Server"}. Also, it will set this clientHandler's
* loggedIn boolean to true, which could be used later to refuse access to users
* who haven't formally logged in using this command => //todo: shun non-logged-in users
*
*/
public static final String clientLogin = "LOGIN";
/**
* todo:doc
*
*/
public static final String nameChange = "NAMEC";
/**
* todo:doc
*/
public static final String pingFromClient = "CPING";
/**
* todo: doc
*/
public static final String clientQuitRequest = "QUITS";
}

View File

@ -19,8 +19,15 @@ public class ClientHandler implements Runnable {
private BufferedReader in;
private Socket socket;
private InetAddress ip;
private
Scanner sc;
/**
* 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
* client has to use the {@code Protocol.clientLogin} command.
*/
private boolean loggedIn;
private Scanner sc;
public ServerPinger serverPinger;
public static HashSet<ClientHandler> connectedClients = new HashSet<>();
public static HashSet<ClientHandler> disconnectedClients = new HashSet<>(); //todo: implement re-connection
@ -38,6 +45,7 @@ public class ClientHandler implements Runnable {
this.socket = socket;
this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
this.in = new BufferedReader(new InputStreamReader((socket.getInputStream())));
this.loggedIn = false;
this.clientUserName = nameDuplicateChecker.checkName("U.N. Owen");
connectedClients.add(this);
serverPinger = new ServerPinger(socket, this);
@ -73,7 +81,18 @@ public class ClientHandler implements Runnable {
return ghostClients;
}
//Setters
public boolean isLoggedIn() {
return loggedIn;
}
public void setLoggedIn(boolean loggedIn) {
this.loggedIn = loggedIn;
}
//Setters:
public String getClientUserName() {
return clientUserName;
}
@Override
@ -81,7 +100,7 @@ public class ClientHandler implements Runnable {
String msg;
while (socket.isConnected() && !socket.isClosed()) {
try {
msg = in.readLine(); //todo: here is where the server throws an exception when the client quits
msg = in.readLine();
JServerProtocolParser.parse(msg, this);
} catch (IOException e) {
//e.printStackTrace();
@ -92,9 +111,7 @@ public class ClientHandler implements Runnable {
}
}
public String getClientUserName() {
return clientUserName;
}
/**
* Lets the client change their username, if the username is already taken, a similar
@ -148,12 +165,10 @@ public class ClientHandler implements Runnable {
/** Sends a given message to client. The message has to already be protocol-formatted. ALL
* communication with the client has to happen via this method!
* todo: check for exception if out is closed.
* @param msg the given message. Should already be protocol-formatted.
*/
public void sendMsgToClient(String msg) {
try {
//todo: socket closed handling
out.write(msg);
out.newLine();
out.flush();

View File

@ -4,6 +4,7 @@ package ch.unibas.dmi.dbis.cs108.multiplayer.server;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol;
public class JServerProtocolParser {
public static final Logger LOGGER = LogManager.getLogger();
@ -17,6 +18,9 @@ public class JServerProtocolParser {
/**
* Used by the server (i.e. ClientHandler) to parse an incoming protocol message.
* For documentation on the individual Protocol messages, view the Protocol.java
* class or hover over the commands (e.g. Protocol.chatMsgToAll) with your mouse
* in this class.
*
* @param msg the encoded message that needs to be parsed
* @param h this ClientHandler (required so this method can access the ClientHandler's methods)
@ -30,33 +34,27 @@ public class JServerProtocolParser {
System.out.println("Received unknown command");
}
switch (header) {
case CHATA:
//sends chat message to all connected clients
case Protocol.chatMsgToAll:
h.broadcastChatMessage(msg.substring(6));
break;
case "LOGON":
//sets name to whatever follows LOGON$
case Protocol.clientLogin:
h.setLoggedIn(true);
try {
h.setUsernameOnLogin(msg.substring(6));
} catch (Exception e) {
h.setUsernameOnLogin("U.N. Owen");
}
break;
case "NAMEC":
//changes name to whatever follows NAMEC$. If the new name is already in use, it will append
//random numbers to the name.
case Protocol.nameChange:
h.changeUsername(msg.substring(6));
break;
case "CPING":
//sends a pingback to the client
h.sendMsgToClient("PINGB");
case Protocol.pingFromClient:
h.sendMsgToClient(Protocol.pingBack);
break;
case "PINGB":
//registers pingback from client
case Protocol.pingBack:
h.serverPinger.setGotPingBack(true);
break;
case "QUITS":
//safely disconnects the user
case Protocol.clientQuitRequest:
h.removeClientOnLogout();
break;
default:

View File

@ -54,13 +54,14 @@ public class nameDuplicateChecker {
* Also, if the name is empty, it assigns a default value ("U.N. Owen").
*/
public static String checkName(String name) {
String rtrn = name; //if this line is used, only duplicate names get a suffix.
//String rtrn = extendName(name); //if this line is used, all clients get a suffix
rtrn = rtrn.replace("$","");
rtrn = rtrn.replace(":","");
if (rtrn.equalsIgnoreCase("")) {rtrn = "U.N. Owen";}
String tempname = name; //if this line is used, only duplicate names get a suffix.
//String tempname = extendName(name); //if this line is used, all clients get a suffix
tempname = tempname.replace("$","");
tempname = tempname.replace(":","");
if (tempname.equalsIgnoreCase("")) {tempname = "U.N. Owen";}
String rtrn = tempname;
while (isTaken(rtrn)) { //todo: handle the (very unlikely) case that all names are taken.
rtrn = extendName(name);
rtrn = extendName(tempname);
}
return rtrn;
}

View File

@ -3,6 +3,7 @@ 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;
@ -11,7 +12,7 @@ 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 uf
* 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 {
@ -19,11 +20,11 @@ public class CentralServerData {
public static final Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
private Set<Client> clientsOnServer;
private Set<ClientHandler> clientsOnServer;
private Set<Game> activeGames;
private Set<Game> gamesOpenToJoin;
private Map<Client, Socket> clientSocketMap;
private Map<Socket, Client> socketClientMap;
private Map<Game, Client> gameClientMap;
private Map<ClientHandler, Socket> clientSocketMap;
private Map<Socket, ClientHandler> socketClientMap;
private Map<Game, ClientHandler> gameClientMap;
}

View File

@ -1,5 +1,67 @@
package ch.unibas.dmi.dbis.cs108.sebaschi;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
import java.util.ArrayList;
import java.util.List;
/**
* The Lobby one is in after a client sends the CRTGM command. THe Server
*/
public class Lobby {
private static final int MAX_NO_OF_CLIENTS = 6;
private static int lobbies;
/**
* 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;
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++;
}
/**
* Getter
*
* @return the admin of the lobby.
*/
public ClientHandler getAdmin() {
return this.admin;
}
/**
* Adds a player to the lobby.
*
* @param player who wants to join the lobby.
*/
public void addPlayer(ClientHandler player) {
players.add(player);
}
}