Merge remote-tracking branch 'origin/develop' into develop

# Conflicts:
#	src/main/java/seng302/model/ClientYacht.java
#	src/main/java/seng302/visualiser/GameView.java
#	src/test/java/seng302/visualiser/map/BoatSailAnimationToggleTest.java
#	src/test/java/steps/ToggleSailSteps.java
This commit is contained in:
Haoming Yin
2017-08-16 10:11:58 +12:00
24 changed files with 705 additions and 1204 deletions
+4
View File
@@ -180,3 +180,7 @@ local.properties
.recommenders/ .recommenders/
Makefile Makefile
infer-out/
infer.txt
log.log
+109 -9
View File
@@ -1,9 +1,11 @@
package seng302.gameServer; package seng302.gameServer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.gameServer.server.messages.BoatAction; import seng302.gameServer.server.messages.BoatAction;
@@ -12,6 +14,7 @@ import seng302.gameServer.server.messages.MarkRoundingMessage;
import seng302.gameServer.server.messages.MarkType; import seng302.gameServer.server.messages.MarkType;
import seng302.gameServer.server.messages.Message; import seng302.gameServer.server.messages.Message;
import seng302.gameServer.server.messages.RoundingBoatStatus; import seng302.gameServer.server.messages.RoundingBoatStatus;
import seng302.gameServer.server.messages.YachtEventCodeMessage;
import seng302.model.GeoPoint; import seng302.model.GeoPoint;
import seng302.model.Player; import seng302.model.Player;
import seng302.model.PolarTable; import seng302.model.PolarTable;
@@ -29,8 +32,8 @@ import seng302.utilities.GeoUtility;
public class GameState implements Runnable { public class GameState implements Runnable {
@FunctionalInterface @FunctionalInterface
interface MarkPassingListener { interface NewMessageListener {
void markPassing(Message message); void notify(Message message);
} }
private Logger logger = LoggerFactory.getLogger(GameState.class); private Logger logger = LoggerFactory.getLogger(GameState.class);
@@ -38,6 +41,11 @@ public class GameState implements Runnable {
private static final Integer STATE_UPDATES_PER_SECOND = 60; private static final Integer STATE_UPDATES_PER_SECOND = 60;
public static Integer MAX_PLAYERS = 8; public static Integer MAX_PLAYERS = 8;
public static Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further public static Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further
public static final Double MARK_COLLISION_DISTANCE = 15d;
public static final Double YACHT_COLLISION_DISTANCE = 25.0;
public static final Double BOUNCE_DISTANCE_MARK = 20.0;
public static final Double BOUNCE_DISTANCE_YACHT = 30.0;
public static final Double COLLISION_VELOCITY_PENALTY = 0.3;
private static Long previousUpdateTime; private static Long previousUpdateTime;
public static Double windDirection; public static Double windDirection;
@@ -50,8 +58,9 @@ public class GameState implements Runnable {
private static GameStages currentStage; private static GameStages currentStage;
private static MarkOrder markOrder; private static MarkOrder markOrder;
private static long startTime; private static long startTime;
private static Set<Mark> marks;
private static List<MarkPassingListener> markListeners; private static List<NewMessageListener> markListeners;
private static Map<Player, String> playerStringMap = new HashMap<>(); private static Map<Player, String> playerStringMap = new HashMap<>();
/* /*
@@ -81,12 +90,18 @@ public class GameState implements Runnable {
markListeners = new ArrayList<>(); markListeners = new ArrayList<>();
new Thread(this).start(); //Run the auto updates on the game state new Thread(this).start(); //Run the auto updates on the game state
marks = new MarkOrder().getAllMarks();
} }
public static String getHostIpAddress() { public static String getHostIpAddress() {
return hostIpAddress; return hostIpAddress;
} }
public static Set<Mark> getMarks(){
return Collections.unmodifiableSet(marks);
}
public static List<Player> getPlayers() { public static List<Player> getPlayers() {
return players; return players;
} }
@@ -120,7 +135,7 @@ public class GameState implements Runnable {
} }
public static void setCurrentStage(GameStages currentStage) { public static void setCurrentStage(GameStages currentStage) {
if (currentStage == GameStages.RACING){ if (currentStage == GameStages.RACING) {
startTime = System.currentTimeMillis(); startTime = System.currentTimeMillis();
} }
@@ -221,12 +236,49 @@ public class GameState implements Runnable {
yacht.runAutoPilot(); yacht.runAutoPilot();
yacht.updateLocation(timeInterval); yacht.updateLocation(timeInterval);
if (!yacht.getFinishedRace()) { if (!yacht.getFinishedRace()) {
checkForCollision(yacht);
checkForLegProgression(yacht); checkForLegProgression(yacht);
} }
} }
} }
public static void checkForCollision(ServerYacht serverYacht) {
ServerYacht collidedYacht = checkCollision(serverYacht);
if (collidedYacht != null) {
GeoPoint originalLocation = serverYacht.getLocation();
serverYacht.setLocation(
calculateBounceBack(serverYacht, originalLocation, BOUNCE_DISTANCE_YACHT)
);
serverYacht.setCurrentVelocity(
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
);
collidedYacht.setLocation(
calculateBounceBack(collidedYacht, originalLocation, BOUNCE_DISTANCE_YACHT)
);
collidedYacht.setCurrentVelocity(
collidedYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
);
notifyMessageListeners(
new YachtEventCodeMessage(serverYacht.getSourceId())
);
} else {
Mark collidedMark = markCollidedWith(serverYacht);
if (collidedMark != null) {
serverYacht.setLocation(
calculateBounceBack(serverYacht, collidedMark, BOUNCE_DISTANCE_MARK)
);
serverYacht.setCurrentVelocity(
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
);
notifyMessageListeners(
new YachtEventCodeMessage(serverYacht.getSourceId())
);
}
}
}
private void updateVelocity(ServerYacht yacht) { private void updateVelocity(ServerYacht yacht) {
Double velocity = yacht.getCurrentVelocity(); Double velocity = yacht.getCurrentVelocity();
Double trueWindAngle = Math.abs(windDirection - yacht.getHeading()); Double trueWindAngle = Math.abs(windDirection - yacht.getHeading());
@@ -235,7 +287,7 @@ public class GameState implements Runnable {
// TODO: 15/08/17 remove magic numbers from these equations. // TODO: 15/08/17 remove magic numbers from these equations.
if (yacht.getSailIn()) { if (yacht.getSailIn()) {
if (velocity < maxBoatSpeed - 500) { if (velocity < maxBoatSpeed - 500) {
yacht.changeVelocity(maxBoatSpeed / 150); yacht.changeVelocity(maxBoatSpeed / 100);
} else if (velocity > maxBoatSpeed + 500) { } else if (velocity > maxBoatSpeed + 500) {
yacht.changeVelocity(-maxBoatSpeed / 100); yacht.changeVelocity(-maxBoatSpeed / 100);
} else { } else {
@@ -317,6 +369,7 @@ public class GameState implements Runnable {
} }
} }
/** /**
* If we pass the start line gate in the correct direction, progress * If we pass the start line gate in the correct direction, progress
* *
@@ -449,6 +502,50 @@ public class GameState implements Runnable {
return false; return false;
} }
private static Mark markCollidedWith(ServerYacht yacht) {
Set<Mark> marksInRace = GameState.getMarks();
for (Mark mark : marksInRace) {
if (GeoUtility.getDistance(yacht.getLocation(), mark)
<= MARK_COLLISION_DISTANCE) {
return mark;
}
}
return null;
}
/**
* Calculate the new position of the boat after it has had a collision
*
* @return The boats new position
*/
private static GeoPoint calculateBounceBack(ServerYacht yacht, GeoPoint collidedWith, Double bounceDistance) {
Double heading = GeoUtility.getBearing(yacht.getLocation(), collidedWith);
// Invert heading
heading -= 180;
Integer newHeading = Math.floorMod(heading.intValue(), 360);
return GeoUtility.getGeoCoordinate(yacht.getLocation(), newHeading.doubleValue(), bounceDistance);
}
/**
* Collision detection which iterates through all the yachts and check if any yacht collided
* with this yacht. Return collided yacht or null if no collision.
*
* @return yacht to compare to all other yachts.
*/
private static ServerYacht checkCollision(ServerYacht yacht) {
for (ServerYacht otherYacht : GameState.getYachts().values()) {
if (otherYacht != yacht) {
Double distance = GeoUtility.getDistance(otherYacht.getLocation(), yacht.getLocation());
if (distance < YACHT_COLLISION_DISTANCE) {
return otherYacht;
}
}
}
return null;
}
private void sendMarkRoundingMessage(ServerYacht yacht) { private void sendMarkRoundingMessage(ServerYacht yacht) {
Integer sourceID = yacht.getSourceId(); Integer sourceID = yacht.getSourceId();
Integer currentMarkSeqID = yacht.getCurrentMarkSeqID(); Integer currentMarkSeqID = yacht.getCurrentMarkSeqID();
@@ -461,11 +558,14 @@ public class GameState implements Runnable {
sourceID, RoundingBoatStatus.RACING, roundingMark.getRoundingSide(), markType, sourceID, RoundingBoatStatus.RACING, roundingMark.getRoundingSide(), markType,
roundingMark.getSourceID()); roundingMark.getSourceID());
for (MarkPassingListener mpl : markListeners) { notifyMessageListeners(markRoundingMessage);
mpl.markPassing(markRoundingMessage);
}
} }
private static void notifyMessageListeners(Message message) {
for (NewMessageListener mpl : markListeners) {
mpl.notify(message);
}
}
private void logMarkRounding(ServerYacht yacht) { private void logMarkRounding(ServerYacht yacht) {
Mark roundingMark = yacht.getClosestCurrentMark(); Mark roundingMark = yacht.getClosestCurrentMark();
@@ -475,7 +575,7 @@ public class GameState implements Runnable {
} }
public static void addMarkPassListener(MarkPassingListener listener) { public static void addMarkPassListener(NewMessageListener listener) {
markListeners.add(listener); markListeners.add(listener);
} }
} }
@@ -4,18 +4,22 @@ import java.io.IOException;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Observable;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import seng302.gameServer.server.messages.Message; import seng302.gameServer.server.messages.Message;
import seng302.model.GeoPoint;
import seng302.model.Player; import seng302.model.Player;
import seng302.model.PolarTable; import seng302.model.PolarTable;
import seng302.model.ServerYacht;
import seng302.model.mark.CompoundMark;
import seng302.utilities.GeoUtility;
import seng302.visualiser.GameClient;
/** /**
* 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
* Created by wmu16 on 13/07/17. * Created by wmu16 on 13/07/17.
*/ */
public class MainServerThread extends Observable implements Runnable, ClientConnectionDelegate{ public class MainServerThread implements Runnable, ClientConnectionDelegate {
private static final int PORT = 4942; private static final int PORT = 4942;
private static final Integer CLIENT_UPDATES_PER_SECOND = 10; private static final Integer CLIENT_UPDATES_PER_SECOND = 10;
@@ -27,6 +31,8 @@ public class MainServerThread extends Observable implements Runnable, ClientConn
private ServerSocket serverSocket = null; private ServerSocket serverSocket = null;
private ArrayList<ServerToClientThread> serverToClientThreads = new ArrayList<>(); private ArrayList<ServerToClientThread> serverToClientThreads = new ArrayList<>();
private GameClient gameClient;
public MainServerThread() { public MainServerThread() {
new GameState("localhost"); new GameState("localhost");
try { try {
@@ -47,7 +53,6 @@ public class MainServerThread extends Observable implements Runnable, ClientConn
HeartbeatThread heartbeatThread; HeartbeatThread heartbeatThread;
serverListenThread = new ServerListenThread(serverSocket, this); serverListenThread = new ServerListenThread(serverSocket, this);
heartbeatThread = new HeartbeatThread(this); heartbeatThread = new HeartbeatThread(this);
heartbeatThread.start(); heartbeatThread.start();
@@ -98,14 +103,16 @@ public class MainServerThread extends Observable implements Runnable, ClientConn
} }
static void serverLog(String message, int logLevel){ static void serverLog(String message, int logLevel) {
if(logLevel <= LOG_LEVEL){ if (logLevel <= LOG_LEVEL) {
System.out.println("[SERVER " + LocalDateTime.now().toLocalTime().toString() + "] " + message); System.out.println(
"[SERVER " + LocalDateTime.now().toLocalTime().toString() + "] " + message);
} }
} }
/** /**
* A client has tried to connect to the server * A client has tried to connect to the server
*
* @param serverToClientThread The player that connected * @param serverToClientThread The player that connected
*/ */
@Override @Override
@@ -121,6 +128,7 @@ public class MainServerThread extends Observable implements Runnable, ClientConn
/** /**
* A player has left the game, remove the player from the GameState * A player has left the game, remove the player from the GameState
*
* @param player The player that left * @param player The player that left
*/ */
@Override @Override
@@ -142,11 +150,10 @@ public class MainServerThread extends Observable implements Runnable, ClientConn
} }
} }
serverToClientThreads.remove(closedConnection); serverToClientThreads.remove(closedConnection);
setChanged();
notifyObservers();
} }
public void startGame() { public void startGame() {
initialiseBoatPositions();
Timer t = new Timer(); Timer t = new Timer();
t.schedule(new TimerTask() { t.schedule(new TimerTask() {
@@ -163,4 +170,56 @@ public class MainServerThread extends Observable implements Runnable, ClientConn
public void terminate() { public void terminate() {
terminated = true; terminated = true;
} }
/**
* Pass GameClient to main server thread so it can access the properties inside.
*
* @param gameClient gameClient
*/
public void setGameClient(GameClient gameClient) {
this.gameClient = gameClient;
}
/**
* Initialise boats to specific spaced out geopoints behind starting line.
*/
private void initialiseBoatPositions() {
// Getting the start line compound marks
CompoundMark cm = gameClient.getCourseData().getCompoundMarks().get(1);
GeoPoint startMark1 = new GeoPoint(cm.getMarks().get(0).getLat(),
cm.getMarks().get(0).getLng());
GeoPoint startMark2 = new GeoPoint(cm.getMarks().get(1).getLat(),
cm.getMarks().get(1).getLng());
// Calculating midpoint
Double perpendicularAngle = GeoUtility.getBearing(startMark1, startMark2);
Double length = GeoUtility.getDistance(startMark1, startMark2);
GeoPoint midpoint = GeoUtility.getGeoCoordinate(startMark1, perpendicularAngle, length / 2);
// Setting each boats position side by side
double DISTANCE_FACTOR = 50.0; // distance apart in meters
int boatIndex = 0;
for (ServerYacht yacht : GameState.getYachts().values()) {
int distanceApart = boatIndex / 2;
if (boatIndex % 2 == 1 && boatIndex != 0) {
distanceApart++;
distanceApart *= -1;
}
GeoPoint spawnMark = GeoUtility
.getGeoCoordinate(midpoint, perpendicularAngle, distanceApart * DISTANCE_FACTOR);
if (yacht.getHeading() < perpendicularAngle) {
spawnMark = GeoUtility
.getGeoCoordinate(spawnMark, perpendicularAngle + 90, DISTANCE_FACTOR);
} else {
spawnMark = GeoUtility
.getGeoCoordinate(spawnMark, perpendicularAngle + 270, DISTANCE_FACTOR);
}
yacht.setLocation(spawnMark);
boatIndex++;
}
}
} }
@@ -19,9 +19,15 @@ import java.util.zip.CRC32;
import java.util.zip.Checksum; import java.util.zip.Checksum;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.gameServer.server.messages.YachtEventCodeMessage;
import seng302.model.Player;
import seng302.model.stream.packets.PacketType;
import seng302.model.stream.packets.StreamPacket;
import seng302.model.stream.xml.generator.Race;
import seng302.model.stream.xml.generator.Regatta;
import seng302.utilities.XMLGenerator;
import seng302.gameServer.server.messages.BoatAction; import seng302.gameServer.server.messages.BoatAction;
import seng302.gameServer.server.messages.BoatLocationMessage; import seng302.gameServer.server.messages.BoatLocationMessage;
import seng302.gameServer.server.messages.BoatStatus;
import seng302.gameServer.server.messages.BoatSubMessage; import seng302.gameServer.server.messages.BoatSubMessage;
import seng302.gameServer.server.messages.ClientType; import seng302.gameServer.server.messages.ClientType;
import seng302.gameServer.server.messages.Message; import seng302.gameServer.server.messages.Message;
@@ -32,13 +38,7 @@ import seng302.gameServer.server.messages.RegistrationResponseMessage;
import seng302.gameServer.server.messages.RegistrationResponseStatus; import seng302.gameServer.server.messages.RegistrationResponseStatus;
import seng302.gameServer.server.messages.XMLMessage; import seng302.gameServer.server.messages.XMLMessage;
import seng302.gameServer.server.messages.XMLMessageSubType; import seng302.gameServer.server.messages.XMLMessageSubType;
import seng302.model.Player;
import seng302.model.ServerYacht; import seng302.model.ServerYacht;
import seng302.model.stream.packets.PacketType;
import seng302.model.stream.packets.StreamPacket;
import seng302.model.stream.xml.generator.Race;
import seng302.model.stream.xml.generator.Regatta;
import seng302.utilities.XMLGenerator;
/** /**
* A class describing a single connection to a Client for the purposes of sending and receiving on * A class describing a single connection to a Client for the purposes of sending and receiving on
@@ -75,6 +75,8 @@ public class ServerToClientThread implements Runnable, Observer {
private List<ConnectionListener> connectionListeners = new ArrayList<>(); private List<ConnectionListener> connectionListeners = new ArrayList<>();
private ServerYacht yacht;
public ServerToClientThread(Socket socket) { public ServerToClientThread(Socket socket) {
this.socket = socket; this.socket = socket;
seqNo = 0; seqNo = 0;
@@ -167,7 +169,6 @@ public class ServerToClientThread implements Runnable, Observer {
int sync2; int sync2;
// TODO: 14/07/17 wmu16 - Work out how to fix this while loop // TODO: 14/07/17 wmu16 - Work out how to fix this while loop
while (socket.isConnected()) { while (socket.isConnected()) {
try { try {
@@ -346,6 +347,14 @@ public class ServerToClientThread implements Runnable, Observer {
return socket; return socket;
} }
public ServerYacht getYacht() {
return yacht;
}
public void sendCollisionMessage(Integer yachtId) {
sendMessage(new YachtEventCodeMessage(yachtId));
}
public void addConnectionListener(ConnectionListener listener) { public void addConnectionListener(ConnectionListener listener) {
connectionListeners.add(listener); connectionListeners.add(listener);
} }
@@ -0,0 +1,52 @@
package seng302.gameServer.server.messages;
/**
* Created by zyt10 on 10/08/17.
*/
public class YachtEventCodeMessage extends Message {
private final MessageType MESSAGE_TYPE = MessageType.YACHT_EVENT_CODE;
private final int MESSAGE_VERSION = 1; //Always set to 1
private final int MESSAGE_SIZE = 22;
// Message fields
private long timeStamp;
private long ack = 0x00; //Unused
private int raceId;
private int destSourceId;
private int incidentId;
private int eventId;
public YachtEventCodeMessage(Integer subjectId) {
timeStamp = System.currentTimeMillis() / 1000L;
ack = 0;
raceId = 1;
destSourceId = subjectId; // collision boat source id
incidentId = 0;
eventId = 33;
setHeader(new Header(MESSAGE_TYPE, 0x01, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
// Write message fields
putUnsignedByte((byte) MESSAGE_VERSION);
putInt((int) timeStamp, 6);
putInt((int) ack, 2);
putInt((int) raceId, 4);
putInt((int) destSourceId, 4);
putInt((int) incidentId, 4);
putInt((int) eventId, 1);
writeCRC();
rewind();
}
/**
* @return The length of this message
*/
public int getSize() {
return MESSAGE_SIZE;
}
}
+36 -461
View File
@@ -3,8 +3,9 @@ package seng302.model;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Observable;
import java.util.Observer;
import javafx.beans.property.ReadOnlyDoubleProperty; import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper; import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.ReadOnlyLongProperty; import javafx.beans.property.ReadOnlyLongProperty;
@@ -12,31 +13,23 @@ import javafx.beans.property.ReadOnlyLongWrapper;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.gameServer.GameState;
import seng302.model.mark.CompoundMark; import seng302.model.mark.CompoundMark;
import seng302.model.mark.Mark;
import seng302.utilities.GeoUtility;
/** /**
* Yacht class for the racing boat. * Yacht class for the racing boat. <p> 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
* Class created to store more variables (eg. boat statuses) compared to the XMLParser boat class, * not used anymore.
* also done outside Boat class because some old variables are not used anymore.
*/ */
public class ClientYacht { public class ClientYacht extends Observable {
@FunctionalInterface @FunctionalInterface
public interface YachtLocationListener { public interface YachtLocationListener {
void notifyLocation(ClientYacht clientYacht, double lat, double lon, double heading,
void notifyLocation(ClientYacht yacht, double lat, double lon, double heading, Boolean sailsIn, double velocity);
double velocity, boolean sailIn);
} }
private Logger logger = LoggerFactory.getLogger(ClientYacht.class); private Logger logger = LoggerFactory.getLogger(ClientYacht.class);
private static final Double ROUNDING_DISTANCE = 50d; // TODO: 3/08/17 wmu16 - Look into this value further
//BOTH AFAIK //BOTH AFAIK
private String boatType; private String boatType;
@@ -47,29 +40,15 @@ public class ClientYacht {
private String country; private String country;
private Long estimateTimeAtFinish; private Long estimateTimeAtFinish;
private Boolean sailIn = false;
private Integer currentMarkSeqID = 0; private Integer currentMarkSeqID = 0;
private Long markRoundTime; private Long markRoundTime;
private Double distanceToCurrentMark;
private Long timeTillNext; private Long timeTillNext;
private Double heading; private Double heading;
private Integer legNumber = 0; private Integer legNumber = 0;
//SERVER SIDE
public static final Double TURN_STEP = 5.0; //This should be in some utils class somewhere 2bh. Public for tests sake.
private Double lastHeading;
private Boolean sailIn = false;
private GeoPoint location; private GeoPoint location;
private Integer boatStatus; private Integer boatStatus;
private Double velocity; private Double currentVelocity;
private Boolean isAuto;
private Double autoHeading;
//MARK ROUNDING INFO
private GeoPoint lastLocation; //For purposes of mark rounding calculations
private Boolean hasEnteredRoundingZone; //The distance that the boat must be from the mark to round
private Boolean hasPassedLine;
private Boolean hasPassedThroughGate;
private Boolean finishedRace;
//CLIENT SIDE //CLIENT SIDE
private List<YachtLocationListener> locationListeners = new ArrayList<>(); private List<YachtLocationListener> locationListeners = new ArrayList<>();
@@ -79,7 +58,6 @@ public class ClientYacht {
private CompoundMark lastMarkRounded; private CompoundMark lastMarkRounded;
private Integer positionInt = 0; private Integer positionInt = 0;
private Color colour; private Color colour;
private Boolean clientSailsIn = true;
public ClientYacht(String boatType, Integer sourceId, String hullID, String shortName, public ClientYacht(String boatType, Integer sourceId, String hullID, String shortName,
String boatName, String country) { String boatName, String country) {
@@ -89,389 +67,19 @@ public class ClientYacht {
this.shortName = shortName; this.shortName = shortName;
this.boatName = boatName; this.boatName = boatName;
this.country = country; this.country = country;
this.sailIn = false;
this.isAuto = false;
this.location = new GeoPoint(57.670341, 11.826856); this.location = new GeoPoint(57.670341, 11.826856);
this.lastLocation = location;
this.heading = 120.0; //In degrees this.heading = 120.0; //In degrees
this.velocity = 0d; //in mms-1 this.currentVelocity = 0d;
this.boatStatus = 1;
this.hasEnteredRoundingZone = false;
this.hasPassedLine = false;
this.hasPassedThroughGate = false;
this.finishedRace = false;
} }
/** /**
* @param timeInterval since last update in milliseconds * Add ServerToClientThread as the observer, this observer pattern mainly server for the boat
* rounding package.
*/ */
public void update(Long timeInterval) { @Override
public void addObserver(Observer o) {
Double secondsElapsed = timeInterval / 1000000.0; super.addObserver(o);
Double windSpeedKnots = GameState.getWindSpeedKnots();
Double trueWindAngle = Math.abs(GameState.getWindDirection() - heading);
Double boatSpeedInKnots = PolarTable.getBoatSpeed(windSpeedKnots, trueWindAngle);
Double maxBoatSpeed = boatSpeedInKnots / 1.943844492 * 1000;
if (sailIn && velocity <= maxBoatSpeed && maxBoatSpeed != 0d) {
if (velocity < maxBoatSpeed) {
velocity += maxBoatSpeed / 15; // Acceleration
}
if (velocity > maxBoatSpeed) {
velocity = maxBoatSpeed; // Prevent the boats from exceeding top speed
}
} else { // Deceleration
if (velocity > 0d) {
if (maxBoatSpeed != 0d) {
velocity -= maxBoatSpeed / 600;
} else {
velocity -= velocity / 100;
}
if (velocity < 0) {
velocity = 0d;
}
}
}
runAutoPilot();
//UPDATE BOAT LOCATION
lastLocation = location;
location = GeoUtility.getGeoCoordinate(location, heading, velocity * secondsElapsed);
//CHECK FOR MARK ROUNDING
if (!finishedRace) {
checkForLegProgression();
}
// TODO: 3/08/17 wmu16 - Implement line cross check here
}
/**
* Calculates the distance to the next mark (closest of the two if a gate mark). For purposes
* of mark rounding
*
* @return A distance in metres. Returns -1 if there is no next mark
* @throws IndexOutOfBoundsException If the next mark is null (ie the last mark in the race)
* Check first using {@link seng302.model.mark.MarkOrder#isLastMark(Integer)}
*/
public Double calcDistanceToCurrentMark() throws IndexOutOfBoundsException {
CompoundMark nextMark = GameState.getMarkOrder().getCurrentMark(currentMarkSeqID);
if (nextMark.isGate()) {
Mark sub1 = nextMark.getSubMark(1);
Mark sub2 = nextMark.getSubMark(2);
Double distance1 = GeoUtility.getDistance(location, sub1);
Double distance2 = GeoUtility.getDistance(location, sub2);
return (distance1 < distance2) ? distance1 : distance2;
} else {
return GeoUtility.getDistance(location, nextMark.getSubMark(1));
}
}
/**
* 4 Different cases of progression in the race
* 1 - Passing the start line
* 2 - Passing any in-race Gate
* 3 - Passing any in-race Mark
* 4 - Passing the finish line
*/
private void checkForLegProgression() {
CompoundMark currentMark = GameState.getMarkOrder().getCurrentMark(currentMarkSeqID);
if (currentMarkSeqID == 0) {
checkStartLineCrossing(currentMark);
} else if (GameState.getMarkOrder().isLastMark(currentMarkSeqID)) {
checkFinishLineCrossing(currentMark);
} else if (currentMark.isGate()) {
checkGateRounding(currentMark);
} else {
checkMarkRounding(currentMark);
}
}
/**
* If we pass the start line gate in the correct direction, progress
*
* @param currentMark The current gate
*/
private void checkStartLineCrossing(CompoundMark currentMark) {
Mark mark1 = currentMark.getSubMark(1);
Mark mark2 = currentMark.getSubMark(2);
CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID);
Integer crossedLine = GeoUtility.checkCrossedLine(mark1, mark2, lastLocation, location);
if (crossedLine > 0) {
Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, nextMark.getMidPoint());
if (crossedLine == 2 && isClockwiseCross || crossedLine == 1 && !isClockwiseCross) {
currentMarkSeqID++;
logMarkRounding(currentMark);
}
}
}
/**
* This algorithm checks for mark rounding. And increments the currentMarSeqID number attribute
* of the yacht if so.
* A visual representation of this algorithm can be seen on the Wiki under
* 'mark passing algorithm'
*/
private void checkMarkRounding(CompoundMark currentMark) {
distanceToCurrentMark = calcDistanceToCurrentMark();
GeoPoint nextPoint = GameState.getMarkOrder().getNextMark(currentMarkSeqID).getMidPoint();
GeoPoint prevPoint = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID)
.getMidPoint();
GeoPoint midPoint = GeoUtility.getDirtyMidPoint(nextPoint, prevPoint);
//1 TEST FOR ENTERING THE ROUNDING DISTANCE
if (distanceToCurrentMark < ROUNDING_DISTANCE) {
hasEnteredRoundingZone = true;
}
//In case current mark is a gate, loop through all marks just in case
for (Mark thisCurrentMark : currentMark.getMarks()) {
if (GeoUtility.isPointInTriangle(lastLocation, location, midPoint, thisCurrentMark)) {
hasPassedLine = true;
}
}
if (hasPassedLine && hasEnteredRoundingZone) {
currentMarkSeqID++;
hasPassedLine = false;
hasEnteredRoundingZone = false;
hasPassedThroughGate = false;
logMarkRounding(currentMark);
}
}
/**
* Checks if a gate line has been crossed and in the correct direction
*
* @param currentMark The current gate
*/
private void checkGateRounding(CompoundMark currentMark) {
Mark mark1 = currentMark.getSubMark(1);
Mark mark2 = currentMark.getSubMark(2);
CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID);
CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID);
Integer crossedLine = GeoUtility.checkCrossedLine(mark1, mark2, lastLocation, location);
//We have crossed the line
if (crossedLine > 0) {
Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, prevMark.getMidPoint());
//Check we cross the line in the correct direction
if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) {
hasPassedThroughGate = true;
}
}
Boolean prevMarkSide = GeoUtility.isClockwise(mark1, mark2, prevMark.getMidPoint());
Boolean nextMarkSide = GeoUtility.isClockwise(mark1, mark2, nextMark.getMidPoint());
if (hasPassedThroughGate) {
//Check if we need to round this gate after passing through
if (prevMarkSide == nextMarkSide) {
checkMarkRounding(currentMark);
} else {
currentMarkSeqID++;
logMarkRounding(currentMark);
}
}
}
/**
* If we pass the finish gate in the correct direction
*
* @param currentMark The current gate
*/
private void checkFinishLineCrossing(CompoundMark currentMark) {
Mark mark1 = currentMark.getSubMark(1);
Mark mark2 = currentMark.getSubMark(2);
CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID);
Integer crossedLine = GeoUtility.checkCrossedLine(mark1, mark2, lastLocation, location);
if (crossedLine > 0) {
Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, prevMark.getMidPoint());
if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) {
currentMarkSeqID++;
finishedRace = true;
logMarkRounding(currentMark);
logger.debug(sourceId + " finished");
// TODO: 8/08/17 wmu16 - Do something!
}
}
}
/**
* Adjusts the heading of the boat by a given amount, while recording the boats
* last heading.
*
* @param amount the amount by which to adjust the boat heading.
*/
public void adjustHeading(Double amount) {
Double newVal = heading + amount;
lastHeading = heading;
heading = (double) Math.floorMod(newVal.longValue(), 360L);
}
/**
* Swaps the boats direction from one side of the wind to the other.
*/
public void tackGybe(Double windDirection) {
if (isAuto) {
disableAutoPilot();
} else {
Double normalizedHeading = normalizeHeading();
Double newVal = (-2 * normalizedHeading) + heading;
Double newHeading = (double) Math.floorMod(newVal.longValue(), 360L);
setAutoPilot(newHeading);
}
}
/**
* Enables the boats auto pilot feature, which will move the boat towards a given heading.
*
* @param thisHeading The heading to move the boat towards.
*/
private void setAutoPilot(Double thisHeading) {
isAuto = true;
autoHeading = thisHeading;
}
/**
* Disables the auto pilot function.
*/
public void disableAutoPilot() {
isAuto = false;
}
/**
* Moves the boat towards the given heading when the auto pilot was set. Disables the auto pilot
* in the event that the boat is within the range of 1 turn step of its goal.
*/
public void runAutoPilot() {
if (isAuto) {
turnTowardsHeading(autoHeading);
if (Math.abs(heading - autoHeading)
<= TURN_STEP) { //Cancel when within 1 turn step of target.
isAuto = false;
}
}
}
public void toggleSailIn() {
sailIn = !sailIn;
}
public void turnUpwind() {
disableAutoPilot();
Double normalizedHeading = normalizeHeading();
if (normalizedHeading == 0) {
if (lastHeading < 180) {
adjustHeading(-TURN_STEP);
} else {
adjustHeading(TURN_STEP);
}
} else if (normalizedHeading == 180) {
if (lastHeading < 180) {
adjustHeading(TURN_STEP);
} else {
adjustHeading(-TURN_STEP);
}
} else if (normalizedHeading < 180) {
adjustHeading(-TURN_STEP);
} else {
adjustHeading(TURN_STEP);
}
}
public void turnDownwind() {
disableAutoPilot();
Double normalizedHeading = normalizeHeading();
if (normalizedHeading == 0) {
if (lastHeading < 180) {
adjustHeading(TURN_STEP);
} else {
adjustHeading(-TURN_STEP);
}
} else if (normalizedHeading == 180) {
if (lastHeading < 180) {
adjustHeading(-TURN_STEP);
} else {
adjustHeading(TURN_STEP);
}
} else if (normalizedHeading < 180) {
adjustHeading(TURN_STEP);
} else {
adjustHeading(-TURN_STEP);
}
}
/**
* Takes the VMG from the polartable for upwind or downwind depending on the boats direction,
* and uses this to calculate a heading to move the yacht towards.
*/
public void turnToVMG() {
if (isAuto) {
disableAutoPilot();
} else {
Double normalizedHeading = normalizeHeading();
Double optimalHeading;
HashMap<Double, Double> optimalPolarMap;
if (normalizedHeading >= 90 && normalizedHeading <= 270) { // Downwind
optimalPolarMap = PolarTable.getOptimalDownwindVMG(GameState.getWindSpeedKnots());
} else {
optimalPolarMap = PolarTable.getOptimalUpwindVMG(GameState.getWindSpeedKnots());
}
optimalHeading = optimalPolarMap.keySet().iterator().next();
if (normalizedHeading > 180) {
optimalHeading = 360 - optimalHeading;
}
// Take optimal heading and turn into a boat heading rather than a wind heading.
optimalHeading =
optimalHeading + GameState.getWindDirection();
setAutoPilot(optimalHeading);
}
}
/**
* Takes a given heading and rotates the boat towards that heading.
* This does not care about being upwind or downwind, just which direction will reach a given
* heading faster.
*
* @param newHeading The heading to turn the yacht towards.
*/
private void turnTowardsHeading(Double newHeading) {
Double newVal = heading - newHeading;
if (Math.floorMod(newVal.longValue(), 360L) > 180) {
adjustHeading(TURN_STEP / 5);
} else {
adjustHeading(-TURN_STEP / 5);
}
}
/**
* Returns a heading normalized for the wind direction. Heading direction into the wind is 0,
* directly away is 180.
*
* @return The normalized heading accounting for wind direction.
*/
private Double normalizeHeading() {
Double normalizedHeading = heading - GameState.windDirection;
normalizedHeading = (double) Math.floorMod(normalizedHeading.longValue(), 360L);
return normalizedHeading;
} }
public String getBoatType() { public String getBoatType() {
@@ -521,9 +129,6 @@ public class ClientYacht {
} }
public void setLegNumber(Integer legNumber) { public void setLegNumber(Integer legNumber) {
// if (colour != null && position != "-" && legNumber != this.legNumber) {
// RaceViewController.updateYachtPositionSparkline(this, legNumber);
// }
this.legNumber = legNumber; this.legNumber = legNumber;
} }
@@ -560,18 +165,10 @@ public class ClientYacht {
return velocityProperty.getReadOnlyProperty(); return velocityProperty.getReadOnlyProperty();
} }
public double getVelocityMMS() {
return velocity;
}
public ReadOnlyLongProperty timeTillNextProperty() { public ReadOnlyLongProperty timeTillNextProperty() {
return timeTillNextProperty.getReadOnlyProperty(); return timeTillNextProperty.getReadOnlyProperty();
} }
public Double getVelocityKnots() {
return velocity / 1000 * 1.943844492; // TODO: 26/07/17 cir27 - remove magic number
}
public Long getTimeTillNext() { public Long getTimeTillNext() {
return timeTillNext; return timeTillNext;
} }
@@ -592,6 +189,11 @@ public class ClientYacht {
return location; return location;
} }
public void toggleSail() {
sailIn = !sailIn;
}
//// TODO: 15/08/17 asd
/** /**
* Sets the current location of the boat in lat and long whilst preserving the last location * Sets the current location of the boat in lat and long whilst preserving the last location
* *
@@ -599,8 +201,6 @@ public class ClientYacht {
* @param lng Longitude * @param lng Longitude
*/ */
public void setLocation(Double lat, Double lng) { public void setLocation(Double lat, Double lng) {
lastLocation.setLat(location.getLat());
lastLocation.setLng(location.getLng());
location.setLat(lat); location.setLat(lat);
location.setLng(lng); location.setLng(lng);
} }
@@ -613,10 +213,6 @@ public class ClientYacht {
this.heading = heading; this.heading = heading;
} }
public Boolean getSailIn() {
return sailIn;
}
@Override @Override
public String toString() { public String toString() {
return boatName; return boatName;
@@ -626,7 +222,7 @@ public class ClientYacht {
this.timeSinceLastMarkProperty.set(timeSinceLastMark); this.timeSinceLastMarkProperty.set(timeSinceLastMark);
} }
public ReadOnlyLongProperty timeSinceLastMarkProperty () { public ReadOnlyLongProperty timeSinceLastMarkProperty() {
return timeSinceLastMarkProperty.getReadOnlyProperty(); return timeSinceLastMarkProperty.getReadOnlyProperty();
} }
@@ -643,51 +239,30 @@ public class ClientYacht {
this.colour = colour; this.colour = colour;
} }
public void toggleClientSail() { // public Double getCurrentVelocity() {
clientSailsIn = !clientSailsIn; // return currentVelocity;
} // }
//
// public void setCurrentVelocity(Double currentVelocity) {
// this.currentVelocity = currentVelocity;
// }
public Double getVelocity() {
return velocity;
}
public void setVelocity(Double velocity) {
this.velocity = velocity;
}
public Double getDistanceToCurrentMark() {
return distanceToCurrentMark;
}
public Boolean getClientSailsIn() {
return clientSailsIn;
}
public void updateLocation(double lat, double lng, double heading, double velocity) { public void updateLocation(double lat, double lng, double heading, double velocity) {
setLocation(lat, lng); setLocation(lat, lng);
this.heading = heading; this.heading = heading;
this.velocity = velocity; // this.currentVelocity = velocity;
updateVelocityProperty(velocity); updateVelocityProperty(velocity);
for (YachtLocationListener yll : locationListeners) { for (YachtLocationListener yll : locationListeners) {
yll.notifyLocation(this, lat, lng, heading, velocity, clientSailsIn); yll.notifyLocation(this, lat, lng, heading, sailIn, velocity);
} }
} }
private void logMarkRounding(CompoundMark currentMark) {
String typeString = "mark";
if (currentMark.isGate()) {
typeString = "gate";
}
logger.debug(
String.format("BoatID %d passed %s %s with id %d. Now on leg %d",
sourceId,
typeString,
currentMark.getMarks().get(0).getName(),
currentMark.getId(),
currentMarkSeqID));
}
public void addLocationListener(YachtLocationListener listener) { public void addLocationListener(YachtLocationListener listener) {
locationListeners.add(listener); locationListeners.add(listener);
} }
public boolean getSailIn () {
return sailIn;
}
} }
+2 -2
View File
@@ -6,12 +6,12 @@ import javafx.scene.paint.Color;
* Enum for generating colours. * Enum for generating colours.
*/ */
public enum Colors { public enum Colors {
RED, PERU, SEAGREEN, GREEN, BLUE, PURPLE; RED, PERU, GOLD, GREEN, BLUE, PURPLE, DEEPPINK, GRAY;
static Integer index = 0; static Integer index = 0;
public static Color getColor() { public static Color getColor() {
if (index == 6) { if (index == 8) {
index = 0; index = 0;
} }
return Color.valueOf(values()[index++].toString()); return Color.valueOf(values()[index++].toString());
@@ -28,4 +28,9 @@ public class GeoPoint {
public void setLng(double lng) { public void setLng(double lng) {
this.lng = lng; this.lng = lng;
} }
@Override
public String toString() {
return "lat: " + lat + " lng: " + lng;
}
} }
@@ -92,6 +92,10 @@ public class ServerYacht extends Observable {
location = GeoUtility.getGeoCoordinate(location, heading, currentVelocity * secondsElapsed); location = GeoUtility.getGeoCoordinate(location, heading, currentVelocity * secondsElapsed);
} }
public void setLocation(GeoPoint geoPoint) {
location = geoPoint;
}
/** /**
* Add ServerToClientThread as the observer, this observer pattern mainly server for the boat * Add ServerToClientThread as the observer, this observer pattern mainly server for the boat
* rounding package. * rounding package.
@@ -386,4 +390,6 @@ public class ServerYacht extends Observable {
public Boolean hasPassedLine() { public Boolean hasPassedLine() {
return hasPassedLine; return hasPassedLine;
} }
} }
@@ -2,10 +2,6 @@ package seng302.model.mark;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
@@ -19,14 +15,15 @@ import seng302.model.stream.xml.generator.Race;
import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.utilities.XMLGenerator; import seng302.utilities.XMLGenerator;
import seng302.utilities.XMLParser; import seng302.utilities.XMLParser;
import java.util.*;
/** /**
* Class to hold the order of the marks in the race. * Class to hold the order of the marks in the race.
*/ */
public class MarkOrder { public class MarkOrder {
private List<CompoundMark> raceMarkOrder; private List<CompoundMark> raceMarkOrder;
private Logger logger = LoggerFactory.getLogger(MarkOrder.class); private Logger logger = LoggerFactory.getLogger(MarkOrder.class);
private Set<Mark> allMarks;
public MarkOrder(){ public MarkOrder(){
loadRaceProperties(); loadRaceProperties();
@@ -76,6 +73,10 @@ public class MarkOrder {
return raceMarkOrder.get(currentSeqID + 1); return raceMarkOrder.get(currentSeqID + 1);
} }
public Set<Mark> getAllMarks(){
return Collections.unmodifiableSet(allMarks);
}
/** /**
* Loads the race order from an XML string * Loads the race order from an XML string
* @param xml An AC35 RaceXML * @param xml An AC35 RaceXML
@@ -86,6 +87,7 @@ public class MarkOrder {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db; DocumentBuilder db;
Document doc; Document doc;
allMarks = new HashSet<>();
try { try {
db = dbf.newDocumentBuilder(); db = dbf.newDocumentBuilder();
@@ -107,6 +109,7 @@ public class MarkOrder {
CompoundMark compoundMark = marks.get(corner.getCompoundMarkID()); CompoundMark compoundMark = marks.get(corner.getCompoundMarkID());
compoundMark.setRoundingSide(RoundingSide.getRoundingSide(corner.getRounding())); compoundMark.setRoundingSide(RoundingSide.getRoundingSide(corner.getRounding()));
course.add(compoundMark); course.add(compoundMark);
allMarks.addAll(compoundMark.getMarks());
} }
return course; return course;
@@ -0,0 +1,34 @@
package seng302.model.stream.parser;
/**
* Stores parsed data from yacht event code packet
*/
public class YachtEventData {
private Long subjectId;
private Long incidentId;
private Integer eventId;
private Long timeStamp;
public YachtEventData(Long subjectId, Long incidentId, Integer eventId, Long timeStamp) {
this.subjectId = subjectId;
this.incidentId = incidentId;
this.eventId = eventId;
this.timeStamp = timeStamp;
}
public Long getSubjectId() {
return subjectId;
}
public Long getIncidentId() {
return incidentId;
}
public Integer getEventId() {
return eventId;
}
public Long getTimeStamp() {
return timeStamp;
}
}
@@ -13,15 +13,12 @@ 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.parser.MarkRoundingData; import seng302.model.stream.parser.*;
import seng302.model.stream.parser.PositionUpdateData;
import seng302.model.stream.parser.PositionUpdateData.DeviceType; import seng302.model.stream.parser.PositionUpdateData.DeviceType;
import seng302.model.stream.parser.RaceStartData;
import seng302.model.stream.parser.RaceStatusData;
/** /**
* 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 streaming
* streaming protocol, and parsing it into basic data types or collections. * protocol, and parsing it into basic data types or collections.
* *
* Created by kre39 on 23/04/17. * Created by kre39 on 23/04/17.
*/ */
@@ -34,8 +31,9 @@ public class StreamParser {
* @return the packet sequence number if the packet is of type HEARTBEAT, null otherwise. * @return the packet sequence number if the packet is of type HEARTBEAT, null otherwise.
*/ */
public static Long extractHeartBeat(StreamPacket packet) { public static Long extractHeartBeat(StreamPacket packet) {
if (packet.getType() != PacketType.HEARTBEAT) if (packet.getType() != PacketType.HEARTBEAT) {
return null; return null;
}
long heartbeat = bytesToLong(packet.getPayload()); long heartbeat = bytesToLong(packet.getPayload());
System.out.println("heartbeat = " + heartbeat); System.out.println("heartbeat = " + heartbeat);
return heartbeat; return heartbeat;
@@ -52,16 +50,17 @@ public class StreamParser {
* containing the parsed packet data. * containing the parsed packet data.
*/ */
public static RaceStatusData extractRaceStatus(StreamPacket packet) { public static RaceStatusData extractRaceStatus(StreamPacket packet) {
if (packet.getType() != PacketType.RACE_STATUS) if (packet.getType() != PacketType.RACE_STATUS) {
return null; return null;
}
byte[] payload = packet.getPayload(); byte[] payload = packet.getPayload();
int messageVersionNo = payload[0]; int messageVersionNo = payload[0];
long currentTime = bytesToLong(Arrays.copyOfRange(payload, 1, 7)); long currentTime = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
long raceId = bytesToLong(Arrays.copyOfRange(payload, 7, 11)); long raceId = bytesToLong(Arrays.copyOfRange(payload, 7, 11));
int raceStatus = payload[11]; int raceStatus = payload[11];
long expectedStartTime = bytesToLong(Arrays.copyOfRange(payload,12,18)); long expectedStartTime = bytesToLong(Arrays.copyOfRange(payload, 12, 18));
long windDir = bytesToLong(Arrays.copyOfRange(payload,18,20)); long windDir = bytesToLong(Arrays.copyOfRange(payload, 18, 20));
long rawWindSpeed = bytesToLong(Arrays.copyOfRange(payload,20,22)); long rawWindSpeed = bytesToLong(Arrays.copyOfRange(payload, 20, 22));
// DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); // DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
// currentTime = format.format((new Date(currentTime))) // currentTime = format.format((new Date(currentTime)))
@@ -70,7 +69,6 @@ public class StreamParser {
windDir, rawWindSpeed, raceStatus, currentTime, expectedStartTime windDir, rawWindSpeed, raceStatus, currentTime, expectedStartTime
); );
// long timeTillStart = // long timeTillStart =
// ((new Date(expectedStartTime)).getTime() - (new Date(currentTime)).getTime()) / 1000; // ((new Date(expectedStartTime)).getTime() - (new Date(currentTime)).getTime()) / 1000;
// //
@@ -139,8 +137,9 @@ public class StreamParser {
* DISPLAY_TEXT_MESSAGE. * DISPLAY_TEXT_MESSAGE.
*/ */
public static List<String> extractDisplayMessage(StreamPacket packet) { public static List<String> extractDisplayMessage(StreamPacket packet) {
if (packet.getType() != PacketType.DISPLAY_TEXT_MESSAGE) if (packet.getType() != PacketType.DISPLAY_TEXT_MESSAGE) {
return null; return null;
}
List<String> message = new ArrayList<>(); List<String> message = new ArrayList<>();
byte[] payload = packet.getPayload(); byte[] payload = packet.getPayload();
int messageVersionNo = payload[0]; int messageVersionNo = payload[0];
@@ -166,10 +165,11 @@ public class StreamParser {
* XML_MESSAGE. * XML_MESSAGE.
*/ */
public static Document extractXmlMessage(StreamPacket packet) { public static Document extractXmlMessage(StreamPacket packet) {
if ( packet.getType() != PacketType.RACE_XML && if (packet.getType() != PacketType.RACE_XML &&
packet.getType() != PacketType.REGATTA_XML && packet.getType() != PacketType.REGATTA_XML &&
packet.getType() != PacketType.BOAT_XML ) packet.getType() != PacketType.BOAT_XML) {
return null; return null;
}
byte[] payload = packet.getPayload(); byte[] payload = packet.getPayload();
int messageType = payload[9]; int messageType = payload[9];
@@ -194,8 +194,8 @@ public class StreamParser {
* Extracts the race start status from the packet and returns it as a long array. * Extracts the race start status from the packet and returns it as a long array.
* *
* @param packet Packet parsed in to use the payload * @param packet Packet parsed in to use the payload
* @return An array of form [raceID, raceStartTime, notificationType, timeStamp] or null if * @return An array of form [raceID, raceStartTime, notificationType, timeStamp] or null if the
* the packet type is not of RACE_START_STATUS. * packet type is not of RACE_START_STATUS.
*/ */
public static RaceStartData extractRaceStartStatus(StreamPacket packet) { public static RaceStartData extractRaceStartStatus(StreamPacket packet) {
if (packet.getType() != PacketType.RACE_START_STATUS) { if (packet.getType() != PacketType.RACE_START_STATUS) {
@@ -212,23 +212,25 @@ public class StreamParser {
/** /**
* Parses the the byte array in a StreamPacket for yacht events to retrieve the necessary info * Parses the the byte array in a StreamPacket for yacht events to retrieve the necessary info
* and returns it a an array of longs. * and returns it as YachtEventData.
* *
* @param packet Packet parsed in to use the payload * @param packet Packet parsed in to use the payload
* @return the event data in the form [boatID, incidentID, eventID, timeStamp]. Returns null if * @return the event data in the form of YachtEventData. Returns null if the packet is not of
* the packet is not of type YACHT_EVENT_CODE. * type YACHT_EVENT_CODE.
*/ */
public static long[] extractYachtEventCode(StreamPacket packet) { public static YachtEventData extractYachtEventCode(StreamPacket packet) {
if (packet.getType() != PacketType.YACHT_EVENT_CODE) if (packet.getType() != PacketType.YACHT_EVENT_CODE) {
return null; return null;
}
byte[] payload = packet.getPayload(); byte[] payload = packet.getPayload();
int messageVersionNo = payload[0]; int messageVersionNo = payload[0];
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7)); long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
long ackNumber = bytesToLong(Arrays.copyOfRange(payload, 7, 9));
long raceId = bytesToLong(Arrays.copyOfRange(payload, 9, 13)); long raceId = bytesToLong(Arrays.copyOfRange(payload, 9, 13));
long subjectId = bytesToLong(Arrays.copyOfRange(payload, 13, 17)); long subjectId = bytesToLong(Arrays.copyOfRange(payload, 13, 17));
long incidentId = bytesToLong(Arrays.copyOfRange(payload, 17, 21)); long incidentId = bytesToLong(Arrays.copyOfRange(payload, 17, 21));
int eventId = payload[21]; int eventId = payload[21];
return new long[] {subjectId, incidentId, eventId, timeStamp}; return new YachtEventData(subjectId, incidentId, eventId, timeStamp);
} }
/** /**
@@ -239,15 +241,16 @@ public class StreamParser {
* Returns null if the packet is not of type YACHT_ACTION_CODE. * Returns null if the packet is not of type YACHT_ACTION_CODE.
*/ */
public static long[] extractYachtActionCode(StreamPacket packet) { public static long[] extractYachtActionCode(StreamPacket packet) {
if (packet.getType() != PacketType.YACHT_ACTION_CODE) if (packet.getType() != PacketType.YACHT_ACTION_CODE) {
return null; return null;
}
byte[] payload = packet.getPayload(); byte[] payload = packet.getPayload();
int messageVersionNo = payload[0]; int messageVersionNo = payload[0];
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7)); long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
long subjectId = bytesToLong(Arrays.copyOfRange(payload, 9, 13)); long subjectId = bytesToLong(Arrays.copyOfRange(payload, 9, 13));
long incidentId = bytesToLong(Arrays.copyOfRange(payload, 13, 17)); long incidentId = bytesToLong(Arrays.copyOfRange(payload, 13, 17));
int eventId = payload[17]; int eventId = payload[17];
return new long[] {subjectId, incidentId, eventId, timeStamp}; return new long[]{subjectId, incidentId, eventId, timeStamp};
} }
/** /**
@@ -258,8 +261,9 @@ public class StreamParser {
* CHATTER_TEXT. * CHATTER_TEXT.
*/ */
public static String extractChatterText(StreamPacket packet) { public static String extractChatterText(StreamPacket packet) {
if (packet.getType() != PacketType.CHATTER_TEXT) if (packet.getType() != PacketType.CHATTER_TEXT) {
return null; return null;
}
byte[] payload = packet.getPayload(); byte[] payload = packet.getPayload();
int messageVersionNo = payload[0]; int messageVersionNo = payload[0];
int messageType = payload[1]; int messageType = payload[1];
@@ -276,8 +280,9 @@ public class StreamParser {
* is not of type BOAT_LOCATION. * is not of type BOAT_LOCATION.
*/ */
public static PositionUpdateData extractBoatLocation(StreamPacket packet) { public static PositionUpdateData extractBoatLocation(StreamPacket packet) {
if (packet.getType() != PacketType.BOAT_LOCATION) if (packet.getType() != PacketType.BOAT_LOCATION) {
return null; return null;
}
byte[] payload = packet.getPayload(); byte[] payload = packet.getPayload();
int deviceType = (int) payload[15]; int deviceType = (int) payload[15];
long timeValid = bytesToLong(Arrays.copyOfRange(payload, 1, 7)); long timeValid = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
@@ -293,10 +298,11 @@ public class StreamParser {
double groundSpeed = bytesToLong(Arrays.copyOfRange(payload, 38, 40)) / 1000.0; double groundSpeed = bytesToLong(Arrays.copyOfRange(payload, 38, 40)) / 1000.0;
DeviceType type; DeviceType type;
if (deviceType == 1) if (deviceType == 1) {
type = DeviceType.YACHT_TYPE; type = DeviceType.YACHT_TYPE;
else } else {
type = DeviceType.MARK_TYPE; type = DeviceType.MARK_TYPE;
}
return new PositionUpdateData((int) boatId, type, lat, lon, heading, groundSpeed); return new PositionUpdateData((int) boatId, type, lat, lon, heading, groundSpeed);
} }
@@ -309,8 +315,9 @@ public class StreamParser {
* if packet is not of type MARK_ROUNDING. * if packet is not of type MARK_ROUNDING.
*/ */
public static MarkRoundingData extractMarkRounding(StreamPacket packet) { public static MarkRoundingData extractMarkRounding(StreamPacket packet) {
if (packet.getType() != PacketType.MARK_ROUNDING) if (packet.getType() != PacketType.MARK_ROUNDING) {
return null; return null;
}
byte[] payload = packet.getPayload(); byte[] payload = packet.getPayload();
int messageVersionNo = payload[0]; int messageVersionNo = payload[0];
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7)); long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
@@ -325,16 +332,17 @@ public class StreamParser {
} }
/** /**
* Returns a list containing the string value of data within the given stream packet for * Returns a list containing the string value of data within the given stream packet for course
* course wind. * wind.
* *
* @param packet The packet containing the payload * @param packet The packet containing the payload
* @return the string values of the wind packet. Returns null if the packet is not of type * @return the string values of the wind packet. Returns null if the packet is not of type
* COURSE_WIND. * COURSE_WIND.
*/ */
public static List<String> extractCourseWind(StreamPacket packet) { public static List<String> extractCourseWind(StreamPacket packet) {
if (packet.getType() != PacketType.COURSE_WIND) if (packet.getType() != PacketType.COURSE_WIND) {
return null; return null;
}
byte[] payload = packet.getPayload(); byte[] payload = packet.getPayload();
int messageVersionNo = payload[0]; int messageVersionNo = payload[0];
int selectedWindId = payload[1]; int selectedWindId = payload[1];
@@ -366,13 +374,13 @@ public class StreamParser {
* Returns the parsed data from a StreamPacket for average wind data. * Returns the parsed data from a StreamPacket for average wind data.
* *
* @param packet The packet containing the payload * @param packet The packet containing the payload
* @return The wind data in the form * @return The wind data in the form [rawPeriod, rawSamplePeriod, period2, speed2, period3,
* [rawPeriod, rawSamplePeriod, period2, speed2, period3, speed3, period4, speed4, timestamp] * speed3, period4, speed4, timestamp] or null if the packet is not of type AVG_WIND.
* or null if the packet is not of type AVG_WIND.
*/ */
public static long[] extractAvgWind(StreamPacket packet) { public static long[] extractAvgWind(StreamPacket packet) {
if (packet.getType() != PacketType.AVG_WIND) if (packet.getType() != PacketType.AVG_WIND) {
return null; return null;
}
byte[] payload = packet.getPayload(); byte[] payload = packet.getPayload();
int messageVersionNo = payload[0]; int messageVersionNo = payload[0];
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7)); long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
@@ -384,7 +392,7 @@ public class StreamParser {
long speed3 = bytesToLong(Arrays.copyOfRange(payload, 17, 19)); long speed3 = bytesToLong(Arrays.copyOfRange(payload, 17, 19));
long period4 = bytesToLong(Arrays.copyOfRange(payload, 19, 21)); long period4 = bytesToLong(Arrays.copyOfRange(payload, 19, 21));
long speed4 = bytesToLong(Arrays.copyOfRange(payload, 21, 23)); long speed4 = bytesToLong(Arrays.copyOfRange(payload, 21, 23));
return new long[] { return new long[]{
rawPeriod, rawSamplePeriod, period2, speed2, period3, speed3, period4, speed4, timeStamp rawPeriod, rawSamplePeriod, period2, speed2, period3, speed3, period4, speed4, timeStamp
}; };
} }
@@ -410,8 +418,7 @@ public class StreamParser {
} }
/** /**
* takes an array of up to 7 bytes and returns a positive * takes an array of up to 7 bytes and returns a positive long constructed from the input bytes
* long constructed from the input bytes
* *
* @param bytes the byte array to conver to Long * @param bytes the byte array to conver to Long
* @return a positive long if there is less than 7 bytes -1 otherwise * @return a positive long if there is less than 7 bytes -1 otherwise
@@ -21,6 +21,7 @@ import seng302.model.stream.parser.MarkRoundingData;
import seng302.model.stream.parser.PositionUpdateData; import seng302.model.stream.parser.PositionUpdateData;
import seng302.model.stream.parser.PositionUpdateData.DeviceType; import seng302.model.stream.parser.PositionUpdateData.DeviceType;
import seng302.model.stream.parser.RaceStatusData; import seng302.model.stream.parser.RaceStatusData;
import seng302.model.stream.parser.YachtEventData;
import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.stream.xml.parser.RegattaXMLData; import seng302.model.stream.xml.parser.RegattaXMLData;
import seng302.utilities.StreamParser; import seng302.utilities.StreamParser;
@@ -102,6 +103,8 @@ public class GameClient {
loadStartScreen(); loadStartScreen();
} }
}); });
server.setGameClient(this);
} }
private void loadStartScreen() { private void loadStartScreen() {
@@ -126,7 +129,8 @@ public class GameClient {
* @return the lobby controller. * @return the lobby controller.
*/ */
private LobbyController loadLobby() { private LobbyController loadLobby() {
FXMLLoader fxmlLoader = new FXMLLoader(GameClient.class.getResource("/views/LobbyView.fxml")); FXMLLoader fxmlLoader = new FXMLLoader(
GameClient.class.getResource("/views/LobbyView.fxml"));
try { try {
holderPane.getChildren().clear(); holderPane.getChildren().clear();
holderPane.getChildren().add(fxmlLoader.load()); holderPane.getChildren().add(fxmlLoader.load());
@@ -155,6 +159,20 @@ public class GameClient {
raceView.loadRace(allBoatsMap, courseData, raceState, player); raceView.loadRace(allBoatsMap, courseData, raceState, player);
} }
private void loadFinishScreenView() {
FXMLLoader fxmlLoader = new FXMLLoader(
getClass().getResource("/views/FinishScreenView.fxml"));
try {
final Node finishScreenFX = fxmlLoader.load();
Platform.runLater(() -> {
holderPane.getChildren().clear();
holderPane.getChildren().add(finishScreenFX);
});
} catch (IOException e) {
e.printStackTrace();
}
}
private void parsePackets() { private void parsePackets() {
while (socketThread.getPacketQueue().peek() != null) { while (socketThread.getPacketQueue().peek() != null) {
StreamPacket packet = socketThread.getPacketQueue().poll(); StreamPacket packet = socketThread.getPacketQueue().poll();
@@ -205,14 +223,19 @@ public class GameClient {
case MARK_ROUNDING: case MARK_ROUNDING:
updateMarkRounding(StreamParser.extractMarkRounding(packet)); updateMarkRounding(StreamParser.extractMarkRounding(packet));
break; break;
case YACHT_EVENT_CODE:
showCollisionAlert(StreamParser.extractYachtEventCode(packet));
break;
} }
} }
} }
private void startRaceIfAllDataReceived() { private void startRaceIfAllDataReceived() {
if (allXMLReceived() && raceView == null) if (allXMLReceived() && raceView == null) {
loadRaceView(); loadRaceView();
} }
}
private boolean allXMLReceived() { private boolean allXMLReceived() {
return courseData != null && allBoatsMap != null && regattaData != null; return courseData != null && allBoatsMap != null && regattaData != null;
@@ -257,8 +280,19 @@ public class GameClient {
private void processRaceStatusUpdate(RaceStatusData data) { private void processRaceStatusUpdate(RaceStatusData data) {
if (allXMLReceived()) { if (allXMLReceived()) {
raceState.updateState(data); raceState.updateState(data);
if (raceView != null) if (raceView != null) {
raceView.getGameView().setWindDir(raceState.getWindDirection()); raceView.getGameView().setWindDir(raceState.getWindDirection());
}
boolean raceFinished = true;
for (ClientYacht yacht : allBoatsMap.values()) {
if (yacht.getBoatStatus() != 3) {
raceFinished = false;
}
}
if (raceFinished == true) {
loadFinishScreenView();
}
for (long[] boatData : data.getBoatData()) { for (long[] boatData : data.getBoatData()) {
ClientYacht clientYacht = allBoatsMap.get((int) boatData[0]); ClientYacht clientYacht = allBoatsMap.get((int) boatData[0]);
clientYacht.setEstimateTimeTillNextMark(raceState.getRaceTime() - boatData[1]); clientYacht.setEstimateTimeTillNextMark(raceState.getRaceTime() - boatData[1]);
@@ -313,11 +347,25 @@ public class GameClient {
//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
socketThread.sendBoatAction(BoatAction.SAILS_IN); socketThread.sendBoatAction(BoatAction.SAILS_IN);
raceView.getGameView().getPlayerYacht().toggleClientSail(); raceView.getGameView().getPlayerYacht().toggleSail();
break; break;
case PAGE_UP: case PAGE_UP:
case PAGE_DOWN: case PAGE_DOWN:
socketThread.sendBoatAction(BoatAction.MAINTAIN_HEADING); break; socketThread.sendBoatAction(BoatAction.MAINTAIN_HEADING); break;
} }
} }
public RaceXMLData getCourseData() {
return courseData;
}
/**
* Tells race view to show a collision animation.
*/
private void showCollisionAlert(YachtEventData yachtEventData) {
// 33 is the agreed code to show collision
if (yachtEventData.getEventId() == 33) {
raceView.showCollision(yachtEventData.getSubjectId());
}
}
} }
+99 -76
View File
@@ -7,23 +7,24 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javafx.animation.AnimationTimer; import javafx.animation.AnimationTimer;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Point2D; import javafx.geometry.Point2D;
import javafx.scene.Group; import javafx.scene.Group;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.paint.Paint; import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon; import javafx.scene.shape.Polygon;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import seng302.model.ClientYacht; import seng302.model.ClientYacht;
import javafx.util.Duration;
import seng302.model.Colors; import seng302.model.Colors;
import seng302.model.GeoPoint; import seng302.model.GeoPoint;
import seng302.model.Limit; import seng302.model.Limit;
@@ -112,7 +113,7 @@ public class GameView extends Pane {
VERTICAL VERTICAL
} }
public GameView () { public GameView() {
gameObjects = this.getChildren(); gameObjects = this.getChildren();
// create image view for map, bind panel size to image // create image view for map, bind panel size to image
gameObjects.add(mapImage); gameObjects.add(mapImage);
@@ -122,49 +123,10 @@ public class GameView extends Pane {
gameObjects.add(fpsDisplay); gameObjects.add(fpsDisplay);
gameObjects.add(raceBorder); gameObjects.add(raceBorder);
gameObjects.add(markers); gameObjects.add(markers);
//
// this.setOnKeyPressed(new EventHandler<KeyEvent>() {
// @Override public void handle(KeyEvent event) {
// event.consume();
// switch (event.getCode()) {
// case Z:
// scaleFactor = scaleFactor * 1.2;
// break;
// case X:
// scaleFactor = scaleFactor * 0.8;
// break;
// }
// if (event.getCode() == KeyCode.Z || event.getCode() == KeyCode.X) {
// for (Node child : getChildren()) {
// child.setScaleX(child.getScaleX() * scaleFactor);
// child.setScaleY(child.getScaleY() * scaleFactor);
// }
// }
// }
// });
//
// this.setOnScroll(new EventHandler<ScrollEvent>() {
// @Override public void handle(ScrollEvent event) {
// event.consume();
// if (event.getDeltaY() == 0) {
// return;
// }
//
// double scaleFactor =
// (event.getDeltaY() > 0)
// ? SCALE_DELTA
// : 1/SCALE_DELTA;
// for (Node child : getChildren()) {
// child.setScaleX(child.getScaleX() * scaleFactor);
// child.setScaleY(child.getScaleY() * scaleFactor);
// }
// }
// });
initializeTimer(); initializeTimer();
} }
private void initializeTimer () { private void initializeTimer() {
Arrays.fill(frameTimes, 1_000_000_000 / 60); Arrays.fill(frameTimes, 1_000_000_000 / 60);
timer = new AnimationTimer() { timer = new AnimationTimer() {
private long lastTime = 0; private long lastTime = 0;
@@ -207,8 +169,8 @@ public class GameView extends Pane {
} }
/** /**
* First find the top right and bottom left points' geo locations, then retrieve * First find the top right and bottom left points' geo locations, then retrieve map from google
* map from google to display on image view. - Haoming 22/5/2017 * to display on image view. - Haoming 22/5/2017
*/ */
private void drawGoogleMap() { private void drawGoogleMap() {
findMetersPerPixel(); findMetersPerPixel();
@@ -266,13 +228,23 @@ public class GameView extends Pane {
for (Mark mark : cMark.getMarks()) { for (Mark mark : cMark.getMarks()) {
makeAndBindMarker(mark, colour); makeAndBindMarker(mark, colour);
} }
//UNCOMMENT THIS TO HIGHLIGHT SUBMARKS 1 and 2 RED AND GREEN RESPECTIVELY FOR DEBUG
//(instead of above for loop)
// for (Mark mark : cMark.getMarks()) {
// if (mark.getSeqID() == 1) {
// makeAndBindMarker(mark, Color.RED);
// } else {
// makeAndBindMarker(mark, Color.GREEN);
// }
// }
//Create gate line //Create gate line
if (cMark.isGate()) { if (cMark.isGate()) {
for (int i = 1; i < cMark.getMarks().size(); i++) { for (int i = 1; i < cMark.getMarks().size(); i++) {
gates.add( gates.add(
makeAndBindGate( makeAndBindGate(
markerObjects.get(cMark.getSubMark(i)), markerObjects.get(cMark.getSubMark(i)),
markerObjects.get(cMark.getSubMark(i+1)), markerObjects.get(cMark.getSubMark(i + 1)),
colour colour
) )
); );
@@ -389,7 +361,7 @@ public class GameView extends Pane {
boatObjectGroup.getChildren().add(newBoat); boatObjectGroup.getChildren().add(newBoat);
trails.getChildren().add(newBoat.getTrail()); trails.getChildren().add(newBoat.getTrail());
// TODO: 1/08/17 Make this less vile to look at. // TODO: 1/08/17 Make this less vile to look at.
clientYacht.addLocationListener((boat, lat, lon, heading, velocity, sailIn) -> { clientYacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> {
BoatObject bo = boatObjects.get(boat); BoatObject bo = boatObjects.get(boat);
Point2D p2d = findScaledXY(lat, lon); Point2D p2d = findScaledXY(lat, lon);
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir); bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
@@ -443,25 +415,25 @@ public class GameView extends Pane {
annotations.put(clientYacht, newAnnotation); annotations.put(clientYacht, newAnnotation);
} }
private void drawFps(Double fps){ private void drawFps(Double fps) {
Platform.runLater(() -> fpsDisplay.setText(String.format("%d FPS", Math.round(fps)))); Platform.runLater(() -> fpsDisplay.setText(String.format("%d FPS", Math.round(fps))));
} }
/** /**
* Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the point * Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the point with
* with the leftmost point, rightmost point, southern most point and northern most point * the leftmost point, rightmost point, southern most point and northern most point
* respectively. * respectively.
*/ */
private void findMinMaxPoint(List<GeoPoint> points) { private void findMinMaxPoint(List<GeoPoint> points) {
List<GeoPoint> sortedPoints = new ArrayList<>(points); List<GeoPoint> sortedPoints = new ArrayList<>(points);
sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLat)); sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLat));
minLatPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng()); minLatPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng());
GeoPoint maxLat = sortedPoints.get(sortedPoints.size()-1); GeoPoint maxLat = sortedPoints.get(sortedPoints.size() - 1);
maxLatPoint = new GeoPoint(maxLat.getLat(), maxLat.getLng()); maxLatPoint = new GeoPoint(maxLat.getLat(), maxLat.getLng());
sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLng)); sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLng));
minLonPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng()); minLonPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng());
GeoPoint maxLon = sortedPoints.get(sortedPoints.size()-1); GeoPoint maxLon = sortedPoints.get(sortedPoints.size() - 1);
maxLonPoint = new GeoPoint(maxLon.getLat(), maxLon.getLng()); maxLonPoint = new GeoPoint(maxLon.getLat(), maxLon.getLng());
if (maxLonPoint.getLng() - minLonPoint.getLng() > 180) { if (maxLonPoint.getLng() - minLonPoint.getLng() > 180) {
horizontalInversion = true; horizontalInversion = true;
@@ -483,13 +455,17 @@ public class GameView extends Pane {
referenceAngle = Math.abs( referenceAngle = Math.abs(
GeoUtility.getBearingRad(referencePoint, minLonPoint) GeoUtility.getBearingRad(referencePoint, minLonPoint)
); );
referencePointX = bufferSize + distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility.getDistance(referencePoint, minLonPoint); referencePointX =
bufferSize + distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility
.getDistance(referencePoint, minLonPoint);
referenceAngle = Math.abs(GeoUtility.getDistance(referencePoint, maxLatPoint)); referenceAngle = Math.abs(GeoUtility.getDistance(referencePoint, maxLatPoint));
referencePointY = canvasHeight - (bufferSize + bufferSize); referencePointY = canvasHeight - (bufferSize + bufferSize);
referencePointY -= distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility.getDistance(referencePoint, maxLatPoint); referencePointY -= distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility
.getDistance(referencePoint, maxLatPoint);
referencePointY = referencePointY / 2; referencePointY = referencePointY / 2;
referencePointY += bufferSize; referencePointY += bufferSize;
referencePointY += distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility.getDistance(referencePoint, maxLatPoint); referencePointY += distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility
.getDistance(referencePoint, maxLatPoint);
} else { } else {
referencePointY = canvasHeight - bufferSize; referencePointY = canvasHeight - bufferSize;
referenceAngle = Math.abs( referenceAngle = Math.abs(
@@ -498,10 +474,13 @@ public class GameView extends Pane {
) )
); );
referencePointX = bufferSize; referencePointX = bufferSize;
referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility.getDistance(referencePoint, minLonPoint); referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility
referencePointX += ((canvasWidth - (bufferSize + bufferSize)) - (minLonToMaxLon * distanceScaleFactor)) / 2; .getDistance(referencePoint, minLonPoint);
referencePointX +=
((canvasWidth - (bufferSize + bufferSize)) - (minLonToMaxLon * distanceScaleFactor))
/ 2;
} }
if(horizontalInversion) { if (horizontalInversion) {
referencePointX = canvasWidth - bufferSize - (referencePointX - bufferSize); referencePointX = canvasWidth - bufferSize - (referencePointX - bufferSize);
} }
} }
@@ -545,7 +524,7 @@ public class GameView extends Pane {
return findScaledXY(unscaled.getLat(), unscaled.getLng()); return findScaledXY(unscaled.getLat(), unscaled.getLng());
} }
private Point2D findScaledXY (double unscaledLat, double unscaledLon) { private Point2D findScaledXY(double unscaledLat, double unscaledLon) {
double distanceFromReference; double distanceFromReference;
double angleFromReference; double angleFromReference;
double xAxisLocation = referencePointX; double xAxisLocation = referencePointX;
@@ -558,22 +537,30 @@ public class GameView extends Pane {
minLatPoint, new GeoPoint(unscaledLat, unscaledLon) minLatPoint, new GeoPoint(unscaledLat, unscaledLon)
); );
if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) { if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) {
xAxisLocation += Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); xAxisLocation += Math
yAxisLocation -= Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); .round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
yAxisLocation -= Math
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
} else if (angleFromReference >= 0) { } else if (angleFromReference >= 0) {
angleFromReference = angleFromReference - Math.PI / 2; angleFromReference = angleFromReference - Math.PI / 2;
xAxisLocation += Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); xAxisLocation += Math
yAxisLocation += Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); .round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
yAxisLocation += Math
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
} else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) { } else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) {
angleFromReference = Math.abs(angleFromReference); angleFromReference = Math.abs(angleFromReference);
xAxisLocation -= Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); xAxisLocation -= Math
yAxisLocation -= Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); .round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
yAxisLocation -= Math
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
} else { } else {
angleFromReference = Math.abs(angleFromReference) - Math.PI / 2; angleFromReference = Math.abs(angleFromReference) - Math.PI / 2;
xAxisLocation -= Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); xAxisLocation -= Math
yAxisLocation += Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); .round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
yAxisLocation += Math
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
} }
if(horizontalInversion) { if (horizontalInversion) {
xAxisLocation = canvasWidth - bufferSize - (xAxisLocation - bufferSize); xAxisLocation = canvasWidth - bufferSize - (xAxisLocation - bufferSize);
} }
return new Point2D(xAxisLocation, yAxisLocation); return new Point2D(xAxisLocation, yAxisLocation);
@@ -600,7 +587,7 @@ public class GameView extends Pane {
metersPerPixelY = dVertical / dy; metersPerPixelY = dVertical / dy;
} }
public void setAnnotationVisibilities (boolean teamName, boolean velocity, boolean estTime, public void setAnnotationVisibilities(boolean teamName, boolean velocity, boolean estTime,
boolean legTime, boolean trail, boolean wake) { boolean legTime, boolean trail, boolean wake) {
for (BoatObject boatObject : boatObjects.values()) { for (BoatObject boatObject : boatObjects.values()) {
boatObject.setVisibility(teamName, velocity, estTime, legTime, trail, wake); boatObject.setVisibility(teamName, velocity, estTime, legTime, trail, wake);
@@ -613,7 +600,7 @@ public class GameView extends Pane {
} }
} }
public void setFPSVisibility (boolean visibility) { public void setFPSVisibility(boolean visibility) {
fpsDisplay.setVisible(visibility); fpsDisplay.setVisible(visibility);
} }
@@ -623,7 +610,7 @@ public class GameView extends Pane {
); );
} }
public void pauseRace () { public void pauseRace() {
timer.stop(); timer.stop();
} }
@@ -632,8 +619,7 @@ public class GameView extends Pane {
this.windDir = windDir; this.windDir = windDir;
} }
public void startRace() {
public void startRace () {
timer.start(); timer.start();
} }
@@ -641,9 +627,10 @@ public class GameView extends Pane {
return playerYacht; return playerYacht;
} }
public void setBoatAsPlayer (ClientYacht playerYacht) { public void setBoatAsPlayer (ClientYacht playerYacht) {
this.playerYacht = playerYacht; this.playerYacht = playerYacht;
this.playerYacht.toggleClientSail(); this.playerYacht.toggleSail();
boatObjects.get(playerYacht).setAsPlayer(); boatObjects.get(playerYacht).setAsPlayer();
annotations.get(playerYacht).addAnnotation( annotations.get(playerYacht).addAnnotation(
"velocity", "velocity",
@@ -657,4 +644,40 @@ public class GameView extends Pane {
gameObjects.add(annotations.get(playerYacht)); gameObjects.add(annotations.get(playerYacht));
}); });
} }
/**
* Given yacht geopoint by race view controller, drawCollision will calculate canvas X and Y and
* display a flashing red circle on collision point.
*
* @param collisionPoint yacht collision point
*/
public void drawCollision(GeoPoint collisionPoint) {
Platform.runLater(() -> {
Point2D point = findScaledXY(collisionPoint);
double circleRadius = 0.0;
Circle circle = new Circle(point.getX(), point.getY(), circleRadius, Color.RED);
gameObjects.add(circle);
circle.setFill(Color.TRANSPARENT);
circle.setStroke(Color.RED);
circle.setStrokeWidth(3);
Timeline timeline = new Timeline();
timeline.setCycleCount(1);
KeyFrame keyframe1 = new KeyFrame(Duration.ZERO,
new KeyValue(circle.radiusProperty(), 0),
new KeyValue(circle.strokeProperty(), Color.TRANSPARENT));
KeyFrame keyFrame2 = new KeyFrame(new Duration(1000),
new KeyValue(circle.radiusProperty(), 50),
new KeyValue(circle.strokeProperty(), Color.RED));
KeyFrame keyFrame3 = new KeyFrame(new Duration(1500),
new KeyValue(circle.strokeProperty(), Color.TRANSPARENT));
timeline.getKeyFrames().addAll(keyframe1, keyFrame2, keyFrame3);
timeline.play();
timeline.setOnFinished(event -> gameObjects.remove(circle));
});
}
} }
@@ -79,7 +79,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private GameView gameView; private GameView gameView;
private RaceState raceState; private RaceState raceState;
private Timeline timerTimeline;
private Timer timer = new Timer(); private Timer timer = new Timer();
private List<Series<String, Double>> sparkLineData = new ArrayList<>(); private List<Series<String, Double>> sparkLineData = new ArrayList<>();
private ImportantAnnotationsState importantAnnotations; private ImportantAnnotationsState importantAnnotations;
@@ -209,9 +208,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/** /**
* Used to add any new yachts 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() {
// TODO: 2/08/17 there is about 0 chance of this working. Once we are keeping track of boat positions it can be fixed. // TODO: 2/08/17 there is about 0 chance of this working. Once we are keeping track of boat positions it can be fixed.
// Collect the racing yachts that aren't already in the chart // Collect the racing yachts that aren't already in the chart
sparkLineData.clear(); sparkLineData.clear();
@@ -234,9 +234,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
// 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)
sparkLineData.sort((o1, o2) -> { sparkLineData.sort((o1, o2) -> {
Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size()-1).getXValue()); Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size() - 1).getXValue());
Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size()-1).getXValue()); Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size() - 1).getXValue());
if (leg2 < leg1){ if (leg2 < leg1) {
return 1; return 1;
} else { } else {
return -1; return -1;
@@ -249,7 +249,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
.filter(spark -> !raceSparkLine.getData().contains(spark)) .filter(spark -> !raceSparkLine.getData().contains(spark))
.forEach(spark -> { .forEach(spark -> {
raceSparkLine.getData().add(spark); raceSparkLine.getData().add(spark);
spark.getNode().lookup(".chart-series-line").setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName())); spark.getNode().lookup(".chart-series-line")
.setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName()));
}) })
); );
} }
@@ -285,26 +286,27 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/** /**
* gets the rgb string of the yachts colour to use for the chart via css * gets the rgb string of the yachts colour to use for the chart via css
*
* @param yachtId id of yacht passed in to get the yachts 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 yachtId){ private String getBoatColorAsRGB(String yachtId) {
Color color = participants.get(Integer.valueOf(yachtId)).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);
} }
return String.format( "#%02X%02X%02X", return String.format("#%02X%02X%02X",
(int)( color.getRed() * 255 ), (int) (color.getRed() * 255),
(int)( color.getGreen() * 255 ), (int) (color.getGreen() * 255),
(int)( color.getBlue() * 255 ) (int) (color.getBlue() * 255)
); );
} }
/** /**
* Initialises a timer which updates elements of the RaceView such as wind direction, yacht * 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
* Updates of each of these attributes are called ONCE EACH SECOND * each of these attributes are called ONCE EACH SECOND
*/ */
private void initializeUpdateTimer() { private void initializeUpdateTimer() {
timer.scheduleAtFixedRate(new TimerTask() { timer.scheduleAtFixedRate(new TimerTask() {
@@ -319,9 +321,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
} }
/** /**
* Iterates over all corners until ones SeqID matches with the yachts current leg number. * Iterates over all corners until ones SeqID matches with the yachts current leg number. Then
* Then it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark * it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark Returns
* Returns null if no next mark found. * 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
*/ */
@@ -396,7 +399,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
textToAdd.setStyle(""); textToAdd.setStyle("");
vboxEntries.add(textToAdd); vboxEntries.add(textToAdd);
} }
// System.out.println("finished a loop :))))))))))))");
} }
Platform.runLater(() -> Platform.runLater(() ->
positionVbox.getChildren().setAll(vboxEntries) positionVbox.getChildren().setAll(vboxEntries)
@@ -476,9 +478,11 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
} }
private Point2D getPointRotation(Point2D ref, Double distance, Double angle){ private Point2D getPointRotation(Point2D ref, Double distance, Double angle) {
Double newX = ref.getX() + (ref.getX() + distance -ref.getX())*Math.cos(angle) - (ref.getY() + distance -ref.getY())*Math.sin(angle); Double newX = ref.getX() + (ref.getX() + distance - ref.getX()) * Math.cos(angle)
Double newY = ref.getY() + (ref.getX() + distance -ref.getX())*Math.sin(angle) + (ref.getY() + distance -ref.getY())*Math.cos(angle); - (ref.getY() + distance - ref.getY()) * Math.sin(angle);
Double newY = ref.getY() + (ref.getX() + distance - ref.getX()) * Math.sin(angle)
+ (ref.getY() + distance - ref.getY()) * Math.cos(angle);
return new Point2D(newX, newY); return new Point2D(newX, newY);
} }
@@ -503,8 +507,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/** /**
* Initialised the combo box with any yachts currently in the race and adds the required listener * Initialised the combo box with any yachts currently in the race and adds the required
* for the combobox to take action upon selection * listener for the combobox to take action upon selection
*/ */
private void initialiseBoatSelectionComboBox() { private void initialiseBoatSelectionComboBox() {
yachtSelectionComboBox.setItems( yachtSelectionComboBox.setItems(
@@ -592,11 +596,22 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
// } // }
} }
public void updateRaceData (RaceXMLData raceData) { public void updateRaceData(RaceXMLData raceData) {
this.courseData = raceData; this.courseData = raceData;
gameView.updateBorder(raceData.getCourseLimit()); gameView.updateBorder(raceData.getCourseLimit());
} }
/**
* Called by game client after receiving yacht event packet. Parameter subject id is the
* offending yacht. This function in turn will pass the yacht location to game view to display a
* collision alert.
*
* @param subjectId source id of offending yacht
*/
public void showCollision(Long subjectId) {
gameView.drawCollision(participants.get((int) (long) subjectId).getLocation());
}
public GameView getGameView() { public GameView getGameView() {
return gameView; return gameView;
} }
-171
View File
@@ -1,171 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<BoatConfig>
<Modified>2015-08-28T17:32:59+0100</Modified>
<Version>12</Version>
<Snapshot>219</Snapshot>
<Settings>
<RaceBoatType Type="AC45"/>
<BoatDimension BoatLength="14.019" HullLength="13.449"/>
<ZoneSize MarkZoneSize="40.347" CourseZoneSize="53.796"/>
<ZoneLimits Limit1="200" Limit2="100" Limit3="53.796" Limit4="0" Limit5="-100"/>
</Settings>
<BoatShapes>
<BoatShape ShapeID="0">
<Vertices>
<Vtx Seq="3" Y="25" X="0"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="14">
<Vertices>
<Vtx Seq="1" Y="0" X="-1"/>
<Vtx Seq="2" Y="0.75" X="-1"/>
<Vtx Seq="3" Y="0.75" X="-0.25"/>
<Vtx Seq="4" Y="3.5" X="-0.25"/>
<Vtx Seq="5" Y="4.5" X="-1"/>
<Vtx Seq="6" Y="6.5" X="-1"/>
<Vtx Seq="7" Y="7" X="-0.5"/>
<Vtx Seq="8" Y="7" X="0.5"/>
<Vtx Seq="9" Y="6.5" X="1"/>
<Vtx Seq="10" Y="4.5" X="1"/>
<Vtx Seq="11" Y="3.5" X="0.25"/>
<Vtx Seq="12" Y="0.75" X="0.25"/>
<Vtx Seq="13" Y="0.75" X="1"/>
<Vtx Seq="14" Y="0" X="1"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="15">
<Vertices>
<Vtx Seq="1" Y="0" X="-3.46"/>
<Vtx Seq="2" Y="13.449" X="-3.46"/>
<Vtx Seq="3" Y="14.019" X="0"/>
<Vtx Seq="4" Y="13.449" X="3.46"/>
<Vtx Seq="5" Y="0" X="3.46"/>
</Vertices>
<Catamaran>
<Vtx Seq="1" Y="1.769" X="-2.752"/>
<Vtx Seq="2" Y="0" X="-2.813"/>
<Vtx Seq="3" Y="0" X="-3.34"/>
<Vtx Seq="4" Y="5.351" X="-3.46"/>
<Vtx Seq="5" Y="10.544" X="-3.387"/>
<Vtx Seq="6" Y="13.449" X="-3.075"/>
<Vtx Seq="7" Y="10.851" X="-2.793"/>
<Vtx Seq="8" Y="6.669" X="-2.699"/>
<Vtx Seq="9" Y="6.669" X="2.699"/>
<Vtx Seq="10" Y="10.851" X="2.793"/>
<Vtx Seq="11" Y="13.449" X="3.075"/>
<Vtx Seq="12" Y="10.544" X="3.387"/>
<Vtx Seq="13" Y="5.351" X="3.46"/>
<Vtx Seq="14" Y="0" X="3.34"/>
<Vtx Seq="15" Y="0" X="2.813"/>
<Vtx Seq="16" Y="1.769" X="2.752"/>
</Catamaran>
<Bowsprit>
<Vtx Seq="1" Y="6.669" X="-0.2"/>
<Vtx Seq="2" Y="11.377" X="-0.2"/>
<Vtx Seq="3" Y="14.019" X="0"/>
<Vtx Seq="4" Y="11.377" X="0.2"/>
<Vtx Seq="5" Y="6.669" X="0.2"/>
</Bowsprit>
<Trampoline>
<Vtx Seq="1" Y="2" X="-2.699"/>
<Vtx Seq="2" Y="6.438" X="-2.699"/>
<Vtx Seq="3" Y="6.438" X="2.699"/>
<Vtx Seq="4" Y="2" X="2.699"/>
</Trampoline>
</BoatShape>
<BoatShape ShapeID="18">
<Vertices>
<Vtx Seq="1" Y="0" X="-1.04"/>
<Vtx Seq="2" Y="0.11" X="-1.18"/>
<Vtx Seq="3" Y="0.42" X="-1.28"/>
<Vtx Seq="4" Y="3.74" X="-1.29"/>
<Vtx Seq="5" Y="5.36" X="-1.21"/>
<Vtx Seq="6" Y="6.29" X="-1.08"/>
<Vtx Seq="7" Y="7.15" X="-0.84"/>
<Vtx Seq="8" Y="7.63" X="-0.62"/>
<Vtx Seq="9" Y="7.94" X="-0.34"/>
<Vtx Seq="10" Y="8.06" X="0"/>
<Vtx Seq="11" Y="7.94" X="0.34"/>
<Vtx Seq="12" Y="7.63" X="0.62"/>
<Vtx Seq="13" Y="7.15" X="0.84"/>
<Vtx Seq="14" Y="6.29" X="1.08"/>
<Vtx Seq="15" Y="5.36" X="1.21"/>
<Vtx Seq="16" Y="3.74" X="1.29"/>
<Vtx Seq="17" Y="0.42" X="1.28"/>
<Vtx Seq="18" Y="0.11" X="1.18"/>
<Vtx Seq="19" Y="0" X="1.04"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="24">
<Vertices>
<Vtx Seq="1" Y="0" X="-2.5"/>
<Vtx Seq="2" Y="7" X="-2.5"/>
<Vtx Seq="3" Y="12.6" X="-2.2"/>
<Vtx Seq="4" Y="12.6" X="2.2"/>
<Vtx Seq="5" Y="7" X="2.5"/>
<Vtx Seq="6" Y="0" X="2.5"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="34">
<Vertices>
<Vtx Seq="1" Y="0" X="-1.16"/>
<Vtx Seq="2" Y="5.51" X="-1.16"/>
<Vtx Seq="3" Y="5.846" X="-0.84"/>
<Vtx Seq="4" Y="5.846" X="0.84"/>
<Vtx Seq="5" Y="5.51" X="1.16"/>
<Vtx Seq="6" Y="0" X="1.16"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="35">
<Vertices>
<Vtx Seq="1" Y="0" X="-1.461"/>
<Vtx Seq="2" Y="6" X="-1.461"/>
<Vtx Seq="3" Y="7" X="-1.44"/>
<Vtx Seq="4" Y="8" X="-1.38"/>
<Vtx Seq="5" Y="9" X="-1.17"/>
<Vtx Seq="6" Y="10" X="-0.76"/>
<Vtx Seq="7" Y="10.6" X="-0.34"/>
<Vtx Seq="8" Y="10.61" X="0"/>
<Vtx Seq="9" Y="10.6" X="0.34"/>
<Vtx Seq="10" Y="10" X="0.76"/>
<Vtx Seq="11" Y="9" X="1.17"/>
<Vtx Seq="12" Y="8" X="1.38"/>
<Vtx Seq="13" Y="7" X="1.44"/>
<Vtx Seq="14" Y="6" X="1.461"/>
<Vtx Seq="15" Y="0" X="1.461"/>
</Vertices>
</BoatShape>
</BoatShapes>
<Boats>
<Boat Type="Yacht" SourceID="201" ShapeID="15" StoweName="USA" ShortName="ORACLE" ShorterName="USA" BoatName="ORACLE TEAM USA" HullNum="AC4515" Skipper="SPITHILL" Helmsman="SPITHILL" Country="USA" PeliID="101" RadioIP="172.20.2.101">
<GPSposition Z="1.78" Y="-0.331" X="-0.006"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
<Boat Type="Yacht" SourceID="301" ShapeID="15" StoweName="SWE" ShortName="ARTEMIS" ShorterName="SWE" BoatName="ARTEMIS RACING" HullNum="AC4517" Skipper="OUTTERIDGE" Helmsman="OUTTERIDGE" Country="SWE" PeliID="102" RadioIP="172.20.2.102">
<GPSposition Z="1.727" Y="-0.359" X="-0.0121"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
<Boat Type="Yacht" SourceID="401" ShapeID="15" StoweName="NZL" ShortName="ETNZ" ShorterName="NZL" BoatName="EMIRATES TEAM NZ" HullNum="AC4503" Skipper="ASHBY" Helmsman="BURLING" Country="NZL" PeliID="103" RadioIP="172.20.2.103">
<GPSposition Z="1.881" Y="-0.291" X="-0.003"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
<Boat Type="Yacht" SourceID="501" ShapeID="15" StoweName="JPN" ShortName="JAPAN" ShorterName="JPN" BoatName="SOFTBANK TEAM JAPAN" HullNum="AC4504" Skipper="BARKER" Helmsman="BARKER" Country="JPN" PeliID="104" RadioIP="172.20.2.104">
<GPSposition Z="1.805" Y="-0.322" X="-0.003"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
<Boat Type="Yacht" SourceID="601" ShapeID="15" StoweName="FRA" ShortName="FRANCE" ShorterName="FRA" BoatName="GROUPAMA TEAM FRANCE" HullNum="AC4505" Skipper="CAMMAS" Helmsman="CAMMAS" Country="FRA" PeliID="105" RadioIP="172.20.2.105">
<GPSposition Z="1.863" Y="-0.3" X="-0.003"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
<Boat Type="Yacht" SourceID="701" ShapeID="15" StoweName="GBR" ShortName="GBR" ShorterName="GBR" BoatName="LAND ROVER BAR" HullNum="AC4516" Skipper="ANSLIE" Helmsman="ANSLIE" Country="GBR" PeliID="106" RadioIP="172.20.2.106">
<GPSposition Z="1.734" Y="-0.352" X="0"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
</Boats>
</BoatConfig>
-161
View File
@@ -1,161 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<BoatConfig>
<Modified>2015-08-28T17:32:59+0100</Modified>
<Version>12</Version>
<Snapshot>219</Snapshot>
<Settings>
<RaceBoatType Type="AC45"/>
<BoatDimension BoatLength="14.019" HullLength="13.449"/>
<ZoneSize MarkZoneSize="40.347" CourseZoneSize="53.796"/>
<ZoneLimits Limit1="200" Limit2="100" Limit3="53.796" Limit4="0" Limit5="-100"/>
</Settings>
<BoatShapes>
<BoatShape ShapeID="0">
<Vertices>
<Vtx Seq="3" Y="25" X="0"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="14">
<Vertices>
<Vtx Seq="1" Y="0" X="-1"/>
<Vtx Seq="2" Y="0.75" X="-1"/>
<Vtx Seq="3" Y="0.75" X="-0.25"/>
<Vtx Seq="4" Y="3.5" X="-0.25"/>
<Vtx Seq="5" Y="4.5" X="-1"/>
<Vtx Seq="6" Y="6.5" X="-1"/>
<Vtx Seq="7" Y="7" X="-0.5"/>
<Vtx Seq="8" Y="7" X="0.5"/>
<Vtx Seq="9" Y="6.5" X="1"/>
<Vtx Seq="10" Y="4.5" X="1"/>
<Vtx Seq="11" Y="3.5" X="0.25"/>
<Vtx Seq="12" Y="0.75" X="0.25"/>
<Vtx Seq="13" Y="0.75" X="1"/>
<Vtx Seq="14" Y="0" X="1"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="15">
<Vertices>
<Vtx Seq="1" Y="0" X="-3.46"/>
<Vtx Seq="2" Y="13.449" X="-3.46"/>
<Vtx Seq="3" Y="14.019" X="0"/>
<Vtx Seq="4" Y="13.449" X="3.46"/>
<Vtx Seq="5" Y="0" X="3.46"/>
</Vertices>
<Catamaran>
<Vtx Seq="1" Y="1.769" X="-2.752"/>
<Vtx Seq="2" Y="0" X="-2.813"/>
<Vtx Seq="3" Y="0" X="-3.34"/>
<Vtx Seq="4" Y="5.351" X="-3.46"/>
<Vtx Seq="5" Y="10.544" X="-3.387"/>
<Vtx Seq="6" Y="13.449" X="-3.075"/>
<Vtx Seq="7" Y="10.851" X="-2.793"/>
<Vtx Seq="8" Y="6.669" X="-2.699"/>
<Vtx Seq="9" Y="6.669" X="2.699"/>
<Vtx Seq="10" Y="10.851" X="2.793"/>
<Vtx Seq="11" Y="13.449" X="3.075"/>
<Vtx Seq="12" Y="10.544" X="3.387"/>
<Vtx Seq="13" Y="5.351" X="3.46"/>
<Vtx Seq="14" Y="0" X="3.34"/>
<Vtx Seq="15" Y="0" X="2.813"/>
<Vtx Seq="16" Y="1.769" X="2.752"/>
</Catamaran>
<Bowsprit>
<Vtx Seq="1" Y="6.669" X="-0.2"/>
<Vtx Seq="2" Y="11.377" X="-0.2"/>
<Vtx Seq="3" Y="14.019" X="0"/>
<Vtx Seq="4" Y="11.377" X="0.2"/>
<Vtx Seq="5" Y="6.669" X="0.2"/>
</Bowsprit>
<Trampoline>
<Vtx Seq="1" Y="2" X="-2.699"/>
<Vtx Seq="2" Y="6.438" X="-2.699"/>
<Vtx Seq="3" Y="6.438" X="2.699"/>
<Vtx Seq="4" Y="2" X="2.699"/>
</Trampoline>
</BoatShape>
<BoatShape ShapeID="18">
<Vertices>
<Vtx Seq="1" Y="0" X="-1.04"/>
<Vtx Seq="2" Y="0.11" X="-1.18"/>
<Vtx Seq="3" Y="0.42" X="-1.28"/>
<Vtx Seq="4" Y="3.74" X="-1.29"/>
<Vtx Seq="5" Y="5.36" X="-1.21"/>
<Vtx Seq="6" Y="6.29" X="-1.08"/>
<Vtx Seq="7" Y="7.15" X="-0.84"/>
<Vtx Seq="8" Y="7.63" X="-0.62"/>
<Vtx Seq="9" Y="7.94" X="-0.34"/>
<Vtx Seq="10" Y="8.06" X="0"/>
<Vtx Seq="11" Y="7.94" X="0.34"/>
<Vtx Seq="12" Y="7.63" X="0.62"/>
<Vtx Seq="13" Y="7.15" X="0.84"/>
<Vtx Seq="14" Y="6.29" X="1.08"/>
<Vtx Seq="15" Y="5.36" X="1.21"/>
<Vtx Seq="16" Y="3.74" X="1.29"/>
<Vtx Seq="17" Y="0.42" X="1.28"/>
<Vtx Seq="18" Y="0.11" X="1.18"/>
<Vtx Seq="19" Y="0" X="1.04"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="24">
<Vertices>
<Vtx Seq="1" Y="0" X="-2.5"/>
<Vtx Seq="2" Y="7" X="-2.5"/>
<Vtx Seq="3" Y="12.6" X="-2.2"/>
<Vtx Seq="4" Y="12.6" X="2.2"/>
<Vtx Seq="5" Y="7" X="2.5"/>
<Vtx Seq="6" Y="0" X="2.5"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="34">
<Vertices>
<Vtx Seq="1" Y="0" X="-1.16"/>
<Vtx Seq="2" Y="5.51" X="-1.16"/>
<Vtx Seq="3" Y="5.846" X="-0.84"/>
<Vtx Seq="4" Y="5.846" X="0.84"/>
<Vtx Seq="5" Y="5.51" X="1.16"/>
<Vtx Seq="6" Y="0" X="1.16"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="35">
<Vertices>
<Vtx Seq="1" Y="0" X="-1.461"/>
<Vtx Seq="2" Y="6" X="-1.461"/>
<Vtx Seq="3" Y="7" X="-1.44"/>
<Vtx Seq="4" Y="8" X="-1.38"/>
<Vtx Seq="5" Y="9" X="-1.17"/>
<Vtx Seq="6" Y="10" X="-0.76"/>
<Vtx Seq="7" Y="10.6" X="-0.34"/>
<Vtx Seq="8" Y="10.61" X="0"/>
<Vtx Seq="9" Y="10.6" X="0.34"/>
<Vtx Seq="10" Y="10" X="0.76"/>
<Vtx Seq="11" Y="9" X="1.17"/>
<Vtx Seq="12" Y="8" X="1.38"/>
<Vtx Seq="13" Y="7" X="1.44"/>
<Vtx Seq="14" Y="6" X="1.461"/>
<Vtx Seq="15" Y="0" X="1.461"/>
</Vertices>
</BoatShape>
</BoatShapes>
<Boats>
<Boat Type="Yacht" SourceID="201" ShapeID="15" StoweName="USA" ShortName="ORACLE" ShorterName="USA" BoatName="ORACLE TEAM USA" HullNum="AC4515" Skipper="SPITHILL" Helmsman="SPITHILL" Country="USA" PeliID="101" RadioIP="172.20.2.101">
<GPSposition Z="1.78" Y="-0.331" X="-0.006"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
<Boat Type="Yacht" SourceID="401" ShapeID="15" StoweName="NZL" ShortName="ETNZ" ShorterName="NZL" BoatName="EMIRATES TEAM NZ" HullNum="AC4503" Skipper="ASHBY" Helmsman="BURLING" Country="NZL" PeliID="103" RadioIP="172.20.2.103">
<GPSposition Z="1.881" Y="-0.291" X="-0.003"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
<Boat Type="Yacht" SourceID="501" ShapeID="15" StoweName="JPN" ShortName="JAPAN" ShorterName="JPN" BoatName="SOFTBANK TEAM JAPAN" HullNum="AC4504" Skipper="BARKER" Helmsman="BARKER" Country="JPN" PeliID="104" RadioIP="172.20.2.104">
<GPSposition Z="1.805" Y="-0.322" X="-0.003"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
<Boat Type="Yacht" SourceID="701" ShapeID="15" StoweName="GBR" ShortName="GBR" ShorterName="GBR" BoatName="LAND ROVER BAR" HullNum="AC4516" Skipper="ANSLIE" Helmsman="ANSLIE" Country="GBR" PeliID="106" RadioIP="172.20.2.106">
<GPSposition Z="1.734" Y="-0.352" X="0"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
</Boats>
</BoatConfig>
-171
View File
@@ -1,171 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<BoatConfig>
<Modified>2015-08-28T17:32:59+0100</Modified>
<Version>12</Version>
<Snapshot>219</Snapshot>
<Settings>
<RaceBoatType Type="AC45"/>
<BoatDimension BoatLength="14.019" HullLength="13.449"/>
<ZoneSize MarkZoneSize="40.347" CourseZoneSize="53.796"/>
<ZoneLimits Limit1="200" Limit2="100" Limit3="53.796" Limit4="0" Limit5="-100"/>
</Settings>
<BoatShapes>
<BoatShape ShapeID="0">
<Vertices>
<Vtx Seq="3" Y="25" X="0"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="14">
<Vertices>
<Vtx Seq="1" Y="0" X="-1"/>
<Vtx Seq="2" Y="0.75" X="-1"/>
<Vtx Seq="3" Y="0.75" X="-0.25"/>
<Vtx Seq="4" Y="3.5" X="-0.25"/>
<Vtx Seq="5" Y="4.5" X="-1"/>
<Vtx Seq="6" Y="6.5" X="-1"/>
<Vtx Seq="7" Y="7" X="-0.5"/>
<Vtx Seq="8" Y="7" X="0.5"/>
<Vtx Seq="9" Y="6.5" X="1"/>
<Vtx Seq="10" Y="4.5" X="1"/>
<Vtx Seq="11" Y="3.5" X="0.25"/>
<Vtx Seq="12" Y="0.75" X="0.25"/>
<Vtx Seq="13" Y="0.75" X="1"/>
<Vtx Seq="14" Y="0" X="1"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="15">
<Vertices>
<Vtx Seq="1" Y="0" X="-3.46"/>
<Vtx Seq="2" Y="13.449" X="-3.46"/>
<Vtx Seq="3" Y="14.019" X="0"/>
<Vtx Seq="4" Y="13.449" X="3.46"/>
<Vtx Seq="5" Y="0" X="3.46"/>
</Vertices>
<Catamaran>
<Vtx Seq="1" Y="1.769" X="-2.752"/>
<Vtx Seq="2" Y="0" X="-2.813"/>
<Vtx Seq="3" Y="0" X="-3.34"/>
<Vtx Seq="4" Y="5.351" X="-3.46"/>
<Vtx Seq="5" Y="10.544" X="-3.387"/>
<Vtx Seq="6" Y="13.449" X="-3.075"/>
<Vtx Seq="7" Y="10.851" X="-2.793"/>
<Vtx Seq="8" Y="6.669" X="-2.699"/>
<Vtx Seq="9" Y="6.669" X="2.699"/>
<Vtx Seq="10" Y="10.851" X="2.793"/>
<Vtx Seq="11" Y="13.449" X="3.075"/>
<Vtx Seq="12" Y="10.544" X="3.387"/>
<Vtx Seq="13" Y="5.351" X="3.46"/>
<Vtx Seq="14" Y="0" X="3.34"/>
<Vtx Seq="15" Y="0" X="2.813"/>
<Vtx Seq="16" Y="1.769" X="2.752"/>
</Catamaran>
<Bowsprit>
<Vtx Seq="1" Y="6.669" X="-0.2"/>
<Vtx Seq="2" Y="11.377" X="-0.2"/>
<Vtx Seq="3" Y="14.019" X="0"/>
<Vtx Seq="4" Y="11.377" X="0.2"/>
<Vtx Seq="5" Y="6.669" X="0.2"/>
</Bowsprit>
<Trampoline>
<Vtx Seq="1" Y="2" X="-2.699"/>
<Vtx Seq="2" Y="6.438" X="-2.699"/>
<Vtx Seq="3" Y="6.438" X="2.699"/>
<Vtx Seq="4" Y="2" X="2.699"/>
</Trampoline>
</BoatShape>
<BoatShape ShapeID="18">
<Vertices>
<Vtx Seq="1" Y="0" X="-1.04"/>
<Vtx Seq="2" Y="0.11" X="-1.18"/>
<Vtx Seq="3" Y="0.42" X="-1.28"/>
<Vtx Seq="4" Y="3.74" X="-1.29"/>
<Vtx Seq="5" Y="5.36" X="-1.21"/>
<Vtx Seq="6" Y="6.29" X="-1.08"/>
<Vtx Seq="7" Y="7.15" X="-0.84"/>
<Vtx Seq="8" Y="7.63" X="-0.62"/>
<Vtx Seq="9" Y="7.94" X="-0.34"/>
<Vtx Seq="10" Y="8.06" X="0"/>
<Vtx Seq="11" Y="7.94" X="0.34"/>
<Vtx Seq="12" Y="7.63" X="0.62"/>
<Vtx Seq="13" Y="7.15" X="0.84"/>
<Vtx Seq="14" Y="6.29" X="1.08"/>
<Vtx Seq="15" Y="5.36" X="1.21"/>
<Vtx Seq="16" Y="3.74" X="1.29"/>
<Vtx Seq="17" Y="0.42" X="1.28"/>
<Vtx Seq="18" Y="0.11" X="1.18"/>
<Vtx Seq="19" Y="0" X="1.04"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="24">
<Vertices>
<Vtx Seq="1" Y="0" X="-2.5"/>
<Vtx Seq="2" Y="7" X="-2.5"/>
<Vtx Seq="3" Y="12.6" X="-2.2"/>
<Vtx Seq="4" Y="12.6" X="2.2"/>
<Vtx Seq="5" Y="7" X="2.5"/>
<Vtx Seq="6" Y="0" X="2.5"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="34">
<Vertices>
<Vtx Seq="1" Y="0" X="-1.16"/>
<Vtx Seq="2" Y="5.51" X="-1.16"/>
<Vtx Seq="3" Y="5.846" X="-0.84"/>
<Vtx Seq="4" Y="5.846" X="0.84"/>
<Vtx Seq="5" Y="5.51" X="1.16"/>
<Vtx Seq="6" Y="0" X="1.16"/>
</Vertices>
</BoatShape>
<BoatShape ShapeID="35">
<Vertices>
<Vtx Seq="1" Y="0" X="-1.461"/>
<Vtx Seq="2" Y="6" X="-1.461"/>
<Vtx Seq="3" Y="7" X="-1.44"/>
<Vtx Seq="4" Y="8" X="-1.38"/>
<Vtx Seq="5" Y="9" X="-1.17"/>
<Vtx Seq="6" Y="10" X="-0.76"/>
<Vtx Seq="7" Y="10.6" X="-0.34"/>
<Vtx Seq="8" Y="10.61" X="0"/>
<Vtx Seq="9" Y="10.6" X="0.34"/>
<Vtx Seq="10" Y="10" X="0.76"/>
<Vtx Seq="11" Y="9" X="1.17"/>
<Vtx Seq="12" Y="8" X="1.38"/>
<Vtx Seq="13" Y="7" X="1.44"/>
<Vtx Seq="14" Y="6" X="1.461"/>
<Vtx Seq="15" Y="0" X="1.461"/>
</Vertices>
</BoatShape>
</BoatShapes>
<Boats>
<Boat Type="Yacht" SourceID="201" ShapeID="15" StoweName="USA" ShortName="ORACLE" ShorterName="USA" BoatName="ORACLE TEAM USA" HullNum="AC4515" Skipper="SPITHILL" Helmsman="SPITHILL" Country="USA" PeliID="101" RadioIP="172.20.2.101">
<GPSposition Z="1.78" Y="-0.331" X="-0.006"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
<Boat Type="Yacht" SourceID="301" ShapeID="15" StoweName="SWE" ShortName="ARTEMIS" ShorterName="SWE" BoatName="ARTEMIS RACING" HullNum="AC4517" Skipper="OUTTERIDGE" Helmsman="OUTTERIDGE" Country="SWE" PeliID="102" RadioIP="172.20.2.102">
<GPSposition Z="1.727" Y="-0.359" X="-0.0121"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
<Boat Type="Yacht" SourceID="401" ShapeID="15" StoweName="NZL" ShortName="ETNZ" ShorterName="NZL" BoatName="EMIRATES TEAM NZ" HullNum="AC4503" Skipper="ASHBY" Helmsman="BURLING" Country="NZL" PeliID="103" RadioIP="172.20.2.103">
<GPSposition Z="1.881" Y="-0.291" X="-0.003"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
<Boat Type="Yacht" SourceID="501" ShapeID="15" StoweName="JPN" ShortName="JAPAN" ShorterName="JPN" BoatName="SOFTBANK TEAM JAPAN" HullNum="AC4504" Skipper="BARKER" Helmsman="BARKER" Country="JPN" PeliID="104" RadioIP="172.20.2.104">
<GPSposition Z="1.805" Y="-0.322" X="-0.003"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
<Boat Type="Yacht" SourceID="601" ShapeID="15" StoweName="FRA" ShortName="FRANCE" ShorterName="FRA" BoatName="GROUPAMA TEAM FRANCE" HullNum="AC4505" Skipper="CAMMAS" Helmsman="CAMMAS" Country="FRA" PeliID="105" RadioIP="172.20.2.105">
<GPSposition Z="1.863" Y="-0.3" X="-0.003"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
<Boat Type="Yacht" SourceID="701" ShapeID="15" StoweName="GBR" ShortName="GBR" ShorterName="GBR" BoatName="LAND ROVER BAR" HullNum="AC4516" Skipper="ANSLIE" Helmsman="ANSLIE" Country="GBR" PeliID="106" RadioIP="172.20.2.106">
<GPSposition Z="1.734" Y="-0.352" X="0"/>
<MastTop Z="21.496" Y="3.7" X="0"/>
<FlagPosition Z="0" Y="6.2" X="0"/>
</Boat>
</Boats>
</BoatConfig>
@@ -1,5 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
@@ -10,6 +15,7 @@
<?import javafx.scene.layout.RowConstraints?> <?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?> <?import javafx.scene.text.Text?>
<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"> <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">
<children> <children>
<GridPane fx:id="startScreen2" layoutX="365.0" layoutY="285.0" nodeOrientation="LEFT_TO_RIGHT" prefWidth="800.0" style="-fx-background-color: #2C2c36;"> <GridPane fx:id="startScreen2" layoutX="365.0" layoutY="285.0" nodeOrientation="LEFT_TO_RIGHT" prefWidth="800.0" style="-fx-background-color: #2C2c36;">
@@ -25,7 +31,7 @@
<Insets left="5.0" right="5.0" /> <Insets left="5.0" right="5.0" />
</GridPane.margin> </GridPane.margin>
</Button> </Button>
<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"> <TextField fx:id="ipTextField" alignment="CENTER" maxWidth="-Infinity" prefHeight="25.0" prefWidth="148.0" promptText="Host IP" text="localhost" GridPane.halignment="RIGHT" GridPane.rowIndex="4">
<GridPane.margin> <GridPane.margin>
<Insets bottom="10.0" left="5.0" right="85.0" top="10.0" /> <Insets bottom="10.0" left="5.0" right="85.0" top="10.0" />
</GridPane.margin> </GridPane.margin>
+3 -3
View File
@@ -9,9 +9,9 @@ public class ColorsTest {
@Test @Test
public void testNextColor() { public void testNextColor() {
Color expectedColors[] = {Color.RED, Color.PERU, Color.SEAGREEN, Color.GREEN, Color.BLUE, Color.PURPLE}; Color expectedColors[] = {Color.RED, Color.PERU, Color.GOLD, Color.GREEN, Color.BLUE,
for (int i = 0; i<6; i++) Color.PURPLE, Color.DEEPPINK, Color.GRAY};
{ for (int i = 0; i < 8; i++) {
Assert.assertEquals(expectedColors[i], Colors.getColor()); Assert.assertEquals(expectedColors[i], Colors.getColor());
} }
} }
@@ -0,0 +1,67 @@
package seng302.model;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import seng302.gameServer.GameState;
import seng302.utilities.GeoUtility;
/**
* Test update function in Yacht.java to make sure yacht will not be collide each other within 25.0
* meters.
*/
public class UpdateYachtTest {
private ServerYacht yacht1 = new ServerYacht("Yacht", 1, "1", "Yacht" + 1, "Yacht" + 1, "Test1");
private ServerYacht yacht2 = new ServerYacht("Yacht", 2, "2", "Yacht" + 2, "Yacht" + 2, "Test2");
private GeoPoint geoPoint1 = new GeoPoint(50.0, 50.0);
private GeoPoint geoPoint2 = GeoUtility.getGeoCoordinate(geoPoint1, 90.0, 50.0);
@Before
public void setUpRace() {
new GameState("");
GameState.addYacht(1, yacht1);
GameState.addYacht(2, yacht2);
PolarTable.parsePolarFile(getClass().getResourceAsStream("/config/acc_polars.csv"));
}
@Test
public void testUpdateYachtWithCollision() {
// Yacht 1 heading towards 90 degrees heading
yacht1.setLocation(geoPoint1);
// Yacht 2 heading towards 270 degrees heading
yacht2.setLocation(geoPoint1);
// Start yacht 1 and rest yacht 2
if (!yacht1.getSailIn()) {
yacht1.toggleSailIn();
}
GameState.checkForCollision(yacht1);
double moved = GeoUtility.getDistance(yacht1.getLocation(), geoPoint1);
Assert.assertEquals(GameState.BOUNCE_DISTANCE_YACHT, moved, 0.1);
}
@Test
public void testUpdateYachtWithoutCollision() {
// Yacht 1 heading towards 90 degrees heading
yacht1.setLocation(geoPoint1);
// Yacht 2 heading towards 270 degrees heading
yacht2.setLocation(geoPoint2);
// Start yacht 1 and rest yacht 2
if (!yacht1.getSailIn()) {
yacht1.toggleSailIn();
}
GameState.checkForCollision(yacht1);
Assert.assertTrue(
GameState.YACHT_COLLISION_DISTANCE < GeoUtility.getDistance(geoPoint1, geoPoint2
)
); //Check that yachts are actually far enough apart for no collision.
Assert.assertEquals(geoPoint1.getLat(), yacht1.getLocation().getLat(), 0.001);
Assert.assertEquals(geoPoint1.getLng(), yacht1.getLocation().getLng(), 0.001);
Assert.assertEquals(geoPoint2.getLat(), yacht1.getLocation().getLat(), 0.001);
Assert.assertEquals(geoPoint2.getLng(), yacht1.getLocation().getLng(), 0.001);
}
}
@@ -1,15 +1,9 @@
package seng302.visualiser.ClientToServerTests; package seng302.visualiser.ClientToServerTests;
import java.util.ArrayList;
import org.junit.After; import org.junit.After;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import seng302.gameServer.GameStages;
import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread; import seng302.gameServer.MainServerThread;
import seng302.gameServer.server.messages.BoatAction;
import seng302.model.ServerYacht;
import seng302.visualiser.ClientToServerThread; import seng302.visualiser.ClientToServerThread;
/** /**
@@ -22,32 +16,32 @@ public class RegularPacketsTest {
@Before @Before
public void setup() throws Exception { public void setup() throws Exception {
new GameState("localhost"); // new GameState("localhost");
serverThread = new MainServerThread(); // serverThread = new MainServerThread();
clientThread = new ClientToServerThread("localhost", 4942); // clientThread = new ClientToServerThread("localhost", 4942);
GameState.setCurrentStage(GameStages.RACING); // GameState.setCurrentStage(GameStages.RACING);
} }
@Test @Test
public void packetsSentAtRegularIntervals () throws Exception { public void packetsSentAtRegularIntervals () throws Exception {
final double TEST_DISTANCE = 10.0; // final double TEST_DISTANCE = 10.0;
serverThread.startGame(); // serverThread.startGame();
SleepThreadMaxDelay(); // SleepThreadMaxDelay();
ServerYacht yacht = new ArrayList<>(GameState.getYachts().values()).get(0); // ServerYacht yacht = new ArrayList<>(GameState.getYachts().values()).get(0);
double startAngle = yacht.getHeading(); // double startAngle = yacht.getHeading();
long startTime = System.currentTimeMillis(); // long startTime = System.currentTimeMillis();
clientThread.sendBoatAction(BoatAction.UPWIND); // clientThread.sendBoatAction(BoatAction.UPWIND);
Thread.sleep(200); // Thread.sleep(200);
while (Math.abs(yacht.getHeading() - startAngle) < TEST_DISTANCE) { // while (Math.abs(yacht.getHeading() - startAngle) < TEST_DISTANCE) {
Thread.sleep(1); // Thread.sleep(1);
} // }
clientThread.sendBoatAction(BoatAction.MAINTAIN_HEADING); // clientThread.sendBoatAction(BoatAction.MAINTAIN_HEADING);
long endTime = System.currentTimeMillis(); // long endTime = System.currentTimeMillis();
SleepThreadMaxDelay(); // SleepThreadMaxDelay();
//Allowed to be two loops of delay due to loop delay and processing delay at client + server ends. // //Allowed to be two loops of delay due to loop delay and processing delay at client + server ends.
Assert.assertEquals( // Assert.assertEquals(
TEST_DISTANCE / ServerYacht.TURN_STEP * ClientToServerThread.PACKET_SENDING_INTERVAL_MS, // TEST_DISTANCE / ServerYacht.TURN_STEP * ClientToServerThread.PACKET_SENDING_INTERVAL_MS,
(endTime - startTime), 2 * ClientToServerThread.PACKET_SENDING_INTERVAL_MS); // (endTime - startTime), 2 * ClientToServerThread.PACKET_SENDING_INTERVAL_MS);
} }
// @Test // @Test
@@ -72,10 +66,10 @@ public class RegularPacketsTest {
@After @After
public void teardown () throws Exception { public void teardown () throws Exception {
clientThread.setSocketToClose(); // clientThread.setSocketToClose();
serverThread.terminate(); // serverThread.terminate();
GameState.setCurrentStage(GameStages.LOBBYING); // GameState.setCurrentStage(GameStages.LOBBYING);
for (int i = 0; i<20; i++) // for (int i = 0; i<20; i++)
SleepThreadMaxDelay(); //Make sure socket is closed and toolkit remade. // SleepThreadMaxDelay(); //Make sure socket is closed and toolkit remade.
} }
} }
@@ -1,6 +1,7 @@
package seng302.visualiser.map; package seng302.visualiser.map;
import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -21,8 +22,8 @@ public class BoatSailAnimationToggleTest {
@Test @Test
public void sailToggleTest() throws Exception { public void sailToggleTest() throws Exception {
assertFalse(yacht.getSailIn()); assertFalse(yacht.getSailIn());
yacht.toggleClientSail(); yacht.toggleSail();
assertFalse(yacht.getSailIn()); assertTrue(yacht.getSailIn());
} }
} }
-4
View File
@@ -17,14 +17,10 @@ import seng302.visualiser.ClientToServerThread;
*/ */
public class ToggleSailSteps { public class ToggleSailSteps {
MainServerThread mst; MainServerThread mst;
ClientToServerThread client; ClientToServerThread client;
boolean sailsIn = false;
long startTime; long startTime;
@Given("^The game is running$") @Given("^The game is running$")
public void the_game_is_running() throws Throwable { public void the_game_is_running() throws Throwable {
mst = new MainServerThread(); mst = new MainServerThread();