diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/Client.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/Client.java index 009c604..7dcfc92 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/Client.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/Client.java @@ -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) { diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/MessageFormatter.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/MessageFormatter.java index 379ecb8..43b7301 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/MessageFormatter.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/MessageFormatter.java @@ -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: diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/helpers/Protocol.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/helpers/Protocol.java new file mode 100644 index 0000000..b52ac78 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/helpers/Protocol.java @@ -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"; + +} 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 53e7caa..54fe9d2 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 @@ -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 connectedClients = new HashSet<>(); public static HashSet 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(); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/JServerProtocolParser.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/JServerProtocolParser.java index 2fc1f79..cabf363 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/JServerProtocolParser.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/JServerProtocolParser.java @@ -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: diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/nameDuplicateChecker.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/nameDuplicateChecker.java index 8959a1d..340bdc8 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/nameDuplicateChecker.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/nameDuplicateChecker.java @@ -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; }