Merge branch 'master' into story_61

Also got client-server handshake working. Multiple clients can connect and control their own boat.
The client's boat has a larger black triangle drawn around it. Probably needs tidying up.
Added isClientBoat boolean to VisualiserBoat.

WindGenerator is now an interface, implemented by ConstantWindGenerator (useful for testing) and RandomWindGenerator. Added ConstantWindGeneratorTest.
RandomWindGenerator was formerly WindGenerator.
The WindGenerator is passed in to MockRace.

CommandFactory throws a CommandConstructionException if it cannot create a command.

MessageSerialiser and MessageDeserialiser correctly terminate on error.

Readded VisualiserInput's switch statement in the main loop, pending a refactor.

Removed the sleep statement from LobbyController - it was blocking javaFX thread.

Lobby.fxml: moved buttons and text input into grid panes, so they don't break when you resize screen.

Added some test object creation functions in several test classes (MockRaceTest.createMockRace(), BoatXMLReaderTest.createBoatDataSource(), etc...).

#story[1095]
This commit is contained in:
fjc40
2017-08-10 23:49:21 +12:00
45 changed files with 1320 additions and 556 deletions
@@ -36,6 +36,11 @@ public class ClientConnection implements Runnable {
*/
private HeartBeatService heartBeatService;
/**
* The thread the {@link HeartBeatService} runs on.
*/
private Thread heartBeatThread;
/**
* Used to allocate source ID to client, if they request to participate.
@@ -53,16 +58,31 @@ public class ClientConnection implements Runnable {
*/
private CompositeCommand compositeCommand;
/**
* The race the client is connected to.
*/
private RaceLogic raceLogic;
/**
* Used to send the race snapshot to client.
*/
private MockOutput mockOutput;
/**
* The thread the {@link MockOutput} runs on.
*/
private Thread mockOutputThread;
/**
* Used to receive client input, and turn it into commands.
*/
private ControllerServer controllerServer;
/**
* The thread the {@link ControllerServer} runs on.
*/
private Thread controllerServerThread;
/**
* Used to write messages to socket.
@@ -100,13 +120,15 @@ public class ClientConnection implements Runnable {
* @param sourceIdAllocator Used to allocate a source ID for the client.
* @param latestMessages Latest race snapshot to send to client.
* @param compositeCommand Collection of commands for race to execute.
* @param raceLogic The race the client is connected to.
* @throws IOException Thrown if there is a problem with the client socket.
*/
public ClientConnection(Socket socket, SourceIdAllocator sourceIdAllocator, LatestMessages latestMessages, CompositeCommand compositeCommand) throws IOException {
public ClientConnection(Socket socket, SourceIdAllocator sourceIdAllocator, LatestMessages latestMessages, CompositeCommand compositeCommand, RaceLogic raceLogic) throws IOException {
this.socket = socket;
this.sourceIdAllocator = sourceIdAllocator;
this.latestMessages = latestMessages;
this.compositeCommand = compositeCommand;
this.raceLogic = raceLogic;
this.outputQueue = new LinkedBlockingQueue<>();
this.inputQueue = new LinkedBlockingQueue<>();
@@ -120,7 +142,8 @@ public class ClientConnection implements Runnable {
this.heartBeatService = new HeartBeatService(outputQueue);
new Thread(heartBeatService, "ClientConnection()->HeartBeatService thread " + heartBeatService).start();
this.heartBeatThread = new Thread(heartBeatService, "ClientConnection()->HeartBeatService thread " + heartBeatService);
this.heartBeatThread.start();
}
@@ -145,7 +168,7 @@ public class ClientConnection implements Runnable {
* @throws HandshakeException Thrown if something goes wrong with the handshake.
* @throws SourceIDAllocationException Thrown if we cannot allocate a sourceID.
*/
private void handshake() throws SourceIDAllocationException, HandshakeException {
private void handshake() throws SourceIDAllocationException, HandshakeException {
//This function is a bit messy, and could probably be refactored a bit.
@@ -162,16 +185,20 @@ public class ClientConnection implements Runnable {
allocatedSourceID = sourceIdAllocator.allocateSourceID();
this.controllerServer = new ControllerServer(compositeCommand, inputQueue, allocatedSourceID);
new Thread(controllerServer, "ClientConnection.run()->ControllerServer thread" + controllerServer).start();
this.controllerServer = new ControllerServer(compositeCommand, inputQueue, allocatedSourceID, raceLogic.getRace());
this.controllerServerThread = new Thread(controllerServer, "ClientConnection.run()->ControllerServer thread" + controllerServer);
this.controllerServerThread.start();
}
this.mockOutput = new MockOutput(latestMessages, outputQueue);
new Thread(mockOutput, "ClientConnection.run()->MockOutput thread" + mockOutput).start();
sendJoinAcceptanceMessage(allocatedSourceID);
this.mockOutput = new MockOutput(latestMessages, outputQueue);
this.mockOutputThread = new Thread(mockOutput, "ClientConnection.run()->MockOutput thread" + mockOutput);
this.mockOutputThread.start();
connectionState = ConnectionStateEnum.CONNECTED;
}
@@ -240,4 +267,22 @@ public class ClientConnection implements Runnable {
}
/**
* Terminates this connection.
*/
public void terminate() {
if (this.heartBeatThread != null) {
this.heartBeatThread.interrupt();
}
if (this.mockOutputThread != null) {
this.mockOutputThread.interrupt();
}
if (this.controllerServerThread != null) {
this.controllerServerThread.interrupt();
}
}
}