mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 06:18:44 +00:00
Merge branch 'issue47_disconnect_crash_rebranch' into 'develop'
Issue47 disconnect crash rebranch Fix for all disconnection issues. Fix is not robust. Need consistent interface between disconnection of ServerToClientThreads and MainServerThread. See merge request !66
This commit is contained in:
@@ -429,7 +429,6 @@ public class GameState implements Runnable {
|
|||||||
private void checkForLegProgression(ServerYacht yacht) {
|
private void checkForLegProgression(ServerYacht yacht) {
|
||||||
Integer currentMarkSeqID = yacht.getCurrentMarkSeqID();
|
Integer currentMarkSeqID = yacht.getCurrentMarkSeqID();
|
||||||
CompoundMark currentMark = markOrder.getCurrentMark(currentMarkSeqID);
|
CompoundMark currentMark = markOrder.getCurrentMark(currentMarkSeqID);
|
||||||
// System.out.println(yacht.getCurrentMarkSeqID());
|
|
||||||
|
|
||||||
Boolean hasProgressed;
|
Boolean hasProgressed;
|
||||||
if (currentMarkSeqID == 0) {
|
if (currentMarkSeqID == 0) {
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
|||||||
|
|
||||||
// TODO: 14/07/17 wmu16 - Send out disconnect packet to clients
|
// TODO: 14/07/17 wmu16 - Send out disconnect packet to clients
|
||||||
try {
|
try {
|
||||||
|
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
|
||||||
|
serverToClientThread.terminate();
|
||||||
|
}
|
||||||
serverSocket.close();
|
serverSocket.close();
|
||||||
return;
|
return;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -172,6 +175,7 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
|||||||
thread.sendSetupMessages();
|
thread.sendSetupMessages();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
serverToClientThread.addDisconnectListener(this::clientDisconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -181,11 +185,11 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void clientDisconnected(Player player) {
|
public void clientDisconnected(Player player) {
|
||||||
try {
|
// try {
|
||||||
player.getSocket().close();
|
// player.getSocket().close();
|
||||||
} catch (Exception e) {
|
// } catch (Exception e) {
|
||||||
serverLog("Cannot disconnect the socket for the disconnected player.", 0);
|
// serverLog("Cannot disconnect the socket for the disconnected player.", 0);
|
||||||
}
|
// }
|
||||||
serverLog("Player " + player.getYacht().getSourceId() + "'s socket disconnected", 0);
|
serverLog("Player " + player.getYacht().getSourceId() + "'s socket disconnected", 0);
|
||||||
GameState.removeYacht(player.getYacht().getSourceId());
|
GameState.removeYacht(player.getYacht().getSourceId());
|
||||||
GameState.removePlayer(player);
|
GameState.removePlayer(player);
|
||||||
@@ -193,11 +197,12 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
|||||||
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
|
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
|
||||||
if (serverToClientThread.getSocket() == player.getSocket()) {
|
if (serverToClientThread.getSocket() == player.getSocket()) {
|
||||||
closedConnection = serverToClientThread;
|
closedConnection = serverToClientThread;
|
||||||
} else {
|
} else if (GameState.getCurrentStage() != GameStages.RACING){
|
||||||
serverToClientThread.sendSetupMessages();
|
serverToClientThread.sendSetupMessages();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
serverToClientThreads.remove(closedConnection);
|
serverToClientThreads.remove(closedConnection);
|
||||||
|
closedConnection.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startGame() {
|
public void startGame() {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class ServerListenThread implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void run(){
|
public void run(){
|
||||||
while (true){
|
while (serverSocket != null && !serverSocket.isClosed()){
|
||||||
acceptConnection();
|
acceptConnection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,17 +40,20 @@ import seng302.model.stream.xml.generator.Regatta;
|
|||||||
import seng302.utilities.XMLGenerator;
|
import seng302.utilities.XMLGenerator;
|
||||||
import seng302.gameServer.messages.BoatAction;
|
import seng302.gameServer.messages.BoatAction;
|
||||||
import seng302.gameServer.messages.BoatLocationMessage;
|
import seng302.gameServer.messages.BoatLocationMessage;
|
||||||
import seng302.gameServer.messages.BoatSubMessage;
|
|
||||||
import seng302.gameServer.messages.ClientType;
|
import seng302.gameServer.messages.ClientType;
|
||||||
import seng302.gameServer.messages.Message;
|
import seng302.gameServer.messages.Message;
|
||||||
import seng302.gameServer.messages.RaceStatus;
|
|
||||||
import seng302.gameServer.messages.RaceStatusMessage;
|
|
||||||
import seng302.gameServer.messages.RaceType;
|
|
||||||
import seng302.gameServer.messages.RegistrationResponseMessage;
|
import seng302.gameServer.messages.RegistrationResponseMessage;
|
||||||
import seng302.gameServer.messages.RegistrationResponseStatus;
|
import seng302.gameServer.messages.RegistrationResponseStatus;
|
||||||
import seng302.gameServer.messages.XMLMessage;
|
import seng302.gameServer.messages.XMLMessage;
|
||||||
import seng302.gameServer.messages.XMLMessageSubType;
|
import seng302.gameServer.messages.XMLMessageSubType;
|
||||||
|
import seng302.gameServer.messages.YachtEventCodeMessage;
|
||||||
|
import seng302.model.Player;
|
||||||
import seng302.model.ServerYacht;
|
import seng302.model.ServerYacht;
|
||||||
|
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
|
* A class describing a single connection to a Client for the purposes of sending and receiving on
|
||||||
@@ -67,6 +70,12 @@ public class ServerToClientThread implements Runnable, Observer {
|
|||||||
void notifyConnection ();
|
void notifyConnection ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: 17/08/17 this is only temporary disconnects should be handled consistently
|
||||||
|
@FunctionalInterface
|
||||||
|
interface DisconnectListener {
|
||||||
|
void notifyDisconnect (Player player);
|
||||||
|
}
|
||||||
|
|
||||||
private Logger logger = LoggerFactory.getLogger(ServerToClientThread.class);
|
private Logger logger = LoggerFactory.getLogger(ServerToClientThread.class);
|
||||||
|
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
@@ -86,8 +95,10 @@ public class ServerToClientThread implements Runnable, Observer {
|
|||||||
private XMLGenerator xml;
|
private XMLGenerator xml;
|
||||||
|
|
||||||
private List<ConnectionListener> connectionListeners = new ArrayList<>();
|
private List<ConnectionListener> connectionListeners = new ArrayList<>();
|
||||||
|
private DisconnectListener disconnectListener;
|
||||||
|
|
||||||
private ServerYacht yacht;
|
private ServerYacht yacht;
|
||||||
|
private Player player;
|
||||||
|
|
||||||
public ServerToClientThread(Socket socket) {
|
public ServerToClientThread(Socket socket) {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
@@ -134,8 +145,9 @@ public class ServerToClientThread implements Runnable, Observer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
yacht.addObserver(this); // TODO: yacht can notify mark rounding message hyi25 13/8/17
|
yacht.addObserver(this); // TODO: yacht can notify mark rounding message hyi25 13/8/17
|
||||||
|
player = new Player(socket, yacht);
|
||||||
GameState.addYacht(sourceId, yacht);
|
GameState.addYacht(sourceId, yacht);
|
||||||
GameState.addPlayer(new Player(socket, yacht));
|
GameState.addPlayer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -181,8 +193,7 @@ public class ServerToClientThread implements Runnable, Observer {
|
|||||||
int sync2;
|
int sync2;
|
||||||
// TODO: 14/07/17 wmu16 - Work out how to fix this while loop
|
// TODO: 14/07/17 wmu16 - Work out how to fix this while loop
|
||||||
|
|
||||||
while (socket.isConnected()) {
|
while (socket.isConnected() && !socket.isClosed()) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
crcBuffer = new ByteArrayOutputStream();
|
crcBuffer = new ByteArrayOutputStream();
|
||||||
sync1 = readByte();
|
sync1 = readByte();
|
||||||
@@ -200,7 +211,6 @@ public class ServerToClientThread implements Runnable, Observer {
|
|||||||
long computedCrc = checksum.getValue();
|
long computedCrc = checksum.getValue();
|
||||||
long packetCrc = Message.bytesToLong(getBytes(4));
|
long packetCrc = Message.bytesToLong(getBytes(4));
|
||||||
if (computedCrc == packetCrc) {
|
if (computedCrc == packetCrc) {
|
||||||
//System.out.println("RECEIVED A PACKET");
|
|
||||||
switch (PacketType.assignPacketType(type, payload)) {
|
switch (PacketType.assignPacketType(type, payload)) {
|
||||||
case BOAT_ACTION:
|
case BOAT_ACTION:
|
||||||
BoatAction actionType = ServerPacketParser
|
BoatAction actionType = ServerPacketParser
|
||||||
@@ -237,6 +247,7 @@ public class ServerToClientThread implements Runnable, Observer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logger.warn("Closed serverToClientThread" + thread, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendSetupMessages() {
|
public void sendSetupMessages() {
|
||||||
@@ -248,7 +259,11 @@ public class ServerToClientThread implements Runnable, Observer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//@TODO calculate lat/lng values
|
//@TODO calculate lat/lng values
|
||||||
xml.setRegatta(new Regatta("Party Parrot Test Server", "Bermuda Test Course", 57.6679590, 11.8503233));
|
xml.setRegatta(
|
||||||
|
new Regatta(
|
||||||
|
"Party Parrot Test Server", "Bermuda Test Course",
|
||||||
|
57.6679590, 11.8503233)
|
||||||
|
);
|
||||||
xml.setRace(race);
|
xml.setRace(race);
|
||||||
|
|
||||||
XMLMessage xmlMessage;
|
XMLMessage xmlMessage;
|
||||||
@@ -276,10 +291,12 @@ public class ServerToClientThread implements Runnable, Observer {
|
|||||||
private int readByte() throws Exception {
|
private int readByte() throws Exception {
|
||||||
int currentByte = -1;
|
int currentByte = -1;
|
||||||
try {
|
try {
|
||||||
// @TODO @FIX ConnectionReset Exception when a client disconnects before it is garbage collected
|
|
||||||
currentByte = is.read();
|
currentByte = is.read();
|
||||||
crcBuffer.write(currentByte);
|
crcBuffer.write(currentByte);
|
||||||
|
} catch (SocketException se) {
|
||||||
|
disconnectListener.notifyDisconnect(this.player);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
disconnectListener.notifyDisconnect(this.player);
|
||||||
logger.warn("Socket read failed", 1);
|
logger.warn("Socket read failed", 1);
|
||||||
}
|
}
|
||||||
if (currentByte == -1) {
|
if (currentByte == -1) {
|
||||||
@@ -306,8 +323,7 @@ public class ServerToClientThread implements Runnable, Observer {
|
|||||||
try {
|
try {
|
||||||
os.write(message.getBuffer());
|
os.write(message.getBuffer());
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
//serverLog("Player " + sourceId + " side socket disconnected", 1);
|
logger.warn("Player " + sourceId + " side socket disconnected", 1);
|
||||||
return;
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.warn("Message send failed", 1);
|
logger.warn("Message send failed", 1);
|
||||||
}
|
}
|
||||||
@@ -358,4 +374,16 @@ public class ServerToClientThread implements Runnable, Observer {
|
|||||||
public void removeConnectionListener(ConnectionListener listener) {
|
public void removeConnectionListener(ConnectionListener listener) {
|
||||||
connectionListeners.remove(listener);
|
connectionListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void terminate () {
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
logger.warn("IOException attempting to terminate serverToClientThread " + this.thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDisconnectListener(DisconnectListener disconnectListener) {
|
||||||
|
this.disconnectListener = disconnectListener;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -15,10 +14,6 @@ import java.util.TimerTask;
|
|||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.zip.CRC32;
|
import java.util.zip.CRC32;
|
||||||
import java.util.zip.Checksum;
|
import java.util.zip.Checksum;
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.scene.control.Alert;
|
|
||||||
import javafx.scene.control.Alert.AlertType;
|
|
||||||
import javafx.scene.control.ButtonType;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import seng302.gameServer.messages.BoatAction;
|
import seng302.gameServer.messages.BoatAction;
|
||||||
@@ -48,16 +43,20 @@ public class ClientToServerThread implements Runnable {
|
|||||||
void newPacket();
|
void newPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface DisconnectedFromHostListener {
|
||||||
|
void notifYDisconnection (String message);
|
||||||
|
}
|
||||||
|
|
||||||
private class ByteReadException extends Exception {
|
private class ByteReadException extends Exception {
|
||||||
private ByteReadException(String message) {
|
private ByteReadException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int LOG_LEVEL = 1;
|
|
||||||
|
|
||||||
private Queue<StreamPacket> streamPackets = new ConcurrentLinkedQueue<>();
|
private Queue<StreamPacket> streamPackets = new ConcurrentLinkedQueue<>();
|
||||||
private List<ClientSocketListener> listeners = new ArrayList<>();
|
private List<ClientSocketListener> listeners = new ArrayList<>();
|
||||||
|
private List<DisconnectedFromHostListener> disconnectionListeners = new ArrayList<>();
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
|
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
@@ -74,7 +73,6 @@ public class ClientToServerThread implements Runnable {
|
|||||||
|
|
||||||
private int clientId = -1;
|
private int clientId = -1;
|
||||||
|
|
||||||
// private Boolean updateClient = true;
|
|
||||||
private ByteArrayOutputStream crcBuffer;
|
private ByteArrayOutputStream crcBuffer;
|
||||||
private boolean socketOpen = true;
|
private boolean socketOpen = true;
|
||||||
|
|
||||||
@@ -101,20 +99,6 @@ public class ClientToServerThread implements Runnable {
|
|||||||
thread.start();
|
thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints out log messages and the time happened.
|
|
||||||
* Only perform task if log level is below LOG_LEVEL variable.
|
|
||||||
*
|
|
||||||
* @param message a string of message to be printed out
|
|
||||||
* @param logLevel an int for log level
|
|
||||||
*/
|
|
||||||
static void clientLog(String message, int logLevel) {
|
|
||||||
if (logLevel <= LOG_LEVEL) {
|
|
||||||
System.out.println(
|
|
||||||
"[CLIENT " + LocalDateTime.now().toLocalTime().toString() + "] " + message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform the thread loop. It exits the loop if ClientState connected to host
|
* Perform the thread loop. It exits the loop if ClientState connected to host
|
||||||
* variable is false.
|
* variable is false.
|
||||||
@@ -123,7 +107,7 @@ public class ClientToServerThread implements Runnable {
|
|||||||
int sync1;
|
int sync1;
|
||||||
int sync2;
|
int sync2;
|
||||||
// TODO: 14/07/17 wmu16 - Work out how to fix this while loop
|
// TODO: 14/07/17 wmu16 - Work out how to fix this while loop
|
||||||
while(socketOpen) {
|
while(!socket.isClosed() && socket.isConnected() && socketOpen) {
|
||||||
try {
|
try {
|
||||||
crcBuffer = new ByteArrayOutputStream();
|
crcBuffer = new ByteArrayOutputStream();
|
||||||
sync1 = readByte();
|
sync1 = readByte();
|
||||||
@@ -155,24 +139,18 @@ public class ClientToServerThread implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
clientLog("Packet has been dropped", 1);
|
logger.warn("Packet has been dropped", 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ByteReadException e) {
|
} catch (ByteReadException e) {
|
||||||
e.printStackTrace();
|
logger.warn("Byte read exception on ClientToServerThread", 1);
|
||||||
|
notifyDisconnectListeners("Connection to server was interrupted");
|
||||||
closeSocket();
|
closeSocket();
|
||||||
Platform.runLater(() -> {
|
|
||||||
Alert alert = new Alert(AlertType.ERROR);
|
|
||||||
alert.setHeaderText("Host has disconnected");
|
|
||||||
alert.setContentText("Cannot find Server");
|
|
||||||
alert.showAndWait();
|
|
||||||
});
|
|
||||||
clientLog(e.getMessage(), 1);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logger.warn("Closed connection to server", 1);
|
||||||
|
notifyDisconnectListeners("Connection to server was terminated");
|
||||||
closeSocket();
|
closeSocket();
|
||||||
clientLog("Closed connection to Server", 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendCustomizationRequest(CustomizeRequestType reqType, byte[] payload) {
|
public void sendCustomizationRequest(CustomizeRequestType reqType, byte[] payload) {
|
||||||
@@ -181,8 +159,17 @@ public class ClientToServerThread implements Runnable {
|
|||||||
os.write(requestMessage.getBuffer());
|
os.write(requestMessage.getBuffer());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Could not send customization request");
|
logger.error("Could not send customization request");
|
||||||
|
notifyDisconnectListeners("Could not communicate with server");
|
||||||
|
closeSocket();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void notifyDisconnectListeners (String message) {
|
||||||
|
if (socketOpen) {
|
||||||
|
for (DisconnectedFromHostListener listener : disconnectionListeners) {
|
||||||
|
listener.notifYDisconnection(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -195,7 +182,8 @@ public class ClientToServerThread implements Runnable {
|
|||||||
os.write(requestMessage.getBuffer());
|
os.write(requestMessage.getBuffer());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Could not send registration request. Exiting");
|
logger.error("Could not send registration request. Exiting");
|
||||||
System.exit(1);
|
notifyDisconnectListeners("Failed to register with server");
|
||||||
|
closeSocket();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,7 +199,6 @@ public class ClientToServerThread implements Runnable {
|
|||||||
|
|
||||||
if (status.equals(RegistrationResponseStatus.SUCCESS_PLAYING)){
|
if (status.equals(RegistrationResponseStatus.SUCCESS_PLAYING)){
|
||||||
clientId = sourceId;
|
clientId = sourceId;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,11 +212,8 @@ public class ClientToServerThread implements Runnable {
|
|||||||
else{
|
else{
|
||||||
alertErrorText = "Could not connect to server";
|
alertErrorText = "Could not connect to server";
|
||||||
}
|
}
|
||||||
|
notifyDisconnectListeners(alertErrorText);
|
||||||
Platform.runLater(() -> {
|
closeSocket();
|
||||||
new Alert(AlertType.ERROR, alertErrorText, ButtonType.OK).showAndWait();
|
|
||||||
System.exit(1);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -260,7 +244,7 @@ public class ClientToServerThread implements Runnable {
|
|||||||
new TimerTask() {
|
new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
sendBoatAction(new BoatActionMessage(BoatAction.DOWNWIND));
|
sendBoatActionMessage(new BoatActionMessage(BoatAction.DOWNWIND));
|
||||||
}
|
}
|
||||||
}, 0, PACKET_SENDING_INTERVAL_MS
|
}, 0, PACKET_SENDING_INTERVAL_MS
|
||||||
);
|
);
|
||||||
@@ -273,14 +257,14 @@ public class ClientToServerThread implements Runnable {
|
|||||||
new TimerTask() {
|
new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
sendBoatAction(new BoatActionMessage(BoatAction.UPWIND));
|
sendBoatActionMessage(new BoatActionMessage(BoatAction.UPWIND));
|
||||||
}
|
}
|
||||||
}, 0, PACKET_SENDING_INTERVAL_MS
|
}, 0, PACKET_SENDING_INTERVAL_MS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sendBoatAction(new BoatActionMessage(actionType));
|
sendBoatActionMessage(new BoatActionMessage(actionType));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -298,12 +282,14 @@ public class ClientToServerThread implements Runnable {
|
|||||||
* Sends a boat action of the given message type.
|
* Sends a boat action of the given message type.
|
||||||
* @param message The given message type.
|
* @param message The given message type.
|
||||||
*/
|
*/
|
||||||
private void sendBoatAction(BoatActionMessage message) {
|
private void sendBoatActionMessage(BoatActionMessage message) {
|
||||||
if (clientId != -1) {
|
if (clientId != -1) {
|
||||||
try {
|
try {
|
||||||
os.write(message.getBuffer());
|
os.write(message.getBuffer());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
clientLog("Could not write to server", 1);
|
logger.warn("IOException on attempting to sendBoatAction from Client");
|
||||||
|
notifyDisconnectListeners("Cannot communicate with server");
|
||||||
|
closeSocket();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,8 +297,9 @@ public class ClientToServerThread implements Runnable {
|
|||||||
private void closeSocket() {
|
private void closeSocket() {
|
||||||
try {
|
try {
|
||||||
socket.close();
|
socket.close();
|
||||||
|
socketOpen = false;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
clientLog("Failed to close the socket", 1);
|
logger.warn("IOException on attempting to close ClientToServerSocket");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,16 +319,28 @@ public class ClientToServerThread implements Runnable {
|
|||||||
listeners.remove(streamListener);
|
listeners.remove(streamListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addDisconnectionListener (DisconnectedFromHostListener listener) {
|
||||||
|
disconnectionListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeDisconnectionListener (DisconnectedFromHostListener listener) {
|
||||||
|
disconnectionListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
private int readByte() throws ByteReadException {
|
private int readByte() throws ByteReadException {
|
||||||
int currentByte = -1;
|
int currentByte = -1;
|
||||||
try {
|
try {
|
||||||
currentByte = is.read();
|
currentByte = is.read();
|
||||||
crcBuffer.write(currentByte);
|
crcBuffer.write(currentByte);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
clientLog("Read byte failed", 1);
|
logger.warn("IOException on readByte Client side", 1);
|
||||||
|
notifyDisconnectListeners("Cannot read from server.");
|
||||||
|
closeSocket();
|
||||||
}
|
}
|
||||||
if (currentByte == -1) {
|
if (currentByte == -1) {
|
||||||
clientLog("InputStream reach end of stream", 1);
|
notifyDisconnectListeners("Cannot read from server.");
|
||||||
|
closeSocket();
|
||||||
|
logger.warn("InputStream reach end of stream", 1);
|
||||||
}
|
}
|
||||||
return currentByte;
|
return currentByte;
|
||||||
}
|
}
|
||||||
@@ -360,8 +359,7 @@ public class ClientToServerThread implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getClientId () {
|
int getClientId () {
|
||||||
return clientId;
|
return clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,14 @@ import javafx.collections.FXCollections;
|
|||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.Alert.AlertType;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import seng302.gameServer.GameState;
|
import seng302.gameServer.GameState;
|
||||||
import seng302.gameServer.MainServerThread;
|
import seng302.gameServer.MainServerThread;
|
||||||
import seng302.gameServer.messages.BoatAction;
|
import seng302.gameServer.messages.BoatAction;
|
||||||
import seng302.gameServer.messages.BoatStatus;
|
import seng302.gameServer.messages.BoatStatus;
|
||||||
import seng302.gameServer.messages.BoatAction;
|
|
||||||
import seng302.model.ClientYacht;
|
import seng302.model.ClientYacht;
|
||||||
import seng302.model.RaceState;
|
import seng302.model.RaceState;
|
||||||
import seng302.model.stream.packets.StreamPacket;
|
import seng302.model.stream.packets.StreamPacket;
|
||||||
@@ -70,16 +71,17 @@ public class GameClient {
|
|||||||
*/
|
*/
|
||||||
public void runAsClient(String ipAddress, Integer portNumber) {
|
public void runAsClient(String ipAddress, Integer portNumber) {
|
||||||
try {
|
try {
|
||||||
socketThread = new ClientToServerThread(ipAddress, portNumber);
|
startClientToServerThread(ipAddress, portNumber);
|
||||||
} catch (IOException ioe) {
|
socketThread.addDisconnectionListener((cause) -> {
|
||||||
ioe.printStackTrace();
|
showConnectionError(cause);
|
||||||
System.out.println("Unable to connect to host...");
|
Platform.runLater(this::loadStartScreen);
|
||||||
}
|
});
|
||||||
|
|
||||||
socketThread.addStreamObserver(this::parsePackets);
|
socketThread.addStreamObserver(this::parsePackets);
|
||||||
LobbyController lobbyController = loadLobby();
|
LobbyController lobbyController = loadLobby();
|
||||||
|
lobbyController.setSocketThread(socketThread);
|
||||||
|
lobbyController.setPlayerID(socketThread.getClientId());
|
||||||
|
lobbyController.setPlayerListSource(clientLobbyList);
|
||||||
lobbyController.disableReadyButton();
|
lobbyController.disableReadyButton();
|
||||||
|
|
||||||
if (regattaData != null){
|
if (regattaData != null){
|
||||||
lobbyController.setTitle(regattaData.getRegattaName());
|
lobbyController.setTitle(regattaData.getRegattaName());
|
||||||
lobbyController.setCourseName(regattaData.getCourseName());
|
lobbyController.setCourseName(regattaData.getCourseName());
|
||||||
@@ -91,6 +93,10 @@ public class GameClient {
|
|||||||
|
|
||||||
lobbyController.addCloseListener((exitCause) -> this.loadStartScreen());
|
lobbyController.addCloseListener((exitCause) -> this.loadStartScreen());
|
||||||
this.lobbyController = lobbyController;
|
this.lobbyController = lobbyController;
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
showConnectionError("Unable to find server");
|
||||||
|
Platform.runLater(this::loadStartScreen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,20 +107,18 @@ public class GameClient {
|
|||||||
public void runAsHost(String ipAddress, Integer portNumber) {
|
public void runAsHost(String ipAddress, Integer portNumber) {
|
||||||
server = new MainServerThread();
|
server = new MainServerThread();
|
||||||
try {
|
try {
|
||||||
socketThread = new ClientToServerThread(ipAddress, portNumber);
|
startClientToServerThread(ipAddress, portNumber);
|
||||||
} catch (IOException ioe) {
|
socketThread.addDisconnectionListener((cause) -> {
|
||||||
ioe.printStackTrace();
|
Platform.runLater(this::loadStartScreen);
|
||||||
System.out.println("Unable to make local connection to host...");
|
});
|
||||||
}
|
|
||||||
socketThread.addStreamObserver(this::parsePackets);
|
|
||||||
LobbyController lobbyController = loadLobby();
|
LobbyController lobbyController = loadLobby();
|
||||||
|
lobbyController.setSocketThread(socketThread);
|
||||||
|
lobbyController.setPlayerID(socketThread.getClientId());
|
||||||
lobbyController.setPlayerListSource(clientLobbyList);
|
lobbyController.setPlayerListSource(clientLobbyList);
|
||||||
|
|
||||||
if (regattaData != null) {
|
if (regattaData != null) {
|
||||||
lobbyController.setTitle("Hosting: " + regattaData.getRegattaName());
|
lobbyController.setTitle("Hosting: " + regattaData.getRegattaName());
|
||||||
lobbyController.setCourseName(regattaData.getCourseName());
|
lobbyController.setCourseName(regattaData.getCourseName());
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
lobbyController.setTitle("Hosting: " + ipAddress);
|
lobbyController.setTitle("Hosting: " + ipAddress);
|
||||||
lobbyController.setCourseName("");
|
lobbyController.setCourseName("");
|
||||||
}
|
}
|
||||||
@@ -125,12 +129,16 @@ public class GameClient {
|
|||||||
lobbyController.disableReadyButton();
|
lobbyController.disableReadyButton();
|
||||||
server.startGame();
|
server.startGame();
|
||||||
} else if (exitCause == CloseStatus.LEAVE) {
|
} else if (exitCause == CloseStatus.LEAVE) {
|
||||||
|
server.terminate();
|
||||||
|
server = null;
|
||||||
loadStartScreen();
|
loadStartScreen();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.lobbyController = lobbyController;
|
this.lobbyController = lobbyController;
|
||||||
//server.setGameClient(this);
|
} catch (IOException ioe) {
|
||||||
|
showConnectionError("Cannot connect to server as host");
|
||||||
|
Platform.runLater(this::loadStartScreen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadStartScreen() {
|
private void loadStartScreen() {
|
||||||
@@ -145,10 +153,24 @@ public class GameClient {
|
|||||||
holderPane.getChildren().clear();
|
holderPane.getChildren().clear();
|
||||||
holderPane.getChildren().add(fxmlLoader.load());
|
holderPane.getChildren().add(fxmlLoader.load());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
showConnectionError("JavaFX crashed. Please restart the app");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showConnectionError (String message) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
Alert alert = new Alert(AlertType.ERROR);
|
||||||
|
alert.setHeaderText("Connection Error");
|
||||||
|
alert.setContentText(message);
|
||||||
|
alert.showAndWait();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startClientToServerThread (String ipAddress, int portNumber) throws IOException {
|
||||||
|
socketThread = new ClientToServerThread(ipAddress, portNumber);
|
||||||
|
socketThread.addStreamObserver(this::parsePackets);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a view of the lobby into the clients pane
|
* Loads a view of the lobby into the clients pane
|
||||||
*
|
*
|
||||||
@@ -163,12 +185,7 @@ public class GameClient {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
LobbyController lobbyController = fxmlLoader.getController();
|
return fxmlLoader.getController();
|
||||||
lobbyController.setSocketThread(socketThread);
|
|
||||||
lobbyController.setPlayerListSource(clientLobbyList);
|
|
||||||
lobbyController.setPlayerID(socketThread.getClientId());
|
|
||||||
|
|
||||||
return lobbyController;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadRaceView() {
|
private void loadRaceView() {
|
||||||
|
|||||||
@@ -278,7 +278,31 @@ public class GameView extends Pane {
|
|||||||
colour = Color.BLACK;
|
colour = Color.BLACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Creating mark arrows.
|
createMarkArrows(sequence);
|
||||||
|
|
||||||
|
//Scale race to markers if there is no border.
|
||||||
|
if (borderPoints == null) {
|
||||||
|
rescaleRace(new ArrayList<>(markerObjects.keySet()));
|
||||||
|
}
|
||||||
|
//Move the Markers to initial position.
|
||||||
|
markerObjects.forEach(((mark, marker) -> {
|
||||||
|
Point2D p2d = findScaledXY(mark.getLat(), mark.getLng());
|
||||||
|
marker.setLayoutX(p2d.getX());
|
||||||
|
marker.setLayoutY(p2d.getY());
|
||||||
|
}));
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
markers.getChildren().clear();
|
||||||
|
markers.getChildren().addAll(gates);
|
||||||
|
markers.getChildren().addAll(markerObjects.values());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates all the data needed for to create mark arrows. Requires that a course has been
|
||||||
|
* added to the gameview.
|
||||||
|
* @param sequence The order in which marks are traversed.
|
||||||
|
*/
|
||||||
|
private void createMarkArrows (List<Corner> sequence) {
|
||||||
for (int i=1; i < sequence.size()-1; i++) { //General case.
|
for (int i=1; i < sequence.size()-1; i++) { //General case.
|
||||||
double averageLat = 0;
|
double averageLat = 0;
|
||||||
double averageLng = 0;
|
double averageLng = 0;
|
||||||
@@ -298,7 +322,7 @@ public class GameView extends Pane {
|
|||||||
averageLng += mark.getLng();
|
averageLng += mark.getLng();
|
||||||
}
|
}
|
||||||
GeoPoint nextMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks);
|
GeoPoint nextMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks);
|
||||||
// TODO: 16/08/17 This comparison is cancer and deserves to die.
|
// TODO: 16/08/17 This comparison doesn't need to exist but the alternative is to user server enum client side.
|
||||||
for (Mark mark : course.get(i).getMarks()) {
|
for (Mark mark : course.get(i).getMarks()) {
|
||||||
markerObjects.get(mark).addArrows(
|
markerObjects.get(mark).addArrows(
|
||||||
mark.getRoundingSide() == RoundingSide.STARBOARD ? MarkArrowFactory.RoundingSide.STARBOARD : MarkArrowFactory.RoundingSide.PORT,
|
mark.getRoundingSide() == RoundingSide.STARBOARD ? MarkArrowFactory.RoundingSide.STARBOARD : MarkArrowFactory.RoundingSide.PORT,
|
||||||
@@ -307,9 +331,11 @@ public class GameView extends Pane {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
createStartLineArrows();
|
||||||
|
createFinishLineArrows();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: 16/08/17 Make this cleaner
|
private void createStartLineArrows () {
|
||||||
//First mark case
|
|
||||||
double averageLat = 0;
|
double averageLat = 0;
|
||||||
double averageLng = 0;
|
double averageLng = 0;
|
||||||
int numMarks = 0;
|
int numMarks = 0;
|
||||||
@@ -326,10 +352,12 @@ public class GameView extends Pane {
|
|||||||
GeoUtility.getBearing(mark, firstMarkAv)
|
GeoUtility.getBearing(mark, firstMarkAv)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
//Last Mark case
|
}
|
||||||
numMarks = 0;
|
|
||||||
averageLat = 0;
|
private void createFinishLineArrows () {
|
||||||
averageLng = 0;
|
double numMarks = 0;
|
||||||
|
double averageLat = 0;
|
||||||
|
double averageLng = 0;
|
||||||
for (Mark mark : course.get(course.size()-2).getMarks()) {
|
for (Mark mark : course.get(course.size()-2).getMarks()) {
|
||||||
numMarks += 1;
|
numMarks += 1;
|
||||||
averageLat += mark.getLat();
|
averageLat += mark.getLat();
|
||||||
@@ -343,22 +371,6 @@ public class GameView extends Pane {
|
|||||||
GeoUtility.getBearing(mark, mark)
|
GeoUtility.getBearing(mark, mark)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Scale race to markers if there is no border.
|
|
||||||
if (borderPoints == null) {
|
|
||||||
rescaleRace(new ArrayList<>(markerObjects.keySet()));
|
|
||||||
}
|
|
||||||
//Move the Markers to initial position.
|
|
||||||
markerObjects.forEach(((mark, marker) -> {
|
|
||||||
Point2D p2d = findScaledXY(mark.getLat(), mark.getLng());
|
|
||||||
marker.setLayoutX(p2d.getX());
|
|
||||||
marker.setLayoutY(p2d.getY());
|
|
||||||
}));
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
markers.getChildren().clear();
|
|
||||||
markers.getChildren().addAll(gates);
|
|
||||||
markers.getChildren().addAll(markerObjects.values());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -49,17 +49,13 @@ public class Marker extends Group {
|
|||||||
*/
|
*/
|
||||||
public void addArrows(MarkArrowFactory.RoundingSide roundingSide, double entryAngle,
|
public void addArrows(MarkArrowFactory.RoundingSide roundingSide, double entryAngle,
|
||||||
double exitAngle) {
|
double exitAngle) {
|
||||||
|
//Change Color.GRAY to this.colour to revert all gray arrows.
|
||||||
enterArrows.add(
|
enterArrows.add(
|
||||||
MarkArrowFactory.constructEntryArrow(roundingSide, entryAngle, exitAngle, colour)
|
MarkArrowFactory.constructEntryArrow(roundingSide, entryAngle, exitAngle, Color.GRAY)
|
||||||
);
|
);
|
||||||
exitArrows.add(
|
exitArrows.add(
|
||||||
MarkArrowFactory.constructExitArrow(roundingSide, exitAngle, colour)
|
MarkArrowFactory.constructExitArrow(roundingSide, exitAngle, Color.GRAY)
|
||||||
);
|
);
|
||||||
// Platform.runLater(() -> {
|
|
||||||
// this.getChildren().add(enterArrows.get(enterArrows.size()-1));
|
|
||||||
// this.getChildren().add(exitArrows.get(exitArrows.size()-1));
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -140,7 +140,6 @@ public class GeoUtilityTest {
|
|||||||
// for (int i = 0; i < 100000; i++) {
|
// for (int i = 0; i < 100000; i++) {
|
||||||
// assertTrue(GeoUtility.isPointInTriangle(v1, v2, v3, p1));
|
// assertTrue(GeoUtility.isPointInTriangle(v1, v2, v3, p1));
|
||||||
// }
|
// }
|
||||||
// System.out.println((System.nanoTime() - startTime) / 1000000000.0);
|
|
||||||
|
|
||||||
// test for different orders of vertices, which should not affect the result
|
// test for different orders of vertices, which should not affect the result
|
||||||
assertTrue(GeoUtility.isPointInTriangle(v2, v1, v3, p1));
|
assertTrue(GeoUtility.isPointInTriangle(v2, v1, v3, p1));
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package seng302.visualiser.ClientToServerTests;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import seng302.gameServer.MainServerThread;
|
||||||
|
import seng302.visualiser.ClientToServerThread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by cir27 on 17/08/17.
|
||||||
|
*/
|
||||||
|
public class DisconnectionTest {
|
||||||
|
@Test
|
||||||
|
public void testServerDisconnection () throws Exception {
|
||||||
|
MainServerThread serverThread = new MainServerThread();
|
||||||
|
ClientToServerThread clientThread = new ClientToServerThread("localhost", 4942);
|
||||||
|
Thread.sleep(1000);
|
||||||
|
clientThread.addDisconnectionListener(message -> Assert.assertTrue(message != null));
|
||||||
|
serverThread.terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user