Merged with develop. Fixed many bugs in Visualiser.

#bugs
This commit is contained in:
Calum
2017-07-26 02:49:31 +12:00
parent acd54dec7a
commit 08e369f1ae
41 changed files with 1693 additions and 1636 deletions
-1
View File
@@ -7,7 +7,6 @@ import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import seng302.model.PolarTable;
import seng302.model.stream.parsers.StreamParser;
import seng302.model.stream.StreamReceiver;
public class App extends Application {
+64 -67
View File
@@ -1,78 +1,75 @@
package seng302.client;
import com.sun.org.apache.xpath.internal.operations.Bool;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import seng302.models.Yacht;
import seng302.model.Yacht;
/**
* Used by the client to store static variables to be used in game.
*/
public class ClientState {
private static String hostIp = "";
private static Boolean isHost = false;
private static Boolean raceStarted = false;
private static Boolean connectedToHost = false;
private static Map<Integer, Yacht> boats = new ConcurrentHashMap<>();
private static Boolean dirtyState = true;
private static String clientSourceId = "";
public static String getHostIp() {
return hostIp;
}
public static void setHostIp(String hostIp) {
ClientState.hostIp = hostIp;
}
public static Boolean isHost() {
return isHost;
}
public static void setHost(Boolean isHost) {
ClientState.isHost = isHost;
}
public static Boolean isRaceStarted() {
return raceStarted;
}
public static void setRaceStarted(Boolean raceStarted) {
ClientState.raceStarted = raceStarted;
}
public static Boolean isConnectedToHost() {
return connectedToHost;
}
public static void setConnectedToHost(Boolean connectedToHost) {
ClientState.connectedToHost = connectedToHost;
}
public static Map<Integer, Yacht> getBoats() {
return boats;
}
public static Boolean isDirtyState() {
return dirtyState;
}
public static void setDirtyState(Boolean dirtyState) {
ClientState.dirtyState = dirtyState;
}
public static String getClientSourceId() {
return clientSourceId;
}
public static void setClientSourceId(String clientSourceId) {
ClientState.clientSourceId = clientSourceId;
}
public static void setBoats(Map<Integer, Yacht> boats) {
ClientState.boats = boats;
}
// private static String hostIp = "";
// private static Boolean isHost = false;
// private static Boolean raceStarted = false;
// private static Boolean connectedToHost = false;
// private static Map<Integer, Yacht> boats = new ConcurrentHashMap<>();
// private static Boolean dirtyState = true;
// private static String clientSourceId = "";
//
// public static String getHostIp() {
// return hostIp;
// }
//
// public static void setHostIp(String hostIp) {
// ClientState.hostIp = hostIp;
// }
//
// public static Boolean isHost() {
// return isHost;
// }
//
// public static void setHost(Boolean isHost) {
// ClientState.isHost = isHost;
// }
//
// public static Boolean isRaceStarted() {
// return raceStarted;
// }
//
// public static void setRaceStarted(Boolean raceStarted) {
// ClientState.raceStarted = raceStarted;
// }
//
// public static Boolean isConnectedToHost() {
// return connectedToHost;
// }
//
// public static void setConnectedToHost(Boolean connectedToHost) {
// ClientState.connectedToHost = connectedToHost;
// }
//
// public static Map<Integer, Yacht> getBoats() {
// return boats;
// }
//
// public static Boolean isDirtyState() {
// return dirtyState;
// }
//
// public static void setDirtyState(Boolean dirtyState) {
// ClientState.dirtyState = dirtyState;
// }
//
// public static String getClientSourceId() {
// return clientSourceId;
// }
//
// public static void setClientSourceId(String clientSourceId) {
// ClientState.clientSourceId = clientSourceId;
// }
//
// public static void setBoats(Map<Integer, Yacht> boats) {
// ClientState.boats = boats;
// }
}
@@ -1,43 +1,43 @@
package seng302.client;
import java.util.Observable;
/**
* Used by LobbyController to run a separate thread-loop
* updates the controller when change is detected.
*/
public class ClientStateQueryingRunnable extends Observable implements Runnable {
private Boolean terminate = false;
public ClientStateQueryingRunnable() {}
@Override
public void run() {
while(!terminate) {
// Sleeping the thread so it will respond to the if statement below
// if you know a better fix, pls tell me :) -ryan
try {
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ClientState.isRaceStarted() && ClientState.isConnectedToHost()) {
setChanged();
notifyObservers("game started");
terminate();
}
if (ClientState.isDirtyState()) {
setChanged();
notifyObservers("update players");
ClientState.setDirtyState(false);
}
}
}
public void terminate() {
terminate = true;
}
}
//package seng302.client;
//
//import java.util.Observable;
//
///**
// * Used by LobbyController to run a separate thread-loop
// * updates the controller when change is detected.
// */
//public class ClientStateQueryingRunnable extends Observable implements Runnable {
//
// private Boolean terminate = false;
//
// public ClientStateQueryingRunnable() {}
//
// @Override
// public void run() {
// while(!terminate) {
// // Sleeping the thread so it will respond to the if statement below
// // if you know a better fix, pls tell me :) -ryan
// try {
// Thread.sleep(0);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// if (ClientState.isRaceStarted() && ClientState.isConnectedToHost()) {
// setChanged();
// notifyObservers("game started");
// terminate();
// }
//
// if (ClientState.isDirtyState()) {
// setChanged();
// notifyObservers("update players");
// ClientState.setDirtyState(false);
// }
// }
// }
//
// public void terminate() {
// terminate = true;
// }
//}
@@ -1,99 +1,99 @@
package seng302.controllers;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import seng302.client.ClientPacketParser;
import seng302.client.ClientToServerThread;
import seng302.server.messages.BoatActionMessage;
import seng302.server.messages.BoatActionType;
public class Controller implements Initializable {
@FXML
private AnchorPane contentPane;
private ClientToServerThread clientToServerThread;
private long lastSendingTime;
private int KEY_STROKE_SENDING_FREQUENCY = 50;
private Object setContentPane(String jfxUrl) {
try {
contentPane.getChildren().removeAll();
contentPane.getChildren().clear();
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
FXMLLoader fxmlLoader = new FXMLLoader((getClass().getResource(jfxUrl)));
Parent view = fxmlLoader.load();
contentPane.getChildren().addAll(view);
return fxmlLoader.getController();
} catch (javafx.fxml.LoadException e) {
System.err.println(e.getCause());
} catch (IOException e) {
System.err.println(e);
}
return null;
}
@Override
public void initialize(URL location, ResourceBundle resources) {
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
StartScreenController startScreenController = (StartScreenController) setContentPane("/views/StartScreenView.fxml");
startScreenController.setController(this);
ClientPacketParser.boatLocations.clear();
lastSendingTime = System.currentTimeMillis();
}
/** Handle the key-pressed event from the text field. */
public void keyPressed(KeyEvent e) {
BoatActionMessage boatActionMessage;
long currentTime = System.currentTimeMillis();
if (currentTime - lastSendingTime > KEY_STROKE_SENDING_FREQUENCY) {
lastSendingTime = currentTime;
switch (e.getCode()) {
case SPACE: // align with vmg
boatActionMessage = new BoatActionMessage(BoatActionType.VMG);
clientToServerThread.sendBoatActionMessage(boatActionMessage);
break;
case PAGE_UP: // upwind
boatActionMessage = new BoatActionMessage(BoatActionType.UPWIND);
clientToServerThread.sendBoatActionMessage(boatActionMessage);
break;
case PAGE_DOWN: // downwind
boatActionMessage = new BoatActionMessage(BoatActionType.DOWNWIND);
clientToServerThread.sendBoatActionMessage(boatActionMessage);
break;
case ENTER: // tack/gybe
boatActionMessage = new BoatActionMessage(BoatActionType.TACK_GYBE);
clientToServerThread.sendBoatActionMessage(boatActionMessage);
break;
//TODO Allow a zoom in and zoom out methods
case Z: // zoom in
System.out.println("Zoom in");
break;
case X: // zoom out
System.out.println("Zoom out");
break;
}
}
}
public void keyReleased(KeyEvent e) {
switch (e.getCode()) {
//TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
case SHIFT: // sails in/sails out
BoatActionMessage boatActionMessage = new BoatActionMessage(BoatActionType.SAILS_IN);
clientToServerThread.sendBoatActionMessage(boatActionMessage);
break;
}
}
public void setClientToServerThread(ClientToServerThread ctt) {
clientToServerThread = ctt;
}
}
//package seng302.controllers;
//
//import java.io.IOException;
//import java.net.URL;
//import java.util.ResourceBundle;
//import javafx.fxml.FXML;
//import javafx.fxml.FXMLLoader;
//import javafx.fxml.Initializable;
//import javafx.scene.Parent;
//import javafx.scene.input.KeyEvent;
//import javafx.scene.layout.AnchorPane;
//import seng302.client.ClientPacketParser;
//import seng302.client.ClientToServerThread;
//import seng302.server.messages.BoatActionMessage;
//import seng302.server.messages.BoatActionType;
//
//public class Controller implements Initializable {
//
// @FXML
// private AnchorPane contentPane;
// private ClientToServerThread clientToServerThread;
// private long lastSendingTime;
// private int KEY_STROKE_SENDING_FREQUENCY = 50;
//
// private Object setContentPane(String jfxUrl) {
// try {
// contentPane.getChildren().removeAll();
// contentPane.getChildren().clear();
// contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
// FXMLLoader fxmlLoader = new FXMLLoader((getClass().getResource(jfxUrl)));
// Parent view = fxmlLoader.load();
// contentPane.getChildren().addAll(view);
// return fxmlLoader.getController();
// } catch (javafx.fxml.LoadException e) {
// System.err.println(e.getCause());
// } catch (IOException e) {
// System.err.println(e);
// }
// return null;
// }
//
// @Override
// public void initialize(URL location, ResourceBundle resources) {
// contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
// StartScreenController startScreenController = (StartScreenController) setContentPane("/views/StartScreenView.fxml");
// startScreenController.setController(this);
// ClientPacketParser.boatLocations.clear();
//
// lastSendingTime = System.currentTimeMillis();
// }
//
// /** Handle the key-pressed event from the text field. */
// public void keyPressed(KeyEvent e) {
// BoatActionMessage boatActionMessage;
// long currentTime = System.currentTimeMillis();
// if (currentTime - lastSendingTime > KEY_STROKE_SENDING_FREQUENCY) {
// lastSendingTime = currentTime;
// switch (e.getCode()) {
// case SPACE: // align with vmg
// boatActionMessage = new BoatActionMessage(BoatActionType.VMG);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case PAGE_UP: // upwind
// boatActionMessage = new BoatActionMessage(BoatActionType.UPWIND);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case PAGE_DOWN: // downwind
// boatActionMessage = new BoatActionMessage(BoatActionType.DOWNWIND);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case ENTER: // tack/gybe
// boatActionMessage = new BoatActionMessage(BoatActionType.TACK_GYBE);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// //TODO Allow a zoom in and zoom out methods
// case Z: // zoom in
// System.out.println("Zoom in");
// break;
// case X: // zoom out
// System.out.println("Zoom out");
// break;
// }
// }
// }
//
// public void keyReleased(KeyEvent e) {
// switch (e.getCode()) {
// //TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
// case SHIFT: // sails in/sails out
// BoatActionMessage boatActionMessage = new BoatActionMessage(BoatActionType.SAILS_IN);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// }
// }
//
// public void setClientToServerThread(ClientToServerThread ctt) {
// clientToServerThread = ctt;
// }
//}
File diff suppressed because it is too large Load Diff
@@ -2,10 +2,11 @@ package seng302.gameServer;
import java.util.*;
import seng302.models.Player;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import seng302.model.Player;
import seng302.models.Yacht;
import seng302.model.Yacht;
import seng302.model.stream.packets.StreamPacket;
import seng302.server.messages.BoatActionType;
/**
@@ -23,14 +24,26 @@ public class GameState {
private static Map<Integer, Yacht> yachts;
private static Boolean isRaceStarted;
private static GameStages currentStage;
// TODO: 26/07/17 cir27 - Super hackish fix until something more permanent can be made.
private static ObservableList<String> observablePlayers = FXCollections.observableArrayList();
private static Map<Player, String> playerStringMap = new HashMap<>();
/*
Ideally I would like to make this class an object instantiated by the server and given to
it's created threads if necessary. Outside of that I think the dependencies on it
(atm only Yacht & GameClient) can be removed from most other classes. The observable list of
players could be pulled directly from the server by the GameClient since it instantiates it
and it is reasonable for it to pull data. The current setup of publicly available statics is
pretty meh IMO because anything can change it making it unreliable and like people did with
the old ServerParser class everything that needs shared just gets thrown in the static
collections and things become a real mess.
*/
public GameState(String hostIpAddress) {
windDirection = 170d;
windSpeed = 10000d;
yachts = new HashMap<>();
players = new ArrayList<>();
GameState.hostIpAddress = hostIpAddress;
players = new ArrayList<>();
currentStage = GameStages.LOBBYING;
@@ -48,13 +61,22 @@ public class GameState {
public static List<Player> getPlayers() {
return players;
}
public static ObservableList<String> getObservablePlayers () {
return observablePlayers;
}
public static void addPlayer(Player player) {
players.add(player);
String playerText = player.getYacht().getSourceId() + " " + player.getYacht().getBoatName() + " " + player.getYacht().getCountry();
observablePlayers.add(playerText);
playerStringMap.put(player, playerText);
}
public static void removePlayer(Player player) {
players.remove(player);
observablePlayers.remove(playerStringMap.get(player));
playerStringMap.remove(player);
}
public static void addYacht(Integer sourceId, Yacht yacht) {
@@ -2,16 +2,14 @@ package seng302.gameServer;
import java.time.LocalDateTime;
import java.util.Observable;
import seng302.client.ClientPacketParser;
import seng302.models.Player;
import seng302.models.stream.PacketBufferDelegate;
import seng302.models.stream.packets.StreamPacket;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.PriorityBlockingQueue;
import seng302.model.Player;
import seng302.model.stream.PacketBufferDelegate;
import seng302.model.stream.packets.StreamPacket;
/**
* A class describing the overall server, which creates and collects server threads for each client
@@ -84,7 +82,7 @@ public class MainServerThread extends Observable implements Runnable, PacketBuff
while (!packetBuffer.isEmpty()){
try {
StreamPacket packet = packetBuffer.take();
ClientPacketParser.parsePacket(packet);
// ClientPacketParser.parsePacket(packet);
} catch (InterruptedException e) {
continue;
}
@@ -159,4 +157,16 @@ public class MainServerThread extends Observable implements Runnable, PacketBuff
serverToClientThread.sendRaceStatusMessage();
}
}
public void shutDown() {
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
try {
serverToClientThread.getSocket().close();
} catch (IOException ioe) {
serverLog("Failed to close socket " + serverToClientThread.getSocket().toString(), 0);
}
}
serverToClientThreads = null;
thread = null;
}
}
@@ -1,7 +1,7 @@
package seng302.gameServer;
import java.util.Arrays;
import seng302.models.stream.packets.StreamPacket;
import seng302.model.stream.packets.StreamPacket;
import seng302.server.messages.BoatActionType;
@@ -12,35 +12,26 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
import org.apache.commons.io.IOUtils;
import seng302.models.Player;
import seng302.models.Yacht;
import seng302.models.stream.packets.PacketType;
import seng302.models.stream.packets.StreamPacket;
import seng302.models.xml.Race;
import seng302.models.xml.Regatta;
import seng302.models.xml.XMLGenerator;
import seng302.model.Player;
import seng302.model.Yacht;
import seng302.model.stream.packets.PacketType;
import seng302.model.stream.packets.StreamPacket;
import seng302.model.stream.xml.generator.Race;
import seng302.model.stream.xml.generator.Regatta;
import seng302.model.stream.xml.generator.XMLGenerator;
import seng302.server.messages.BoatActionType;
import seng302.server.messages.BoatLocationMessage;
import seng302.server.messages.BoatStatus;
import seng302.server.messages.BoatSubMessage;
import seng302.server.messages.Message;
import java.io.*;
import java.net.Socket;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
import seng302.server.messages.RaceStatus;
import seng302.server.messages.RaceStatusMessage;
import seng302.server.messages.RaceType;
import seng302.server.messages.XMLMessage;
import seng302.server.messages.XMLMessageSubType;
import seng302.server.messages.XMLMessage;
import seng302.server.messages.XMLMessageSubType;
import seng302.utilities.GeoPoint;
/**
* A class describing a single connection to a Client for the purposes of sending and receiving on
@@ -153,7 +144,7 @@ public class ServerToClientThread implements Runnable, Observer {
long packetCrc = Message.bytesToLong(getBytes(4));
if (computedCrc == packetCrc) {
//System.out.println("RECEIVED A PACKET");
switch (PacketType.assignPacketType(type)) {
switch (PacketType.assignPacketType(type, payload)) {
case BOAT_ACTION:
BoatActionType actionType = ServerPacketParser
.extractBoatAction(
@@ -306,7 +297,7 @@ public class ServerToClientThread implements Runnable, Observer {
yacht.getLocation().getLat(),
yacht.getLocation().getLng(),
yacht.getHeading(),
(long) yacht.getVelocity());
yacht.getVelocity().longValue());
sendMessage(boatLocationMessage);
}
+3 -3
View File
@@ -9,7 +9,7 @@ import java.net.Socket;
public class Player {
private Socket socket;
private Boat boat;
private Yacht yacht;
private Integer lastMarkPassed;
@@ -30,8 +30,8 @@ public class Player {
this.lastMarkPassed = lastMarkPassed;
}
public Boat getYacht() {
return boat;
public Yacht getYacht() {
return yacht;
}
@Override
+3 -3
View File
@@ -3,8 +3,8 @@ package seng302.model;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import seng302.model.stream.parsers.RaceStartData;
import seng302.model.stream.parsers.RaceStatusData;
import seng302.model.stream.parser.RaceStartData;
import seng302.model.stream.parser.RaceStatusData;
/**
* Class for storing race data that does not relate to specific vessels or marks such as time or wind.
@@ -19,7 +19,7 @@ public class RaceState {
private double windDirection;
private long raceTime;
private long expectedStartTime;
private boolean isRaceStarted;
private boolean isRaceStarted = false;
// long timeTillStart;
public RaceState() {
@@ -5,17 +5,13 @@ import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.ReadOnlyLongProperty;
import javafx.beans.property.ReadOnlyLongWrapper;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import seng302.model.mark.Mark;
import static seng302.utilities.GeoUtility.getGeoCoordinate;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import javafx.scene.paint.Color;
import seng302.client.ClientPacketParser;
import seng302.controllers.RaceViewController;
import seng302.gameServer.GameState;
import seng302.models.mark.Mark;
import seng302.utilities.GeoPoint;
/**
@@ -24,17 +20,8 @@ import seng302.utilities.GeoPoint;
* Class created to store more variables (eg. boat statuses) compared to the XMLParser boat class,
* also done outside Boat class because some old variables are not used anymore.
*/
public class Boat {
private final Double TURN_STEP = 5.0;
private Double lastHeading;
private Boolean sailIn;
// Used in boat group
private Color colour = Color.BLACK;
public class Yacht {
//BOTH AFAIK
private String boatType;
private Integer sourceId;
private String hullID; //matches HullNum in the XML spec.
@@ -42,28 +29,32 @@ public class Boat {
private String boatName;
private String country;
// Boat status
private Integer boatStatus;
private Integer legNumber = 0;
private Integer position = 0;
private Long estimateTimeAtFinish;
private Long markRoundTime;
private Double lat;
private Double lon;
private Double heading;
private ReadOnlyDoubleWrapper velocity = new ReadOnlyDoubleWrapper();
private ReadOnlyLongWrapper timeTillNext = new ReadOnlyLongWrapper();
private ReadOnlyLongWrapper timeSinceLastMark = new ReadOnlyLongWrapper();
private String position;
private GeoPoint location;
private Double heading;
private Double velocity;
private Long timeTillNext;
private Long markRoundTime;
private Double heading;
private Double lat;
private Double lon;
private Integer legNumber = 0;
// Mark rounding
//SERVER SIDE
private final Double TURN_STEP = 5.0;
private Double lastHeading;
private Boolean sailIn;
private String position;
private GeoPoint location;
private Integer boatStatus;
private Double velocity;
//CLIENT SIDE
private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper();
private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper();
private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper();
private ReadOnlyDoubleProperty headingProperty = new ReadOnlyDoubleWrapper();
private Mark lastMarkRounded;
private Mark nextMark;
private Integer positionInt = 0;
private Color colour;
/**
@@ -110,8 +101,6 @@ public class Boat {
public Yacht(String boatType, Integer sourceId, String hullID, String shortName,
String boatName, String country) {
public Boat(String boatType, Integer sourceID, String hullID, String shortName,
String boatName, String country) {
this.boatType = boatType;
this.sourceId = sourceId;
this.hullID = hullID;
@@ -134,18 +123,13 @@ public class Boat {
Double thisHeading = ((double) Math.floorMod(heading.longValue(), 360L));
Double windSpeedKnots = 0d;
Double boatSpeedInKnots = PolarTable.getBoatSpeed(windSpeedKnots, thisHeading);
velocity = boatSpeedInKnots / ClientPacketParser.MS_TO_KNOTS * 3000;
velocity = boatSpeedInKnots / 1.94384449 * 3000; // TODO: 25/07/17 cir27 - remove magic numbers
//System.out.println("velocity = " + velocity);
Double metersCovered = velocity * secondsElapsed;
location = getGeoCoordinate(location, heading, metersCovered);
}
}
public Double getHeading() {
return heading;
}
public void adjustHeading(Double amount) {
lastHeading = heading;
// TODO: 24/07/17 wmu16 - '%' in java does remainder, we need modulo. All this must be changed here, this is why we have neg values!
@@ -244,15 +228,14 @@ public class Boat {
}
public void setLegNumber(Integer legNumber) {
if (colour != null && position != "-" && legNumber != this.legNumber&& RaceViewController.sparkLineStatus(
sourceId)) {
RaceViewController.updateYachtPositionSparkline(this, legNumber);
}
// if (colour != null && position != "-" && legNumber != this.legNumber) {
// RaceViewController.updateYachtPositionSparkline(this, legNumber);
// }
this.legNumber = legNumber;
}
public void setEstimateTimeTillNextMark(Long estimateTimeAtNextMark) {
timeTillNext.set(estimateTimeAtNextMark);
public void setEstimateTimeTillNextMark(Long estimateTimeTillNextMark) {
timeTillNext = estimateTimeTillNextMark;
}
public String getEstimateTimeAtFinish() {
@@ -264,37 +247,28 @@ public class Boat {
this.estimateTimeAtFinish = estimateTimeAtFinish;
}
public Integer getPosition() {
return position;
public Integer getPositionInteger() {
return positionInt;
}
public void setPosition(Integer position) {
this.position = position;
public void setPositionInteger(Integer position) {
this.positionInt = position;
}
public Color getColour() {
return colour;
public void setVelocityProperty(double velocity) {
this.velocityProperty.set(velocity);
}
public void setColour(Color colour) {
this.colour = colour;
}
public void setVelocity(double velocity) {
this.velocity.set(velocity);
}
public void setMarkRoundingTime(Long markRoundingTime) {
this.markRoundTime = markRoundingTime;
}
public ReadOnlyDoubleProperty getVelocityProperty() {
return velocity.getReadOnlyProperty();
return velocityProperty.getReadOnlyProperty();
}
public ReadOnlyLongProperty timeTillNextProperty() {
return timeTillNext.getReadOnlyProperty();
return timeTillNextProperty.getReadOnlyProperty();
}
public Long getMarkRoundTime() {
@@ -339,6 +313,8 @@ public class Boat {
public void setHeading(Double heading) {
this.heading = heading;
}
public Boolean getSailIn() {
return sailIn;
}
@@ -352,12 +328,37 @@ public class Boat {
return location;
}
public void setTimeSinceLastMark (long timeSinceLastMark) {
this.timeSinceLastMark.set(timeSinceLastMark);
public void updateTimeSinceLastMarkProperty(long timeSinceLastMark) {
this.timeSinceLastMarkProperty.set(timeSinceLastMark);
}
public ReadOnlyLongProperty timeSinceLastMarkProperty () {
return timeSinceLastMark.getReadOnlyProperty();
return timeSinceLastMarkProperty.getReadOnlyProperty();
}
public Long getTimeTillNext() {
return timeTillNext;
}
public void setTimeTillNext(Long timeTillNext) {
this.timeTillNext = timeTillNext;
}
public Color getColour() {
return colour;
}
public void setColour(Color colour) {
this.colour = colour;
}
public Double getVelocity() {
return velocity;
}
public void setVelocity(Double velocity) {
this.velocity = velocity;
}
}
@@ -1,4 +1,4 @@
package seng302.model.stream.parsers;
package seng302.model.stream.parser;
/**
@@ -11,7 +11,7 @@ public class MarkRoundingData {
private int roundingSide;
private long timeStamp;
public MarkRoundingData(int boatId, int markId, int roundingSide, long timeStamp) {
MarkRoundingData(int boatId, int markId, int roundingSide, long timeStamp) {
this.boatId = boatId;
this.markId = markId;
this.roundingSide = roundingSide;
@@ -1,4 +1,4 @@
package seng302.model.stream.parsers;
package seng302.model.stream.parser;
public class PositionUpdateData {
@@ -14,7 +14,7 @@ public class PositionUpdateData {
private double heading;
private double groundSpeed;
public PositionUpdateData(int deviceId, DeviceType type, double lat, double lon,
PositionUpdateData(int deviceId, DeviceType type, double lat, double lon,
double heading, double groundSpeed) {
this.deviceId = deviceId;
this.type = type;
@@ -1,16 +1,16 @@
package seng302.model.stream.parsers;
package seng302.model.stream.parser;
/**
* Class for storing data parsed from race start status packet
*/
public class RaceStartData {
long raceId;
long raceStartTime;
int notificationType;
long timeStamp;
private long raceId;
private long raceStartTime;
private int notificationType;
private long timeStamp;
public RaceStartData (long raceId, long raceStartTime, int notificationType, long timeStamp) {
RaceStartData (long raceId, long raceStartTime, int notificationType, long timeStamp) {
this.raceId = raceId;
this.raceStartTime = raceStartTime;
this.notificationType = notificationType;
@@ -1,4 +1,4 @@
package seng302.model.stream.parsers;
package seng302.model.stream.parser;
import java.util.ArrayList;
import java.util.List;
@@ -17,9 +17,9 @@ public class RaceStatusData {
private boolean raceStarted = false;
private long currentTime;
private long expectedStartTime;
List<long[]> boatData = new ArrayList<>();
private List<long[]> boatData = new ArrayList<>();
public RaceStatusData(
RaceStatusData(
long windDir, long rawWindSpeed, int raceStatus, long currentTime, long expectedStartTime) {
windDirection = windDir / WIND_DIR_FACTOR;
@@ -29,7 +29,7 @@ public class RaceStatusData {
this.expectedStartTime = expectedStartTime;
}
public void addBoatData (long boatID, long estTimeToNextMark, long estTimeToFinish, int leg) {
void addBoatData (long boatID, long estTimeToNextMark, long estTimeToFinish, int leg) {
boatData.add(new long[] {boatID, estTimeToNextMark, estTimeToFinish, leg});
}
@@ -1,6 +1,6 @@
package seng302.model.stream.parsers;
package seng302.model.stream.parser;
import seng302.model.stream.parsers.PositionUpdateData.DeviceType;
import seng302.model.stream.parser.PositionUpdateData.DeviceType;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
@@ -14,7 +14,6 @@ import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import seng302.model.stream.packets.PacketType;
import seng302.model.stream.packets.StreamPacket;
import seng302.model.stream.parsers.xml.RegattaXMLData;
/**
* StreamParser is a utilities class for taking byte data, formatted according to the AC35
@@ -1,11 +1,10 @@
package seng302.models.xml;
import seng302.models.Yacht;
package seng302.model.stream.xml.generator;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import seng302.model.Yacht;
/**
* A Race object that can be parsed into XML
@@ -1,4 +1,4 @@
package seng302.models.xml;
package seng302.model.stream.xml.generator;
/**
* A Race regatta that can be parsed into XML
@@ -1,13 +1,11 @@
package seng302.models.xml;
package seng302.model.stream.xml.generator;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.io.IOUtils;
import seng302.server.messages.XMLMessageSubType;
import java.io.*;
import java.net.URISyntaxException;
/**
* An XML generator to generate the Race, Boat, and Regatta XML dynamically
@@ -1,4 +1,4 @@
package seng302.model.stream.parsers.xml;
package seng302.model.stream.xml.parser;
import java.util.HashMap;
import java.util.List;
@@ -1,4 +1,4 @@
package seng302.model.stream.parsers.xml;
package seng302.model.stream.xml.parser;
/**
* Stores data from regatta xml packet.
@@ -1,4 +1,4 @@
package seng302.model.stream.parsers.xml;
package seng302.model.stream.xml.parser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -7,7 +7,7 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import seng302.model.Boat;
import seng302.model.Yacht;
import seng302.model.Corner;
import seng302.model.Limit;
import seng302.model.mark.GateMark;
@@ -124,8 +124,8 @@ public class XMLParser {
* @param doc XML Document Object
* @return Mapping of sourceIds to Boats.
*/
public static Map<Integer, Boat> parseBoats(Document doc){
Map<Integer, Boat> competingBoats = new HashMap<>();
public static Map<Integer, Yacht> parseBoats(Document doc){
Map<Integer, Yacht> competingBoats = new HashMap<>();
Element docEle = doc.getDocumentElement();
@@ -134,14 +134,14 @@ public class XMLParser {
Node currentBoat = boatsList.item(i);
if (currentBoat.getNodeName().equals("Boat")) {
// Boat boat = new Boat(currentBoat);
Boat boat = new Boat(XMLParser.getNodeAttributeString(currentBoat, "Type"),
Yacht yacht = new Yacht(XMLParser.getNodeAttributeString(currentBoat, "Type"),
XMLParser.getNodeAttributeInt(currentBoat, "SourceID"),
XMLParser.getNodeAttributeString(currentBoat, "HullNum"),
XMLParser.getNodeAttributeString(currentBoat, "ShortName"),
XMLParser.getNodeAttributeString(currentBoat, "BoatName"),
XMLParser.getNodeAttributeString(currentBoat, "Country"));
if (boat.getBoatType().equals("Yacht")) {
competingBoats.put(boat.getSourceID(), boat);
if (yacht.getBoatType().equals("Yacht")) {
competingBoats.put(yacht.getSourceId(), yacht);
}
}
}
@@ -3,7 +3,7 @@ package seng302.visualiser;
import seng302.model.stream.packets.StreamPacket;
/**
* Created by cir27 on 21/07/17.
* Functional interface for receiving packets from client socket.
*/
@FunctionalInterface
public interface ClientSocketListener {
@@ -9,48 +9,42 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.net.UnknownHostException;
import java.time.LocalDateTime;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
import seng302.model.stream.packets.StreamPacket;
import seng302.models.stream.packets.StreamPacket;
import seng302.server.messages.BoatActionMessage;
import seng302.server.messages.Message;
/**
* Created by kre39 on 13/07/17.
*/
public class ClientToServerThread extends Thread {
private Queue<StreamPacket> streamPackets = new ConcurrentLinkedQueue<>();
private List<ClientSocketListener> listeners = new ArrayList<>();
public class ClientToServerThread implements Runnable {
private static final int LOG_LEVEL = 1;
private Queue<StreamPacket> streamPackets = new ConcurrentLinkedQueue<>();
private List<ClientSocketListener> listeners = new ArrayList<>();
private Thread thread;
private Integer ourID;
private Socket socket;
private InputStream is;
private OutputStream os;
private int clientId;
private Boolean updateClient = true;
private ByteArrayOutputStream crcBuffer;
public ClientToServerThread(String ipAddress, Integer portNumber) throws Exception{
public ClientToServerThread(String ipAddress, Integer portNumber) throws IOException{
socket = new Socket(ipAddress, portNumber);
is = socket.getInputStream();
os = socket.getOutputStream();
Integer allocatedID = threeWayHandshake();
if (allocatedID != null) {
ourID = allocatedID;
clientLog("Successful handshake. Allocated ID: " + ourID, 1);
ClientState.setClientSourceId(String.valueOf(ourID));
clientId = allocatedID;
clientLog("Successful handshake. Allocated ID: " + clientId, 1);
} else {
clientLog("Unsuccessful handshake", 1);
closeSocket();
@@ -59,7 +53,6 @@ public class ClientToServerThread implements Runnable {
thread = new Thread(this);
thread.start();
}
static void clientLog(String message, int logLevel){
@@ -72,7 +65,7 @@ public class ClientToServerThread implements Runnable {
int sync1;
int sync2;
// TODO: 14/07/17 wmu16 - Work out how to fix this while loop
while(ClientState.isConnectedToHost()) {
while(true) { /**REMOVED SOMETHING HERE ClientState.isConnectedToHost() */
try {
//Perform a write if it is time to as delegated by the MainServerThread
if (updateClient) {
@@ -113,8 +106,8 @@ public class ClientToServerThread implements Runnable {
return;
}
}
closeSocket();
clientLog("Disconnected from server", 0);
// closeSocket();
// clientLog("Disconnected from server", 0);
}
@@ -0,0 +1,305 @@
package seng302.visualiser;
import java.io.IOException;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.Pane;
import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread;
import seng302.model.Yacht;
import seng302.model.RaceState;
import seng302.model.mark.Mark;
import seng302.model.stream.parser.PositionUpdateData.DeviceType;
import seng302.model.stream.parser.MarkRoundingData;
import seng302.model.stream.parser.RaceStatusData;
import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.stream.parser.StreamParser;
import seng302.model.stream.xml.parser.RegattaXMLData;
import seng302.model.stream.xml.parser.XMLParser;
import seng302.model.stream.parser.PositionUpdateData;
import seng302.model.stream.packets.StreamPacket;
import seng302.visualiser.ClientToServerThread;
import seng302.visualiser.controllers.LobbyController;
import seng302.visualiser.controllers.LobbyController.CloseStatus;
import seng302.visualiser.controllers.RaceViewController;
/**
* Created by cir27 on 20/07/17.
*/
public class GameClient {
private Pane holderPane;
private ClientToServerThread socketThread;
private MainServerThread server;
private RaceViewController raceView;
private Map<Integer, Yacht> allBoatsMap;
private RegattaXMLData regattaData;
private RaceXMLData courseData;
private RaceState raceState = new RaceState();
private ObservableList<String> lobbyList = FXCollections.observableArrayList();
public GameClient(Pane holder) {
this.holderPane = holder;
}
public void runAsClient (String ipAddress, Integer portNumber) {
try {
socketThread = new ClientToServerThread(ipAddress, portNumber);
} catch (IOException ioe) {
ioe.printStackTrace();
System.out.println("Unable to connect to host...");
}
LobbyController lobbyController = loadLobby("/views/LobbyView.fxml");
lobbyController.setPlayerListSource(lobbyList);
lobbyController.setTitle("Connected to host - IP : " + ipAddress + " Port : " + portNumber);
lobbyController.addCloseListener((exitCause) -> this.loadStartScreen());
socketThread.addStreamObserver(this::parsePacket);
}
public void runAsHost (String ipAddress, Integer portNumber) {
server = new MainServerThread();
try {
socketThread = new ClientToServerThread(ipAddress, portNumber);
} catch (IOException ioe) {
ioe.printStackTrace();
System.out.println("Unable to make local connection to host...");
}
LobbyController lobbyController = loadLobby("/views/HostLobbyView.fxml");
lobbyController.setPlayerListSource(GameState.getObservablePlayers());
lobbyController.setTitle("Hosting Lobby - IP : " + ipAddress + " Port : " + portNumber);
lobbyController.addCloseListener(exitCause -> {
if (exitCause == CloseStatus.READY) {
server.startGame();
socketThread.addStreamObserver(this::parsePacket);
} else if (exitCause == CloseStatus.LEAVE) {
loadStartScreen();
}
});
}
private void loadStartScreen () {
socketThread.closeSocket();
socketThread = null;
if (server != null) {
// TODO: 26/07/17 cir27 - handle disconnecting
server.shutDown();
server = null;
}
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/views/StartScreenView.fxml"));
try {
holderPane.getChildren().clear();
holderPane.getChildren().add(fxmlLoader.load());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Loads a view of the lobby into the clients pane
* @param lobbyView fxml file for the desired lobby
* @return the lobby controller.
*/
private LobbyController loadLobby (String lobbyView) {
FXMLLoader fxmlLoader = new FXMLLoader(GameClient.class.getResource(lobbyView));
try {
holderPane.getChildren().clear();
holderPane.getChildren().add(fxmlLoader.load());
} catch (IOException e) {
e.printStackTrace();
}
return fxmlLoader.getController();
}
private void loadRaceView () {
// allBoatsMap.forEach((id, boat) -> {
// if (courseData.getParticipants().contains(id))
// racingBoats.put(id, boat);
// });
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/views/RaceView.fxml"));
raceView = fxmlLoader.getController();
try {
holderPane.getChildren().add(fxmlLoader.load());
} catch (IOException e) {
e.printStackTrace();
}
raceView.loadRace(allBoatsMap, courseData, raceState);
}
private void parsePacket(StreamPacket packet) {
switch (packet.getType()) {
case RACE_STATUS:
processRaceStatusUpdate(StreamParser.extractRaceStatus(packet));
break;
case REGATTA_XML:
System.out.println("REGATTA XML");
regattaData = XMLParser.parseRegatta(
StreamParser.extractXmlMessage(packet)
);
raceState.setTimeZone(
TimeZone.getTimeZone(
ZoneId.ofOffset("UTC", ZoneOffset.ofHours(regattaData.getUtcOffset()))
)
);
startRaceIfAllDataReceived();
break;
case RACE_XML:
System.out.println("RACE XML");
courseData = XMLParser.parseRace(
StreamParser.extractXmlMessage(packet)
);
if (raceView != null) {
raceView.updateRaceData(courseData);
}
startRaceIfAllDataReceived();
break;
case BOAT_XML:
System.out.println("BOAT XML");
allBoatsMap = XMLParser.parseBoats(
StreamParser.extractXmlMessage(packet)
);
lobbyList.clear();
allBoatsMap.forEach((id, boat) -> lobbyList.add(id.toString() + boat.getBoatName()));
startRaceIfAllDataReceived();
break;
case RACE_START_STATUS:
raceState.updateState(StreamParser.extractRaceStartStatus(packet));
break;
case BOAT_LOCATION:
updatePosition(StreamParser.extractBoatLocation(packet));
break;
case MARK_ROUNDING:
updateMarkRounding(StreamParser.extractMarkRounding(packet));
break;
}
}
private void startRaceIfAllDataReceived() {
if (allXMLReceived())
loadRaceView();
}
private boolean allXMLReceived () {
return courseData != null && allBoatsMap != null && regattaData != null;
}
/**
* Updates the position of a boat. Boat and position are given in the provided data.
* @param positionData
*/
private void updatePosition(PositionUpdateData positionData) {
if (positionData.getType() == DeviceType.YACHT_TYPE) {
if (allXMLReceived() && allBoatsMap.containsKey(positionData.getDeviceId())) {
Yacht yacht = allBoatsMap.get(positionData.getDeviceId());
yacht.setVelocityProperty(positionData.getGroundSpeed());
yacht.setLat(positionData.getLat());
yacht.setLon(positionData.getLon());
yacht.setHeading(positionData.getHeading());
}
} else if (positionData.getType() == DeviceType.MARK_TYPE) {
Mark mark = courseData.getCompoundMarks().get(positionData.getDeviceId());
}
}
/**
* Updates the boat as having passed the mark. Boat and mark are given by the ids in the
* provided data.
* @param roundingData Contains data for the rounding of a mark.
*/
private void updateMarkRounding(MarkRoundingData roundingData) {
if (allXMLReceived()) {
Yacht yacht = allBoatsMap.get(roundingData.getBoatId());
yacht.setMarkRoundingTime(roundingData.getTimeStamp());
yacht.updateTimeSinceLastMarkProperty(
raceState.getRaceTime() - roundingData.getTimeStamp());
yacht.setLastMarkRounded(
courseData.getCompoundMarks().get(
roundingData.getMarkId()
)
);
}
}
private void processRaceStatusUpdate (RaceStatusData data) {
if (allXMLReceived()) {
raceState.updateState(data);
for (long[] boatData : data.getBoatData()) {
Yacht yacht = allBoatsMap.get((int) boatData[0]);
yacht.setEstimateTimeTillNextMark(raceState.getRaceTime() - boatData[1]);
yacht.setEstimateTimeAtFinish(boatData[2]);
int legNumber = (int) boatData[3];
yacht.setLegNumber(legNumber);
if (legNumber != yacht.getLegNumber()) {
int placing = 1;
for (Yacht otherYacht : allBoatsMap.values()) {
if (otherYacht.getSourceId() != boatData[0] &&
yacht.getLegNumber() <= otherYacht.getLegNumber())
placing++;
}
yacht.setPositionInteger(placing);
}
}
}
}
private void close () {
socketThread.closeSocket();
}
// /** Handle the key-pressed event from the text field. */
// public void keyPressed(KeyEvent e) {
// BoatActionMessage boatActionMessage;
// switch (e.getCode()){
// case SPACE: // align with vmg
// boatActionMessage = new BoatActionMessage(BoatActionType.VMG);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case PAGE_UP: // upwind
// boatActionMessage = new BoatActionMessage(BoatActionType.UPWIND);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case PAGE_DOWN: // downwind
// boatActionMessage = new BoatActionMessage(BoatActionType.DOWNWIND);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case ENTER: // tack/gybe
// boatActionMessage = new BoatActionMessage(BoatActionType.TACK_GYBE);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// //TODO Allow a zoom in and zoom out methods
// case Z: // zoom in
// System.out.println("Zoom in");
// break;
// case X: // zoom out
// System.out.println("Zoom out");
// break;
// }
// }
// public void keyReleased(KeyEvent e) {
// switch (e.getCode()) {
// //TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
// case SHIFT: // sails in/sails out
// BoatActionMessage boatActionMessage = new BoatActionMessage(BoatActionType.SAILS_IN);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// }
// }
//
// onKeyPressed="#keyPressed" onKeyReleased="#keyReleased"
}
+12 -13
View File
@@ -27,14 +27,13 @@ import seng302.visualiser.fxObjects.AnnotationBox;
import seng302.visualiser.fxObjects.BoatObject;
import seng302.visualiser.fxObjects.MarkObject;
import seng302.model.Colors;
import seng302.model.Boat;
import seng302.model.Yacht;
import seng302.model.map.Boundary;
import seng302.model.map.CanvasMap;
import seng302.model.mark.GateMark;
import seng302.model.mark.Mark;
import seng302.model.mark.MarkType;
import seng302.model.mark.SingleMark;
import seng302.model.stream.parsers.StreamParser;
import seng302.utilities.GeoPoint;
import seng302.utilities.GeoUtility;
@@ -65,7 +64,7 @@ public class GameView extends Pane {
private double metersPerPixelY;
private Map<SingleMark, MarkObject> markObjects = new HashMap<>();
private Map<Boat, BoatObject> boatObjects = new HashMap<>();
private Map<Yacht, BoatObject> boatObjects = new HashMap<>();
private List<AnnotationBox> annotations = new ArrayList<>();
private Text fpsDisplay = new Text();
@@ -255,17 +254,17 @@ public class GameView extends Pane {
/**
* Draws all the boats.
*/
public void setBoats(List<Boat> boats) {
public void setBoats(List<Yacht> yachts) {
Group annotationsGroup = new Group();
Group wakesGroup = new Group();
Group boatObjectGroup = new Group();
BoatObject newObject;
for (Boat boat : boats) {
for (Yacht yacht : yachts) {
newObject = new BoatObject();
// newObject.bindBoat(boat);
newObject.setFill(Colors.getColor());
createAnnotationBox(boat);
createAnnotationBox(yacht);
}
// Group wakes = new Group();
@@ -279,19 +278,19 @@ public class GameView extends Pane {
gameObjects.addAll(boatObjects.values());
}
private AnnotationBox createAnnotationBox (Boat boat) {
private AnnotationBox createAnnotationBox (Yacht yacht) {
AnnotationBox newAnnotation;
newAnnotation = new AnnotationBox();
newAnnotation.addAnnotation("name", boat.getShortName());
newAnnotation.addAnnotation("name", yacht.getShortName());
// newAnnotation.addAnnotation("country", boat.getCountry());
newAnnotation.addAnnotation(
"velocity",
boat.getVelocityProperty(),
yacht.getVelocityProperty(),
(velocity) -> String.format("%.2f ms", velocity.doubleValue())
);
newAnnotation.addAnnotation(
"nextMark",
boat.timeTillNextProperty(),
yacht.timeTillNextProperty(),
(time) -> {
DateFormat format = new SimpleDateFormat("mm:ss");
return format.format(time);
@@ -299,7 +298,7 @@ public class GameView extends Pane {
);
newAnnotation.addAnnotation(
"lastMark",
boat.timeTillNextProperty(),
yacht.timeTillNextProperty(),
(time) -> {
DateFormat format = new SimpleDateFormat("mm:ss");
return format.format(time);
@@ -518,9 +517,9 @@ public class GameView extends Pane {
return fpsDisplay.visibleProperty();
}
public void selectBoat (Boat selectedBoat) {
public void selectBoat (Yacht selectedYacht) {
boatObjects.forEach((boat, group) ->
group.setIsSelected(boat == selectedBoat)
group.setIsSelected(boat == selectedYacht)
);
}
@@ -1,8 +1,10 @@
package seng302.controllers;
package seng302.visualiser.controllers;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@@ -15,12 +17,8 @@ import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import seng302.client.ClientPacketParser;
import seng302.models.Yacht;
import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
import seng302.model.Boat;
import seng302.model.stream.parsers.StreamParser;
import seng302.model.stream.parsers.xml.XMLParser.RaceXMLObject.Participant;
import seng302.model.Yacht;
import seng302.model.stream.parser.StreamParser;
public class FinishScreenViewController implements Initializable {
@@ -37,6 +35,8 @@ public class FinishScreenViewController implements Initializable {
@FXML
private TableColumn<Yacht, String> countryCol;
ObservableList<Yacht> data = FXCollections.observableArrayList();
@Override
public void initialize(URL location, ResourceBundle resources) {
finishScreenGridPane.getStylesheets()
@@ -44,7 +44,6 @@ public class FinishScreenViewController implements Initializable {
finishOrderTable.getStylesheets().add(getClass().getResource("/css/master.css").toString());
// set up data for table
ObservableList<Yacht> data = FXCollections.observableArrayList();
finishOrderTable.setItems(data);
// setting table col data
@@ -60,24 +59,15 @@ public class FinishScreenViewController implements Initializable {
countryCol.setCellValueFactory(
new PropertyValueFactory<>("country")
);
// check if the boat is racing
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML()
.getParticipants();
ArrayList<Integer> participantIDs = new ArrayList<>();
for (Participant p : participants) {
participantIDs.add(p.getsourceID());
}
// add data to table
for (Yacht boat : StreamParser.getBoatsPos().values()) {
if (participantIDs.contains(boat.getSourceID())) {
data.add(boat);
}
}
finishOrderTable.refresh();
}
public void setFinishers (List<Yacht> participants) {
List<Yacht> sorted = new ArrayList<>(participants);
sorted.sort(Comparator.comparingInt(Yacht::getPositionInteger));
finishOrderTable.getItems().setAll(sorted);
}
private void setContentPane(String jfxUrl) {
try {
// get the main controller anchor pane (FinishView -> MainView)
@@ -1,14 +1,9 @@
package seng302.visualiser.controllers;
import java.io.IOException;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.URL;
import java.util.*;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
@@ -24,17 +19,28 @@ import javafx.scene.layout.Pane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.text.Text;
import seng302.client.ClientState;
import seng302.client.ClientStateQueryingRunnable;
import seng302.gameServer.GameStages;
import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread;
/**
* A class describing the actions of the lobby screen
* Created by wmu16 on 10/07/17.
*/
public class LobbyController implements Initializable, Observer{
public class LobbyController implements Initializable {
public enum CloseStatus {
LEAVE,
READY
}
@FunctionalInterface
public interface LobbyCloseListener {
void notify(CloseStatus exitCause);
}
@FXML
private ListView<String> competitorsListView;
@FXML
private GridPane lobbyScreen;
@FXML
@@ -42,7 +48,7 @@ public class LobbyController implements Initializable, Observer{
@FXML
private Button readyButton;
@FXML
private ListView firstListView;
private ListView<String> firstListView;
@FXML
private ListView secondListView;
@FXML
@@ -83,10 +89,11 @@ public class LobbyController implements Initializable, Observer{
private static ObservableList<String> sixthCompetitor = FXCollections.observableArrayList();
private static ObservableList<String> seventhCompetitor = FXCollections.observableArrayList();
private static ObservableList<String> eighthCompetitor = FXCollections.observableArrayList();
private ClientStateQueryingRunnable clientStateQueryingRunnable;
// private ClientStateQueryingRunnable clientStateQueryingRunnable;
private Boolean switchedPane = false;
private MainServerThread mainServerThread;
private List<LobbyCloseListener> lobbyListeners = new ArrayList<>();
private void setContentPane(String jfxUrl) {
try {
@@ -105,51 +112,54 @@ public class LobbyController implements Initializable, Observer{
@Override
public void initialize(URL location, ResourceBundle resources) {
if (ClientState.isHost()) {
lobbyIpText.setText("Lobby Host IP: " + ClientState.getHostIp());
readyButton.setDisable(false);
}
else {
lobbyIpText.setText("Connected to IP: ");
readyButton.setDisable(true);
}
initialiseListView();
// if (ClientState.isHost()) {
// lobbyIpText.setText("Lobby Host IP: " + ClientState.getHostIp());
// readyButton.setDisable(false);
// }
// else {
// lobbyIpText.setText("Connected to IP: ");
// readyButton.setDisable(true);
// readyButton.setVisible(false);
// }
// initialiseListView();
// initialiseLobbyControllerThread();
initialiseImageView(); // parrot gif init
// initialiseImageView(); // parrot gif init
// set up client state query thread, so that when it receives the race-started packet
// it can switch to the race view
ClientStateQueryingRunnable clientStateQueryingRunnable = new ClientStateQueryingRunnable();
clientStateQueryingRunnable.addObserver(this);
Thread clientStateQueryingThread = new Thread(clientStateQueryingRunnable, "Client State querying thread");
clientStateQueryingThread.setDaemon(true);
clientStateQueryingThread.start();
// ClientStateQueryingRunnable clientStateQueryingRunnable = new ClientStateQueryingRunnable();
// clientStateQueryingRunnable.addObserver(this);
// Thread clientStateQueryingThread = new Thread(clientStateQueryingRunnable, "Client State querying thread");
// clientStateQueryingThread.setDaemon(true);
// clientStateQueryingThread.start();
}
@Override
public void update(Observable o, Object arg) {
Platform.runLater(new Runnable() {
@Override
public void run() {
if (arg.equals("game started") && !switchedPane) {
switchToRaceView();
}
if (arg.equals(("update players"))) {
initialiseListView();
}
}
});
}
// @Override
// public void update(Observable o, Object arg) {
// Platform.runLater(new Runnable() {
// @Override
// public void run() {
// if (arg.equals("game started") && !switchedPane) {
// switchToRaceView();
// }
// if (arg.equals(("update players"))) {
// initialiseListView();
// }
// }
// });
// }
private void initialiseListView() {
firstListView.getItems().clear();
secondListView.getItems().clear();
thirdListView.getItems().clear();
fourthListView.getItems().clear();
fifthListView.getItems().clear();
sixthListView.getItems().clear();
seventhListView.getItems().clear();
eighthListView.getItems().clear();
// firstListView.getItems().clear();
// secondListView.getItems().clear();
// thirdListView.getItems().clear();
// fourthListView.getItems().clear();
// fifthListView.getItems().clear();
// sixthListView.getItems().clear();
// seventhListView.getItems().clear();
// eighthListView.getItems().clear();
competitors = new ArrayList<>();
Collections.addAll(competitors, firstCompetitor, secondCompetitor, thirdCompetitor,
@@ -159,19 +169,19 @@ public class LobbyController implements Initializable, Observer{
ol.removeAll();
}
firstCompetitor.add(ClientState.getClientSourceId());
// firstCompetitor.add(ClientState.getClientSourceId());
int competitorIndex = 1;
for (Integer yachtId : ClientState.getBoats().keySet()) {
// break if there are more than 7 competitors
if (competitorIndex >= 8) {
break;
}
if (!yachtId.equals(Integer.parseInt(ClientState.getClientSourceId()))) {
competitors.get(competitorIndex).add(String.valueOf(yachtId));
competitorIndex++;
}
}
// int competitorIndex = 1;
// for (Integer yachtId : ClientState.getBoats().keySet()) {
// // break if there are more than 7 competitors
// if (competitorIndex >= 8) {
// break;
// }
// if (!yachtId.equals(Integer.parseInt(ClientState.getClientSourceId()))) {
// competitors.get(competitorIndex).add(String.valueOf(yachtId));
// competitorIndex++;
// }
// }
firstListView.setItems(firstCompetitor);
secondListView.setItems(secondCompetitor);
@@ -183,20 +193,20 @@ public class LobbyController implements Initializable, Observer{
eighthListView.setItems(eighthCompetitor);
}
private void initialiseLobbyControllerThread() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Platform.runLater(new Runnable() {
@Override
public void run() {
}
});
}
});
thread.start();
}
// private void initialiseLobbyControllerThread() {
// Thread thread = new Thread(new Runnable() {
// @Override
// public void run() {
// Platform.runLater(new Runnable() {
// @Override
// public void run() {
//
// }
// });
// }
// });
// thread.start();
// }
private void initialiseImageView() {
Image image1 = new Image(getClass().getResourceAsStream("/ParrotGif/alistair.gif"));
@@ -223,7 +233,7 @@ public class LobbyController implements Initializable, Observer{
setContentPane("/views/StartScreenView.fxml");
GameState.setCurrentStage(GameStages.CANCELLED);
// TODO: 20/07/17 wmu16 - Implement some way of terminating the game
ClientState.setConnectedToHost(false);
// ClientState.setConnectedToHost(false);
}
@FXML
@@ -231,7 +241,8 @@ public class LobbyController implements Initializable, Observer{
// setContentPane("/views/RaceView.fxml");
playTheme();
GameState.setCurrentStage(GameStages.RACING);
mainServerThread.startGame();
for (LobbyCloseListener readyListener : lobbyListeners)
readyListener.notify(CloseStatus.READY);
}
@@ -253,14 +264,27 @@ public class LobbyController implements Initializable, Observer{
}
}
private void switchToRaceView() {
if (!switchedPane) {
switchedPane = true;
setContentPane("/views/RaceView.fxml");
}
// private void switchToRaceView() {
// if (!switchedPane) {
// switchedPane = true;
// setContentPane("/views/RaceView.fxml");
// }
// }
// TODO: 26/07/17 cir27 - Could probably be done in a cleaner way.
public void setTitle (String title) {
lobbyIpText.setText(title);
}
public void setMainServerThread(MainServerThread mainServerThread) {
this.mainServerThread = mainServerThread;
public void addCloseListener(LobbyCloseListener listener) {
lobbyListeners.add(listener);
}
public void setPlayerListSource (ObservableList<String> players) {
if (competitorsListView != null)
competitorsListView.setItems(players);
if (firstListView != null) {
firstListView.setItems(players);
firstImageView.setVisible(false);
}
}
}
@@ -1,12 +1,10 @@
package seng302.visualiser.controllers;
import com.sun.javafx.collections.SortableList;
import java.util.concurrent.TimeUnit;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Point2D;
@@ -33,20 +31,15 @@ import javafx.stage.StageStyle;
import javafx.util.Duration;
import javafx.util.StringConverter;
import seng302.model.Corner;
import seng302.model.stream.parsers.xml.RaceXMLData;
import seng302.utilities.GeoUtility;
import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.visualiser.GameView;
import seng302.visualiser.controllers.annotations.Annotation;
import seng302.visualiser.controllers.annotations.ImportantAnnotationController;
import seng302.visualiser.controllers.annotations.ImportantAnnotationDelegate;
import seng302.visualiser.controllers.annotations.ImportantAnnotationsState;
import seng302.visualiser.fxObjects.BoatObject;
import seng302.visualiser.fxObjects.MarkObject;
import seng302.model.*;
import seng302.model.mark.GateMark;
import seng302.model.mark.Mark;
import seng302.model.mark.SingleMark;
import seng302.model.stream.parsers.StreamParser;
import java.io.IOException;
import java.util.*;
@@ -75,10 +68,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
@FXML
private Button selectAnnotationBtn;
@FXML
private ComboBox<Boat> boatSelectionComboBox;
private ComboBox<Yacht> yachtSelectionComboBox;
//Race Data
private Map<Integer, Boat> participants;
private Map<Integer, Yacht> participants;
private Map<Integer, Mark> markers;
private RaceXMLData courseData;
private GameView gameView;
@@ -102,7 +95,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
}
public void loadRace (Map<Integer, Boat> participants, RaceXMLData raceData, RaceState raceState) {
public void loadRace (Map<Integer, Yacht> participants, RaceXMLData raceData, RaceState raceState) {
this.participants = participants;
this.courseData = raceData;
this.markers = raceData.getCompoundMarks();
@@ -205,30 +198,30 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/**
* Used to add any new boats into the race that may have started late or not have had data received yet
* Used to add any new yachts into the race that may have started late or not have had data received yet
*/
private void updateSparkLine(){
// Collect the racing boats that aren't already in the chart
List<Boat> sparkLineCandidates = new ArrayList<>();
// participants.forEach((id, boat) ->{
// if (!sparkLineData.containsKey(id) && boat.getPosition() != null && !boat.getPosition().equals("-"))
// sparkLineCandidates.add(boat);
// Collect the racing yachts that aren't already in the chart
List<Yacht> sparkLineCandidates = new ArrayList<>();
// participants.forEach((id, yacht) ->{
// if (!sparkLineData.containsKey(id) && yacht.getPosition() != null && !yacht.getPosition().equals("-"))
// sparkLineCandidates.add(yacht);
// });
participants.forEach((id, boat) -> sparkLineCandidates.add(boat));
participants.forEach((id, yacht) -> sparkLineCandidates.add(yacht));
sparklineYAxis.setUpperBound(participants.size() + 1);
// Create a new data series for new boats
sparkLineCandidates.stream().filter(yacht -> yacht.getPosition() != null).forEach(yacht -> {
// Create a new data series for new yachts
sparkLineCandidates.stream().filter(yacht -> yacht.getPositionInteger() != null).forEach(yacht -> {
Series<String, Double> yachtData = new Series<>();
yachtData.setName(yacht.getSourceID().toString());
yachtData.setName(yacht.getSourceId().toString());
yachtData.getData().add(
new XYChart.Data<>(
Integer.toString(yacht.getLegNumber()),
1.0 + participants.size() - yacht.getPosition()
1.0 + participants.size() - yacht.getPositionInteger()
)
);
sparkLineData.put(yacht.getSourceID(), yachtData);
sparkLineData.put(yacht.getSourceId(), yachtData);
});
// Lambda function to sort the series in order of leg (later legs shown more to the right)
@@ -256,36 +249,36 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/**
* Updates the yachts sparkline of the desired boat and using the new leg number
* @param boat The yacht to be updated on the sparkline
* Updates the yachts sparkline of the desired yacht and using the new leg number
* @param yacht The yacht to be updated on the sparkline
* @param legNumber the leg number that the position will be assigned to
*/
public void updateYachtPositionSparkline(Boat boat, Integer legNumber){
public void updateYachtPositionSparkline(Yacht yacht, Integer legNumber){
for (XYChart.Series<String, Double> positionData : sparkLineData.values()) {
positionData.getData().add(
new Data<>(
Integer.toString(legNumber),
1.0 + participants.size() - boat.getPosition()
1.0 + participants.size() - yacht.getPositionInteger()
)
);
}
// XYChart.Series<String, Double> positionData = sparkLineData.get(boat.getSourceID());
// XYChart.Series<String, Double> positionData = sparkLineData.get(yacht.getSourceID());
// positionData.getData().add(
// new XYChart.Data<>(
// Integer.toString(legNumber),
// 1.0 + participants.size() - boat.getPosition()
// 1.0 + participants.size() - yacht.getPosition()
// )
// );
}
/**
* gets the rgb string of the boats colour to use for the chart via css
* @param boatId id of boat passed in to get the boats colour
* gets the rgb string of the yachts colour to use for the chart via css
* @param yachtId id of yacht passed in to get the yachts colour
* @return the colour as an rgb string
*/
private String getBoatColorAsRGB(String boatId){
Color color = participants.get(Integer.valueOf(boatId)).getColour();
private String getBoatColorAsRGB(String yachtId){
Color color = participants.get(Integer.valueOf(yachtId)).getColour();
if (color == null){
return String.format("#%02X%02X%02X",255,255,255);
}
@@ -298,7 +291,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/**
* Initialises a timer which updates elements of the RaceView such as wind direction, boat
* Initialises a timer which updates elements of the RaceView such as wind direction, yacht
* orderings etc.. which are dependent on the info from the stream parser constantly.
* Updates of each of these attributes are called ONCE EACH SECOND
*/
@@ -322,28 +315,28 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/**
* Iterates over all corners until ones SeqID matches with the boats current leg number.
* Iterates over all corners until ones SeqID matches with the yachts current leg number.
* Then it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark
* Returns null if no next mark found.
* @param bg The BoatGroup to find the next mark of
* @return The next Mark or null if none found
*/
private Mark getNextMark(BoatObject bg) {
Integer legNumber = bg.getBoat().getLegNumber();
List<Corner> markSequence = courseData.getMarkSequence();
if (legNumber == 0) {
return null;
} else if (legNumber == markSequence.size() - 1) {
return null;
}
for (Corner corner : markSequence) {
if (legNumber + 2 == corner.getSeqID()) {
return courseData.getCompoundMarks().get(corner.getCompoundMarkID());
}
}
//
// Integer legNumber = bg.getYacht().getLegNumber();
// List<Corner> markSequence = courseData.getMarkSequence();
//
// if (legNumber == 0) {
// return null;
// } else if (legNumber == markSequence.size() - 1) {
// return null;
// }
//
// for (Corner corner : markSequence) {
// if (legNumber + 2 == corner.getSeqID()) {
// return courseData.getCompoundMarks().get(corner.getCompoundMarkID());
// }
// }
return null;
}
@@ -370,7 +363,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
}
/**
* Updates the order of the boats as from the StreamParser and sets them in the boat order
* Updates the order of the yachts as from the StreamParser and sets them in the yacht order
* section
*/
private void updateOrder() {
@@ -378,28 +371,28 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
positionVbox.getChildren().removeAll();
positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
// list of racing boat id
List<Boat> sorted = new ArrayList<>(participants.values());
sorted.sort(Comparator.comparingInt(Boat::getPosition));
// list of racing yacht id
List<Yacht> sorted = new ArrayList<>(participants.values());
sorted.sort(Comparator.comparingInt(Yacht::getPositionInteger));
for (Boat boat : sorted) {
if (boat.getBoatStatus() == 3) { // 3 is finish status
Text textToAdd = new Text(boat.getPosition() + ". " +
boat.getShortName() + " (Finished)");
for (Yacht yacht : sorted) {
if (yacht.getBoatStatus() == 3) { // 3 is finish status
Text textToAdd = new Text(yacht.getPositionInteger() + ". " +
yacht.getShortName() + " (Finished)");
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
positionVbox.getChildren().add(textToAdd);
} else {
Text textToAdd = new Text(boat.getPosition() + ". " +
boat.getShortName() + " ");
Text textToAdd = new Text(yacht.getPositionInteger() + ". " +
yacht.getShortName() + " ");
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
textToAdd.setStyle("");
positionVbox.getChildren().add(textToAdd);
}
}
// participants.forEach((id, boat) ->{
// Text textToAdd = new Text(boat.getPosition() + ". " +
// boat.getShortName() + " ");
// participants.forEach((id, yacht) ->{
// Text textToAdd = new Text(yacht.getPosition() + ". " +
// yacht.getShortName() + " ");
// textToAdd.setFill(Paint.valueOf("#d3d3d3"));
// textToAdd.setStyle("");
// positionVbox.getChildren().add(textToAdd);
@@ -442,9 +435,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
// Double resultingAngle = angleAndSpeed.keySet().iterator().next();
//
//
// Point2D boatCurrentPos = new Point2D(bg.getBoatLayoutX(), bg.getBoatLayoutY());
// Point2D yachtCurrentPos = new Point2D(bg.getBoatLayoutX(), bg.getBoatLayoutY());
// Point2D gateMidPoint = markPoint1.midpoint(markPoint2);
// Integer lineFuncResult = GeoUtility.lineFunction(boatCurrentPos, gateMidPoint, markPoint2);
// Integer lineFuncResult = GeoUtility.lineFunction(yachtCurrentPos, gateMidPoint, markPoint2);
// Line rightLayline = new Line();
// Line leftLayline = new Line();
// if (lineFuncResult == 1) {
@@ -498,15 +491,15 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/**
* Initialised the combo box with any boats currently in the race and adds the required listener
* Initialised the combo box with any yachts currently in the race and adds the required listener
* for the combobox to take action upon selection
*/
private void initialiseBoatSelectionComboBox() {
boatSelectionComboBox.setItems(
yachtSelectionComboBox.setItems(
FXCollections.observableArrayList(participants.values())
);
//Null check is if the listener is fired but nothing selected
boatSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> {
yachtSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> {
if (selectedBoat != null) {
gameView.selectBoat(selectedBoat);
}
@@ -514,18 +507,18 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
}
/**
* Grabs the boats currently in the race as from the StreamParser and sets them to be selectable
* in the boat selection combo box
* Grabs the yachts currently in the race as from the StreamParser and sets them to be selectable
* in the yacht selection combo box
*/
private void updateBoatSelectionComboBox() {
ObservableList<Boat> observableBoats = FXCollections.observableArrayList();
observableBoats.addAll(participants.values());
boatSelectionComboBox.setItems(observableBoats);
ObservableList<Yacht> observableYachts = FXCollections.observableArrayList();
observableYachts.addAll(participants.values());
yachtSelectionComboBox.setItems(observableYachts);
}
/**
* Display the list of boats in the order they finished the race
* Display the list of yachts in the order they finished the race
*/
private void loadRaceResultView() {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/FinishView.fxml"));
@@ -580,18 +573,18 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
/**
* Sets all the annotations of the selected boat to be visible and all others to be hidden
* Sets all the annotations of the selected yacht to be visible and all others to be hidden
*
* @param boat The yacht for which we want to view all annotations
* @param yacht The yacht for which we want to view all annotations
*/
private void setSelectedBoat(Boat boat) {
private void setSelectedBoat(Yacht yacht) {
// for (BoatObject bg : gameViewController.getBoatGroups()) {
// //We need to iterate over all race groups to get the matching boat group belonging to this boat if we
// //are to toggle its annotations, there is no other backwards knowledge of a yacht to its boatgroup.
// if (bg.getBoat().getHullID().equals(boat.getHullID())) {
// //We need to iterate over all race groups to get the matching yacht group belonging to this yacht if we
// //are to toggle its annotations, there is no other backwards knowledge of a yacht to its yachtgroup.
// if (bg.getBoat().getHullID().equals(yacht.getHullID())) {
//// updateLaylines(bg);
// bg.setIsSelected(true);
//// selectedBoat = boat;
//// selectedBoat = yacht;
// } else {
// bg.setIsSelected(false);
// }
@@ -2,9 +2,12 @@ package seng302.visualiser.controllers;
import java.net.Inet4Address;
import java.net.NetworkInterface;
import java.net.URL;
import java.util.Enumeration;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.TextField;
@@ -12,20 +15,18 @@ import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import seng302.client.ClientState;
import seng302.client.ClientToServerThread;
import seng302.visualiser.ClientToServerThread;
import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import seng302.visualiser.GameClient;
/**
* A Class describing the actions of the start screen controller
* Created by wmu16 on 10/07/17.
*/
public class StartScreenController {
public class StartScreenController implements Initializable {
@FXML
private TextField ipTextField;
@@ -33,27 +34,35 @@ public class StartScreenController {
private TextField portTextField;
@FXML
private GridPane startScreen2;
@FXML
private AnchorPane holder;
/**
* Loads the fxml content into the parent pane
* @param jfxUrl
* @return the controller of the fxml
*/
private Object setContentPane(String jfxUrl) {
try {
AnchorPane contentPane = (AnchorPane) startScreen2.getParent();
contentPane.getChildren().removeAll();
contentPane.getChildren().clear();
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(jfxUrl));
contentPane.getChildren().addAll((Pane) fxmlLoader.load());
GameClient gameClient;
return fxmlLoader.getController();
} catch (IOException e) {
e.printStackTrace();
}
return null;
public void initialize(URL url, ResourceBundle resourceBundle) {
// gameClient = new GameClient(holder);
}
//
// /**
// * Loads the fxml content into the parent pane
// * @param jfxUrl
// * @return the controller of the fxml
// */
// private Object setContentPane(String jfxUrl) {
// try {
// AnchorPane contentPane = (AnchorPane) startScreen2.getParent();
// contentPane.getChildren().removeAll();
// contentPane.getChildren().clear();
// contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
// FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(jfxUrl));
// contentPane.getChildren().addAll((Pane) fxmlLoader.load());
//
// return fxmlLoader.getController();
// } catch (IOException e) {
// e.printStackTrace();
// }
// return null;
// }
/**
@@ -65,33 +74,33 @@ public class StartScreenController {
*/
@FXML
public void hostButtonPressed() {
try {
String ipAddress = InetAddress.getLocalHost().getHostAddress();
new GameState(ipAddress);
new MainServerThread().start();
ClientToServerThread clientToServerThread = new ClientToServerThread("localhost", 4950);
// controller.setClientToServerThread(clientToServerThread);
clientToServerThread.start();
// get the lobby controller so that we can pass the game server thread to it
new GameState(getLocalHostIp());
MainServerThread mainServerThread = new MainServerThread();
ClientState.setHost(true);
// host will connect and handshake to itself after setting up the server
// TODO: 24/07/17 wmu16 - Make port number some static global type constant?
ClientToServerThread clientToServerThread = new ClientToServerThread(ClientState.getHostIp(), 4942);
ClientState.setConnectedToHost(true);
controller.setClientToServerThread(clientToServerThread);
LobbyController lobbyController = (LobbyController) setContentPane("/views/LobbyView.fxml");
lobbyController.setMainServerThread(mainServerThread);
} catch (Exception e) {
Alert alert = new Alert(AlertType.ERROR);
alert.setHeaderText("Cannot host");
alert.setContentText("Oops, failed to host, try to restart.");
alert.showAndWait();
e.printStackTrace();
}
new GameState(getLocalHostIp());
gameClient = new GameClient(holder);
gameClient.runAsHost(getLocalHostIp(), 4942);
// try {
//// String ipAddress = InetAddress.getLocalHost().getHostAddress();
//// new GameState(ipAddress);
//// new MainServerThread();
//// ClientToServerThread clientToServerThread = new ClientToServerThread("localhost", 4950);
//// controller.setClientToServerThread(clientToServerThread);
// // get the lobby controller so that we can pass the game server thread to it
// new GameState(getLocalHostIp());
// MainServerThread mainServerThread = new MainServerThread();
//// ClientState.setHost(true);
// // host will connect and handshake to itself after setting up the server
// // TODO: 24/07/17 wmu16 - Make port number some static global type constant?
//// ClientToServerThread clientToServerThread = new ClientToServerThread(ClientState.getHostIp(), 4942);
//// ClientState.setConnectedToHost(true);
//// controller.setClientToServerThread(clientToServerThread);
// LobbyController lobbyController = (LobbyController) setContentPane("/views/LobbyView.fxml");
// lobbyController.setMainServerThread(mainServerThread);
// } catch (Exception e) {
// Alert alert = new Alert(AlertType.ERROR);
// alert.setHeaderText("Cannot host");
// alert.setContentText("Oops, failed to host, try to restart.");
// alert.showAndWait();
// e.printStackTrace();
// }
}
/**
@@ -103,27 +112,30 @@ public class StartScreenController {
@FXML
public void connectButtonPressed() {
// TODO: 10/07/17 wmu16 - Finish function
try {
String ipAddress = ipTextField.getText().trim().toLowerCase();
Integer port = Integer.valueOf(portTextField.getText().trim());
gameClient = new GameClient(holder);
gameClient.runAsClient(ipTextField.getText().trim().toLowerCase(), 4942);
ClientToServerThread clientToServerThread = new ClientToServerThread(ipAddress, port);
ClientState.setHost(false);
ClientState.setConnectedToHost(true);
controller.setClientToServerThread(clientToServerThread);
setContentPane("/views/LobbyView.fxml");
} catch (Exception e) {
Alert alert = new Alert(AlertType.ERROR);
alert.setHeaderText("Cannot reach the host");
alert.setContentText("Please check your host IP address.");
alert.showAndWait();
}
// try {
// String ipAddress = ipTextField.getText().trim().toLowerCase();
// Integer port = Integer.valueOf(portTextField.getText().trim());
//
//// ClientToServerThread clientToServerThread = new ClientToServerThread(ipAddress, port);
//// ClientState.setHost(false);
//// ClientState.setConnectedToHost(true);
//
//// controller.setClientToServerThread(clientToServerThread);
//// setContentPane("/views/LobbyView.fxml");
// } catch (Exception e) {
// Alert alert = new Alert(AlertType.ERROR);
// alert.setHeaderText("Cannot reach the host");
// alert.setContentText("Please check your host IP address.");
// alert.showAndWait();
// }
}
public void setController(Controller controller) {
this.controller = controller;
}
// public void setController(Controller controller) {
// this.controller = controller;
// }
/**
* Gets the local host ip address and sets this ip to ClientState.
@@ -158,7 +170,7 @@ public class StartScreenController {
if (ipAddress == null) {
System.out.println("[HOST] Cannot obtain local host ip address.");
}
ClientState.setHostIp(ipAddress);
// ClientState.setHostIp(ipAddress);
return ipAddress;
}
}
@@ -1,215 +0,0 @@
package seng302.visualiser.controllers.client;
import java.io.IOException;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.Pane;
import seng302.model.Boat;
import seng302.model.RaceState;
import seng302.model.mark.Mark;
import seng302.model.stream.parsers.PositionUpdateData.DeviceType;
import seng302.model.stream.parsers.MarkRoundingData;
import seng302.model.stream.parsers.RaceStatusData;
import seng302.model.stream.parsers.xml.RaceXMLData;
import seng302.model.stream.parsers.StreamParser;
import seng302.model.stream.parsers.xml.RegattaXMLData;
import seng302.model.stream.parsers.xml.XMLParser;
import seng302.model.stream.parsers.PositionUpdateData;
import seng302.model.stream.packets.StreamPacket;
import seng302.visualiser.ClientToServerThread;
import seng302.visualiser.controllers.RaceViewController;
/**
* Created by cir27 on 20/07/17.
*/
public class ClientController {
private Pane holderPane;
private ClientToServerThread socketThread;
private RaceViewController raceView;
private Map<Integer, Boat> allBoatsMap;
private Map<Integer, Boat> racingBoats = new HashMap<>();
private RegattaXMLData regattaData;
private RaceXMLData courseData;
private RaceState raceState = new RaceState();
public ClientController (String ipAddress, Pane holder) {
this.holderPane = holder;
socketThread = new ClientToServerThread(ipAddress, 4950);
socketThread.start();
socketThread.addStreamObserver(this::parsePacket);
}
private void loadRaceView () {
allBoatsMap.forEach((id, boat) -> {
if (courseData.getParticipants().contains(id))
racingBoats.put(id, boat);
});
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("RaceView.fxml"));
raceView = fxmlLoader.getController();
try {
holderPane.getChildren().add(fxmlLoader.load());
} catch (IOException e) {
e.printStackTrace();
}
raceView.loadRace(racingBoats, courseData, raceState);
}
private void parsePacket(StreamPacket packet) {
switch (packet.getType()) {
case RACE_STATUS:
processRaceStatusUpdate(StreamParser.extractRaceStatus(packet));
break;
case REGATTA_XML:
regattaData = XMLParser.parseRegatta(
StreamParser.extractXmlMessage(packet)
);
raceState.setTimeZone(
TimeZone.getTimeZone(
ZoneId.ofOffset("UTC", ZoneOffset.ofHours(regattaData.getUtcOffset()))
)
);
startRaceIfAllDataReceived();
break;
case RACE_XML:
courseData = XMLParser.parseRace(
StreamParser.extractXmlMessage(packet)
);
if (raceView != null) {
raceView.updateRaceData(courseData);
}
startRaceIfAllDataReceived();
break;
case BOAT_XML:
allBoatsMap = XMLParser.parseBoats(
StreamParser.extractXmlMessage(packet)
);
startRaceIfAllDataReceived();
break;
case RACE_START_STATUS:
raceState.updateState(StreamParser.extractRaceStartStatus(packet));
break;
case BOAT_LOCATION:
updatePosition(StreamParser.extractBoatLocation(packet));
break;
case MARK_ROUNDING:
updateMarkRounding(StreamParser.extractMarkRounding(packet));
break;
}
}
private void startRaceIfAllDataReceived() {
if (courseData != null && allBoatsMap != null && regattaData != null)
loadRaceView();
}
/**
* Updates the position of a boat. Boat and position are given in the provided data.
* @param positionData
*/
private void updatePosition(PositionUpdateData positionData) {
if (positionData.getType() == DeviceType.YACHT_TYPE) {
Boat boat = racingBoats.get(positionData.getDeviceId());
boat.setVelocity(positionData.getGroundSpeed());
boat.setLat(positionData.getLat());
boat.setLon(positionData.getLon());
boat.setHeading(positionData.getHeading());
} else if (positionData.getType() == DeviceType.MARK_TYPE) {
Mark mark = courseData.getCompoundMarks().get(positionData.getDeviceId());
}
}
/**
* Updates the boat as having passed the mark. Boat and mark are given by the ids in the
* provided data.
* @param roundingData Contains data for the rounding of a mark.
*/
private void updateMarkRounding(MarkRoundingData roundingData) {
Boat boat = racingBoats.get(roundingData.getBoatId());
boat.setMarkRoundingTime(roundingData.getTimeStamp());
boat.setTimeSinceLastMark(raceState.getRaceTime() - roundingData.getTimeStamp());
boat.setLastMarkRounded(
courseData.getCompoundMarks().get(
roundingData.getMarkId()
)
);
}
private void processRaceStatusUpdate (RaceStatusData data) {
raceState.updateState(data);
for (long[] boatData : data.getBoatData()) {
Boat boat = allBoatsMap.get((int) boatData[0]);
boat.setEstimateTimeTillNextMark(raceState.getRaceTime() - boatData[1]);
boat.setEstimateTimeAtFinish(boatData[2]);
int legNumber = (int) boatData[3];
boat.setLegNumber(legNumber);
if (legNumber != boat.getLegNumber()) {
int placing = 1;
for (Boat otherBoat : allBoatsMap.values()) {
if (otherBoat.getSourceID() != boatData[0] &&
boat.getLegNumber() <= otherBoat.getLegNumber())
placing++;
}
boat.setPosition(placing);
}
}
}
private void close () {
socketThread.closeSocket();
}
// /** Handle the key-pressed event from the text field. */
// public void keyPressed(KeyEvent e) {
// BoatActionMessage boatActionMessage;
// switch (e.getCode()){
// case SPACE: // align with vmg
// boatActionMessage = new BoatActionMessage(BoatActionType.VMG);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case PAGE_UP: // upwind
// boatActionMessage = new BoatActionMessage(BoatActionType.UPWIND);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case PAGE_DOWN: // downwind
// boatActionMessage = new BoatActionMessage(BoatActionType.DOWNWIND);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// case ENTER: // tack/gybe
// boatActionMessage = new BoatActionMessage(BoatActionType.TACK_GYBE);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// //TODO Allow a zoom in and zoom out methods
// case Z: // zoom in
// System.out.println("Zoom in");
// break;
// case X: // zoom out
// System.out.println("Zoom out");
// break;
// }
// }
// public void keyReleased(KeyEvent e) {
// switch (e.getCode()) {
// //TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
// case SHIFT: // sails in/sails out
// BoatActionMessage boatActionMessage = new BoatActionMessage(BoatActionType.SAILS_IN);
// clientToServerThread.sendBoatActionMessage(boatActionMessage);
// break;
// }
// }
//
// onKeyPressed="#keyPressed" onKeyReleased="#keyReleased"
}
@@ -1,13 +0,0 @@
package seng302.visualiser.controllers.host;
import javafx.scene.layout.Pane;
/**
* Created by cir27 on 20/07/17.
*/
public class HostController {
Pane mainHolder;
public HostController (Pane holder) {
this.mainHolder = holder;
}
}
@@ -10,12 +10,7 @@ import javafx.scene.paint.Paint;
import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon;
import javafx.scene.transform.Rotate;
import seng302.model.Boat;
import seng302.utilities.GeoUtility;
import seng302.model.mark.GateMark;
import seng302.model.mark.Mark;
import seng302.model.mark.SingleMark;
import seng302.model.stream.parsers.StreamParser;
import seng302.model.Yacht;
/**
* BoatGroup is a javafx group that by default contains a graphical objects for representing a 2
@@ -38,7 +33,6 @@ public class BoatObject extends Group {
private Double lastRotation = 0.0;
private long framesToMove;
//Graphical objects
private Boat boat;
private Group lineGroup = new Group();
private Polygon boatPoly;
private Wake wake;
@@ -161,7 +155,7 @@ public class BoatObject extends Group {
boatPoly.getLayoutY()
);
l.getStrokeDashArray().setAll(3d, 7d);
l.setStroke(boat.getColour());
l.setStroke(colour);
l.setCache(true);
l.setCacheHint(CacheHint.SPEED);
lineGroup.getChildren().add(l);
@@ -199,7 +193,7 @@ public class BoatObject extends Group {
rotateTo(rotation);
wake.setRotation(rotation, groundSpeed);
boat.setVelocity(groundSpeed);
// yacht.setVelocity(groundSpeed);
lastTimeValid = timeValid;
isStopped = false;
lastRotation = rotation;
@@ -278,19 +272,6 @@ public class BoatObject extends Group {
return laylines;
}
public Boat getBoat() {
return boat;
}
/**
* Returns all raceIds associated with this group. For BoatGroups the ID's are for the boat.
*
* @return An array containing all ID's associated with this RaceObject.
*/
public long getRaceId() {
return boat.getSourceID();
}
public Group getWake () {
return wake;
}
@@ -316,9 +297,4 @@ public class BoatObject extends Group {
return isStopped;
}
@Override
public String toString() {
return boat.toString();
}
}
-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="mainPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.GameViewController" />
+1 -9
View File
@@ -1,17 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<GridPane fx:id="lobbyScreen" nodeOrientation="LEFT_TO_RIGHT" prefHeight="533.0" prefWidth="802.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.LobbyController">
<columnConstraints>
+2 -9
View File
@@ -2,19 +2,12 @@
<?import javafx.scene.image.*?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<GridPane fx:id="lobbyScreen" nodeOrientation="LEFT_TO_RIGHT" prefHeight="960.0" prefWidth="1530.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.controllers.LobbyController">
<GridPane fx:id="lobbyScreen" nodeOrientation="LEFT_TO_RIGHT" prefHeight="960.0" prefWidth="1530.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.LobbyController">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
</columnConstraints>
+2 -3
View File
@@ -67,8 +67,7 @@
</children>
</AnchorPane>
<AnchorPane fx:id="contentAnchorPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" style="-fx-background-color: skyblue;" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2147483647" GridPane.valignment="TOP">
<children>
<fx:include fx:id="gameView" source="GameView.fxml" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children></AnchorPane>
</AnchorPane>
</children>
</GridPane>
+52 -52
View File
@@ -1,59 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<GridPane fx:id="startScreen2" nodeOrientation="LEFT_TO_RIGHT" prefWidth="800.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.StartScreenController">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="442.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="358.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints vgrow="SOMETIMES" />
<RowConstraints minHeight="72.0" prefHeight="72.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="65.0" minHeight="36.0" prefHeight="46.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="108.0" minHeight="72.0" prefHeight="98.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="72.0" prefHeight="72.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label alignment="CENTER" text="Welcome to Race Vision" textFill="WHITE" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
<font>
<Font size="40.0" />
</font>
</Label>
<Button mnemonicParsing="false" onAction="#hostButtonPressed" prefHeight="25.0" prefWidth="175.0" text="Host" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2" />
<Button mnemonicParsing="false" onAction="#connectButtonPressed" prefHeight="25.0" prefWidth="147.0" text="Connect" GridPane.columnIndex="1" GridPane.rowIndex="4">
<GridPane.margin>
<Insets left="5.0" right="5.0" />
</GridPane.margin></Button>
<TextField fx:id="ipTextField" alignment="CENTER" maxWidth="-Infinity" prefHeight="25.0" prefWidth="148.0" promptText="Host IP" text="132.181.14." GridPane.halignment="RIGHT" GridPane.rowIndex="4">
<GridPane.margin>
<Insets bottom="10.0" left="5.0" right="85.0" top="10.0" />
</GridPane.margin>
</TextField>
<Text fill="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="OR" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="3">
<font>
<Font size="21.0" />
</font>
</Text>
<TextField fx:id="portTextField" alignment="CENTER" maxWidth="75.0" prefHeight="25.0" prefWidth="55.0" promptText="Port" text="4942" GridPane.halignment="RIGHT" GridPane.rowIndex="4">
<opaqueInsets>
<Insets />
</opaqueInsets>
<GridPane.margin>
<Insets left="5.0" right="5.0" />
</GridPane.margin>
</TextField>
</children>
</GridPane>
<AnchorPane fx:id="holder" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="960.0" prefWidth="1530.0" style="-fx-background-color: #2C2c36;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.StartScreenController">
<children>
<GridPane fx:id="startScreen2" layoutX="365.0" layoutY="285.0" nodeOrientation="LEFT_TO_RIGHT" prefWidth="800.0" style="-fx-background-color: #2C2c36;">
<children>
<Label alignment="CENTER" text="Welcome to Race Vision" textFill="WHITE" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
<font>
<Font size="40.0" />
</font>
</Label>
<Button mnemonicParsing="false" onAction="#hostButtonPressed" prefHeight="25.0" prefWidth="175.0" text="Host" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2" />
<Button mnemonicParsing="false" onAction="#connectButtonPressed" prefHeight="25.0" prefWidth="147.0" text="Connect" GridPane.columnIndex="1" GridPane.rowIndex="4">
<GridPane.margin>
<Insets left="5.0" right="5.0" />
</GridPane.margin>
</Button>
<TextField fx:id="ipTextField" alignment="CENTER" maxWidth="-Infinity" prefHeight="25.0" prefWidth="148.0" promptText="Host IP" text="132.181.14." GridPane.halignment="RIGHT" GridPane.rowIndex="4">
<GridPane.margin>
<Insets bottom="10.0" left="5.0" right="85.0" top="10.0" />
</GridPane.margin>
</TextField>
<Text fill="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="OR" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="3">
<font>
<Font size="21.0" />
</font>
</Text>
<TextField fx:id="portTextField" alignment="CENTER" maxWidth="75.0" prefHeight="25.0" prefWidth="55.0" promptText="Port" text="4942" GridPane.halignment="RIGHT" GridPane.rowIndex="4">
<opaqueInsets>
<Insets />
</opaqueInsets>
<GridPane.margin>
<Insets left="5.0" right="5.0" />
</GridPane.margin>
</TextField>
</children>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="442.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="358.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints />
<RowConstraints />
<RowConstraints maxHeight="42.0" minHeight="10.0" prefHeight="42.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="26.0" minHeight="13.0" prefHeight="26.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="66.0" minHeight="59.0" prefHeight="59.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="65.0" minHeight="36.0" prefHeight="46.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="108.0" minHeight="72.0" prefHeight="98.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="72.0" prefHeight="72.0" vgrow="SOMETIMES" />
</rowConstraints>
</GridPane>
</children>
</AnchorPane>