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:
parent
3c94bd5324
commit
b6d2a04e76
@ -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.ClientPinger;
|
||||||
|
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
@ -36,7 +37,7 @@ public class Client {
|
|||||||
systemName = "U.N. Owen";
|
systemName = "U.N. Owen";
|
||||||
}
|
}
|
||||||
if (systemName == null) systemName = "U.N. Owen";
|
if (systemName == null) systemName = "U.N. Owen";
|
||||||
sendMsgToServer("LOGON$" + systemName);
|
sendMsgToServer(Protocol.clientLogin + "$" + systemName);
|
||||||
|
|
||||||
clientPinger = new ClientPinger(this, this.socket);
|
clientPinger = new ClientPinger(this, this.socket);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.multiplayer.client;
|
package ch.unibas.dmi.dbis.cs108.multiplayer.client;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
|
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.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
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
|
* 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
|
* @param msg the Messaged to be reformatted
|
||||||
* @return the reformatted message in the form HEADR$msg
|
* @return the reformatted message in the form HEADR$msg
|
||||||
@ -27,7 +28,7 @@ public class MessageFormatter {
|
|||||||
}
|
}
|
||||||
switch (header) {
|
switch (header) {
|
||||||
case "/c":
|
case "/c":
|
||||||
stringBuilder.append("CHATA$");
|
stringBuilder.append(Protocol.chatMsgToAll + "$");
|
||||||
try {
|
try {
|
||||||
s = msg.substring(3);
|
s = msg.substring(3);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -35,15 +36,15 @@ public class MessageFormatter {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "/q":
|
case "/q":
|
||||||
stringBuilder.append("QUITS$");
|
stringBuilder.append(Protocol.clientQuitRequest + "$");
|
||||||
s = "";
|
s = "";
|
||||||
break;
|
break;
|
||||||
case "/n":
|
case "/n":
|
||||||
stringBuilder.append("NAMEC$");
|
stringBuilder.append(Protocol.nameChange + "$");
|
||||||
try {
|
try {
|
||||||
s = msg.substring(3);
|
s = msg.substring(3);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println("Well what do you want your name to be?");
|
s = "U.N. Owen";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -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";
|
||||||
|
|
||||||
|
}
|
||||||
@ -19,8 +19,15 @@ public class ClientHandler implements Runnable {
|
|||||||
private BufferedReader in;
|
private BufferedReader in;
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
private InetAddress ip;
|
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 ServerPinger serverPinger;
|
||||||
public static HashSet<ClientHandler> connectedClients = new HashSet<>();
|
public static HashSet<ClientHandler> connectedClients = new HashSet<>();
|
||||||
public static HashSet<ClientHandler> disconnectedClients = new HashSet<>(); //todo: implement re-connection
|
public static HashSet<ClientHandler> disconnectedClients = new HashSet<>(); //todo: implement re-connection
|
||||||
@ -38,6 +45,7 @@ public class ClientHandler implements Runnable {
|
|||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
|
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");
|
this.clientUserName = nameDuplicateChecker.checkName("U.N. Owen");
|
||||||
connectedClients.add(this);
|
connectedClients.add(this);
|
||||||
serverPinger = new ServerPinger(socket, this);
|
serverPinger = new ServerPinger(socket, this);
|
||||||
@ -73,7 +81,18 @@ public class ClientHandler implements Runnable {
|
|||||||
return ghostClients;
|
return ghostClients;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Setters
|
public boolean isLoggedIn() {
|
||||||
|
return loggedIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoggedIn(boolean loggedIn) {
|
||||||
|
this.loggedIn = loggedIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Setters:
|
||||||
|
public String getClientUserName() {
|
||||||
|
return clientUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -81,7 +100,7 @@ public class ClientHandler implements Runnable {
|
|||||||
String msg;
|
String msg;
|
||||||
while (socket.isConnected() && !socket.isClosed()) {
|
while (socket.isConnected() && !socket.isClosed()) {
|
||||||
try {
|
try {
|
||||||
msg = in.readLine(); //todo: here is where the server throws an exception when the client quits
|
msg = in.readLine();
|
||||||
JServerProtocolParser.parse(msg, this);
|
JServerProtocolParser.parse(msg, this);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
//e.printStackTrace();
|
//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
|
* 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
|
/** 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!
|
* 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.
|
* @param msg the given message. Should already be protocol-formatted.
|
||||||
*/
|
*/
|
||||||
public void sendMsgToClient(String msg) {
|
public void sendMsgToClient(String msg) {
|
||||||
try {
|
try {
|
||||||
//todo: socket closed handling
|
|
||||||
out.write(msg);
|
out.write(msg);
|
||||||
out.newLine();
|
out.newLine();
|
||||||
out.flush();
|
out.flush();
|
||||||
|
|||||||
@ -4,6 +4,7 @@ package ch.unibas.dmi.dbis.cs108.multiplayer.server;
|
|||||||
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
|
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol;
|
||||||
|
|
||||||
public class JServerProtocolParser {
|
public class JServerProtocolParser {
|
||||||
public static final Logger LOGGER = LogManager.getLogger();
|
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.
|
* 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 msg the encoded message that needs to be parsed
|
||||||
* @param h this ClientHandler (required so this method can access the ClientHandler's methods)
|
* @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");
|
System.out.println("Received unknown command");
|
||||||
}
|
}
|
||||||
switch (header) {
|
switch (header) {
|
||||||
case CHATA:
|
case Protocol.chatMsgToAll:
|
||||||
//sends chat message to all connected clients
|
|
||||||
h.broadcastChatMessage(msg.substring(6));
|
h.broadcastChatMessage(msg.substring(6));
|
||||||
break;
|
break;
|
||||||
case "LOGON":
|
case Protocol.clientLogin:
|
||||||
//sets name to whatever follows LOGON$
|
h.setLoggedIn(true);
|
||||||
try {
|
try {
|
||||||
h.setUsernameOnLogin(msg.substring(6));
|
h.setUsernameOnLogin(msg.substring(6));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
h.setUsernameOnLogin("U.N. Owen");
|
h.setUsernameOnLogin("U.N. Owen");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "NAMEC":
|
case Protocol.nameChange:
|
||||||
//changes name to whatever follows NAMEC$. If the new name is already in use, it will append
|
|
||||||
//random numbers to the name.
|
|
||||||
h.changeUsername(msg.substring(6));
|
h.changeUsername(msg.substring(6));
|
||||||
break;
|
break;
|
||||||
case "CPING":
|
case Protocol.pingFromClient:
|
||||||
//sends a pingback to the client
|
h.sendMsgToClient(Protocol.pingBack);
|
||||||
h.sendMsgToClient("PINGB");
|
|
||||||
break;
|
break;
|
||||||
case "PINGB":
|
case Protocol.pingBack:
|
||||||
//registers pingback from client
|
|
||||||
h.serverPinger.setGotPingBack(true);
|
h.serverPinger.setGotPingBack(true);
|
||||||
break;
|
break;
|
||||||
case "QUITS":
|
case Protocol.clientQuitRequest:
|
||||||
//safely disconnects the user
|
|
||||||
h.removeClientOnLogout();
|
h.removeClientOnLogout();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -54,13 +54,14 @@ public class nameDuplicateChecker {
|
|||||||
* Also, if the name is empty, it assigns a default value ("U.N. Owen").
|
* Also, if the name is empty, it assigns a default value ("U.N. Owen").
|
||||||
*/
|
*/
|
||||||
public static String checkName(String name) {
|
public static String checkName(String name) {
|
||||||
String rtrn = name; //if this line is used, only duplicate names get a suffix.
|
String tempname = 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
|
//String tempname = extendName(name); //if this line is used, all clients get a suffix
|
||||||
rtrn = rtrn.replace("$","");
|
tempname = tempname.replace("$","");
|
||||||
rtrn = rtrn.replace(":","");
|
tempname = tempname.replace(":","");
|
||||||
if (rtrn.equalsIgnoreCase("")) {rtrn = "U.N. Owen";}
|
if (tempname.equalsIgnoreCase("")) {tempname = "U.N. Owen";}
|
||||||
|
String rtrn = tempname;
|
||||||
while (isTaken(rtrn)) { //todo: handle the (very unlikely) case that all names are taken.
|
while (isTaken(rtrn)) { //todo: handle the (very unlikely) case that all names are taken.
|
||||||
rtrn = extendName(name);
|
rtrn = extendName(tempname);
|
||||||
}
|
}
|
||||||
return rtrn;
|
return rtrn;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user