diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 527656ba..141f90d4 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -36,7 +36,6 @@ import seng302.utilities.XMLParser; * Created by wmu16 on 10/07/17. */ public class GameState implements Runnable { - @FunctionalInterface interface NewMessageListener { @@ -180,6 +179,14 @@ public class GameState implements Runnable { return windDirection; } + public static void setWindDirection(Double newWindDirection) { + windDirection = newWindDirection; + } + + public static void setWindSpeed(Double newWindSpeed) { + windSpeed = newWindSpeed; + } + public static Double getWindSpeedMMS() { return windSpeed; } @@ -250,7 +257,6 @@ public class GameState implements Runnable { } } - /** * Called periodically in this GameState thread to update the GameState values */ @@ -271,6 +277,8 @@ public class GameState implements Runnable { checkForLegProgression(yacht); raceFinished = false; } + + } if (raceFinished) { @@ -329,17 +337,20 @@ public class GameState implements Runnable { notifyMessageListeners( 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()) - ); + } + 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()) + ); + } } } } @@ -426,6 +437,7 @@ public class GameState implements Runnable { } if (hasProgressed) { + yacht.incrementLegNumber(); sendMarkRoundingMessage(yacht); logMarkRounding(yacht); yacht.setHasPassedLine(false); diff --git a/src/main/java/seng302/gameServer/MainServerThread.java b/src/main/java/seng302/gameServer/MainServerThread.java index 95724c98..3abd949d 100644 --- a/src/main/java/seng302/gameServer/MainServerThread.java +++ b/src/main/java/seng302/gameServer/MainServerThread.java @@ -3,17 +3,8 @@ package seng302.gameServer; import java.io.IOException; import java.net.ServerSocket; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; -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 java.util.*; +import seng302.gameServer.server.messages.*; import seng302.model.GeoPoint; import seng302.model.Player; import seng302.model.PolarTable; @@ -29,12 +20,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; @@ -55,6 +51,7 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { GameState.addMarkPassListener(this::broadcastMessage); terminated = false; thread = new Thread(this, "MainServer"); + startUpdatingWind(); thread.start(); } @@ -108,6 +105,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) { @@ -189,7 +225,8 @@ 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); diff --git a/src/main/java/seng302/gameServer/messages/RaceStatusMessage.java b/src/main/java/seng302/gameServer/messages/RaceStatusMessage.java index f1ede439..e1c9af55 100644 --- a/src/main/java/seng302/gameServer/messages/RaceStatusMessage.java +++ b/src/main/java/seng302/gameServer/messages/RaceStatusMessage.java @@ -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/model/ClientYacht.java b/src/main/java/seng302/model/ClientYacht.java index 0ca2298f..61447b3f 100644 --- a/src/main/java/seng302/model/ClientYacht.java +++ b/src/main/java/seng302/model/ClientYacht.java @@ -38,13 +38,13 @@ public class ClientYacht extends Observable { 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 = true; @@ -57,7 +57,6 @@ 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(); @@ -201,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; } diff --git a/src/main/java/seng302/model/RaceState.java b/src/main/java/seng302/model/RaceState.java index 303a5d02..501a3417 100644 --- a/src/main/java/seng302/model/RaceState.java +++ b/src/main/java/seng302/model/RaceState.java @@ -4,9 +4,15 @@ 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; @@ -29,10 +35,12 @@ public class RaceState { private long expectedStartTime; private boolean isRaceStarted = false; 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) { @@ -84,6 +92,19 @@ public class RaceState { 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) { diff --git a/src/main/java/seng302/model/ServerYacht.java b/src/main/java/seng302/model/ServerYacht.java index 60f646a4..34f45f20 100644 --- a/src/main/java/seng302/model/ServerYacht.java +++ b/src/main/java/seng302/model/ServerYacht.java @@ -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; @@ -65,6 +66,7 @@ public class ServerYacht extends Observable { 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/utilities/StreamParser.java b/src/main/java/seng302/utilities/StreamParser.java index 09055dc4..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)]; diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index 7839569b..a379a1ff 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -14,7 +14,8 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.Pane; import seng302.gameServer.GameState; import seng302.gameServer.MainServerThread; -import seng302.gameServer.messages.BoatAction; +import seng302.gameServer.server.messages.BoatAction; +import seng302.gameServer.server.messages.BoatStatus; import seng302.model.ClientYacht; import seng302.model.RaceState; import seng302.model.stream.packets.StreamPacket; @@ -27,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; @@ -190,6 +192,8 @@ public class GameClient { } catch (IOException e) { e.printStackTrace(); } + FinishScreenViewController controller = fxmlLoader.getController(); + controller.setFinishers(raceState.getPlayerPositions()); return fxmlLoader; } @@ -235,6 +239,7 @@ public class GameClient { allBoatsMap.forEach((id, boat) -> clientLobbyList.add(id + " " + boat.getBoatName()) ); + raceState.setBoats(allBoatsMap.values()); break; case RACE_START_STATUS: @@ -291,26 +296,14 @@ public class GameClient { */ private void updateMarkRounding(MarkRoundingData roundingData) { if (allXMLReceived()) { - ClientYacht yacht = allBoatsMap.get(roundingData.getBoatId()); - int placing = 1; -// int originalPlacing = yacht.getPlacing(); - for (ClientYacht otherYacht : allBoatsMap.values()) { - if (otherYacht != yacht && yacht.getLegNumber() + 1 <= otherYacht.getLegNumber()) { - placing++; - } - } -// if (placing != originalPlacing) { - yacht.setPlacing(placing); - for (ClientYacht otherYacht : allBoatsMap.values()) { - if (otherYacht.getPlacing() < placing) { - otherYacht.setPlacing(otherYacht.getPlacing() + 1); - } - } -// } - yacht.roundMark( - courseData.getCompoundMarks().get(roundingData.getMarkId()), - roundingData.getTimeStamp(), - raceState.getRaceTime() - roundingData.getTimeStamp() + ClientYacht clientYacht = allBoatsMap.get(roundingData.getBoatId()); + clientYacht.setMarkRoundingTime(roundingData.getTimeStamp()); + clientYacht.updateTimeSinceLastMarkProperty( + raceState.getRaceTime() - roundingData.getTimeStamp()); + clientYacht.setLastMarkRounded( + courseData.getCompoundMarks().get( + roundingData.getMarkId() + ) ); } } @@ -320,33 +313,34 @@ public class GameClient { raceState.updateState(data); boolean raceFinished = true; for (ClientYacht yacht : allBoatsMap.values()) { - if (yacht.getBoatStatus() != 3) { + if (yacht.getBoatStatus() != BoatStatus.FINISHED.getCode()) { raceFinished = false; } } - if (raceFinished == true) { + + 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.setBoatStatus((int) boatData[4]); + if (legNumber != clientYacht.getLegNumber()) { + clientYacht.setLegNumber(legNumber); + updatePlayerPositions(); + } + } + + if (raceFinished) { close(); loadFinishScreenView(); } + } + } - for (long[] boatData : data.getBoatData()) { - ClientYacht yacht = allBoatsMap.get((int) boatData[0]); - yacht.setEstimateTimeTillNextMark(raceState.getRaceTime() - boatData[1]); - yacht.setEstimateTimeAtFinish(boatData[2]); - int legNumber = (int) boatData[3]; -// yacht.setLegNumber(legNumber); - yacht.setBoatStatus((int) boatData[4]); -// if (legNumber != yacht.getLegNumber()) { -// System.out.println("WHAT THE FUCK IT WORKS????"); -// int placing = 1; -// for (ClientYacht otherClientYacht : allBoatsMap.values()) { -// if (otherClientYacht.getSourceId() != boatData[0] && -// yacht.getLegNumber() <= otherClientYacht.getLegNumber()) -// placing++; -// } -// yacht.setPlacing(placing); -// } - } + private void updatePlayerPositions() { + raceState.sortPlayers(); + for (ClientYacht yacht : raceState.getPlayerPositions()) { + yacht.setPosition(raceState.getPlayerPositions().indexOf(yacht) + 1); } } diff --git a/src/main/java/seng302/visualiser/controllers/FinishScreenViewController.java b/src/main/java/seng302/visualiser/controllers/FinishScreenViewController.java index 671436bf..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,7 +62,7 @@ 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::getPlacing)); finishOrderTable.getItems().setAll(sorted); diff --git a/src/main/java/seng302/visualiser/controllers/RaceViewController.java b/src/main/java/seng302/visualiser/controllers/RaceViewController.java index 802bde7d..505ff05e 100644 --- a/src/main/java/seng302/visualiser/controllers/RaceViewController.java +++ b/src/main/java/seng302/visualiser/controllers/RaceViewController.java @@ -11,6 +11,8 @@ import java.util.concurrent.TimeUnit; import javafx.animation.Timeline; import javafx.application.Platform; import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.geometry.Point2D; @@ -73,6 +75,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel private ComboBox yachtSelectionComboBox; @FXML private Text fpsDisplay; + @FXML + private Text windSpeedText; + //Race Data private Map participants; private Map markers; @@ -102,8 +107,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } public void loadRace ( - Map participants, RaceXMLData raceData, RaceState raceState, ClientYacht player - ) { + Map participants, RaceXMLData raceData, RaceState raceState, + ClientYacht player) { + this.participants = participants; this.courseData = raceData; this.markers = raceData.getCompoundMarks(); @@ -115,6 +121,15 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel initialiseBoatSelectionComboBox(); initialiseSparkLine(); + raceState.getPlayerPositions().addListener((ListChangeListener) c -> { + while (c.next()) { + if (c.wasPermutated()) { + updateOrder(raceState.getPlayerPositions()); + } + } + }); + + updateOrder(raceState.getPlayerPositions()); gameView = new GameView(); gameView.setFrameRateFXText(fpsDisplay); Platform.runLater(() -> contentAnchorPane.getChildren().add(0, gameView)); @@ -126,6 +141,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel gameView.enableZoom(); gameView.setBoatAsPlayer(player); gameView.startRace(); + raceState.addCollisionListener(gameView::drawCollision); raceState.windDirectionProperty().addListener((obs, oldDirection, newDirection) -> { gameView.setWindDir(newDirection.doubleValue()); @@ -279,7 +295,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel * @param yacht The yacht to be updated on the sparkline * @param legNumber the leg number that the position will be assigned to */ - void updateClientYachtPositionSparkline(ClientYacht yacht, Integer legNumber){ + void updateYachtPositionSparkline(ClientYacht yacht, Integer legNumber){ for (XYChart.Series positionData : sparkLineData) { positionData.getData().add( new Data<>( @@ -363,9 +379,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel * Updates the wind direction arrow and text as from info from the StreamParser * @param direction the from north angle of the wind. */ - private void updateWindDirection(double direction) { - windDirectionText.setText(String.format("%.1f°", direction)); - windArrowText.setRotate(direction); + private void updateWind() { + windDirectionText.setText(String.format("%.1f°", raceState.getWindDirection())); + windArrowText.setRotate(raceState.getWindDirection()); + windSpeedText.setText("Speed: " + String.format("%.1f", raceState.getWindSpeed()) + " Knots"); } @@ -385,21 +402,20 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel * Updates the order of the yachts as from the StreamParser and sets them in the yacht order * section */ - private void updateOrder() { - List sorted = new ArrayList<>(participants.values()); - sorted.sort(Comparator.comparingInt(ClientYacht::getPlacing)); + private void updateOrder(ObservableList yachts) { List vboxEntries = new ArrayList<>(); - for (ClientYacht yacht : sorted) { - if (yacht.getBoatStatus() == 3) { // 3 is finish status - Text textToAdd = new Text(yacht.getPlacing() + ". " + - yacht.getShortName() + " (Finished)"); + for (int i = 0; i < yachts.size(); i++) { +// System.out.println("yacht == null " + String.valueOf(yacht == null)); + if (yachts.get(i).getBoatStatus() == 3) { // 3 is finish status + Text textToAdd = new Text(i + 1 + ". " + + yachts.get(i).getShortName() + " (Finished)"); textToAdd.setFill(Paint.valueOf("#d3d3d3")); vboxEntries.add(textToAdd); } else { - Text textToAdd = new Text(yacht.getPlacing() + ". " + - yacht.getShortName() + " "); + Text textToAdd = new Text(i + 1 + ". " + + yachts.get(i).getShortName() + " "); textToAdd.setFill(Paint.valueOf("#d3d3d3")); textToAdd.setStyle(""); vboxEntries.add(textToAdd); @@ -476,16 +492,17 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } - 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 newY = ref.getY() + (ref.getX() + distance -ref.getX())*Math.sin(angle) + (ref.getY() + distance -ref.getY())*Math.cos(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 newY = ref.getY() + (ref.getX() + distance - ref.getX()) * Math.sin(angle) + + (ref.getY() + distance - ref.getY()) * Math.cos(angle); return new Point2D(newX, newY); } public Line makeLeftLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) { - Point2D ep = getPointRotation(startPoint, 50.0, baseAngle + layLineAngle); Line line = new Line(startPoint.getX(), startPoint.getY(), ep.getX(), ep.getY()); return line; @@ -541,7 +558,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel TimeUnit.MILLISECONDS.toHours(milliseconds), TimeUnit.MILLISECONDS.toMinutes(milliseconds) % 60, //Modulus 60 minutes per hour TimeUnit.MILLISECONDS.toSeconds(milliseconds) % 60 //Modulus 60 seconds per minute - ); + ); } private void setAnnotations(Integer annotationLevel) {