Added empty data sources, to be used by VisualiserRace.

Created FrameRateTracker which can be used to track framerate.
Updated RequestToJoinEnum and JoinAcceptanceEnum to match the agreed connection API.
Added AssignPlayerBoat message, which is used internally on the client to assign the player a source ID once they have connected.
Fixed some race conditions in the MessageRouter.
Updated ConnectionAcceptor.CheckClientConnection to wait slightly longer before removing connection (there was a slight race condition before).
Race no longer has a reference to LatestMessages. LatestMessages no longer has specific messages types in it.
Created RaceState class, which contains the state that is shared between VisualiserRaceState and MockRaceState (currently only used on visualiser).
Split VisualiserRace into VisualiserRaceState and VisualiserRaceService.
Added the VisualiserRace commands (BoatLocatonCommand, RaceStatusCommand, etc...).
Slightly increased the preferred width of race.fxml table columns.
issues #27 #37 #35
#story[1095]
This commit is contained in:
fjc40
2017-08-14 00:53:05 +12:00
parent 89b0aa8b77
commit 7366aba5ec
53 changed files with 2498 additions and 1129 deletions
@@ -4,7 +4,6 @@ package visualiser.Controllers;
import javafx.application.Platform;
import javafx.beans.property.Property;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
@@ -12,7 +11,6 @@ import javafx.scene.layout.StackPane;
import javafx.scene.shape.Circle;
import shared.model.Bearing;
import shared.model.Wind;
import visualiser.model.VisualiserRace;
/**
* Controller for the arrow.fxml view.
@@ -4,14 +4,14 @@ import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.layout.AnchorPane;
import visualiser.gameController.ControllerClient;
import visualiser.network.ServerConnection;
import visualiser.model.VisualiserBoat;
import visualiser.model.VisualiserRace;
import visualiser.model.VisualiserRaceEvent;
import java.net.Socket;
import java.net.URL;
import java.util.ResourceBundle;
/**
* Controller that everything is overlayed onto. This makes it so that changing scenes does not resize your stage.
*/
@@ -38,10 +38,9 @@ public class MainController extends Controller {
* Transitions from the StartController screen (displays pre-race information) to the RaceController (displays the actual race).
* @param visualiserRace The object modelling the race.
* @param controllerClient Socket Client that manipulates the controller.
* @param serverConnection The connection to the server.
*/
public void beginRace(VisualiserRace visualiserRace, ControllerClient controllerClient, ServerConnection serverConnection) {
raceController.startRace(visualiserRace, controllerClient, serverConnection);
public void beginRace(VisualiserRaceEvent visualiserRace, ControllerClient controllerClient) {
raceController.startRace(visualiserRace, controllerClient);
}
/**
@@ -4,7 +4,9 @@ package visualiser.Controllers;
import javafx.animation.AnimationTimer;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import javafx.fxml.FXML;
import javafx.scene.chart.LineChart;
import javafx.scene.control.*;
@@ -28,6 +30,8 @@ import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Controller used to display a running race.
*/
@@ -37,12 +41,16 @@ public class RaceController extends Controller {
/**
* The race object which describes the currently occurring race.
*/
private VisualiserRace visualiserRace;
private VisualiserRaceEvent visualiserRace;
/**
* An additional observable list of boats. This is used by the table view, to allow it to sort boats without effecting the race's own list of boats.
* Service for sending keystrokes to server
*/
private ObservableList<VisualiserBoat> tableBoatList;
private ControllerClient controllerClient;
/**
* The canvas that draws the race.
@@ -59,15 +67,6 @@ public class RaceController extends Controller {
*/
@FXML private ArrowController arrowController;
/**
* Service for sending keystrokes to server
*/
private ControllerClient controllerClient;
/**
* The connection to the server.
*/
private ServerConnection serverConnection;
@FXML private GridPane canvasBase;
@@ -162,7 +161,7 @@ public class RaceController extends Controller {
* Initialises the frame rate functionality. This allows for toggling the frame rate, and connect the fps label to the race's fps property.
* @param visualiserRace The race to connect the fps label to.
*/
private void initialiseFps(VisualiserRace visualiserRace) {
private void initialiseFps(VisualiserRaceEvent visualiserRace) {
//On/off toggle.
initialiseFpsToggle();
@@ -192,9 +191,9 @@ public class RaceController extends Controller {
* Initialises the fps label to update when the race fps changes.
* @param visualiserRace The race to monitor the frame rate of.
*/
private void initialiseFpsLabel(VisualiserRace visualiserRace) {
private void initialiseFpsLabel(VisualiserRaceEvent visualiserRace) {
visualiserRace.fpsProperty().addListener((observable, oldValue, newValue) -> {
visualiserRace.getFrameRateProperty().addListener((observable, oldValue, newValue) -> {
Platform.runLater(() -> this.FPS.setText("FPS: " + newValue.toString()));
});
@@ -206,14 +205,15 @@ public class RaceController extends Controller {
* Initialises the information table view to listen to a given race.
* @param race Race to listen to.
*/
public void initialiseInfoTable(VisualiserRace race) {
public void initialiseInfoTable(VisualiserRaceEvent race) {
//Copy list of boats.
this.tableBoatList = FXCollections.observableArrayList(race.getBoats());
SortedList<VisualiserBoat> sortedBoats = new SortedList<>(race.getVisualiserRaceState().getBoats());
sortedBoats.comparatorProperty().bind(boatInfoTable.comparatorProperty());
//Set up table.
boatInfoTable.setItems(this.tableBoatList);
boatInfoTable.setItems(sortedBoats);
//Set up each column.
@@ -293,12 +293,12 @@ public class RaceController extends Controller {
/**
* Initialises the {@link Sparkline}, and listens to a specified {@link VisualiserRace}.
* Initialises the {@link Sparkline}, and listens to a specified {@link VisualiserRaceEvent}.
* @param race The race to listen to.
*/
private void initialiseSparkline(VisualiserRace race) {
private void initialiseSparkline(VisualiserRaceEvent race) {
//The race.getBoats() we are passing in is sorted by position in race inside the race class.
this.sparkline = new Sparkline(this.visualiserRace, this.sparklineChart);
this.sparkline = new Sparkline(this.visualiserRace.getVisualiserRaceState(), this.sparklineChart);
}
@@ -306,7 +306,7 @@ public class RaceController extends Controller {
* Initialises the {@link ResizableRaceCanvas}, provides the race to read data from.
* @param race Race to read data from.
*/
private void initialiseRaceCanvas(VisualiserRace race) {
private void initialiseRaceCanvas(VisualiserRaceEvent race) {
//Create canvas.
raceCanvas = new ResizableRaceCanvas(race);
@@ -329,18 +329,18 @@ public class RaceController extends Controller {
* Intialises the race time zone label with the race's time zone.
* @param race The race to get time zone from.
*/
private void initialiseRaceTimezoneLabel(VisualiserRace race) {
timeZone.setText(race.getRaceClock().getTimeZone());
private void initialiseRaceTimezoneLabel(VisualiserRaceEvent race) {
timeZone.setText(race.getVisualiserRaceState().getRaceClock().getTimeZone());
}
/**
* Initialises the race clock to listen to the specified race.
* @param race The race to listen to.
*/
private void initialiseRaceClock(VisualiserRace race) {
private void initialiseRaceClock(VisualiserRaceEvent race) {
//RaceClock.duration isn't necessarily being changed in the javaFX thread, so we need to runlater the update.
race.getRaceClock().durationProperty().addListener((observable, oldValue, newValue) -> {
race.getVisualiserRaceState().getRaceClock().durationProperty().addListener((observable, oldValue, newValue) -> {
Platform.runLater(() -> {
timer.setText(newValue);
});
@@ -353,13 +353,11 @@ public class RaceController extends Controller {
* Displays a specified race.
* @param visualiserRace Object modelling the race.
* @param controllerClient Socket Client that manipulates the controller.
* @param serverConnection The connection to the server.
*/
public void startRace(VisualiserRace visualiserRace, ControllerClient controllerClient, ServerConnection serverConnection) {
public void startRace(VisualiserRaceEvent visualiserRace, ControllerClient controllerClient) {
this.visualiserRace = visualiserRace;
this.controllerClient = controllerClient;
this.serverConnection = serverConnection;
initialiseRace();
@@ -375,7 +373,7 @@ public class RaceController extends Controller {
* Transition from the race view to the finish view.
* @param boats boats there are in the race.
*/
public void finishRace(ObservableList<VisualiserBoat> boats){
public void finishRace(ObservableList<VisualiserBoat> boats) {
race.setVisible(false);
parent.enterFinish(boats);
}
@@ -385,8 +383,8 @@ public class RaceController extends Controller {
* Initialises the arrow controller with data from the race to observe.
* @param race The race to observe.
*/
private void initialiseArrow(VisualiserRace race) {
arrowController.setWindProperty(race.windProperty());
private void initialiseArrow(VisualiserRaceEvent race) {
arrowController.setWindProperty(race.getVisualiserRaceState().windProperty());
}
@@ -399,7 +397,7 @@ public class RaceController extends Controller {
public void handle(long arg0) {
//Get the current race status.
RaceStatusEnum raceStatus = visualiserRace.getRaceStatusEnum();
RaceStatusEnum raceStatus = visualiserRace.getVisualiserRaceState().getRaceStatusEnum();
//If the race has finished, go to finish view.
@@ -408,7 +406,7 @@ public class RaceController extends Controller {
stop();
//Hide this, and display the finish controller.
finishRace(visualiserRace.getBoats());
finishRace(visualiserRace.getVisualiserRaceState().getBoats());
} else {
//Otherwise, render the canvas.
@@ -421,7 +419,7 @@ public class RaceController extends Controller {
}
//Return to main screen if we lose connection.
if (!serverConnection.isAlive()) {
if (!visualiserRace.getServerConnection().isAlive()) {
race.setVisible(false);
parent.enterTitle();
//TODO currently this doesn't work correctly - the title screen remains visible after clicking join game
@@ -9,7 +9,7 @@ import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import mock.model.commandFactory.CompositeCommand;
import network.Messages.Enums.RaceStatusEnum;
import network.Messages.Enums.RequestToJoinEnum;
import network.Messages.LatestMessages;
@@ -20,9 +20,10 @@ import shared.exceptions.InvalidRaceDataException;
import shared.exceptions.InvalidRegattaDataException;
import shared.exceptions.XMLReaderException;
import visualiser.gameController.ControllerClient;
import visualiser.model.VisualiserRaceState;
import visualiser.network.ServerConnection;
import visualiser.model.VisualiserBoat;
import visualiser.model.VisualiserRace;
import visualiser.model.VisualiserRaceEvent;
import java.io.IOException;
import java.net.Socket;
@@ -31,10 +32,11 @@ import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Controller to for waiting for the race to start.
*/
public class StartController extends Controller implements Observer {
public class StartController extends Controller {
@FXML private GridPane start;
@FXML private AnchorPane startWrapper;
@@ -70,9 +72,10 @@ public class StartController extends Controller implements Observer {
/**
* Our connection to the server.
* The race + connection to server.
*/
private ServerConnection serverConnection;
private VisualiserRaceEvent visualiserRaceEvent;
/**
* Writes BoatActions to outgoing message queue.
@@ -80,27 +83,9 @@ public class StartController extends Controller implements Observer {
private ControllerClient controllerClient;
/**
* The race object which describes the currently occurring race.
*/
private VisualiserRace visualiserRace;
/**
* An array of colors used to assign colors to each boat - passed in to the VisualiserRace constructor.
*/
private List<Color> colors = new ArrayList<>(Arrays.asList(
Color.BLUEVIOLET,
Color.BLACK,
Color.RED,
Color.ORANGE,
Color.DARKOLIVEGREEN,
Color.LIMEGREEN,
Color.PURPLE,
Color.DARKGRAY,
Color.YELLOW
));
@@ -117,41 +102,17 @@ public class StartController extends Controller implements Observer {
/**
* Starts the race.
* Called once we have received all XML files from the server.
* @param latestMessages The set of latest race messages to use for race.
* @throws XMLReaderException Thrown if XML file cannot be parsed.
* @throws InvalidRaceDataException Thrown if XML file cannot be parsed.
* @throws InvalidBoatDataException Thrown if XML file cannot be parsed.
* @throws InvalidRegattaDataException Thrown if XML file cannot be parsed.
*/
private void startRace(LatestMessages latestMessages) throws XMLReaderException, InvalidRaceDataException, InvalidBoatDataException, InvalidRegattaDataException {
//Create data sources from latest messages for the race.
RaceDataSource raceDataSource = new RaceXMLReader(latestMessages.getRaceXMLMessage().getXmlMessage(), XMLFileType.Contents);
BoatDataSource boatDataSource = new BoatXMLReader(latestMessages.getBoatXMLMessage().getXmlMessage(), XMLFileType.Contents);
RegattaDataSource regattaDataSource = new RegattaXMLReader(latestMessages.getRegattaXMLMessage().getXmlMessage(), XMLFileType.Contents);
//Create race.
this.visualiserRace = new VisualiserRace(boatDataSource, raceDataSource, regattaDataSource, latestMessages, this.colors);
new Thread(this.visualiserRace).start();
//Ugly. TODO refactor
ObservableList<VisualiserBoat> boats = visualiserRace.getBoats();
for (VisualiserBoat boat : boats) {
if (boat.getSourceID() == serverConnection.getAllocatedSourceID()) {
boat.setClientBoat(true);
}
}
private void startRace() {
//Initialise the boat table.
initialiseBoatTable(this.visualiserRace);
initialiseBoatTable(this.visualiserRaceEvent.getVisualiserRaceState());
//Initialise the race name.
initialiseRaceName(this.visualiserRace);
initialiseRaceName(this.visualiserRaceEvent.getVisualiserRaceState());
//Initialises the race clock.
initialiseRaceClock(this.visualiserRace);
initialiseRaceClock(this.visualiserRaceEvent.getVisualiserRaceState());
//Starts the race countdown timer.
countdownTimer();
@@ -169,7 +130,7 @@ public class StartController extends Controller implements Observer {
* Initialises the boat table that is to be shown on the pane.
* @param visualiserRace The race to get data from.
*/
private void initialiseBoatTable(VisualiserRace visualiserRace) {
private void initialiseBoatTable(VisualiserRaceState visualiserRace) {
//Get the boats.
ObservableList<VisualiserBoat> boats = visualiserRace.getBoats();
@@ -184,7 +145,7 @@ public class StartController extends Controller implements Observer {
* Initialises the race name which is shown on the pane.
* @param visualiserRace The race to get data from.
*/
private void initialiseRaceName(VisualiserRace visualiserRace) {
private void initialiseRaceName(VisualiserRaceState visualiserRace) {
raceTitleLabel.setText(visualiserRace.getRegattaName());
@@ -194,7 +155,7 @@ public class StartController extends Controller implements Observer {
* Initialises the race clock/timer labels for the start time, current time, and remaining time.
* @param visualiserRace The race to get data from.
*/
private void initialiseRaceClock(VisualiserRace visualiserRace) {
private void initialiseRaceClock(VisualiserRaceState visualiserRace) {
//Start time.
initialiseRaceClockStartTime(visualiserRace);
@@ -212,7 +173,7 @@ public class StartController extends Controller implements Observer {
* Initialises the race current time label.
* @param visualiserRace The race to get data from.
*/
private void initialiseRaceClockStartTime(VisualiserRace visualiserRace) {
private void initialiseRaceClockStartTime(VisualiserRaceState visualiserRace) {
raceStartLabel.setText(visualiserRace.getRaceClock().getStartingTimeString());
@@ -229,7 +190,7 @@ public class StartController extends Controller implements Observer {
* Initialises the race current time label.
* @param visualiserRace The race to get data from.
*/
private void initialiseRaceClockCurrentTime(VisualiserRace visualiserRace) {
private void initialiseRaceClockCurrentTime(VisualiserRaceState visualiserRace) {
visualiserRace.getRaceClock().currentTimeProperty().addListener((observable, oldValue, newValue) -> {
Platform.runLater(() -> {
@@ -243,7 +204,7 @@ public class StartController extends Controller implements Observer {
* Initialises the race duration label.
* @param visualiserRace The race to get data from.
*/
private void initialiseRaceClockDuration(VisualiserRace visualiserRace) {
private void initialiseRaceClockDuration(VisualiserRaceState visualiserRace) {
visualiserRace.getRaceClock().durationProperty().addListener((observable, oldValue, newValue) -> {
Platform.runLater(() -> {
@@ -261,13 +222,13 @@ public class StartController extends Controller implements Observer {
@Override
public void handle(long arg0) {
//TODO instead of having an AnimationTimer checking the race status, we could provide a Property<RaceStatusEnum>, and connect a listener to that.
//Get the current race status.
RaceStatusEnum raceStatus = visualiserRace.getRaceStatusEnum();
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
//Display it.
raceStatusLabel.setText("Race Status: " + raceStatus.name());
//If the race has reached the preparatory phase, or has started...
if (raceStatus == RaceStatusEnum.PREPARATORY || raceStatus == RaceStatusEnum.STARTED) {
//Stop this timer.
@@ -275,9 +236,10 @@ public class StartController extends Controller implements Observer {
//Hide this, and display the race controller.
startWrapper.setVisible(false);
start.setVisible(false);
//start.setVisible(false);//TODO is this needed?
parent.beginRace(visualiserRaceEvent, controllerClient);
parent.beginRace(visualiserRace, controllerClient, serverConnection);
}
}
}.start();
@@ -285,58 +247,25 @@ public class StartController extends Controller implements Observer {
/**
* Function to handle changes in objects we observe.
* We observe LatestMessages.
* @param o The observed object.
* @param arg The {@link Observable#notifyObservers(Object)} parameter.
*/
@Override
public void update(Observable o, Object arg) {
//Check that we actually have LatestMessages.
if (o instanceof LatestMessages) {
LatestMessages latestMessages = (LatestMessages) o;
//If we've received all of the xml files, start the race. Only start it if it hasn't already been created.
if (latestMessages.hasAllXMLMessages() && this.visualiserRace == null) {
//Need to handle it in the javafx thread.
Platform.runLater(() -> {
try {
this.startRace(latestMessages);
} catch (XMLReaderException | InvalidBoatDataException | InvalidRaceDataException | InvalidRegattaDataException e) {
//We currently don't handle this in meaningful way, as it should never occur.
//If we reach this point it means that malformed XML files were sent.
e.printStackTrace();
}
});
}
}
}
/**
* Show starting information for a race given a socket.
* @param socket network source of information
*/
public void enterLobby(Socket socket) {
startWrapper.setVisible(true);
try {
LatestMessages latestMessages = new LatestMessages();
this.serverConnection = new ServerConnection(socket, latestMessages, RequestToJoinEnum.PARTICIPANT);
this.controllerClient = serverConnection.getControllerClient();
this.visualiserRaceEvent = new VisualiserRaceEvent(socket, RequestToJoinEnum.PARTICIPANT);
this.controllerClient = visualiserRaceEvent.getControllerClient();
//Store a reference to latestMessages so that we can observe it.
latestMessages.addObserver(this);
new Thread(this.serverConnection).start();
startWrapper.setVisible(true);
startRace();
} catch (IOException e) {
Logger.getGlobal().log(Level.WARNING, "Could not connection to server.", e);
//TODO should probably let this propagate, so that we only enter this scene if everything works
Logger.getGlobal().log(Level.WARNING, "Could not connect to server.", e);
}
}