diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 6cc37e91..654a57b4 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -20,6 +20,8 @@ import seng302.gameServer.messages.MarkRoundingMessage; import seng302.gameServer.messages.MarkType; import seng302.gameServer.messages.Message; import seng302.gameServer.messages.RoundingBoatStatus; +import seng302.gameServer.messages.XMLMessage; +import seng302.gameServer.messages.XMLMessageSubType; import seng302.gameServer.messages.YachtEventCodeMessage; import seng302.model.GeoPoint; import seng302.model.Limit; @@ -51,8 +53,13 @@ public class GameState implements Runnable { private Logger logger = LoggerFactory.getLogger(GameState.class); - private static final Integer STATE_UPDATES_PER_SECOND = 60; + + public static final int WARNING_TIME = 10 * -1000; + public static final int PREPATORY_TIME = 5 * -1000; + public static final int TIME_TILL_START = 10 * 1000; public static Integer MAX_PLAYERS = 8; + + private static final Integer STATE_UPDATES_PER_SECOND = 60; public static Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further public static final Double MARK_COLLISION_DISTANCE = 15d; public static final Double YACHT_COLLISION_DISTANCE = 25.0; @@ -197,7 +204,7 @@ public class GameState implements Runnable { } public static void resetStartTime(){ - startTime = System.currentTimeMillis() + MainServerThread.TIME_TILL_START; + startTime = System.currentTimeMillis() + TIME_TILL_START; } public static Double getWindDirection() { @@ -299,6 +306,7 @@ public class GameState implements Runnable { yacht.updateLocation(timeInterval); if (yacht.getBoatStatus() != BoatStatus.FINISHED) { checkCollision(yacht); + checkTokenPickUp(yacht); checkForLegProgression(yacht); raceFinished = false; } @@ -331,6 +339,24 @@ public class GameState implements Runnable { return false; } + /** + * Checks all tokens to see if a yacht has picked one up + * + * @param serverYacht The yacht to check for + */ + private void checkTokenPickUp(ServerYacht serverYacht) { + for (Token token : tokens) { + Double distance = GeoUtility.getDistance(token, serverYacht.getLocation()); + if (distance < YACHT_COLLISION_DISTANCE) { + tokens.remove(token); + serverYacht.setPowerUp(token.getTokenType()); + notifyMessageListeners(MessageFactory.getRaceXML()); + break; + } + } + } + + public static void checkCollision(ServerYacht serverYacht) { ServerYacht collidedYacht = checkYachtCollision(serverYacht); if (collidedYacht != null) { @@ -703,7 +729,7 @@ public class GameState implements Runnable { } - public static void addMarkPassListener(NewMessageListener listener) { + public static void addMessageEventListener(NewMessageListener listener) { markListeners.add(listener); } @@ -718,4 +744,5 @@ public class GameState implements Runnable { public static void resetCustomizationFlag() { customizationFlag = false; } + } diff --git a/src/main/java/seng302/gameServer/MainServerThread.java b/src/main/java/seng302/gameServer/MainServerThread.java index 2303b0c7..c0b59751 100644 --- a/src/main/java/seng302/gameServer/MainServerThread.java +++ b/src/main/java/seng302/gameServer/MainServerThread.java @@ -1,21 +1,20 @@ package seng302.gameServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import seng302.gameServer.messages.*; import seng302.model.GeoPoint; import seng302.model.Player; import seng302.model.PolarTable; import seng302.model.ServerYacht; import seng302.model.mark.CompoundMark; -import seng302.model.stream.xml.generator.RaceXMLTemplate; import seng302.model.token.Token; import seng302.model.token.TokenType; import seng302.utilities.GeoUtility; import java.io.IOException; import java.net.ServerSocket; -import java.time.LocalDateTime; import java.util.*; -import seng302.utilities.XMLGenerator; /** * A class describing the overall server, which creates and collects server threads for each client @@ -23,12 +22,10 @@ import seng302.utilities.XMLGenerator; */ public class MainServerThread implements Runnable, ClientConnectionDelegate { + private Logger logger = LoggerFactory.getLogger(MainServerThread.class); + private static final int PORT = 4942; 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; @@ -45,14 +42,15 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { try { serverSocket = new ServerSocket(PORT); } catch (IOException e) { - serverLog("IO error in server thread handler upon trying to make new server socket", 0); + logger.trace("IO error in server thread handler upon trying to make new server socket", + 0); } PolarTable.parsePolarFile(getClass().getResourceAsStream("/config/acc_polars.csv")); - GameState.addMarkPassListener(this::broadcastMessage); + GameState.addMessageEventListener(this::broadcastMessage); terminated = false; thread = new Thread(this, "MainServer"); startUpdatingWind(); - startSpawningTokens(); +// startSpawningTokens(); thread.start(); } @@ -67,24 +65,22 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { try { Thread.sleep(1000 / CLIENT_UPDATES_PER_SECOND); } catch (InterruptedException e) { - serverLog("Interrupted exception in Main Server Thread thread sleep", 1); + logger.trace("Interrupted exception in Main Server Thread thread sleep", 1); } if (GameState.getCurrentStage() == GameStages.LOBBYING && GameState .getCustomizationFlag()) { // TODO: 16/08/17 ajm412: This can probably be done in a nicer way via those fancy functional interfaces. - for (ServerToClientThread thread : serverToClientThreads) { - thread.sendSetupMessages(); - } + sendSetupMessages(); GameState.resetCustomizationFlag(); } if (GameState.getCurrentStage() == GameStages.PRE_RACE) { - updateClients(); + sendBoatLocations(); } //RACING if (GameState.getCurrentStage() == GameStages.RACING) { - updateClients(); + sendBoatLocations(); } //FINISHED @@ -105,12 +101,18 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { } } - public void updateClients() { - for (ServerToClientThread serverToClientThread : serverToClientThreads) { - serverToClientThread.sendBoatLocationPackets(); + public void sendBoatLocations() { + for (ServerYacht serverYacht : GameState.getYachts().values()) { + broadcastMessage(MessageFactory.getBoatLocationMessage(serverYacht)); } } + public void sendSetupMessages() { + broadcastMessage(MessageFactory.getRaceXML()); + broadcastMessage(MessageFactory.getRegattaXML()); + broadcastMessage(MessageFactory.getBoatXML()); + } + private void broadcastMessage(Message message) { for (ServerToClientThread serverToClientThread : serverToClientThreads) { serverToClientThread.sendMessage(message); @@ -154,10 +156,10 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { t.schedule(new TimerTask() { @Override public void run() { - broadcastMessage(makeRaceStatusMessage()); + broadcastMessage(MessageFactory.getRaceStatusMessage()); if (GameState.getCurrentStage() == GameStages.PRE_RACE || GameState.getCurrentStage() == GameStages.LOBBYING) { - broadcastMessage(makeRaceStartMessage()); + broadcastMessage(MessageFactory.getRaceStartStatusMessage()); } } }, 0, 500); @@ -184,6 +186,7 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { @Override public void run() { spawnNewCoins(); + broadcastMessage(MessageFactory.getRaceXML()); } }, 0, 60000); } @@ -210,28 +213,6 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { for (int i = 0; i < random.nextInt(allTokens.size() - 1); i++) { GameState.addToken(allTokens.get(i)); } - - XMLGenerator xmlGenerator = new XMLGenerator(); - List yachts = new ArrayList<>(GameState.getYachts().values()); - List tokens = GameState.getTokens(); - RaceXMLTemplate raceXMLTemplate = new RaceXMLTemplate(yachts, tokens); - xmlGenerator.setRaceTemplate(raceXMLTemplate); - - XMLMessage raceXMLMessage = new XMLMessage( - xmlGenerator.getRaceAsXml(), - XMLMessageSubType.RACE, - xmlGenerator.getRaceAsXml().length()); - - broadcastMessage(raceXMLMessage); - } - - - - static void serverLog(String message, int logLevel) { - if (logLevel <= LOG_LEVEL) { - System.out.println( - "[SERVER " + LocalDateTime.now().toLocalTime().toString() + "] " + message); - } } /** @@ -241,13 +222,9 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { */ @Override public void clientConnected(ServerToClientThread serverToClientThread) { - serverLog("Player Connected From " + serverToClientThread.getThread().getName(), 0); + logger.debug("Player Connected From " + serverToClientThread.getThread().getName(), 0); serverToClientThreads.add(serverToClientThread); - serverToClientThread.addConnectionListener(() -> { - for (ServerToClientThread thread : serverToClientThreads) { - thread.sendSetupMessages(); - } - }); + serverToClientThread.addConnectionListener(this::sendSetupMessages); serverToClientThread.addDisconnectListener(this::clientDisconnected); } @@ -258,71 +235,20 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { */ @Override public void clientDisconnected(Player player) { -// try { -// player.getSocket().close(); -// } catch (Exception e) { -// serverLog("Cannot disconnect the socket for the disconnected player.", 0); -// } - serverLog("Player " + player.getYacht().getSourceId() + "'s socket disconnected", 0); + logger.debug("Player " + player.getYacht().getSourceId() + "'s socket disconnected", 0); GameState.removeYacht(player.getYacht().getSourceId()); GameState.removePlayer(player); ServerToClientThread closedConnection = null; for (ServerToClientThread serverToClientThread : serverToClientThreads) { if (serverToClientThread.getSocket() == player.getSocket()) { - closedConnection = serverToClientThread; - } else if (GameState.getCurrentStage() != GameStages.RACING){ - serverToClientThread.sendSetupMessages(); + serverToClientThreads.remove(closedConnection); + closedConnection.terminate(); } } - serverToClientThreads.remove(closedConnection); - closedConnection.terminate(); - } - - 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 - - List boatSubMessages = new ArrayList<>(); - RaceStatus raceStatus; - - for (Player player : GameState.getPlayers()) { - ServerYacht y = player.getYacht(); - BoatSubMessage m = new BoatSubMessage(y.getSourceId(), y.getBoatStatus(), - y.getLegNumber(), - 0, 0, 1234L, - 1234L); - boatSubMessages.add(m); + if (GameState.getCurrentStage() != GameStages.RACING) { + sendSetupMessages(); } - - 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.STARTED; - } - - return new RaceStatusMessage(1, raceStatus, GameState.getStartTime(), - GameState.getWindDirection(), - GameState.getWindSpeedMMS().longValue(), GameState.getPlayers().size(), - RaceType.MATCH_RACE, 1, boatSubMessages); } public void terminate() { diff --git a/src/main/java/seng302/gameServer/MessageFactory.java b/src/main/java/seng302/gameServer/MessageFactory.java new file mode 100644 index 00000000..76410c1e --- /dev/null +++ b/src/main/java/seng302/gameServer/MessageFactory.java @@ -0,0 +1,130 @@ +package seng302.gameServer; + +import java.util.ArrayList; +import java.util.List; +import seng302.gameServer.messages.BoatLocationMessage; +import seng302.gameServer.messages.BoatSubMessage; +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.gameServer.messages.XMLMessage; +import seng302.gameServer.messages.XMLMessageSubType; +import seng302.model.Player; +import seng302.model.ServerYacht; +import seng302.model.stream.xml.generator.RaceXMLTemplate; +import seng302.model.stream.xml.generator.RegattaXMLTemplate; +import seng302.model.token.Token; +import seng302.utilities.XMLGenerator; + +/** + * A Class for interfacing between the data we have in the GameState to the messages we need to send + * through the MainServerThread. + * + * WARNING DO NOT USE THIS CLASS IF GAMESTATE HAS NOT BEEN INSTANTIATED. + * // TODO: 29/08/17 wmu16 - Make GameState non static to fix this ¯\_(ツ)_/¯ + * Created by wmu16 on 29/08/17. + */ +public class MessageFactory { + + private static XMLGenerator xmlGenerator = new XMLGenerator(); + + + public static RaceStartStatusMessage getRaceStartStatusMessage() { + return new RaceStartStatusMessage( + 1, + GameState.getStartTime(), + 1, + RaceStartNotificationType.SET_RACE_START_TIME); + } + + public static RaceStatusMessage getRaceStatusMessage() { + // variables taken from GameServerThread + + List boatSubMessages = new ArrayList<>(); + RaceStatus raceStatus; + + for (Player player : GameState.getPlayers()) { + ServerYacht y = player.getYacht(); + BoatSubMessage m = new BoatSubMessage(y.getSourceId(), y.getBoatStatus(), + y.getLegNumber(), + 0, 0, 1234L, + 1234L); + boatSubMessages.add(m); + } + + 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 > GameState.WARNING_TIME) { + raceStatus = RaceStatus.WARNING; + } + + if (timeTillStart > GameState.PREPATORY_TIME) { + raceStatus = RaceStatus.PREPARATORY; + } + } else { + raceStatus = RaceStatus.STARTED; + } + + return new RaceStatusMessage(1, raceStatus, GameState.getStartTime(), + GameState.getWindDirection(), + GameState.getWindSpeedMMS().longValue(), GameState.getPlayers().size(), + RaceType.MATCH_RACE, 1, boatSubMessages); + } + + public static BoatLocationMessage getBoatLocationMessage(ServerYacht yacht) { + return new BoatLocationMessage( + yacht.getSourceId(), + 0, // TODO: 29/08/17 wmu16 - Work out what to do with seqNo. Currently not used + yacht.getLocation().getLat(), + yacht.getLocation().getLng(), + yacht.getHeading(), + yacht.getCurrentVelocity().longValue()); + } + + public static XMLMessage getRaceXML() { + List yachts = new ArrayList<>(GameState.getYachts().values()); + List tokens = GameState.getTokens(); + RaceXMLTemplate raceXMLTemplate = new RaceXMLTemplate(yachts, tokens); + xmlGenerator.setRaceTemplate(raceXMLTemplate); + + XMLMessage raceXMLMessage = new XMLMessage( + xmlGenerator.getRaceAsXml(), + XMLMessageSubType.RACE, + xmlGenerator.getRaceAsXml().length()); + + return raceXMLMessage; + } + + public static XMLMessage getRegattaXML() { + //@TODO calculate lat/lng values + xmlGenerator.setRegattaTemplate( + new RegattaXMLTemplate( + "Party Parrot Test Server", "Bermuda Test Course", + 57.6679590, 11.8503233) + ); + + return new XMLMessage( + xmlGenerator.getRegattaAsXml(), + XMLMessageSubType.REGATTA, + xmlGenerator.getRegattaAsXml().length()); + } + + public static XMLMessage getBoatXML() { + List yachts = new ArrayList<>(GameState.getYachts().values()); + List tokens = GameState.getTokens(); + RaceXMLTemplate raceXMLTemplate = new RaceXMLTemplate(yachts, tokens); + xmlGenerator.setRaceTemplate(raceXMLTemplate); + + return new XMLMessage( + xmlGenerator.getBoatsAsXml(), + XMLMessageSubType.BOAT, + xmlGenerator.getBoatsAsXml().length()); + } +} diff --git a/src/main/java/seng302/gameServer/ServerToClientThread.java b/src/main/java/seng302/gameServer/ServerToClientThread.java index 7d516370..12ecf009 100644 --- a/src/main/java/seng302/gameServer/ServerToClientThread.java +++ b/src/main/java/seng302/gameServer/ServerToClientThread.java @@ -44,7 +44,7 @@ import seng302.utilities.XMLGenerator; * its own thread. All server threads created and owned by the server thread handler which can * trigger client updates on its threads Created by wmu16 on 13/07/17. */ -public class ServerToClientThread implements Runnable, Observer { +public class ServerToClientThread implements Runnable { /** * Called to notify listeners when this thread receives a connection correctly. @@ -128,21 +128,11 @@ public class ServerToClientThread implements Runnable, Observer { "Yacht", sourceId, sourceId.toString(), fName, fName + " " + lName, "NZ" ); - yacht.addObserver(this); // TODO: yacht can notify mark rounding message hyi25 13/8/17 player = new Player(socket, yacht); GameState.addYacht(sourceId, yacht); GameState.addPlayer(player); } - @Override - public void update(Observable o, Object arg) { - if (arg != null) { - sendMessage((Message) arg); - } else { - sendSetupMessages(); - } - } - private void completeRegistration(ClientType clientType) throws IOException { // Fail if not a player if (!clientType.equals(ClientType.PLAYER)){ @@ -234,45 +224,6 @@ public class ServerToClientThread implements Runnable, Observer { logger.warn("Closed serverToClientThread" + thread, 1); } - - /** - * Generates XML messages of each type and sends them to the client - */ - // TODO: 29/08/17 wmu16 - This functionality should not even be here - public void sendSetupMessages() { - xmlGenerator = new XMLGenerator(); - List yachts = new ArrayList<>(GameState.getYachts().values()); - List tokens = GameState.getTokens(); - RaceXMLTemplate raceXMLTemplate = new RaceXMLTemplate(yachts, tokens); - xmlGenerator.setRaceTemplate(raceXMLTemplate); - - //@TODO calculate lat/lng values - xmlGenerator.setRegattaTemplate( - new RegattaXMLTemplate( - "Party Parrot Test Server", "Bermuda Test Course", - 57.6679590, 11.8503233) - ); - - XMLMessage regattaXMLMessage = new XMLMessage( - xmlGenerator.getRegattaAsXml(), - XMLMessageSubType.REGATTA, - xmlGenerator.getRegattaAsXml().length()); - - XMLMessage boatXMLMessage = new XMLMessage( - xmlGenerator.getBoatsAsXml(), - XMLMessageSubType.BOAT, - xmlGenerator.getBoatsAsXml().length()); - - XMLMessage raceXMLMessage = new XMLMessage( - xmlGenerator.getRaceAsXml(), - XMLMessageSubType.RACE, - xmlGenerator.getRaceAsXml().length()); - - sendMessage(regattaXMLMessage); - sendMessage(boatXMLMessage); - sendMessage(raceXMLMessage); - } - private void closeSocket() { try { socket.close(); @@ -327,23 +278,6 @@ public class ServerToClientThread implements Runnable, Observer { return seqNo; } - - public void sendBoatLocationPackets() { - ArrayList yachts = new ArrayList<>(GameState.getYachts().values()); - for (ServerYacht yacht : yachts) { - BoatLocationMessage boatLocationMessage = - new BoatLocationMessage( - yacht.getSourceId(), - getSeqNo(), - yacht.getLocation().getLat(), - yacht.getLocation().getLng(), - yacht.getHeading(), - yacht.getCurrentVelocity().longValue()); - - sendMessage(boatLocationMessage); - } - } - public Thread getThread() { return thread; } diff --git a/src/main/java/seng302/model/ServerYacht.java b/src/main/java/seng302/model/ServerYacht.java index 64143023..74a1495c 100644 --- a/src/main/java/seng302/model/ServerYacht.java +++ b/src/main/java/seng302/model/ServerYacht.java @@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory; import seng302.gameServer.GameState; import seng302.gameServer.messages.BoatStatus; import seng302.model.mark.Mark; +import seng302.model.token.TokenType; import seng302.utilities.GeoUtility; /** @@ -16,7 +17,7 @@ import seng302.utilities.GeoUtility; * compared to the XMLParser boat class, also done outside Boat class because some old variables are * not used anymore. */ -public class ServerYacht extends Observable { +public class ServerYacht { private Logger logger = LoggerFactory.getLogger(ClientYacht.class); @@ -30,10 +31,8 @@ public class ServerYacht extends Observable { private String boatName; private String country; private BoatStatus boatStatus; - private Color boatColor; - //Location private Double lastHeading; private Boolean sailIn; @@ -52,6 +51,9 @@ public class ServerYacht extends Observable { private Boolean hasPassedLine; private Boolean hasPassedThroughGate; + //PowerUp + private TokenType powerUp; + public ServerYacht(String boatType, Integer sourceId, String hullID, String shortName, String boatName, String country) { @@ -71,6 +73,7 @@ public class ServerYacht extends Observable { this.currentMarkSeqID = 0; this.legNumber = 0; this.boatColor = Colors.getColor(sourceId - 1); + this.powerUp = null; this.hasEnteredRoundingZone = false; this.hasPassedLine = false; @@ -101,13 +104,12 @@ public class ServerYacht extends Observable { location = geoPoint; } - /** - * Add ServerToClientThread as the observer, this observer pattern mainly server for the boat - * rounding package. - */ - @Override - public void addObserver(Observer o) { - super.addObserver(o); + public void setPowerUp(TokenType powerUp) { + this.powerUp = powerUp; + } + + public TokenType getPowerUp() { + return powerUp; } /** diff --git a/src/main/java/seng302/visualiser/ClientToServerThread.java b/src/main/java/seng302/visualiser/ClientToServerThread.java index 57256760..cfb45cf6 100644 --- a/src/main/java/seng302/visualiser/ClientToServerThread.java +++ b/src/main/java/seng302/visualiser/ClientToServerThread.java @@ -33,8 +33,6 @@ import seng302.model.stream.packets.StreamPacket; */ public class ClientToServerThread implements Runnable { - - /** * Functional interface for receiving packets from client socket. */ @@ -95,7 +93,7 @@ public class ClientToServerThread implements Runnable { sendRegistrationRequest(); - thread = new Thread(this); + thread = new Thread(this, "ClientToServer"); thread.start(); } diff --git a/src/main/java/seng302/visualiser/GameView.java b/src/main/java/seng302/visualiser/GameView.java index 668a9cbc..53e4e58d 100644 --- a/src/main/java/seng302/visualiser/GameView.java +++ b/src/main/java/seng302/visualiser/GameView.java @@ -26,17 +26,23 @@ import javafx.scene.shape.Circle; import javafx.scene.shape.Polygon; import javafx.scene.text.Text; import javafx.util.Duration; +import seng302.gameServer.GameState; +import seng302.gameServer.messages.XMLMessage; +import seng302.gameServer.messages.XMLMessageSubType; import seng302.model.ClientYacht; import seng302.gameServer.messages.RoundingSide; import seng302.model.ClientYacht; import seng302.model.Colors; import seng302.model.GeoPoint; import seng302.model.Limit; +import seng302.model.ServerYacht; import seng302.model.mark.CompoundMark; import seng302.model.mark.Corner; import seng302.model.mark.Mark; +import seng302.model.stream.xml.generator.RaceXMLTemplate; import seng302.model.token.Token; import seng302.utilities.GeoUtility; +import seng302.utilities.XMLGenerator; import seng302.visualiser.fxObjects.AnnotationBox; import seng302.visualiser.fxObjects.BoatObject; import seng302.visualiser.fxObjects.CourseBoundary;