Merge remote-tracking branch 'origin/Story62_Creating_Game_Loop' into Story62_Reading_Keystrokes

# Conflicts:
#	src/main/java/seng302/controllers/RaceViewController.java
#	src/main/java/seng302/gameServer/MainServerThread.java
#	src/main/java/seng302/gameServer/ServerToClientThread.java
This commit is contained in:
Zhi You Tan
2017-07-21 11:13:40 +12:00
18 changed files with 297 additions and 170 deletions
+2 -2
View File
@@ -6,8 +6,8 @@ import javafx.scene.Parent;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.stage.Stage; import javafx.stage.Stage;
import seng302.client.ClientPacketParser;
import seng302.models.PolarTable; import seng302.models.PolarTable;
import seng302.models.stream.StreamParser;
import seng302.models.stream.StreamReceiver; import seng302.models.stream.StreamReceiver;
public class App extends Application { public class App extends Application {
@@ -26,7 +26,7 @@ public class App extends Application {
primaryStage.show(); primaryStage.show();
primaryStage.setOnCloseRequest(e -> { primaryStage.setOnCloseRequest(e -> {
StreamParser.appClose(); ClientPacketParser.appClose();
StreamReceiver.noMoreBytes(); StreamReceiver.noMoreBytes();
System.exit(0); System.exit(0);
}); });
@@ -1,4 +1,4 @@
package seng302.models.stream; package seng302.client;
import java.io.IOException; import java.io.IOException;
@@ -22,6 +22,7 @@ import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import seng302.models.Yacht; import seng302.models.Yacht;
import seng302.models.mark.Mark; import seng302.models.mark.Mark;
import seng302.models.stream.XMLParser;
import seng302.models.stream.packets.BoatPositionPacket; import seng302.models.stream.packets.BoatPositionPacket;
import seng302.models.stream.packets.StreamPacket; import seng302.models.stream.packets.StreamPacket;
@@ -31,7 +32,7 @@ import seng302.models.stream.packets.StreamPacket;
* that are threadsafe so the visualiser can always access the latest speed and position available * that are threadsafe so the visualiser can always access the latest speed and position available
* Created by kre39 on 23/04/17. * Created by kre39 on 23/04/17.
*/ */
public class StreamParser{ public class ClientPacketParser {
public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> markLocations = new ConcurrentHashMap<>(); public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> markLocations = new ConcurrentHashMap<>();
public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> boatLocations = new ConcurrentHashMap<>(); public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> boatLocations = new ConcurrentHashMap<>();
@@ -58,7 +59,7 @@ public class StreamParser{
/** /**
* Used to initialise the thread name and stream parser object so a thread can be executed * Used to initialise the thread name and stream parser object so a thread can be executed
*/ */
public StreamParser() { public ClientPacketParser() {
} }
/** /**
* Looks at the type of the packet then sends it to the appropriate parser to extract the * Looks at the type of the packet then sends it to the appropriate parser to extract the
@@ -106,9 +107,6 @@ public class StreamParser{
case AVG_WIND: case AVG_WIND:
extractAvgWind(packet); extractAvgWind(packet);
break; break;
case BOAT_ACTION:
extractBoatAction(packet);
break;
} }
} catch (NullPointerException e) { } catch (NullPointerException e) {
System.out.println("Error parsing packet"); System.out.println("Error parsing packet");
@@ -491,27 +489,6 @@ public class StreamParser{
long speed4 = bytesToLong(Arrays.copyOfRange(payload, 21, 23)); long speed4 = bytesToLong(Arrays.copyOfRange(payload, 21, 23));
} }
private static void extractBoatAction(StreamPacket packet) {
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
long actionType = bytesToLong(Arrays.copyOfRange(payload, 0, 1));
if (actionType == 1) {
System.out.println("VMG");
} else if (actionType == 2) {
System.out.println("SAILS IN");
} else if (actionType == 3) {
System.out.println("SAILS OUT");
} else if (actionType == 4) {
System.out.println("TACK/GYBE");
} else if (actionType == 5) {
System.out.println("UPWIND");
} else if (actionType == 6) {
System.out.println("DOWNWIND");
}
}
/** /**
* takes an array of up to 7 bytes and returns a positive * takes an array of up to 7 bytes and returns a positive
* long constructed from the input bytes * long constructed from the input bytes
@@ -8,10 +8,8 @@ import java.net.Socket;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import java.util.zip.Checksum; import java.util.zip.Checksum;
import seng302.models.stream.StreamParser;
import seng302.models.stream.packets.StreamPacket; import seng302.models.stream.packets.StreamPacket;
import seng302.server.messages.BoatActionMessage; import seng302.server.messages.BoatActionMessage;
import seng302.server.messages.BoatActionType;
import seng302.server.messages.Message; import seng302.server.messages.Message;
/** /**
@@ -76,7 +74,8 @@ public class ClientToServerThread extends Thread {
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) {
StreamParser.parsePacket(new StreamPacket(type, payloadLength, timeStamp, payload)); ClientPacketParser
.parsePacket(new StreamPacket(type, payloadLength, timeStamp, payload));
// TODO: 17/07/17 wmu16 - Fix this or maybe we dont need to go through the main server at all!?!? // TODO: 17/07/17 wmu16 - Fix this or maybe we dont need to go through the main server at all!?!?
// packetBufferDelegate.addToBuffer(new StreamPacket(type, payloadLength, timeStamp, payload)); // packetBufferDelegate.addToBuffer(new StreamPacket(type, payloadLength, timeStamp, payload));
} else { } else {
@@ -15,13 +15,13 @@ import javafx.scene.Group;
import javafx.scene.canvas.Canvas; import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext; import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon; import javafx.scene.shape.Polygon;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import seng302.client.ClientPacketParser;
import seng302.fxObjects.BoatGroup; import seng302.fxObjects.BoatGroup;
import seng302.models.Colors; import seng302.models.Colors;
import seng302.models.Yacht; import seng302.models.Yacht;
@@ -32,7 +32,6 @@ import seng302.models.mark.MarkType;
import seng302.models.mark.SingleMark; import seng302.models.mark.SingleMark;
import seng302.models.map.Boundary; import seng302.models.map.Boundary;
import seng302.models.map.CanvasMap; import seng302.models.map.CanvasMap;
import seng302.models.stream.StreamParser;
import seng302.models.stream.XMLParser; import seng302.models.stream.XMLParser;
import seng302.models.stream.XMLParser.RaceXMLObject.Limit; import seng302.models.stream.XMLParser.RaceXMLObject.Limit;
import seng302.models.stream.XMLParser.RaceXMLObject.Participant; import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
@@ -156,13 +155,13 @@ public class CanvasController {
raceViewController.updateSparkLine(); raceViewController.updateSparkLine();
} }
updateGroups(); updateGroups();
if (StreamParser.isRaceFinished()) { if (ClientPacketParser.isRaceFinished()) {
this.stop(); this.stop();
} }
lastTime = now; lastTime = now;
} }
} }
if (StreamParser.isRaceFinished()) { if (ClientPacketParser.isRaceFinished()) {
this.stop(); this.stop();
switchToFinishScreen(); switchToFinishScreen();
} }
@@ -231,7 +230,7 @@ public class CanvasController {
* in a compound mark etc.. * in a compound mark etc..
*/ */
private void addRaceBorder() { private void addRaceBorder() {
XMLParser.RaceXMLObject raceXMLObject = StreamParser.getXmlObject().getRaceXML(); XMLParser.RaceXMLObject raceXMLObject = ClientPacketParser.getXmlObject().getRaceXML();
ArrayList<Limit> courseLimits = raceXMLObject.getCourseLimit(); ArrayList<Limit> courseLimits = raceXMLObject.getCourseLimit();
raceBorder.setStroke(new Color(0.0f, 0.0f, 0.74509807f, 1)); raceBorder.setStroke(new Color(0.0f, 0.0f, 0.74509807f, 1));
raceBorder.setStrokeWidth(3); raceBorder.setStrokeWidth(3);
@@ -249,7 +248,7 @@ public class CanvasController {
for (BoatGroup boatGroup : boatGroups) { for (BoatGroup boatGroup : boatGroups) {
// some raceObjects will have multiple ID's (for instance gate marks) // some raceObjects will have multiple ID's (for instance gate marks)
//checking if the current "ID" has any updates associated with it //checking if the current "ID" has any updates associated with it
if (StreamParser.boatLocations.containsKey(boatGroup.getRaceId())) { if (ClientPacketParser.boatLocations.containsKey(boatGroup.getRaceId())) {
if (boatGroup.isStopped()) { if (boatGroup.isStopped()) {
updateBoatGroup(boatGroup); updateBoatGroup(boatGroup);
} }
@@ -258,7 +257,7 @@ public class CanvasController {
} }
for (MarkGroup markGroup : markGroups) { for (MarkGroup markGroup : markGroups) {
for (Long id : markGroup.getRaceIds()) { for (Long id : markGroup.getRaceIds()) {
if (StreamParser.markLocations.containsKey(id)) { if (ClientPacketParser.markLocations.containsKey(id)) {
updateMarkGroup(id, markGroup); updateMarkGroup(id, markGroup);
} }
} }
@@ -267,13 +266,14 @@ public class CanvasController {
} }
private void checkForCourseChanges() { private void checkForCourseChanges() {
if (StreamParser.isNewRaceXmlReceived()){ if (ClientPacketParser.isNewRaceXmlReceived()) {
addRaceBorder(); addRaceBorder();
} }
} }
private void updateBoatGroup(BoatGroup boatGroup) { private void updateBoatGroup(BoatGroup boatGroup) {
PriorityBlockingQueue<BoatPositionPacket> movementQueue = StreamParser.boatLocations.get(boatGroup.getRaceId()); PriorityBlockingQueue<BoatPositionPacket> movementQueue = ClientPacketParser.boatLocations
.get(boatGroup.getRaceId());
// giving the movementQueue a 5 packet buffer to account for slightly out of order packets // giving the movementQueue a 5 packet buffer to account for slightly out of order packets
if (movementQueue.size() > 0) { if (movementQueue.size() > 0) {
try { try {
@@ -291,7 +291,8 @@ public class CanvasController {
} }
void updateMarkGroup (long raceId, MarkGroup markGroup) { void updateMarkGroup (long raceId, MarkGroup markGroup) {
PriorityBlockingQueue<BoatPositionPacket> movementQueue = StreamParser.markLocations.get(raceId); PriorityBlockingQueue<BoatPositionPacket> movementQueue = ClientPacketParser.markLocations
.get(raceId);
if (movementQueue.size() > 0){ if (movementQueue.size() > 0){
try { try {
BoatPositionPacket positionPacket = movementQueue.take(); BoatPositionPacket positionPacket = movementQueue.take();
@@ -307,12 +308,12 @@ public class CanvasController {
* Draws all the boats. * Draws all the boats.
*/ */
private void initializeBoats() { private void initializeBoats() {
Map<Integer, Yacht> boats = StreamParser.getBoats(); Map<Integer, Yacht> boats = ClientPacketParser.getBoats();
Group wakes = new Group(); Group wakes = new Group();
Group trails = new Group(); Group trails = new Group();
Group annotations = new Group(); Group annotations = new Group();
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML() ArrayList<Participant> participants = ClientPacketParser.getXmlObject().getRaceXML()
.getParticipants(); .getParticipants();
ArrayList<Integer> participantIDs = new ArrayList<>(); ArrayList<Integer> participantIDs = new ArrayList<>();
for (Participant p : participants) { for (Participant p : participants) {
@@ -336,7 +337,8 @@ public class CanvasController {
} }
private void initializeMarks() { private void initializeMarks() {
List<Mark> allMarks = StreamParser.getXmlObject().getRaceXML().getNonDupCompoundMarks(); List<Mark> allMarks = ClientPacketParser.getXmlObject().getRaceXML()
.getNonDupCompoundMarks();
for (Mark mark : allMarks) { for (Mark mark : allMarks) {
if (mark.getMarkType() == MarkType.SINGLE_MARK) { if (mark.getMarkType() == MarkType.SINGLE_MARK) {
SingleMark sMark = (SingleMark) mark; SingleMark sMark = (SingleMark) mark;
@@ -402,7 +404,7 @@ public class CanvasController {
*/ */
private void fitMarksToCanvas() { private void fitMarksToCanvas() {
//Check is called once to avoid unnecessarily change the course limits once the race is running //Check is called once to avoid unnecessarily change the course limits once the race is running
StreamParser.isNewRaceXmlReceived(); ClientPacketParser.isNewRaceXmlReceived();
findMinMaxPoint(); findMinMaxPoint();
double minLonToMaxLon = scaleRaceExtremities(); double minLonToMaxLon = scaleRaceExtremities();
calculateReferencePointLocation(minLonToMaxLon); calculateReferencePointLocation(minLonToMaxLon);
@@ -418,7 +420,7 @@ public class CanvasController {
*/ */
private void findMinMaxPoint() { private void findMinMaxPoint() {
List<Limit> sortedPoints = new ArrayList<>(); List<Limit> sortedPoints = new ArrayList<>();
for (Limit limit : StreamParser.getXmlObject().getRaceXML().getCourseLimit()) { for (Limit limit : ClientPacketParser.getXmlObject().getRaceXML().getCourseLimit()) {
sortedPoints.add(limit); sortedPoints.add(limit);
} }
sortedPoints.sort(Comparator.comparingDouble(Limit::getLat)); sortedPoints.sort(Comparator.comparingDouble(Limit::getLat));
@@ -9,7 +9,7 @@ import javafx.fxml.Initializable;
import javafx.scene.Parent; import javafx.scene.Parent;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import seng302.models.stream.StreamParser; import seng302.client.ClientPacketParser;
import seng302.client.ClientToServerThread; import seng302.client.ClientToServerThread;
import seng302.server.messages.BoatActionMessage; import seng302.server.messages.BoatActionMessage;
import seng302.server.messages.BoatActionType; import seng302.server.messages.BoatActionType;
@@ -42,7 +42,7 @@ public class Controller implements Initializable {
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString()); contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
StartScreenController startScreenController = (StartScreenController) setContentPane("/views/StartScreenView.fxml"); StartScreenController startScreenController = (StartScreenController) setContentPane("/views/StartScreenView.fxml");
startScreenController.setController(this); startScreenController.setController(this);
StreamParser.boatLocations.clear(); ClientPacketParser.boatLocations.clear();
} }
/** Handle the key-pressed event from the text field. */ /** Handle the key-pressed event from the text field. */
@@ -15,8 +15,8 @@ import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import seng302.client.ClientPacketParser;
import seng302.models.Yacht; import seng302.models.Yacht;
import seng302.models.stream.StreamParser;
import seng302.models.stream.XMLParser.RaceXMLObject.Participant; import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
public class FinishScreenViewController implements Initializable { public class FinishScreenViewController implements Initializable {
@@ -59,7 +59,7 @@ public class FinishScreenViewController implements Initializable {
); );
// check if the boat is racing // check if the boat is racing
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML() ArrayList<Participant> participants = ClientPacketParser.getXmlObject().getRaceXML()
.getParticipants(); .getParticipants();
ArrayList<Integer> participantIDs = new ArrayList<>(); ArrayList<Integer> participantIDs = new ArrayList<>();
for (Participant p : participants) { for (Participant p : participants) {
@@ -67,7 +67,7 @@ public class FinishScreenViewController implements Initializable {
} }
// add data to table // add data to table
for (Yacht boat : StreamParser.getBoatsPos().values()) { for (Yacht boat : ClientPacketParser.getBoatsPos().values()) {
if (participantIDs.contains(boat.getSourceID())) { if (participantIDs.contains(boat.getSourceID())) {
data.add(boat); data.add(boat);
} }
@@ -4,7 +4,6 @@ import javafx.animation.KeyFrame;
import javafx.animation.Timeline; import javafx.animation.Timeline;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.geometry.Point2D; import javafx.geometry.Point2D;
@@ -17,7 +16,6 @@ import javafx.scene.control.Button;
import javafx.scene.control.CheckBox; import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox; import javafx.scene.control.ComboBox;
import javafx.scene.control.Slider; import javafx.scene.control.Slider;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
@@ -29,6 +27,7 @@ import javafx.stage.Stage;
import javafx.stage.StageStyle; import javafx.stage.StageStyle;
import javafx.util.Duration; import javafx.util.Duration;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import seng302.client.ClientPacketParser;
import seng302.utilities.GeoUtility; import seng302.utilities.GeoUtility;
import seng302.controllers.annotations.Annotation; import seng302.controllers.annotations.Annotation;
import seng302.controllers.annotations.ImportantAnnotationController; import seng302.controllers.annotations.ImportantAnnotationController;
@@ -40,7 +39,6 @@ import seng302.models.*;
import seng302.models.mark.GateMark; import seng302.models.mark.GateMark;
import seng302.models.mark.Mark; import seng302.models.mark.Mark;
import seng302.models.mark.SingleMark; import seng302.models.mark.SingleMark;
import seng302.models.stream.StreamParser;
import seng302.models.stream.XMLParser; import seng302.models.stream.XMLParser;
import java.io.IOException; import java.io.IOException;
@@ -95,16 +93,16 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
raceSparkLine.getYAxis().setTranslateX(-5); raceSparkLine.getYAxis().setTranslateX(-5);
raceSparkLine.getYAxis().setAutoRanging(false); raceSparkLine.getYAxis().setAutoRanging(false);
sparklineYAxis.setTickMarkVisible(false); sparklineYAxis.setTickMarkVisible(false);
//startingBoats = new ArrayList<>(StreamParser.getBoats().values()); startingBoats = new ArrayList<>(ClientPacketParser.getBoats().values());
//includedCanvasController.setup(this); includedCanvasController.setup(this);
//includedCanvasController.initializeCanvas(); includedCanvasController.initializeCanvas();
//initializeUpdateTimer(); initializeUpdateTimer();
//initialiseFPSCheckBox(); initialiseFPSCheckBox();
//initialiseAnnotationSlider(); initialiseAnnotationSlider();
//initialiseBoatSelectionComboBox(); initialiseBoatSelectionComboBox();
//includedCanvasController.timer.start(); includedCanvasController.timer.start();
//selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView()); selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
} }
@@ -305,7 +303,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private Mark getNextMark(BoatGroup bg) { private Mark getNextMark(BoatGroup bg) {
Integer legNumber = bg.getBoat().getLegNumber(); Integer legNumber = bg.getBoat().getLegNumber();
List<XMLParser.RaceXMLObject.Corner> markSequence = StreamParser.getXmlObject().getRaceXML().getCompoundMarkSequence(); List<XMLParser.RaceXMLObject.Corner> markSequence = ClientPacketParser.getXmlObject()
.getRaceXML().getCompoundMarkSequence();
if (legNumber == 0) { if (legNumber == 0) {
return null; return null;
@@ -317,7 +316,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
if (legNumber + 2 == corner.getSeqID()) { if (legNumber + 2 == corner.getSeqID()) {
Integer thisCompoundMarkID = corner.getCompoundMarkID(); Integer thisCompoundMarkID = corner.getCompoundMarkID();
for (Mark mark : StreamParser.getXmlObject().getRaceXML().getAllCompoundMarks()) { for (Mark mark : ClientPacketParser.getXmlObject().getRaceXML()
.getAllCompoundMarks()) {
if (mark.getCompoundMarkID() == thisCompoundMarkID) { if (mark.getCompoundMarkID() == thisCompoundMarkID) {
return mark; return mark;
} }
@@ -330,11 +330,11 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/** /**
* Updates the wind direction arrow and text as from info from the StreamParser * Updates the wind direction arrow and text as from info from the ClientPacketParser
*/ */
private void updateWindDirection() { private void updateWindDirection() {
windDirectionText.setText(String.format("%.1f°", StreamParser.getWindDirection())); windDirectionText.setText(String.format("%.1f°", ClientPacketParser.getWindDirection()));
windArrowText.setRotate(StreamParser.getWindDirection()); windArrowText.setRotate(ClientPacketParser.getWindDirection());
} }
@@ -342,7 +342,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
* Updates the clock for the race * Updates the clock for the race
*/ */
private void updateRaceTime() { private void updateRaceTime() {
if (StreamParser.isRaceFinished()) { if (ClientPacketParser.isRaceFinished()) {
timerLabel.setFill(Color.RED); timerLabel.setFill(Color.RED);
timerLabel.setText("Race Finished!"); timerLabel.setText("Race Finished!");
} else { } else {
@@ -352,18 +352,18 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/** /**
* Grabs the boats currently in the race as from the StreamParser and sets them to be selectable * Grabs the boats currently in the race as from the ClientPacketParser and sets them to be selectable
* in the boat selection combo box * in the boat selection combo box
*/ */
private void updateBoatSelectionComboBox() { private void updateBoatSelectionComboBox() {
ObservableList<Yacht> observableBoats = FXCollections ObservableList<Yacht> observableBoats = FXCollections
.observableArrayList(StreamParser.getBoatsPos().values()); .observableArrayList(ClientPacketParser.getBoatsPos().values());
boatSelectionComboBox.setItems(observableBoats); boatSelectionComboBox.setItems(observableBoats);
} }
/** /**
* Updates the order of the boats as from the StreamParser and sets them in the boat order * Updates the order of the boats as from the ClientPacketParser and sets them in the boat order
* section * section
*/ */
private void updateOrder() { private void updateOrder() {
@@ -372,15 +372,15 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString()); positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
// list of racing boat id // list of racing boat id
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML() ArrayList<Participant> participants = ClientPacketParser.getXmlObject().getRaceXML()
.getParticipants(); .getParticipants();
ArrayList<Integer> participantIDs = new ArrayList<>(); ArrayList<Integer> participantIDs = new ArrayList<>();
for (Participant p : participants) { for (Participant p : participants) {
participantIDs.add(p.getsourceID()); participantIDs.add(p.getsourceID());
} }
if (StreamParser.isRaceStarted()) { if (ClientPacketParser.isRaceStarted()) {
for (Yacht boat : StreamParser.getBoatsPos().values()) { for (Yacht boat : ClientPacketParser.getBoatsPos().values()) {
if (participantIDs.contains(boat.getSourceID())) { // check if the boat is racing if (participantIDs.contains(boat.getSourceID())) { // check if the boat is racing
if (boat.getBoatStatus() == 3) { // 3 is finish status if (boat.getBoatStatus() == 3) { // 3 is finish status
Text textToAdd = new Text(boat.getPosition() + ". " + Text textToAdd = new Text(boat.getPosition() + ". " +
@@ -398,7 +398,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
} }
} }
} else { } else {
for (Yacht boat : StreamParser.getBoats().values()) { for (Yacht boat : ClientPacketParser.getBoats().values()) {
if (participantIDs.contains(boat.getSourceID())) { // check if the boat is racing if (participantIDs.contains(boat.getSourceID())) { // check if the boat is racing
Text textToAdd = new Text(boat.getPosition() + ". " + Text textToAdd = new Text(boat.getPosition() + ". " +
boat.getShortName() + " "); boat.getShortName() + " ");
@@ -436,9 +436,11 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
Point2D markPoint2 = includedCanvasController.findScaledXY(singleMark2.getLatitude(), singleMark2.getLongitude()); Point2D markPoint2 = includedCanvasController.findScaledXY(singleMark2.getLatitude(), singleMark2.getLongitude());
HashMap<Double, Double> angleAndSpeed; HashMap<Double, Double> angleAndSpeed;
if (isUpwind) { if (isUpwind) {
angleAndSpeed = PolarTable.getOptimalUpwindVMG(StreamParser.getWindSpeed()); angleAndSpeed = PolarTable
.getOptimalUpwindVMG(ClientPacketParser.getWindSpeed());
} else { } else {
angleAndSpeed = PolarTable.getOptimalDownwindVMG(StreamParser.getWindSpeed()); angleAndSpeed = PolarTable
.getOptimalDownwindVMG(ClientPacketParser.getWindSpeed());
} }
Double resultingAngle = angleAndSpeed.keySet().iterator().next(); Double resultingAngle = angleAndSpeed.keySet().iterator().next();
@@ -450,11 +452,19 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
Line rightLayline = new Line(); Line rightLayline = new Line();
Line leftLayline = new Line(); Line leftLayline = new Line();
if (lineFuncResult == 1) { if (lineFuncResult == 1) {
rightLayline = makeRightLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection()); rightLayline = makeRightLayline(markPoint2, 180 - resultingAngle,
leftLayline = makeLeftLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection()); ClientPacketParser
.getWindDirection());
leftLayline = makeLeftLayline(markPoint1, 180 - resultingAngle,
ClientPacketParser
.getWindDirection());
} else if (lineFuncResult == -1) { } else if (lineFuncResult == -1) {
rightLayline = makeRightLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection()); rightLayline = makeRightLayline(markPoint1, 180 - resultingAngle,
leftLayline = makeLeftLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection()); ClientPacketParser
.getWindDirection());
leftLayline = makeLeftLayline(markPoint2, 180 - resultingAngle,
ClientPacketParser
.getWindDirection());
} }
leftLayline.setStrokeWidth(0.5); leftLayline.setStrokeWidth(0.5);
@@ -550,16 +560,16 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private String getTimeSinceStartOfRace() { private String getTimeSinceStartOfRace() {
String timerString = "0:00"; String timerString = "0:00";
if (StreamParser.getTimeSinceStart() > 0) { if (ClientPacketParser.getTimeSinceStart() > 0) {
String timerMinute = Long.toString(StreamParser.getTimeSinceStart() / 60); String timerMinute = Long.toString(ClientPacketParser.getTimeSinceStart() / 60);
String timerSecond = Long.toString(StreamParser.getTimeSinceStart() % 60); String timerSecond = Long.toString(ClientPacketParser.getTimeSinceStart() % 60);
if (timerSecond.length() == 1) { if (timerSecond.length() == 1) {
timerSecond = "0" + timerSecond; timerSecond = "0" + timerSecond;
} }
timerString = "-" + timerMinute + ":" + timerSecond; timerString = "-" + timerMinute + ":" + timerSecond;
} else { } else {
String timerMinute = Long.toString(-1 * StreamParser.getTimeSinceStart() / 60); String timerMinute = Long.toString(-1 * ClientPacketParser.getTimeSinceStart() / 60);
String timerSecond = Long.toString(-1 * StreamParser.getTimeSinceStart() % 60); String timerSecond = Long.toString(-1 * ClientPacketParser.getTimeSinceStart() % 60);
if (timerSecond.length() == 1) { if (timerSecond.length() == 1) {
timerSecond = "0" + timerSecond; timerSecond = "0" + timerSecond;
} }
@@ -5,8 +5,8 @@ import javafx.scene.Group;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle; import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import seng302.client.ClientPacketParser;
import seng302.models.Yacht; import seng302.models.Yacht;
import seng302.models.stream.StreamParser;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -88,7 +88,7 @@ public class BoatAnnotations extends Group{
if (boat.getTimeTillNext() != null) { if (boat.getTimeTillNext() != null) {
DateFormat format = new SimpleDateFormat("mm:ss"); DateFormat format = new SimpleDateFormat("mm:ss");
String timeToNextMark = format String timeToNextMark = format
.format(boat.getTimeTillNext() - StreamParser.getCurrentTimeLong()); .format(boat.getTimeTillNext() - ClientPacketParser.getCurrentTimeLong());
estTimeToNextMarkObject.setText("Next mark: " + timeToNextMark); estTimeToNextMarkObject.setText("Next mark: " + timeToNextMark);
} else { } else {
estTimeToNextMarkObject.setText("Next mark: -"); estTimeToNextMarkObject.setText("Next mark: -");
@@ -97,7 +97,7 @@ public class BoatAnnotations extends Group{
if (boat.getMarkRoundTime() != null) { if (boat.getMarkRoundTime() != null) {
DateFormat format = new SimpleDateFormat("mm:ss"); DateFormat format = new SimpleDateFormat("mm:ss");
String elapsedTime = format String elapsedTime = format
.format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundTime()); .format(ClientPacketParser.getCurrentTimeLong() - boat.getMarkRoundTime());
legTimeObject.setText("Last mark: " + elapsedTime); legTimeObject.setText("Last mark: " + elapsedTime);
}else { }else {
legTimeObject.setText("Last mark: - "); legTimeObject.setText("Last mark: - ");
@@ -9,13 +9,13 @@ import javafx.scene.paint.Color;
import javafx.scene.shape.Line; import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon; import javafx.scene.shape.Polygon;
import javafx.scene.transform.Rotate; import javafx.scene.transform.Rotate;
import seng302.client.ClientPacketParser;
import seng302.models.Yacht; import seng302.models.Yacht;
import seng302.utilities.GeoUtility; import seng302.utilities.GeoUtility;
import seng302.controllers.CanvasController; import seng302.controllers.CanvasController;
import seng302.models.mark.GateMark; import seng302.models.mark.GateMark;
import seng302.models.mark.Mark; import seng302.models.mark.Mark;
import seng302.models.mark.SingleMark; import seng302.models.mark.SingleMark;
import seng302.models.stream.StreamParser;
/** /**
* BoatGroup is a javafx group that by default contains a graphical objects for representing a 2 * BoatGroup is a javafx group that by default contains a graphical objects for representing a 2
@@ -238,7 +238,7 @@ public class BoatGroup extends Group {
*/ */
public Boolean isUpwindLeg(CanvasController canvasController, Mark nextMark) { public Boolean isUpwindLeg(CanvasController canvasController, Mark nextMark) {
Double windAngle = StreamParser.getWindDirection(); Double windAngle = ClientPacketParser.getWindDirection();
GateMark thisGateMark = (GateMark) nextMark; GateMark thisGateMark = (GateMark) nextMark;
SingleMark nextMark1 = thisGateMark.getSingleMark1(); SingleMark nextMark1 = thisGateMark.getSingleMark1();
SingleMark nextMark2 = thisGateMark.getSingleMark2(); SingleMark nextMark2 = thisGateMark.getSingleMark2();
+47 -11
View File
@@ -1,8 +1,13 @@
package seng302.gameServer; package seng302.gameServer;
import java.util.List;
import java.util.Map;
import java.util.Set;
import seng302.models.Player; import seng302.models.Player;
import java.util.ArrayList; import java.util.ArrayList;
import seng302.models.Yacht;
import seng302.server.messages.BoatActionType;
/** /**
* A Static class to hold information about the current state of the game (model) * A Static class to hold information about the current state of the game (model)
@@ -10,8 +15,13 @@ import java.util.ArrayList;
*/ */
public class GameState { public class GameState {
private static Long previousUpdateTime;
private static Double windDirection = 0d;
private static Double windSpeed = 0d;
private static String hostIpAddress; private static String hostIpAddress;
private static ArrayList<Player> players; private static List<Player> players;
private static Map<Integer, Yacht> yachts;
private static Boolean isRaceStarted; private static Boolean isRaceStarted;
private static GameStages currentStage; private static GameStages currentStage;
@@ -20,13 +30,15 @@ public class GameState {
players = new ArrayList<>(); players = new ArrayList<>();
currentStage = GameStages.LOBBYING; currentStage = GameStages.LOBBYING;
isRaceStarted = false; isRaceStarted = false;
//set this when game stage changes to prerace
previousUpdateTime = System.currentTimeMillis();
} }
public static String getHostIpAddress() { public static String getHostIpAddress() {
return hostIpAddress; return hostIpAddress;
} }
public static ArrayList<Player> getPlayers() { public static List<Player> getPlayers() {
return players; return players;
} }
@@ -38,6 +50,10 @@ public class GameState {
players.remove(player); players.remove(player);
} }
public static void addYacht(Integer sourceId, Yacht yatch) {
yachts.put(sourceId, yatch);
}
public static Boolean getIsRaceStarted() { public static Boolean getIsRaceStarted() {
return isRaceStarted; return isRaceStarted;
} }
@@ -50,16 +66,36 @@ public class GameState {
GameState.currentStage = currentStage; GameState.currentStage = currentStage;
} }
/** public static Double getWindDirection() {
* This iterates through all players and updates each players info to its new state based on its current data return windDirection;
*/ }
private void update(){
for(Player player : players) { public static Double getWindSpeed() {
// TODO: 10/07/17 wmu16 - Update all player info return windSpeed;
}
public static void updateBoat(Integer sourceId, BoatActionType actionType) {
switch (actionType) {
case VMG:
break;
case SAILS_IN:
break;
case SAILS_OUT:
break;
case TACK_GYBE:
break;
case UPWIND:
break;
case DOWNWIND:
break;
} }
} }
public static void update() {
Long timeInterval = System.currentTimeMillis() - previousUpdateTime;
previousUpdateTime = System.currentTimeMillis();
for (Yacht yacht : yachts.values()) {
yacht.update(timeInterval);
}
}
} }
@@ -1,8 +1,8 @@
package seng302.gameServer; package seng302.gameServer;
import seng302.client.ClientPacketParser;
import seng302.models.Player; import seng302.models.Player;
import seng302.models.stream.PacketBufferDelegate; import seng302.models.stream.PacketBufferDelegate;
import seng302.models.stream.StreamParser;
import seng302.models.stream.packets.StreamPacket; import seng302.models.stream.packets.StreamPacket;
import java.io.IOException; import java.io.IOException;
@@ -57,11 +57,12 @@ public class MainServerThread extends Thread implements PacketBufferDelegate, Cl
e.printStackTrace(); e.printStackTrace();
} }
if (GameState.getCurrentStage() == GameStages.PRE_RACE) {
GameState.update();
}
//RACING //RACING
if (GameState.getCurrentStage() == GameStages.RACING) { if (GameState.getCurrentStage() == GameStages.RACING) {
updateClients(); GameState.update();
} }
@@ -71,12 +72,11 @@ public class MainServerThread extends Thread implements PacketBufferDelegate, Cl
} }
updateClients(); updateClients();
while (!packetBuffer.isEmpty()){ while (!packetBuffer.isEmpty()){
System.out.println("WHATUPPP"); System.out.println("WHATUPPP");
try { try {
StreamPacket packet = packetBuffer.take(); StreamPacket packet = packetBuffer.take();
StreamParser.parsePacket(packet); ClientPacketParser.parsePacket(packet);
} catch (InterruptedException e) { } catch (InterruptedException e) {
continue; continue;
} }
@@ -0,0 +1,37 @@
package seng302.gameServer;
import java.util.Arrays;
import seng302.models.stream.packets.StreamPacket;
import seng302.server.messages.BoatActionType;
public class ServerPacketParser {
public static BoatActionType extractBoatAction(StreamPacket packet) {
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
long actionTypeValue = bytesToLong(Arrays.copyOfRange(payload, 0, 1));
return BoatActionType.getType((int) actionTypeValue);
}
/**
* takes an array of up to 7 bytes and returns a positive
* long constructed from the input bytes
*
* @return a positive long if there is less than 7 bytes -1 otherwise
*/
private static long bytesToLong(byte[] bytes) {
long partialLong = 0;
int index = 0;
for (byte b : bytes) {
if (index > 6) {
return -1;
}
partialLong = partialLong | (b & 0xFFL) << (index * 8);
index++;
}
return partialLong;
}
}
@@ -1,18 +1,22 @@
package seng302.gameServer; package seng302.gameServer;
import seng302.gameServer.GameState;
import java.util.Random;
import seng302.client.ClientPacketParser;
import seng302.models.Player; import seng302.models.Player;
import seng302.models.stream.PacketBufferDelegate; import seng302.models.Yacht;
import seng302.models.stream.StreamParser; import seng302.models.stream.packets.PacketType;
import seng302.models.stream.packets.StreamPacket; import seng302.models.stream.packets.StreamPacket;
import seng302.server.messages.ChatterMessage; import seng302.server.messages.ChatterMessage;
import seng302.server.messages.Heartbeat; import seng302.server.messages.Heartbeat;
import seng302.server.messages.BoatActionType;
import seng302.server.messages.Message; import seng302.server.messages.Message;
import java.io.*; import java.io.*;
import java.net.Socket; import java.net.Socket;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import java.util.zip.Checksum; import java.util.zip.Checksum;
import seng302.utilities.GeoPoint;
/** /**
* A class describing a single connection to a Client for the purposes of sending and receiving on its own thread. * A class describing a single connection to a Client for the purposes of sending and receiving on its own thread.
@@ -20,7 +24,6 @@ import java.util.zip.Checksum;
* Created by wmu16 on 13/07/17. * Created by wmu16 on 13/07/17.
*/ */
public class ServerToClientThread extends Thread { public class ServerToClientThread extends Thread {
private static final Integer MAX_ID_ATTEMPTS = 10; private static final Integer MAX_ID_ATTEMPTS = 10;
private InputStream is; private InputStream is;
@@ -33,6 +36,8 @@ public class ServerToClientThread extends Thread {
private Boolean connected = true; private Boolean connected = true;
private Boolean updateClient = true; private Boolean updateClient = true;
private Integer sourceId;
public ServerToClientThread(Socket socket) { public ServerToClientThread(Socket socket) {
this.socket = socket; this.socket = socket;
try { try {
@@ -43,6 +48,9 @@ public class ServerToClientThread extends Thread {
} }
// threeWayHandshake(); // threeWayHandshake();
GameState.addPlayer(new Player(socket)); GameState.addPlayer(new Player(socket));
Random rand = new Random();
sourceId = rand.nextInt(100000);
GameState.addYacht(sourceId, new Yacht("Kappa", "Kap", new GeoPoint(0.0, 0.0), 0.0));
} }
public void run() { public void run() {
@@ -86,8 +94,14 @@ public class ServerToClientThread extends Thread {
long packetCrc = Message.bytesToLong(getBytes(4)); long packetCrc = Message.bytesToLong(getBytes(4));
if (computedCrc == packetCrc) { if (computedCrc == packetCrc) {
//System.out.println("RECEIVED A PACKET"); //System.out.println("RECEIVED A PACKET");
StreamParser.parsePacket(new StreamPacket(type, payloadLength, timeStamp, payload)); switch (PacketType.assignPacketType(type)) {
// TODO: 17/07/17 wmu16 - Fix this or maybe we dont need to go through the main server at all!?!? case BOAT_ACTION:
BoatActionType actionType = ServerPacketParser
.extractBoatAction(
new StreamPacket(type, payloadLength, timeStamp, payload));
GameState.updateBoat(sourceId, actionType);
break;
}
} else { } else {
System.err.println("Packet has been dropped"); System.err.println("Packet has been dropped");
} }
+5 -2
View File
@@ -1,6 +1,9 @@
package seng302.models; package seng302.models;
import java.io.*; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -140,7 +143,7 @@ public final class PolarTable {
} }
private static Double getClosestMatch(Double thisWindSpeed) { public static Double getClosestMatch(Double thisWindSpeed) {
ArrayList<Double> windValues = new ArrayList<>(polarTable.keySet()); ArrayList<Double> windValues = new ArrayList<>(polarTable.keySet());
+49 -32
View File
@@ -1,11 +1,14 @@
package seng302.models; package seng302.models;
import javafx.scene.paint.Color; import static seng302.utilities.GeoUtility.getGeoCoordinate;
import seng302.models.mark.Mark;
import seng302.controllers.RaceViewController;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Map;
import javafx.scene.paint.Color;
import seng302.controllers.RaceViewController;
import seng302.models.mark.Mark;
import seng302.utilities.GeoPoint;
/** /**
* Yacht class for the racing boat. * Yacht class for the racing boat.
@@ -35,10 +38,9 @@ public class Yacht {
private Integer penaltiesServed; private Integer penaltiesServed;
private Long estimateTimeAtFinish; private Long estimateTimeAtFinish;
private String position; private String position;
private Double lat; private GeoPoint location;
private Double lon; private Double heading;
private Float heading; private Double velocity;
private double velocity;
private Long timeTillNext; private Long timeTillNext;
private Long markRoundTime; private Long markRoundTime;
@@ -52,8 +54,12 @@ public class Yacht {
* *
* @param boatName Create a yacht object with name. * @param boatName Create a yacht object with name.
*/ */
public Yacht(String boatName) { public Yacht(String boatName, String shortName, GeoPoint location, Double heading) {
this.boatName = boatName; this.boatName = boatName;
this.shortName = shortName;
this.location = location;
this.heading = heading;
this.velocity = 0.0;
} }
/** /**
@@ -81,6 +87,41 @@ public class Yacht {
this.position = "-"; this.position = "-";
} }
/**
* @param timeInterval since last update in milliseconds
*/
public void update(Long timeInterval) {
Double secondsElapsed = timeInterval / 1000000.0;
Double metersCovered = velocity * secondsElapsed;
location = getGeoCoordinate(location, heading, metersCovered);
}
/**
* Adjusts the yachts velocity based on the wind direction and speed from the polar table.
*
* @param windDir current wind Direction TODO: 20/07/17 ajm412: (TWA or AWA, not 100% sure?)
* @param windSpd current wind Speed
*/
public void updateYachtVelocity(Double windDir, Double windSpd) {
Double closestSpd = PolarTable.getClosestMatch(windSpd);
Map<Double, Double> polarsFromClosestSpd = PolarTable.getPolarTable().get(closestSpd);
Double closest = 0d;
Double closest_key = 0d;
for (Double key : polarsFromClosestSpd.keySet()) {
Double difference = Math.abs(key - windDir);
if (difference <= closest) {
closest = difference;
closest_key = key;
}
}
// System.out.println("Closest angle " + closest_key);
// System.out.println("WindDir " + windDir);
velocity = polarsFromClosestSpd.get(closest_key);
}
public String getBoatType() { public String getBoatType() {
return boatType; return boatType;
} }
@@ -206,30 +247,6 @@ public class Yacht {
return nextMark; return nextMark;
} }
public Double getLat() {
return lat;
}
public void setLat(Double lat) {
this.lat = lat;
}
public Double getLon() {
return lon;
}
public void setLon(Double lon) {
this.lon = lon;
}
public Float getHeading() {
return heading;
}
public void setHeading(Float heading) {
this.heading = heading;
}
@Override @Override
public String toString() { public String toString() {
return boatName; return boatName;
@@ -18,7 +18,7 @@ public class BoatActionMessage extends Message{
allocateBuffer(); allocateBuffer();
writeHeaderToBuffer(); writeHeaderToBuffer();
// Write message fields // Write message fields
putInt((int) BoatActionType.getBoatPacketType(actionType), 1); putInt(actionType.getValue(), 1);
writeCRC(); writeCRC();
rewind(); rewind();
@@ -1,5 +1,8 @@
package seng302.server.messages; package seng302.server.messages;
import java.util.HashMap;
import java.util.Map;
/** /**
* Created by kre39 on 12/07/17. * Created by kre39 on 12/07/17.
*/ */
@@ -12,31 +15,24 @@ public enum BoatActionType {
UPWIND(5), UPWIND(5),
DOWNWIND(6); DOWNWIND(6);
private int type; private final int type;
private static final Map<Integer, BoatActionType> intToTypeMap = new HashMap<>();
static {
for (BoatActionType type : BoatActionType.values()) {
intToTypeMap.put(type.getValue(), type);
}
}
BoatActionType(int type){ BoatActionType(int type){
this.type = type; this.type = type;
} }
public int getType(){ public static BoatActionType getType(int value) {
return this.type; return intToTypeMap.get(value);
} }
public static Short getBoatPacketType(BoatActionType type){ public int getValue() {
switch (type){ return this.type;
case VMG:
return 1;
case SAILS_IN:
return 2;
case SAILS_OUT:
return 3;
case TACK_GYBE:
return 4;
case UPWIND:
return 5;
case DOWNWIND:
return 6;
}
return 0;
} }
} }
@@ -0,0 +1,36 @@
package seng302.models;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import seng302.utilities.GeoPoint;
public class YachtTest {
Double windDir;
Double windSpd;
List<Yacht> yachts = new ArrayList<Yacht>();
@Before
public void setUp() {
PolarTable.parsePolarFile(getClass().getResourceAsStream("/config/acc_polars.csv"));
windDir = 90d;
windSpd = 10d;
yachts.add(new Yacht("Yacht 1", "Y1", new GeoPoint(-30.0, 20.0), 160.0));
yachts.add(new Yacht("Yacht 2", "Y2", new GeoPoint(-40.0, -20.0), 100.0));
yachts.add(new Yacht("Yacht 3", "Y3", new GeoPoint(-35.0, -15.5), 20.0));
}
@Test
public void testVelocityUpdate() {
for (Yacht yacht : yachts) {
yacht.updateYachtVelocity(windDir, windSpd);
System.out.println(yacht.getVelocity());
// TODO: 20/07/17 ajm412: add assertions.
}
}
}