Merged with develop. Fixed many bugs in Visualiser.

#bugs
This commit is contained in:
Calum
2017-07-26 02:49:31 +12:00
parent acd54dec7a
commit 08e369f1ae
41 changed files with 1693 additions and 1636 deletions
-1
View File
@@ -7,7 +7,6 @@ import javafx.scene.Scene;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.stage.Stage; import javafx.stage.Stage;
import seng302.model.PolarTable; import seng302.model.PolarTable;
import seng302.model.stream.parsers.StreamParser;
import seng302.model.stream.StreamReceiver; import seng302.model.stream.StreamReceiver;
public class App extends Application { public class App extends Application {
+64 -67
View File
@@ -1,78 +1,75 @@
package seng302.client; package seng302.client;
import com.sun.org.apache.xpath.internal.operations.Bool;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import seng302.models.Yacht; import seng302.model.Yacht;
/** /**
* Used by the client to store static variables to be used in game. * Used by the client to store static variables to be used in game.
*/ */
public class ClientState { public class ClientState {
private static String hostIp = ""; // private static String hostIp = "";
private static Boolean isHost = false; // private static Boolean isHost = false;
private static Boolean raceStarted = false; // private static Boolean raceStarted = false;
private static Boolean connectedToHost = false; // private static Boolean connectedToHost = false;
private static Map<Integer, Yacht> boats = new ConcurrentHashMap<>(); // private static Map<Integer, Yacht> boats = new ConcurrentHashMap<>();
private static Boolean dirtyState = true; // private static Boolean dirtyState = true;
private static String clientSourceId = ""; // private static String clientSourceId = "";
//
public static String getHostIp() { // public static String getHostIp() {
return hostIp; // return hostIp;
} // }
//
public static void setHostIp(String hostIp) { // public static void setHostIp(String hostIp) {
ClientState.hostIp = hostIp; // ClientState.hostIp = hostIp;
} // }
//
public static Boolean isHost() { // public static Boolean isHost() {
return isHost; // return isHost;
} // }
//
public static void setHost(Boolean isHost) { // public static void setHost(Boolean isHost) {
ClientState.isHost = isHost; // ClientState.isHost = isHost;
} // }
//
public static Boolean isRaceStarted() { // public static Boolean isRaceStarted() {
return raceStarted; // return raceStarted;
} // }
//
public static void setRaceStarted(Boolean raceStarted) { // public static void setRaceStarted(Boolean raceStarted) {
ClientState.raceStarted = raceStarted; // ClientState.raceStarted = raceStarted;
} // }
//
public static Boolean isConnectedToHost() { // public static Boolean isConnectedToHost() {
return connectedToHost; // return connectedToHost;
} // }
//
public static void setConnectedToHost(Boolean connectedToHost) { // public static void setConnectedToHost(Boolean connectedToHost) {
ClientState.connectedToHost = connectedToHost; // ClientState.connectedToHost = connectedToHost;
} // }
//
public static Map<Integer, Yacht> getBoats() { // public static Map<Integer, Yacht> getBoats() {
return boats; // return boats;
} // }
//
public static Boolean isDirtyState() { // public static Boolean isDirtyState() {
return dirtyState; // return dirtyState;
} // }
//
public static void setDirtyState(Boolean dirtyState) { // public static void setDirtyState(Boolean dirtyState) {
ClientState.dirtyState = dirtyState; // ClientState.dirtyState = dirtyState;
} // }
//
public static String getClientSourceId() { // public static String getClientSourceId() {
return clientSourceId; // return clientSourceId;
} // }
//
public static void setClientSourceId(String clientSourceId) { // public static void setClientSourceId(String clientSourceId) {
ClientState.clientSourceId = clientSourceId; // ClientState.clientSourceId = clientSourceId;
} // }
//
public static void setBoats(Map<Integer, Yacht> boats) { // public static void setBoats(Map<Integer, Yacht> boats) {
ClientState.boats = boats; // ClientState.boats = boats;
} // }
} }
@@ -1,43 +1,43 @@
package seng302.client; //package seng302.client;
//
import java.util.Observable; //import java.util.Observable;
//
/** ///**
* Used by LobbyController to run a separate thread-loop // * Used by LobbyController to run a separate thread-loop
* updates the controller when change is detected. // * updates the controller when change is detected.
*/ // */
public class ClientStateQueryingRunnable extends Observable implements Runnable { //public class ClientStateQueryingRunnable extends Observable implements Runnable {
//
private Boolean terminate = false; // private Boolean terminate = false;
//
public ClientStateQueryingRunnable() {} // public ClientStateQueryingRunnable() {}
//
@Override // @Override
public void run() { // public void run() {
while(!terminate) { // while(!terminate) {
// Sleeping the thread so it will respond to the if statement below // // Sleeping the thread so it will respond to the if statement below
// if you know a better fix, pls tell me :) -ryan // // if you know a better fix, pls tell me :) -ryan
try { // try {
Thread.sleep(0); // Thread.sleep(0);
} catch (InterruptedException e) { // } catch (InterruptedException e) {
e.printStackTrace(); // e.printStackTrace();
} // }
//
if (ClientState.isRaceStarted() && ClientState.isConnectedToHost()) { // if (ClientState.isRaceStarted() && ClientState.isConnectedToHost()) {
setChanged(); // setChanged();
notifyObservers("game started"); // notifyObservers("game started");
terminate(); // terminate();
} // }
//
if (ClientState.isDirtyState()) { // if (ClientState.isDirtyState()) {
setChanged(); // setChanged();
notifyObservers("update players"); // notifyObservers("update players");
ClientState.setDirtyState(false); // ClientState.setDirtyState(false);
} // }
} // }
} // }
//
public void terminate() { // public void terminate() {
terminate = true; // terminate = true;
} // }
} //}
@@ -1,99 +1,99 @@
package seng302.controllers; //package seng302.controllers;
//
import java.io.IOException; //import java.io.IOException;
import java.net.URL; //import java.net.URL;
import java.util.ResourceBundle; //import java.util.ResourceBundle;
import javafx.fxml.FXML; //import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; //import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable; //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.client.ClientPacketParser; //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;
//
public class Controller implements Initializable { //public class Controller implements Initializable {
//
@FXML // @FXML
private AnchorPane contentPane; // private AnchorPane contentPane;
private ClientToServerThread clientToServerThread; // private ClientToServerThread clientToServerThread;
private long lastSendingTime; // private long lastSendingTime;
private int KEY_STROKE_SENDING_FREQUENCY = 50; // private int KEY_STROKE_SENDING_FREQUENCY = 50;
//
private Object setContentPane(String jfxUrl) { // private Object setContentPane(String jfxUrl) {
try { // try {
contentPane.getChildren().removeAll(); // contentPane.getChildren().removeAll();
contentPane.getChildren().clear(); // contentPane.getChildren().clear();
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString()); // contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
FXMLLoader fxmlLoader = new FXMLLoader((getClass().getResource(jfxUrl))); // FXMLLoader fxmlLoader = new FXMLLoader((getClass().getResource(jfxUrl)));
Parent view = fxmlLoader.load(); // Parent view = fxmlLoader.load();
contentPane.getChildren().addAll(view); // contentPane.getChildren().addAll(view);
return fxmlLoader.getController(); // return fxmlLoader.getController();
} catch (javafx.fxml.LoadException e) { // } catch (javafx.fxml.LoadException e) {
System.err.println(e.getCause()); // System.err.println(e.getCause());
} catch (IOException e) { // } catch (IOException e) {
System.err.println(e); // System.err.println(e);
} // }
return null; // return null;
} // }
//
@Override // @Override
public void initialize(URL location, ResourceBundle resources) { // public void initialize(URL location, ResourceBundle resources) {
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);
ClientPacketParser.boatLocations.clear(); // ClientPacketParser.boatLocations.clear();
//
lastSendingTime = System.currentTimeMillis(); // lastSendingTime = System.currentTimeMillis();
} // }
//
/** Handle the key-pressed event from the text field. */ // /** Handle the key-pressed event from the text field. */
public void keyPressed(KeyEvent e) { // public void keyPressed(KeyEvent e) {
BoatActionMessage boatActionMessage; // BoatActionMessage boatActionMessage;
long currentTime = System.currentTimeMillis(); // long currentTime = System.currentTimeMillis();
if (currentTime - lastSendingTime > KEY_STROKE_SENDING_FREQUENCY) { // if (currentTime - lastSendingTime > KEY_STROKE_SENDING_FREQUENCY) {
lastSendingTime = currentTime; // lastSendingTime = currentTime;
switch (e.getCode()) { // switch (e.getCode()) {
case SPACE: // align with vmg // case SPACE: // align with vmg
boatActionMessage = new BoatActionMessage(BoatActionType.VMG); // boatActionMessage = new BoatActionMessage(BoatActionType.VMG);
clientToServerThread.sendBoatActionMessage(boatActionMessage); // clientToServerThread.sendBoatActionMessage(boatActionMessage);
break; // break;
case PAGE_UP: // upwind // case PAGE_UP: // upwind
boatActionMessage = new BoatActionMessage(BoatActionType.UPWIND); // boatActionMessage = new BoatActionMessage(BoatActionType.UPWIND);
clientToServerThread.sendBoatActionMessage(boatActionMessage); // clientToServerThread.sendBoatActionMessage(boatActionMessage);
break; // break;
case PAGE_DOWN: // downwind // case PAGE_DOWN: // downwind
boatActionMessage = new BoatActionMessage(BoatActionType.DOWNWIND); // boatActionMessage = new BoatActionMessage(BoatActionType.DOWNWIND);
clientToServerThread.sendBoatActionMessage(boatActionMessage); // clientToServerThread.sendBoatActionMessage(boatActionMessage);
break; // break;
case ENTER: // tack/gybe // case ENTER: // tack/gybe
boatActionMessage = new BoatActionMessage(BoatActionType.TACK_GYBE); // boatActionMessage = new BoatActionMessage(BoatActionType.TACK_GYBE);
clientToServerThread.sendBoatActionMessage(boatActionMessage); // clientToServerThread.sendBoatActionMessage(boatActionMessage);
break; // break;
//TODO Allow a zoom in and zoom out methods // //TODO Allow a zoom in and zoom out methods
case Z: // zoom in // case Z: // zoom in
System.out.println("Zoom in"); // System.out.println("Zoom in");
break; // break;
case X: // zoom out // case X: // zoom out
System.out.println("Zoom out"); // System.out.println("Zoom out");
break; // break;
} // }
} // }
} // }
//
public void keyReleased(KeyEvent e) { // public void keyReleased(KeyEvent e) {
switch (e.getCode()) { // switch (e.getCode()) {
//TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet) // //TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
case SHIFT: // sails in/sails out // case SHIFT: // sails in/sails out
BoatActionMessage boatActionMessage = new BoatActionMessage(BoatActionType.SAILS_IN); // BoatActionMessage boatActionMessage = new BoatActionMessage(BoatActionType.SAILS_IN);
clientToServerThread.sendBoatActionMessage(boatActionMessage); // clientToServerThread.sendBoatActionMessage(boatActionMessage);
break; // break;
} // }
} // }
//
public void setClientToServerThread(ClientToServerThread ctt) { // public void setClientToServerThread(ClientToServerThread ctt) {
clientToServerThread = ctt; // clientToServerThread = ctt;
} // }
} //}
File diff suppressed because it is too large Load Diff
@@ -2,10 +2,11 @@ package seng302.gameServer;
import java.util.*; import java.util.*;
import seng302.models.Player; import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import seng302.model.Player; import seng302.model.Player;
import seng302.model.Yacht;
import seng302.models.Yacht; import seng302.model.stream.packets.StreamPacket;
import seng302.server.messages.BoatActionType; import seng302.server.messages.BoatActionType;
/** /**
@@ -23,14 +24,26 @@ public class GameState {
private static Map<Integer, Yacht> yachts; private static Map<Integer, Yacht> yachts;
private static Boolean isRaceStarted; private static Boolean isRaceStarted;
private static GameStages currentStage; private static GameStages currentStage;
// TODO: 26/07/17 cir27 - Super hackish fix until something more permanent can be made.
private static ObservableList<String> observablePlayers = FXCollections.observableArrayList();
private static Map<Player, String> playerStringMap = new HashMap<>();
/*
Ideally I would like to make this class an object instantiated by the server and given to
it's created threads if necessary. Outside of that I think the dependencies on it
(atm only Yacht & GameClient) can be removed from most other classes. The observable list of
players could be pulled directly from the server by the GameClient since it instantiates it
and it is reasonable for it to pull data. The current setup of publicly available statics is
pretty meh IMO because anything can change it making it unreliable and like people did with
the old ServerParser class everything that needs shared just gets thrown in the static
collections and things become a real mess.
*/
public GameState(String hostIpAddress) { public GameState(String hostIpAddress) {
windDirection = 170d; windDirection = 170d;
windSpeed = 10000d; windSpeed = 10000d;
yachts = new HashMap<>(); yachts = new HashMap<>();
players = new ArrayList<>(); players = new ArrayList<>();
GameState.hostIpAddress = hostIpAddress; GameState.hostIpAddress = hostIpAddress;
players = new ArrayList<>(); players = new ArrayList<>();
currentStage = GameStages.LOBBYING; currentStage = GameStages.LOBBYING;
@@ -48,13 +61,22 @@ public class GameState {
public static List<Player> getPlayers() { public static List<Player> getPlayers() {
return players; return players;
} }
public static ObservableList<String> getObservablePlayers () {
return observablePlayers;
}
public static void addPlayer(Player player) { public static void addPlayer(Player player) {
players.add(player); players.add(player);
String playerText = player.getYacht().getSourceId() + " " + player.getYacht().getBoatName() + " " + player.getYacht().getCountry();
observablePlayers.add(playerText);
playerStringMap.put(player, playerText);
} }
public static void removePlayer(Player player) { public static void removePlayer(Player player) {
players.remove(player); players.remove(player);
observablePlayers.remove(playerStringMap.get(player));
playerStringMap.remove(player);
} }
public static void addYacht(Integer sourceId, Yacht yacht) { public static void addYacht(Integer sourceId, Yacht yacht) {
@@ -2,16 +2,14 @@ package seng302.gameServer;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Observable; import java.util.Observable;
import seng302.client.ClientPacketParser;
import seng302.models.Player;
import seng302.models.stream.PacketBufferDelegate;
import seng302.models.stream.packets.StreamPacket;
import java.io.IOException; import java.io.IOException;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.PriorityBlockingQueue;
import seng302.model.Player;
import seng302.model.stream.PacketBufferDelegate;
import seng302.model.stream.packets.StreamPacket;
/** /**
* A class describing the overall server, which creates and collects server threads for each client * A class describing the overall server, which creates and collects server threads for each client
@@ -84,7 +82,7 @@ public class MainServerThread extends Observable implements Runnable, PacketBuff
while (!packetBuffer.isEmpty()){ while (!packetBuffer.isEmpty()){
try { try {
StreamPacket packet = packetBuffer.take(); StreamPacket packet = packetBuffer.take();
ClientPacketParser.parsePacket(packet); // ClientPacketParser.parsePacket(packet);
} catch (InterruptedException e) { } catch (InterruptedException e) {
continue; continue;
} }
@@ -159,4 +157,16 @@ public class MainServerThread extends Observable implements Runnable, PacketBuff
serverToClientThread.sendRaceStatusMessage(); serverToClientThread.sendRaceStatusMessage();
} }
} }
public void shutDown() {
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
try {
serverToClientThread.getSocket().close();
} catch (IOException ioe) {
serverLog("Failed to close socket " + serverToClientThread.getSocket().toString(), 0);
}
}
serverToClientThreads = null;
thread = null;
}
} }
@@ -1,7 +1,7 @@
package seng302.gameServer; package seng302.gameServer;
import java.util.Arrays; import java.util.Arrays;
import seng302.models.stream.packets.StreamPacket; import seng302.model.stream.packets.StreamPacket;
import seng302.server.messages.BoatActionType; import seng302.server.messages.BoatActionType;
@@ -12,35 +12,26 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; import java.util.Observer;
import java.util.Random;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import java.util.zip.Checksum; import java.util.zip.Checksum;
import org.apache.commons.io.IOUtils; import seng302.model.Player;
import seng302.models.Player; import seng302.model.Yacht;
import seng302.models.Yacht; import seng302.model.stream.packets.PacketType;
import seng302.models.stream.packets.PacketType; import seng302.model.stream.packets.StreamPacket;
import seng302.models.stream.packets.StreamPacket; import seng302.model.stream.xml.generator.Race;
import seng302.models.xml.Race; import seng302.model.stream.xml.generator.Regatta;
import seng302.models.xml.Regatta; import seng302.model.stream.xml.generator.XMLGenerator;
import seng302.models.xml.XMLGenerator;
import seng302.server.messages.BoatActionType; import seng302.server.messages.BoatActionType;
import seng302.server.messages.BoatLocationMessage; import seng302.server.messages.BoatLocationMessage;
import seng302.server.messages.BoatStatus; import seng302.server.messages.BoatStatus;
import seng302.server.messages.BoatSubMessage; import seng302.server.messages.BoatSubMessage;
import seng302.server.messages.Message; import seng302.server.messages.Message;
import java.io.*;
import java.net.Socket;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
import seng302.server.messages.RaceStatus; import seng302.server.messages.RaceStatus;
import seng302.server.messages.RaceStatusMessage; import seng302.server.messages.RaceStatusMessage;
import seng302.server.messages.RaceType; import seng302.server.messages.RaceType;
import seng302.server.messages.XMLMessage; import seng302.server.messages.XMLMessage;
import seng302.server.messages.XMLMessageSubType; import seng302.server.messages.XMLMessageSubType;
import seng302.server.messages.XMLMessage;
import seng302.server.messages.XMLMessageSubType;
import seng302.utilities.GeoPoint;
/** /**
* 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
@@ -153,7 +144,7 @@ public class ServerToClientThread implements Runnable, Observer {
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");
switch (PacketType.assignPacketType(type)) { switch (PacketType.assignPacketType(type, payload)) {
case BOAT_ACTION: case BOAT_ACTION:
BoatActionType actionType = ServerPacketParser BoatActionType actionType = ServerPacketParser
.extractBoatAction( .extractBoatAction(
@@ -306,7 +297,7 @@ public class ServerToClientThread implements Runnable, Observer {
yacht.getLocation().getLat(), yacht.getLocation().getLat(),
yacht.getLocation().getLng(), yacht.getLocation().getLng(),
yacht.getHeading(), yacht.getHeading(),
(long) yacht.getVelocity()); yacht.getVelocity().longValue());
sendMessage(boatLocationMessage); sendMessage(boatLocationMessage);
} }
+3 -3
View File
@@ -9,7 +9,7 @@ import java.net.Socket;
public class Player { public class Player {
private Socket socket; private Socket socket;
private Boat boat; private Yacht yacht;
private Integer lastMarkPassed; private Integer lastMarkPassed;
@@ -30,8 +30,8 @@ public class Player {
this.lastMarkPassed = lastMarkPassed; this.lastMarkPassed = lastMarkPassed;
} }
public Boat getYacht() { public Yacht getYacht() {
return boat; return yacht;
} }
@Override @Override
+3 -3
View File
@@ -3,8 +3,8 @@ package seng302.model;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.TimeZone; import java.util.TimeZone;
import seng302.model.stream.parsers.RaceStartData; import seng302.model.stream.parser.RaceStartData;
import seng302.model.stream.parsers.RaceStatusData; import seng302.model.stream.parser.RaceStatusData;
/** /**
* Class for storing race data that does not relate to specific vessels or marks such as time or wind. * Class for storing race data that does not relate to specific vessels or marks such as time or wind.
@@ -19,7 +19,7 @@ public class RaceState {
private double windDirection; private double windDirection;
private long raceTime; private long raceTime;
private long expectedStartTime; private long expectedStartTime;
private boolean isRaceStarted; private boolean isRaceStarted = false;
// long timeTillStart; // long timeTillStart;
public RaceState() { public RaceState() {
@@ -5,17 +5,13 @@ import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.ReadOnlyLongProperty; import javafx.beans.property.ReadOnlyLongProperty;
import javafx.beans.property.ReadOnlyLongWrapper; import javafx.beans.property.ReadOnlyLongWrapper;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import seng302.model.mark.Mark; import seng302.model.mark.Mark;
import static seng302.utilities.GeoUtility.getGeoCoordinate; import static seng302.utilities.GeoUtility.getGeoCoordinate;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import javafx.scene.paint.Color;
import seng302.client.ClientPacketParser;
import seng302.controllers.RaceViewController;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
import seng302.models.mark.Mark;
import seng302.utilities.GeoPoint; import seng302.utilities.GeoPoint;
/** /**
@@ -24,17 +20,8 @@ import seng302.utilities.GeoPoint;
* Class created to store more variables (eg. boat statuses) compared to the XMLParser boat class, * Class created to store more variables (eg. boat statuses) compared to the XMLParser boat class,
* also done outside Boat class because some old variables are not used anymore. * also done outside Boat class because some old variables are not used anymore.
*/ */
public class Boat { public class Yacht {
//BOTH AFAIK
private final Double TURN_STEP = 5.0;
private Double lastHeading;
private Boolean sailIn;
// Used in boat group
private Color colour = Color.BLACK;
private String boatType; private String boatType;
private Integer sourceId; private Integer sourceId;
private String hullID; //matches HullNum in the XML spec. private String hullID; //matches HullNum in the XML spec.
@@ -42,28 +29,32 @@ public class Boat {
private String boatName; private String boatName;
private String country; private String country;
// Boat status
private Integer boatStatus;
private Integer legNumber = 0;
private Integer position = 0;
private Long estimateTimeAtFinish; private Long estimateTimeAtFinish;
private Long markRoundTime;
private Double lat;
private Double lon;
private Double heading;
private ReadOnlyDoubleWrapper velocity = new ReadOnlyDoubleWrapper();
private ReadOnlyLongWrapper timeTillNext = new ReadOnlyLongWrapper();
private ReadOnlyLongWrapper timeSinceLastMark = new ReadOnlyLongWrapper();
private String position;
private GeoPoint location;
private Double heading;
private Double velocity;
private Long timeTillNext; private Long timeTillNext;
private Long markRoundTime; private Long markRoundTime;
private Double heading;
private Double lat;
private Double lon;
private Integer legNumber = 0;
// Mark rounding //SERVER SIDE
private final Double TURN_STEP = 5.0;
private Double lastHeading;
private Boolean sailIn;
private String position;
private GeoPoint location;
private Integer boatStatus;
private Double velocity;
//CLIENT SIDE
private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper();
private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper();
private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper();
private ReadOnlyDoubleProperty headingProperty = new ReadOnlyDoubleWrapper();
private Mark lastMarkRounded; private Mark lastMarkRounded;
private Mark nextMark; private Mark nextMark;
private Integer positionInt = 0;
private Color colour;
/** /**
@@ -110,8 +101,6 @@ public class Boat {
public Yacht(String boatType, Integer sourceId, String hullID, String shortName, public Yacht(String boatType, Integer sourceId, String hullID, String shortName,
String boatName, String country) { String boatName, String country) {
public Boat(String boatType, Integer sourceID, String hullID, String shortName,
String boatName, String country) {
this.boatType = boatType; this.boatType = boatType;
this.sourceId = sourceId; this.sourceId = sourceId;
this.hullID = hullID; this.hullID = hullID;
@@ -134,18 +123,13 @@ public class Boat {
Double thisHeading = ((double) Math.floorMod(heading.longValue(), 360L)); Double thisHeading = ((double) Math.floorMod(heading.longValue(), 360L));
Double windSpeedKnots = 0d; Double windSpeedKnots = 0d;
Double boatSpeedInKnots = PolarTable.getBoatSpeed(windSpeedKnots, thisHeading); Double boatSpeedInKnots = PolarTable.getBoatSpeed(windSpeedKnots, thisHeading);
velocity = boatSpeedInKnots / ClientPacketParser.MS_TO_KNOTS * 3000; velocity = boatSpeedInKnots / 1.94384449 * 3000; // TODO: 25/07/17 cir27 - remove magic numbers
//System.out.println("velocity = " + velocity); //System.out.println("velocity = " + velocity);
Double metersCovered = velocity * secondsElapsed; Double metersCovered = velocity * secondsElapsed;
location = getGeoCoordinate(location, heading, metersCovered); location = getGeoCoordinate(location, heading, metersCovered);
} }
} }
public Double getHeading() {
return heading;
}
public void adjustHeading(Double amount) { public void adjustHeading(Double amount) {
lastHeading = heading; lastHeading = heading;
// TODO: 24/07/17 wmu16 - '%' in java does remainder, we need modulo. All this must be changed here, this is why we have neg values! // TODO: 24/07/17 wmu16 - '%' in java does remainder, we need modulo. All this must be changed here, this is why we have neg values!
@@ -244,15 +228,14 @@ public class Boat {
} }
public void setLegNumber(Integer legNumber) { public void setLegNumber(Integer legNumber) {
if (colour != null && position != "-" && legNumber != this.legNumber&& RaceViewController.sparkLineStatus( // if (colour != null && position != "-" && legNumber != this.legNumber) {
sourceId)) { // RaceViewController.updateYachtPositionSparkline(this, legNumber);
RaceViewController.updateYachtPositionSparkline(this, legNumber); // }
}
this.legNumber = legNumber; this.legNumber = legNumber;
} }
public void setEstimateTimeTillNextMark(Long estimateTimeAtNextMark) { public void setEstimateTimeTillNextMark(Long estimateTimeTillNextMark) {
timeTillNext.set(estimateTimeAtNextMark); timeTillNext = estimateTimeTillNextMark;
} }
public String getEstimateTimeAtFinish() { public String getEstimateTimeAtFinish() {
@@ -264,37 +247,28 @@ public class Boat {
this.estimateTimeAtFinish = estimateTimeAtFinish; this.estimateTimeAtFinish = estimateTimeAtFinish;
} }
public Integer getPosition() { public Integer getPositionInteger() {
return position; return positionInt;
} }
public void setPosition(Integer position) { public void setPositionInteger(Integer position) {
this.position = position; this.positionInt = position;
} }
public Color getColour() { public void setVelocityProperty(double velocity) {
return colour; this.velocityProperty.set(velocity);
} }
public void setColour(Color colour) {
this.colour = colour;
}
public void setVelocity(double velocity) {
this.velocity.set(velocity);
}
public void setMarkRoundingTime(Long markRoundingTime) { public void setMarkRoundingTime(Long markRoundingTime) {
this.markRoundTime = markRoundingTime; this.markRoundTime = markRoundingTime;
} }
public ReadOnlyDoubleProperty getVelocityProperty() { public ReadOnlyDoubleProperty getVelocityProperty() {
return velocity.getReadOnlyProperty(); return velocityProperty.getReadOnlyProperty();
} }
public ReadOnlyLongProperty timeTillNextProperty() { public ReadOnlyLongProperty timeTillNextProperty() {
return timeTillNext.getReadOnlyProperty(); return timeTillNextProperty.getReadOnlyProperty();
} }
public Long getMarkRoundTime() { public Long getMarkRoundTime() {
@@ -339,6 +313,8 @@ public class Boat {
public void setHeading(Double heading) { public void setHeading(Double heading) {
this.heading = heading; this.heading = heading;
}
public Boolean getSailIn() { public Boolean getSailIn() {
return sailIn; return sailIn;
} }
@@ -352,12 +328,37 @@ public class Boat {
return location; return location;
} }
public void setTimeSinceLastMark (long timeSinceLastMark) { public void updateTimeSinceLastMarkProperty(long timeSinceLastMark) {
this.timeSinceLastMark.set(timeSinceLastMark); this.timeSinceLastMarkProperty.set(timeSinceLastMark);
} }
public ReadOnlyLongProperty timeSinceLastMarkProperty () { public ReadOnlyLongProperty timeSinceLastMarkProperty () {
return timeSinceLastMark.getReadOnlyProperty(); return timeSinceLastMarkProperty.getReadOnlyProperty();
} }
public Long getTimeTillNext() {
return timeTillNext;
}
public void setTimeTillNext(Long timeTillNext) {
this.timeTillNext = timeTillNext;
}
public Color getColour() {
return colour;
}
public void setColour(Color colour) {
this.colour = colour;
}
public Double getVelocity() {
return velocity;
}
public void setVelocity(Double velocity) {
this.velocity = velocity;
}
} }
@@ -1,4 +1,4 @@
package seng302.model.stream.parsers; package seng302.model.stream.parser;
/** /**
@@ -11,7 +11,7 @@ public class MarkRoundingData {
private int roundingSide; private int roundingSide;
private long timeStamp; private long timeStamp;
public MarkRoundingData(int boatId, int markId, int roundingSide, long timeStamp) { MarkRoundingData(int boatId, int markId, int roundingSide, long timeStamp) {
this.boatId = boatId; this.boatId = boatId;
this.markId = markId; this.markId = markId;
this.roundingSide = roundingSide; this.roundingSide = roundingSide;
@@ -1,4 +1,4 @@
package seng302.model.stream.parsers; package seng302.model.stream.parser;
public class PositionUpdateData { public class PositionUpdateData {
@@ -14,7 +14,7 @@ public class PositionUpdateData {
private double heading; private double heading;
private double groundSpeed; private double groundSpeed;
public PositionUpdateData(int deviceId, DeviceType type, double lat, double lon, PositionUpdateData(int deviceId, DeviceType type, double lat, double lon,
double heading, double groundSpeed) { double heading, double groundSpeed) {
this.deviceId = deviceId; this.deviceId = deviceId;
this.type = type; this.type = type;
@@ -1,16 +1,16 @@
package seng302.model.stream.parsers; package seng302.model.stream.parser;
/** /**
* Class for storing data parsed from race start status packet * Class for storing data parsed from race start status packet
*/ */
public class RaceStartData { public class RaceStartData {
long raceId; private long raceId;
long raceStartTime; private long raceStartTime;
int notificationType; private int notificationType;
long timeStamp; private long timeStamp;
public RaceStartData (long raceId, long raceStartTime, int notificationType, long timeStamp) { RaceStartData (long raceId, long raceStartTime, int notificationType, long timeStamp) {
this.raceId = raceId; this.raceId = raceId;
this.raceStartTime = raceStartTime; this.raceStartTime = raceStartTime;
this.notificationType = notificationType; this.notificationType = notificationType;
@@ -1,4 +1,4 @@
package seng302.model.stream.parsers; package seng302.model.stream.parser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -17,9 +17,9 @@ public class RaceStatusData {
private boolean raceStarted = false; private boolean raceStarted = false;
private long currentTime; private long currentTime;
private long expectedStartTime; private long expectedStartTime;
List<long[]> boatData = new ArrayList<>(); private List<long[]> boatData = new ArrayList<>();
public RaceStatusData( RaceStatusData(
long windDir, long rawWindSpeed, int raceStatus, long currentTime, long expectedStartTime) { long windDir, long rawWindSpeed, int raceStatus, long currentTime, long expectedStartTime) {
windDirection = windDir / WIND_DIR_FACTOR; windDirection = windDir / WIND_DIR_FACTOR;
@@ -29,7 +29,7 @@ public class RaceStatusData {
this.expectedStartTime = expectedStartTime; this.expectedStartTime = expectedStartTime;
} }
public void addBoatData (long boatID, long estTimeToNextMark, long estTimeToFinish, int leg) { void addBoatData (long boatID, long estTimeToNextMark, long estTimeToFinish, int leg) {
boatData.add(new long[] {boatID, estTimeToNextMark, estTimeToFinish, leg}); boatData.add(new long[] {boatID, estTimeToNextMark, estTimeToFinish, leg});
} }
@@ -1,6 +1,6 @@
package seng302.model.stream.parsers; package seng302.model.stream.parser;
import seng302.model.stream.parsers.PositionUpdateData.DeviceType; import seng302.model.stream.parser.PositionUpdateData.DeviceType;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.util.ArrayList; import java.util.ArrayList;
@@ -14,7 +14,6 @@ import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import seng302.model.stream.packets.PacketType; import seng302.model.stream.packets.PacketType;
import seng302.model.stream.packets.StreamPacket; import seng302.model.stream.packets.StreamPacket;
import seng302.model.stream.parsers.xml.RegattaXMLData;
/** /**
* StreamParser is a utilities class for taking byte data, formatted according to the AC35 * StreamParser is a utilities class for taking byte data, formatted according to the AC35
@@ -1,11 +1,10 @@
package seng302.models.xml; package seng302.model.stream.xml.generator;
import seng302.models.Yacht;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import seng302.model.Yacht;
/** /**
* A Race object that can be parsed into XML * A Race object that can be parsed into XML
@@ -1,4 +1,4 @@
package seng302.models.xml; package seng302.model.stream.xml.generator;
/** /**
* A Race regatta that can be parsed into XML * A Race regatta that can be parsed into XML
@@ -1,13 +1,11 @@
package seng302.models.xml; package seng302.model.stream.xml.generator;
import freemarker.template.Configuration; import freemarker.template.Configuration;
import freemarker.template.Template; import freemarker.template.Template;
import freemarker.template.TemplateException; import freemarker.template.TemplateException;
import org.apache.commons.io.IOUtils;
import seng302.server.messages.XMLMessageSubType; import seng302.server.messages.XMLMessageSubType;
import java.io.*; import java.io.*;
import java.net.URISyntaxException;
/** /**
* An XML generator to generate the Race, Boat, and Regatta XML dynamically * An XML generator to generate the Race, Boat, and Regatta XML dynamically
@@ -1,4 +1,4 @@
package seng302.model.stream.parsers.xml; package seng302.model.stream.xml.parser;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -1,4 +1,4 @@
package seng302.model.stream.parsers.xml; package seng302.model.stream.xml.parser;
/** /**
* Stores data from regatta xml packet. * Stores data from regatta xml packet.
@@ -1,4 +1,4 @@
package seng302.model.stream.parsers.xml; package seng302.model.stream.xml.parser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -7,7 +7,7 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import seng302.model.Boat; import seng302.model.Yacht;
import seng302.model.Corner; import seng302.model.Corner;
import seng302.model.Limit; import seng302.model.Limit;
import seng302.model.mark.GateMark; import seng302.model.mark.GateMark;
@@ -124,8 +124,8 @@ public class XMLParser {
* @param doc XML Document Object * @param doc XML Document Object
* @return Mapping of sourceIds to Boats. * @return Mapping of sourceIds to Boats.
*/ */
public static Map<Integer, Boat> parseBoats(Document doc){ public static Map<Integer, Yacht> parseBoats(Document doc){
Map<Integer, Boat> competingBoats = new HashMap<>(); Map<Integer, Yacht> competingBoats = new HashMap<>();
Element docEle = doc.getDocumentElement(); Element docEle = doc.getDocumentElement();
@@ -134,14 +134,14 @@ public class XMLParser {
Node currentBoat = boatsList.item(i); Node currentBoat = boatsList.item(i);
if (currentBoat.getNodeName().equals("Boat")) { if (currentBoat.getNodeName().equals("Boat")) {
// Boat boat = new Boat(currentBoat); // Boat boat = new Boat(currentBoat);
Boat boat = new Boat(XMLParser.getNodeAttributeString(currentBoat, "Type"), Yacht yacht = new Yacht(XMLParser.getNodeAttributeString(currentBoat, "Type"),
XMLParser.getNodeAttributeInt(currentBoat, "SourceID"), XMLParser.getNodeAttributeInt(currentBoat, "SourceID"),
XMLParser.getNodeAttributeString(currentBoat, "HullNum"), XMLParser.getNodeAttributeString(currentBoat, "HullNum"),
XMLParser.getNodeAttributeString(currentBoat, "ShortName"), XMLParser.getNodeAttributeString(currentBoat, "ShortName"),
XMLParser.getNodeAttributeString(currentBoat, "BoatName"), XMLParser.getNodeAttributeString(currentBoat, "BoatName"),
XMLParser.getNodeAttributeString(currentBoat, "Country")); XMLParser.getNodeAttributeString(currentBoat, "Country"));
if (boat.getBoatType().equals("Yacht")) { if (yacht.getBoatType().equals("Yacht")) {
competingBoats.put(boat.getSourceID(), boat); competingBoats.put(yacht.getSourceId(), yacht);
} }
} }
} }
@@ -3,7 +3,7 @@ package seng302.visualiser;
import seng302.model.stream.packets.StreamPacket; import seng302.model.stream.packets.StreamPacket;
/** /**
* Created by cir27 on 21/07/17. * Functional interface for receiving packets from client socket.
*/ */
@FunctionalInterface @FunctionalInterface
public interface ClientSocketListener { public interface ClientSocketListener {
@@ -9,48 +9,42 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.net.UnknownHostException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import java.util.zip.Checksum; import java.util.zip.Checksum;
import seng302.model.stream.packets.StreamPacket; import seng302.model.stream.packets.StreamPacket;
import seng302.models.stream.packets.StreamPacket;
import seng302.server.messages.BoatActionMessage; import seng302.server.messages.BoatActionMessage;
import seng302.server.messages.Message; import seng302.server.messages.Message;
/** /**
* Created by kre39 on 13/07/17. * Created by kre39 on 13/07/17.
*/ */
public class ClientToServerThread extends Thread {
private Queue<StreamPacket> streamPackets = new ConcurrentLinkedQueue<>();
private List<ClientSocketListener> listeners = new ArrayList<>();
public class ClientToServerThread implements Runnable { public class ClientToServerThread implements Runnable {
private static final int LOG_LEVEL = 1; private static final int LOG_LEVEL = 1;
private Queue<StreamPacket> streamPackets = new ConcurrentLinkedQueue<>();
private List<ClientSocketListener> listeners = new ArrayList<>();
private Thread thread; private Thread thread;
private Integer ourID;
private Socket socket; private Socket socket;
private InputStream is; private InputStream is;
private OutputStream os; private OutputStream os;
private int clientId;
private Boolean updateClient = true; private Boolean updateClient = true;
private ByteArrayOutputStream crcBuffer; private ByteArrayOutputStream crcBuffer;
public ClientToServerThread(String ipAddress, Integer portNumber) throws Exception{ public ClientToServerThread(String ipAddress, Integer portNumber) throws IOException{
socket = new Socket(ipAddress, portNumber); socket = new Socket(ipAddress, portNumber);
is = socket.getInputStream(); is = socket.getInputStream();
os = socket.getOutputStream(); os = socket.getOutputStream();
Integer allocatedID = threeWayHandshake(); Integer allocatedID = threeWayHandshake();
if (allocatedID != null) { if (allocatedID != null) {
ourID = allocatedID; clientId = allocatedID;
clientLog("Successful handshake. Allocated ID: " + ourID, 1); clientLog("Successful handshake. Allocated ID: " + clientId, 1);
ClientState.setClientSourceId(String.valueOf(ourID));
} else { } else {
clientLog("Unsuccessful handshake", 1); clientLog("Unsuccessful handshake", 1);
closeSocket(); closeSocket();
@@ -59,7 +53,6 @@ public class ClientToServerThread implements Runnable {
thread = new Thread(this); thread = new Thread(this);
thread.start(); thread.start();
} }
static void clientLog(String message, int logLevel){ static void clientLog(String message, int logLevel){
@@ -72,7 +65,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(ClientState.isConnectedToHost()) { while(true) { /**REMOVED SOMETHING HERE ClientState.isConnectedToHost() */
try { try {
//Perform a write if it is time to as delegated by the MainServerThread //Perform a write if it is time to as delegated by the MainServerThread
if (updateClient) { if (updateClient) {
@@ -113,8 +106,8 @@ public class ClientToServerThread implements Runnable {
return; return;
} }
} }
closeSocket(); // closeSocket();
clientLog("Disconnected from server", 0); // clientLog("Disconnected from server", 0);
} }
@@ -0,0 +1,305 @@
package seng302.visualiser;
import java.io.IOException;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.Pane;
import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread;
import seng302.model.Yacht;
import seng302.model.RaceState;
import seng302.model.mark.Mark;
import seng302.model.stream.parser.PositionUpdateData.DeviceType;
import seng302.model.stream.parser.MarkRoundingData;
import seng302.model.stream.parser.RaceStatusData;
import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.stream.parser.StreamParser;
import seng302.model.stream.xml.parser.RegattaXMLData;
import seng302.model.stream.xml.parser.XMLParser;
import seng302.model.stream.parser.PositionUpdateData;
import seng302.model.stream.packets.StreamPacket;
import seng302.visualiser.ClientToServerThread;
import seng302.visualiser.controllers.LobbyController;
import seng302.visualiser.controllers.LobbyController.CloseStatus;
import seng302.visualiser.controllers.RaceViewController;
/**
* Created by cir27 on 20/07/17.
*/
public class GameClient {
private Pane holderPane;
private ClientToServerThread socketThread;
private MainServerThread server;
private RaceViewController raceView;
private Map<Integer, Yacht> allBoatsMap;
private RegattaXMLData regattaData;
private RaceXMLData courseData;
private RaceState raceState = new RaceState();
private ObservableList<String> lobbyList = FXCollections.observableArrayList();
public GameClient(Pane holder) {
this.holderPane = holder;
}
public void runAsClient (String ipAddress, Integer portNumber) {
try {
socketThread = new ClientToServerThread(ipAddress, portNumber);
} catch (IOException ioe) {
ioe.printStackTrace();
System.out.println("Unable to connect to host...");
}
LobbyController lobbyController = loadLobby("/views/LobbyView.fxml");
lobbyController.setPlayerListSource(lobbyList);
lobbyController.setTitle("Connected to host - IP : " + ipAddress + " Port : " + portNumber);
lobbyController.addCloseListener((exitCause) -> this.loadStartScreen());
socketThread.addStreamObserver(this::parsePacket);
}
public void runAsHost (String ipAddress, Integer portNumber) {
server = new MainServerThread();
try {
socketThread = new ClientToServerThread(ipAddress, portNumber);
} catch (IOException ioe) {
ioe.printStackTrace();
System.out.println("Unable to make local connection to host...");
}
LobbyController lobbyController = loadLobby("/views/HostLobbyView.fxml");
lobbyController.setPlayerListSource(GameState.getObservablePlayers());
lobbyController.setTitle("Hosting Lobby - IP : " + ipAddress + " Port : " + portNumber);
lobbyController.addCloseListener(exitCause -> {
if (exitCause == CloseStatus.READY) {
server.startGame();
socketThread.addStreamObserver(this::parsePacket);
} else if (exitCause == CloseStatus.LEAVE) {
loadStartScreen();
}
});
}
private void loadStartScreen () {
socketThread.closeSocket();
socketThread = null;
if (server != null) {
// TODO: 26/07/17 cir27 - handle disconnecting
server.shutDown();
server = null;
}
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/views/StartScreenView.fxml"));
try {
holderPane.getChildren().clear();
holderPane.getChildren().add(fxmlLoader.load());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Loads a view of the lobby into the clients pane
* @param lobbyView fxml file for the desired lobby
* @return the lobby controller.
*/
private LobbyController loadLobby (String lobbyView) {
FXMLLoader fxmlLoader = new FXMLLoader(GameClient.class.getResource(lobbyView));
try {
holderPane.getChildren().clear();
holderPane.getChildren().add(fxmlLoader.load());
} catch (IOException e) {
e.printStackTrace();
}
return fxmlLoader.getController();
}
private void loadRaceView () {
// allBoatsMap.forEach((id, boat) -> {
// if (courseData.getParticipants().contains(id))
// racingBoats.put(id, boat);
// });
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/views/RaceView.fxml"));
raceView = fxmlLoader.getController();
try {
holderPane.getChildren().add(fxmlLoader.load());
} catch (IOException e) {
e.printStackTrace();
}
raceView.loadRace(allBoatsMap, courseData, raceState);
}
private void parsePacket(StreamPacket packet) {
switch (packet.getType()) {
case RACE_STATUS:
processRaceStatusUpdate(StreamParser.extractRaceStatus(packet));
break;
case REGATTA_XML:
System.out.println("REGATTA XML");
regattaData = XMLParser.parseRegatta(
StreamParser.extractXmlMessage(packet)
);
raceState.setTimeZone(
TimeZone.getTimeZone(
ZoneId.ofOffset("UTC", ZoneOffset.ofHours(regattaData.getUtcOffset()))
)
);
startRaceIfAllDataReceived();
break;
case RACE_XML:
System.out.println("RACE XML");
courseData = XMLParser.parseRace(
StreamParser.extractXmlMessage(packet)
);
if (raceView != null) {
raceView.updateRaceData(courseData);
}
startRaceIfAllDataReceived();
break;
case BOAT_XML:
System.out.println("BOAT XML");
allBoatsMap = XMLParser.parseBoats(
StreamParser.extractXmlMessage(packet)
);
lobbyList.clear();
allBoatsMap.forEach((id, boat) -> lobbyList.add(id.toString() + boat.getBoatName()));
startRaceIfAllDataReceived();
break;
case RACE_START_STATUS:
raceState.updateState(StreamParser.extractRaceStartStatus(packet));
break;
case BOAT_LOCATION:
updatePosition(StreamParser.extractBoatLocation(packet));
break;
case MARK_ROUNDING:
updateMarkRounding(StreamParser.extractMarkRounding(packet));
break;
}
}
private void startRaceIfAllDataReceived() {
if (allXMLReceived())
loadRaceView();
}
private boolean allXMLReceived () {
return courseData != null && allBoatsMap != null && regattaData != null;
}
/**
* Updates the position of a boat. Boat and position are given in the provided data.
* @param positionData
*/
private void updatePosition(PositionUpdateData positionData) {
if (positionData.getType() == DeviceType.YACHT_TYPE) {
if (allXMLReceived() && allBoatsMap.containsKey(positionData.getDeviceId())) {
Yacht yacht = allBoatsMap.get(positionData.getDeviceId());
yacht.setVelocityProperty(positionData.getGroundSpeed());
yacht.setLat(positionData.getLat());
yacht.setLon(positionData.getLon());
yacht.setHeading(positionData.getHeading());
}
} else if (positionData.getType() == DeviceType.MARK_TYPE) {
Mark mark = courseData.getCompoundMarks().get(positionData.getDeviceId());
}
}
/**
* Updates the boat as having passed the mark. Boat and mark are given by the ids in the
* provided data.
* @param roundingData Contains data for the rounding of a mark.
*/
private void updateMarkRounding(MarkRoundingData roundingData) {
if (allXMLReceived()) {
Yacht yacht = allBoatsMap.get(roundingData.getBoatId());
yacht.setMarkRoundingTime(roundingData.getTimeStamp());
yacht.updateTimeSinceLastMarkProperty(
raceState.getRaceTime() - roundingData.getTimeStamp());
yacht.setLastMarkRounded(
courseData.getCompoundMarks().get(
roundingData.getMarkId()
)
);
}
}
private void processRaceStatusUpdate (RaceStatusData data) {
if (allXMLReceived()) {
raceState.updateState(data);
for (long[] boatData : data.getBoatData()) {
Yacht yacht = allBoatsMap.get((int) boatData[0]);
yacht.setEstimateTimeTillNextMark(raceState.getRaceTime() - boatData[1]);
yacht.setEstimateTimeAtFinish(boatData[2]);
int legNumber = (int) boatData[3];
yacht.setLegNumber(legNumber);
if (legNumber != yacht.getLegNumber()) {
int placing = 1;
for (Yacht otherYacht : allBoatsMap.values()) {
if (otherYacht.getSourceId() != boatData[0] &&
yacht.getLegNumber() <= otherYacht.getLegNumber())
placing++;
}
yacht.setPositionInteger(placing);
}
}
}
}
private void close () {
socketThread.closeSocket();
}
// /** Handle the key-pressed event from the text field. */
// public void keyPressed(KeyEvent e) {
// BoatActionMessage boatActionMessage;
// switch (e.getCode()){
// case SPACE: // align with vmg
// boatActionMessage = new BoatActionMessage(BoatActionType.VMG);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case PAGE_UP: // upwind
// boatActionMessage = new BoatActionMessage(BoatActionType.UPWIND);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case PAGE_DOWN: // downwind
// boatActionMessage = new BoatActionMessage(BoatActionType.DOWNWIND);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case ENTER: // tack/gybe
// boatActionMessage = new BoatActionMessage(BoatActionType.TACK_GYBE);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// //TODO Allow a zoom in and zoom out methods
// case Z: // zoom in
// System.out.println("Zoom in");
// break;
// case X: // zoom out
// System.out.println("Zoom out");
// break;
// }
// }
// public void keyReleased(KeyEvent e) {
// switch (e.getCode()) {
// //TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
// case SHIFT: // sails in/sails out
// BoatActionMessage boatActionMessage = new BoatActionMessage(BoatActionType.SAILS_IN);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// }
// }
//
// onKeyPressed="#keyPressed" onKeyReleased="#keyReleased"
}
+12 -13
View File
@@ -27,14 +27,13 @@ import seng302.visualiser.fxObjects.AnnotationBox;
import seng302.visualiser.fxObjects.BoatObject; import seng302.visualiser.fxObjects.BoatObject;
import seng302.visualiser.fxObjects.MarkObject; import seng302.visualiser.fxObjects.MarkObject;
import seng302.model.Colors; import seng302.model.Colors;
import seng302.model.Boat; import seng302.model.Yacht;
import seng302.model.map.Boundary; import seng302.model.map.Boundary;
import seng302.model.map.CanvasMap; import seng302.model.map.CanvasMap;
import seng302.model.mark.GateMark; import seng302.model.mark.GateMark;
import seng302.model.mark.Mark; import seng302.model.mark.Mark;
import seng302.model.mark.MarkType; import seng302.model.mark.MarkType;
import seng302.model.mark.SingleMark; import seng302.model.mark.SingleMark;
import seng302.model.stream.parsers.StreamParser;
import seng302.utilities.GeoPoint; import seng302.utilities.GeoPoint;
import seng302.utilities.GeoUtility; import seng302.utilities.GeoUtility;
@@ -65,7 +64,7 @@ public class GameView extends Pane {
private double metersPerPixelY; private double metersPerPixelY;
private Map<SingleMark, MarkObject> markObjects = new HashMap<>(); private Map<SingleMark, MarkObject> markObjects = new HashMap<>();
private Map<Boat, BoatObject> boatObjects = new HashMap<>(); private Map<Yacht, BoatObject> boatObjects = new HashMap<>();
private List<AnnotationBox> annotations = new ArrayList<>(); private List<AnnotationBox> annotations = new ArrayList<>();
private Text fpsDisplay = new Text(); private Text fpsDisplay = new Text();
@@ -255,17 +254,17 @@ public class GameView extends Pane {
/** /**
* Draws all the boats. * Draws all the boats.
*/ */
public void setBoats(List<Boat> boats) { public void setBoats(List<Yacht> yachts) {
Group annotationsGroup = new Group(); Group annotationsGroup = new Group();
Group wakesGroup = new Group(); Group wakesGroup = new Group();
Group boatObjectGroup = new Group(); Group boatObjectGroup = new Group();
BoatObject newObject; BoatObject newObject;
for (Boat boat : boats) { for (Yacht yacht : yachts) {
newObject = new BoatObject(); newObject = new BoatObject();
// newObject.bindBoat(boat); // newObject.bindBoat(boat);
newObject.setFill(Colors.getColor()); newObject.setFill(Colors.getColor());
createAnnotationBox(boat); createAnnotationBox(yacht);
} }
// Group wakes = new Group(); // Group wakes = new Group();
@@ -279,19 +278,19 @@ public class GameView extends Pane {
gameObjects.addAll(boatObjects.values()); gameObjects.addAll(boatObjects.values());
} }
private AnnotationBox createAnnotationBox (Boat boat) { private AnnotationBox createAnnotationBox (Yacht yacht) {
AnnotationBox newAnnotation; AnnotationBox newAnnotation;
newAnnotation = new AnnotationBox(); newAnnotation = new AnnotationBox();
newAnnotation.addAnnotation("name", boat.getShortName()); newAnnotation.addAnnotation("name", yacht.getShortName());
// newAnnotation.addAnnotation("country", boat.getCountry()); // newAnnotation.addAnnotation("country", boat.getCountry());
newAnnotation.addAnnotation( newAnnotation.addAnnotation(
"velocity", "velocity",
boat.getVelocityProperty(), yacht.getVelocityProperty(),
(velocity) -> String.format("%.2f ms", velocity.doubleValue()) (velocity) -> String.format("%.2f ms", velocity.doubleValue())
); );
newAnnotation.addAnnotation( newAnnotation.addAnnotation(
"nextMark", "nextMark",
boat.timeTillNextProperty(), yacht.timeTillNextProperty(),
(time) -> { (time) -> {
DateFormat format = new SimpleDateFormat("mm:ss"); DateFormat format = new SimpleDateFormat("mm:ss");
return format.format(time); return format.format(time);
@@ -299,7 +298,7 @@ public class GameView extends Pane {
); );
newAnnotation.addAnnotation( newAnnotation.addAnnotation(
"lastMark", "lastMark",
boat.timeTillNextProperty(), yacht.timeTillNextProperty(),
(time) -> { (time) -> {
DateFormat format = new SimpleDateFormat("mm:ss"); DateFormat format = new SimpleDateFormat("mm:ss");
return format.format(time); return format.format(time);
@@ -518,9 +517,9 @@ public class GameView extends Pane {
return fpsDisplay.visibleProperty(); return fpsDisplay.visibleProperty();
} }
public void selectBoat (Boat selectedBoat) { public void selectBoat (Yacht selectedYacht) {
boatObjects.forEach((boat, group) -> boatObjects.forEach((boat, group) ->
group.setIsSelected(boat == selectedBoat) group.setIsSelected(boat == selectedYacht)
); );
} }
@@ -1,8 +1,10 @@
package seng302.controllers; package seng302.visualiser.controllers;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
@@ -15,12 +17,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.model.Yacht;
import seng302.models.Yacht; import seng302.model.stream.parser.StreamParser;
import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
import seng302.model.Boat;
import seng302.model.stream.parsers.StreamParser;
import seng302.model.stream.parsers.xml.XMLParser.RaceXMLObject.Participant;
public class FinishScreenViewController implements Initializable { public class FinishScreenViewController implements Initializable {
@@ -37,6 +35,8 @@ public class FinishScreenViewController implements Initializable {
@FXML @FXML
private TableColumn<Yacht, String> countryCol; private TableColumn<Yacht, String> countryCol;
ObservableList<Yacht> data = FXCollections.observableArrayList();
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
finishScreenGridPane.getStylesheets() finishScreenGridPane.getStylesheets()
@@ -44,7 +44,6 @@ public class FinishScreenViewController implements Initializable {
finishOrderTable.getStylesheets().add(getClass().getResource("/css/master.css").toString()); finishOrderTable.getStylesheets().add(getClass().getResource("/css/master.css").toString());
// set up data for table // set up data for table
ObservableList<Yacht> data = FXCollections.observableArrayList();
finishOrderTable.setItems(data); finishOrderTable.setItems(data);
// setting table col data // setting table col data
@@ -60,24 +59,15 @@ public class FinishScreenViewController implements Initializable {
countryCol.setCellValueFactory( countryCol.setCellValueFactory(
new PropertyValueFactory<>("country") new PropertyValueFactory<>("country")
); );
// check if the boat is racing
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML()
.getParticipants();
ArrayList<Integer> participantIDs = new ArrayList<>();
for (Participant p : participants) {
participantIDs.add(p.getsourceID());
}
// add data to table
for (Yacht boat : StreamParser.getBoatsPos().values()) {
if (participantIDs.contains(boat.getSourceID())) {
data.add(boat);
}
}
finishOrderTable.refresh(); finishOrderTable.refresh();
} }
public void setFinishers (List<Yacht> participants) {
List<Yacht> sorted = new ArrayList<>(participants);
sorted.sort(Comparator.comparingInt(Yacht::getPositionInteger));
finishOrderTable.getItems().setAll(sorted);
}
private void setContentPane(String jfxUrl) { private void setContentPane(String jfxUrl) {
try { try {
// get the main controller anchor pane (FinishView -> MainView) // get the main controller anchor pane (FinishView -> MainView)
@@ -1,14 +1,9 @@
package seng302.visualiser.controllers; package seng302.visualiser.controllers;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.URL; import java.net.URL;
import java.util.*; import java.util.*;
import javafx.application.Platform;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
@@ -24,17 +19,28 @@ import javafx.scene.layout.Pane;
import javafx.scene.media.Media; import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer; import javafx.scene.media.MediaPlayer;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import seng302.client.ClientState;
import seng302.client.ClientStateQueryingRunnable;
import seng302.gameServer.GameStages; import seng302.gameServer.GameStages;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread;
/** /**
* A class describing the actions of the lobby screen * A class describing the actions of the lobby screen
* Created by wmu16 on 10/07/17. * Created by wmu16 on 10/07/17.
*/ */
public class LobbyController implements Initializable, Observer{ public class LobbyController implements Initializable {
public enum CloseStatus {
LEAVE,
READY
}
@FunctionalInterface
public interface LobbyCloseListener {
void notify(CloseStatus exitCause);
}
@FXML
private ListView<String> competitorsListView;
@FXML @FXML
private GridPane lobbyScreen; private GridPane lobbyScreen;
@FXML @FXML
@@ -42,7 +48,7 @@ public class LobbyController implements Initializable, Observer{
@FXML @FXML
private Button readyButton; private Button readyButton;
@FXML @FXML
private ListView firstListView; private ListView<String> firstListView;
@FXML @FXML
private ListView secondListView; private ListView secondListView;
@FXML @FXML
@@ -83,10 +89,11 @@ public class LobbyController implements Initializable, Observer{
private static ObservableList<String> sixthCompetitor = FXCollections.observableArrayList(); private static ObservableList<String> sixthCompetitor = FXCollections.observableArrayList();
private static ObservableList<String> seventhCompetitor = FXCollections.observableArrayList(); private static ObservableList<String> seventhCompetitor = FXCollections.observableArrayList();
private static ObservableList<String> eighthCompetitor = FXCollections.observableArrayList(); private static ObservableList<String> eighthCompetitor = FXCollections.observableArrayList();
private ClientStateQueryingRunnable clientStateQueryingRunnable; // private ClientStateQueryingRunnable clientStateQueryingRunnable;
private Boolean switchedPane = false; private Boolean switchedPane = false;
private MainServerThread mainServerThread;
private List<LobbyCloseListener> lobbyListeners = new ArrayList<>();
private void setContentPane(String jfxUrl) { private void setContentPane(String jfxUrl) {
try { try {
@@ -105,51 +112,54 @@ public class LobbyController implements Initializable, Observer{
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
if (ClientState.isHost()) { // if (ClientState.isHost()) {
lobbyIpText.setText("Lobby Host IP: " + ClientState.getHostIp()); // lobbyIpText.setText("Lobby Host IP: " + ClientState.getHostIp());
readyButton.setDisable(false); // readyButton.setDisable(false);
} // }
else { // else {
lobbyIpText.setText("Connected to IP: "); // lobbyIpText.setText("Connected to IP: ");
readyButton.setDisable(true); // readyButton.setDisable(true);
} // readyButton.setVisible(false);
initialiseListView(); // }
// initialiseListView();
// initialiseLobbyControllerThread(); // initialiseLobbyControllerThread();
initialiseImageView(); // parrot gif init // initialiseImageView(); // parrot gif init
// set up client state query thread, so that when it receives the race-started packet // set up client state query thread, so that when it receives the race-started packet
// it can switch to the race view // it can switch to the race view
ClientStateQueryingRunnable clientStateQueryingRunnable = new ClientStateQueryingRunnable(); // ClientStateQueryingRunnable clientStateQueryingRunnable = new ClientStateQueryingRunnable();
clientStateQueryingRunnable.addObserver(this); // clientStateQueryingRunnable.addObserver(this);
Thread clientStateQueryingThread = new Thread(clientStateQueryingRunnable, "Client State querying thread"); // Thread clientStateQueryingThread = new Thread(clientStateQueryingRunnable, "Client State querying thread");
clientStateQueryingThread.setDaemon(true); // clientStateQueryingThread.setDaemon(true);
clientStateQueryingThread.start(); // clientStateQueryingThread.start();
} }
@Override // @Override
public void update(Observable o, Object arg) { // public void update(Observable o, Object arg) {
Platform.runLater(new Runnable() { // Platform.runLater(new Runnable() {
@Override // @Override
public void run() { // public void run() {
if (arg.equals("game started") && !switchedPane) { // if (arg.equals("game started") && !switchedPane) {
switchToRaceView(); // switchToRaceView();
} // }
if (arg.equals(("update players"))) { // if (arg.equals(("update players"))) {
initialiseListView(); // initialiseListView();
} // }
} // }
}); // });
} // }
private void initialiseListView() { private void initialiseListView() {
firstListView.getItems().clear(); // firstListView.getItems().clear();
secondListView.getItems().clear(); // secondListView.getItems().clear();
thirdListView.getItems().clear(); // thirdListView.getItems().clear();
fourthListView.getItems().clear(); // fourthListView.getItems().clear();
fifthListView.getItems().clear(); // fifthListView.getItems().clear();
sixthListView.getItems().clear(); // sixthListView.getItems().clear();
seventhListView.getItems().clear(); // seventhListView.getItems().clear();
eighthListView.getItems().clear(); // eighthListView.getItems().clear();
competitors = new ArrayList<>(); competitors = new ArrayList<>();
Collections.addAll(competitors, firstCompetitor, secondCompetitor, thirdCompetitor, Collections.addAll(competitors, firstCompetitor, secondCompetitor, thirdCompetitor,
@@ -159,19 +169,19 @@ public class LobbyController implements Initializable, Observer{
ol.removeAll(); ol.removeAll();
} }
firstCompetitor.add(ClientState.getClientSourceId()); // firstCompetitor.add(ClientState.getClientSourceId());
int competitorIndex = 1; // int competitorIndex = 1;
for (Integer yachtId : ClientState.getBoats().keySet()) { // for (Integer yachtId : ClientState.getBoats().keySet()) {
// break if there are more than 7 competitors // // break if there are more than 7 competitors
if (competitorIndex >= 8) { // if (competitorIndex >= 8) {
break; // break;
} // }
if (!yachtId.equals(Integer.parseInt(ClientState.getClientSourceId()))) { // if (!yachtId.equals(Integer.parseInt(ClientState.getClientSourceId()))) {
competitors.get(competitorIndex).add(String.valueOf(yachtId)); // competitors.get(competitorIndex).add(String.valueOf(yachtId));
competitorIndex++; // competitorIndex++;
} // }
} // }
firstListView.setItems(firstCompetitor); firstListView.setItems(firstCompetitor);
secondListView.setItems(secondCompetitor); secondListView.setItems(secondCompetitor);
@@ -183,20 +193,20 @@ public class LobbyController implements Initializable, Observer{
eighthListView.setItems(eighthCompetitor); eighthListView.setItems(eighthCompetitor);
} }
private void initialiseLobbyControllerThread() { // private void initialiseLobbyControllerThread() {
Thread thread = new Thread(new Runnable() { // Thread thread = new Thread(new Runnable() {
@Override // @Override
public void run() { // public void run() {
Platform.runLater(new Runnable() { // Platform.runLater(new Runnable() {
@Override // @Override
public void run() { // public void run() {
//
} // }
}); // });
} // }
}); // });
thread.start(); // thread.start();
} // }
private void initialiseImageView() { private void initialiseImageView() {
Image image1 = new Image(getClass().getResourceAsStream("/ParrotGif/alistair.gif")); Image image1 = new Image(getClass().getResourceAsStream("/ParrotGif/alistair.gif"));
@@ -223,7 +233,7 @@ public class LobbyController implements Initializable, Observer{
setContentPane("/views/StartScreenView.fxml"); setContentPane("/views/StartScreenView.fxml");
GameState.setCurrentStage(GameStages.CANCELLED); GameState.setCurrentStage(GameStages.CANCELLED);
// TODO: 20/07/17 wmu16 - Implement some way of terminating the game // TODO: 20/07/17 wmu16 - Implement some way of terminating the game
ClientState.setConnectedToHost(false); // ClientState.setConnectedToHost(false);
} }
@FXML @FXML
@@ -231,7 +241,8 @@ public class LobbyController implements Initializable, Observer{
// setContentPane("/views/RaceView.fxml"); // setContentPane("/views/RaceView.fxml");
playTheme(); playTheme();
GameState.setCurrentStage(GameStages.RACING); GameState.setCurrentStage(GameStages.RACING);
mainServerThread.startGame(); for (LobbyCloseListener readyListener : lobbyListeners)
readyListener.notify(CloseStatus.READY);
} }
@@ -253,14 +264,27 @@ public class LobbyController implements Initializable, Observer{
} }
} }
private void switchToRaceView() { // private void switchToRaceView() {
if (!switchedPane) { // if (!switchedPane) {
switchedPane = true; // switchedPane = true;
setContentPane("/views/RaceView.fxml"); // setContentPane("/views/RaceView.fxml");
} // }
// }
// TODO: 26/07/17 cir27 - Could probably be done in a cleaner way.
public void setTitle (String title) {
lobbyIpText.setText(title);
} }
public void setMainServerThread(MainServerThread mainServerThread) { public void addCloseListener(LobbyCloseListener listener) {
this.mainServerThread = mainServerThread; lobbyListeners.add(listener);
}
public void setPlayerListSource (ObservableList<String> players) {
if (competitorsListView != null)
competitorsListView.setItems(players);
if (firstListView != null) {
firstListView.setItems(players);
firstImageView.setVisible(false);
}
} }
} }
@@ -1,12 +1,10 @@
package seng302.visualiser.controllers; package seng302.visualiser.controllers;
import com.sun.javafx.collections.SortableList;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javafx.animation.KeyFrame; 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.collections.transformation.SortedList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.geometry.Point2D; import javafx.geometry.Point2D;
@@ -33,20 +31,15 @@ import javafx.stage.StageStyle;
import javafx.util.Duration; import javafx.util.Duration;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import seng302.model.Corner; import seng302.model.Corner;
import seng302.model.stream.parsers.xml.RaceXMLData; import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.utilities.GeoUtility;
import seng302.visualiser.GameView; import seng302.visualiser.GameView;
import seng302.visualiser.controllers.annotations.Annotation; import seng302.visualiser.controllers.annotations.Annotation;
import seng302.visualiser.controllers.annotations.ImportantAnnotationController; import seng302.visualiser.controllers.annotations.ImportantAnnotationController;
import seng302.visualiser.controllers.annotations.ImportantAnnotationDelegate; import seng302.visualiser.controllers.annotations.ImportantAnnotationDelegate;
import seng302.visualiser.controllers.annotations.ImportantAnnotationsState; import seng302.visualiser.controllers.annotations.ImportantAnnotationsState;
import seng302.visualiser.fxObjects.BoatObject; import seng302.visualiser.fxObjects.BoatObject;
import seng302.visualiser.fxObjects.MarkObject;
import seng302.model.*; import seng302.model.*;
import seng302.model.mark.GateMark;
import seng302.model.mark.Mark; import seng302.model.mark.Mark;
import seng302.model.mark.SingleMark;
import seng302.model.stream.parsers.StreamParser;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
@@ -75,10 +68,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
@FXML @FXML
private Button selectAnnotationBtn; private Button selectAnnotationBtn;
@FXML @FXML
private ComboBox<Boat> boatSelectionComboBox; private ComboBox<Yacht> yachtSelectionComboBox;
//Race Data //Race Data
private Map<Integer, Boat> participants; private Map<Integer, Yacht> participants;
private Map<Integer, Mark> markers; private Map<Integer, Mark> markers;
private RaceXMLData courseData; private RaceXMLData courseData;
private GameView gameView; private GameView gameView;
@@ -102,7 +95,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView()); selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
} }
public void loadRace (Map<Integer, Boat> participants, RaceXMLData raceData, RaceState raceState) { public void loadRace (Map<Integer, Yacht> participants, RaceXMLData raceData, RaceState raceState) {
this.participants = participants; this.participants = participants;
this.courseData = raceData; this.courseData = raceData;
this.markers = raceData.getCompoundMarks(); this.markers = raceData.getCompoundMarks();
@@ -205,30 +198,30 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/** /**
* Used to add any new boats into the race that may have started late or not have had data received yet * Used to add any new yachts into the race that may have started late or not have had data received yet
*/ */
private void updateSparkLine(){ private void updateSparkLine(){
// Collect the racing boats that aren't already in the chart // Collect the racing yachts that aren't already in the chart
List<Boat> sparkLineCandidates = new ArrayList<>(); List<Yacht> sparkLineCandidates = new ArrayList<>();
// participants.forEach((id, boat) ->{ // participants.forEach((id, yacht) ->{
// if (!sparkLineData.containsKey(id) && boat.getPosition() != null && !boat.getPosition().equals("-")) // if (!sparkLineData.containsKey(id) && yacht.getPosition() != null && !yacht.getPosition().equals("-"))
// sparkLineCandidates.add(boat); // sparkLineCandidates.add(yacht);
// }); // });
participants.forEach((id, boat) -> sparkLineCandidates.add(boat)); participants.forEach((id, yacht) -> sparkLineCandidates.add(yacht));
sparklineYAxis.setUpperBound(participants.size() + 1); sparklineYAxis.setUpperBound(participants.size() + 1);
// Create a new data series for new boats // Create a new data series for new yachts
sparkLineCandidates.stream().filter(yacht -> yacht.getPosition() != null).forEach(yacht -> { sparkLineCandidates.stream().filter(yacht -> yacht.getPositionInteger() != null).forEach(yacht -> {
Series<String, Double> yachtData = new Series<>(); Series<String, Double> yachtData = new Series<>();
yachtData.setName(yacht.getSourceID().toString()); yachtData.setName(yacht.getSourceId().toString());
yachtData.getData().add( yachtData.getData().add(
new XYChart.Data<>( new XYChart.Data<>(
Integer.toString(yacht.getLegNumber()), Integer.toString(yacht.getLegNumber()),
1.0 + participants.size() - yacht.getPosition() 1.0 + participants.size() - yacht.getPositionInteger()
) )
); );
sparkLineData.put(yacht.getSourceID(), yachtData); sparkLineData.put(yacht.getSourceId(), yachtData);
}); });
// Lambda function to sort the series in order of leg (later legs shown more to the right) // Lambda function to sort the series in order of leg (later legs shown more to the right)
@@ -256,36 +249,36 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/** /**
* Updates the yachts sparkline of the desired boat and using the new leg number * Updates the yachts sparkline of the desired yacht and using the new leg number
* @param boat The yacht to be updated on the sparkline * @param yacht The yacht to be updated on the sparkline
* @param legNumber the leg number that the position will be assigned to * @param legNumber the leg number that the position will be assigned to
*/ */
public void updateYachtPositionSparkline(Boat boat, Integer legNumber){ public void updateYachtPositionSparkline(Yacht yacht, Integer legNumber){
for (XYChart.Series<String, Double> positionData : sparkLineData.values()) { for (XYChart.Series<String, Double> positionData : sparkLineData.values()) {
positionData.getData().add( positionData.getData().add(
new Data<>( new Data<>(
Integer.toString(legNumber), Integer.toString(legNumber),
1.0 + participants.size() - boat.getPosition() 1.0 + participants.size() - yacht.getPositionInteger()
) )
); );
} }
// XYChart.Series<String, Double> positionData = sparkLineData.get(boat.getSourceID()); // XYChart.Series<String, Double> positionData = sparkLineData.get(yacht.getSourceID());
// positionData.getData().add( // positionData.getData().add(
// new XYChart.Data<>( // new XYChart.Data<>(
// Integer.toString(legNumber), // Integer.toString(legNumber),
// 1.0 + participants.size() - boat.getPosition() // 1.0 + participants.size() - yacht.getPosition()
// ) // )
// ); // );
} }
/** /**
* gets the rgb string of the boats colour to use for the chart via css * gets the rgb string of the yachts colour to use for the chart via css
* @param boatId id of boat passed in to get the boats colour * @param yachtId id of yacht passed in to get the yachts colour
* @return the colour as an rgb string * @return the colour as an rgb string
*/ */
private String getBoatColorAsRGB(String boatId){ private String getBoatColorAsRGB(String yachtId){
Color color = participants.get(Integer.valueOf(boatId)).getColour(); Color color = participants.get(Integer.valueOf(yachtId)).getColour();
if (color == null){ if (color == null){
return String.format("#%02X%02X%02X",255,255,255); return String.format("#%02X%02X%02X",255,255,255);
} }
@@ -298,7 +291,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/** /**
* Initialises a timer which updates elements of the RaceView such as wind direction, boat * Initialises a timer which updates elements of the RaceView such as wind direction, yacht
* orderings etc.. which are dependent on the info from the stream parser constantly. * orderings etc.. which are dependent on the info from the stream parser constantly.
* Updates of each of these attributes are called ONCE EACH SECOND * Updates of each of these attributes are called ONCE EACH SECOND
*/ */
@@ -322,28 +315,28 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/** /**
* Iterates over all corners until ones SeqID matches with the boats current leg number. * Iterates over all corners until ones SeqID matches with the yachts current leg number.
* Then it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark * Then it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark
* Returns null if no next mark found. * Returns null if no next mark found.
* @param bg The BoatGroup to find the next mark of * @param bg The BoatGroup to find the next mark of
* @return The next Mark or null if none found * @return The next Mark or null if none found
*/ */
private Mark getNextMark(BoatObject bg) { private Mark getNextMark(BoatObject bg) {
//
Integer legNumber = bg.getBoat().getLegNumber(); // Integer legNumber = bg.getYacht().getLegNumber();
List<Corner> markSequence = courseData.getMarkSequence(); // List<Corner> markSequence = courseData.getMarkSequence();
//
if (legNumber == 0) { // if (legNumber == 0) {
return null; // return null;
} else if (legNumber == markSequence.size() - 1) { // } else if (legNumber == markSequence.size() - 1) {
return null; // return null;
} // }
//
for (Corner corner : markSequence) { // for (Corner corner : markSequence) {
if (legNumber + 2 == corner.getSeqID()) { // if (legNumber + 2 == corner.getSeqID()) {
return courseData.getCompoundMarks().get(corner.getCompoundMarkID()); // return courseData.getCompoundMarks().get(corner.getCompoundMarkID());
} // }
} // }
return null; return null;
} }
@@ -370,7 +363,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
} }
/** /**
* Updates the order of the boats as from the StreamParser and sets them in the boat order * Updates the order of the yachts as from the StreamParser and sets them in the yacht order
* section * section
*/ */
private void updateOrder() { private void updateOrder() {
@@ -378,28 +371,28 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
positionVbox.getChildren().removeAll(); positionVbox.getChildren().removeAll();
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 yacht id
List<Boat> sorted = new ArrayList<>(participants.values()); List<Yacht> sorted = new ArrayList<>(participants.values());
sorted.sort(Comparator.comparingInt(Boat::getPosition)); sorted.sort(Comparator.comparingInt(Yacht::getPositionInteger));
for (Boat boat : sorted) { for (Yacht yacht : sorted) {
if (boat.getBoatStatus() == 3) { // 3 is finish status if (yacht.getBoatStatus() == 3) { // 3 is finish status
Text textToAdd = new Text(boat.getPosition() + ". " + Text textToAdd = new Text(yacht.getPositionInteger() + ". " +
boat.getShortName() + " (Finished)"); yacht.getShortName() + " (Finished)");
textToAdd.setFill(Paint.valueOf("#d3d3d3")); textToAdd.setFill(Paint.valueOf("#d3d3d3"));
positionVbox.getChildren().add(textToAdd); positionVbox.getChildren().add(textToAdd);
} else { } else {
Text textToAdd = new Text(boat.getPosition() + ". " + Text textToAdd = new Text(yacht.getPositionInteger() + ". " +
boat.getShortName() + " "); yacht.getShortName() + " ");
textToAdd.setFill(Paint.valueOf("#d3d3d3")); textToAdd.setFill(Paint.valueOf("#d3d3d3"));
textToAdd.setStyle(""); textToAdd.setStyle("");
positionVbox.getChildren().add(textToAdd); positionVbox.getChildren().add(textToAdd);
} }
} }
// participants.forEach((id, boat) ->{ // participants.forEach((id, yacht) ->{
// Text textToAdd = new Text(boat.getPosition() + ". " + // Text textToAdd = new Text(yacht.getPosition() + ". " +
// boat.getShortName() + " "); // yacht.getShortName() + " ");
// textToAdd.setFill(Paint.valueOf("#d3d3d3")); // textToAdd.setFill(Paint.valueOf("#d3d3d3"));
// textToAdd.setStyle(""); // textToAdd.setStyle("");
// positionVbox.getChildren().add(textToAdd); // positionVbox.getChildren().add(textToAdd);
@@ -442,9 +435,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
// Double resultingAngle = angleAndSpeed.keySet().iterator().next(); // Double resultingAngle = angleAndSpeed.keySet().iterator().next();
// //
// //
// Point2D boatCurrentPos = new Point2D(bg.getBoatLayoutX(), bg.getBoatLayoutY()); // Point2D yachtCurrentPos = new Point2D(bg.getBoatLayoutX(), bg.getBoatLayoutY());
// Point2D gateMidPoint = markPoint1.midpoint(markPoint2); // Point2D gateMidPoint = markPoint1.midpoint(markPoint2);
// Integer lineFuncResult = GeoUtility.lineFunction(boatCurrentPos, gateMidPoint, markPoint2); // Integer lineFuncResult = GeoUtility.lineFunction(yachtCurrentPos, gateMidPoint, markPoint2);
// Line rightLayline = new Line(); // Line rightLayline = new Line();
// Line leftLayline = new Line(); // Line leftLayline = new Line();
// if (lineFuncResult == 1) { // if (lineFuncResult == 1) {
@@ -498,15 +491,15 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/** /**
* Initialised the combo box with any boats currently in the race and adds the required listener * Initialised the combo box with any yachts currently in the race and adds the required listener
* for the combobox to take action upon selection * for the combobox to take action upon selection
*/ */
private void initialiseBoatSelectionComboBox() { private void initialiseBoatSelectionComboBox() {
boatSelectionComboBox.setItems( yachtSelectionComboBox.setItems(
FXCollections.observableArrayList(participants.values()) FXCollections.observableArrayList(participants.values())
); );
//Null check is if the listener is fired but nothing selected //Null check is if the listener is fired but nothing selected
boatSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> { yachtSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> {
if (selectedBoat != null) { if (selectedBoat != null) {
gameView.selectBoat(selectedBoat); gameView.selectBoat(selectedBoat);
} }
@@ -514,18 +507,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 yachts currently in the race as from the StreamParser and sets them to be selectable
* in the boat selection combo box * in the yacht selection combo box
*/ */
private void updateBoatSelectionComboBox() { private void updateBoatSelectionComboBox() {
ObservableList<Boat> observableBoats = FXCollections.observableArrayList(); ObservableList<Yacht> observableYachts = FXCollections.observableArrayList();
observableBoats.addAll(participants.values()); observableYachts.addAll(participants.values());
boatSelectionComboBox.setItems(observableBoats); yachtSelectionComboBox.setItems(observableYachts);
} }
/** /**
* Display the list of boats in the order they finished the race * Display the list of yachts in the order they finished the race
*/ */
private void loadRaceResultView() { private void loadRaceResultView() {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/FinishView.fxml")); FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/FinishView.fxml"));
@@ -580,18 +573,18 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/** /**
* Sets all the annotations of the selected boat to be visible and all others to be hidden * Sets all the annotations of the selected yacht to be visible and all others to be hidden
* *
* @param boat The yacht for which we want to view all annotations * @param yacht The yacht for which we want to view all annotations
*/ */
private void setSelectedBoat(Boat boat) { private void setSelectedBoat(Yacht yacht) {
// for (BoatObject bg : gameViewController.getBoatGroups()) { // for (BoatObject bg : gameViewController.getBoatGroups()) {
// //We need to iterate over all race groups to get the matching boat group belonging to this boat if we // //We need to iterate over all race groups to get the matching yacht group belonging to this yacht if we
// //are to toggle its annotations, there is no other backwards knowledge of a yacht to its boatgroup. // //are to toggle its annotations, there is no other backwards knowledge of a yacht to its yachtgroup.
// if (bg.getBoat().getHullID().equals(boat.getHullID())) { // if (bg.getBoat().getHullID().equals(yacht.getHullID())) {
//// updateLaylines(bg); //// updateLaylines(bg);
// bg.setIsSelected(true); // bg.setIsSelected(true);
//// selectedBoat = boat; //// selectedBoat = yacht;
// } else { // } else {
// bg.setIsSelected(false); // bg.setIsSelected(false);
// } // }
@@ -2,9 +2,12 @@ package seng302.visualiser.controllers;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.NetworkInterface; import java.net.NetworkInterface;
import java.net.URL;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.ResourceBundle;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
@@ -12,20 +15,18 @@ 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.ClientState; import seng302.client.ClientState;
import seng302.client.ClientToServerThread;
import seng302.visualiser.ClientToServerThread; import seng302.visualiser.ClientToServerThread;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread; import seng302.gameServer.MainServerThread;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import seng302.visualiser.GameClient;
/** /**
* A Class describing the actions of the start screen controller * A Class describing the actions of the start screen controller
* Created by wmu16 on 10/07/17. * Created by wmu16 on 10/07/17.
*/ */
public class StartScreenController { public class StartScreenController implements Initializable {
@FXML @FXML
private TextField ipTextField; private TextField ipTextField;
@@ -33,27 +34,35 @@ public class StartScreenController {
private TextField portTextField; private TextField portTextField;
@FXML @FXML
private GridPane startScreen2; private GridPane startScreen2;
@FXML
private AnchorPane holder;
/** GameClient gameClient;
* Loads the fxml content into the parent pane
* @param jfxUrl
* @return the controller of the fxml
*/
private Object setContentPane(String jfxUrl) {
try {
AnchorPane contentPane = (AnchorPane) startScreen2.getParent();
contentPane.getChildren().removeAll();
contentPane.getChildren().clear();
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(jfxUrl));
contentPane.getChildren().addAll((Pane) fxmlLoader.load());
return fxmlLoader.getController(); public void initialize(URL url, ResourceBundle resourceBundle) {
} catch (IOException e) { // gameClient = new GameClient(holder);
e.printStackTrace();
}
return null;
} }
//
// /**
// * Loads the fxml content into the parent pane
// * @param jfxUrl
// * @return the controller of the fxml
// */
// private Object setContentPane(String jfxUrl) {
// try {
// AnchorPane contentPane = (AnchorPane) startScreen2.getParent();
// contentPane.getChildren().removeAll();
// contentPane.getChildren().clear();
// contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
// FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(jfxUrl));
// contentPane.getChildren().addAll((Pane) fxmlLoader.load());
//
// return fxmlLoader.getController();
// } catch (IOException e) {
// e.printStackTrace();
// }
// return null;
// }
/** /**
@@ -65,33 +74,33 @@ public class StartScreenController {
*/ */
@FXML @FXML
public void hostButtonPressed() { public void hostButtonPressed() {
try { new GameState(getLocalHostIp());
String ipAddress = InetAddress.getLocalHost().getHostAddress(); gameClient = new GameClient(holder);
new GameState(ipAddress); gameClient.runAsHost(getLocalHostIp(), 4942);
new MainServerThread().start(); // try {
ClientToServerThread clientToServerThread = new ClientToServerThread("localhost", 4950); //// String ipAddress = InetAddress.getLocalHost().getHostAddress();
// controller.setClientToServerThread(clientToServerThread); //// new GameState(ipAddress);
clientToServerThread.start(); //// new MainServerThread();
// get the lobby controller so that we can pass the game server thread to it //// ClientToServerThread clientToServerThread = new ClientToServerThread("localhost", 4950);
new GameState(getLocalHostIp()); //// controller.setClientToServerThread(clientToServerThread);
MainServerThread mainServerThread = new MainServerThread(); // // get the lobby controller so that we can pass the game server thread to it
ClientState.setHost(true); // new GameState(getLocalHostIp());
// host will connect and handshake to itself after setting up the server // MainServerThread mainServerThread = new MainServerThread();
// TODO: 24/07/17 wmu16 - Make port number some static global type constant? //// ClientState.setHost(true);
ClientToServerThread clientToServerThread = new ClientToServerThread(ClientState.getHostIp(), 4942); // // host will connect and handshake to itself after setting up the server
ClientState.setConnectedToHost(true); // // TODO: 24/07/17 wmu16 - Make port number some static global type constant?
controller.setClientToServerThread(clientToServerThread); //// ClientToServerThread clientToServerThread = new ClientToServerThread(ClientState.getHostIp(), 4942);
LobbyController lobbyController = (LobbyController) setContentPane("/views/LobbyView.fxml"); //// ClientState.setConnectedToHost(true);
lobbyController.setMainServerThread(mainServerThread); //// controller.setClientToServerThread(clientToServerThread);
} catch (Exception e) { // LobbyController lobbyController = (LobbyController) setContentPane("/views/LobbyView.fxml");
Alert alert = new Alert(AlertType.ERROR); // lobbyController.setMainServerThread(mainServerThread);
alert.setHeaderText("Cannot host"); // } catch (Exception e) {
alert.setContentText("Oops, failed to host, try to restart."); // Alert alert = new Alert(AlertType.ERROR);
alert.showAndWait(); // alert.setHeaderText("Cannot host");
e.printStackTrace(); // alert.setContentText("Oops, failed to host, try to restart.");
} // alert.showAndWait();
// e.printStackTrace();
// }
} }
/** /**
@@ -103,27 +112,30 @@ public class StartScreenController {
@FXML @FXML
public void connectButtonPressed() { public void connectButtonPressed() {
// TODO: 10/07/17 wmu16 - Finish function // TODO: 10/07/17 wmu16 - Finish function
try { gameClient = new GameClient(holder);
String ipAddress = ipTextField.getText().trim().toLowerCase(); gameClient.runAsClient(ipTextField.getText().trim().toLowerCase(), 4942);
Integer port = Integer.valueOf(portTextField.getText().trim());
ClientToServerThread clientToServerThread = new ClientToServerThread(ipAddress, port); // try {
ClientState.setHost(false); // String ipAddress = ipTextField.getText().trim().toLowerCase();
ClientState.setConnectedToHost(true); // Integer port = Integer.valueOf(portTextField.getText().trim());
//
controller.setClientToServerThread(clientToServerThread); //// ClientToServerThread clientToServerThread = new ClientToServerThread(ipAddress, port);
setContentPane("/views/LobbyView.fxml"); //// ClientState.setHost(false);
} catch (Exception e) { //// ClientState.setConnectedToHost(true);
Alert alert = new Alert(AlertType.ERROR); //
alert.setHeaderText("Cannot reach the host"); //// controller.setClientToServerThread(clientToServerThread);
alert.setContentText("Please check your host IP address."); //// setContentPane("/views/LobbyView.fxml");
alert.showAndWait(); // } catch (Exception e) {
} // Alert alert = new Alert(AlertType.ERROR);
// alert.setHeaderText("Cannot reach the host");
// alert.setContentText("Please check your host IP address.");
// alert.showAndWait();
// }
} }
public void setController(Controller controller) { // public void setController(Controller controller) {
this.controller = controller; // this.controller = controller;
} // }
/** /**
* Gets the local host ip address and sets this ip to ClientState. * Gets the local host ip address and sets this ip to ClientState.
@@ -158,7 +170,7 @@ public class StartScreenController {
if (ipAddress == null) { if (ipAddress == null) {
System.out.println("[HOST] Cannot obtain local host ip address."); System.out.println("[HOST] Cannot obtain local host ip address.");
} }
ClientState.setHostIp(ipAddress); // ClientState.setHostIp(ipAddress);
return ipAddress; return ipAddress;
} }
} }
@@ -1,215 +0,0 @@
package seng302.visualiser.controllers.client;
import java.io.IOException;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.Pane;
import seng302.model.Boat;
import seng302.model.RaceState;
import seng302.model.mark.Mark;
import seng302.model.stream.parsers.PositionUpdateData.DeviceType;
import seng302.model.stream.parsers.MarkRoundingData;
import seng302.model.stream.parsers.RaceStatusData;
import seng302.model.stream.parsers.xml.RaceXMLData;
import seng302.model.stream.parsers.StreamParser;
import seng302.model.stream.parsers.xml.RegattaXMLData;
import seng302.model.stream.parsers.xml.XMLParser;
import seng302.model.stream.parsers.PositionUpdateData;
import seng302.model.stream.packets.StreamPacket;
import seng302.visualiser.ClientToServerThread;
import seng302.visualiser.controllers.RaceViewController;
/**
* Created by cir27 on 20/07/17.
*/
public class ClientController {
private Pane holderPane;
private ClientToServerThread socketThread;
private RaceViewController raceView;
private Map<Integer, Boat> allBoatsMap;
private Map<Integer, Boat> racingBoats = new HashMap<>();
private RegattaXMLData regattaData;
private RaceXMLData courseData;
private RaceState raceState = new RaceState();
public ClientController (String ipAddress, Pane holder) {
this.holderPane = holder;
socketThread = new ClientToServerThread(ipAddress, 4950);
socketThread.start();
socketThread.addStreamObserver(this::parsePacket);
}
private void loadRaceView () {
allBoatsMap.forEach((id, boat) -> {
if (courseData.getParticipants().contains(id))
racingBoats.put(id, boat);
});
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("RaceView.fxml"));
raceView = fxmlLoader.getController();
try {
holderPane.getChildren().add(fxmlLoader.load());
} catch (IOException e) {
e.printStackTrace();
}
raceView.loadRace(racingBoats, courseData, raceState);
}
private void parsePacket(StreamPacket packet) {
switch (packet.getType()) {
case RACE_STATUS:
processRaceStatusUpdate(StreamParser.extractRaceStatus(packet));
break;
case REGATTA_XML:
regattaData = XMLParser.parseRegatta(
StreamParser.extractXmlMessage(packet)
);
raceState.setTimeZone(
TimeZone.getTimeZone(
ZoneId.ofOffset("UTC", ZoneOffset.ofHours(regattaData.getUtcOffset()))
)
);
startRaceIfAllDataReceived();
break;
case RACE_XML:
courseData = XMLParser.parseRace(
StreamParser.extractXmlMessage(packet)
);
if (raceView != null) {
raceView.updateRaceData(courseData);
}
startRaceIfAllDataReceived();
break;
case BOAT_XML:
allBoatsMap = XMLParser.parseBoats(
StreamParser.extractXmlMessage(packet)
);
startRaceIfAllDataReceived();
break;
case RACE_START_STATUS:
raceState.updateState(StreamParser.extractRaceStartStatus(packet));
break;
case BOAT_LOCATION:
updatePosition(StreamParser.extractBoatLocation(packet));
break;
case MARK_ROUNDING:
updateMarkRounding(StreamParser.extractMarkRounding(packet));
break;
}
}
private void startRaceIfAllDataReceived() {
if (courseData != null && allBoatsMap != null && regattaData != null)
loadRaceView();
}
/**
* Updates the position of a boat. Boat and position are given in the provided data.
* @param positionData
*/
private void updatePosition(PositionUpdateData positionData) {
if (positionData.getType() == DeviceType.YACHT_TYPE) {
Boat boat = racingBoats.get(positionData.getDeviceId());
boat.setVelocity(positionData.getGroundSpeed());
boat.setLat(positionData.getLat());
boat.setLon(positionData.getLon());
boat.setHeading(positionData.getHeading());
} else if (positionData.getType() == DeviceType.MARK_TYPE) {
Mark mark = courseData.getCompoundMarks().get(positionData.getDeviceId());
}
}
/**
* Updates the boat as having passed the mark. Boat and mark are given by the ids in the
* provided data.
* @param roundingData Contains data for the rounding of a mark.
*/
private void updateMarkRounding(MarkRoundingData roundingData) {
Boat boat = racingBoats.get(roundingData.getBoatId());
boat.setMarkRoundingTime(roundingData.getTimeStamp());
boat.setTimeSinceLastMark(raceState.getRaceTime() - roundingData.getTimeStamp());
boat.setLastMarkRounded(
courseData.getCompoundMarks().get(
roundingData.getMarkId()
)
);
}
private void processRaceStatusUpdate (RaceStatusData data) {
raceState.updateState(data);
for (long[] boatData : data.getBoatData()) {
Boat boat = allBoatsMap.get((int) boatData[0]);
boat.setEstimateTimeTillNextMark(raceState.getRaceTime() - boatData[1]);
boat.setEstimateTimeAtFinish(boatData[2]);
int legNumber = (int) boatData[3];
boat.setLegNumber(legNumber);
if (legNumber != boat.getLegNumber()) {
int placing = 1;
for (Boat otherBoat : allBoatsMap.values()) {
if (otherBoat.getSourceID() != boatData[0] &&
boat.getLegNumber() <= otherBoat.getLegNumber())
placing++;
}
boat.setPosition(placing);
}
}
}
private void close () {
socketThread.closeSocket();
}
// /** Handle the key-pressed event from the text field. */
// public void keyPressed(KeyEvent e) {
// BoatActionMessage boatActionMessage;
// switch (e.getCode()){
// case SPACE: // align with vmg
// boatActionMessage = new BoatActionMessage(BoatActionType.VMG);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case PAGE_UP: // upwind
// boatActionMessage = new BoatActionMessage(BoatActionType.UPWIND);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case PAGE_DOWN: // downwind
// boatActionMessage = new BoatActionMessage(BoatActionType.DOWNWIND);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case ENTER: // tack/gybe
// boatActionMessage = new BoatActionMessage(BoatActionType.TACK_GYBE);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// //TODO Allow a zoom in and zoom out methods
// case Z: // zoom in
// System.out.println("Zoom in");
// break;
// case X: // zoom out
// System.out.println("Zoom out");
// break;
// }
// }
// public void keyReleased(KeyEvent e) {
// switch (e.getCode()) {
// //TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
// case SHIFT: // sails in/sails out
// BoatActionMessage boatActionMessage = new BoatActionMessage(BoatActionType.SAILS_IN);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// }
// }
//
// onKeyPressed="#keyPressed" onKeyReleased="#keyReleased"
}
@@ -1,13 +0,0 @@
package seng302.visualiser.controllers.host;
import javafx.scene.layout.Pane;
/**
* Created by cir27 on 20/07/17.
*/
public class HostController {
Pane mainHolder;
public HostController (Pane holder) {
this.mainHolder = holder;
}
}
@@ -10,12 +10,7 @@ import javafx.scene.paint.Paint;
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.model.Boat; import seng302.model.Yacht;
import seng302.utilities.GeoUtility;
import seng302.model.mark.GateMark;
import seng302.model.mark.Mark;
import seng302.model.mark.SingleMark;
import seng302.model.stream.parsers.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
@@ -38,7 +33,6 @@ public class BoatObject extends Group {
private Double lastRotation = 0.0; private Double lastRotation = 0.0;
private long framesToMove; private long framesToMove;
//Graphical objects //Graphical objects
private Boat boat;
private Group lineGroup = new Group(); private Group lineGroup = new Group();
private Polygon boatPoly; private Polygon boatPoly;
private Wake wake; private Wake wake;
@@ -161,7 +155,7 @@ public class BoatObject extends Group {
boatPoly.getLayoutY() boatPoly.getLayoutY()
); );
l.getStrokeDashArray().setAll(3d, 7d); l.getStrokeDashArray().setAll(3d, 7d);
l.setStroke(boat.getColour()); l.setStroke(colour);
l.setCache(true); l.setCache(true);
l.setCacheHint(CacheHint.SPEED); l.setCacheHint(CacheHint.SPEED);
lineGroup.getChildren().add(l); lineGroup.getChildren().add(l);
@@ -199,7 +193,7 @@ public class BoatObject extends Group {
rotateTo(rotation); rotateTo(rotation);
wake.setRotation(rotation, groundSpeed); wake.setRotation(rotation, groundSpeed);
boat.setVelocity(groundSpeed); // yacht.setVelocity(groundSpeed);
lastTimeValid = timeValid; lastTimeValid = timeValid;
isStopped = false; isStopped = false;
lastRotation = rotation; lastRotation = rotation;
@@ -278,19 +272,6 @@ public class BoatObject extends Group {
return laylines; return laylines;
} }
public Boat getBoat() {
return boat;
}
/**
* Returns all raceIds associated with this group. For BoatGroups the ID's are for the boat.
*
* @return An array containing all ID's associated with this RaceObject.
*/
public long getRaceId() {
return boat.getSourceID();
}
public Group getWake () { public Group getWake () {
return wake; return wake;
} }
@@ -316,9 +297,4 @@ public class BoatObject extends Group {
return isStopped; return isStopped;
} }
@Override
public String toString() {
return boat.toString();
}
} }
-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="mainPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.GameViewController" />
+1 -9
View File
@@ -1,17 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?> <?import javafx.scene.text.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<GridPane fx:id="lobbyScreen" nodeOrientation="LEFT_TO_RIGHT" prefHeight="533.0" prefWidth="802.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.LobbyController"> <GridPane fx:id="lobbyScreen" nodeOrientation="LEFT_TO_RIGHT" prefHeight="533.0" prefWidth="802.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.LobbyController">
<columnConstraints> <columnConstraints>
+2 -9
View File
@@ -2,19 +2,12 @@
<?import javafx.scene.image.*?> <?import javafx.scene.image.*?>
<?import javafx.geometry.*?> <?import javafx.geometry.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?> <?import javafx.scene.text.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<GridPane fx:id="lobbyScreen" nodeOrientation="LEFT_TO_RIGHT" prefHeight="960.0" prefWidth="1530.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.controllers.LobbyController">
<GridPane fx:id="lobbyScreen" nodeOrientation="LEFT_TO_RIGHT" prefHeight="960.0" prefWidth="1530.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.LobbyController">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
</columnConstraints> </columnConstraints>
+2 -3
View File
@@ -67,8 +67,7 @@
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane fx:id="contentAnchorPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" style="-fx-background-color: skyblue;" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2147483647" GridPane.valignment="TOP"> <AnchorPane fx:id="contentAnchorPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" style="-fx-background-color: skyblue;" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2147483647" GridPane.valignment="TOP">
<children>
<fx:include fx:id="gameView" source="GameView.fxml" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" /> </AnchorPane>
</children></AnchorPane>
</children> </children>
</GridPane> </GridPane>
+52 -52
View File
@@ -1,59 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import java.lang.*?> <?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?> <?import javafx.scene.text.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<GridPane fx:id="startScreen2" nodeOrientation="LEFT_TO_RIGHT" prefWidth="800.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.StartScreenController"> <AnchorPane fx:id="holder" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="960.0" prefWidth="1530.0" style="-fx-background-color: #2C2c36;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.StartScreenController">
<columnConstraints> <children>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="442.0" /> <GridPane fx:id="startScreen2" layoutX="365.0" layoutY="285.0" nodeOrientation="LEFT_TO_RIGHT" prefWidth="800.0" style="-fx-background-color: #2C2c36;">
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="358.0" /> <children>
</columnConstraints> <Label alignment="CENTER" text="Welcome to Race Vision" textFill="WHITE" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
<rowConstraints> <font>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <Font size="40.0" />
<RowConstraints vgrow="SOMETIMES" /> </font>
<RowConstraints minHeight="72.0" prefHeight="72.0" vgrow="SOMETIMES" /> </Label>
<RowConstraints maxHeight="65.0" minHeight="36.0" prefHeight="46.0" vgrow="SOMETIMES" /> <Button mnemonicParsing="false" onAction="#hostButtonPressed" prefHeight="25.0" prefWidth="175.0" text="Host" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2" />
<RowConstraints maxHeight="108.0" minHeight="72.0" prefHeight="98.0" vgrow="SOMETIMES" /> <Button mnemonicParsing="false" onAction="#connectButtonPressed" prefHeight="25.0" prefWidth="147.0" text="Connect" GridPane.columnIndex="1" GridPane.rowIndex="4">
<RowConstraints minHeight="72.0" prefHeight="72.0" vgrow="SOMETIMES" /> <GridPane.margin>
</rowConstraints> <Insets left="5.0" right="5.0" />
<children> </GridPane.margin>
<Label alignment="CENTER" text="Welcome to Race Vision" textFill="WHITE" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM"> </Button>
<font> <TextField fx:id="ipTextField" alignment="CENTER" maxWidth="-Infinity" prefHeight="25.0" prefWidth="148.0" promptText="Host IP" text="132.181.14." GridPane.halignment="RIGHT" GridPane.rowIndex="4">
<Font size="40.0" /> <GridPane.margin>
</font> <Insets bottom="10.0" left="5.0" right="85.0" top="10.0" />
</Label> </GridPane.margin>
<Button mnemonicParsing="false" onAction="#hostButtonPressed" prefHeight="25.0" prefWidth="175.0" text="Host" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2" /> </TextField>
<Button mnemonicParsing="false" onAction="#connectButtonPressed" prefHeight="25.0" prefWidth="147.0" text="Connect" GridPane.columnIndex="1" GridPane.rowIndex="4"> <Text fill="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="OR" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="3">
<GridPane.margin> <font>
<Insets left="5.0" right="5.0" /> <Font size="21.0" />
</GridPane.margin></Button> </font>
<TextField fx:id="ipTextField" alignment="CENTER" maxWidth="-Infinity" prefHeight="25.0" prefWidth="148.0" promptText="Host IP" text="132.181.14." GridPane.halignment="RIGHT" GridPane.rowIndex="4"> </Text>
<GridPane.margin> <TextField fx:id="portTextField" alignment="CENTER" maxWidth="75.0" prefHeight="25.0" prefWidth="55.0" promptText="Port" text="4942" GridPane.halignment="RIGHT" GridPane.rowIndex="4">
<Insets bottom="10.0" left="5.0" right="85.0" top="10.0" /> <opaqueInsets>
</GridPane.margin> <Insets />
</TextField> </opaqueInsets>
<Text fill="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="OR" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="3"> <GridPane.margin>
<font> <Insets left="5.0" right="5.0" />
<Font size="21.0" /> </GridPane.margin>
</font> </TextField>
</Text> </children>
<TextField fx:id="portTextField" alignment="CENTER" maxWidth="75.0" prefHeight="25.0" prefWidth="55.0" promptText="Port" text="4942" GridPane.halignment="RIGHT" GridPane.rowIndex="4"> <columnConstraints>
<opaqueInsets> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="442.0" />
<Insets /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="358.0" />
</opaqueInsets> </columnConstraints>
<GridPane.margin> <rowConstraints>
<Insets left="5.0" right="5.0" /> <RowConstraints />
</GridPane.margin> <RowConstraints />
</TextField> <RowConstraints maxHeight="42.0" minHeight="10.0" prefHeight="42.0" vgrow="SOMETIMES" />
</children> <RowConstraints maxHeight="26.0" minHeight="13.0" prefHeight="26.0" vgrow="SOMETIMES" />
</GridPane> <RowConstraints maxHeight="66.0" minHeight="59.0" prefHeight="59.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="65.0" minHeight="36.0" prefHeight="46.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="108.0" minHeight="72.0" prefHeight="98.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="72.0" prefHeight="72.0" vgrow="SOMETIMES" />
</rowConstraints>
</GridPane>
</children>
</AnchorPane>
+8 -8
View File
@@ -9,17 +9,17 @@ import static org.junit.Assert.assertTrue;
public class TestRaceTimer { public class TestRaceTimer {
@Test @Test
public void testPositiveTimeString(){ public void testPositiveTimeString(){
RaceViewController controller = new RaceViewController(); // RaceViewController controller = new RaceViewController();
String result = controller.convertTimeToMinutesSeconds(61); // String result = controller.convertTimeToMinutesSeconds(61);
//
assertTrue(result.equals("01:01")); // assertTrue(result.equals("01:01"));
} }
@Test @Test
public void testNegativeTimeString(){ public void testNegativeTimeString(){
RaceViewController controller = new RaceViewController(); // RaceViewController controller = new RaceViewController();
String result = controller.convertTimeToMinutesSeconds(-61); // String result = controller.convertTimeToMinutesSeconds(-61);
//
assertTrue(result.equals("-01:01")); // assertTrue(result.equals("-01:01"));
} }
} }
+3 -3
View File
@@ -1,17 +1,17 @@
package seng302.models; package seng302.models;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import seng302.model.PolarTable;
import seng302.model.Yacht;
import seng302.utilities.GeoPoint; import seng302.utilities.GeoPoint;
public class YachtTest { public class YachtTest {
Double windDir; Double windDir;
Double windSpd; Double windSpd;
List<Yacht> yachts = new ArrayList<Yacht>(); List<Yacht> yachts = new ArrayList<>();
@Before @Before
public void setUp() { public void setUp() {