diff --git a/racevisionGame/src/main/java/mock/app/MockOutput.java b/racevisionGame/src/main/java/mock/app/MockOutput.java index 68136ea0..023235cc 100644 --- a/racevisionGame/src/main/java/mock/app/MockOutput.java +++ b/racevisionGame/src/main/java/mock/app/MockOutput.java @@ -27,7 +27,12 @@ public class MockOutput implements RunnableWithFramePeriod { */ private LatestMessages latestMessages; + //These sequence number track the last race/boat/regatta xml message we've sent. + private int lastSentRaceNumber = -1; + private int lastSentBoatNumber = -1; + + private int lastSentRegattaNumber = -1; @@ -69,7 +74,6 @@ public class MockOutput implements RunnableWithFramePeriod { long previousFrameTime = System.currentTimeMillis(); - boolean sentXMLs = false; @@ -82,16 +86,26 @@ public class MockOutput implements RunnableWithFramePeriod { previousFrameTime = currentFrameTime; - //Send XML messages. - if (!sentXMLs) { + //Send XML messages if needed. + + if (lastSentRaceNumber != latestMessages.getRaceXMLMessage().getSequenceNumber()) { + lastSentRaceNumber = latestMessages.getRaceXMLMessage().getSequenceNumber(); outgoingMessages.put(latestMessages.getRaceXMLMessage()); - outgoingMessages.put(latestMessages.getRegattaXMLMessage()); + } + + if (lastSentBoatNumber != latestMessages.getBoatXMLMessage().getSequenceNumber()) { + lastSentBoatNumber = latestMessages.getBoatXMLMessage().getSequenceNumber(); outgoingMessages.put(latestMessages.getBoatXMLMessage()); + } - sentXMLs = true; + if (lastSentRegattaNumber != latestMessages.getRegattaXMLMessage().getSequenceNumber()) { + lastSentRegattaNumber = latestMessages.getRegattaXMLMessage().getSequenceNumber(); + outgoingMessages.put(latestMessages.getRegattaXMLMessage()); } + + List snapshot = latestMessages.getSnapshot(); for (AC35Data message : snapshot) { outgoingMessages.put(message); diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 357d34ce..9bcece2c 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -43,7 +43,7 @@ public class MockRace extends RaceState { /** * Registry for all collider object in this race */ - protected ColliderRegistry colliderRegistry; + private ColliderRegistry colliderRegistry; /** @@ -58,6 +58,12 @@ public class MockRace extends RaceState { private WindGenerator windGenerator; + /** + * The polars file to use for each boat. + */ + private Polars polars; + + /** * Constructs a race object with a given RaceDataSource, BoatDataSource, and RegattaDataSource and sends events to the given mockOutput. @@ -74,9 +80,10 @@ public class MockRace extends RaceState { this.setRaceDataSource(raceDataSource); this.setRegattaDataSource(regattaDataSource); + this.polars = polars; this.scaleFactor = timeScale; - this.boats = this.generateMockBoats(boatDataSource.getBoats(), raceDataSource.getParticipants(), polars); + this.boats = new ArrayList<>(); this.shrinkBoundary = GPSCoordinate.getShrinkBoundary(this.getBoundary()); @@ -100,31 +107,35 @@ public class MockRace extends RaceState { /** - * Generates a list of MockBoats given a list of Boats, and a list of participating boats. - * @param boats The map of Boats describing boats that are potentially in the race. Maps boat sourceID to boat. - * @param sourceIDs The list of boat sourceIDs describing which specific boats are actually participating. - * @param polars The polars table to be used for boat simulation. - * @return A list of MockBoats that are participating in the race. + * Generates a MockBoat from the BoatDataSource, given a source ID. Also adds it to the participant list. + * @param sourceID The source ID to assign the boat. + * @return A MockBoat that is now participating in the race. */ - private List generateMockBoats(Map boats, List sourceIDs, Polars polars) { - - List mockBoats = new ArrayList<>(sourceIDs.size()); + public void generateMockBoat(Integer sourceID) { - //For each sourceID participating... - for (int sourceID : sourceIDs) { + //Get the boat associated with the sourceID. + Boat boat = getBoatDataSource().getBoats().get(sourceID); - //Get the boat associated with the sourceID. - Boat boat = boats.get(sourceID); + //Construct a MockBoat using the Boat and Polars. + MockBoat mockBoat = new MockBoat(boat, polars); + mockBoat.setCurrentLeg(this.getLegs().get(0)); - //Construct a MockBoat using the Boat and Polars. - MockBoat mockBoat = new MockBoat(boat, polars); + //Update participant list. + getRaceDataSource().getParticipants().add(sourceID); - mockBoats.add(mockBoat); + this.boats.add(mockBoat); - } - - return mockBoats; + getRaceDataSource().incrementSequenceNumber(); + } + /** + * Removes a MockBoat from the race, by sourceID. Also removes it from the participant list. + * @param sourceID Source ID of boat to remove. + */ + public void removeMockBoat(Integer sourceID) { + this.boats.removeIf(mockBoat -> mockBoat.getSourceID() == sourceID); + getRaceDataSource().getParticipants().remove(sourceID); + getRaceDataSource().incrementSequenceNumber(); } diff --git a/racevisionGame/src/main/java/mock/model/RaceLogic.java b/racevisionGame/src/main/java/mock/model/RaceLogic.java index 5b35d449..a1bddd9a 100644 --- a/racevisionGame/src/main/java/mock/model/RaceLogic.java +++ b/racevisionGame/src/main/java/mock/model/RaceLogic.java @@ -48,6 +48,9 @@ public class RaceLogic implements RunnableWithFramePeriod, Observer { */ @Override public void run() { + + prestartCountdown(); + race.initialiseBoats(); countdown(); @@ -60,6 +63,35 @@ public class RaceLogic implements RunnableWithFramePeriod, Observer { } + /** + * The countdown timer until the prestart period is finished. This timer will stop 3 minutes before the race starts, and players can no longer start participating. + */ + private void prestartCountdown() { + + long previousFrameTime = System.currentTimeMillis(); + + while (((race.getRaceStatusEnum() == RaceStatusEnum.PRESTART) + || (race.getRaceStatusEnum() == RaceStatusEnum.NOT_ACTIVE)) && loopBool) { + + long currentTime = System.currentTimeMillis(); + + //Update race time. + race.updateRaceTime(currentTime); + + //Update the race status based on the current time. + race.updateRaceStatusEnum(); + + //Provide boat's with an estimated time at next mark until the race starts. + race.setBoatsTimeNextMark(race.getRaceClock().getCurrentTime()); + + //Parse the race snapshot. + server.parseSnapshot(); + + waitForFramePeriod(previousFrameTime, currentTime, 50); + previousFrameTime = currentTime; + } + } + /** * Countdown timer until race starts. */ diff --git a/racevisionGame/src/main/java/mock/model/SourceIdAllocator.java b/racevisionGame/src/main/java/mock/model/SourceIdAllocator.java index 3b62a8a7..b609e5a5 100644 --- a/racevisionGame/src/main/java/mock/model/SourceIdAllocator.java +++ b/racevisionGame/src/main/java/mock/model/SourceIdAllocator.java @@ -2,6 +2,7 @@ package mock.model; import mock.exceptions.SourceIDAllocationException; +import network.Messages.Enums.RaceStatusEnum; import java.util.ArrayList; import java.util.List; @@ -13,24 +14,18 @@ public class SourceIdAllocator { /** - * This list contains all unallocated source IDs. + * The race we are allocating for. */ - List unallocatedIDs = new ArrayList<>(); + private MockRace mockRace; - /** - * This list contains all allocated source IDs. - */ - List allocatedIDs = new ArrayList<>(); - /** - * Creates a source ID allocator, using the given list of unallocated source IDs. - * @param unallocatedIDs List of unallocated source IDs. + * Creates a SourceIdAllocator for a given race. + * @param mockRace Race to allocate source IDs for. */ - public SourceIdAllocator(List unallocatedIDs) { - //We need to copy the list. - this.unallocatedIDs.addAll(unallocatedIDs); + public SourceIdAllocator(MockRace mockRace) { + this.mockRace = mockRace; } @@ -41,11 +36,23 @@ public class SourceIdAllocator { */ public synchronized int allocateSourceID() throws SourceIDAllocationException { + if (mockRace.getRaceStatusEnum() != RaceStatusEnum.PRESTART) { + throw new SourceIDAllocationException("Could not allocate a source ID. Can only allocate during pre-start period. It is currently: " + mockRace.getRaceStatusEnum()); + } + + List allocatedIDs = mockRace.getRaceDataSource().getParticipants(); + List allIDs = new ArrayList<>(mockRace.getBoatDataSource().getBoats().keySet()); + + //Get list of unallocated ids. + List unallocatedIDs = new ArrayList<>(allIDs); + unallocatedIDs.removeAll(allocatedIDs); + + if (!unallocatedIDs.isEmpty()) { int sourceID = unallocatedIDs.remove(0); - allocatedIDs.add(sourceID); + mockRace.generateMockBoat(sourceID); return sourceID; @@ -61,10 +68,6 @@ public class SourceIdAllocator { * @param sourceID Source ID to return. */ public void returnSourceID(Integer sourceID) { - - //We remove an Integer, not an int, so that we remove by value not by index. - allocatedIDs.remove(sourceID); - - unallocatedIDs.add(sourceID); + mockRace.removeMockBoat(sourceID); } } diff --git a/racevisionGame/src/main/java/shared/model/RaceState.java b/racevisionGame/src/main/java/shared/model/RaceState.java index 19a1a52c..48361da2 100644 --- a/racevisionGame/src/main/java/shared/model/RaceState.java +++ b/racevisionGame/src/main/java/shared/model/RaceState.java @@ -188,7 +188,20 @@ public abstract class RaceState { * @return List of mark boats. */ public List getMarks() { - return new ArrayList<>(boatDataSource.getMarkerBoats().values()); + //BoatDataSource contains a collection of Marks, and RaceDataSource contains a collection of Compound marks (which contain marks). RaceDataSource is the "definitive" source of mark data. + List marks = new ArrayList<>(getCompoundMarks().size() * 2); + + for (CompoundMark compoundMark : getCompoundMarks()) { + if (compoundMark.getMark1() != null) { + marks.add(compoundMark.getMark1()); + } + + if (compoundMark.getMark2() != null) { + marks.add(compoundMark.getMark2()); + } + } + + return marks; } /**