Merge branch 'Jonas_Stuff' into 'master'

Added a Class and Method that can reformat the terminal commands into commands...

See merge request cs108-fs22/Gruppe-8!1
This commit is contained in:
Seraina Schöb 2022-03-27 19:43:54 +00:00
commit 44893525fd
51 changed files with 536 additions and 1353 deletions

Binary file not shown.

View File

@ -1,30 +0,0 @@
package ch.unibas.dmi.dbis.cs108.BudaClientServerStuff;
import java.io.*;
import java.net.Socket;
public class BudaClient {
public static void main(String[] args) {
Socket sock = null;
try {
sock = new Socket("localhost", 8090);
OutputStream out= sock.getOutputStream();
BufferedReader conin = new BufferedReader(new InputStreamReader(System.in));
String line = ""; //this String is the line that will be sent to the server
while (true) {
line = conin.readLine();
out.write(line.getBytes());
if (line.equalsIgnoreCase("Quitx")) {
break;
}
//line.startsWith() //todo: automatically handle name lengths
//TODO: Implement inputStream in.
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -1,66 +0,0 @@
package ch.unibas.dmi.dbis.cs108.BudaClientServerStuff;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class BudaClientThread implements Runnable {
int number;
Socket socket;
String name;
public BudaClientThread(int number, Socket socket) {
this.number = number;
this.socket = socket;
name = "";
}
public void run() {
System.out.println("Connection " + number + " established.");
try {
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
byte[] command;
String comString;
while (true) {
command = new byte[5];
in.read(command); //BudaClientThread waits to receive a line from the inputstream.
comString = new String(command);
if (comString.equalsIgnoreCase("Quitx")) { //todo: do as switch.
BudaServer.quit = true;
System.out.println("I just set quit to true!");
break;
} else if (comString.equalsIgnoreCase("NAME:")) {
setName(in);
} else if (comString.equalsIgnoreCase("NAMES")) {
printnames();
} else {
System.out.println("Client number " + number + " sent this message: \"" + comString + "\" and I'm not sure what it means.");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void printnames() {
for (BudaClientThread t: BudaServer.Clients) {
System.out.println("user named "+ t.name + " is connected (#" + t.number + ")");
}
}
public void setName(InputStream in) throws IOException {
String nameString = "";
int i;
while (true) {
i = in.read();
if (i == 46) break; //the name ends with a "."
nameString = nameString + (char) i;
}
this.name = nameString;
System.out.println("Client number " + number + " changed their name to: " + nameString);
}
}

View File

@ -1,24 +0,0 @@
package ch.unibas.dmi.dbis.cs108.BudaClientServerStuff;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
public class BudaServer {
public static boolean quit = false; //todo: meaningfully implement this
public static HashSet<BudaClientThread> Clients = new HashSet<BudaClientThread>();
static int connections = 0;
public static void main(String[] args) {
ServerConnector ServC = new ServerConnector();
Thread ServCThread = new Thread(ServC);
ServCThread.start(); //the ServCThread listens for new connections so the server can do other things
while (!quit) {
//Main server stuff goes here
}
//ServCThread.stop(); //todo: find some alternative for this.
System.out.println("stopping the main BudaServer thread.");
System.out.println("Quitting after the next connection is made.");
}
}

View File

@ -1,25 +0,0 @@
import java.io.*;
import java.net.Socket;
public class ClientListener implements Runnable{
private final Socket sock;
public ClientListener(Socket sock) {
this.sock = sock;
}
public void run(){
byte[] command = new byte[5];
String comString;
try {
InputStream in = sock.getInputStream();
in.read(command);
System.out.println("Got a line!");
comString = new String(command);
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -1,14 +0,0 @@
This is a demo of some (basic) client / server functionality.
Run BudaClient.java for the Client and BudaServer.java for the Server. Everything connects locally via port 8090
The client reads from the console input and sends that to the server. The Server generally reads messages in five characters (so a text-based protocol could be based on commands of five characters).
The server can connect to an arbitrary number of clients.
Type "Name:Jonas B." to change the client's name to Jonas B (the "." is where the name stops).
Type "Names" for the Server to display everyone who is connected along with their name (if they have set a name).
Typing "Quitx" should quit everything but I havent quite managed to implement that yet so just repeatedly use ctrl+c to quit everything.
-Jonas

View File

@ -1,29 +0,0 @@
package ch.unibas.dmi.dbis.cs108.BudaClientServerStuff;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerConnector implements Runnable{
public void run() {
try {
System.out.println(
"Warte auf Verbindungen auf Port 8090...");
ServerSocket servSock = new ServerSocket(8090);
while (true) {
Socket socket = servSock.accept();
System.out.println("got a connection: socket " + BudaServer.connections + socket.toString());
BudaClientThread newClientThread = new BudaClientThread(++BudaServer.connections, socket);
BudaServer.Clients.add(newClientThread);
Thread bCT = new Thread(newClientThread);
bCT.start();
}
} catch (IOException e) {
System.out.println("server got an error");
System.err.println(e);
System.exit(1);
}
}
}

View File

@ -1,9 +1,10 @@
package ch.unibas.dmi.dbis.cs108.Klassenstruktur;
public class Ghost extends Passenger {
protected boolean isOG; //true if the Ghost is the original ghost.
public boolean getIsOG() {
return isOG;
}
protected boolean isOG; //true if the Ghost is the original ghost.
public boolean getIsOG() {
return isOG;
}
}

View File

@ -1,22 +1,25 @@
package ch.unibas.dmi.dbis.cs108.Klassenstruktur;
public class GhostNPC extends Ghost{
public class GhostNPC extends Ghost {
/**
* Creates a new GhostNPC. Should be used at game start or if a HumanNPC is turned into a ghost.
* @param position position on the train
* @param name player name. if null, then a default name is used.
* @param isOG true if the ghost is the original ghost.
*/
public GhostNPC(int position, String name, boolean isOG) {
this.isOG = isOG;
this.position = position;
this.clientHandler = null;
isGhost = true;
isPlayer = false;
kickedOff = false;
if (name == null) {
this.name = "Robot Nr. " + position;
} else this.name = name;
}
/**
* Creates a new GhostNPC. Should be used at game start or if a HumanNPC is turned into a ghost.
*
* @param position position on the train
* @param name player name. if null, then a default name is used.
* @param isOG true if the ghost is the original ghost.
*/
public GhostNPC(int position, String name, boolean isOG) {
this.isOG = isOG;
this.position = position;
this.clientHandler = null;
isGhost = true;
isPlayer = false;
kickedOff = false;
if (name == null) {
this.name = "Robot Nr. " + position;
} else {
this.name = name;
}
}
}

View File

@ -2,28 +2,32 @@ package ch.unibas.dmi.dbis.cs108.Klassenstruktur;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
public class GhostPlayer extends Ghost{
public class GhostPlayer extends Ghost {
/**
* Creates a new GhostPlayer. Should be used at game start or if a HumanPlayer is turned into a ghost.
* @param position position on the train
* @param name name. if null, then a default name is used.
* @param isOG true if the ghost is the original ghost.
*/
public GhostPlayer(int position, String name, ClientHandler clientHandler, boolean isOG) {
this.position = position;
this.clientHandler = clientHandler;
this.isOG = isOG;
isGhost = true;
isPlayer = true;
kickedOff = false;
if (name == null) {
this.name = "Player Nr. " + position;
} else this.name = name;
/**
* Creates a new GhostPlayer. Should be used at game start or if a HumanPlayer is turned into a
* ghost.
*
* @param position position on the train
* @param name name. if null, then a default name is used.
* @param isOG true if the ghost is the original ghost.
*/
public GhostPlayer(int position, String name, ClientHandler clientHandler, boolean isOG) {
this.position = position;
this.clientHandler = clientHandler;
this.isOG = isOG;
isGhost = true;
isPlayer = true;
kickedOff = false;
if (name == null) {
this.name = "Player Nr. " + position;
} else {
this.name = name;
}
}
public void send(String msg) {
//todo(Jonas): pass message along to client.
}
public void send(String msg) {
//todo(Jonas): pass message along to client.
}
}

View File

@ -1,4 +1,5 @@
package ch.unibas.dmi.dbis.cs108.Klassenstruktur;
public class Human extends Passenger {
}

View File

@ -1,20 +1,23 @@
package ch.unibas.dmi.dbis.cs108.Klassenstruktur;
public class HumanNPC extends Human {
/**
* Creates a new HumanNPC.
* @param position position on the train
* @param name player name. if null, then a default name is used.
*
*/
public HumanNPC(int position, String name) {
this.position = position;
this.clientHandler = null;
isGhost = false;
isPlayer = false;
kickedOff = false;
if (name == null) {
this.name = "Robot Nr. " + position;
} else this.name = name;
/**
* Creates a new HumanNPC.
*
* @param position position on the train
* @param name player name. if null, then a default name is used.
*/
public HumanNPC(int position, String name) {
this.position = position;
this.clientHandler = null;
isGhost = false;
isPlayer = false;
kickedOff = false;
if (name == null) {
this.name = "Robot Nr. " + position;
} else {
this.name = name;
}
}
}

View File

@ -2,22 +2,27 @@ package ch.unibas.dmi.dbis.cs108.Klassenstruktur;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
public class HumanPlayer extends Human{
/**
* Creates a new GhostPlayer. Should be used at game start or if a HumanPlayer is turned into a ghost.
* @param position position on the train
* @param name name. if null, then a default name is used.
*/
public HumanPlayer(int position, String name, ClientHandler clientHandler, boolean isOG) {
this.position = position;
this.clientHandler = clientHandler;
isGhost = false;
isPlayer = true;
kickedOff = false;
if (name == null) {
this.name = "Player Nr. " + position;
} else this.name = name;
public class HumanPlayer extends Human {
/**
* Creates a new GhostPlayer. Should be used at game start or if a HumanPlayer is turned into a
* ghost.
*
* @param position position on the train
* @param name name. if null, then a default name is used.
*/
public HumanPlayer(int position, String name, ClientHandler clientHandler, boolean isOG) {
this.position = position;
this.clientHandler = clientHandler;
isGhost = false;
isPlayer = true;
kickedOff = false;
if (name == null) {
this.name = "Player Nr. " + position;
} else {
this.name = name;
}
}
}

View File

@ -3,67 +3,72 @@ package ch.unibas.dmi.dbis.cs108.Klassenstruktur;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
public class Passenger {
protected int position; //the player's Cabin number (0 to 5)
protected String name; //the player's Name
protected Boolean isGhost; //boolean regarding if the player is a ghost. Could probably be removed since ghost is a subclass but I'm keeping it in.
protected Boolean isPlayer; //same here
protected Boolean kickedOff; //true if the player has been voted off.
protected ClientHandler clientHandler; //the socket for the client associated with this Passenger, for NPCs, this can be null.
/**
* Sends a protocol message to the respective player.
* @param msg the message that is sent to this player.
**/
public void send(String msg) {
//todo: send protocol message to the respective client OR process messages for NPCS
}
protected int position; //the player's Cabin number (0 to 5)
protected String name; //the player's Name
protected Boolean isGhost; //boolean regarding if the player is a ghost. Could probably be removed since ghost is a subclass but I'm keeping it in.
protected Boolean isPlayer; //same here
protected Boolean kickedOff; //true if the player has been voted off.
protected ClientHandler clientHandler; //the socket for the client associated with this Passenger, for NPCs, this can be null.
/**
* sets the Position of this passenger
* @param position the position of this passenger
*/
public void setPosition(int position) {
this.position = position;
}
/**
* Sends a protocol message to the respective player.
*
* @param msg the message that is sent to this player.
**/
public void send(String msg) {
//todo: send protocol message to the respective client OR process messages for NPCS
}
/**
* sets the name of this passenger.
* @param name the new name for this passenger.
*/
public void setName(String name) {
this.name = name;
}
/**
* sets the Position of this passenger
*
* @param position the position of this passenger
*/
public void setPosition(int position) {
this.position = position;
}
/**
* sets the kickedOff status of this Passenger
* @param kickedOff should be set to true if the passenger has been kicked off.
*/
public void setKickedOff(boolean kickedOff) {
this.kickedOff = kickedOff;
}
/**
* sets the name of this passenger.
*
* @param name the new name for this passenger.
*/
public void setName(String name) {
this.name = name;
}
public int getPosition() {
return position;
}
/**
* sets the kickedOff status of this Passenger
*
* @param kickedOff should be set to true if the passenger has been kicked off.
*/
public void setKickedOff(boolean kickedOff) {
this.kickedOff = kickedOff;
}
public int getPosition() {
return position;
}
public String getName() {
return name;
}
public String getName() {
return name;
}
public Boolean getIsGhost() {
return isGhost;
}
public Boolean getIsGhost() {
return isGhost;
}
public Boolean getKickedOff() {
return kickedOff;
}
public Boolean getKickedOff() {
return kickedOff;
}
public Boolean getIsPlayer() {
return isPlayer;
}
public Boolean getIsPlayer() {
return isPlayer;
}
public ClientHandler getClientHandler() {
return clientHandler;
}
public ClientHandler getClientHandler() {
return clientHandler;
}
}

View File

@ -1,44 +0,0 @@
package ch.unibas.dmi.dbis.cs108.Spiellogikentwurf;
public class Game {
/**
* Can be extended for optional Game-settings
**/
protected int nrOfPlayers; //sets the length of the train
protected int nrOfGhosts; // sets how many Ghosts we start witch
protected int nrOfUsers; // safes how many clients are active in this Game
protected GameFunctions gameFunctions;
/**
* Constructs a Game instance where:
*
* @param nrOfPlayers is the length of the Train
* @param nrOfGhosts is the number of OG Ghosts you want to start with and
* @param nrOfUsers is the number of active users at the time (non NPCs)
*/
Game(int nrOfPlayers, int nrOfGhosts, int nrOfUsers)
throws TrainOverflow { //ToDo: Who handles Exception how and where
this.nrOfPlayers = nrOfPlayers;
this.nrOfGhosts = nrOfGhosts;
this.nrOfUsers = nrOfUsers;
this.gameFunctions = new GameFunctions(nrOfPlayers, nrOfGhosts, nrOfUsers);
}
public static void main(String[] args) {
try {
Game game1 = new Game(6, 1, 1);
for (int j = 0; j < game1.nrOfPlayers; j++) {
System.out.println(game1.gameFunctions.passengerTrain[j].getPosition());
}
} catch (TrainOverflow e) {
System.out.println(e.getMessage());
}
}
}

View File

@ -1,61 +0,0 @@
package ch.unibas.dmi.dbis.cs108.Spiellogikentwurf;
import ch.unibas.dmi.dbis.cs108.Klassenstruktur.Human;
import ch.unibas.dmi.dbis.cs108.Klassenstruktur.Passenger;
public class GameFunctions {
/**
* Can be extended for optional Game-settings
**/
int nrOfPlayers; //sets the length of the train
int nrOfGhosts; // sets how many Ghosts we start witch
int nrOfUsers; // safes how many clients are active in this Game
Train train; // safes who sits where
Passenger[] passengerTrain;
/**
* Constructs a GameFunctions instance where nrOfPlayers >= nrOfUsers. Fills passengerTrain with
* only humans
*
* @param nrOfPlayers is the length of the Train
* @param nrOfGhosts is the number of OG Ghosts you want to start with and
* @param nrOfUsers is the number of active users at the time (non NPCs)
* @throws TrainOverflow if nrOfPlayers < nrOfUsers
*/
GameFunctions(int nrOfPlayers, int nrOfGhosts, int nrOfUsers)
throws TrainOverflow { //ToDo: where will Exception be handled?
this.nrOfPlayers = nrOfPlayers;
this.nrOfGhosts = nrOfGhosts;
this.nrOfUsers = nrOfUsers;
this.train = new Train(nrOfPlayers, nrOfUsers);
Passenger[] passengerTrain = new Passenger[nrOfPlayers]; //Creates an array with Passengers with correlation positions (Train)
for (int i = 0; i < nrOfPlayers; i++) {
Human h = new Human();
h.setPosition(train.orderOfTrain[i]);
passengerTrain[i] = h;
}
}
public int getNrOfGhosts() {
return nrOfGhosts;
}
public int getNrOfPlayers() {
return nrOfPlayers;
}
public int getNrOfUsers() {
return nrOfUsers;
}
public void ghostyfy(int position) {
}
public Passenger voteEval() {
return new Passenger();
}
}

View File

@ -1,79 +0,0 @@
package ch.unibas.dmi.dbis.cs108.Spiellogikentwurf;
public class Train {
int[] orderOfTrain; //gives the random order in which the passengers enter the train
int positionOfGhost;
/**
* Constructs a Train with orderOfTrain of the size nrOfPlayers, filled with a Random order of the
* numbers 0-5. Puts the ghost approx in the middle of this order.
*
* @param nrOfPlayers sets how many Players fit in the Train
* @param nrOfUsers sets how many of the Players are Users (vs NPCs)
* @throws TrainOverflow if you want to play with to many users (Train is to small)
*/
Train(int nrOfPlayers, int nrOfUsers) throws TrainOverflow {
if (nrOfPlayers < nrOfUsers) {
throw new TrainOverflow(); //ToDo: What kind of Exception must be thrown here and who handles it how?
}
int[] userTrain = new int[nrOfPlayers];
int[] check = new int[nrOfPlayers];
int i = 0;
int zeroCounter = 0;
while (i < nrOfPlayers) {
int randomNr = (int) ((Math.random() * ((nrOfPlayers)) + 0));
if (randomNr == 0) {
if (zeroCounter == 0) {
i++;
zeroCounter++;
}
}
if (!isInArray(check, randomNr)) {
userTrain[i] = randomNr;
check[randomNr] = randomNr;
i++;
}
}
this.orderOfTrain = userTrain;
this.positionOfGhost = nrOfPlayers / 2;
}
/**
* Checks if the key is to be found in an array
*
* @param array the array to be searched
* @param key the value searched for
* @return true if and only if key is found
*/
static public boolean isInArray(int[] array, int key) {
int i = 0;
while (i < array.length) {
if (array[i] == key) {
return true;
}
i++;
}
return false;
}
public static void main(String[] args) {
int[] a = {1, 2, 3, 4, 5};
System.out.println(isInArray(a, 0));
//Test
try {
Train t = new Train(6, 1);
for (int i = 0; i < 6; i++) {
System.out.print("|" + t.orderOfTrain[i] + "|");
}
System.out.println(" Ghost:" + t.positionOfGhost);
} catch (TrainOverflow e) {
System.out.println(e.getMessage());
}
}
}

View File

@ -1,11 +0,0 @@
package ch.unibas.dmi.dbis.cs108.Spiellogikentwurf;
public class TrainOverflow extends Exception {
private static final String message = "Too many users are logged on";
@Override
public String getMessage() {
return message;
}
}

View File

@ -1,12 +0,0 @@
package ch.unibas.dmi.dbis.cs108.example;
/**
* A simple HelloWorld class.
*/
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}

View File

@ -1,32 +0,0 @@
package ch.unibas.dmi.dbis.cs108.example.gui.javafx;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
* This is an example JavaFX-Application.
*/
public class GUI extends Application {
/**
* Launching this method will not work on some platforms.
* What you should do is to create a separate main class and launch the GUI class from there as is done in {@link Main}
*/
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
String javaVersion = System.getProperty("java.version");
String javafxVersion = System.getProperty("javafx.version");
Label l = new Label("Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".");
Scene scene = new Scene(new StackPane(l), 640, 480);
stage.setScene(scene);
stage.show();
}
}

View File

@ -1,14 +0,0 @@
package ch.unibas.dmi.dbis.cs108.example.gui.javafx;
import javafx.application.Application;
public class Main {
/**
* This is simply a wrapper to launch the {@link GUI} class.
* The reason this class exists is documented in {@link GUI#main(String[])}
*/
public static void main(String[] args) {
Application.launch(GUI.class, args);
}
}

View File

@ -1,29 +0,0 @@
package ch.unibas.dmi.dbis.cs108.example.gui.swing;
import javax.swing.JFrame;
import javax.swing.JLabel;
/**
* This is an example Swing Application
*/
public class SwingGUI {
private static void createAndShowGUI() {
// Make sure we have nice window decorations
JFrame.setDefaultLookAndFeelDecorated(true);
// Create and set up the window.
JFrame frame = new JFrame("HelloWorldSwing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Add the ubiquitous "Hello World" label
JLabel label = new JLabel("Hello World");
frame.getContentPane().add(label);
// Display the window
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
// Schedule a job for the event-dispatching thread: // creating and showing this application's GUI
javax.swing.SwingUtilities.invokeLater(() -> createAndShowGUI());
}
}

View File

@ -1,19 +1,20 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client;
import ch.unibas.dmi.dbis.cs108.multiplayer.protocol.NoLegalProtocolCommandStringFoundException;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.NameGenerator;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.ClientPinger;
import java.net.Socket;
import java.io.*;
import java.net.UnknownHostException;
import java.util.Scanner;
public class Client {
private Socket socket;
private BufferedReader in;
private BufferedWriter out;
public String userName;
public ClientPinger clientPinger;
public Client(Socket socket, String userName) {
try {
@ -21,37 +22,32 @@ public class Client {
this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
this.in = new BufferedReader((new InputStreamReader((socket.getInputStream()))));
//TODO add the system based generated username here.
//TODO hide connecting logik(next 4 lines)
this.userName = userName;
this.out.write(getUsername());
this.out.newLine();
this.out.flush();
clientPinger = new ClientPinger(this.out, this.socket);
} catch (IOException e) {
e.printStackTrace();
closeEverything(socket, in, out);
}
}
/**
*
*/
public void sendMessage() {
try {
Scanner sc = new Scanner(System.in);
while (socket.isConnected()) {
String msg = sc.nextLine();
String encodedMsg = "";
try {
encodedMsg = encodeMessage(msg);
} catch (NoLegalProtocolCommandStringFoundException e) {
System.out.println("ERROR: no legal command found");
encodedMsg = "";
} catch (EmptyClientInputException e) {
//Maybe this exception shouldn't do anything.
} finally {
out.write(encodedMsg);
out.newLine();
out.flush();
}
String formattedMSG = MessageFormatter.formatMsg(msg);
out.write(formattedMSG);
out.newLine();
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
@ -59,23 +55,10 @@ public class Client {
}
}
/**
* Uses <code>NTtBProtocolParser</code> to turn Client input into the NTtB Protocol format. Must
* be called before a client input is sent to the server.
*
* @param msg the msg to be encoded.
* @return Message encoded adhering to the NTtB Protocoll.
*/
private String encodeMessage(String msg)
throws NoLegalProtocolCommandStringFoundException, EmptyClientInputException {
NTtBProtocolParser pp = new NTtBProtocolParser(this);
return pp.parseMsg(msg);
}
//TODO implement decoding of server input
private String decodeServerMsg(String msg){return null;}
/**
* Listens for incoming messages
* Starts a thread which listens for incoming messages
*/
public void chatListener() {
/*TODO: what type of decoding has to be done
@ -84,12 +67,13 @@ public class Client {
new Thread(new Runnable() {
@Override
public void run() {
String chatMsg;
while (socket.isConnected()) {
try {
chatMsg = in.readLine();
System.out.println(chatMsg);
parse(chatMsg);
} catch (IOException e) {
e.printStackTrace();
closeEverything(socket, in, out);
@ -100,6 +84,29 @@ public class Client {
}).start();
}
/**
* Sends a message to the server, as is.
* @param msg the message sent. Should already be protocol-formatted.
*/
public void sendMsgToServer(String msg) {
try {
out.write(msg);
out.newLine();
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* parses a received message according to the client protocol.
* @param msg the message to be parsed.
*/
public void parse(String msg) {
JClientProtocolParser.parse(msg, this);
}
public void closeEverything(Socket socket, BufferedReader in, BufferedWriter out) {
//TODO Correctly closing a clients connection
@ -129,14 +136,17 @@ public class Client {
} else {
hostname = args[0];
}
System.out.println("Choose a nickname: ");
String systemName = System.getProperty("user.name");
System.out.println("Choose a nickname (Suggestion: " + systemName + "): ");
String username = sc.next();
Socket socket;
try {
socket = new Socket(hostname, 42069);
Client client = new Client(socket, username);
client.chatListener();
client.sendMessage();
Thread cP = new Thread(client.clientPinger);
cP.start();
client.sendMessage(); //this one blocks.
} catch (UnknownHostException e) {
System.out.println("Invalid host IP");
} catch (IOException e) {
@ -148,6 +158,4 @@ public class Client {
public String getUsername() {
return userName;
}
}

View File

@ -1,15 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client;
public class EmptyClientInputException extends Exception {
String exceptionMsg;
Client whoDunIt;
public EmptyClientInputException(Client whoDunIt) {
this.whoDunIt = whoDunIt;
this.exceptionMsg = whoDunIt.getUsername() + " tried to send an empty message";
}
public String getExceptionMsg(){
return exceptionMsg;
}
}

View File

@ -1,36 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client;
import ch.unibas.dmi.dbis.cs108.multiplayer.protocol.NightTrainProtocol;
import ch.unibas.dmi.dbis.cs108.multiplayer.protocol.NoLegalProtocolCommandStringFoundException;
import java.util.HashMap;
import java.util.HashSet;
public class InputToProtocolMap {
private static final HashMap<String, NightTrainProtocol.NTtBCommands> encoding;
private static final HashSet<String> legalClientInput;
static {
//First add all legal commands to a map
HashMap<String, NightTrainProtocol.NTtBCommands> builder = new HashMap<>();
builder.put("chat", NightTrainProtocol.NTtBCommands.CHATA);
builder.put("cn", NightTrainProtocol.NTtBCommands.CUSRN);
builder.put("list", NightTrainProtocol.NTtBCommands.LISTP);
builder.put("exit", NightTrainProtocol.NTtBCommands.LEAVG);
//TODO extend according to extended function
//Initialize static final map and set
legalClientInput = new HashSet<>(builder.keySet());
encoding = new HashMap<>(builder);
}
public static String encode(String toEncode) throws NoLegalProtocolCommandStringFoundException {
if (legalClientInput.contains(toEncode)) {
return encoding.get(toEncode).toString();
} else {
throw new NoLegalProtocolCommandStringFoundException();
}
}
}

View File

@ -0,0 +1,34 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
public class JClientProtocolParser {
/**
* Used by the client to parse an incoming protocol message.
* @param msg the encoded message that needs to be parsed
* @param c this Client(required so this method can access the Client's methods)
*/
public static void parse(String msg, Client c) {
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");
}
//System.out.println(header); helpful for debugging
switch (header) {
case "SPING":
c.sendMsgToServer("PINGB");
break;
case "PINGB":
c.clientPinger.setGotPingBack(true);
break;
case "CHATM":
System.out.println(msg.substring(6));
break;
default:
System.out.println("Received unknown command");
}
}
}

View File

@ -0,0 +1,44 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client;
public class MessageFormatter {
/**
* Takes a given Message and reformats it to where the JServerProtocolParser.parse() method can
* handle it. May need to be redesigned one the games uses a GUI
*
* @param msg the Messaged to be reformatted
* @return the reformatted message in the form HEADR$msg
*/
public static String formatMsg(String msg) {
String header = ""; //header is first two characters
StringBuilder stringBuilder = new StringBuilder();
String s; // just a friendly helper to save message in
try {
header = msg.substring(0, 2);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
switch (header) {
case "/c":
stringBuilder.append("CHATA$");
s = msg.substring(3);
break;
case "/q":
stringBuilder.append("QUITS$");
s = msg.substring(3);
break;
case "/n":
stringBuilder.append("NAMEC$");
s = msg.substring(3);
break;
default:
s = msg;
}
stringBuilder.append(s);
return stringBuilder.toString();
}
}

View File

@ -1,76 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client;
import ch.unibas.dmi.dbis.cs108.multiplayer.protocol.NoLegalProtocolCommandStringFoundException;
import java.util.ArrayList;
import java.util.Scanner;
/**
* Implements a protocol parser for the NTtB protocoll, that transforms client input into a server
* readable format.
*/
public class NTtBProtocolParser implements ProtocolParser {
//TODO Possibly bad name, rename to clientMsgParser?
public final Client caller;
public static InputToProtocolMap legalCommands = new InputToProtocolMap();
public NTtBProtocolParser(Client caller) {
this.caller = caller;
}
@Override
public String parseMsg(String msg)
throws NoLegalProtocolCommandStringFoundException, EmptyClientInputException {
Scanner sc = new Scanner(msg);
ArrayList<String> input = new ArrayList<>();
String parsedMsg;
while (sc.hasNext()) {
input.add(sc.next());
}
return buildProtocolMsg(input);
}
private String buildProtocolMsg(ArrayList<String> input)
throws EmptyClientInputException, NoLegalProtocolCommandStringFoundException {
//TODO
if (emptyClientInput(input)) {
throw new EmptyClientInputException(caller);
}
StringBuilder s = new StringBuilder(); //friendly little helper
s.append(InputToProtocolMap.encode(input.get(0)));
if (containsParameters(input)) {
int size = input.size();
for (int i = 1; i < size; i++) {
s.append("$");
s.append(input.get(i).toLowerCase()); //parameters are always lower case (is that good?)
}
}
return s.toString();
}
/**
* Checks if input has parameters
* <p>
* if the list size is smaller than 2, i.e. not larger than 1, the input only contains a command.
*
* @param input the tokenized input string.
* @return true if input list is larger than 2.
*/
private boolean containsParameters(ArrayList<String> input) {
return input.size() > 1;
}
/**
* checks if client input is empty
*
* @param clientInput the clients input.
* @return true if client didn't send any input besides whitespace
*/
private boolean emptyClientInput(ArrayList<String> clientInput) {
return clientInput.isEmpty();
}
}

View File

@ -1,12 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client;
public interface ProtocolParser {
/**
* Takes a String from client input and parses into server readable message.
*
* @param msg the message to be parsed
* @return a String message formatted for the specific protocol
*/
String parseMsg(String msg) throws Exception;
}

View File

@ -31,27 +31,27 @@ public class ClientPinger implements Runnable {
@Override
public void run() {
try {
Thread.sleep(2000);
while (socket.isConnected()) {
gotPingBack = false;
out.write("CPING");
out.newLine();
out.flush();
Thread.sleep(2000);
Thread.sleep(4000);
if (gotPingBack) {
if (!isConnected) { //if !isConnected, then the connection had been lost before.
isConnected = true;
System.out.println("Connection regained!");
}
} else {
isConnected = false;
System.out.println("Lost connection. Waiting to reconnect...");
if (isConnected) {
isConnected = false;
System.out.println("Lost connection. Waiting to reconnect...");
}
}
}
isConnected = false; //in case the socket accidentally disconnects (can this happen?)
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
isConnected = false; //in case the socket accidentally disconnects (can this happen?)
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}
}

View File

@ -0,0 +1,31 @@
Client commands:
Implemented:
* CHATA$message Send chat message to all
* QUITS quit server/ leave server
* CPING Ping from client to server.
* PINGB Pingback from client to server.
* NAMEC$name Change name to whatever is specified
Future / planned:
* CRTGM Create a new game
* CHATW whisper chat
* CHATG ghost chat
* LEAVG leave a game
* JOING join a game
* VOTEG ghost voting who to infect
* VOTEH humans voting who is the ghost
* LISTP list players/clients in session with the Server
Server Commands:
Implemented:
* SPING Ping from server to client
* PINGB Pingback from client to server.
Future / planned:
* MSGRS "Message received": Paramaters: a string detailing to the client that and what the server received as command.
* SEROR Server had an error. (used for debugging)
* NOCMD No command found.

View File

@ -31,26 +31,27 @@ public class ServerPinger implements Runnable {
@Override
public void run() {
try {
Thread.sleep(2000);
while (socket.isConnected()) {
gotPingBack = false;
out.write("SPING");
out.newLine();
out.flush();
Thread.sleep(2000);
Thread.sleep(4000);
if (gotPingBack) {
if (!isConnected) { //if !isConnected, then the connection had been lost before.
isConnected = true;
System.out.println("Connection regained!");
}
} else {
isConnected = false;
System.out.println("Lost connection. Waiting to reconnect...");
if (isConnected) {
isConnected = false;
System.out.println("Lost connection. Waiting to reconnect...");
}
}
}
isConnected = false; //in case the socket accidentally disconnects (can this happen?)
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}
}

View File

@ -1,50 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.protocol;
import java.util.LinkedList;
import java.util.Queue;
/**
* This class defines what type the ClientMsgDecoder returns after decoding the message. This is
* done so the output can be split into a response to the client and action in to the game logik.
* commands should map to methods(maybe classes) parameters map to method parameters
*/
public class NTtBFormatMsg {
private String msgToClient;
private NightTrainProtocol.NTtBCommands command;
private final String[] parameters; //TODO maybe use array?
public NTtBFormatMsg(String msgToClient, NightTrainProtocol.NTtBCommands command,
String[] parameters) {
this.msgToClient = msgToClient;
this.command = command;
this.parameters = parameters;
}
public NTtBFormatMsg() {
this.msgToClient = "";
this.command = null;
this.parameters = new String[]{""};
}
public String getMessage() {
return msgToClient;
}
public NightTrainProtocol.NTtBCommands getCommand() {
return command;
}
public String[] getParameters() {
return parameters;
}
protected void setMsgToClient(String msgToClient) {
this.msgToClient = msgToClient;
}
protected void setCommand(NightTrainProtocol.NTtBCommands command) {
this.command = command;
}
}

View File

@ -1,9 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.protocol;
public interface NTtBInputType {
String msg = null;
public String getValue();
public void setValue();
}

View File

@ -1,18 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.protocol;
public class NTtBParameter implements NTtBInputType {
String parameterValue;
public NTtBParameter(String parameterValue) {
this.parameterValue = parameterValue;
}
@Override
public String getValue() {
return parameterValue;
}
@Override
public void setValue() {
//Possibly do not need
}
}

View File

@ -1,21 +0,0 @@
/**
* Client commands:
* CRTGM: Create a new game
* CHATA: chat to all
* CHATW: whisper chat
* CHATG: ghost chat
* LEAVG: leave a game
* JOING: join a game
* VOTEG: ghost voting who to infect
* VOTEH: humans voting who is the ghost
* QUITS: quit server/ leave server
* LISTP: list players/clients in session with the Server
* CPING: Ping from client to server.
*/
/**
Server Commands:
* MSGRS: "Message received": Paramaters: a string detailing to the client that and what the server received as command.
* SEROR: Server had an error. (used for debugging)
* SPING: Ping from server to client;
* NOCMD: Co command found.
*/

View File

@ -1,66 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.protocol;
import java.util.HashMap;
import java.util.HashSet;
/*
The NightTrainProtocol implements the Communication-Protocol of the
"Night Train to Budapest"" game. It acts as an Interface between Client and server. All Client Messages are
piped through this protocol, in order for the Server to execute the correct action. It is used by the ClientHandler
for this purpose.
*/
public class NightTrainProtocol {
//TODO: initialite the fields
private static HashMap<String, NTtBCommands> stringNTtBCommandsHashMap = initializeMapping();
private static ProtocolValidator protocolValidator;
private static HashSet<String> legalStrings = new HashSet<>(stringNTtBCommandsHashMap.keySet());
public enum NTtBCommands {
//Client Commands
CRTGM, CHATA, CHATW, CHATG, LEAVG, JOING, VOTEG, QUITS, LISTP, CUSRN,CPING,
//Server Responses
MSGRS, SEROR, SPING, NOCMD
}
private static HashMap<String, NTtBCommands> initializeMapping(){
HashMap<String, NTtBCommands> map = new HashMap<>();
for(NTtBCommands cmd: NTtBCommands.values()) {
map.put(cmd.toString(), cmd);
}
return map;
}
//getters & setters
public static HashMap<String, NTtBCommands> getStringNTtBCommandsHashMap() {
return stringNTtBCommandsHashMap;
}
public static HashSet<String> getLegalStrings() {
return legalStrings;
}
//Utility Methods:
/**
* Validates a given string is a valid representation
* of a protocol command
* @param cmd, the string command to be validated
* @return true if <code>cmd</code> is a valid command
*/
public static boolean isLegalCmdString(String cmd) {
return legalStrings.contains(cmd);
}
public static NTtBCommands getCmdEnumObject(String cmd) throws NoLegalProtocolCommandStringFoundException {
if(isLegalCmdString(cmd)){
return stringNTtBCommandsHashMap.get(cmd);
} else {
throw new NoLegalProtocolCommandStringFoundException();
}
}
//TODO analyize what methods are needed
}

View File

@ -1,4 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.protocol;
public class NoLegalProtocolCommandStringFoundException extends Exception {
}

View File

@ -1,6 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.protocol;
public interface ProtocolDecoder {
public NTtBFormatMsg decodeMsg(String msg);
}

View File

@ -1,23 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.protocol;
import java.util.EnumSet;
import java.util.HashSet;
//TODO Possibly redundant!!
public class ProtocolValidator {
//TODO String or NTtBCommands HashSet?
public static HashSet<NightTrainProtocol.NTtBCommands> legalCommands = initializeLegalCommands();
//Initialize the legalCommands set with the protocol values.
private static HashSet<NightTrainProtocol.NTtBCommands> initializeLegalCommands(){
EnumSet<NightTrainProtocol.NTtBCommands> enumVals = EnumSet.allOf(NightTrainProtocol.NTtBCommands.class);
return new HashSet<>(enumVals);
}
public boolean validateCommand(String s) {
//TODO implement if needed
return false;
}
}

View File

@ -1,13 +1,21 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.server;
/* This class is built to contain the usernames of all players in a single string.
/**
* This class is built to contain the usernames of all players in a single string.
* This allows a duplicate check (--> ClientHandler) when a new player chooses
* a name: does the string with all the previous names contain the new player's
* desired username? If yes, he is being assigned a random name. If no, he can keep
* his desired name. */
* his desired name.
* **/
public class AllClientNames {
static StringBuilder names = new StringBuilder();
/**
* Safes a new name to the List of all Names
* @param currentName the new name to be added
* @return All names adding the new currentName
*/
public static String allNames(String currentName) {
return names.append(currentName).toString();
}

View File

@ -1,147 +1,167 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.server;
import ch.unibas.dmi.dbis.cs108.multiplayer.protocol.NTtBFormatMsg;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.ServerPinger;
import java.io.*;
import java.net.Socket;
import java.util.HashSet;
import java.util.Scanner;
public class ClientHandler implements Runnable {
private String clientUserName;
private BufferedWriter out;
private BufferedReader in;
private Socket socket;
Scanner sc;
public static HashSet<ClientHandler> connectedClients = new HashSet<>();
public static HashSet<ClientHandler> lobby = new HashSet<>();
public static HashSet<ClientHandler> ghostClients = new HashSet<>();
private ClientMsgDecoder clientMsgDecoder = new ClientMsgDecoder();
public class ClientHandler implements Runnable {
/**
* Implements the login logik in client-server
* architecture.
* @param socket the socket on which to make the connection.
*/
public ClientHandler(Socket socket) {
try {
this.socket = socket;
this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
this.in = new BufferedReader((new InputStreamReader((socket.getInputStream()))));
this.clientUserName = in.readLine();
private String clientUserName;
private BufferedWriter out;
private BufferedReader in;
private Socket socket;
Scanner sc;
public ServerPinger serverPinger;
public static HashSet<ClientHandler> connectedClients = new HashSet<>();
public static HashSet<ClientHandler> lobby = new HashSet<>();
public static HashSet<ClientHandler> ghostClients = new HashSet<>();
// duplicate handling: if username already taken, assign random name to client
if (AllClientNames.allNames("").contains(clientUserName)) {
clientUserName = NameGenerator.randomName();
}
// add username to list of all client names for future duplicate checking
AllClientNames.allNames(clientUserName);
connectedClients.add(this);
broadcastMessage("SERVER: " + clientUserName + " has joined the Server");
} catch (IOException e) {
e.printStackTrace();
closeEverything(socket, in, out);
}
/**
* Implements the login logic in client-server architecture.
*
* @param socket the socket on which to make the connection.
*/
public ClientHandler(Socket socket) {
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
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();
broadcastMessage("SERVER: " + clientUserName + " has joined the Server");
} catch (IOException e) {
e.printStackTrace();
closeEverything(socket, in, out);
}
}
//Getters:
public BufferedWriter getOut() {
return out;
//Getters:
public BufferedWriter getOut() {
return out;
}
public BufferedReader getIn() {
return in;
}
public Socket getSocket() {
return socket;
}
public static HashSet<ClientHandler> getConnectedClients() {
return connectedClients;
}
public static HashSet<ClientHandler> getLobby() {
return lobby;
}
public static HashSet<ClientHandler> getGhostClients() {
return ghostClients;
}
//Setters
@Override
/**
* The main logic of the client handler.
* Since every client is put on a string this is where
* most interactions between client and server are held
**/
public void run() {
String msg;
while (socket.isConnected()) {
try {
msg = in.readLine();
JServerProtocolParser.parse(msg, this);
} catch (IOException e) {
e.printStackTrace();
closeEverything(socket, in, out);
break;
}
}
}
public BufferedReader getIn() {
return in;
public String getClientUserName() {
return clientUserName;
}
/**
* Lets the client change their respective username, if the username is already taken, a similar option is chosen
* @param newName The desired new name to replace the old one with.
*/
public void changeUsername(String newName) {
if (AllClientNames.allNames("").contains(newName)) {
newName = NameGenerator.randomName(newName);
}
String h = this.clientUserName; //just a friendly little helper
this.clientUserName = newName;
AllClientNames.allNames(newName);
broadcastMessage(h +" have changed their nickname to " + clientUserName);
}
public static HashSet<ClientHandler> getConnectedClients() {
return connectedClients;
/**
* Broadcasts a Message to all active clients in the form "Username: msg"
* @param msg the Message to be broadcasted
*/
public void broadcastMessage(String msg) {
for (ClientHandler client : connectedClients) {
client.sendMsgToClient("CHATM:" + clientUserName + ": \"" + msg + "\"");
}
}
public static HashSet<ClientHandler> getLobby() {
return lobby;
//TODO: Documentation
/**
*
* @param msg
*/
public void sendMsgToClient(String msg) {
try {
out.write(msg);
out.newLine();
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public static HashSet<ClientHandler> getGhostClients() {
return ghostClients;
public void removeClientHandler() {
connectedClients.remove(this);
broadcastMessage("SERVER: " + clientUserName + " has left the server");
}
public void closeEverything(Socket socket, BufferedReader in, BufferedWriter out) {
removeClientHandler();
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public ClientMsgDecoder getClientMsgDecoder() {
return clientMsgDecoder;
}
public void decodeMsg(String msg) {
//Setters
@Override
/**
* The main logik of the client handler.
* Since every client is put on a string this is where
* most interactions between client and server are held..
*/
public void run() {
String msg;
NTtBFormatMsg response;
while(socket.isConnected()) {
try {
msg = in.readLine();
response = clientMsgDecoder.decodeMsg(msg); //The response of the server to the clients message
out.write(response.getMessage());
out.newLine();
out.flush();
//TODO if merely an acknowledgement is sent back to the client, how does the client recieve game updates?
} catch (IOException e) {
e.printStackTrace();
closeEverything(socket, in, out);
break;
}
}
}
public String getClientUserName() {
return clientUserName;
}
public void broadcastMessage(String msg) {
for (ClientHandler client : connectedClients) {
try {
if(!client.clientUserName.equals((clientUserName))) {
client.out.write(msg);
} else {
client.out.write("Message: **" + msg + "** sent!");
}
client.out.newLine();
client.out.flush();
} catch (IOException e) {
e.printStackTrace();
closeEverything(socket, in ,out);
}
}
}
public void removeClientHandler() {
connectedClients.remove(this);
broadcastMessage("SERVER: " + clientUserName + " has left the server");
}
public void closeEverything(Socket socket, BufferedReader in, BufferedWriter out) {
removeClientHandler();
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void decodeMsg(String msg){
}
}
}

View File

@ -1,96 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.server;
import ch.unibas.dmi.dbis.cs108.multiplayer.protocol.NTtBFormatMsg;
import ch.unibas.dmi.dbis.cs108.multiplayer.protocol.NightTrainProtocol;
import ch.unibas.dmi.dbis.cs108.multiplayer.protocol.NightTrainProtocol.NTtBCommands;
import ch.unibas.dmi.dbis.cs108.multiplayer.protocol.NoLegalProtocolCommandStringFoundException;
import ch.unibas.dmi.dbis.cs108.multiplayer.protocol.ProtocolDecoder;
/**
* Decodes the correctly formatted String containing command and parameters. For reasons of
* seperation of work this class only tokenizes the string and acknowledges to the client that smth
* was recieved. Actual method calls, change of state, method calles etc. are delegated to{@link
* ch.unibas.dmi.dbis.cs108.multiplayer.server.cmd.methods.CommandExecuter} from within {@link
* ClientHandler}.
*/
public class ClientMsgDecoder implements ProtocolDecoder {
/**
* The point of contact for the ClientHandler who calls this method to convert a String in to
* usable, tokanized format defined by {@link NTtBFormatMsg}.
*
* @param msg coming from the client handlers input reader.
* @return {@link NTtBFormatMsg}
*/
@Override
public NTtBFormatMsg decodeMsg(String msg) {
//Declare needed variables
String[] msgTokens; //List where we'll put the string tokens seperated by $.
String ackMsg; //The command token
String[] parameters;
NightTrainProtocol.NTtBCommands cmdObject;
//Initalize fields for return object
msgTokens = tokenizeMsg(msg);
ackMsg = serverResponseBuilder(msgTokens);
parameters = new String[msgTokens.length - 1];
cmdObject = getCommandConstant(msgTokens[0]);
return new NTtBFormatMsg(ackMsg, cmdObject, parameters);
}
/**
* Constructs the server acknowledgement response witch is instantly sent back to the client. The
* response is merely that a command was recieved and which one.
* <p>
* If garbage was recieved a SEROR will be appended. It is assumed that the msgTokens array is not
* empty.
*
* @param msgTokens an array containing the command String
* @return a String containing the immediate response of the server to the client.
*/
private String serverResponseBuilder(String[] msgTokens) {
StringBuilder sb = new StringBuilder();
//assumes non-empty array!
sb.append("SERVER: ");
NightTrainProtocol.NTtBCommands cmd = getCommandConstant(msgTokens[0]);
if (cmd.equals(NTtBCommands.SEROR)) {
return cmd + "invalid input";
}
sb.append("Command *").append(cmd).append("* recieved!");
return sb.toString();
}
/**
* Turns the string into a command object. If no matching protocol command was found, it returns
* an SEROR type.
*
* @param stringToken String that should match the String representation of a NTtBCommands,java
* field.
* @return type {@link ch.unibas.dmi.dbis.cs108.multiplayer.protocol.NightTrainProtocol.NTtBCommands}
* object
*/
private NightTrainProtocol.NTtBCommands getCommandConstant(String stringToken) {
try {
return NightTrainProtocol.getCmdEnumObject(stringToken);
} catch (NoLegalProtocolCommandStringFoundException e) {
return NightTrainProtocol.NTtBCommands.SEROR;
}
}
//TODO What happens if there is no delimeter?
/**
* Splits the input string along the delimiter "$".
*
* @param msg Clients input
* @return an array of String objects containing the command and parameters of the message.
*/
private String[] tokenizeMsg(String msg) {
return msg.split("$");
}
}

View File

@ -1,8 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.server;
public class InSessionLogik {
Server server;
public InSessionLogik(Server server) {
this.server = server;
}
}

View File

@ -0,0 +1,40 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.server;
public class JServerProtocolParser {
/**
* Used by the server (i.e. ClientHandler) to parse an incoming protocol message.
*
* @param msg the encoded message that needs to be parsed
* @param h this ClientHandler (required so this method can access the ClientHandler's methods)
*/
public static void parse(String msg, ClientHandler h) {
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");
}
//System.out.println(header); helpful for debugging
switch (header) {
case "CHATA":
h.broadcastMessage(msg.substring(6));
break;
case "NAMEC":
h.changeUsername(msg.substring(6));
break;
case "CPING":
h.sendMsgToClient("PINGB");
break;
case "PINGB":
h.serverPinger.setGotPingBack(true);
break;
case "QUITS":
h.closeEverything(h.getSocket(), h.getIn(), h.getOut());
break;
default:
System.out.println("Received unknown command");
}
}
}

View File

@ -2,19 +2,29 @@ package ch.unibas.dmi.dbis.cs108.multiplayer.server;
import java.util.Random;
// Creates a String beginning with "player_" followed by 4 random letters
public class NameGenerator {
static String randomName() {
StringBuilder name = new StringBuilder();
Random r = new Random();
for (int i = 0; i < 4; i++) {
char c = (char)(r.nextInt(26) + 'a');
name.append(c);
/**
* Creates a random alteration of a Name by adding 4 numbers at the end of the Name that shall be alterd
* @param username the to be altered username
* @return username + four numbers
*/
static String randomName(String username) {
StringBuilder name;
while (true) {
name = new StringBuilder();
Random r = new Random();
for (int i = 0; i < 4; i++) {
int c = r.nextInt(10);
name.append(c);
}
if (!AllClientNames.allNames("").contains(username + name)) {
break;
}
}
return "player_" + name;
}
public static void main (String[] args) {
System.out.println(randomName());
return username + name;
}
}

View File

@ -1,5 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.server;
public class NoCommandTokenException extends Exception {
}

View File

@ -26,6 +26,12 @@ public class Server {
Thread th = new Thread(nextClient);
connectedClients.add(nextClient);
th.start();
// close socket + remove client if client is disconnected
if (socket.getInputStream().read() == -1) {
System.out.println("client disconnected. closing socket");
socket.close();
connectedClients.remove(nextClient);
}
// close socket + remove client if client is disconnected
if (socket.getInputStream().read() == -1) {

View File

@ -1,110 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.server.cmd.methods;
import ch.unibas.dmi.dbis.cs108.multiplayer.protocol.NTtBFormatMsg;
import ch.unibas.dmi.dbis.cs108.multiplayer.protocol.NightTrainProtocol.NTtBCommands;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
import java.io.IOException;
/**
* This Class implements actually acting on the clients messages.
*/
public class CommandExecuter {
private ClientHandler caller;
private static String msgPrefix = "SERVER: ";
public void execute(NTtBFormatMsg msg) {
switch (msg.getCommand()) {
case CRTGM:
break;
case CHATA:
broadcastClientMsg(msg.getParameters());
break;
case CHATG:
//TODO
break;
case LEAVG:
//TODO
break;
case JOING:
//TODO
break;
case VOTEG:
//TODO
break;
case QUITS:
quitServer();
break;
case CHATW:
wisper(msg.getParameters());
break;
case LISTP:
//TODO
break;
case CUSRN:
changeNickname(msg.getParameters());
break;
case CPING:
pongS();
break;
case MSGRS:
//TODO
break;
case SEROR:
//TODO
break;
case SPING:
pongC();
break;
default:
this.noCommand();
break;
}
}
private void wisper(String[] parameters) {
//TODO
}
private void changeNickname(String[] parameters) {
//TODO
}
private void quitServer() {
//TODO
}
private void pongC() {
//TODO
}
private void pongS() {
//TODO
}
private void noCommand() {
try {
caller.getOut().write(msgPrefix + String.valueOf(NTtBCommands.NOCMD));
} catch (IOException e) {
System.out.println("IOException in noCommand() in CommandExecuter.java");
e.printStackTrace();
}
}
/**
* boradcast chat message to everyone
*
* @param parameters should only have one entry i.e. parameters.length == 1 should be true;
*/
private static void broadcastClientMsg(String[] parameters) {
try {
for (ClientHandler clients : ClientHandler.connectedClients) {
clients.getOut().write(parameters[0]);
}
} catch (IOException e) {
System.out.println("IOEXCEPTION in CommandExecuter.java at broadcastClientMsg");
}
}
}

View File

@ -1,8 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.server.cmd.methods;
public interface msgToMethod {
void quit();
}

View File

@ -1,8 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.server;
/**
* Implements the communication protocol in the connecting phase.
* After this one can consider the server and client in session;
*/
public class connectingLogik {
}