mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 06:18:44 +00:00
Merge branch '1047_Hosting_Game' into Story62_Reading_Keystrokes
# Conflicts: # src/main/resources/views/RaceView.fxml
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
engines:
|
||||||
|
pmd:
|
||||||
|
enabled: true
|
||||||
|
channel: "beta"
|
||||||
|
|
||||||
|
fixme:
|
||||||
|
enabled: true
|
||||||
|
config:
|
||||||
|
strings:
|
||||||
|
- FIXME
|
||||||
|
- TODO
|
||||||
|
- BUG
|
||||||
|
- FIX
|
||||||
|
|
||||||
|
ratings:
|
||||||
|
paths:
|
||||||
|
- "**.java"
|
||||||
@@ -14,7 +14,7 @@ public class App extends Application {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage primaryStage) throws Exception {
|
public void start(Stage primaryStage) throws Exception {
|
||||||
PolarTable.parsePolarFile(getClass().getResource("/config/acc_polars.csv").getFile());
|
PolarTable.parsePolarFile(getClass().getResourceAsStream("/config/acc_polars.csv"));
|
||||||
|
|
||||||
Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml"));
|
Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml"));
|
||||||
primaryStage.setTitle("RaceVision");
|
primaryStage.setTitle("RaceVision");
|
||||||
@@ -29,51 +29,13 @@ public class App extends Application {
|
|||||||
StreamReceiver.noMoreBytes();
|
StreamReceiver.noMoreBytes();
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
StreamReceiver sr = null;
|
|
||||||
|
|
||||||
new ServerThread("Racevision Test Server");
|
|
||||||
|
|
||||||
try {
|
|
||||||
Thread.sleep(2000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.length == 1 && args[0].equals("-standalone")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.length == 3 && args[0].equals("-server")) {
|
|
||||||
|
|
||||||
sr = new StreamReceiver(args[1], Integer.valueOf(args[2]), "RaceStream");
|
|
||||||
|
|
||||||
} else if (args.length == 2 && args[0].equals("-server")) {
|
|
||||||
switch (args[1]) {
|
|
||||||
case "internal":
|
|
||||||
sr = new StreamReceiver("localhost", 4949, "RaceStream");
|
|
||||||
break;
|
|
||||||
case "staffserver":
|
|
||||||
sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream");
|
|
||||||
break;
|
|
||||||
case "official":
|
|
||||||
sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Change the StreamReceiver in this else block to change the default data source.
|
|
||||||
else{
|
|
||||||
sr = new StreamReceiver("localhost", 4949, "RaceStream");
|
|
||||||
}
|
|
||||||
|
|
||||||
sr.start();
|
|
||||||
StreamParser streamParser = new StreamParser("StreamParser");
|
|
||||||
streamParser.start();
|
|
||||||
|
|
||||||
launch(args);
|
launch(args);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
package seng302.controllers;
|
package seng302.controllers;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
import javafx.animation.AnimationTimer;
|
import javafx.animation.AnimationTimer;
|
||||||
import javafx.beans.property.SimpleDoubleProperty;
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
import javafx.scene.canvas.Canvas;
|
import javafx.scene.canvas.Canvas;
|
||||||
@@ -10,6 +17,8 @@ import javafx.scene.canvas.GraphicsContext;
|
|||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.shape.Polygon;
|
import javafx.scene.shape.Polygon;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
@@ -90,7 +99,7 @@ public class CanvasController {
|
|||||||
VERTICAL
|
VERTICAL
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setup(RaceViewController raceViewController){
|
public void setup(RaceViewController raceViewController) {
|
||||||
this.raceViewController = raceViewController;
|
this.raceViewController = raceViewController;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +122,7 @@ public class CanvasController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initializeCanvas (){
|
public void initializeCanvas() {
|
||||||
|
|
||||||
gc = canvas.getGraphicsContext2D();
|
gc = canvas.getGraphicsContext2D();
|
||||||
gc.setGlobalAlpha(0.5);
|
gc.setGlobalAlpha(0.5);
|
||||||
@@ -163,11 +172,29 @@ public class CanvasController {
|
|||||||
}
|
}
|
||||||
if (StreamParser.isRaceFinished()) {
|
if (StreamParser.isRaceFinished()) {
|
||||||
this.stop();
|
this.stop();
|
||||||
|
switchToFinishScreen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void switchToFinishScreen() {
|
||||||
|
try {
|
||||||
|
// canvas view -> anchor pane -> grid pane -> main view
|
||||||
|
GridPane gridPane = (GridPane) canvasPane.getParent().getParent();
|
||||||
|
AnchorPane contentPane = (AnchorPane) gridPane.getParent();
|
||||||
|
contentPane.getChildren().removeAll();
|
||||||
|
contentPane.getChildren().clear();
|
||||||
|
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
|
contentPane.getChildren().addAll(
|
||||||
|
(Pane) FXMLLoader.load(getClass().getResource("/views/FinishScreenView.fxml")));
|
||||||
|
} catch (javafx.fxml.LoadException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* First find the top right and bottom left points' geo locations, then retrieve
|
* First find the top right and bottom left points' geo locations, then retrieve
|
||||||
* map from google to display on image view. - Haoming 22/5/2017
|
* map from google to display on image view. - Haoming 22/5/2017
|
||||||
@@ -176,19 +203,29 @@ public class CanvasController {
|
|||||||
findMetersPerPixel();
|
findMetersPerPixel();
|
||||||
Point2D topLeftPoint = findScaledXY(maxLatPoint.getLatitude(), minLonPoint.getLongitude());
|
Point2D topLeftPoint = findScaledXY(maxLatPoint.getLatitude(), minLonPoint.getLongitude());
|
||||||
// distance from top left extreme to panel origin (top left corner)
|
// distance from top left extreme to panel origin (top left corner)
|
||||||
double distanceFromTopLeftToOrigin = Math.sqrt(Math.pow(topLeftPoint.getX() * metersPerPixelX, 2) + Math.pow(topLeftPoint.getY() * metersPerPixelY, 2));
|
double distanceFromTopLeftToOrigin = Math.sqrt(
|
||||||
|
Math.pow(topLeftPoint.getX() * metersPerPixelX, 2) + Math
|
||||||
|
.pow(topLeftPoint.getY() * metersPerPixelY, 2));
|
||||||
// angle from top left extreme to panel origin
|
// angle from top left extreme to panel origin
|
||||||
double bearingFromTopLeftToOrigin = Math.toDegrees(Math.atan2(-topLeftPoint.getX(), topLeftPoint.getY()));
|
double bearingFromTopLeftToOrigin = Math
|
||||||
|
.toDegrees(Math.atan2(-topLeftPoint.getX(), topLeftPoint.getY()));
|
||||||
// the top left extreme
|
// the top left extreme
|
||||||
Position topLeftPos = new Position(maxLatPoint.getLatitude(), minLonPoint.getLongitude());
|
Position topLeftPos = new Position(maxLatPoint.getLatitude(), minLonPoint.getLongitude());
|
||||||
Position originPos = GeoUtility.getGeoCoordinate(topLeftPos, bearingFromTopLeftToOrigin, distanceFromTopLeftToOrigin);
|
Position originPos = GeoUtility
|
||||||
|
.getGeoCoordinate(topLeftPos, bearingFromTopLeftToOrigin, distanceFromTopLeftToOrigin);
|
||||||
|
|
||||||
// distance from origin corner to bottom right corner of the panel
|
// distance from origin corner to bottom right corner of the panel
|
||||||
double distanceFromOriginToBottomRight = Math.sqrt(Math.pow(PANEL_HEIGHT* metersPerPixelY, 2) + Math.pow(PANEL_WIDTH * metersPerPixelX, 2));
|
double distanceFromOriginToBottomRight = Math.sqrt(
|
||||||
double bearingFromOriginToBottomRight = Math.toDegrees(Math.atan2(PANEL_WIDTH, -PANEL_HEIGHT));
|
Math.pow(PANEL_HEIGHT * metersPerPixelY, 2) + Math
|
||||||
Position bottomRightPos = GeoUtility.getGeoCoordinate(originPos, bearingFromOriginToBottomRight, distanceFromOriginToBottomRight);
|
.pow(PANEL_WIDTH * metersPerPixelX, 2));
|
||||||
|
double bearingFromOriginToBottomRight = Math
|
||||||
|
.toDegrees(Math.atan2(PANEL_WIDTH, -PANEL_HEIGHT));
|
||||||
|
Position bottomRightPos = GeoUtility
|
||||||
|
.getGeoCoordinate(originPos, bearingFromOriginToBottomRight,
|
||||||
|
distanceFromOriginToBottomRight);
|
||||||
|
|
||||||
Boundary boundary = new Boundary(originPos.getLat(), bottomRightPos.getLng(), bottomRightPos.getLat(), originPos.getLng());
|
Boundary boundary = new Boundary(originPos.getLat(), bottomRightPos.getLng(),
|
||||||
|
bottomRightPos.getLat(), originPos.getLng());
|
||||||
CanvasMap canvasMap = new CanvasMap(boundary);
|
CanvasMap canvasMap = new CanvasMap(boundary);
|
||||||
mapImage.setImage(canvasMap.getMapImage());
|
mapImage.setImage(canvasMap.getMapImage());
|
||||||
}
|
}
|
||||||
@@ -196,9 +233,10 @@ public class CanvasController {
|
|||||||
/**
|
/**
|
||||||
* Adds border marks to the canvas, taken from the XML file
|
* Adds border marks to the canvas, taken from the XML file
|
||||||
*
|
*
|
||||||
* NOTE: This is quite confusing as objects are grabbed from the XMLParser such as Mark and CompoundMark which are
|
* NOTE: This is quite confusing as objects are grabbed from the XMLParser such as Mark and
|
||||||
* named the same as those in the model package but are, however not the same, so they do not have things such as
|
* CompoundMark which are named the same as those in the model package but are, however not the
|
||||||
* a type and must be derived from the number of marks in a compound mark etc..
|
* same, so they do not have things such as a type and must be derived from the number of marks
|
||||||
|
* in a compound mark etc..
|
||||||
*/
|
*/
|
||||||
private void addRaceBorder() {
|
private void addRaceBorder() {
|
||||||
XMLParser.RaceXMLObject raceXMLObject = StreamParser.getXmlObject().getRaceXML();
|
XMLParser.RaceXMLObject raceXMLObject = StreamParser.getXmlObject().getRaceXML();
|
||||||
@@ -215,7 +253,7 @@ public class CanvasController {
|
|||||||
raceBorder.getPoints().setAll(boundaryPoints);
|
raceBorder.getPoints().setAll(boundaryPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateGroups(){
|
private void updateGroups() {
|
||||||
for (BoatGroup boatGroup : boatGroups) {
|
for (BoatGroup boatGroup : boatGroups) {
|
||||||
// some raceObjects will have multiple ID's (for instance gate marks)
|
// some raceObjects will have multiple ID's (for instance gate marks)
|
||||||
//checking if the current "ID" has any updates associated with it
|
//checking if the current "ID" has any updates associated with it
|
||||||
@@ -245,7 +283,7 @@ public class CanvasController {
|
|||||||
private void updateBoatGroup(BoatGroup boatGroup) {
|
private void updateBoatGroup(BoatGroup boatGroup) {
|
||||||
PriorityBlockingQueue<BoatPositionPacket> movementQueue = StreamParser.boatLocations.get(boatGroup.getRaceId());
|
PriorityBlockingQueue<BoatPositionPacket> movementQueue = StreamParser.boatLocations.get(boatGroup.getRaceId());
|
||||||
// giving the movementQueue a 5 packet buffer to account for slightly out of order packets
|
// giving the movementQueue a 5 packet buffer to account for slightly out of order packets
|
||||||
if (movementQueue.size() > 0){
|
if (movementQueue.size() > 0) {
|
||||||
try {
|
try {
|
||||||
BoatPositionPacket positionPacket = movementQueue.take();
|
BoatPositionPacket positionPacket = movementQueue.take();
|
||||||
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
|
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
|
||||||
@@ -267,7 +305,7 @@ public class CanvasController {
|
|||||||
BoatPositionPacket positionPacket = movementQueue.take();
|
BoatPositionPacket positionPacket = movementQueue.take();
|
||||||
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
|
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
|
||||||
markGroup.moveMarkTo(p2d.getX(), p2d.getY(), raceId);
|
markGroup.moveMarkTo(p2d.getX(), p2d.getY(), raceId);
|
||||||
} catch (InterruptedException e){
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,7 +320,8 @@ public class CanvasController {
|
|||||||
Group trails = new Group();
|
Group trails = new Group();
|
||||||
Group annotations = new Group();
|
Group annotations = new Group();
|
||||||
|
|
||||||
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML().getParticipants();
|
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML()
|
||||||
|
.getParticipants();
|
||||||
ArrayList<Integer> participantIDs = new ArrayList<>();
|
ArrayList<Integer> participantIDs = new ArrayList<>();
|
||||||
for (Participant p : participants) {
|
for (Participant p : participants) {
|
||||||
participantIDs.add(p.getsourceID());
|
participantIDs.add(p.getsourceID());
|
||||||
@@ -315,7 +354,8 @@ public class CanvasController {
|
|||||||
} else {
|
} else {
|
||||||
GateMark gMark = (GateMark) mark;
|
GateMark gMark = (GateMark) mark;
|
||||||
|
|
||||||
MarkGroup markGroup = new MarkGroup(gMark, findScaledXY(gMark.getSingleMark1()), findScaledXY(gMark.getSingleMark2())); //should be 2 objects in the list.
|
MarkGroup markGroup = new MarkGroup(gMark, findScaledXY(gMark.getSingleMark1()),
|
||||||
|
findScaledXY(gMark.getSingleMark2())); //should be 2 objects in the list.
|
||||||
markGroups.add(markGroup);
|
markGroups.add(markGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,6 +387,7 @@ public class CanvasController {
|
|||||||
public double prefWidth(double height) {
|
public double prefWidth(double height) {
|
||||||
return getWidth();
|
return getWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double prefHeight(double width) {
|
public double prefHeight(double width) {
|
||||||
return getHeight();
|
return getHeight();
|
||||||
@@ -364,7 +405,8 @@ public class CanvasController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates x and y location for every marker that fits it to the canvas the race will be drawn on.
|
* Calculates x and y location for every marker that fits it to the canvas the race will be
|
||||||
|
* drawn on.
|
||||||
*/
|
*/
|
||||||
private void fitMarksToCanvas() {
|
private void fitMarksToCanvas() {
|
||||||
//Check is called once to avoid unnecessarily change the course limits once the race is running
|
//Check is called once to avoid unnecessarily change the course limits once the race is running
|
||||||
@@ -378,8 +420,9 @@ public class CanvasController {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the marker with the leftmost
|
* Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the marker
|
||||||
* marker, rightmost marker, southern most marker and northern most marker respectively.
|
* with the leftmost marker, rightmost marker, southern most marker and northern most marker
|
||||||
|
* respectively.
|
||||||
*/
|
*/
|
||||||
private void findMinMaxPoint() {
|
private void findMinMaxPoint() {
|
||||||
List<Limit> sortedPoints = new ArrayList<>();
|
List<Limit> sortedPoints = new ArrayList<>();
|
||||||
@@ -404,12 +447,13 @@ public class CanvasController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the location of a reference point, this is always the point with minimum latitude, in relation to the
|
* Calculates the location of a reference point, this is always the point with minimum latitude,
|
||||||
* canvas.
|
* in relation to the canvas.
|
||||||
*
|
*
|
||||||
* @param minLonToMaxLon The horizontal distance between the point of minimum longitude to maximum longitude.
|
* @param minLonToMaxLon The horizontal distance between the point of minimum longitude to
|
||||||
|
* maximum longitude.
|
||||||
*/
|
*/
|
||||||
private void calculateReferencePointLocation (double minLonToMaxLon) {
|
private void calculateReferencePointLocation(double minLonToMaxLon) {
|
||||||
Mark referencePoint = minLatPoint;
|
Mark referencePoint = minLatPoint;
|
||||||
double referenceAngle;
|
double referenceAngle;
|
||||||
|
|
||||||
@@ -438,20 +482,23 @@ public class CanvasController {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the scale factor necessary to fit all race markers within the onscreen map and assigns it to distanceScaleFactor
|
* Finds the scale factor necessary to fit all race markers within the onscreen map and assigns
|
||||||
* Returns the max horizontal distance of the map.
|
* it to distanceScaleFactor Returns the max horizontal distance of the map.
|
||||||
*/
|
*/
|
||||||
private double scaleRaceExtremities () {
|
private double scaleRaceExtremities() {
|
||||||
|
|
||||||
double vertAngle = Math.abs(Mark.calculateHeadingRad(minLatPoint, maxLatPoint));
|
double vertAngle = Math.abs(Mark.calculateHeadingRad(minLatPoint, maxLatPoint));
|
||||||
double vertDistance = Math.cos(vertAngle) * Mark.calculateDistance(minLatPoint, maxLatPoint);
|
double vertDistance =
|
||||||
|
Math.cos(vertAngle) * Mark.calculateDistance(minLatPoint, maxLatPoint);
|
||||||
double horiAngle = Mark.calculateHeadingRad(minLonPoint, maxLonPoint);
|
double horiAngle = Mark.calculateHeadingRad(minLonPoint, maxLonPoint);
|
||||||
|
|
||||||
if (horiAngle <= (Math.PI / 2))
|
if (horiAngle <= (Math.PI / 2)) {
|
||||||
horiAngle = (Math.PI / 2) - horiAngle;
|
horiAngle = (Math.PI / 2) - horiAngle;
|
||||||
else
|
} else {
|
||||||
horiAngle = horiAngle - (Math.PI / 2);
|
horiAngle = horiAngle - (Math.PI / 2);
|
||||||
double horiDistance = Math.cos(horiAngle) * Mark.calculateDistance(minLonPoint, maxLonPoint);
|
}
|
||||||
|
double horiDistance =
|
||||||
|
Math.cos(horiAngle) * Mark.calculateDistance(minLonPoint, maxLonPoint);
|
||||||
|
|
||||||
double vertScale = (CANVAS_HEIGHT - (BUFFER_SIZE + BUFFER_SIZE)) / vertDistance;
|
double vertScale = (CANVAS_HEIGHT - (BUFFER_SIZE + BUFFER_SIZE)) / vertDistance;
|
||||||
|
|
||||||
@@ -465,8 +512,8 @@ public class CanvasController {
|
|||||||
return horiDistance;
|
return horiDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Point2D findScaledXY (Mark unscaled) {
|
private Point2D findScaledXY(Mark unscaled) {
|
||||||
return findScaledXY (unscaled.getLatitude(), unscaled.getLongitude());
|
return findScaledXY(unscaled.getLatitude(), unscaled.getLongitude());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Point2D findScaledXY (double unscaledLat, double unscaledLon) {
|
public Point2D findScaledXY (double unscaledLat, double unscaledLon) {
|
||||||
@@ -475,23 +522,35 @@ public class CanvasController {
|
|||||||
int xAxisLocation = (int) referencePointX;
|
int xAxisLocation = (int) referencePointX;
|
||||||
int yAxisLocation = (int) referencePointY;
|
int yAxisLocation = (int) referencePointY;
|
||||||
|
|
||||||
angleFromReference = Mark.calculateHeadingRad(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat, unscaledLon);
|
angleFromReference = Mark
|
||||||
distanceFromReference = Mark.calculateDistance(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat, unscaledLon);
|
.calculateHeadingRad(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat,
|
||||||
|
unscaledLon);
|
||||||
|
distanceFromReference = Mark
|
||||||
|
.calculateDistance(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat,
|
||||||
|
unscaledLon);
|
||||||
if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) {
|
if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) {
|
||||||
xAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
xAxisLocation += (int) Math
|
||||||
yAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||||
|
yAxisLocation -= (int) Math
|
||||||
|
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||||
} else if (angleFromReference >= 0) {
|
} else if (angleFromReference >= 0) {
|
||||||
angleFromReference = angleFromReference - Math.PI / 2;
|
angleFromReference = angleFromReference - Math.PI / 2;
|
||||||
xAxisLocation += (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
xAxisLocation += (int) Math
|
||||||
yAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||||
|
yAxisLocation += (int) Math
|
||||||
|
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||||
} else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) {
|
} else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) {
|
||||||
angleFromReference = Math.abs(angleFromReference);
|
angleFromReference = Math.abs(angleFromReference);
|
||||||
xAxisLocation -= (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
xAxisLocation -= (int) Math
|
||||||
yAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||||
|
yAxisLocation -= (int) Math
|
||||||
|
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||||
} else {
|
} else {
|
||||||
angleFromReference = Math.abs(angleFromReference) - Math.PI / 2;
|
angleFromReference = Math.abs(angleFromReference) - Math.PI / 2;
|
||||||
xAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
xAxisLocation -= (int) Math
|
||||||
yAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||||
|
yAxisLocation += (int) Math
|
||||||
|
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||||
}
|
}
|
||||||
if(horizontalInversion) {
|
if(horizontalInversion) {
|
||||||
xAxisLocation = CANVAS_WIDTH - BUFFER_SIZE - (xAxisLocation - BUFFER_SIZE);
|
xAxisLocation = CANVAS_WIDTH - BUFFER_SIZE - (xAxisLocation - BUFFER_SIZE);
|
||||||
@@ -502,7 +561,7 @@ public class CanvasController {
|
|||||||
/**
|
/**
|
||||||
* Find the number of meters per pixel.
|
* Find the number of meters per pixel.
|
||||||
*/
|
*/
|
||||||
private void findMetersPerPixel () {
|
private void findMetersPerPixel() {
|
||||||
Point2D p1, p2;
|
Point2D p1, p2;
|
||||||
Mark m1, m2;
|
Mark m1, m2;
|
||||||
double theta, distance, dx, dy, dHorizontal, dVertical;
|
double theta, distance, dx, dy, dHorizontal, dVertical;
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package seng302.controllers;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import javafx.scene.control.cell.PropertyValueFactory;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import seng302.models.Yacht;
|
||||||
|
import seng302.models.stream.StreamParser;
|
||||||
|
import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
|
||||||
|
|
||||||
|
public class FinishScreenViewController implements Initializable {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private GridPane finishScreenGridPane;
|
||||||
|
@FXML
|
||||||
|
private TableView<Yacht> finishOrderTable;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<Yacht, String> posCol;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<Yacht, String> boatNameCol;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<Yacht, String> shortNameCol;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<Yacht, String> countryCol;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
finishScreenGridPane.getStylesheets()
|
||||||
|
.add(getClass().getResource("/css/master.css").toString());
|
||||||
|
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
|
||||||
|
posCol.setCellValueFactory(
|
||||||
|
new PropertyValueFactory<>("position")
|
||||||
|
);
|
||||||
|
boatNameCol.setCellValueFactory(
|
||||||
|
new PropertyValueFactory<>("boatName")
|
||||||
|
);
|
||||||
|
shortNameCol.setCellValueFactory(
|
||||||
|
new PropertyValueFactory<>("shortName")
|
||||||
|
);
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setContentPane(String jfxUrl) {
|
||||||
|
try {
|
||||||
|
// get the main controller anchor pane (FinishView -> MainView)
|
||||||
|
AnchorPane contentPane = (AnchorPane) finishScreenGridPane.getParent();
|
||||||
|
contentPane.getChildren().removeAll();
|
||||||
|
contentPane.getChildren().clear();
|
||||||
|
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
|
contentPane.getChildren()
|
||||||
|
.addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl)));
|
||||||
|
} catch (javafx.fxml.LoadException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchToStartScreenView() {
|
||||||
|
setContentPane("/views/StartScreenView.fxml");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package seng302.controllers;
|
||||||
|
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import seng302.gameServer.GameServerThread;
|
||||||
|
import seng302.gameServer.GameStages;
|
||||||
|
import seng302.gameServer.GameState;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class describing the actions of the lobby screen
|
||||||
|
* Created by wmu16 on 10/07/17.
|
||||||
|
*/
|
||||||
|
public class LobbyController {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private GridPane lobbyScreen;
|
||||||
|
@FXML
|
||||||
|
private Text lobbyIpText;
|
||||||
|
@FXML
|
||||||
|
private TableView lobbyTable;
|
||||||
|
@FXML
|
||||||
|
private TableColumn ipTableColumn;
|
||||||
|
@FXML
|
||||||
|
private TableColumn colourTableColumn;
|
||||||
|
@FXML
|
||||||
|
private TableColumn readyTableColumn;
|
||||||
|
|
||||||
|
private GameServerThread gameServerThread;
|
||||||
|
|
||||||
|
private void setContentPane(String jfxUrl) {
|
||||||
|
try {
|
||||||
|
AnchorPane contentPane = (AnchorPane) lobbyScreen.getParent();
|
||||||
|
contentPane.getChildren().removeAll();
|
||||||
|
contentPane.getChildren().clear();
|
||||||
|
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
|
contentPane.getChildren()
|
||||||
|
.addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl)));
|
||||||
|
} catch (javafx.fxml.LoadException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void leaveLobbyButtonPressed() {
|
||||||
|
// TODO: 10/07/17 wmu16 - Finish function!
|
||||||
|
setContentPane("/views/StartScreenView.fxml");
|
||||||
|
System.out.println("Leaving lobby!");
|
||||||
|
GameState.setCurrentStage(GameStages.CANCELLED);
|
||||||
|
gameServerThread.terminateGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void readyButtonPressed() {
|
||||||
|
GameState.setCurrentStage(GameStages.RACING);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setGameServerThread(GameServerThread gameServerThread) {
|
||||||
|
this.gameServerThread = gameServerThread;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,188 +1,86 @@
|
|||||||
package seng302.controllers;
|
package seng302.controllers;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.TableColumn;
|
|
||||||
import javafx.scene.control.TableView;
|
|
||||||
import javafx.scene.control.cell.PropertyValueFactory;
|
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.paint.Color;
|
import seng302.gameServer.GameServerThread;
|
||||||
import seng302.models.Yacht;
|
import seng302.gameServer.GameState;
|
||||||
import seng302.models.stream.StreamParser;
|
import seng302.models.stream.StreamReceiver;
|
||||||
import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
|
|
||||||
|
|
||||||
public class StartScreenController implements Initializable {
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Class describing the actions of the start screen controller
|
||||||
|
* Created by wmu16 on 10/07/17.
|
||||||
|
*/
|
||||||
|
public class StartScreenController {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private GridPane gridPane;
|
private TextField ipTextField;
|
||||||
@FXML
|
@FXML
|
||||||
private Label timeTillLive;
|
private GridPane startScreen2;
|
||||||
@FXML
|
|
||||||
private Button streamButton;
|
|
||||||
@FXML
|
|
||||||
private Button switchToRaceViewButton;
|
|
||||||
@FXML
|
|
||||||
private TableView<Yacht> teamList;
|
|
||||||
@FXML
|
|
||||||
private TableColumn<Yacht, String> boatNameCol;
|
|
||||||
@FXML
|
|
||||||
private TableColumn<Yacht, String> shortNameCol;
|
|
||||||
@FXML
|
|
||||||
private TableColumn<Yacht, String> countryCol;
|
|
||||||
@FXML
|
|
||||||
private TableColumn<Yacht, String> posCol;
|
|
||||||
@FXML
|
|
||||||
private Label realTime;
|
|
||||||
|
|
||||||
private boolean switchedToRaceView = false;
|
/**
|
||||||
|
* Loads the fxml content into the parent pane
|
||||||
private void setContentPane(String jfxUrl) {
|
* @param jfxUrl
|
||||||
|
* @return the controller of the fxml
|
||||||
|
*/
|
||||||
|
private Object setContentPane(String jfxUrl) {
|
||||||
try {
|
try {
|
||||||
// get the main controller anchor pane (MainView.fxml)
|
AnchorPane contentPane = (AnchorPane) startScreen2.getParent();
|
||||||
AnchorPane contentPane = (AnchorPane) gridPane.getParent();
|
|
||||||
contentPane.getChildren().removeAll();
|
contentPane.getChildren().removeAll();
|
||||||
contentPane.getChildren().clear();
|
contentPane.getChildren().clear();
|
||||||
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
contentPane.getChildren()
|
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(jfxUrl));
|
||||||
.addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl)));
|
contentPane.getChildren().addAll((Pane) fxmlLoader.load());
|
||||||
|
|
||||||
|
return fxmlLoader.getController();
|
||||||
} catch (javafx.fxml.LoadException e) {
|
} catch (javafx.fxml.LoadException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
|
||||||
gridPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
|
||||||
teamList.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Running a timer to update the livestream status on welcome screen. Update interval is 1
|
* ATTEMPTS TO:
|
||||||
* second.
|
* Sets up a new game state with your IP address as designated as the host.
|
||||||
|
* Starts a thread to listen for incoming connections
|
||||||
|
* Switches to the lobby screen
|
||||||
*/
|
*/
|
||||||
public void startStream() {
|
@FXML
|
||||||
if (StreamParser.isStreamStatus()) {
|
public void hostButtonPressed() {
|
||||||
streamButton.setVisible(false);
|
try {
|
||||||
realTime.setVisible(true);
|
String ipAddress = InetAddress.getLocalHost().getHostAddress();
|
||||||
timeTillLive.setVisible(true);
|
new GameState(ipAddress);
|
||||||
timeTillLive.setTextFill(Color.GREEN);
|
GameServerThread gameServerThread = new GameServerThread("Game Server");
|
||||||
timeTillLive.setText("Connecting...");
|
System.out.println("Server thread started");
|
||||||
Timer timer = new Timer();
|
|
||||||
timer.scheduleAtFixedRate(new TimerTask() {
|
// get the lobby controller so that we can pass the game server thread to it
|
||||||
@Override
|
LobbyController lobbyController = (LobbyController) setContentPane("/views/LobbyView.fxml");
|
||||||
public void run() {
|
lobbyController.setGameServerThread(gameServerThread);
|
||||||
Platform.runLater(() -> {
|
|
||||||
if (StreamParser.isRaceStarted()) {
|
} catch (UnknownHostException e) {
|
||||||
if (!switchedToRaceView) {
|
System.err.println("COULD NOT FIND YOUR IP ADDRESS!");
|
||||||
switchToRaceView();
|
e.printStackTrace();
|
||||||
}
|
|
||||||
timer.cancel();
|
|
||||||
}
|
|
||||||
if (StreamParser.isRaceFinished()) {
|
|
||||||
realTime.setText(StreamParser.getCurrentTimeString());
|
|
||||||
timeTillLive.setTextFill(Color.RED);
|
|
||||||
timeTillLive.setText("Race finished! Waiting for new race...");
|
|
||||||
switchToRaceViewButton.setDisable(true);
|
|
||||||
} else if (StreamParser.getTimeSinceStart() > 0) {
|
|
||||||
realTime.setText(StreamParser.getCurrentTimeString());
|
|
||||||
updateTeamList();
|
|
||||||
timeTillLive.setTextFill(Color.RED);
|
|
||||||
switchToRaceViewButton.setDisable(false);
|
|
||||||
String timerMinute = Long
|
|
||||||
.toString(StreamParser.getTimeSinceStart() / 60);
|
|
||||||
String timerSecond = Long
|
|
||||||
.toString(StreamParser.getTimeSinceStart() % 60);
|
|
||||||
if (timerSecond.length() == 1) {
|
|
||||||
timerSecond = "0" + timerSecond;
|
|
||||||
}
|
|
||||||
String timerString = "-" + timerMinute + ":" + timerSecond;
|
|
||||||
timeTillLive.setText(timerString);
|
|
||||||
} else {
|
|
||||||
realTime.setText(StreamParser.getCurrentTimeString());
|
|
||||||
updateTeamList();
|
|
||||||
timeTillLive.setTextFill(Color.BLACK);
|
|
||||||
switchToRaceViewButton.setDisable(false);
|
|
||||||
String timerMinute = Long
|
|
||||||
.toString(-1 * StreamParser.getTimeSinceStart() / 60);
|
|
||||||
String timerSecond = Long
|
|
||||||
.toString(-1 * StreamParser.getTimeSinceStart() % 60);
|
|
||||||
if (timerSecond.length() == 1) {
|
|
||||||
timerSecond = "0" + timerSecond;
|
|
||||||
}
|
|
||||||
String timerString = timerMinute + ":" + timerSecond;
|
|
||||||
timeTillLive.setText(timerString);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 0, 1000);
|
|
||||||
} else {
|
|
||||||
timeTillLive.setText("Stream not available.");
|
|
||||||
timeTillLive.setTextFill(Color.RED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchToRaceView() {
|
|
||||||
StreamParser.boatLocations.clear();
|
|
||||||
switchedToRaceView = true;
|
|
||||||
setContentPane("/views/RaceView.fxml");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTeamList() {
|
@FXML
|
||||||
ObservableList<Yacht> data = FXCollections.observableArrayList();
|
public void connectButtonPressed() {
|
||||||
|
// TODO: 10/07/17 wmu16 - Finish function
|
||||||
teamList.setItems(data);
|
String ipAddress = ipTextField.getText().trim();
|
||||||
|
StreamReceiver sr = new StreamReceiver(ipAddress, GameServerThread.PORT_NUMBER, "HostStream");
|
||||||
boatNameCol.setCellValueFactory(
|
sr.start();
|
||||||
new PropertyValueFactory<>("boatName")
|
|
||||||
);
|
|
||||||
shortNameCol.setCellValueFactory(
|
|
||||||
new PropertyValueFactory<>("shortName")
|
|
||||||
);
|
|
||||||
countryCol.setCellValueFactory(
|
|
||||||
new PropertyValueFactory<>("country")
|
|
||||||
);
|
|
||||||
posCol.setCellValueFactory(
|
|
||||||
new PropertyValueFactory<>("position")
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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 boats to the start screen list
|
|
||||||
if (StreamParser.isRaceStarted()) { // if race is started, use StreamParser.getBoatsPos()
|
|
||||||
for (Yacht boat : StreamParser.getBoatsPos().values()) {
|
|
||||||
if (participantIDs.contains(boat.getSourceID())) {
|
|
||||||
data.add(boat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // else use StreamParser.getBoats()
|
|
||||||
for (Yacht boat : StreamParser.getBoats().values()) {
|
|
||||||
if (participantIDs.contains(boat.getSourceID())) {
|
|
||||||
data.add(boat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
teamList.refresh();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
import seng302.models.Player;
|
||||||
|
|
||||||
|
public interface ClientConnectionDelegate {
|
||||||
|
/**
|
||||||
|
* A player has connected to the server
|
||||||
|
* @param player The player that has connected
|
||||||
|
*/
|
||||||
|
void clientConnected(Player player);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A player has disconnected from the server
|
||||||
|
* @param player The player that has disconnected
|
||||||
|
*/
|
||||||
|
void clientDisconnected(Player player);
|
||||||
|
}
|
||||||
@@ -0,0 +1,371 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
import seng302.models.Player;
|
||||||
|
import seng302.models.Yacht;
|
||||||
|
import seng302.server.messages.*;
|
||||||
|
import seng302.server.simulator.Boat;
|
||||||
|
import seng302.server.simulator.Simulator;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.SocketOption;
|
||||||
|
import java.net.SocketOptions;
|
||||||
|
import java.nio.channels.ServerSocketChannel;
|
||||||
|
import java.nio.channels.SocketChannel;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class GameServerThread implements Runnable, Observer, ClientConnectionDelegate{
|
||||||
|
|
||||||
|
private static final Integer MAX_NUM_PLAYERS = 10;
|
||||||
|
public static final int PORT_NUMBER = 4950;
|
||||||
|
|
||||||
|
private Boolean hosting = true;
|
||||||
|
|
||||||
|
private ServerSocketChannel server;
|
||||||
|
private long startTime;
|
||||||
|
private short seqNum;
|
||||||
|
|
||||||
|
private final int RACE_STATUS_PERIOD = 1000/2;
|
||||||
|
private final int RACE_START_STATUS_PERIOD = 1000;
|
||||||
|
private final int BOAT_LOCATION_PERIOD = 1000/5;
|
||||||
|
private final int TIME_TILL_RACE_START = 20*1000;
|
||||||
|
private static final int LOG_LEVEL = 1;
|
||||||
|
|
||||||
|
public GameServerThread(String threadName){
|
||||||
|
Thread runner = new Thread(this, threadName);
|
||||||
|
runner.setDaemon(true);
|
||||||
|
seqNum = 0;
|
||||||
|
|
||||||
|
runner.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serverLog(String message, int logLevel){
|
||||||
|
if(logLevel <= LOG_LEVEL){
|
||||||
|
System.out.println("[SERVER] " + message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns an XML Message from the file specified
|
||||||
|
* @param fileName The source XML file
|
||||||
|
* @param type The XML Message type
|
||||||
|
* @return The XML Message
|
||||||
|
*/
|
||||||
|
private Message getXmlMessage(String fileName, XMLMessageSubType type){
|
||||||
|
String fileContents = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
InputStream thisStream = this.getClass().getResourceAsStream(fileName);
|
||||||
|
fileContents = new String(org.apache.commons.io.IOUtils.toByteArray(thisStream));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (NullPointerException e){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileContents != null){
|
||||||
|
return new XMLMessage(fileContents, type, seqNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Get a race status message for the current race
|
||||||
|
*/
|
||||||
|
private Message getRaceStatusMessage(){
|
||||||
|
|
||||||
|
List<BoatSubMessage> boatSubMessages = new ArrayList<>();
|
||||||
|
BoatStatus boatStatus;
|
||||||
|
RaceStatus raceStatus;
|
||||||
|
boolean thereAreBoatsNotFinished = false;
|
||||||
|
|
||||||
|
for (Player player : GameState.getPlayers()){
|
||||||
|
Yacht y = player.getYacht();
|
||||||
|
|
||||||
|
if (GameState.getCurrentStage() == GameStages.PRE_RACE){
|
||||||
|
boatStatus = BoatStatus.PRESTART;
|
||||||
|
thereAreBoatsNotFinished = true;
|
||||||
|
}
|
||||||
|
else if(false){ //@TODO if boat has finished
|
||||||
|
boatStatus = BoatStatus.FINISHED;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
boatStatus = BoatStatus.PRESTART;
|
||||||
|
thereAreBoatsNotFinished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BoatSubMessage m = new BoatSubMessage(y.getSourceID(), boatStatus, y.getLastMarkRounded().getId(), 0, 0, 1234l, 1234l);
|
||||||
|
boatSubMessages.add(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thereAreBoatsNotFinished){
|
||||||
|
if (GameState.getCurrentStage() == GameStages.RACING){
|
||||||
|
raceStatus = RaceStatus.STARTED;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
long timeDifference = startTime - currentTime;
|
||||||
|
|
||||||
|
if (timeDifference > 60*3){
|
||||||
|
raceStatus = RaceStatus.PRESTART;
|
||||||
|
}
|
||||||
|
else if (timeDifference > 60){
|
||||||
|
raceStatus = RaceStatus.WARNING;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
raceStatus = RaceStatus.PREPARATORY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
raceStatus = RaceStatus.TERMINATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RaceStatusMessage(1, raceStatus, startTime, WindDirection.SOUTH,
|
||||||
|
100, GameState.getPlayers().size(), RaceType.MATCH_RACE, 1, boatSubMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start sending race start status messages until race starts
|
||||||
|
*/
|
||||||
|
private void startSendingRaceStartStatusMessages(){
|
||||||
|
Timer t = new Timer();
|
||||||
|
t.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Message raceStartStatusMessage = new RaceStartStatusMessage(seqNum, startTime , 1,
|
||||||
|
RaceStartNotificationType.SET_RACE_START_TIME);
|
||||||
|
try {
|
||||||
|
if (startTime < System.currentTimeMillis() && GameState.getCurrentStage() != GameStages.RACING){
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
broadcast(raceStartStatusMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0, RACE_START_STATUS_PERIOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start sending race start status messages until race starts
|
||||||
|
*/
|
||||||
|
private void startSendingRaceStatusMessages(){
|
||||||
|
|
||||||
|
Timer t = new Timer();
|
||||||
|
t.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Message raceStatusMessage = getRaceStatusMessage();
|
||||||
|
try {
|
||||||
|
broadcast(raceStatusMessage);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0, RACE_STATUS_PERIOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the race, boat, and regatta XML files to the client
|
||||||
|
*/
|
||||||
|
private void sendXml(){
|
||||||
|
try{
|
||||||
|
Message raceData = getXmlMessage("/server_config/race.xml", XMLMessageSubType.RACE);
|
||||||
|
Message boatData = getXmlMessage("/server_config/boats.xml", XMLMessageSubType.BOAT);
|
||||||
|
Message regatta = getXmlMessage("/server_config/regatta.xml", XMLMessageSubType.REGATTA);
|
||||||
|
|
||||||
|
if (raceData != null){
|
||||||
|
broadcast(raceData);
|
||||||
|
}
|
||||||
|
if (boatData != null){
|
||||||
|
broadcast(boatData);
|
||||||
|
}
|
||||||
|
if (regatta != null){
|
||||||
|
broadcast(regatta);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
serverLog("Couldn't send an XML Message: " + e.getMessage(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the post-start race course information
|
||||||
|
*/
|
||||||
|
private void sendPostStartCourseXml(){
|
||||||
|
Timer t = new Timer();
|
||||||
|
t.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Message raceData = getXmlMessage("/server_config/courseLimits.xml", XMLMessageSubType.RACE);
|
||||||
|
if (raceData != null) {
|
||||||
|
broadcast(raceData);
|
||||||
|
}
|
||||||
|
}catch (IOException e) {
|
||||||
|
serverLog("Couldn't send an XML Message: " + e.getMessage(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},1000);
|
||||||
|
//Delays the new course xml data for 25 seconds so the boats are able to pass the starting line
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
ServerListenThread serverListenThread;
|
||||||
|
HeartbeatThread heartbeatThread;
|
||||||
|
Boolean serverIsSendingMessages = false;
|
||||||
|
|
||||||
|
try{
|
||||||
|
server = ServerSocketChannel.open();
|
||||||
|
server.socket().bind(new InetSocketAddress("localhost", PORT_NUMBER));
|
||||||
|
|
||||||
|
serverListenThread = new ServerListenThread(server, this);
|
||||||
|
heartbeatThread = new HeartbeatThread(this);
|
||||||
|
|
||||||
|
heartbeatThread.start();
|
||||||
|
serverListenThread.start();
|
||||||
|
}
|
||||||
|
catch (IOException e){
|
||||||
|
serverLog("Failed to bind socket: " + e.getMessage(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (hosting) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(100);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GameState.getCurrentStage() == GameStages.RACING && !serverIsSendingMessages) {
|
||||||
|
serverLog("Race Started", 0);
|
||||||
|
|
||||||
|
sendXml();
|
||||||
|
startSendingRaceStartStatusMessages();
|
||||||
|
//startSendingRaceStatusMessages();
|
||||||
|
sendPostStartCourseXml();
|
||||||
|
serverIsSendingMessages = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (GameState.getCurrentStage() == GameStages.FINISHED) {
|
||||||
|
serverLog("Race Finished", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
startTime = System.currentTimeMillis() + TIME_TILL_RACE_START;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Start sending static boat position updates when race has finished
|
||||||
|
// */
|
||||||
|
// private void startSendingRaceFinishedBoatPositions(){
|
||||||
|
// Timer t = new Timer();
|
||||||
|
// t.schedule(new TimerTask() {
|
||||||
|
// @Override
|
||||||
|
// public void run() {
|
||||||
|
// try {
|
||||||
|
// for (Boat b : raceSimulator.getBoats()){
|
||||||
|
// Message m = new BoatLocationMessage(b.getSourceID(), seqNum, b.getLat(),
|
||||||
|
// b.getLng(), b.getLastPassedCorner().getBearingToNextCorner(),
|
||||||
|
// ((long) 0));
|
||||||
|
//
|
||||||
|
// server.send(m);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// } catch (IOException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }, 0, BOAT_LOCATION_PERIOD);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A client has tried to connect to the server
|
||||||
|
* @param player The player that connected
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void clientConnected(Player player) {
|
||||||
|
if (GameState.getPlayers().size() < MAX_NUM_PLAYERS && GameState.getCurrentStage() == GameStages.LOBBYING) {
|
||||||
|
serverLog("Player Connected", 0);
|
||||||
|
GameState.addPlayer(player);
|
||||||
|
sendXml();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A player has left the game, remove the player from the GameState
|
||||||
|
* @param player The player that left
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void clientDisconnected(Player player) {
|
||||||
|
serverLog("Player disconnected", 0);
|
||||||
|
GameState.removePlayer(player);
|
||||||
|
sendXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unicast(Message message, SocketChannel client) throws IOException {
|
||||||
|
message.send(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void broadcast(Message message) throws IOException{
|
||||||
|
for(Player player : GameState.getPlayers()) {
|
||||||
|
//System.out.println("Sending message seqNo[" + seqNum + "] to Player: " + player.toString());
|
||||||
|
message.send(player.getSocketChannel());
|
||||||
|
}
|
||||||
|
seqNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a boat location message when they are updated by the simulator
|
||||||
|
* @param o .
|
||||||
|
* @param arg .
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void update(Observable o, Object arg) {
|
||||||
|
/* Only send if server started
|
||||||
|
// TODO: I don't understand why i need to check server is null or not ... confused - haoming 2/5/17
|
||||||
|
if(server == null || !server.isStarted()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int numOfBoatsFinished = 0;
|
||||||
|
for (Boat boat : (List<Boat>) arg){
|
||||||
|
try {
|
||||||
|
if (boat.isFinished()) {
|
||||||
|
numOfBoatsFinished ++;
|
||||||
|
if (!boatsFinished.get(boat.getSourceID())) {
|
||||||
|
boatsFinished.put(boat.getSourceID(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message m = new BoatLocationMessage(boat.getSourceID(), 1, boat.getLat(),
|
||||||
|
boat.getLng(), boat.getLastPassedCorner().getBearingToNextCorner(),
|
||||||
|
((long) boat.getSpeed()));
|
||||||
|
broadcast(m);
|
||||||
|
} catch (IOException e) {
|
||||||
|
serverLog("Couldn't send a boat status message", 3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (NullPointerException e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (numOfBoatsFinished == ((List<Boat>) arg).size()) {
|
||||||
|
// startSendingRaceFinishedBoatPositions();
|
||||||
|
// }
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
public void terminateGame() {
|
||||||
|
try {
|
||||||
|
//TODO: for now, I just close the socket, but i think we should terminate the whole thread instead. -hyi25 13 July
|
||||||
|
server.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An enum describing the states of the game
|
||||||
|
* Created by wmu16 on 11/07/17.
|
||||||
|
*/
|
||||||
|
public enum GameStages {
|
||||||
|
|
||||||
|
LOBBYING(0),
|
||||||
|
PRE_RACE(1),
|
||||||
|
RACING(2),
|
||||||
|
FINISHED(3),
|
||||||
|
CANCELLED(4);
|
||||||
|
|
||||||
|
private long code;
|
||||||
|
|
||||||
|
GameStages(long code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCode(){
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
import seng302.models.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Static class to hold information about the current state of the game (model)
|
||||||
|
* Created by wmu16 on 10/07/17.
|
||||||
|
*/
|
||||||
|
public class GameState {
|
||||||
|
|
||||||
|
private static String hostIpAddress;
|
||||||
|
private static ArrayList<Player> players;
|
||||||
|
private static Boolean isRaceStarted;
|
||||||
|
private static GameStages currentStage;
|
||||||
|
|
||||||
|
public GameState(String hostIpAddress) {
|
||||||
|
GameState.hostIpAddress = hostIpAddress;
|
||||||
|
players = new ArrayList<>();
|
||||||
|
currentStage = GameStages.LOBBYING;
|
||||||
|
isRaceStarted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getHostIpAddress() {
|
||||||
|
return hostIpAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<Player> getPlayers() {
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addPlayer(Player player) {
|
||||||
|
players.add(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removePlayer(Player player) {
|
||||||
|
players.remove(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean getIsRaceStarted() {
|
||||||
|
return isRaceStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GameStages getCurrentStage() {
|
||||||
|
return currentStage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setCurrentStage(GameStages currentStage) {
|
||||||
|
GameState.currentStage = currentStage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This iterates through all players and updates each players info to its new state based on its current data
|
||||||
|
*/
|
||||||
|
private void update(){
|
||||||
|
for(Player player : players) {
|
||||||
|
// TODO: 10/07/17 wmu16 - Update all player info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
import seng302.models.Player;
|
||||||
|
import seng302.server.messages.Heartbeat;
|
||||||
|
import seng302.server.messages.Message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send Heartbeat messages to connected player at a specified interval
|
||||||
|
* Will call .clientDisconnected on the delegate when a heartbeat message
|
||||||
|
* cannot be sent to a player
|
||||||
|
*/
|
||||||
|
public class HeartbeatThread extends Thread{
|
||||||
|
private final int HEARTBEAT_PERIOD = 5000;
|
||||||
|
private ClientConnectionDelegate delegate;
|
||||||
|
private Integer seqNum;
|
||||||
|
private Stack<Player> disconnectedPlayers;
|
||||||
|
|
||||||
|
HeartbeatThread(ClientConnectionDelegate delegate){
|
||||||
|
this.delegate = delegate;
|
||||||
|
seqNum = 0;
|
||||||
|
disconnectedPlayers = new Stack<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A player has lost connection to the server
|
||||||
|
* The player is added to a stack so that the delegate
|
||||||
|
* can be notified
|
||||||
|
*
|
||||||
|
* @param player The player that has disconnected
|
||||||
|
*/
|
||||||
|
private void playerLostConnection(Player player){
|
||||||
|
disconnectedPlayers.push(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a heartbeat message to each connected player
|
||||||
|
* The delegate is notified if a player has disconnected
|
||||||
|
*/
|
||||||
|
private void sendHeartbeatToAllPlayers(){
|
||||||
|
Message heartbeat = new Heartbeat(seqNum);
|
||||||
|
|
||||||
|
for (Player player : GameState.getPlayers()){
|
||||||
|
if (!player.getSocketChannel().isConnected()){
|
||||||
|
playerLostConnection(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
heartbeat.send(player.getSocketChannel());
|
||||||
|
} catch (IOException e) {
|
||||||
|
playerLostConnection(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDelegate();
|
||||||
|
seqNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the delegate about
|
||||||
|
* each disconnected player
|
||||||
|
*/
|
||||||
|
private void updateDelegate() {
|
||||||
|
while (!disconnectedPlayers.empty()){
|
||||||
|
delegate.clientDisconnected(disconnectedPlayers.pop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(){
|
||||||
|
Timer t = new Timer();
|
||||||
|
|
||||||
|
t.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
sendHeartbeatToAllPlayers();
|
||||||
|
}
|
||||||
|
}, 0, HEARTBEAT_PERIOD);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
import seng302.models.Player;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.ServerSocketChannel;
|
||||||
|
import java.nio.channels.SocketChannel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for a thread to listen to connections
|
||||||
|
* Created by wmu16 on 11/07/17.
|
||||||
|
*/
|
||||||
|
public class ServerListenThread extends Thread{
|
||||||
|
private ServerSocketChannel socketChannel;
|
||||||
|
private ClientConnectionDelegate delegate;
|
||||||
|
|
||||||
|
ServerListenThread(ServerSocketChannel socketChannel, ClientConnectionDelegate delegate){
|
||||||
|
this.socketChannel = socketChannel;
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for a connection and upon finding one, creates a Player object and adds it to the universal GameState
|
||||||
|
*/
|
||||||
|
private void acceptConnection() {
|
||||||
|
try {
|
||||||
|
SocketChannel thisClient = socketChannel.accept();
|
||||||
|
if (thisClient.socket() != null){
|
||||||
|
Player thisPlayer = new Player(thisClient);
|
||||||
|
delegate.clientConnected(thisPlayer);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(){
|
||||||
|
while (true){
|
||||||
|
acceptConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package seng302.gameServerWithThreading;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class describing a single connection to a Client for the purposes of sending and receiving on its own thread.
|
||||||
|
* All server threads created and owned by the server thread handler which can trigger client updates on its threads
|
||||||
|
* Created by wmu16 on 13/07/17.
|
||||||
|
*/
|
||||||
|
public class ServerThread extends Thread {
|
||||||
|
|
||||||
|
private static final Integer MAX_ID_ATTEMPTS = 10;
|
||||||
|
|
||||||
|
private InputStream is;
|
||||||
|
private OutputStream os;
|
||||||
|
private Socket socket;
|
||||||
|
|
||||||
|
private Boolean userIdentified = false;
|
||||||
|
private Boolean connected = true;
|
||||||
|
private Boolean updateClient = true;
|
||||||
|
|
||||||
|
public ServerThread(Socket socket) {
|
||||||
|
this.socket = socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
is = socket.getInputStream();
|
||||||
|
os = socket.getOutputStream();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("IO error in server thread upon grabbing streams");
|
||||||
|
}
|
||||||
|
|
||||||
|
threeWayHandshake();
|
||||||
|
|
||||||
|
// TODO: 13/07/17 wmu16 - Some way of knowing if the client is still connected. perhaps when we read disconnect message switch this bool?
|
||||||
|
while (connected) {
|
||||||
|
|
||||||
|
//Perform a read and update game state
|
||||||
|
try {
|
||||||
|
Integer userInput = is.read();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("IO error in server thread upon reading input stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Perform a write if it is time to as delegated by the ServerThreadHandler
|
||||||
|
if (updateClient) {
|
||||||
|
// TODO: 13/07/17 wmu16 - Write out game state - some function that would write all appropriate messages to this output stream
|
||||||
|
// try {
|
||||||
|
// GameState.outputState(os);
|
||||||
|
// } catch (IOException e) {
|
||||||
|
// System.out.println("IO error in server thread upon writing to output stream");
|
||||||
|
// }
|
||||||
|
updateClient = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closeSocket();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateClient() {
|
||||||
|
updateClient = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to confirm the connection just accepted.
|
||||||
|
* Sends ID, expects that ID echoed for confirmation,
|
||||||
|
* if so, sends a confirmation packet back to that connection
|
||||||
|
* Creates a player instance with that ID and this thread and adds it to the GameState
|
||||||
|
* If not, close the socket and end the threads execution
|
||||||
|
*/
|
||||||
|
private void threeWayHandshake() {
|
||||||
|
// TODO: 13/07/17 Finish using AC35
|
||||||
|
// Integer playerID = GameState.getUniquePlayerID();
|
||||||
|
// Integer confirmationID = null;
|
||||||
|
// Integer identificationAttempt = 0
|
||||||
|
// while (!userIdentified) {
|
||||||
|
// os.write(playerID); //Send out new ID looking for echo
|
||||||
|
// confirmationID = is.read();
|
||||||
|
// if (playerID == idConfirmation) { //ID is echoed back. Connection is a client
|
||||||
|
// os.write( some determined confirmation message ); //Confirm to client
|
||||||
|
// GameState.addPlayer(new Player(playerID, this)); //Create a player in game state for client
|
||||||
|
// userIdentified = true;
|
||||||
|
// } else if (identificationAttempt > MAX_ID_ATTEMPTS) { //No response. not a client. tidy up and go home.
|
||||||
|
// closeSocket();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// identificationAttempt++;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeSocket() {
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("IO error in server thread upon trying to close socket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package seng302.gameServerWithThreading;
|
||||||
|
|
||||||
|
import seng302.gameServer.GameStages;
|
||||||
|
import seng302.gameServer.GameState;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class describing the overall server, which creates and collects server threads for each client
|
||||||
|
* Created by wmu16 on 13/07/17.
|
||||||
|
*/
|
||||||
|
public class ServerThreadHandler extends Thread {
|
||||||
|
|
||||||
|
private static final int PORT = 4950;
|
||||||
|
private static final Integer MAX_NUM_PLAYERS = 10;
|
||||||
|
|
||||||
|
private ServerSocket serverSocket = null;
|
||||||
|
private Socket socket;
|
||||||
|
private ArrayList<ServerThread> serverThreads = new ArrayList<>();
|
||||||
|
|
||||||
|
public ServerThreadHandler() {
|
||||||
|
try {
|
||||||
|
serverSocket = new ServerSocket(PORT);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("IO error in server thread handler upon trying to make new server socket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
//You should handle interrupts in some way, so that the thread won't keep on forever if you exit the app.
|
||||||
|
while (!isInterrupted()) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000 / 60); //60 times per second we should calculate the game state
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (GameState.getCurrentStage() == GameStages.LOBBYING && GameState.getPlayers().size() < MAX_NUM_PLAYERS) {
|
||||||
|
try {
|
||||||
|
socket = serverSocket.accept();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("IO error in server thread handler upon trying to accept connection");
|
||||||
|
}
|
||||||
|
ServerThread thread = new ServerThread(socket);
|
||||||
|
serverThreads.add(thread);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateClients();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
serverSocket.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("IO error in server thread handler upon closing socket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateClients() {
|
||||||
|
for (ServerThread serverThread : serverThreads) {
|
||||||
|
serverThread.updateClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package seng302.models;
|
||||||
|
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.SocketChannel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Class defining a player and their respective details in the game as held by the model
|
||||||
|
* Created by wmu16 on 10/07/17.
|
||||||
|
*/
|
||||||
|
public class Player {
|
||||||
|
|
||||||
|
private SocketChannel socketChannel;
|
||||||
|
private Yacht yacht;
|
||||||
|
private Integer lastMarkPassed;
|
||||||
|
|
||||||
|
|
||||||
|
public Player(SocketChannel socketChannel) {
|
||||||
|
this.socketChannel = socketChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocketChannel getSocketChannel() {
|
||||||
|
return socketChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getLastMarkPassed() {
|
||||||
|
return lastMarkPassed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastMarkPassed(Integer lastMarkPassed) {
|
||||||
|
this.lastMarkPassed = lastMarkPassed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Yacht getYacht() {
|
||||||
|
return yacht;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String playerAddress = null;
|
||||||
|
|
||||||
|
if (socketChannel == null){
|
||||||
|
return "Disconnected Player";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
playerAddress = socketChannel.getRemoteAddress().toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return playerAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == null){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(obj instanceof Player)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((Player) obj).socketChannel.equals(socketChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode(){
|
||||||
|
return socketChannel.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,9 +24,8 @@ public final class PolarTable {
|
|||||||
* Iterates through each row of the polar table, in pairs, to extract the row into a hashmap of angle to boat speed.
|
* Iterates through each row of the polar table, in pairs, to extract the row into a hashmap of angle to boat speed.
|
||||||
* These angle boatspeed hashmaps are then added to an outer hashmap at the end of wind speed key to each row hashmap
|
* These angle boatspeed hashmaps are then added to an outer hashmap at the end of wind speed key to each row hashmap
|
||||||
* as a value
|
* as a value
|
||||||
* @param file containing the polar csv information
|
|
||||||
*/
|
*/
|
||||||
public static void parsePolarFile(String file) {
|
public static void parsePolarFile(InputStream polarFile) {
|
||||||
polarTable = new HashMap<>();
|
polarTable = new HashMap<>();
|
||||||
upwindOptimal = new HashMap<>();
|
upwindOptimal = new HashMap<>();
|
||||||
downwindOptimal = new HashMap<>();
|
downwindOptimal = new HashMap<>();
|
||||||
@@ -34,7 +33,7 @@ public final class PolarTable {
|
|||||||
String line;
|
String line;
|
||||||
Boolean isHeaderLine = true;
|
Boolean isHeaderLine = true;
|
||||||
|
|
||||||
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(polarFile))) {
|
||||||
while ((line = br.readLine()) != null) {
|
while ((line = br.readLine()) != null) {
|
||||||
String[] thisLine = line.split(",");
|
String[] thisLine = line.split(",");
|
||||||
|
|
||||||
@@ -69,6 +68,8 @@ public final class PolarTable {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ public class Yacht {
|
|||||||
private String shortName;
|
private String shortName;
|
||||||
private String boatName;
|
private String boatName;
|
||||||
private String country;
|
private String country;
|
||||||
|
|
||||||
|
// Situational data
|
||||||
|
|
||||||
|
|
||||||
// Boat status
|
// Boat status
|
||||||
private Integer boatStatus;
|
private Integer boatStatus;
|
||||||
private Integer legNumber;
|
private Integer legNumber;
|
||||||
@@ -31,6 +35,9 @@ public class Yacht {
|
|||||||
private Integer penaltiesServed;
|
private Integer penaltiesServed;
|
||||||
private Long estimateTimeAtFinish;
|
private Long estimateTimeAtFinish;
|
||||||
private String position;
|
private String position;
|
||||||
|
private Double lat;
|
||||||
|
private Double lon;
|
||||||
|
private Float heading;
|
||||||
private double velocity;
|
private double velocity;
|
||||||
private Long timeTillNext;
|
private Long timeTillNext;
|
||||||
private Long markRoundTime;
|
private Long markRoundTime;
|
||||||
@@ -191,17 +198,41 @@ public class Yacht {
|
|||||||
this.lastMarkRounded = lastMarkRounded;
|
this.lastMarkRounded = lastMarkRounded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setNextMark(Mark nextMark) {
|
||||||
|
this.nextMark = nextMark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mark getNextMark(){
|
||||||
|
return nextMark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getLat() {
|
||||||
|
return lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLat(Double lat) {
|
||||||
|
this.lat = lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getLon() {
|
||||||
|
return lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLon(Double lon) {
|
||||||
|
this.lon = lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getHeading() {
|
||||||
|
return heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeading(Float heading) {
|
||||||
|
this.heading = heading;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return boatName;
|
return boatName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNextMark(Mark nextMark) {
|
|
||||||
this.nextMark = nextMark;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mark getNextMark(){
|
|
||||||
return nextMark;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,10 +43,21 @@ public class Header {
|
|||||||
buff.position(buffPos);
|
buff.position(buffPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the buffer
|
||||||
|
*/
|
||||||
|
public void reset(){
|
||||||
|
buffPos = 0;
|
||||||
|
buff.clear();
|
||||||
|
buff.position(buffPos);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a ByteBuffer containing the message header
|
* @return a ByteBuffer containing the message header
|
||||||
*/
|
*/
|
||||||
public ByteBuffer getByteBuffer(){
|
public ByteBuffer getByteBuffer(){
|
||||||
|
reset();
|
||||||
|
|
||||||
putInBuffer(ByteBuffer.allocate(1).put((byte)syncByte1).array(), syncByte1);
|
putInBuffer(ByteBuffer.allocate(1).put((byte)syncByte1).array(), syncByte1);
|
||||||
|
|
||||||
putInBuffer(ByteBuffer.allocate(1).put((byte)syncByte2).array(), syncByte2);
|
putInBuffer(ByteBuffer.allocate(1).put((byte)syncByte2).array(), syncByte2);
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public abstract class Message {
|
|||||||
buffer = ByteBuffer.allocate(Header.getSize() + getSize() + CRC_SIZE);
|
buffer = ByteBuffer.allocate(Header.getSize() + getSize() + CRC_SIZE);
|
||||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
bufferPosition = 0;
|
bufferPosition = 0;
|
||||||
|
buffer.position(bufferPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -54,6 +54,15 @@ public class RaceStartStatusMessage extends Message {
|
|||||||
writeCRC();
|
writeCRC();
|
||||||
rewind();
|
rewind();
|
||||||
|
|
||||||
outputStream.write(getBuffer());
|
if (outputStream == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
outputStream.write(getBuffer());
|
||||||
|
}
|
||||||
|
catch (IOException e){
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ Remove scroll bars
|
|||||||
|
|
||||||
.ui-table *.scroll-bar:vertical *.increment-arrow,
|
.ui-table *.scroll-bar:vertical *.increment-arrow,
|
||||||
.ui-table *.scroll-bar:vertical *.decrement-arrow {
|
.ui-table *.scroll-bar:vertical *.decrement-arrow {
|
||||||
-fx-background-color: null;
|
-fx-background-color: #0e6d6c;
|
||||||
-fx-background-radius: 0;
|
-fx-background-radius: 0;
|
||||||
-fx-background-insets: 0;
|
-fx-background-insets: 0;
|
||||||
-fx-padding: 0;
|
-fx-padding: 0;
|
||||||
@@ -177,7 +177,7 @@ Remove scroll bars
|
|||||||
|
|
||||||
.ui-table *.scroll-bar:vertical *.increment-button,
|
.ui-table *.scroll-bar:vertical *.increment-button,
|
||||||
.ui-table *.scroll-bar:vertical *.decrement-button {
|
.ui-table *.scroll-bar:vertical *.decrement-button {
|
||||||
-fx-background-color: null;
|
-fx-background-color: #0e6d6c;
|
||||||
-fx-background-radius: 0;
|
-fx-background-radius: 0;
|
||||||
-fx-background-insets: 0;
|
-fx-background-insets: 0;
|
||||||
-fx-padding: 0;
|
-fx-padding: 0;
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import javafx.geometry.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.text.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<GridPane fx:id="finishScreenGridPane" maxHeight="837.0" maxWidth="837.0" minHeight="837.0" minWidth="837.0" nodeOrientation="LEFT_TO_RIGHT" prefHeight="837.0" prefWidth="837.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.FinishScreenViewController">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints maxHeight="259.0" minHeight="259.0" prefHeight="259.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="64.0" minHeight="64.0" prefHeight="64.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="257.0" minHeight="257.0" prefHeight="257.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="257.0" minHeight="257.0" prefHeight="257.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Label alignment="CENTER" text="Race Finished!" textFill="WHITE" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
|
||||||
|
<font>
|
||||||
|
<Font size="40.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label alignment="CENTER" text="Race Result:" textFill="WHITE" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER">
|
||||||
|
<font>
|
||||||
|
<Font size="28.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<TableView fx:id="finishOrderTable" maxWidth="661.0" prefHeight="324.0" prefWidth="629.0" styleClass="ui-table" GridPane.halignment="CENTER" GridPane.rowIndex="2">
|
||||||
|
<columns>
|
||||||
|
<TableColumn fx:id="posCol" editable="false" maxWidth="74.0" minWidth="74.0" prefWidth="74.0" resizable="false" sortable="false" text="Position" />
|
||||||
|
<TableColumn fx:id="boatNameCol" editable="false" maxWidth="171.0" minWidth="171.0" prefWidth="171.0" resizable="false" sortable="false" text="Boat Name" />
|
||||||
|
<TableColumn fx:id="shortNameCol" editable="false" maxWidth="155.18472290039062" minWidth="107.0" prefWidth="155.18472290039062" resizable="false" sortable="false" text="Short Name" />
|
||||||
|
<TableColumn fx:id="countryCol" editable="false" maxWidth="258.9999694824219" minWidth="147.0" prefWidth="258.9999694824219" resizable="false" sortable="false" text="Country" />
|
||||||
|
</columns>
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="50.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TableView>
|
||||||
|
<Button mnemonicParsing="false" onAction="#switchToStartScreenView" styleClass="blue-ui-btn" text="Return to Start Screen" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="TOP" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<?import javafx.geometry.*?>
|
|
||||||
<?import javafx.scene.text.*?>
|
|
||||||
<?import javafx.scene.control.*?>
|
|
||||||
<?import java.lang.*?>
|
|
||||||
<?import javafx.scene.layout.*?>
|
|
||||||
|
|
||||||
<AnchorPane fx:id="raceResults" maxHeight="1080.0" maxWidth="1920.0" minHeight="1080.0" minWidth="1920.0" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
|
|
||||||
<children>
|
|
||||||
<GridPane layoutX="444.0" layoutY="256.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
|
||||||
<columnConstraints>
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="620.1734008789062" minWidth="10.0" prefWidth="493.2829895019531" />
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="862.0581665039062" minWidth="10.0" prefWidth="786.7170104980469" />
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
|
||||||
</columnConstraints>
|
|
||||||
<rowConstraints>
|
|
||||||
<RowConstraints maxHeight="348.0" minHeight="10.0" prefHeight="112.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="348.0" minHeight="10.0" prefHeight="99.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="348.0" minHeight="7.0" prefHeight="88.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="348.0" minHeight="0.0" prefHeight="278.3376770019531" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="653.0" minHeight="0.0" prefHeight="98.66232299804688" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="812.0" minHeight="10.0" prefHeight="418.4577941894531" vgrow="SOMETIMES" />
|
|
||||||
</rowConstraints>
|
|
||||||
<children>
|
|
||||||
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Race Results:" wrappingWidth="616.5260620117188" GridPane.rowIndex="2">
|
|
||||||
<font>
|
|
||||||
<Font size="45.0" />
|
|
||||||
</font>
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets left="50.0" />
|
|
||||||
</GridPane.margin>
|
|
||||||
</Text>
|
|
||||||
<VBox fx:id="resultsVBox" prefHeight="44.0" prefWidth="571.0" GridPane.rowIndex="3">
|
|
||||||
<opaqueInsets>
|
|
||||||
<Insets />
|
|
||||||
</opaqueInsets>
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets left="50.0" />
|
|
||||||
</GridPane.margin>
|
|
||||||
<padding>
|
|
||||||
<Insets top="60.0" />
|
|
||||||
</padding></VBox>
|
|
||||||
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Finish!" wrappingWidth="623.9530334472656" GridPane.rowIndex="1">
|
|
||||||
<font>
|
|
||||||
<Font size="75.0" />
|
|
||||||
</font>
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets left="50.0" />
|
|
||||||
</GridPane.margin>
|
|
||||||
</Text>
|
|
||||||
</children>
|
|
||||||
</GridPane>
|
|
||||||
</children>
|
|
||||||
</AnchorPane>
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<?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.controllers.LobbyController">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints maxHeight="171.0" minHeight="0.0" prefHeight="31.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="399.0" minHeight="10.0" prefHeight="394.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="63.0" minHeight="10.0" prefHeight="26.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Text fx:id="lobbyIpText" fill="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="Lobby: IP" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER">
|
||||||
|
<font>
|
||||||
|
<Font size="29.0" />
|
||||||
|
</font>
|
||||||
|
</Text>
|
||||||
|
<TableView fx:id="lobbyTable" prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="1">
|
||||||
|
<columns>
|
||||||
|
<TableColumn fx:id="ipTableColumn" prefWidth="259.0" text="IP" />
|
||||||
|
<TableColumn fx:id="colourTableColumn" prefWidth="424.0" text="Colour" />
|
||||||
|
<TableColumn fx:id="readyTableColumn" prefWidth="96.0" text="Ready" />
|
||||||
|
</columns>
|
||||||
|
<columnResizePolicy>
|
||||||
|
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
|
||||||
|
</columnResizePolicy>
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TableView>
|
||||||
|
<GridPane GridPane.rowIndex="2">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Button mnemonicParsing="false" onAction="#readyButtonPressed" prefWidth="101.0" text="Ready" GridPane.halignment="CENTER" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#leaveLobbyButtonPressed" text="Leave Lobby" GridPane.columnIndex="1" GridPane.halignment="CENTER" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
@@ -1,60 +1,48 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import javafx.geometry.*?>
|
<?import javafx.geometry.*?>
|
||||||
<?import javafx.scene.control.*?>
|
|
||||||
<?import javafx.scene.text.*?>
|
|
||||||
<?import javafx.scene.canvas.*?>
|
|
||||||
<?import java.lang.*?>
|
<?import java.lang.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?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="gridPane" 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.controllers.StartScreenController">
|
<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.controllers.StartScreenController">
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
<rowConstraints>
|
<rowConstraints>
|
||||||
<RowConstraints percentHeight="10.0" vgrow="SOMETIMES" />
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
<RowConstraints maxHeight="52.0" minHeight="52.0" prefHeight="52.0" vgrow="SOMETIMES" />
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
<RowConstraints maxHeight="0.0" percentHeight="8.0" prefHeight="0.0" vgrow="SOMETIMES" />
|
<RowConstraints minHeight="72.0" prefHeight="72.0" vgrow="SOMETIMES" />
|
||||||
<RowConstraints maxHeight="28.0" vgrow="SOMETIMES" />
|
<RowConstraints maxHeight="65.0" minHeight="36.0" prefHeight="46.0" vgrow="SOMETIMES" />
|
||||||
<RowConstraints maxHeight="55.0" minHeight="55.0" percentHeight="9.0" prefHeight="55.0" vgrow="SOMETIMES" />
|
<RowConstraints maxHeight="108.0" minHeight="72.0" prefHeight="98.0" vgrow="SOMETIMES" />
|
||||||
<RowConstraints maxHeight="0.0" minHeight="0.0" percentHeight="29.0" prefHeight="0.0" vgrow="SOMETIMES" />
|
<RowConstraints minHeight="72.0" prefHeight="72.0" vgrow="SOMETIMES" />
|
||||||
<RowConstraints maxHeight="93.0" minHeight="72.0" prefHeight="72.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="283.0" minHeight="262.0" prefHeight="283.0" vgrow="SOMETIMES" />
|
|
||||||
</rowConstraints>
|
</rowConstraints>
|
||||||
<children>
|
<children>
|
||||||
<Label alignment="CENTER" text="Welcome to Race Vision" textFill="WHITE" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
|
<Label alignment="CENTER" text="Welcome to Race Vision" textFill="WHITE" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
|
||||||
<font>
|
<font>
|
||||||
<Font size="40.0" />
|
<Font size="40.0" />
|
||||||
</font>
|
</font>
|
||||||
</Label>
|
</Label>
|
||||||
<Label text="Your live AC35 livestream" textFill="WHITE" GridPane.halignment="CENTER" GridPane.rowIndex="1">
|
<Button mnemonicParsing="false" onAction="#hostButtonPressed" prefHeight="25.0" prefWidth="175.0" text="Host" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2" />
|
||||||
<font>
|
<Button mnemonicParsing="false" onAction="#connectButtonPressed" prefHeight="25.0" prefWidth="147.0" text="Connect" GridPane.columnIndex="1" GridPane.rowIndex="4" />
|
||||||
<Font size="20.0" />
|
<TextField fx:id="ipTextField" maxWidth="-Infinity" prefHeight="25.0" prefWidth="200.0" GridPane.halignment="RIGHT" GridPane.rowIndex="4">
|
||||||
</font>
|
<GridPane.margin>
|
||||||
</Label>
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
<Label text="Livestream Status:" textFill="WHITE" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="BOTTOM">
|
</GridPane.margin>
|
||||||
<font>
|
</TextField>
|
||||||
<Font size="28.0" />
|
<Text fill="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="OR" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="3">
|
||||||
</font>
|
<font>
|
||||||
</Label>
|
<Font size="21.0" />
|
||||||
<Label fx:id="timeTillLive" text="0:00 minutes" visible="false" GridPane.halignment="CENTER" GridPane.rowIndex="4">
|
</font>
|
||||||
<font>
|
</Text>
|
||||||
<Font size="27.0" />
|
|
||||||
</font>
|
|
||||||
</Label>
|
|
||||||
<Button fx:id="streamButton" mnemonicParsing="false" onAction="#startStream" styleClass="blue-ui-btn" text="Click to stream" GridPane.halignment="CENTER" GridPane.rowIndex="4" />
|
|
||||||
<Button fx:id="switchToRaceViewButton" disable="true" mnemonicParsing="false" onAction="#switchToRaceView" styleClass="blue-ui-btn" text="Watch Race" GridPane.halignment="CENTER" GridPane.rowIndex="7" GridPane.valignment="TOP" />
|
|
||||||
<TableView fx:id="teamList" maxWidth="661.0" prefHeight="324.0" prefWidth="629.0" styleClass="ui-table" GridPane.halignment="CENTER" GridPane.hgrow="NEVER" GridPane.rowIndex="5" GridPane.vgrow="NEVER">
|
|
||||||
<columns>
|
|
||||||
<TableColumn fx:id="posCol" editable="false" maxWidth="74.0" minWidth="74.0" prefWidth="74.0" resizable="false" sortable="false" text="Position" />
|
|
||||||
<TableColumn fx:id="boatNameCol" editable="false" maxWidth="171.0" minWidth="171.0" prefWidth="171.0" resizable="false" sortable="false" text="Boat Name" />
|
|
||||||
<TableColumn fx:id="shortNameCol" editable="false" maxWidth="155.18472290039062" minWidth="107.0" prefWidth="155.18472290039062" resizable="false" sortable="false" text="Short Name" />
|
|
||||||
<TableColumn fx:id="countryCol" editable="false" maxWidth="258.9999694824219" minWidth="147.0" prefWidth="258.9999694824219" resizable="false" sortable="false" text="Country" />
|
|
||||||
</columns>
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets top="10.0" />
|
|
||||||
</GridPane.margin>
|
|
||||||
</TableView>
|
|
||||||
<Label fx:id="realTime" text="Local time" textFill="WHITE" visible="false" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="BOTTOM" />
|
|
||||||
</children>
|
</children>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
|
|||||||
Reference in New Issue
Block a user