Merge remote-tracking branch 'origin/develop' into Story66_Collision

# Conflicts:
#	src/main/java/seng302/gameServer/GameState.java
#	src/main/java/seng302/gameServer/MainServerThread.java
#	src/main/java/seng302/gameServer/ServerToClientThread.java
#	src/main/java/seng302/model/Yacht.java
#	src/main/java/seng302/model/mark/MarkOrder.java
#	src/main/java/seng302/visualiser/GameView.java
#	src/main/java/seng302/visualiser/controllers/RaceViewController.java
This commit is contained in:
Zhi You Tan
2017-08-15 14:30:01 +12:00
42 changed files with 1106 additions and 477 deletions
@@ -12,8 +12,6 @@ import java.net.SocketException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import java.util.zip.CRC32;
@@ -26,23 +24,41 @@ import seng302.model.stream.packets.StreamPacket;
import seng302.model.stream.xml.generator.Race;
import seng302.model.stream.xml.generator.Regatta;
import seng302.utilities.XMLGenerator;
import seng302.gameServer.server.messages.BoatActionType;
import seng302.gameServer.server.messages.BoatAction;
import seng302.gameServer.server.messages.BoatLocationMessage;
import seng302.gameServer.server.messages.BoatStatus;
import seng302.gameServer.server.messages.BoatSubMessage;
import seng302.gameServer.server.messages.ClientType;
import seng302.gameServer.server.messages.Message;
import seng302.gameServer.server.messages.RaceStatus;
import seng302.gameServer.server.messages.RaceStatusMessage;
import seng302.gameServer.server.messages.RaceType;
import seng302.gameServer.server.messages.RegistrationResponseMessage;
import seng302.gameServer.server.messages.RegistrationResponseStatus;
import seng302.gameServer.server.messages.XMLMessage;
import seng302.gameServer.server.messages.XMLMessageSubType;
import seng302.model.Player;
import seng302.model.Yacht;
import seng302.model.stream.packets.PacketType;
import seng302.model.stream.packets.StreamPacket;
import seng302.model.stream.xml.generator.Race;
import seng302.model.stream.xml.generator.Regatta;
import seng302.utilities.XMLGenerator;
/**
* A class describing a single connection to a Client for the purposes of sending and receiving on
* its own thread. All server threads created and owned by the server thread handler which can
* trigger client updates on its threads Created by wmu16 on 13/07/17.
*/
public class ServerToClientThread implements Runnable, Observer {
public class ServerToClientThread implements Runnable {
/**
* Called to notify listeners when this thread receives a connection correctly.
*/
@FunctionalInterface
interface ConnectionListener {
void notifyConnection ();
}
private static final Integer LOG_LEVEL = 1;
private static final Integer MAX_ID_ATTEMPTS = 10;
@@ -55,41 +71,55 @@ public class ServerToClientThread implements Runnable, Observer {
private ByteArrayOutputStream crcBuffer;
private Boolean userIdentified = false;
private Boolean connected = true;
private Boolean updateClient = true;
// private Boolean initialisedRace = true;
private Integer seqNo;
private Integer sourceId;
private ClientType clientType;
private Boolean isRegistered = false;
private XMLGenerator xml;
private List<ConnectionListener> connectionListeners = new ArrayList<>();
private Yacht yacht;
public ServerToClientThread(Socket socket) {
this.socket = socket;
seqNo = 0;
try{
is = socket.getInputStream();
os = socket.getOutputStream();
} catch (IOException e) {
return;
}
thread = new Thread(this);
thread.start();
}
private void setUpPlayer(){
BufferedReader fn;
String fName = "";
BufferedReader ln;
String lName = "";
try {
is = socket.getInputStream();
os = socket.getOutputStream();
fn = new BufferedReader(
fn = new BufferedReader(
new InputStreamReader(
ServerToClientThread.class.getResourceAsStream(
"/server_config/CSV_Database_of_First_Names.csv"
)
ServerToClientThread.class.getResourceAsStream(
"/server_config/CSV_Database_of_First_Names.csv"
)
)
);
List<String> all = fn.lines().collect(Collectors.toList());
fName = all.get(ThreadLocalRandom.current().nextInt(0, all.size()));
ln = new BufferedReader(
);
List<String> all = fn.lines().collect(Collectors.toList());
fName = all.get(ThreadLocalRandom.current().nextInt(0, all.size()));
ln = new BufferedReader(
new InputStreamReader(
ServerToClientThread.class.getResourceAsStream(
"/server_config/CSV_Database_of_Last_Names.csv"
)
ServerToClientThread.class.getResourceAsStream(
"/server_config/CSV_Database_of_Last_Names.csv"
)
)
);
all = ln.lines().collect(Collectors.toList());
@@ -102,20 +132,16 @@ public class ServerToClientThread implements Runnable, Observer {
if (threeWayHandshake(sourceId)) {
serverLog("Successful handshake. Client allocated id: " + sourceId, 0);
yacht = new Yacht(
"Yacht", sourceId, sourceId.toString(), fName, fName + " " + lName, "NZ"
);
// Yacht yacht = new Yacht("Kappa", "Kap", new GeoPoint(57.6708220, 11.8321340), 90.0);
GameState.addYacht(sourceId, yacht);
GameState.addPlayer(new Player(socket, yacht));
} else {
serverLog("Unsuccessful handshake. Connection rejected", 1);
closeSocket();
return;
}
);
all = ln.lines().collect(Collectors.toList());
lName = all.get(ThreadLocalRandom.current().nextInt(0, all.size()));
seqNo = 0;
thread = new Thread(this);
thread.start();
Yacht yacht = new Yacht(
"Yacht", sourceId, sourceId.toString(), fName, fName + " " + lName, "NZ"
);
GameState.addYacht(sourceId, yacht);
GameState.addPlayer(new Player(socket, yacht));
}
static void serverLog(String message, int logLevel) {
@@ -132,6 +158,35 @@ public class ServerToClientThread implements Runnable, Observer {
}
}
private void completeRegistration(ClientType clientType) throws IOException {
// Fail if not a player
if (!clientType.equals(ClientType.PLAYER)){
RegistrationResponseMessage responseMessage = new RegistrationResponseMessage(0, RegistrationResponseStatus.FAILURE_GENERAL);
os.write(responseMessage.getBuffer());
return;
}
if (GameState.getPlayers().size() >= GameState.MAX_PLAYERS){
RegistrationResponseMessage responseMessage = new RegistrationResponseMessage(0, RegistrationResponseStatus.FAILURE_FULL);
os.write(responseMessage.getBuffer());
return;
}
Integer sourceId = GameState.getUniquePlayerID();
RegistrationResponseMessage responseMessage = new RegistrationResponseMessage(sourceId, RegistrationResponseStatus.SUCCESS_PLAYING);
this.clientType = clientType;
this.sourceId = sourceId;
isRegistered = true;
os.write(responseMessage.getBuffer());
setUpPlayer();
for (ConnectionListener listener : connectionListeners) {
listener.notifyConnection();
}
}
public void run() {
int sync1;
int sync2;
@@ -140,20 +195,6 @@ public class ServerToClientThread implements Runnable, Observer {
while (socket.isConnected()) {
try {
//Perform a write if it is time to as delegated by the MainServerThread
if (updateClient) {
// TODO: 13/07/17 wmu16 - Write out game state - some function that would write all appropriate messages to this output stream
// ChatterMessage chatterMessage = new ChatterMessage(4, 14, "Hello, it's me");
// sendMessage(chatterMessage);
// try {
// GameState.outputState(os);
// } catch (IOException e) {
// System.out.println("IO error in server thread upon writing to output stream");
// }
// sendBoatLocationPackets();
updateClient = false;
}
crcBuffer = new ByteArrayOutputStream();
sync1 = readByte();
sync2 = readByte();
@@ -173,11 +214,18 @@ public class ServerToClientThread implements Runnable, Observer {
//System.out.println("RECEIVED A PACKET");
switch (PacketType.assignPacketType(type, payload)) {
case BOAT_ACTION:
BoatActionType actionType = ServerPacketParser
.extractBoatAction(
new StreamPacket(type, payloadLength, timeStamp, payload));
BoatAction actionType = ServerPacketParser
.extractBoatAction(
new StreamPacket(type, payloadLength, timeStamp, payload));
GameState.updateBoat(sourceId, actionType);
break;
case RACE_REGISTRATION_REQUEST:
ClientType requestedType = ServerPacketParser.extractClientType(
new StreamPacket(type, payloadLength, timeStamp, payload));
completeRegistration(requestedType);
break;
}
} else {
serverLog("Packet has been dropped", 1);
@@ -192,7 +240,7 @@ public class ServerToClientThread implements Runnable, Observer {
}
}
private void sendSetupMessages() {
public void sendSetupMessages() {
xml = new XMLGenerator();
Race race = new Race();
@@ -220,40 +268,6 @@ public class ServerToClientThread implements Runnable, Observer {
public void updateClient() {
sendBoatLocationPackets();
updateClient = true;
}
/**
* Tries to confirm the connection just accepted.
* Sends ID, expects that ID echoed for confirmation,
* if so, sends a confirmation packet back to that connection
* Creates a player instance with that ID and this thread and adds it to the GameState
* If not, close the socket and end the threads execution
*
* @param id the id to try and assign to the connection
* @return A boolean indicating if it was a successful handshake
*/
private Boolean threeWayHandshake(Integer id) {
Integer confirmationID = null;
Integer identificationAttempt = 0;
while (!userIdentified) {
try {
os.write(id); //Send out new ID looking for echo
confirmationID = is.read();
} catch (IOException e) {
serverLog("Three way handshake failed", 1);
}
if (id.equals(confirmationID)) { //ID is echoed back. Connection is a client
return true;
} else if (identificationAttempt > MAX_ID_ATTEMPTS) { //No response. not a client. tidy up and go home.
return false;
}
identificationAttempt++;
}
return true;
}
private void closeSocket() {
@@ -264,7 +278,6 @@ public class ServerToClientThread implements Runnable, Observer {
}
}
private int readByte() throws Exception {
int currentByte = -1;
try {
@@ -314,7 +327,6 @@ public class ServerToClientThread implements Runnable, Observer {
private void sendBoatLocationPackets() {
ArrayList<Yacht> yachts = new ArrayList<>(GameState.getYachts().values());
for (Yacht yacht : yachts) {
// System.out.println("[SERVER] Lat: " + yacht.getLocation().getLat() + " Lon: " + yacht.getLocation().getLng());
BoatLocationMessage boatLocationMessage =
new BoatLocationMessage(
yacht.getSourceId(),
@@ -335,7 +347,6 @@ public class ServerToClientThread implements Runnable, Observer {
public void sendRaceStatusMessage() {
// variables taken from GameServerThread
List<BoatSubMessage> boatSubMessages = new ArrayList<>();
BoatStatus boatStatus;
RaceStatus raceStatus;
@@ -378,4 +389,12 @@ public class ServerToClientThread implements Runnable, Observer {
public void sendCollisionMessage(Integer yachtId) {
sendMessage(new YachtEventCodeMessage(yachtId));
}
public void addConnectionListener(ConnectionListener listener) {
connectionListeners.add(listener);
}
public void removeConnectionListener(ConnectionListener listener) {
connectionListeners.remove(listener);
}
}