diff --git a/.mailmap b/.mailmap index b774add7..b68bd189 100644 --- a/.mailmap +++ b/.mailmap @@ -16,8 +16,9 @@ # https://www.kernel.org/pub/software/scm/git/docs/git-shortlog.html # http://stacktoheap.com/blog/2013/01/06/using-mailmap-to-fix-authors-list-in-git/ Erika Savell -Connor Taylor-Brown +Connor Taylor-Brown Fraser Cope -Jessica Syder Jessica Syder +Jessica Syder Joseph Gardner -Hamish Ball hba56 \ No newline at end of file +Hamish Ball +David Wu \ No newline at end of file diff --git a/racevisionGame/src/main/java/mock/app/Event.java b/racevisionGame/src/main/java/mock/app/Event.java index 1e1b8966..602aed43 100644 --- a/racevisionGame/src/main/java/mock/app/Event.java +++ b/racevisionGame/src/main/java/mock/app/Event.java @@ -63,6 +63,8 @@ public class Event { */ private SourceIdAllocator sourceIdAllocator; + private RaceLogic raceLogic; + private Thread raceThread; private Thread connectionThread; @@ -114,14 +116,17 @@ public class Event { //Read XML files. try { - //this.raceXML = RaceXMLCreator.alterRaceToWind(raceXMLFile, 90); - this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8)); + if(mapIndex==4){ - //this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8), 1000, 5000); - this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, -1, true); + //This is the tutorial map. + this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8), 1000, 5000); + this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, -1); + } else { - this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, windAngle, false); + this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8)); + this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, windAngle); this.raceXML = RaceXMLCreator.scaleRaceSize(raceXML, windSpeed, 15 * 60 * 1000); + } this.boatXML = XMLReader.readXMLFileToString(boatsXMLFile, StandardCharsets.UTF_8); @@ -171,20 +176,20 @@ public class Event { } - RaceLogic newRace = new RaceLogic( + this.raceLogic = new RaceLogic( mockRace, this.latestMessages, this.compositeCommand); - this.raceThread = new Thread(newRace, "Event.Start()->RaceLogic thread"); + this.raceThread = new Thread(raceLogic, "Event.Start()->RaceLogic thread"); raceThread.start(); //Create connection acceptor. - this.sourceIdAllocator = new SourceIdAllocator(newRace.getRace()); + this.sourceIdAllocator = new SourceIdAllocator(raceLogic.getRace()); try { - this.connectionAcceptor = new ConnectionAcceptor(latestMessages, compositeCommand, sourceIdAllocator, newRace); + this.connectionAcceptor = new ConnectionAcceptor(latestMessages, compositeCommand, sourceIdAllocator, raceLogic); } catch (IOException e) { throw new EventConstructionException("Could not create ConnectionAcceptor.", e); @@ -217,16 +222,13 @@ public class Event { } public static String setRaceXMLAtCurrentTimeToNow(String raceXML, long racePreStartTime, long racePreparatoryTime){ - //The start time is current time + 4 minutes. prestart is 3 minutes, and we add another minute. - long millisecondsToAdd = racePreStartTime + racePreparatoryTime; - - long secondsToAdd = millisecondsToAdd / 1000; + long minutesToAdd = 10; DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"); ZonedDateTime creationTime = ZonedDateTime.now(); raceXML = raceXML.replace("RACE_CREATION_TIME", dateFormat.format(creationTime)); - raceXML = raceXML.replace("RACE_START_TIME", dateFormat.format(creationTime.plusSeconds(secondsToAdd))); + raceXML = raceXML.replace("RACE_START_TIME", dateFormat.format(creationTime.plusMinutes(minutesToAdd))); return raceXML; } @@ -241,4 +243,9 @@ public class Event { return new HostGame(ip, 3779,(byte) 1,(byte) 1, RaceStatusEnum.PRESTART, (byte) 1, (byte) 1); } + + public RaceLogic getRaceLogic() { + return raceLogic; + } + } diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 9ede32d1..26c63e9b 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -67,6 +67,11 @@ public class MockRace extends RaceState { private long racePreparatoryTime = Constants.RacePreparatoryTime; + /** + * True if the race has been manually started, false otherwise. + */ + private boolean hasBeenStarted = false; + /** * Constructs a race object with a given RaceDataSource, BoatDataSource, and RegattaDataSource and sends events to the given mockOutput. * @param boatDataSource Data source for boat related data (yachts and marker boats). @@ -157,6 +162,23 @@ public class MockRace extends RaceState { } + /** + * Delays the start of the race, if needed, to ensure that the race doesn't start until host wants it to. + * If the time until start is less that 5 minutes, it is extended to 10 minutes. + */ + public void delayRaceStart() { + long timeToStart = getRaceDataSource().getStartDateTime().toInstant().toEpochMilli() - System.currentTimeMillis(); + + long fiveMinutesMilli = 5 * 60 * 1000; + long tenMinutesMilli = 10 * 60 * 1000; + + if ((timeToStart < fiveMinutesMilli) && (timeToStart > 0) && !hasBeenStarted) { + startRace(tenMinutesMilli, false); + } + + } + + /** * Updates the race status enumeration based on the current time. */ @@ -165,7 +187,6 @@ public class MockRace extends RaceState { //The millisecond duration of the race. Negative means it hasn't started, so we flip sign. long timeToStart = - this.getRaceClock().getDurationMilli(); - if (timeToStart > racePreStartTime) { //Time > 3 minutes is the prestart period. this.setRaceStatusEnum(RaceStatusEnum.PRESTART); @@ -195,6 +216,26 @@ public class MockRace extends RaceState { this.racePreparatoryTime = racePreparatoryTime; } + public long getRacePreparatoryTime() { + return racePreparatoryTime; + } + + + /** + * Starts the race in #timeToStartMilli milliseconds. + * @param timeToStartMilli Millseconds before starting the race. + * @param manualStart True if the race has been manually started, false otherwise. + */ + public void startRace(long timeToStartMilli, boolean manualStart) { + this.hasBeenStarted = manualStart; + ZonedDateTime startTime = ZonedDateTime.now().plus(timeToStartMilli, ChronoUnit.MILLIS); + + getRaceDataSource().setStartDateTime(startTime); + getRaceDataSource().incrementSequenceNumber(); + + this.getRaceClock().setStartingTime(getRaceDataSource().getStartDateTime()); + } + /** * Sets the status of all boats in the race to RACING. */ diff --git a/racevisionGame/src/main/java/mock/model/RaceLogic.java b/racevisionGame/src/main/java/mock/model/RaceLogic.java index 0110716e..8814733a 100644 --- a/racevisionGame/src/main/java/mock/model/RaceLogic.java +++ b/racevisionGame/src/main/java/mock/model/RaceLogic.java @@ -81,6 +81,8 @@ public class RaceLogic implements RunnableWithFramePeriod, Observer { //Update race time. race.updateRaceTime(currentTime); + race.delayRaceStart(); + //Update the race status based on the current time. race.updateRaceStatusEnum(); diff --git a/racevisionGame/src/main/java/mock/xml/RaceXMLCreator.java b/racevisionGame/src/main/java/mock/xml/RaceXMLCreator.java index 2c15e57f..af520a83 100644 --- a/racevisionGame/src/main/java/mock/xml/RaceXMLCreator.java +++ b/racevisionGame/src/main/java/mock/xml/RaceXMLCreator.java @@ -56,13 +56,12 @@ public class RaceXMLCreator { * Rotates the race in a specified direction. * @param s xml file name or contents. * @param fileType Whether s is a file name or contents. - * @param degrees degrees to rotate - * @param tutorial Whether we wish to run the tutorial - this changes the race start time. + * @param degrees degrees to rotate. -1 means don't rotate. * @return the new xml file as a string * @throws XMLReaderException if the xml is not readable * @throws InvalidRaceDataException if the race is invalid */ - public static String alterRaceToWind(String s, XMLFileType fileType, double degrees, boolean tutorial) throws XMLReaderException, InvalidRaceDataException { + public static String alterRaceToWind(String s, XMLFileType fileType, double degrees) throws XMLReaderException, InvalidRaceDataException { RaceXMLReader reader = new RaceXMLReader(s, fileType); @@ -73,11 +72,6 @@ public class RaceXMLCreator { RaceXMLCreator.class.getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd"), XMLRace.class); - if(tutorial){ - setRaceXMLAtCurrentTimeToNow(race, 1000l, 5000l); - } else { - setRaceXMLAtCurrentTimeToNow(race); - } CompoundMark leewardGate = getLeewardGate(reader); diff --git a/racevisionGame/src/main/java/shared/dataInput/EmptyRaceDataSource.java b/racevisionGame/src/main/java/shared/dataInput/EmptyRaceDataSource.java index e395e51f..3df46076 100644 --- a/racevisionGame/src/main/java/shared/dataInput/EmptyRaceDataSource.java +++ b/racevisionGame/src/main/java/shared/dataInput/EmptyRaceDataSource.java @@ -151,4 +151,9 @@ public class EmptyRaceDataSource implements RaceDataSource { public void incrementSequenceNumber() { sequenceNumber++; } + + @Override + public void setStartDateTime(ZonedDateTime time) { + raceStartTime = time; + } } diff --git a/racevisionGame/src/main/java/shared/dataInput/RaceDataSource.java b/racevisionGame/src/main/java/shared/dataInput/RaceDataSource.java index 765098f2..3b933329 100644 --- a/racevisionGame/src/main/java/shared/dataInput/RaceDataSource.java +++ b/racevisionGame/src/main/java/shared/dataInput/RaceDataSource.java @@ -67,6 +67,12 @@ public interface RaceDataSource { */ ZonedDateTime getStartDateTime(); + /** + * Sets the start time/date of the race. + * @param time Time to start at. + */ + void setStartDateTime(ZonedDateTime time); + /** * Returns the creation time/date of the race xml file. * @return The race xml file's creation time. diff --git a/racevisionGame/src/main/java/shared/dataInput/RaceXMLReader.java b/racevisionGame/src/main/java/shared/dataInput/RaceXMLReader.java index af274a9c..0beac26d 100644 --- a/racevisionGame/src/main/java/shared/dataInput/RaceXMLReader.java +++ b/racevisionGame/src/main/java/shared/dataInput/RaceXMLReader.java @@ -507,4 +507,9 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource { public void incrementSequenceNumber() { sequenceNumber++; } + + @Override + public void setStartDateTime(ZonedDateTime time) { + raceStartTime = time; + } } diff --git a/racevisionGame/src/main/java/visualiser/Controllers/InGameLobbyController.java b/racevisionGame/src/main/java/visualiser/Controllers/InGameLobbyController.java index ce6d4b08..4bdda0ff 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/InGameLobbyController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/InGameLobbyController.java @@ -10,6 +10,7 @@ import javafx.fxml.FXML; import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.control.Alert; +import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.Label; import javafx.scene.image.ImageView; @@ -71,13 +72,10 @@ public class InGameLobbyController extends Controller { private Label playerLabel6; @FXML - private Label countdownLabel; + private Button startButton; @FXML - private AnchorPane countdownTenPane; - - @FXML - private Label countdownTenText; + private Button quitButton; private Event game; @@ -209,37 +207,13 @@ public class InGameLobbyController extends Controller { * Starts the race. */ private void startRace() { - //Initialises the race clock. - initialiseRaceClock(this.visualiserRaceEvent.getVisualiserRaceState()); //Starts the race countdown timer. countdownTimer(); } - /** - * Initialises the race clock/timer labels for the start time, current time, and remaining time. - * @param visualiserRace The race to get data from. - */ - private void initialiseRaceClock(VisualiserRaceState visualiserRace) { - //Remaining time. - initialiseRaceClockDuration(visualiserRace); - - } - - - /** - * Initialises the race duration label. - * @param visualiserRace The race to get data from. - */ - private void initialiseRaceClockDuration(VisualiserRaceState visualiserRace) { - visualiserRace.getRaceClock().durationProperty().addListener((observable, oldValue, newValue) -> { - Platform.runLater(() -> { - countdownLabel.setText(newValue); - }); - }); - } /** * Countdown timer until race starts. */ @@ -250,19 +224,6 @@ public class InGameLobbyController extends Controller { //Get the current race status. RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum(); - //Try catch for getting interval times - try { - long interval = ChronoUnit.MILLIS.between(visualiserRaceEvent.getVisualiserRaceState().getRaceClock().getCurrentTime(), visualiserRaceEvent.getVisualiserRaceState().getRaceClock().getStartingTime()); - if(interval<=10000){ - countdownLabel.setVisible(false); - countdownTenPane.setVisible(true); - countdownTenText.setVisible(true); - } - countdownText(interval); - } catch (Exception e){ - - } - //If the race has reached the preparatory phase, or has started... if (raceStatus == RaceStatusEnum.PREPARATORY || raceStatus == RaceStatusEnum.STARTED) { @@ -301,6 +262,8 @@ public class InGameLobbyController extends Controller { this.visualiserRaceEvent.getVisualiserRaceState().getBoats().addListener(this.lobbyUpdateListener); + enableStartIfHost(); + startRace(); } catch (IOException e) { //TODO should probably let this propagate, so that we only enter this scene if everything works @@ -308,6 +271,22 @@ public class InGameLobbyController extends Controller { } } + + /** + * Enables the start button if the client is the host of the game. + */ + private void enableStartIfHost() { + if (isHost) { + startButton.setVisible(true); + startButton.setDisable(false); + } else { + startButton.setVisible(false); + startButton.setDisable(true); + } + } + + + /** * Menu button pressed. Prompt alert then return to menu * @throws IOException socket erro @@ -335,7 +314,7 @@ public class InGameLobbyController extends Controller { * Start button pressed. Currently only prints out start */ public void startBtnPressed(){ - //System.out.println("Should start the race. This button is only visible for the host"); + App.game.getRaceLogic().getRace().startRace(App.game.getRaceLogic().getRace().getRacePreparatoryTime(), true); } public void joinSpecPressed(){ @@ -346,48 +325,5 @@ public class InGameLobbyController extends Controller { //System.out.println("Empty race user pane pressed. Joining racers"); } - /** - * Countdown interval checker that updates the countdown text - * @param interval Countdown interval to check - */ - private void countdownText(long interval){ - countdownTenPane.setVisible(false); - //Do nothing if 5 seconds or less to go - if (interval <=5000){ - countdownTenPane.setVisible(false); - //countdownTenText.setText("5"); - return; - } - - //6 seconds left. Display 1 second for countdown - if (interval <=6000){ - countdownTenText.setText("1"); - return; - } - - //7 seconds left. Display 2 seconds for countdown - if (interval <=7000){ - countdownTenText.setText("2"); - return; - } - - //8 seconds left. Display 3 seconds for countdown - if (interval <=8000){ - countdownTenText.setText("3"); - return; - } - - //9 seconds left. Display 4 seconds for countdown - if (interval <=9000){ - countdownTenText.setText("4"); - return; - } - - //10 seconds left. Display 5 seconds for countdown - if (interval <=10000){ - countdownTenText.setText("5"); - return; - } - } } diff --git a/racevisionGame/src/main/resources/visualiser/scenes/gameLobby.fxml b/racevisionGame/src/main/resources/visualiser/scenes/gameLobby.fxml index 18658445..b0c5dc2e 100644 --- a/racevisionGame/src/main/resources/visualiser/scenes/gameLobby.fxml +++ b/racevisionGame/src/main/resources/visualiser/scenes/gameLobby.fxml @@ -1,39 +1,23 @@ - - - - - - - - - - - - + - - @@ -82,6 +66,7 @@ +