diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index a6716807..604694e1 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -6,16 +6,21 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import seng302.gameServer.server.messages.BoatAction; -import seng302.gameServer.server.messages.BoatStatus; -import seng302.gameServer.server.messages.MarkRoundingMessage; -import seng302.gameServer.server.messages.MarkType; -import seng302.gameServer.server.messages.Message; -import seng302.gameServer.server.messages.RoundingBoatStatus; -import seng302.gameServer.server.messages.YachtEventCodeMessage; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import seng302.gameServer.messages.BoatAction; +import seng302.gameServer.messages.BoatStatus; +import seng302.gameServer.messages.MarkRoundingMessage; +import seng302.gameServer.messages.MarkType; +import seng302.gameServer.messages.Message; +import seng302.gameServer.messages.RoundingBoatStatus; +import seng302.gameServer.messages.YachtEventCodeMessage; import seng302.model.GeoPoint; +import seng302.model.Limit; import seng302.model.Player; import seng302.model.PolarTable; import seng302.model.ServerYacht; @@ -23,6 +28,7 @@ import seng302.model.mark.CompoundMark; import seng302.model.mark.Mark; import seng302.model.mark.MarkOrder; import seng302.utilities.GeoUtility; +import seng302.utilities.XMLParser; /** * A Static class to hold information about the current state of the game (model) @@ -30,9 +36,9 @@ import seng302.utilities.GeoUtility; * Created by wmu16 on 10/07/17. */ public class GameState implements Runnable { - @FunctionalInterface interface NewMessageListener { + void notify(Message message); } @@ -59,6 +65,7 @@ public class GameState implements Runnable { private static MarkOrder markOrder; private static long startTime; private static Set marks; + private static List courseLimit; private static List markListeners; @@ -81,7 +88,7 @@ public class GameState implements Runnable { yachts = new HashMap<>(); players = new ArrayList<>(); GameState.hostIpAddress = hostIpAddress; - ; + currentStage = GameStages.LOBBYING; isRaceStarted = false; //set this when game stage changes to prerace @@ -89,16 +96,34 @@ public class GameState implements Runnable { markOrder = new MarkOrder(); //This could be instantiated at some point with a select map? markListeners = new ArrayList<>(); + resetStartTime(); + new Thread(this, "GameState").start(); //Run the auto updates on the game state marks = new MarkOrder().getAllMarks(); + setCourseLimit("/server_config/race.xml"); + } + + private void setCourseLimit(String url) { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + DocumentBuilder documentBuilder; + Document document = null; + try { + documentBuilder = documentBuilderFactory.newDocumentBuilder(); + document = documentBuilder.parse(new InputSource(getClass().getResourceAsStream(url))); + } catch (Exception e) { + // sorry, we have to catch general one, otherwise we have to catch five different exceptions. + logger.trace("Failed to load course limit for boundary collision detection.", e); + } + courseLimit = XMLParser.parseRace(document).getCourseLimit(); } public static String getHostIpAddress() { return hostIpAddress; } - public static Set getMarks(){ + public static Set getMarks() { return Collections.unmodifiableSet(marks); } @@ -135,10 +160,6 @@ public class GameState implements Runnable { } public static void setCurrentStage(GameStages currentStage) { - if (currentStage == GameStages.RACING) { - startTime = System.currentTimeMillis(); - } - GameState.currentStage = currentStage; } @@ -146,14 +167,26 @@ public class GameState implements Runnable { return markOrder; } - public static long getStartTime(){ + public static long getStartTime() { return startTime; } + public static void resetStartTime(){ + startTime = System.currentTimeMillis() + MainServerThread.TIME_TILL_START; + } + public static Double getWindDirection() { return windDirection; } + public static void setWindDirection(Double newWindDirection) { + windDirection = newWindDirection; + } + + public static void setWindSpeed(Double newWindSpeed) { + windSpeed = newWindSpeed; + } + public static Double getWindSpeedMMS() { return windSpeed; } @@ -190,7 +223,7 @@ public class GameState implements Runnable { } catch (InterruptedException e) { System.out.println("[GameState] interrupted exception"); } - if (currentStage == GameStages.PRE_RACE) { + if (currentStage == GameStages.PRE_RACE || currentStage == GameStages.RACING) { update(); } @@ -224,7 +257,6 @@ public class GameState implements Runnable { } } - /** * Called periodically in this GameState thread to update the GameState values */ @@ -233,15 +265,20 @@ public class GameState implements Runnable { Double timeInterval = (System.currentTimeMillis() - previousUpdateTime) / 1000000.0; previousUpdateTime = System.currentTimeMillis(); + if (System.currentTimeMillis() > startTime) { + GameState.setCurrentStage(GameStages.RACING); + } for (ServerYacht yacht : yachts.values()) { updateVelocity(yacht); yacht.runAutoPilot(); yacht.updateLocation(timeInterval); if (yacht.getBoatStatus() != BoatStatus.FINISHED) { - checkForCollision(yacht); + checkCollision(yacht); checkForLegProgression(yacht); raceFinished = false; } + + } if (raceFinished) { @@ -249,9 +286,28 @@ public class GameState implements Runnable { } } + /** + * Check if the yacht has crossed the course limit + * + * @param yacht the yacht to be tested + * @return a boolean value of if there is a boundary collision + */ + private static Boolean checkBoundaryCollision(ServerYacht yacht) { + for (int i = 0; i < courseLimit.size() - 1; i++) { + if (GeoUtility.checkCrossedLine(courseLimit.get(i), courseLimit.get(i + 1), + yacht.getLastLocation(), yacht.getLocation()) != 0) { + return true; + } + } + if (GeoUtility.checkCrossedLine(courseLimit.get(courseLimit.size() - 1), courseLimit.get(0), + yacht.getLastLocation(), yacht.getLocation()) != 0) { + return true; + } + return false; + } - public static void checkForCollision(ServerYacht serverYacht) { - ServerYacht collidedYacht = checkCollision(serverYacht); + public static void checkCollision(ServerYacht serverYacht) { + ServerYacht collidedYacht = checkYachtCollision(serverYacht); if (collidedYacht != null) { GeoPoint originalLocation = serverYacht.getLocation(); serverYacht.setLocation( @@ -270,7 +326,7 @@ public class GameState implements Runnable { new YachtEventCodeMessage(serverYacht.getSourceId()) ); } else { - Mark collidedMark = markCollidedWith(serverYacht); + Mark collidedMark = checkMarkCollision(serverYacht); if (collidedMark != null) { serverYacht.setLocation( calculateBounceBack(serverYacht, collidedMark, BOUNCE_DISTANCE_MARK) @@ -282,6 +338,20 @@ public class GameState implements Runnable { new YachtEventCodeMessage(serverYacht.getSourceId()) ); } + else{ + if (checkBoundaryCollision(serverYacht)) { + serverYacht.setLocation( + calculateBounceBack(serverYacht, serverYacht.getLocation(), + BOUNCE_DISTANCE_YACHT) + ); + serverYacht.setCurrentVelocity( + serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY + ); + notifyMessageListeners( + new YachtEventCodeMessage(serverYacht.getSourceId()) + ); + } + } } } @@ -296,7 +366,7 @@ public class GameState implements Runnable { if (velocity < maxBoatSpeed - 500) { yacht.changeVelocity(maxBoatSpeed / 100); } else if (velocity > maxBoatSpeed + 500) { - yacht.changeVelocity(-maxBoatSpeed / 100); + yacht.changeVelocity(-velocity / 200); } else { yacht.setCurrentVelocity(maxBoatSpeed); } @@ -305,7 +375,7 @@ public class GameState implements Runnable { yacht.changeVelocity(-velocity / 200); } else if (velocity > 100) { yacht.changeVelocity(-velocity / 50); - } else if (velocity <= 100){ + } else if (velocity <= 100) { yacht.setCurrentVelocity(0d); } } @@ -347,6 +417,7 @@ public class GameState implements Runnable { /** * 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 + * * @param yacht the current yacht to check for progression */ private void checkForLegProgression(ServerYacht yacht) { @@ -366,6 +437,7 @@ public class GameState implements Runnable { } if (hasProgressed) { + yacht.incrementLegNumber(); sendMarkRoundingMessage(yacht); logMarkRounding(yacht); yacht.setHasPassedLine(false); @@ -510,7 +582,7 @@ public class GameState implements Runnable { } - private static Mark markCollidedWith(ServerYacht yacht) { + private static Mark checkMarkCollision(ServerYacht yacht) { Set marksInRace = GameState.getMarks(); for (Mark mark : marksInRace) { if (GeoUtility.getDistance(yacht.getLocation(), mark) @@ -526,12 +598,14 @@ public class GameState implements Runnable { * * @return The boats new position */ - private static GeoPoint calculateBounceBack(ServerYacht yacht, GeoPoint collidedWith, Double bounceDistance) { - Double heading = GeoUtility.getBearing(yacht.getLocation(), collidedWith); + private static GeoPoint calculateBounceBack(ServerYacht yacht, GeoPoint collidedWith, + Double bounceDistance) { + Double heading = GeoUtility.getBearing(yacht.getLastLocation(), collidedWith); // Invert heading heading -= 180; Integer newHeading = Math.floorMod(heading.intValue(), 360); - return GeoUtility.getGeoCoordinate(yacht.getLocation(), newHeading.doubleValue(), bounceDistance); + return GeoUtility + .getGeoCoordinate(yacht.getLocation(), newHeading.doubleValue(), bounceDistance); } /** @@ -540,11 +614,12 @@ public class GameState implements Runnable { * * @return yacht to compare to all other yachts. */ - private static ServerYacht checkCollision(ServerYacht yacht) { + private static ServerYacht checkYachtCollision(ServerYacht yacht) { for (ServerYacht otherYacht : GameState.getYachts().values()) { if (otherYacht != yacht) { - Double distance = GeoUtility.getDistance(otherYacht.getLocation(), yacht.getLocation()); + Double distance = GeoUtility + .getDistance(otherYacht.getLocation(), yacht.getLocation()); if (distance < YACHT_COLLISION_DISTANCE) { return otherYacht; } @@ -563,7 +638,7 @@ public class GameState implements Runnable { // TODO: 13/8/17 figure out the rounding side, rounded mark source ID and boat status. Message markRoundingMessage = new MarkRoundingMessage(0, 0, sourceID, RoundingBoatStatus.RACING, roundingMark.getRoundingSide(), markType, - roundingMark.getSourceID()); + currentMarkSeqID + 1); notifyMessageListeners(markRoundingMessage); } diff --git a/src/main/java/seng302/gameServer/HeartbeatThread.java b/src/main/java/seng302/gameServer/HeartbeatThread.java index b168a197..b9367134 100644 --- a/src/main/java/seng302/gameServer/HeartbeatThread.java +++ b/src/main/java/seng302/gameServer/HeartbeatThread.java @@ -5,15 +5,15 @@ import java.util.Stack; import java.util.Timer; import java.util.TimerTask; import seng302.model.Player; -import seng302.gameServer.server.messages.Heartbeat; -import seng302.gameServer.server.messages.Message; +import seng302.gameServer.messages.Heartbeat; +import seng302.gameServer.messages.Message; /** * Send Heartbeat messages to connected player at a specified interval * Will call .clientDisconnected on the delegate when a heartbeat message * cannot be sent to a player */ -public class HeartbeatThread extends Thread{ +public class HeartbeatThread implements Runnable { private final int HEARTBEAT_PERIOD = 200; private ClientConnectionDelegate delegate; private Integer seqNum; @@ -23,6 +23,9 @@ public class HeartbeatThread extends Thread{ this.delegate = delegate; seqNum = 0; disconnectedPlayers = new Stack<>(); + + Thread thread = new Thread(this, "HeartBeat"); + thread.start(); } /** diff --git a/src/main/java/seng302/gameServer/MainServerThread.java b/src/main/java/seng302/gameServer/MainServerThread.java index bd22063c..f11fe7e4 100644 --- a/src/main/java/seng302/gameServer/MainServerThread.java +++ b/src/main/java/seng302/gameServer/MainServerThread.java @@ -1,18 +1,20 @@ package seng302.gameServer; -import gherkin.lexer.Fi; import java.io.IOException; import java.net.ServerSocket; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import java.util.Random; import java.util.Timer; import java.util.TimerTask; -import seng302.gameServer.server.messages.BoatSubMessage; -import seng302.gameServer.server.messages.Message; -import seng302.gameServer.server.messages.RaceStatus; -import seng302.gameServer.server.messages.RaceStatusMessage; -import seng302.gameServer.server.messages.RaceType; +import seng302.gameServer.messages.BoatSubMessage; +import seng302.gameServer.messages.Message; +import seng302.gameServer.messages.RaceStartNotificationType; +import seng302.gameServer.messages.RaceStartStatusMessage; +import seng302.gameServer.messages.RaceStatus; +import seng302.gameServer.messages.RaceStatusMessage; +import seng302.gameServer.messages.RaceType; import seng302.model.GeoPoint; import seng302.model.Player; import seng302.model.PolarTable; @@ -28,8 +30,17 @@ import seng302.visualiser.GameClient; public class MainServerThread implements Runnable, ClientConnectionDelegate { private static final int PORT = 4942; - private static final Integer CLIENT_UPDATES_PER_SECOND = 10; + private static final Integer CLIENT_UPDATES_PER_SECOND = 60; private static final int LOG_LEVEL = 1; + private static final int WARNING_TIME = 10 * -1000; + private static final int PREPATORY_TIME = 5 * -1000; + public static final int TIME_TILL_START = 10 * 1000; + + private static final int MAX_WIND_SPEED = 12000; + private static final int MIN_WIND_SPEED = 8000; + + public static int windSpeed = 1000; + private boolean terminated; private Thread thread; @@ -50,19 +61,15 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { GameState.addMarkPassListener(this::broadcastMessage); terminated = false; thread = new Thread(this, "MainServer"); + startUpdatingWind(); thread.start(); } public void run() { - ServerListenThread serverListenThread; - HeartbeatThread heartbeatThread; - serverListenThread = new ServerListenThread(serverSocket, this); - heartbeatThread = new HeartbeatThread(this); - - heartbeatThread.start(); - serverListenThread.start(); + new HeartbeatThread(this); + new ServerListenThread(serverSocket, this); //You should handle interrupts in some way, so that the thread won't keep on forever if you exit the app. while (!terminated) { @@ -108,6 +115,45 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { } } + private static void updateWind(){ + Integer direction = GameState.getWindDirection().intValue(); + Integer windSpeed = GameState.getWindSpeedMMS().intValue(); + + Random random = new Random(); + + if (Math.floorMod(random.nextInt(), 2) == 0){ + direction += random.nextInt(4); + windSpeed += random.nextInt(100) + 500; + } + else{ + direction -= random.nextInt(4); + windSpeed -= random.nextInt(100) + 500; + } + + direction = Math.floorMod(direction, 360); + + if (windSpeed > MAX_WIND_SPEED){ + windSpeed -= random.nextInt(1000); + } + + if (windSpeed <= MIN_WIND_SPEED){ + windSpeed += random.nextInt(1000); + } + + GameState.setWindSpeed(Double.valueOf(windSpeed)); + GameState.setWindDirection(direction.doubleValue()); + } + + private static void startUpdatingWind(){ + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + updateWind(); + } + }, 0, 500); + } + static void serverLog(String message, int logLevel) { if (logLevel <= LOG_LEVEL) { @@ -166,10 +212,21 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { @Override public void run() { broadcastMessage(makeRaceStatusMessage()); + if (GameState.getCurrentStage() == GameStages.PRE_RACE || GameState.getCurrentStage() == GameStages.LOBBYING) { + broadcastMessage(makeRaceStartMessage()); + } } }, 0, 500); } + + private RaceStartStatusMessage makeRaceStartMessage() { + Long raceStartTime = GameState.getStartTime(); + + return new RaceStartStatusMessage(1, raceStartTime , + 1, RaceStartNotificationType.SET_RACE_START_TIME); + } + private RaceStatusMessage makeRaceStatusMessage() { // variables taken from GameServerThread @@ -178,16 +235,29 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { for (Player player : GameState.getPlayers()) { ServerYacht y = player.getYacht(); - BoatSubMessage m = new BoatSubMessage(y.getSourceId(), y.getBoatStatus(), 0, + BoatSubMessage m = new BoatSubMessage(y.getSourceId(), y.getBoatStatus(), + y.getLegNumber(), 0, 0, 1234L, 1234L); boatSubMessages.add(m); } - if (GameState.getCurrentStage() == GameStages.RACING) { - raceStatus = RaceStatus.STARTED; + long timeTillStart = System.currentTimeMillis() - GameState.getStartTime(); + + if (GameState.getCurrentStage() == GameStages.LOBBYING) { + raceStatus = RaceStatus.PRESTART; + } else if (GameState.getCurrentStage() == GameStages.PRE_RACE) { + raceStatus = RaceStatus.PRESTART; + + if (timeTillStart > WARNING_TIME) { + raceStatus = RaceStatus.WARNING; + } + + if (timeTillStart > PREPATORY_TIME) { + raceStatus = RaceStatus.PREPARATORY; + } } else { - raceStatus = RaceStatus.WARNING; + raceStatus = RaceStatus.STARTED; } return new RaceStatusMessage(1, raceStatus, GameState.getStartTime(), diff --git a/src/main/java/seng302/gameServer/ServerListenThread.java b/src/main/java/seng302/gameServer/ServerListenThread.java index 60469bb7..e05b76a9 100644 --- a/src/main/java/seng302/gameServer/ServerListenThread.java +++ b/src/main/java/seng302/gameServer/ServerListenThread.java @@ -8,13 +8,16 @@ import java.net.Socket; * A class for a thread to listen to connections * Created by wmu16 on 11/07/17. */ -public class ServerListenThread extends Thread{ +public class ServerListenThread implements Runnable { private ServerSocket serverSocket; private ClientConnectionDelegate delegate; public ServerListenThread(ServerSocket serverSocket, ClientConnectionDelegate delegate){ this.serverSocket = serverSocket; this.delegate = delegate; + + Thread thread = new Thread(this, "ServerListen"); + thread.start(); } /** diff --git a/src/main/java/seng302/gameServer/ServerPacketParser.java b/src/main/java/seng302/gameServer/ServerPacketParser.java index 84f8fd17..21d1497f 100644 --- a/src/main/java/seng302/gameServer/ServerPacketParser.java +++ b/src/main/java/seng302/gameServer/ServerPacketParser.java @@ -2,10 +2,10 @@ package seng302.gameServer; import java.util.Arrays; -import seng302.gameServer.server.messages.ClientType; -import seng302.gameServer.server.messages.Message; +import seng302.gameServer.messages.ClientType; +import seng302.gameServer.messages.Message; import seng302.model.stream.packets.StreamPacket; -import seng302.gameServer.server.messages.BoatAction; +import seng302.gameServer.messages.BoatAction; public class ServerPacketParser { diff --git a/src/main/java/seng302/gameServer/ServerToClientThread.java b/src/main/java/seng302/gameServer/ServerToClientThread.java index bae4216c..479333bd 100644 --- a/src/main/java/seng302/gameServer/ServerToClientThread.java +++ b/src/main/java/seng302/gameServer/ServerToClientThread.java @@ -19,25 +19,25 @@ import java.util.zip.CRC32; import java.util.zip.Checksum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import seng302.gameServer.server.messages.YachtEventCodeMessage; +import seng302.gameServer.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.BoatLocationMessage; -import seng302.gameServer.server.messages.BoatSubMessage; -import seng302.gameServer.server.messages.ClientType; -import seng302.gameServer.server.messages.Message; -import seng302.gameServer.server.messages.RaceStatus; -import seng302.gameServer.server.messages.RaceStatusMessage; -import seng302.gameServer.server.messages.RaceType; -import seng302.gameServer.server.messages.RegistrationResponseMessage; -import seng302.gameServer.server.messages.RegistrationResponseStatus; -import seng302.gameServer.server.messages.XMLMessage; -import seng302.gameServer.server.messages.XMLMessageSubType; +import seng302.gameServer.messages.BoatAction; +import seng302.gameServer.messages.BoatLocationMessage; +import seng302.gameServer.messages.BoatSubMessage; +import seng302.gameServer.messages.ClientType; +import seng302.gameServer.messages.Message; +import seng302.gameServer.messages.RaceStatus; +import seng302.gameServer.messages.RaceStatusMessage; +import seng302.gameServer.messages.RaceType; +import seng302.gameServer.messages.RegistrationResponseMessage; +import seng302.gameServer.messages.RegistrationResponseStatus; +import seng302.gameServer.messages.XMLMessage; +import seng302.gameServer.messages.XMLMessageSubType; import seng302.model.ServerYacht; /** @@ -209,8 +209,6 @@ public class ServerToClientThread implements Runnable, Observer { } } } catch (Exception e) { - // TODO: 24/07/17 zyt10 - fix a logic here when a client disconnected -// serverLog("ERROR OCCURRED, CLOSING SERVER CONNECTION: " + socket.getRemoteSocketAddress().toString(), 1); closeSocket(); return; } @@ -226,7 +224,7 @@ public class ServerToClientThread implements Runnable, Observer { } //@TODO calculate lat/lng values - xml.setRegatta(new Regatta("RaceVision Test Game", 57.6679590, 11.8503233)); + xml.setRegatta(new Regatta("Party Parrot Test Server", "Bermuda Test Course", 57.6679590, 11.8503233)); xml.setRace(race); XMLMessage xmlMessage; diff --git a/src/main/java/seng302/gameServer/server/messages/BoatAction.java b/src/main/java/seng302/gameServer/messages/BoatAction.java similarity index 94% rename from src/main/java/seng302/gameServer/server/messages/BoatAction.java rename to src/main/java/seng302/gameServer/messages/BoatAction.java index cc53668c..9003958a 100644 --- a/src/main/java/seng302/gameServer/server/messages/BoatAction.java +++ b/src/main/java/seng302/gameServer/messages/BoatAction.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/seng302/gameServer/server/messages/BoatActionMessage.java b/src/main/java/seng302/gameServer/messages/BoatActionMessage.java similarity index 94% rename from src/main/java/seng302/gameServer/server/messages/BoatActionMessage.java rename to src/main/java/seng302/gameServer/messages/BoatActionMessage.java index 2fc084e5..419bf72e 100644 --- a/src/main/java/seng302/gameServer/server/messages/BoatActionMessage.java +++ b/src/main/java/seng302/gameServer/messages/BoatActionMessage.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; /** * Created by kre39 on 12/07/17. diff --git a/src/main/java/seng302/gameServer/server/messages/BoatLocationMessage.java b/src/main/java/seng302/gameServer/messages/BoatLocationMessage.java similarity index 99% rename from src/main/java/seng302/gameServer/server/messages/BoatLocationMessage.java rename to src/main/java/seng302/gameServer/messages/BoatLocationMessage.java index 72548c12..faf344ff 100644 --- a/src/main/java/seng302/gameServer/server/messages/BoatLocationMessage.java +++ b/src/main/java/seng302/gameServer/messages/BoatLocationMessage.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; public class BoatLocationMessage extends Message { diff --git a/src/main/java/seng302/gameServer/server/messages/BoatStatus.java b/src/main/java/seng302/gameServer/messages/BoatStatus.java similarity index 87% rename from src/main/java/seng302/gameServer/server/messages/BoatStatus.java rename to src/main/java/seng302/gameServer/messages/BoatStatus.java index 2f09dd4f..7837994b 100644 --- a/src/main/java/seng302/gameServer/server/messages/BoatStatus.java +++ b/src/main/java/seng302/gameServer/messages/BoatStatus.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; /** * The current status of a boat diff --git a/src/main/java/seng302/gameServer/server/messages/BoatSubMessage.java b/src/main/java/seng302/gameServer/messages/BoatSubMessage.java similarity index 98% rename from src/main/java/seng302/gameServer/server/messages/BoatSubMessage.java rename to src/main/java/seng302/gameServer/messages/BoatSubMessage.java index d0d57888..f6b78422 100644 --- a/src/main/java/seng302/gameServer/server/messages/BoatSubMessage.java +++ b/src/main/java/seng302/gameServer/messages/BoatSubMessage.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; import java.nio.ByteBuffer; diff --git a/src/main/java/seng302/gameServer/server/messages/ChatterMessage.java b/src/main/java/seng302/gameServer/messages/ChatterMessage.java similarity index 95% rename from src/main/java/seng302/gameServer/server/messages/ChatterMessage.java rename to src/main/java/seng302/gameServer/messages/ChatterMessage.java index 1295b725..f312109f 100644 --- a/src/main/java/seng302/gameServer/server/messages/ChatterMessage.java +++ b/src/main/java/seng302/gameServer/messages/ChatterMessage.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; /** * Created by kre39 on 20/07/17. diff --git a/src/main/java/seng302/gameServer/server/messages/ClientType.java b/src/main/java/seng302/gameServer/messages/ClientType.java similarity index 93% rename from src/main/java/seng302/gameServer/server/messages/ClientType.java rename to src/main/java/seng302/gameServer/messages/ClientType.java index b96ca5c7..13be441e 100644 --- a/src/main/java/seng302/gameServer/server/messages/ClientType.java +++ b/src/main/java/seng302/gameServer/messages/ClientType.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; public enum ClientType { SPECTATOR(0x00), diff --git a/src/main/java/seng302/gameServer/server/messages/DeviceType.java b/src/main/java/seng302/gameServer/messages/DeviceType.java similarity index 82% rename from src/main/java/seng302/gameServer/server/messages/DeviceType.java rename to src/main/java/seng302/gameServer/messages/DeviceType.java index 4f6d7b80..54925b6d 100644 --- a/src/main/java/seng302/gameServer/server/messages/DeviceType.java +++ b/src/main/java/seng302/gameServer/messages/DeviceType.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; public enum DeviceType { UNKNOWN(0), diff --git a/src/main/java/seng302/gameServer/server/messages/Header.java b/src/main/java/seng302/gameServer/messages/Header.java similarity index 98% rename from src/main/java/seng302/gameServer/server/messages/Header.java rename to src/main/java/seng302/gameServer/messages/Header.java index 074a8ab0..1e388b0d 100644 --- a/src/main/java/seng302/gameServer/server/messages/Header.java +++ b/src/main/java/seng302/gameServer/messages/Header.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; import java.nio.ByteBuffer; diff --git a/src/main/java/seng302/gameServer/server/messages/Heartbeat.java b/src/main/java/seng302/gameServer/messages/Heartbeat.java similarity index 92% rename from src/main/java/seng302/gameServer/server/messages/Heartbeat.java rename to src/main/java/seng302/gameServer/messages/Heartbeat.java index 688aaf40..0a81ceec 100644 --- a/src/main/java/seng302/gameServer/server/messages/Heartbeat.java +++ b/src/main/java/seng302/gameServer/messages/Heartbeat.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; public class Heartbeat extends Message { private final int MESSAGE_SIZE = 4; diff --git a/src/main/java/seng302/gameServer/server/messages/MarkRoundingMessage.java b/src/main/java/seng302/gameServer/messages/MarkRoundingMessage.java similarity index 95% rename from src/main/java/seng302/gameServer/server/messages/MarkRoundingMessage.java rename to src/main/java/seng302/gameServer/messages/MarkRoundingMessage.java index f0d68e15..c32a6927 100644 --- a/src/main/java/seng302/gameServer/server/messages/MarkRoundingMessage.java +++ b/src/main/java/seng302/gameServer/messages/MarkRoundingMessage.java @@ -1,6 +1,4 @@ -package seng302.gameServer.server.messages; - -import seng302.gameServer.GameState; +package seng302.gameServer.messages; public class MarkRoundingMessage extends Message{ private final long MESSAGE_VERSION_NUMBER = 1; diff --git a/src/main/java/seng302/gameServer/server/messages/MarkType.java b/src/main/java/seng302/gameServer/messages/MarkType.java similarity index 85% rename from src/main/java/seng302/gameServer/server/messages/MarkType.java rename to src/main/java/seng302/gameServer/messages/MarkType.java index 6e6ae116..e67a9509 100644 --- a/src/main/java/seng302/gameServer/server/messages/MarkType.java +++ b/src/main/java/seng302/gameServer/messages/MarkType.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; /** * Types of marks boats can round diff --git a/src/main/java/seng302/gameServer/server/messages/Message.java b/src/main/java/seng302/gameServer/messages/Message.java similarity index 99% rename from src/main/java/seng302/gameServer/server/messages/Message.java rename to src/main/java/seng302/gameServer/messages/Message.java index cabba2b3..691bb022 100644 --- a/src/main/java/seng302/gameServer/server/messages/Message.java +++ b/src/main/java/seng302/gameServer/messages/Message.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; import java.nio.ByteBuffer; import java.nio.ByteOrder; diff --git a/src/main/java/seng302/gameServer/server/messages/MessageType.java b/src/main/java/seng302/gameServer/messages/MessageType.java similarity index 94% rename from src/main/java/seng302/gameServer/server/messages/MessageType.java rename to src/main/java/seng302/gameServer/messages/MessageType.java index 4552d781..ea8d8171 100644 --- a/src/main/java/seng302/gameServer/server/messages/MessageType.java +++ b/src/main/java/seng302/gameServer/messages/MessageType.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; /** * Enum containing the types of messages diff --git a/src/main/java/seng302/gameServer/server/messages/RaceStartNotificationType.java b/src/main/java/seng302/gameServer/messages/RaceStartNotificationType.java similarity index 88% rename from src/main/java/seng302/gameServer/server/messages/RaceStartNotificationType.java rename to src/main/java/seng302/gameServer/messages/RaceStartNotificationType.java index 7f9543ff..4824e349 100644 --- a/src/main/java/seng302/gameServer/server/messages/RaceStartNotificationType.java +++ b/src/main/java/seng302/gameServer/messages/RaceStartNotificationType.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; /** * The types of race start status messages diff --git a/src/main/java/seng302/gameServer/server/messages/RaceStartStatusMessage.java b/src/main/java/seng302/gameServer/messages/RaceStartStatusMessage.java similarity index 97% rename from src/main/java/seng302/gameServer/server/messages/RaceStartStatusMessage.java rename to src/main/java/seng302/gameServer/messages/RaceStartStatusMessage.java index ddb960c0..9de0a7b2 100644 --- a/src/main/java/seng302/gameServer/server/messages/RaceStartStatusMessage.java +++ b/src/main/java/seng302/gameServer/messages/RaceStartStatusMessage.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; public class RaceStartStatusMessage extends Message { private final int MESSAGE_SIZE = 20; diff --git a/src/main/java/seng302/gameServer/server/messages/RaceStatus.java b/src/main/java/seng302/gameServer/messages/RaceStatus.java similarity index 91% rename from src/main/java/seng302/gameServer/server/messages/RaceStatus.java rename to src/main/java/seng302/gameServer/messages/RaceStatus.java index 6880ab06..82adfb5b 100644 --- a/src/main/java/seng302/gameServer/server/messages/RaceStatus.java +++ b/src/main/java/seng302/gameServer/messages/RaceStatus.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; /** * The current status of the race diff --git a/src/main/java/seng302/gameServer/server/messages/RaceStatusMessage.java b/src/main/java/seng302/gameServer/messages/RaceStatusMessage.java similarity index 98% rename from src/main/java/seng302/gameServer/server/messages/RaceStatusMessage.java rename to src/main/java/seng302/gameServer/messages/RaceStatusMessage.java index 5cddf0bf..e1c9af55 100644 --- a/src/main/java/seng302/gameServer/server/messages/RaceStatusMessage.java +++ b/src/main/java/seng302/gameServer/messages/RaceStatusMessage.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; import java.util.List; import java.util.zip.CRC32; @@ -39,7 +39,7 @@ public class RaceStatusMessage extends Message{ this.raceId = raceId; this.raceStatus = raceStatus; this.expectedStartTime = expectedStartTime; - this.raceWindDirection = raceWindDirection * windDirFactor; + this.raceWindDirection = raceWindDirection * windDirFactor+100.0; this.windSpeed = windSpeed; this.numBoatsInRace = numBoatsInRace; this.raceType = raceType; diff --git a/src/main/java/seng302/gameServer/server/messages/RaceType.java b/src/main/java/seng302/gameServer/messages/RaceType.java similarity index 85% rename from src/main/java/seng302/gameServer/server/messages/RaceType.java rename to src/main/java/seng302/gameServer/messages/RaceType.java index 5d601083..52e0d628 100644 --- a/src/main/java/seng302/gameServer/server/messages/RaceType.java +++ b/src/main/java/seng302/gameServer/messages/RaceType.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; /** * Enum containing the types of races diff --git a/src/main/java/seng302/gameServer/server/messages/RegistrationRequestMessage.java b/src/main/java/seng302/gameServer/messages/RegistrationRequestMessage.java similarity index 91% rename from src/main/java/seng302/gameServer/server/messages/RegistrationRequestMessage.java rename to src/main/java/seng302/gameServer/messages/RegistrationRequestMessage.java index 59757a18..c7b2a1db 100644 --- a/src/main/java/seng302/gameServer/server/messages/RegistrationRequestMessage.java +++ b/src/main/java/seng302/gameServer/messages/RegistrationRequestMessage.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; public class RegistrationRequestMessage extends Message { diff --git a/src/main/java/seng302/gameServer/server/messages/RegistrationResponseMessage.java b/src/main/java/seng302/gameServer/messages/RegistrationResponseMessage.java similarity index 91% rename from src/main/java/seng302/gameServer/server/messages/RegistrationResponseMessage.java rename to src/main/java/seng302/gameServer/messages/RegistrationResponseMessage.java index e2174da4..28700210 100644 --- a/src/main/java/seng302/gameServer/server/messages/RegistrationResponseMessage.java +++ b/src/main/java/seng302/gameServer/messages/RegistrationResponseMessage.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; public class RegistrationResponseMessage extends Message{ diff --git a/src/main/java/seng302/gameServer/server/messages/RegistrationResponseStatus.java b/src/main/java/seng302/gameServer/messages/RegistrationResponseStatus.java similarity index 95% rename from src/main/java/seng302/gameServer/server/messages/RegistrationResponseStatus.java rename to src/main/java/seng302/gameServer/messages/RegistrationResponseStatus.java index 2ae47a92..90e579bf 100644 --- a/src/main/java/seng302/gameServer/server/messages/RegistrationResponseStatus.java +++ b/src/main/java/seng302/gameServer/messages/RegistrationResponseStatus.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; public enum RegistrationResponseStatus { SUCCESS_SPECTATING(0x00), diff --git a/src/main/java/seng302/gameServer/server/messages/RoundingBoatStatus.java b/src/main/java/seng302/gameServer/messages/RoundingBoatStatus.java similarity index 86% rename from src/main/java/seng302/gameServer/server/messages/RoundingBoatStatus.java rename to src/main/java/seng302/gameServer/messages/RoundingBoatStatus.java index 3b0a6d09..324c03e3 100644 --- a/src/main/java/seng302/gameServer/server/messages/RoundingBoatStatus.java +++ b/src/main/java/seng302/gameServer/messages/RoundingBoatStatus.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; /** * The status of a boat rounding a mark diff --git a/src/main/java/seng302/gameServer/server/messages/RoundingSide.java b/src/main/java/seng302/gameServer/messages/RoundingSide.java similarity index 96% rename from src/main/java/seng302/gameServer/server/messages/RoundingSide.java rename to src/main/java/seng302/gameServer/messages/RoundingSide.java index efb4c929..ffcc7f6c 100644 --- a/src/main/java/seng302/gameServer/server/messages/RoundingSide.java +++ b/src/main/java/seng302/gameServer/messages/RoundingSide.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; /** * The side the boat rounded the mark diff --git a/src/main/java/seng302/gameServer/server/messages/XMLMessage.java b/src/main/java/seng302/gameServer/messages/XMLMessage.java similarity index 97% rename from src/main/java/seng302/gameServer/server/messages/XMLMessage.java rename to src/main/java/seng302/gameServer/messages/XMLMessage.java index 6bcd42ca..361bac0e 100644 --- a/src/main/java/seng302/gameServer/server/messages/XMLMessage.java +++ b/src/main/java/seng302/gameServer/messages/XMLMessage.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; public class XMLMessage extends Message{ private final MessageType MESSAGE_TYPE = MessageType.XML_MESSAGE; diff --git a/src/main/java/seng302/gameServer/server/messages/XMLMessageSubType.java b/src/main/java/seng302/gameServer/messages/XMLMessageSubType.java similarity index 86% rename from src/main/java/seng302/gameServer/server/messages/XMLMessageSubType.java rename to src/main/java/seng302/gameServer/messages/XMLMessageSubType.java index 3b4bdc91..64757c5d 100644 --- a/src/main/java/seng302/gameServer/server/messages/XMLMessageSubType.java +++ b/src/main/java/seng302/gameServer/messages/XMLMessageSubType.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; /** * Enum containing the types of XML messages diff --git a/src/main/java/seng302/gameServer/server/messages/YachtEventCodeMessage.java b/src/main/java/seng302/gameServer/messages/YachtEventCodeMessage.java similarity index 96% rename from src/main/java/seng302/gameServer/server/messages/YachtEventCodeMessage.java rename to src/main/java/seng302/gameServer/messages/YachtEventCodeMessage.java index 08db575a..eb9f557e 100644 --- a/src/main/java/seng302/gameServer/server/messages/YachtEventCodeMessage.java +++ b/src/main/java/seng302/gameServer/messages/YachtEventCodeMessage.java @@ -1,4 +1,4 @@ -package seng302.gameServer.server.messages; +package seng302.gameServer.messages; /** * Created by zyt10 on 10/08/17. diff --git a/src/main/java/seng302/model/ClientYacht.java b/src/main/java/seng302/model/ClientYacht.java index 564754b0..61447b3f 100644 --- a/src/main/java/seng302/model/ClientYacht.java +++ b/src/main/java/seng302/model/ClientYacht.java @@ -8,6 +8,8 @@ import java.util.Observable; import java.util.Observer; import javafx.beans.property.ReadOnlyDoubleProperty; import javafx.beans.property.ReadOnlyDoubleWrapper; +import javafx.beans.property.ReadOnlyIntegerProperty; +import javafx.beans.property.ReadOnlyIntegerWrapper; import javafx.beans.property.ReadOnlyLongProperty; import javafx.beans.property.ReadOnlyLongWrapper; import javafx.scene.paint.Color; @@ -28,19 +30,24 @@ public class ClientYacht extends Observable { Boolean sailsIn, double velocity); } + @FunctionalInterface + public interface MarkRoundingListener { + void notifyRounding(ClientYacht yacht, CompoundMark markPassed, int legNumber); + } + private Logger logger = LoggerFactory.getLogger(ClientYacht.class); - //BOTH AFAIK private String boatType; private Integer sourceId; private String hullID; //matches HullNum in the XML spec. private String shortName; private String boatName; private String country; + private Integer position; private Long estimateTimeAtFinish; - private Boolean sailIn = false; + private Boolean sailIn = true; private Integer currentMarkSeqID = 0; private Long markRoundTime; private Long timeTillNext; @@ -50,13 +57,13 @@ public class ClientYacht extends Observable { private Integer boatStatus; private Double currentVelocity; - //CLIENT SIDE private List locationListeners = new ArrayList<>(); + private List markRoundingListeners = new ArrayList<>(); private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper(); private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper(); private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper(); + private ReadOnlyIntegerWrapper placingProperty = new ReadOnlyIntegerWrapper(); private CompoundMark lastMarkRounded; - private Integer positionInt = 0; private Color colour; public ClientYacht(String boatType, Integer sourceId, String hullID, String shortName, @@ -145,12 +152,16 @@ public class ClientYacht extends Observable { this.estimateTimeAtFinish = estimateTimeAtFinish; } - public Integer getPositionInteger() { - return positionInt; + public Integer getPlacing() { + return placingProperty.get(); } - public void setPositionInteger(Integer position) { - this.positionInt = position; + public void setPlacing(Integer position) { + placingProperty.set(position); + } + + public ReadOnlyIntegerProperty placingProperty() { + return placingProperty.getReadOnlyProperty(); } public void updateVelocityProperty(double velocity) { @@ -189,6 +200,14 @@ public class ClientYacht extends Observable { return location; } + public Integer getPosition() { + return position; + } + + public void setPosition(Integer position) { + this.position = position; + } + public void toggleSail() { sailIn = !sailIn; } @@ -239,14 +258,6 @@ public class ClientYacht extends Observable { this.colour = colour; } -// public Double getCurrentVelocity() { -// return currentVelocity; -// } -// -// public void setCurrentVelocity(Double currentVelocity) { -// this.currentVelocity = currentVelocity; -// } - public void updateLocation(double lat, double lng, double heading, double velocity) { setLocation(lat, lng); @@ -262,7 +273,25 @@ public class ClientYacht extends Observable { locationListeners.add(listener); } + public void addMarkRoundingListener(MarkRoundingListener listener) { + markRoundingListeners.add(listener); + } + + public void removeMarkRoundingListener(MarkRoundingListener listener) { + markRoundingListeners.remove(listener); + } + public boolean getSailIn () { return sailIn; } + + public void roundMark(CompoundMark mark, long markRoundTime, long timeSinceLastMark) { + this.markRoundTime = markRoundTime; + timeSinceLastMarkProperty.set(timeSinceLastMark); + lastMarkRounded = mark; + legNumber += 1; + for (MarkRoundingListener listener : markRoundingListeners) { + listener.notifyRounding(this, lastMarkRounded, legNumber); + } + } } diff --git a/src/main/java/seng302/model/RaceState.java b/src/main/java/seng302/model/RaceState.java index e426dc09..501a3417 100644 --- a/src/main/java/seng302/model/RaceState.java +++ b/src/main/java/seng302/model/RaceState.java @@ -2,7 +2,17 @@ package seng302.model; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Observable; import java.util.TimeZone; +import javafx.beans.property.ReadOnlyDoubleProperty; +import javafx.beans.property.ReadOnlyDoubleWrapper; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import seng302.model.stream.parser.RaceStartData; import seng302.model.stream.parser.RaceStatusData; @@ -12,23 +22,31 @@ import seng302.model.stream.parser.RaceStatusData; */ public class RaceState { + @FunctionalInterface + public interface CollisionListener { + void notifyCollision(GeoPoint location); + } + // private final DateFormat DATE_TIME_FORMAT = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); private final DateFormat DATE_TIME_FORMAT = new SimpleDateFormat("HH:mm:ss"); - - private double windSpeed; - private double windDirection; - private long raceTime; + private ReadOnlyDoubleWrapper windSpeed = new ReadOnlyDoubleWrapper(); + private ReadOnlyDoubleWrapper windDirection = new ReadOnlyDoubleWrapper(); + private long serverSystemTime; private long expectedStartTime; private boolean isRaceStarted = false; -// long timeTillStart; + long timeTillStart; + private ObservableList playerPositions; + private List collisions = new ArrayList<>(); + private List collisionListeners = new ArrayList<>(); public RaceState() { + playerPositions = FXCollections.observableArrayList(); } public void updateState (RaceStatusData data) { - this.windSpeed = data.getWindSpeed(); - this.windDirection = data.getWindDirection(); - this.raceTime = data.getCurrentTime(); + this.windSpeed.set(data.getWindSpeed()); + this.windDirection.set(data.getWindDirection()); + this.serverSystemTime = data.getCurrentTime(); this.expectedStartTime = data.getExpectedStartTime(); this.isRaceStarted = data.isRaceStarted(); } @@ -38,35 +56,67 @@ public class RaceState { } public void updateState (RaceStartData data) { -// this.timeTillStart = data.getRaceStartTime(); - System.out.println(data.getRaceStartTime()); + this.timeTillStart = data.getRaceStartTime(); } public String getRaceTimeStr () { - return DATE_TIME_FORMAT.format(raceTime); + long raceTime = serverSystemTime - expectedStartTime; + if (raceTime < 0) { + return "-" + DATE_TIME_FORMAT.format(-1 * (raceTime - 1000)); + } else { + return DATE_TIME_FORMAT.format(serverSystemTime - expectedStartTime); + } } public long getTimeTillStart () { - return (expectedStartTime - raceTime) / 1000; + return (expectedStartTime - serverSystemTime); } public double getWindSpeed() { - return windSpeed; + return windSpeed.doubleValue(); } - public double getWindDirection() { - return windDirection; + public ReadOnlyDoubleProperty windSpeedProperty() { + return windSpeed.getReadOnlyProperty(); + } + + public ReadOnlyDoubleProperty windDirectionProperty() { + return windDirection.getReadOnlyProperty(); } public long getRaceTime() { - return raceTime; - } - - public long getExpectedStartTime() { - return expectedStartTime; + return serverSystemTime; } public boolean isRaceStarted () { return isRaceStarted; } + + public void setBoats(Collection clientYachts) { + playerPositions.setAll(clientYachts); + } + + public void sortPlayers() { + playerPositions.sort((yacht1, yacht2) -> Integer.compare(yacht2.getLegNumber(), + yacht1.getLegNumber())); + } + + public ObservableList getPlayerPositions() { + return playerPositions; + } + + public void storeCollision(ClientYacht yacht) { + collisions.add(yacht); + for (CollisionListener collisionListener : collisionListeners) { + collisionListener.notifyCollision(yacht.getLocation()); + } + } + + public void addCollisionListener(CollisionListener collisionListener) { + collisionListeners.add(collisionListener); + } + + public void removeCollisionListener(CollisionListener collisionListener) { + collisionListeners.remove(collisionListener); + } } diff --git a/src/main/java/seng302/model/ServerYacht.java b/src/main/java/seng302/model/ServerYacht.java index 2f10929c..34f45f20 100644 --- a/src/main/java/seng302/model/ServerYacht.java +++ b/src/main/java/seng302/model/ServerYacht.java @@ -6,7 +6,7 @@ import java.util.Observer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import seng302.gameServer.GameState; -import seng302.gameServer.server.messages.BoatStatus; +import seng302.gameServer.messages.BoatStatus; import seng302.model.mark.Mark; import seng302.utilities.GeoUtility; @@ -40,6 +40,7 @@ public class ServerYacht extends Observable { private Double currentVelocity; private Boolean isAuto; private Double autoHeading; + private Integer legNumber; //Mark Rounding private Integer currentMarkSeqID; @@ -60,11 +61,12 @@ public class ServerYacht extends Observable { this.country = country; this.sailIn = false; this.isAuto = false; - this.location = new GeoPoint(57.670341, 11.826856); + this.location = new GeoPoint(57.67046, 11.83751); this.lastLocation = location; this.heading = 120.0; //In degrees this.currentVelocity = 0d; //in mms-1 this.currentMarkSeqID = 0; + this.legNumber = 0; this.hasEnteredRoundingZone = false; this.hasPassedLine = false; @@ -381,5 +383,12 @@ public class ServerYacht extends Observable { return hasPassedLine; } + public void incrementLegNumber() { + legNumber++; + } + + public Integer getLegNumber() { + return legNumber; + } } diff --git a/src/main/java/seng302/model/mark/CompoundMark.java b/src/main/java/seng302/model/mark/CompoundMark.java index 70cd114f..3f7ba027 100644 --- a/src/main/java/seng302/model/mark/CompoundMark.java +++ b/src/main/java/seng302/model/mark/CompoundMark.java @@ -2,7 +2,7 @@ package seng302.model.mark; import java.util.ArrayList; import java.util.List; -import seng302.gameServer.server.messages.RoundingSide; +import seng302.gameServer.messages.RoundingSide; import seng302.model.GeoPoint; import seng302.utilities.GeoUtility; @@ -55,7 +55,7 @@ public class CompoundMark { this.name = name; } - public void setRoundingSide(RoundingSide roundingSide) { + public void setRoundingSide(RoundingSide roundingSide) {; switch (roundingSide) { case SP: getSubMark(1).setRoundingSide(RoundingSide.STARBOARD); diff --git a/src/main/java/seng302/model/mark/Mark.java b/src/main/java/seng302/model/mark/Mark.java index de66166c..b5b65e7e 100644 --- a/src/main/java/seng302/model/mark/Mark.java +++ b/src/main/java/seng302/model/mark/Mark.java @@ -2,7 +2,7 @@ package seng302.model.mark; import java.util.ArrayList; import java.util.List; -import seng302.gameServer.server.messages.RoundingSide; +import seng302.gameServer.messages.RoundingSide; import seng302.model.GeoPoint; /** diff --git a/src/main/java/seng302/model/mark/MarkOrder.java b/src/main/java/seng302/model/mark/MarkOrder.java index aa75b494..ab3a1848 100644 --- a/src/main/java/seng302/model/mark/MarkOrder.java +++ b/src/main/java/seng302/model/mark/MarkOrder.java @@ -10,7 +10,7 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; -import seng302.gameServer.server.messages.RoundingSide; +import seng302.gameServer.messages.RoundingSide; import seng302.model.stream.xml.generator.Race; import seng302.model.stream.xml.parser.RaceXMLData; import seng302.utilities.XMLGenerator; @@ -104,10 +104,11 @@ public class MarkOrder { List corners = data.getMarkSequence(); Map marks = data.getCompoundMarks(); List course = new ArrayList<>(); - for (Corner corner : corners){ CompoundMark compoundMark = marks.get(corner.getCompoundMarkID()); - compoundMark.setRoundingSide(RoundingSide.getRoundingSide(corner.getRounding())); + compoundMark.setRoundingSide( + RoundingSide.getRoundingSide(corner.getRounding()) + ); course.add(compoundMark); allMarks.addAll(compoundMark.getMarks()); } diff --git a/src/main/java/seng302/model/stream/xml/generator/Regatta.java b/src/main/java/seng302/model/stream/xml/generator/Regatta.java index 4a90368a..fa802e01 100644 --- a/src/main/java/seng302/model/stream/xml/generator/Regatta.java +++ b/src/main/java/seng302/model/stream/xml/generator/Regatta.java @@ -18,10 +18,10 @@ public class Regatta { private Integer utcOffset; private Double magneticVariation; - public Regatta(String name, Double latitude, Double longitude) { + public Regatta(String name, String courseName, Double latitude, Double longitude) { this.name = name; this.id = DEFAULT_REGATTA_ID; - this.courseName = name; + this.courseName = courseName; this.latitude = latitude; this.longitude = longitude; diff --git a/src/main/java/seng302/utilities/StreamParser.java b/src/main/java/seng302/utilities/StreamParser.java index 304b105d..1f90eac8 100644 --- a/src/main/java/seng302/utilities/StreamParser.java +++ b/src/main/java/seng302/utilities/StreamParser.java @@ -95,13 +95,8 @@ public class StreamParser { boatID = bytesToLong( Arrays.copyOfRange(payload, 24 + (i * 20), 28 + (i * 20))); boatStatus = (int) payload[28 + (i * 20)]; - -// setBoatLegPosition(boat, (int) payload[29 + (i * 20)]); -// boat.setPenaltiesAwarded((int) payload[30 + (i * 20)]); -// boat.setPenaltiesServed((int) payload[31 + (i * 20)]); estTimeAtNextMark = bytesToLong( Arrays.copyOfRange(payload, 32 + (i * 20), 38 + (i * 20))); -// boat.setEstimateTimeTillNextMark(estTimeAtNextMark); estTimeAtFinish = bytesToLong( Arrays.copyOfRange(payload, 38 + (i * 20), 44 + (i * 20))); leg = (int) payload[29 + (i * 20)]; @@ -119,12 +114,12 @@ public class StreamParser { // placing += 1; // } // } -// updatingBoat.setPosition(placing.toString()); +// updatingBoat.setPlacing(placing.toString()); // updatingBoat.setLegNumber(leg); // boatsPos.putIfAbsent(placing, updatingBoat); // boatsPos.replace(placing, updatingBoat); // } else if(updatingBoat.getLegNumber() == null){ -// updatingBoat.setPosition("1"); +// updatingBoat.setPlacing("1"); // updatingBoat.setLegNumber(leg); // } // } diff --git a/src/main/java/seng302/utilities/XMLGenerator.java b/src/main/java/seng302/utilities/XMLGenerator.java index 3dc99694..7fcc8efd 100644 --- a/src/main/java/seng302/utilities/XMLGenerator.java +++ b/src/main/java/seng302/utilities/XMLGenerator.java @@ -9,7 +9,7 @@ import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import seng302.model.stream.xml.generator.Race; import seng302.model.stream.xml.generator.Regatta; -import seng302.gameServer.server.messages.XMLMessageSubType; +import seng302.gameServer.messages.XMLMessageSubType; /** * An XML generator to generate the Race, Boat, and Regatta XML dynamically diff --git a/src/main/java/seng302/visualiser/ClientToServerThread.java b/src/main/java/seng302/visualiser/ClientToServerThread.java index 9f76d37c..dbf93693 100644 --- a/src/main/java/seng302/visualiser/ClientToServerThread.java +++ b/src/main/java/seng302/visualiser/ClientToServerThread.java @@ -21,12 +21,12 @@ import javafx.scene.control.Alert.AlertType; import javafx.scene.control.ButtonType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import seng302.gameServer.server.messages.BoatAction; -import seng302.gameServer.server.messages.BoatActionMessage; -import seng302.gameServer.server.messages.ClientType; -import seng302.gameServer.server.messages.Message; -import seng302.gameServer.server.messages.RegistrationRequestMessage; -import seng302.gameServer.server.messages.RegistrationResponseStatus; +import seng302.gameServer.messages.BoatAction; +import seng302.gameServer.messages.BoatActionMessage; +import seng302.gameServer.messages.ClientType; +import seng302.gameServer.messages.Message; +import seng302.gameServer.messages.RegistrationRequestMessage; +import seng302.gameServer.messages.RegistrationResponseStatus; import seng302.model.stream.packets.PacketType; import seng302.model.stream.packets.StreamPacket; diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index 8acf2c46..82129046 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -12,8 +12,10 @@ import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.scene.input.KeyEvent; import javafx.scene.layout.Pane; +import seng302.gameServer.GameState; import seng302.gameServer.MainServerThread; -import seng302.gameServer.server.messages.BoatAction; +import seng302.gameServer.messages.BoatAction; +import seng302.gameServer.messages.BoatStatus; import seng302.model.ClientYacht; import seng302.model.RaceState; import seng302.model.stream.packets.StreamPacket; @@ -26,6 +28,7 @@ import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RegattaXMLData; import seng302.utilities.StreamParser; import seng302.utilities.XMLParser; +import seng302.visualiser.controllers.FinishScreenViewController; import seng302.visualiser.controllers.LobbyController; import seng302.visualiser.controllers.LobbyController.CloseStatus; import seng302.visualiser.controllers.RaceViewController; @@ -46,6 +49,7 @@ public class GameClient { private RegattaXMLData regattaData; private RaceXMLData courseData; private RaceState raceState = new RaceState(); + private LobbyController lobbyController; private ObservableList clientLobbyList = FXCollections.observableArrayList(); @@ -75,8 +79,18 @@ public class GameClient { LobbyController lobbyController = loadLobby(); lobbyController.setPlayerListSource(clientLobbyList); lobbyController.disableReadyButton(); - lobbyController.setTitle("Connected to host - IP : " + ipAddress + " Port : " + portNumber); + + if (regattaData != null){ + lobbyController.setTitle(regattaData.getRegattaName()); + lobbyController.setCourseName(regattaData.getCourseName()); + } + else{ + lobbyController.setTitle(ipAddress); + lobbyController.setCourseName(""); + } + lobbyController.addCloseListener((exitCause) -> this.loadStartScreen()); + this.lobbyController = lobbyController; } /** @@ -95,15 +109,27 @@ public class GameClient { socketThread.addStreamObserver(this::parsePackets); LobbyController lobbyController = loadLobby(); lobbyController.setPlayerListSource(clientLobbyList); - lobbyController.setTitle("Hosting Lobby - IP : " + ipAddress + " Port : " + portNumber); + + if (regattaData != null){ + lobbyController.setTitle("Hosting: " + regattaData.getRegattaName()); + lobbyController.setCourseName(regattaData.getCourseName()); + } + else{ + lobbyController.setTitle("Hosting: " + ipAddress); + lobbyController.setCourseName(""); + } + lobbyController.addCloseListener(exitCause -> { if (exitCause == CloseStatus.READY) { + GameState.resetStartTime(); + lobbyController.disableReadyButton(); server.startGame(); } else if (exitCause == CloseStatus.LEAVE) { loadStartScreen(); } }); + this.lobbyController = lobbyController; server.setGameClient(this); } @@ -141,17 +167,7 @@ public class GameClient { } private void loadRaceView() { - FXMLLoader fxmlLoader = new FXMLLoader( - RaceViewController.class.getResource("/views/RaceView.fxml")); - try { - final Node node = fxmlLoader.load(); - Platform.runLater(() -> { - holderPane.getChildren().clear(); - holderPane.getChildren().add(node); - }); - } catch (IOException e) { - e.printStackTrace(); - } + FXMLLoader fxmlLoader = loadFXMLToHolder("/views/RaceView.fxml"); holderPane.getScene().setOnKeyPressed(this::keyPressed); holderPane.getScene().setOnKeyReleased(this::keyReleased); raceView = fxmlLoader.getController(); @@ -160,17 +176,25 @@ public class GameClient { } private void loadFinishScreenView() { + FXMLLoader fxmlLoader = loadFXMLToHolder("/views/FinishScreenView.fxml"); + FinishScreenViewController controller = fxmlLoader.getController(); + controller.setFinishers(raceState.getPlayerPositions()); + } + + private FXMLLoader loadFXMLToHolder(String fxmlLocation) { FXMLLoader fxmlLoader = new FXMLLoader( - getClass().getResource("/views/FinishScreenView.fxml")); + getClass().getResource(fxmlLocation) + ); try { - final Node finishScreenFX = fxmlLoader.load(); + final Node fxmlLoaderFX = fxmlLoader.load(); Platform.runLater(() -> { holderPane.getChildren().clear(); - holderPane.getChildren().add(finishScreenFX); + holderPane.getChildren().add(fxmlLoaderFX); }); } catch (IOException e) { e.printStackTrace(); } + return fxmlLoader; } private void parsePackets() { @@ -179,13 +203,18 @@ public class GameClient { switch (packet.getType()) { case RACE_STATUS: processRaceStatusUpdate(StreamParser.extractRaceStatus(packet)); - startRaceIfAllDataReceived(); + + if (raceState.getTimeTillStart() <= 5000) { + startRaceIfAllDataReceived(); + } + break; case REGATTA_XML: regattaData = XMLParser.parseRegatta( StreamParser.extractXmlMessage(packet) ); + raceState.setTimeZone( TimeZone.getTimeZone( ZoneId.ofOffset("UTC", ZoneOffset.ofHours(regattaData.getUtcOffset())) @@ -210,10 +239,12 @@ public class GameClient { allBoatsMap.forEach((id, boat) -> clientLobbyList.add(id + " " + boat.getBoatName()) ); + raceState.setBoats(allBoatsMap.values()); break; case RACE_START_STATUS: raceState.updateState(StreamParser.extractRaceStartStatus(packet)); + if (lobbyController != null) lobbyController.updateRaceState(raceState); break; case BOAT_LOCATION: @@ -247,8 +278,8 @@ public class GameClient { private void updatePosition(PositionUpdateData positionData) { if (positionData.getType() == DeviceType.YACHT_TYPE) { if (allXMLReceived() && allBoatsMap.containsKey(positionData.getDeviceId())) { - ClientYacht clientYacht = allBoatsMap.get(positionData.getDeviceId()); - clientYacht.updateLocation(positionData.getLat(), + ClientYacht yacht = allBoatsMap.get(positionData.getDeviceId()); + yacht.updateLocation(positionData.getLat(), positionData.getLon(), positionData.getHeading(), positionData.getGroundSpeed()); } @@ -266,13 +297,10 @@ public class GameClient { private void updateMarkRounding(MarkRoundingData roundingData) { if (allXMLReceived()) { ClientYacht clientYacht = allBoatsMap.get(roundingData.getBoatId()); - clientYacht.setMarkRoundingTime(roundingData.getTimeStamp()); - clientYacht.updateTimeSinceLastMarkProperty( - raceState.getRaceTime() - roundingData.getTimeStamp()); - clientYacht.setLastMarkRounded( - courseData.getCompoundMarks().get( - roundingData.getMarkId() - ) + clientYacht.roundMark( + courseData.getCompoundMarks().get(roundingData.getMarkId()), + roundingData.getTimeStamp(), + raceState.getRaceTime() - roundingData.getTimeStamp() ); } } @@ -280,37 +308,36 @@ public class GameClient { private void processRaceStatusUpdate(RaceStatusData data) { if (allXMLReceived()) { raceState.updateState(data); - if (raceView != null) { - raceView.getGameView().setWindDir(raceState.getWindDirection()); - } boolean raceFinished = true; for (ClientYacht yacht : allBoatsMap.values()) { - if (yacht.getBoatStatus() != 3) { + if (yacht.getBoatStatus() != BoatStatus.FINISHED.getCode()) { raceFinished = false; } } - if (raceFinished == true) { - close(); - loadFinishScreenView(); - } for (long[] boatData : data.getBoatData()) { ClientYacht clientYacht = allBoatsMap.get((int) boatData[0]); clientYacht.setEstimateTimeTillNextMark(raceState.getRaceTime() - boatData[1]); clientYacht.setEstimateTimeAtFinish(boatData[2]); int legNumber = (int) boatData[3]; - clientYacht.setLegNumber(legNumber); clientYacht.setBoatStatus((int) boatData[4]); if (legNumber != clientYacht.getLegNumber()) { - int placing = 1; - for (ClientYacht otherClientYacht : allBoatsMap.values()) { - if (otherClientYacht.getSourceId() != boatData[0] && - clientYacht.getLegNumber() <= otherClientYacht.getLegNumber()) - placing++; - } - clientYacht.setPositionInteger(placing); + clientYacht.setLegNumber(legNumber); + updatePlayerPositions(); } } + + if (raceFinished) { + close(); + loadFinishScreenView(); + } + } + } + + private void updatePlayerPositions() { + raceState.sortPlayers(); + for (ClientYacht yacht : raceState.getPlayerPositions()) { + yacht.setPosition(raceState.getPlayerPositions().indexOf(yacht) + 1); } } @@ -333,22 +360,16 @@ public class GameClient { socketThread.sendBoatAction(BoatAction.DOWNWIND); break; case ENTER: // tack/gybe socketThread.sendBoatAction(BoatAction.TACK_GYBE); break; - //TODO Allow a zoom in and zoom out methods - case Z: // zoom in - System.out.println("Zoom in"); - break; - case X: // zoom out - System.out.println("Zoom out"); - break; } } + private void keyReleased(KeyEvent e) { switch (e.getCode()) { //TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet) case SHIFT: // sails in/sails out socketThread.sendBoatAction(BoatAction.SAILS_IN); - raceView.getGameView().getPlayerYacht().toggleSail(); + allBoatsMap.get(socketThread.getClientId()).toggleSail(); break; case PAGE_UP: case PAGE_DOWN: @@ -366,7 +387,11 @@ public class GameClient { private void showCollisionAlert(YachtEventData yachtEventData) { // 33 is the agreed code to show collision if (yachtEventData.getEventId() == 33) { - raceView.showCollision(yachtEventData.getSubjectId()); + raceState.storeCollision( + allBoatsMap.get( + yachtEventData.getSubjectId().intValue() + ) + ); } } } diff --git a/src/main/java/seng302/visualiser/GameView.java b/src/main/java/seng302/visualiser/GameView.java index cf8ecd95..9268f3c1 100644 --- a/src/main/java/seng302/visualiser/GameView.java +++ b/src/main/java/seng302/visualiser/GameView.java @@ -16,6 +16,8 @@ import javafx.geometry.Point2D; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; @@ -23,8 +25,9 @@ import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; import javafx.scene.shape.Polygon; import javafx.scene.text.Text; -import seng302.model.ClientYacht; import javafx.util.Duration; +import seng302.gameServer.messages.RoundingSide; +import seng302.model.ClientYacht; import seng302.model.Colors; import seng302.model.GeoPoint; import seng302.model.Limit; @@ -36,6 +39,7 @@ import seng302.visualiser.fxObjects.AnnotationBox; import seng302.visualiser.fxObjects.BoatObject; import seng302.visualiser.fxObjects.CourseBoundary; import seng302.visualiser.fxObjects.Gate; +import seng302.visualiser.fxObjects.MarkArrowFactory; import seng302.visualiser.fxObjects.Marker; import seng302.visualiser.map.Boundary; import seng302.visualiser.map.CanvasMap; @@ -71,11 +75,13 @@ public class GameView extends Pane { private Map boatObjects = new HashMap<>(); private Map annotations = new HashMap<>(); private ObservableList gameObjects; + private BoatObject selectedBoat = null; private Group annotationsGroup = new Group(); private Group wakesGroup = new Group(); private Group boatObjectGroup = new Group(); private Group trails = new Group(); private Group markers = new Group(); + private List course = new ArrayList<>(); private ImageView mapImage = new ImageView(); @@ -92,19 +98,19 @@ public class GameView extends Pane { double scaleFactor = 1; - public void zoomOut() { - scaleFactor = 0.95; - for (Node child : getChildren()) { - child.setScaleX(child.getScaleX() * scaleFactor); - child.setScaleY(child.getScaleY() * scaleFactor); + private void zoomOut() { + scaleFactor = 0.1; + if (this.getScaleX() > 0.5) { + this.setScaleX(this.getScaleX() - scaleFactor); + this.setScaleY(this.getScaleY() - scaleFactor); } } - public void zoomIn() { - scaleFactor = 1.05; - for (Node child : getChildren()) { - child.setScaleX(child.getScaleX() * scaleFactor); - child.setScaleY(child.getScaleY() * scaleFactor); + private void zoomIn() { + scaleFactor = 0.10; + if (this.getScaleX() < 2.5) { + this.setScaleX(this.getScaleX() + scaleFactor); + this.setScaleY(this.getScaleY() + scaleFactor); } } @@ -113,14 +119,25 @@ public class GameView extends Pane { VERTICAL } - public GameView() { + + private void trackBoat() { + if (selectedBoat != null) { + double x = selectedBoat.getBoatLayoutX(); + double y = selectedBoat.getBoatLayoutY(); + double displacementX = this.getWidth(); + double displacementY = this.getHeight(); + this.setLayoutX((-x + (displacementX / 2.0)) * this.getScaleX()); + this.setLayoutY((-y + (displacementY / 2.0)) * this.getScaleY()); + } else { + this.setLayoutX(0); + this.setLayoutY(0); + } + } + + public GameView () { gameObjects = this.getChildren(); // create image view for map, bind panel size to image gameObjects.add(mapImage); - fpsDisplay.setLayoutX(5); - fpsDisplay.setLayoutY(20); - fpsDisplay.setStrokeWidth(2); - gameObjects.add(fpsDisplay); gameObjects.add(raceBorder); gameObjects.add(markers); initializeTimer(); @@ -138,6 +155,7 @@ public class GameView extends Pane { @Override public void handle(long now) { + trackBoat(); if (lastTime == 0) { lastTime = now; } else { @@ -161,9 +179,7 @@ public class GameView extends Pane { lastTime = now; } } -// Platform.runLater(() -> - boatObjects.forEach((boat, boatObject) -> boatObject.updateLocation()); -// ); + boatObjects.forEach((boat, boatObject) -> boatObject.updateLocation()); } }; } @@ -205,6 +221,7 @@ public class GameView extends Pane { mapImage.fitHeightProperty().bind(((AnchorPane) this.getParent()).heightProperty()); } + // TODO: 16/08/17 Break up this function /** * Adds a course to the GameView. The view is scaled accordingly unless a border is set in which * case the course is added relative ot the border. @@ -214,6 +231,23 @@ public class GameView extends Pane { */ public void updateCourse(List newCourse, List sequence) { markerObjects = new HashMap<>(); + + for (Corner corner : sequence) { //Makes course out of all compound marks. + for (CompoundMark compoundMark : newCourse) { + if (corner.getCompoundMarkID() == compoundMark.getId()) { + course.add(compoundMark); + } + } + } + + // TODO: 16/08/17 Updating mark roundings here. It should not happen here. Nor should it be done this way. + for (Corner corner : sequence){ + CompoundMark compoundMark = course.get(corner.getSeqID() - 1); + compoundMark.setRoundingSide( + RoundingSide.getRoundingSide(corner.getRounding()) + ); + } + final List gates = new ArrayList<>(); Paint colour = Color.BLACK; //Creates new markers @@ -228,16 +262,6 @@ public class GameView extends Pane { for (Mark mark : cMark.getMarks()) { 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 if (cMark.isGate()) { for (int i = 1; i < cMark.getMarks().size(); i++) { @@ -252,6 +276,73 @@ public class GameView extends Pane { } colour = Color.BLACK; } + + //Creating mark arrows. + for (int i=1; i < sequence.size()-1; i++) { //General case. + double averageLat = 0; + double averageLng = 0; + int numMarks = 0; + for (Mark mark : course.get(i-1).getMarks()) { + numMarks += 1; + averageLat += mark.getLat(); + averageLng += mark.getLng(); + } + GeoPoint lastMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks); + numMarks = 0; + averageLat = 0; + averageLng = 0; + for (Mark mark : course.get(i+1).getMarks()) { + numMarks += 1; + averageLat += mark.getLat(); + averageLng += mark.getLng(); + } + GeoPoint nextMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks); + // TODO: 16/08/17 This comparison is cancer and deserves to die. + for (Mark mark : course.get(i).getMarks()) { + markerObjects.get(mark).addArrows( + mark.getRoundingSide() == RoundingSide.STARBOARD ? MarkArrowFactory.RoundingSide.STARBOARD : MarkArrowFactory.RoundingSide.PORT, + GeoUtility.getBearing(lastMarkAv, mark), + GeoUtility.getBearing(mark, nextMarkAv) + ); + } + } + + // TODO: 16/08/17 Make this cleaner + //First mark case + double averageLat = 0; + double averageLng = 0; + int numMarks = 0; + for (Mark mark : course.get(1).getMarks()) { + numMarks += 1; + averageLat += mark.getLat(); + averageLng += mark.getLng(); + } + GeoPoint firstMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks); + for (Mark mark : course.get(0).getMarks()) { + markerObjects.get(mark).addArrows( + mark.getRoundingSide() == RoundingSide.STARBOARD ? MarkArrowFactory.RoundingSide.STARBOARD : MarkArrowFactory.RoundingSide.PORT, + 0d, //90 + GeoUtility.getBearing(mark, firstMarkAv) + ); + } + //Last Mark case + numMarks = 0; + averageLat = 0; + averageLng = 0; + for (Mark mark : course.get(course.size()-2).getMarks()) { + numMarks += 1; + averageLat += mark.getLat(); + averageLng += mark.getLng(); + } + GeoPoint secondToLastMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks); + for (Mark mark : course.get(course.size()-1).getMarks()) { + markerObjects.get(mark).addArrows( + mark.getRoundingSide() == RoundingSide.STARBOARD ? MarkArrowFactory.RoundingSide.STARBOARD : MarkArrowFactory.RoundingSide.PORT, + GeoUtility.getBearing(secondToLastMarkAv, mark), + GeoUtility.getBearing(mark, mark) + ); + } + //Scale race to markers if there is no border. if (borderPoints == null) { rescaleRace(new ArrayList<>(markerObjects.keySet())); @@ -259,8 +350,8 @@ public class GameView extends Pane { //Move the Markers to initial position. markerObjects.forEach(((mark, marker) -> { Point2D p2d = findScaledXY(mark.getLat(), mark.getLng()); - marker.setCenterX(p2d.getX()); - marker.setCenterY(p2d.getY()); + marker.setLayoutX(p2d.getX()); + marker.setLayoutY(p2d.getY()); })); Platform.runLater(() -> { markers.getChildren().clear(); @@ -277,11 +368,12 @@ public class GameView extends Pane { */ private void makeAndBindMarker(Mark observableMark, Paint colour) { Marker marker = new Marker(colour); +// marker.addArrows(MarkArrowFactory.RoundingSide.PORT, ThreadLocalRandom.current().nextDouble(91, 180), ThreadLocalRandom.current().nextDouble(1, 90)); markerObjects.put(observableMark, marker); observableMark.addPositionListener((mark, lat, lon) -> { Point2D p2d = findScaledXY(lat, lon); - markerObjects.get(mark).setCenterX(p2d.getX()); - markerObjects.get(mark).setCenterY(p2d.getY()); + markerObjects.get(mark).setLayoutX(p2d.getX()); + markerObjects.get(mark).setLayoutY(p2d.getY()); }); } @@ -296,16 +388,16 @@ public class GameView extends Pane { private Gate makeAndBindGate(Marker m1, Marker m2, Paint colour) { Gate gate = new Gate(colour); gate.startXProperty().bind( - m1.centerXProperty() + m1.layoutXProperty() ); gate.startYProperty().bind( - m1.centerYProperty() + m1.layoutYProperty() ); gate.endXProperty().bind( - m2.centerXProperty() + m2.layoutXProperty() ); gate.endYProperty().bind( - m2.centerYProperty() + m2.layoutYProperty() ); return gate; } @@ -330,6 +422,21 @@ public class GameView extends Pane { raceBorder.getPoints().setAll(boundaryPoints); } + // TODO: 16/08/17 initialize zooming internal to GameView only + /** + * Enables zoom. Has to be called after this is added to a scene. + */ + public void enableZoom () { + if (this.getScene() != null) { + this.getScene().addEventHandler(KeyEvent.KEY_PRESSED, (event) -> { + if (event.getCode() == KeyCode.Z) { + zoomIn(); + } else if (event.getCode() == KeyCode.X) { + zoomOut(); + } + }); + } + } /** * Rescales the race to the size of the window. * @@ -343,31 +450,45 @@ public class GameView extends Pane { // drawGoogleMap(); } + private void setSelectedBoat(BoatObject bo, Boolean isSelected) { + if (this.selectedBoat == bo && !isSelected) { + this.selectedBoat = null; + boatObjects.forEach((boat, group) -> + group.setIsSelected(false) + ); + } else if (isSelected) { + this.selectedBoat = bo; + for (BoatObject group : boatObjects.values()) { + if (group != bo) { + group.setIsSelected(false); + } + } + } + } + /** * Draws all the boats. - * @param clientYachts The yachts to set in the race + * @param yachts The yachts to set in the race */ - public void setBoats(List clientYachts) { + public void setBoats(List yachts) { BoatObject newBoat; final List wakes = new ArrayList<>(); - for (ClientYacht clientYacht : clientYachts) { + for (ClientYacht yacht : yachts) { Paint colour = Colors.getColor(); newBoat = new BoatObject(); + newBoat.addSelectedBoatListener(this::setSelectedBoat); newBoat.setFill(colour); - boatObjects.put(clientYacht, newBoat); - createAndBindAnnotationBox(clientYacht, colour); + boatObjects.put(yacht, newBoat); + createAndBindAnnotationBox(yacht, colour); // wakesGroup.getChildren().add(newBoat.getWake()); wakes.add(newBoat.getWake()); boatObjectGroup.getChildren().add(newBoat); trails.getChildren().add(newBoat.getTrail()); // TODO: 1/08/17 Make this less vile to look at. - clientYacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> { + yacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> { BoatObject bo = boatObjects.get(boat); Point2D p2d = findScaledXY(lat, lon); bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir); -// annotations.get(boat).setLayoutX(p2d.getX()); -// annotations.get(boat).setLayoutY(p2d.getY()); -// annotations.get(boat).setLocation(100d, 100d); annotations.get(boat).setLocation(p2d.getX(), p2d.getY()); bo.setTrajectory( heading, @@ -385,11 +506,11 @@ public class GameView extends Pane { }); } - private void createAndBindAnnotationBox(ClientYacht clientYacht, Paint colour) { + private void createAndBindAnnotationBox(ClientYacht yacht, Paint colour) { AnnotationBox newAnnotation = new AnnotationBox(); newAnnotation.setFill(colour); newAnnotation.addAnnotation( - "name", "Player: " + clientYacht.getShortName() + "name", "Player: " + yacht.getShortName() ); // newAnnotation.addAnnotation( // "velocity", @@ -412,7 +533,7 @@ public class GameView extends Pane { // return format.format(time); // } // ); - annotations.put(clientYacht, newAnnotation); + annotations.put(yacht, newAnnotation); } private void drawFps(Double fps) { @@ -614,7 +735,6 @@ public class GameView extends Pane { timer.stop(); } - public void setWindDir(double windDir) { this.windDir = windDir; } @@ -627,10 +747,14 @@ public class GameView extends Pane { return playerYacht; } - public void setBoatAsPlayer (ClientYacht playerYacht) { this.playerYacht = playerYacht; + playerYacht.toggleSail(); boatObjects.get(playerYacht).setAsPlayer(); + CompoundMark currentMark = course.get(playerYacht.getLegNumber()); + for (Mark mark : currentMark.getMarks()) { + markerObjects.get(mark).showNextExitArrow(); + } annotations.get(playerYacht).addAnnotation( "velocity", playerYacht.getVelocityProperty(), @@ -642,6 +766,31 @@ public class GameView extends Pane { annotationsGroup.getChildren().remove(annotations.get(playerYacht)); gameObjects.add(annotations.get(playerYacht)); }); + playerYacht.addMarkRoundingListener(this::updateMarkArrows); + } + + private void updateMarkArrows (ClientYacht yacht, CompoundMark compoundMark, int legNumber) { + //Only show arrows for this and next leg. + if (compoundMark != null) { + for (Mark mark : compoundMark.getMarks()) { + markerObjects.get(mark).showNextExitArrow(); + } + } + CompoundMark nextMark = null; + if (legNumber < course.size() - 1) { + nextMark = course.get(legNumber); + for (Mark mark : nextMark.getMarks()) { + markerObjects.get(mark).showNextEnterArrow(); + } + } + if (legNumber - 2 >= 0) { + CompoundMark lastMark = course.get(Math.max(0, legNumber - 2)); + if (lastMark != nextMark) { + for (Mark mark : lastMark.getMarks()) { + markerObjects.get(mark).hideAllArrows(); + } + } + } } /** @@ -651,32 +800,35 @@ public class GameView extends Pane { * @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); + Point2D point = findScaledXY(collisionPoint); + double circleRadius = 0.0; + Circle circle = new Circle(point.getX(), point.getY(), circleRadius, Color.RED); - circle.setFill(Color.TRANSPARENT); - circle.setStroke(Color.RED); - circle.setStrokeWidth(3); + circle.setFill(Color.TRANSPARENT); + circle.setStroke(Color.RED); + circle.setStrokeWidth(3); - Timeline timeline = new Timeline(); - timeline.setCycleCount(1); + 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)); + 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.getKeyFrames().addAll(keyframe1, keyFrame2, keyFrame3); - timeline.setOnFinished(event -> gameObjects.remove(circle)); - }); + Platform.runLater(() -> gameObjects.add(circle)); + timeline.setOnFinished(event -> Platform.runLater(() -> gameObjects.remove(circle))); + timeline.play(); + } + + public void setFrameRateFXText(Text fpsDisplay) { + this.fpsDisplay = null; + this.fpsDisplay = fpsDisplay; } } diff --git a/src/main/java/seng302/visualiser/controllers/FinishScreenViewController.java b/src/main/java/seng302/visualiser/controllers/FinishScreenViewController.java index a4dc831b..b2e49f1a 100644 --- a/src/main/java/seng302/visualiser/controllers/FinishScreenViewController.java +++ b/src/main/java/seng302/visualiser/controllers/FinishScreenViewController.java @@ -3,6 +3,7 @@ package seng302.visualiser.controllers; import java.io.IOException; import java.net.URL; import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.ResourceBundle; @@ -61,9 +62,9 @@ public class FinishScreenViewController implements Initializable { finishOrderTable.refresh(); } - public void setFinishers(List participants) { + public void setFinishers(Collection participants) { List sorted = new ArrayList<>(participants); - sorted.sort(Comparator.comparingInt(ClientYacht::getPositionInteger)); + sorted.sort(Comparator.comparingInt(ClientYacht::getPlacing)); finishOrderTable.getItems().setAll(sorted); } diff --git a/src/main/java/seng302/visualiser/controllers/LobbyController.java b/src/main/java/seng302/visualiser/controllers/LobbyController.java index 809bd3b5..fa160e53 100644 --- a/src/main/java/seng302/visualiser/controllers/LobbyController.java +++ b/src/main/java/seng302/visualiser/controllers/LobbyController.java @@ -1,8 +1,7 @@ package seng302.visualiser.controllers; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; + import javafx.application.Platform; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; @@ -14,6 +13,8 @@ import javafx.scene.image.ImageView; import javafx.scene.text.Text; import seng302.gameServer.GameStages; import seng302.gameServer.GameState; +import seng302.model.RaceState; +import seng302.visualiser.GameClient; /** * A class describing the actions of the lobby screen @@ -67,9 +68,14 @@ public class LobbyController { private ImageView seventhImageView; @FXML private ImageView eighthImageView; + @FXML + private Text timeUntilStart; + @FXML + private Text courseNameText; private List imageViews = new ArrayList<>(); private List