Working on putting all protocol messages through Protocol.java.

Some small adjustments & bugfixes to how naming is handled.
Added loggedIn boolean for ClientHandler, not meaningfully implemented yet though.
Other minor changes.
This commit is contained in:
Jonas 2022-04-08 12:36:30 +02:00
parent 3c94bd5324
commit b6d2a04e76
6 changed files with 117 additions and 35 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;
}