From c64c754d22b5735c6f7494375b77a13bd044dca3 Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 6 Apr 2022 21:57:21 +0200 Subject: [PATCH] Another complete overhaul of client-server communication: -Added broadcastAnnouncement message to ClientHandler -Bugfix: "Connection lost" at startup -Added LOGIN$username to protocol --- .../dbis/cs108/multiplayer/client/Client.java | 41 ++++++++-------- .../client/JClientProtocolParser.java | 4 +- .../multiplayer/helpers/ClientPinger.java | 2 +- .../cs108/multiplayer/helpers/Protocol.txt | 5 +- .../multiplayer/server/ClientHandler.java | 49 +++++++++++++++---- .../server/JServerProtocolParser.java | 10 ++++ .../dbis/cs108/multiplayer/server/Server.java | 9 ---- 7 files changed, 77 insertions(+), 43 deletions(-) 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 b10b366..f2cc148 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 @@ -20,16 +20,24 @@ public class Client { private Socket socket; private BufferedReader in; private BufferedWriter out; - public String userName; public ClientPinger clientPinger; - public Client(Socket socket, String userName) { + public Client(Socket socket) { try { this.socket = socket; this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); this.in = new BufferedReader((new InputStreamReader((socket.getInputStream())))); - this.userName = userName; - sendMsgToServer(getUsername()); //todo: dont just send username directly pls + + //sending the initial name to server. + String systemName; + try { + systemName = System.getProperty("user.name"); + } catch (Exception e) { + systemName = "Unknown User"; + } + if (systemName == null) systemName = "Unknown User"; + sendMsgToServer("LOGON$" + systemName); + clientPinger = new ClientPinger(this, this.socket); } catch (IOException e) { e.printStackTrace(); @@ -53,9 +61,7 @@ public class Client { sendMsgToServer(formattedMSG); } Thread.sleep(20); - } catch (IOException e) { - e.printStackTrace(); - } catch (InterruptedException e) { + } catch (IOException | InterruptedException e) { e.printStackTrace(); } //LOGGER.debug("just checked next line"); @@ -67,12 +73,10 @@ public class Client { /** - * Starts a thread which listens for incoming messages + * Starts a thread which listens for incoming chat messages / other messages that the user + * has to see */ public void chatListener() { - /*TODO: what type of decoding has to be done - TODO how shall input be logged? - */ new Thread(new Runnable() { @Override public void run() { @@ -144,19 +148,18 @@ public class Client { String hostname; int port = 42069; //can be set via argument later if needed. if (args.length < 1) { - System.out.println("Enter the host's IP address (or type localhost)"); + System.out.println("Enter the host's IP address (or type l for localhost)"); hostname = sc.next(); + if (hostname == "l") { + hostname = "localhost"; + } } else { hostname = args[0]; } - String systemName = System.getProperty("user.name"); - System.out.println("Choose a nickname (Suggestion: " + systemName - + "): "); //Suggests a name based on System username - String username = sc.next(); Socket socket; try { socket = new Socket(hostname, 42069); - Client client = new Client(socket, username); + Client client = new Client(socket); client.chatListener(); Thread cP = new Thread(client.clientPinger); cP.start(); @@ -169,10 +172,6 @@ public class Client { } - public String getUsername() { - return userName; - } - public Socket getSocket() { return socket; } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java index 1b63341..a04faf5 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java @@ -16,11 +16,13 @@ public class JClientProtocolParser { * @param c this Client(required so this method can access the Client's methods) */ public static void parse(String msg, Client c) { + //LOGGER.debug("got message: " + msg + "."); String header = ""; //"header" is the first 5 characters, i.e. the protocol part try { header = msg.substring(0, 5); } catch (IndexOutOfBoundsException e) { System.out.println("Received unknown command"); + e.printStackTrace(); } switch (header) { case "SPING": @@ -40,7 +42,7 @@ public class JClientProtocolParser { break; case "QUITC": c.closeEverything(); - System.out.println("bye!"); + break; default: System.out.println("Received unknown command"); } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/helpers/ClientPinger.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/helpers/ClientPinger.java index 0606c36..66a9be3 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/helpers/ClientPinger.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/helpers/ClientPinger.java @@ -36,7 +36,7 @@ public class ClientPinger implements Runnable { @Override public void run() { try { - Thread.sleep(2000); + Thread.sleep(20000); while (socket.isConnected() && !socket.isClosed()) { gotPingBack = false; client.sendMsgToServer("CPING"); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/helpers/Protocol.txt b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/helpers/Protocol.txt index 8fa2229..95de010 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/helpers/Protocol.txt +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/helpers/Protocol.txt @@ -1,6 +1,7 @@ -Client commands: +Client to server commands: Implemented: + * LOGON$name Log on, with the name set to whatever follow $. * CHATA$message Send chat message to all * QUITS request to quit server/ leave server. The server should then remove the relevant ClientHandler and close the sockets, but before doing so, it sends @@ -20,7 +21,7 @@ Future / planned: * VOTEH humans voting who is the ghost * LISTP list players/clients in session with the Server -Server Commands: +Server to client Commands: Implemented: * SPING Ping from server to client 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 4439c66..d1d70d2 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 @@ -33,19 +33,21 @@ public class ClientHandler implements Runnable { try { this.socket = socket; this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); - this.in = new BufferedReader((new InputStreamReader((socket.getInputStream())))); - this.clientUserName = in.readLine(); - // duplicate handling: if username already taken, assign random name to client + this.in = new BufferedReader(new InputStreamReader((socket.getInputStream()))); + this.clientUserName = "Mysterious Passenger"; //todo: duplicate handling for this + /* + // todo: duplicate handling more elegantly if (AllClientNames.allNames("").contains(clientUserName)) { clientUserName = NameGenerator.randomName(clientUserName); } // add username to list of all client names for future duplicate checking AllClientNames.allNames(clientUserName); + + */ connectedClients.add(this); serverPinger = new ServerPinger(out, socket); Thread sP = new Thread(serverPinger); sP.start(); - broadcastChatMessage("SERVER: " + clientUserName + " has joined the Server"); } catch (IOException e) { e.printStackTrace(); } @@ -109,23 +111,52 @@ public class ClientHandler implements Runnable { * @param newName The desired new name to replace the old one with. */ public void changeUsername(String newName) { - if (AllClientNames.allNames("").contains(newName)) { + if (AllClientNames.allNames("").contains(newName)) { //todo: more elegant solution newName = NameGenerator.randomName(newName); } String h = this.clientUserName; //just a friendly little helper this.clientUserName = newName; AllClientNames.allNames(newName); - broadcastChatMessage(h + " have changed their nickname to " + clientUserName); + broadcastAnnouncement(h + " has changed their nickname to " + clientUserName); } /** - * Broadcasts a Message to all active clients in the form "Username: msg" + * 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 msg the Message to be broadcasted + * @param name The desired name. + */ + public void setUsernameOnLogin(String name) { + //todo: duplicate checking + this.clientUserName = name; + broadcastAnnouncement( clientUserName + " has joined the Server"); + //todo: add this name to namelist + } + + /** + * Broadcasts a Message to all active clients in the form "Username: @msg" + * + * @param msg the Message to be broadcast */ public void broadcastChatMessage(String msg) { for (ClientHandler client : connectedClients) { - client.sendMsgToClient("CHATM$" + clientUserName + ": \"" + msg + "\""); + client.sendMsgToClient("CHATM$" + 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 ex- + * actly as it is given to this method. Unlike broadcastChatMessage, it will also be printed onto + * the server console. + * + * @param msg the Message to be broadcast + */ + public void broadcastAnnouncement(String msg) { + System.out.println(msg); + for (ClientHandler client : connectedClients) { + client.sendMsgToClient("CHATM$" + msg); } } 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 cc5072f..3ebb67e 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 @@ -21,6 +21,7 @@ public class JServerProtocolParser { * @param h this ClientHandler (required so this method can access the ClientHandler's methods) */ public static void parse(String msg, ClientHandler h) { + //LOGGER.debug("got message: " + msg + "."); String header = ""; //"header" is the first 5 characters, i.e. the protocol part try { header = msg.substring(0, 5); @@ -32,6 +33,14 @@ public class JServerProtocolParser { //sends chat message to all connected clients h.broadcastChatMessage(msg.substring(6)); break; + case "LOGON": + //sets name to whatever follows LOGON$ + try { + h.setUsernameOnLogin(msg.substring(6)); + } catch (Exception e) { + h.setUsernameOnLogin("A Mysterious Passenger"); + } + 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. @@ -47,6 +56,7 @@ public class JServerProtocolParser { break; case "QUITS": //safely disconnects the user + h.broadcastAnnouncement(h.getClientUserName() + " has left the server."); h.disconnectClient(); break; default: diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/Server.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/Server.java index 7efb211..4fa9590 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/Server.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/Server.java @@ -34,15 +34,6 @@ public class Server { Thread th = new Thread(nextClient); connectedClients.add(nextClient); th.start(); - // close socket + remove client if client is disconnected - /* TODO: Modify or remove this disconnection handling - right now, it is not functioning - (when disconnecting there are "broken pipe" exceptions on client side and "stream closed" - exceptions on the server side). - */ - if (nextClient.getSocket().getInputStream().read() == -1) { - System.out.println("client disconnected. closing socket"); - socket.close(); - } } } catch (IOException e) { e.printStackTrace();