package mock.model; import javafx.animation.AnimationTimer; import mock.model.commandFactory.Command; import mock.model.commandFactory.CommandFactory; import mock.model.commandFactory.CompositeCommand; import network.Messages.Enums.BoatActionEnum; import network.Messages.Enums.BoatStatusEnum; import network.Messages.Enums.RaceStatusEnum; import network.Messages.LatestMessages; import visualiser.gameController.ControllerServer; import java.util.Observable; import java.util.Observer; import java.util.Stack; public class RaceLogic implements Observer, Runnable { /** * State of current race modified by this object */ private MockRace race; /** * High-level interface to AC35 protocol server */ private RaceServer server; private CompositeCommand commands; /** * Initialises race loop with state and server message queue * @param race state of race to modify * @param messages to send to server */ public RaceLogic(MockRace race, LatestMessages messages) { this.race = race; this.server = new RaceServer(race, messages); this.commands = new CompositeCommand(); } /** * Initialise boats and start countdown timer */ @Override public void run() { race.initialiseBoats(); this.countdownTimer.start(); } /** * Countdown timer until race starts. */ protected AnimationTimer countdownTimer = new AnimationTimer() { long currentTime = System.currentTimeMillis(); @Override public void handle(long arg0) { //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 boat locations. server.parseBoatLocations(); //Parse the marks. server.parseMarks(); // Change wind direction race.changeWindDirection(); //Parse the race status. server.parseRaceStatus(); if (race.getRaceStatusEnum() == RaceStatusEnum.STARTED) { race.setBoatsStatusToRacing(); raceTimer.start(); this.stop(); } //Update the animations timer's time. currentTime = System.currentTimeMillis(); } }; /** * Timer that runs for the duration of the race, until all boats finish. */ private AnimationTimer raceTimer = new AnimationTimer() { /** * Start time of loop, in milliseconds. */ long timeRaceStarted = System.currentTimeMillis(); /** * Current time during a loop iteration. */ long currentTime = System.currentTimeMillis(); /** * The time of the previous frame, in milliseconds. */ long lastFrameTime = timeRaceStarted; long framePeriod = currentTime - lastFrameTime; @Override public void handle(long arg0) { //Get the current time. currentTime = System.currentTimeMillis(); //Update race time. race.updateRaceTime(currentTime); //As long as there is at least one boat racing, we still simulate the race. if (race.getNumberOfActiveBoats() != 0) { //Get the time period of this frame. framePeriod = currentTime - lastFrameTime; //For each boat, we update its position, and generate a BoatLocationMessage. for (MockBoat boat : race.getBoats()) { //If it is still racing, update its position. if (boat.getStatus() == BoatStatusEnum.RACING) { commands.execute(); race.updatePosition(boat, framePeriod, race.getRaceClock().getDurationMilli()); } } } else { //Otherwise, the race is over! raceFinished.start(); race.setRaceStatusEnum(RaceStatusEnum.FINISHED); this.stop(); } if (race.getNumberOfActiveBoats() != 0) { // Change wind direction race.changeWindDirection(); //Parse the boat locations. server.parseBoatLocations(); //Parse the marks. server.parseMarks(); //Parse the race status. server.parseRaceStatus(); //Update the last frame time. this.lastFrameTime = currentTime; } } }; /** * Broadcast that the race has finished. */ protected AnimationTimer raceFinished = new AnimationTimer(){ int iters = 0; @Override public void handle(long now) { server.parseRaceStatus(); if (iters > 500) { stop(); } iters++; } }; @Override public void update(Observable o, Object arg) { ControllerServer server = (ControllerServer)o; BoatActionEnum action = server.getAction(); MockBoat boat = race.getBoats().get(0); commands.addCommand(CommandFactory.createCommand(race, boat, action)); } }