Merge branch 'newHostServer' into 'master'

Nullptr finish race arrow + race xml issues + Host Discovery + Scaling Race

Acceptance Criteria:

When a game starts I should see it reflected in the host lobby within 5 seconds.  
When a game ends it should not remain in the host lobby for more than 5 seconds.  
Changing the windspeed and the milliseconds value on Event.java line 124 should make the race larger or smaller.  
  
This should fix the race xml issues where the player didn't have a boat / last player joining not seeing their boat. Somehow the client was receiving xml files out of order, and it didn't check the order, so it would use race xml file 1, then replace it with race xml file 0, which was out of date.

See merge request !54
main
David Wu 8 years ago
commit 951c3771fa

@ -78,6 +78,18 @@
<version>0.7</version> <version>0.7</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160810</version>
</dependency>
</dependencies> </dependencies>

@ -80,6 +80,7 @@ public class Event {
* @throws EventConstructionException Thrown if we cannot create an Event for any reason. * @throws EventConstructionException Thrown if we cannot create an Event for any reason.
*/ */
public Event(boolean singlePlayer, int mapIndex) throws EventConstructionException { public Event(boolean singlePlayer, int mapIndex) throws EventConstructionException {
PolarParser.parseNewPolars("mock/polars/acc_polars.csv");
this.mapIndex = mapIndex; this.mapIndex = mapIndex;
String raceXMLFile; String raceXMLFile;
String boatsXMLFile = "mock/mockXML/boatTest.xml"; String boatsXMLFile = "mock/mockXML/boatTest.xml";
@ -109,6 +110,7 @@ public class Event {
} }
double windAngle = 300; double windAngle = 300;
double windSpeed = 12;
//Read XML files. //Read XML files.
try { try {
@ -119,6 +121,7 @@ public class Event {
this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, -1, true); this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, -1, true);
} else { } else {
this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, windAngle, false); this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, windAngle, false);
this.raceXML = RaceXMLCreator.scaleRaceSize(raceXML, windSpeed, 15 * 60 * 1000);
} }
this.boatXML = XMLReader.readXMLFileToString(boatsXMLFile, StandardCharsets.UTF_8); this.boatXML = XMLReader.readXMLFileToString(boatsXMLFile, StandardCharsets.UTF_8);
@ -131,7 +134,6 @@ public class Event {
this.xmlFileType = XMLFileType.Contents; this.xmlFileType = XMLFileType.Contents;
this.boatPolars = PolarParser.parse("mock/polars/acc_polars.csv"); this.boatPolars = PolarParser.parse("mock/polars/acc_polars.csv");
PolarParser.parseNewPolars("mock/polars/acc_polars.csv");
//Parse the XML files into data sources. //Parse the XML files into data sources.
@ -152,7 +154,7 @@ public class Event {
WindGenerator windGenerator = new ShiftingWindGenerator( WindGenerator windGenerator = new ShiftingWindGenerator(
Bearing.fromDegrees(windAngle), Bearing.fromDegrees(windAngle),
12 windSpeed
); );
MockRace mockRace = new MockRace( MockRace mockRace = new MockRace(

@ -24,7 +24,6 @@ import java.util.logging.Logger;
public class RaceServer { public class RaceServer {
private MockRace race; private MockRace race;
private LatestMessages latestMessages; private LatestMessages latestMessages;
private static RaceServer server;
private List<YachtEvent> collisionEvents = new ArrayList<>(); private List<YachtEvent> collisionEvents = new ArrayList<>();
@ -50,16 +49,10 @@ public class RaceServer {
public RaceServer(MockRace race, LatestMessages latestMessages) { public RaceServer(MockRace race, LatestMessages latestMessages) {
server = this;
this.race = race; this.race = race;
this.latestMessages = latestMessages; this.latestMessages = latestMessages;
} }
public static void staticUpdateXML() {
if (server != null) {
server.updateXMLFiles();
}
}
/** /**
* Parses the race to create a snapshot, and places it in latestMessages. * Parses the race to create a snapshot, and places it in latestMessages.

@ -1,17 +1,16 @@
package mock.xml; package mock.xml;
import mock.model.NewPolars;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import shared.dataInput.RaceXMLReader; import shared.dataInput.RaceXMLReader;
import shared.enums.XMLFileType; import shared.enums.XMLFileType;
import shared.exceptions.InvalidRaceDataException; import shared.exceptions.InvalidRaceDataException;
import shared.exceptions.XMLReaderException; import shared.exceptions.XMLReaderException;
import shared.model.Bearing;
import shared.model.CompoundMark; import shared.model.CompoundMark;
import shared.model.Constants; import shared.model.Constants;
import shared.model.GPSCoordinate; import shared.model.GPSCoordinate;
import shared.xml.Race.XMLCompoundMark; import shared.xml.Race.*;
import shared.xml.Race.XMLLimit;
import shared.xml.Race.XMLMark;
import shared.xml.Race.XMLRace;
import shared.xml.XMLUtilities; import shared.xml.XMLUtilities;
import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBException;
@ -20,6 +19,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Comparator;
/** /**
* Helper Class for creating a Race XML * Helper Class for creating a Race XML
@ -125,6 +126,140 @@ public class RaceXMLCreator {
} }
} }
/**
* Rotates the race in a specified direction.
* @param s xml file name or contents.
* @param windSpeed speed that the wind is at.
* @param milliseconds time the race should take at fastest
* @return the new xml file as a string
* @throws XMLReaderException if the xml is not readable
* @throws InvalidRaceDataException if the race is invalid
*/
public static String scaleRaceSize(String s, double windSpeed, double milliseconds) throws XMLReaderException, InvalidRaceDataException {
try {
XMLRace race = XMLUtilities.xmlToClass(
s,
RaceXMLCreator.class.getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd"),
XMLRace.class);
scaleRace(race, windSpeed, milliseconds);
return XMLUtilities.classToXML(race);
} catch (ParserConfigurationException | IOException | SAXException | JAXBException e) {
throw new InvalidRaceDataException("Could not parse or marshall race data file.", e);
}
}
/**
* Gets an estimate of how long a race will take using an average Speed
* @param race race to estimate
* @param averageSpeed average speed that the boats move at
* @return the estimated amount of time it will take a boat to finish the race (skewed to minimum).
*/
public static double getRaceLength(XMLRace race, double averageSpeed){
double raceRoundingTime = 5000; //5 seconds to round a mark
double totalDistance = 0; //in nautical miles
XMLMark prevMark = null;
double avgSpeed = averageSpeed / 60 / 60; //knots is /hour
for (XMLCorner corner : race.getCompoundMarkSequence().getCorner()){
int index = corner.getCompoundMarkID() - 1;
XMLCompoundMark cm = race.getCourse().getCompoundMark().get(index);
XMLMark mark = cm.getMark().get(0);
if (prevMark != null){
totalDistance += getDistance(mark, prevMark);
}
prevMark = mark;
}
//total time = total dist / average speed + race extra rounding time * number of marks
double totalTime = totalDistance / avgSpeed * 1000 +
raceRoundingTime * race.getCompoundMarkSequence().getCorner().size();
return totalTime;
}
/**
* gets the destance between two marks
* @param a mark 1
* @param b mark 2
* @return
*/
private static double getDistance(XMLMark a, XMLMark b){
GPSCoordinate coorda = new GPSCoordinate(a.getTargetLat(), a.getTargetLng());
GPSCoordinate coordb = new GPSCoordinate(b.getTargetLat(), b.getTargetLng());
return GPSCoordinate.calculateDistanceNauticalMiles(coorda, coordb);
}
/**
* Scales the race based on the windspeed the race is running at and the amount of time it should be completed in.
* @param race Race to scale
* @param windSpeed windspeed of the race, this is used with the polars
* @param milliseconds milliseconds the race should take.
*/
private static void scaleRace(XMLRace race, double windSpeed, double milliseconds) {
GPSCoordinate center = getCenter(race);
//sort the compound marks
Collections.sort(race.getCompoundMarkSequence().getCorner(), (c1, c2) -> {
if (c1.getSeqID() < c2.getSeqID()) return -1;
if (c1.getSeqID() > c2.getSeqID()) return 1;
return 0;
});
//sort compound mark id
Collections.sort(race.getCourse().getCompoundMark(), (c1, c2) -> {
if (c1.getCompoundMarkID() < c2.getCompoundMarkID()) return -1;
if (c1.getCompoundMarkID() > c2.getCompoundMarkID()) return 1;
return 0;
});
//get the fastest time it would take.
double bestUpWindSpeed = NewPolars.setBestVMG(Bearing.fromDegrees(0), windSpeed, Bearing.fromDegrees(45)).getSpeed();
double bestDownWindSpeed = NewPolars.setBestVMG(Bearing.fromDegrees(0), windSpeed, Bearing.fromDegrees(45)).getSpeed();
double averageSpeed = (bestDownWindSpeed + bestUpWindSpeed) / 2;
double raceApproximateTime = getRaceLength(race, averageSpeed);
double scale = milliseconds / raceApproximateTime;
for (XMLCorner cm: race.getCompoundMarkSequence().getCorner()){
int index = cm.getCompoundMarkID() - 1;
XMLCompoundMark mark = race.getCourse().getCompoundMark().get(index);
for (XMLMark m: mark.getMark()){
scalePoint(m, center, scale);
}
}
for (XMLLimit limit: race.getCourseLimit().getLimit()){
scalePoint(limit, center, scale);
}
}
/**
* Scales a point from the the center(pivot)
* @param mark mark the scale
* @param center center as pivot
* @param scale scale to scale at.
*/
private static void scalePoint(XMLMark mark, GPSCoordinate center, double scale){
double latDiff = mark.getTargetLat() - center.getLatitude();
double longDiff = mark.getTargetLng() - center.getLongitude();
double latScaled = latDiff * scale + center.getLatitude();
double longScaled = longDiff * scale + center.getLongitude();
mark.setTargetLat(latScaled);
mark.setTargetLng(longScaled);
}
/**
* Scales a boundary from the center(pivot)
* @param limit boundary point
* @param center pivot
* @param scale scale
*/
private static void scalePoint(XMLLimit limit, GPSCoordinate center, double scale){
double latDiff = limit.getLat() - center.getLatitude();
double longDiff = limit.getLon() - center.getLongitude();
double latScaled = latDiff * scale + center.getLatitude();
double longScaled = longDiff * scale + center.getLongitude();
limit.setLat(latScaled);
limit.setLon(longScaled);
}
/** /**
* Converts a Race.CourseLimit.Limit to a GPS coordinate * Converts a Race.CourseLimit.Limit to a GPS coordinate
* @param limit limit to convert * @param limit limit to convert

@ -157,11 +157,10 @@ public class LatestMessages extends Observable {
return false; return false;
} else { } else {
RaceServer.staticUpdateXML();
return true; return true;
} }
} }
} }

@ -183,6 +183,10 @@ public class BoatXMLReader extends XMLReader implements BoatDataSource {
return sequenceNumber; return sequenceNumber;
} }
public void setSequenceNumber(int sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
@Override @Override
public void incrementSequenceNumber() { public void incrementSequenceNumber() {
sequenceNumber++; sequenceNumber++;

@ -22,7 +22,7 @@ public class EmptyBoatDataSource implements BoatDataSource {
private final Map<Integer, Mark> markerMap = new HashMap<>(); private final Map<Integer, Mark> markerMap = new HashMap<>();
private int sequenceNumber = 0; private int sequenceNumber = -1;
public EmptyBoatDataSource() { public EmptyBoatDataSource() {

@ -84,7 +84,7 @@ public class EmptyRaceDataSource implements RaceDataSource {
private RaceTypeEnum raceType = RaceTypeEnum.NOT_A_RACE_TYPE; private RaceTypeEnum raceType = RaceTypeEnum.NOT_A_RACE_TYPE;
private int sequenceNumber = 0; private int sequenceNumber = -1;
public EmptyRaceDataSource() { public EmptyRaceDataSource() {

@ -60,7 +60,7 @@ public class EmptyRegattaDataSource implements RegattaDataSource {
private int sequenceNumber = 0; private int sequenceNumber = -1;
public EmptyRegattaDataSource() { public EmptyRegattaDataSource() {

@ -499,6 +499,10 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource {
return sequenceNumber; return sequenceNumber;
} }
public void setSequenceNumber(int sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
@Override @Override
public void incrementSequenceNumber() { public void incrementSequenceNumber() {
sequenceNumber++; sequenceNumber++;

@ -217,6 +217,10 @@ public class RegattaXMLReader extends XMLReader implements RegattaDataSource {
return sequenceNumber; return sequenceNumber;
} }
public void setSequenceNumber(int sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
@Override @Override
public void incrementSequenceNumber() { public void incrementSequenceNumber() {
sequenceNumber++; sequenceNumber++;

@ -148,9 +148,11 @@ public abstract class RaceState extends Observable{
* @param raceDataSource New race data source. * @param raceDataSource New race data source.
*/ */
public void setRaceDataSource(RaceDataSource raceDataSource) { public void setRaceDataSource(RaceDataSource raceDataSource) {
this.raceDataSource = raceDataSource; if ((this.raceDataSource == null) || (raceDataSource.getSequenceNumber() > this.raceDataSource.getSequenceNumber())) {
this.getRaceClock().setStartingTime(raceDataSource.getStartDateTime()); this.raceDataSource = raceDataSource;
useLegsList(raceDataSource.getLegs()); this.getRaceClock().setStartingTime(raceDataSource.getStartDateTime());
useLegsList(raceDataSource.getLegs());
}
} }
/** /**
@ -158,7 +160,9 @@ public abstract class RaceState extends Observable{
* @param boatDataSource New boat data source. * @param boatDataSource New boat data source.
*/ */
public void setBoatDataSource(BoatDataSource boatDataSource) { public void setBoatDataSource(BoatDataSource boatDataSource) {
this.boatDataSource = boatDataSource; if ((this.boatDataSource == null) || (boatDataSource.getSequenceNumber() > this.boatDataSource.getSequenceNumber())) {
this.boatDataSource = boatDataSource;
}
} }
/** /**
@ -166,7 +170,9 @@ public abstract class RaceState extends Observable{
* @param regattaDataSource New regatta data source. * @param regattaDataSource New regatta data source.
*/ */
public void setRegattaDataSource(RegattaDataSource regattaDataSource) { public void setRegattaDataSource(RegattaDataSource regattaDataSource) {
this.regattaDataSource = regattaDataSource; if ((this.regattaDataSource == null) || (regattaDataSource.getSequenceNumber() > this.regattaDataSource.getSequenceNumber())) {
this.regattaDataSource = regattaDataSource;
}
} }

@ -0,0 +1,70 @@
package shared.utils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
/**
* A helper class that has functions to read information from a url to json object.
*/
public class JsonReader {
/**
* Reads all data from a Reader
* @param rd reader to read from
* @return string that the reader has currently read
* @throws IOException if the reader is invalid
*/
private static String readAll(Reader rd) throws IOException {
StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
return sb.toString();
}
/**
* Reads a Json Object from a URL
* @param url url to read from
* @return JSONObject that has been read
* @throws IOException if the reader cannot obtain information
* @throws JSONException if the read information is not json parsable.
*/
public static JSONObject readJsonFromUrl(String url) throws IOException, JSONException {
InputStream is = new URL(url).openStream();
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
String jsonText = readAll(rd);
JSONObject json = new JSONObject(jsonText);
return json;
} catch (JSONException e) {
return null;
} finally {
is.close();
}
}
/**
* Reads a Json Array from a URL
* @param url url to read from
* @return JSONArray that has been read
* @throws IOException if the reader cannot obtain information
* @throws JSONException if the read information is not json parsable.
*/
public static JSONArray readJsonFromUrlArray(String url) throws IOException, JSONException {
InputStream is = new URL(url).openStream();
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
String jsonText = readAll(rd);
JSONArray json = new JSONArray(jsonText);
return json;
} finally {
is.close();
}
}
}

@ -41,8 +41,8 @@ public class BoatsXMLMessageCommand implements Command {
@Override @Override
public void execute() { public void execute() {
if (boatDataSource.getSequenceNumber() > visualiserRace.getBoatDataSource().getSequenceNumber()) {
visualiserRace.setBoatDataSource(boatDataSource); visualiserRace.setBoatDataSource(boatDataSource);
}
} }
} }

@ -38,7 +38,9 @@ public class RaceXMLMessageCommand implements Command {
@Override @Override
public void execute() { public void execute() {
visualiserRace.setRaceDataSource(raceDataSource); if (raceDataSource.getSequenceNumber() > visualiserRace.getRaceDataSource().getSequenceNumber()) {
visualiserRace.setRaceDataSource(raceDataSource);
}
} }
} }

@ -37,8 +37,9 @@ public class RegattaXMLMessageCommand implements Command {
@Override @Override
public void execute() { public void execute() {
if (regattaDataSource.getSequenceNumber() > visualiserRace.getRegattaDataSource().getSequenceNumber()) {
visualiserRace.setRegattaDataSource(regattaDataSource); visualiserRace.setRegattaDataSource(regattaDataSource);
}
} }
} }

@ -34,17 +34,20 @@ public class XMLMessageCommandFactory {
switch (message.getXmlMsgSubType()) { switch (message.getXmlMsgSubType()) {
case BOAT: case BOAT:
BoatDataSource boatDataSource = new BoatXMLReader(message.getXmlMessage(), XMLFileType.Contents); BoatXMLReader boatDataSource = new BoatXMLReader(message.getXmlMessage(), XMLFileType.Contents);
boatDataSource.setSequenceNumber(message.getSequenceNumber());
return new BoatsXMLMessageCommand(boatDataSource, visualiserRace); return new BoatsXMLMessageCommand(boatDataSource, visualiserRace);
case RACE: case RACE:
RaceDataSource raceDataSource = new RaceXMLReader(message.getXmlMessage(), XMLFileType.Contents); RaceXMLReader raceDataSource = new RaceXMLReader(message.getXmlMessage(), XMLFileType.Contents);
raceDataSource.setSequenceNumber(message.getSequenceNumber());
return new RaceXMLMessageCommand(raceDataSource, visualiserRace); return new RaceXMLMessageCommand(raceDataSource, visualiserRace);
case REGATTA: case REGATTA:
RegattaDataSource regattaDataSource = new RegattaXMLReader(message.getXmlMessage(), XMLFileType.Contents); RegattaXMLReader regattaDataSource = new RegattaXMLReader(message.getXmlMessage(), XMLFileType.Contents);
regattaDataSource.setSequenceNumber(message.getSequenceNumber());
return new RegattaXMLMessageCommand(regattaDataSource, visualiserRace); return new RegattaXMLMessageCommand(regattaDataSource, visualiserRace);

@ -11,6 +11,7 @@ import mock.exceptions.EventConstructionException;
import network.Messages.Enums.RequestToJoinEnum; import network.Messages.Enums.RequestToJoinEnum;
import visualiser.app.App; import visualiser.app.App;
import visualiser.app.MatchBrowserSingleton; import visualiser.app.MatchBrowserSingleton;
import visualiser.network.HttpMatchBrowserHost;
import visualiser.network.MatchBrowserInterface; import visualiser.network.MatchBrowserInterface;
import java.io.IOException; import java.io.IOException;
@ -65,8 +66,11 @@ public class HostGameController extends Controller {
try { try {
App.game = new Event(false, currentMapIndex); App.game = new Event(false, currentMapIndex);
App.gameType = currentMapIndex; App.gameType = currentMapIndex;
HttpMatchBrowserHost matchBrowserHost = new HttpMatchBrowserHost();
new Thread(matchBrowserHost).start();
connectSocket("localhost", 4942); connectSocket("localhost", 4942);
alertMatchBrowser(); //alertMatchBrowser();
} catch (EventConstructionException e) { } catch (EventConstructionException e) {
Logger.getGlobal().log(Level.SEVERE, "Could not create Event.", e); Logger.getGlobal().log(Level.SEVERE, "Could not create Event.", e);

@ -15,11 +15,13 @@ import javafx.scene.control.Label;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial; import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.MeshView; import javafx.scene.shape.MeshView;
import mock.app.Event; import mock.app.Event;
import network.Messages.Enums.RaceStatusEnum; import network.Messages.Enums.RaceStatusEnum;
import network.Messages.Enums.RequestToJoinEnum; import network.Messages.Enums.RequestToJoinEnum;
import shared.model.Boat;
import visualiser.app.App; import visualiser.app.App;
import visualiser.gameController.ControllerClient; import visualiser.gameController.ControllerClient;
import visualiser.layout.SeaSurface; import visualiser.layout.SeaSurface;
@ -28,6 +30,7 @@ import visualiser.layout.View3D;
import visualiser.model.VisualiserBoat; import visualiser.model.VisualiserBoat;
import visualiser.model.VisualiserRaceEvent; import visualiser.model.VisualiserRaceEvent;
import visualiser.model.VisualiserRaceState; import visualiser.model.VisualiserRaceState;
import visualiser.network.HttpMatchBrowserHost;
import java.io.IOException; import java.io.IOException;
import java.net.Socket; import java.net.Socket;
@ -124,61 +127,69 @@ public class InGameLobbyController extends Controller {
@Override @Override
public void onChanged(Change change) { public void onChanged(Change change) {
Platform.runLater( Platform.runLater(() -> {
() -> { while (change.next()) {
while (change.next()){ if (change.wasAdded() || change.wasRemoved() || change.wasUpdated()) {
if (change.wasAdded() || change.wasRemoved() || change.wasUpdated()){ populateLobby();
try{ }
resetLobby(); }
int count = 0; });
int row = 0; }
for (VisualiserBoat boat :visualiserRaceEvent.getVisualiserRaceState().getBoats()) {
View3D playerBoatToSet = new View3D(); public void populateLobby() {
playerBoatToSet.setItems(subjects);
try{
playerContainer.add(playerBoatToSet, (count % 3) , row); resetLobby();
playerContainer.setMargin(playerBoatToSet, new Insets(10, 10, 10, 10)); int count = 0;
int row = 0;
SeaSurface sea = new SeaSurface(750, 200);
sea.setX(250); ArrayList<VisualiserBoat> copy = new ArrayList<>(visualiserRaceEvent.getVisualiserRaceState().getBoats());
sea.setZ(210);
subjects.add(sea); for (VisualiserBoat boat : copy) {
View3D playerBoatToSet = new View3D();
MeshView mesh = new MeshView(importer.getImport()); playerBoatToSet.setItems(subjects);
PhongMaterial boatColorMat = new PhongMaterial(boat.getColor());
mesh.setMaterial(boatColorMat); playerContainer.add(playerBoatToSet, (count % 3) , row);
Subject3D subject = new Subject3D(mesh,0); playerContainer.setMargin(playerBoatToSet, new Insets(10, 10, 10, 10));
subjects.add(subject);
SeaSurface sea = new SeaSurface(750, 200);
playerBoatToSet.setDistance(50); sea.setX(250);
playerBoatToSet.setYaw(45); sea.setZ(210);
playerBoatToSet.setPitch(20); subjects.add(sea);
MeshView mesh = new MeshView(importer.getImport());
PhongMaterial boatColorMat = new PhongMaterial(boat.getColor());
AnimationTimer rotate = new AnimationTimer() { mesh.setMaterial(boatColorMat);
@Override Subject3D subject = new Subject3D(mesh,0);
public void handle(long now) { subjects.add(subject);
subject.setHeading(subject.getHeading().getAngle() + 0.1);
} playerBoatToSet.setDistance(50);
}; playerBoatToSet.setYaw(45);
rotate.start(); playerBoatToSet.setPitch(20);
allPlayerLabels.get(count).setText(boat.getName());
allPlayerLabels.get(count).toFront();
count += 1; AnimationTimer rotate = new AnimationTimer() {
if (count > 2){ @Override
row = 1; public void handle(long now) {
} subject.setHeading(subject.getHeading().getAngle() + 0.1);
}
}
catch(ConcurrentModificationException e){
}
}
} }
};
rotate.start();
allPlayerLabels.get(count).setText(boat.getName());
allPlayerLabels.get(count).toFront();
count += 1;
if (count > 2){
row = 1;
} }
); }
}
catch(ConcurrentModificationException e){
e.printStackTrace();
}
} }
} }
/* /*
private void populatePlayers(ListChangeListener.Change change){ private void populatePlayers(ListChangeListener.Change change){
@ -191,7 +202,6 @@ public class InGameLobbyController extends Controller {
//Initialises the race clock. //Initialises the race clock.
initialiseRaceClock(this.visualiserRaceEvent.getVisualiserRaceState()); initialiseRaceClock(this.visualiserRaceEvent.getVisualiserRaceState());
//Starts the race countdown timer. //Starts the race countdown timer.
countdownTimer(); countdownTimer();
} }
@ -227,10 +237,9 @@ public class InGameLobbyController extends Controller {
new AnimationTimer() { new AnimationTimer() {
@Override @Override
public void handle(long arg0) { public void handle(long arg0) {
//Get the current race status. //Get the current race status.
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum(); RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
//Try catch for getting interval times //Try catch for getting interval times
try { try {
long interval = ChronoUnit.MILLIS.between(visualiserRaceEvent.getVisualiserRaceState().getRaceClock().getCurrentTime(), visualiserRaceEvent.getVisualiserRaceState().getRaceClock().getStartingTime()); long interval = ChronoUnit.MILLIS.between(visualiserRaceEvent.getVisualiserRaceState().getRaceClock().getCurrentTime(), visualiserRaceEvent.getVisualiserRaceState().getRaceClock().getStartingTime());

@ -1,6 +1,7 @@
package visualiser.Controllers; package visualiser.Controllers;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Button; import javafx.scene.control.Button;
@ -11,8 +12,12 @@ import javafx.scene.layout.AnchorPane;
import network.Messages.Enums.RequestToJoinEnum; import network.Messages.Enums.RequestToJoinEnum;
import javafx.scene.media.AudioClip; import javafx.scene.media.AudioClip;
import network.Messages.HostGame; import network.Messages.HostGame;
import org.json.JSONArray;
import org.json.JSONObject;
import shared.utils.JsonReader;
import visualiser.app.MatchBrowserSingleton; import visualiser.app.MatchBrowserSingleton;
import visualiser.model.RaceConnection; import visualiser.model.RaceConnection;
import visualiser.network.HttpMatchBrowserClient;
import visualiser.network.MatchBrowserLobbyInterface; import visualiser.network.MatchBrowserLobbyInterface;
import java.io.IOException; import java.io.IOException;
@ -41,17 +46,17 @@ public class LobbyController extends Controller {
private AudioClip sound; private AudioClip sound;
//the socket for match browser //the socket for match browser
private DatagramSocket udpSocket; private HttpMatchBrowserClient httpMatchBrowserClient;
private MatchBrowserLobbyInterface matchBrowserLobbyInterface;
public void initialize() { public void initialize() {
httpMatchBrowserClient = new HttpMatchBrowserClient();
new Thread(httpMatchBrowserClient, "Match Client").start();
// set up the connection table // set up the connection table
connections = FXCollections.observableArrayList();
customConnections = FXCollections.observableArrayList(); customConnections = FXCollections.observableArrayList();
//connections.add(new RaceConnection("localhost", 4942, "Local Game")); //connections.add(new RaceConnection("localhost", 4942, "Local Game"));
lobbyTable.setItems(connections); lobbyTable.setItems(httpMatchBrowserClient.connections);
gameNameColumn.setCellValueFactory(cellData -> cellData.getValue().gamenameProperty()); gameNameColumn.setCellValueFactory(cellData -> cellData.getValue().gamenameProperty());
hostNameColumn.setCellValueFactory(cellData -> cellData.getValue().hostnameProperty()); hostNameColumn.setCellValueFactory(cellData -> cellData.getValue().hostnameProperty());
statusColumn.setCellValueFactory(cellData -> cellData.getValue().statusProperty()); statusColumn.setCellValueFactory(cellData -> cellData.getValue().statusProperty());
@ -68,7 +73,6 @@ public class LobbyController extends Controller {
joinGameBtn.setDisable(true); joinGameBtn.setDisable(true);
spectateButton.setDisable(true); spectateButton.setDisable(true);
this.udpSocket = MatchBrowserSingleton.getInstance().getUdpSocket();
receiveMatchData(); receiveMatchData();
} }
@ -99,6 +103,7 @@ public class LobbyController extends Controller {
* @throws IOException socket error * @throws IOException socket error
*/ */
public void connectSocket(RequestToJoinEnum joinType) throws IOException { public void connectSocket(RequestToJoinEnum joinType) throws IOException {
httpMatchBrowserClient.interrupt();
RaceConnection connection = lobbyTable.getSelectionModel().getSelectedItem(); RaceConnection connection = lobbyTable.getSelectionModel().getSelectedItem();
Socket socket = new Socket(connection.getHostname(), connection.getPort()); Socket socket = new Socket(connection.getHostname(), connection.getPort());
InGameLobbyController iglc = (InGameLobbyController)loadScene("gameLobby.fxml"); InGameLobbyController iglc = (InGameLobbyController)loadScene("gameLobby.fxml");
@ -124,7 +129,7 @@ public class LobbyController extends Controller {
public void menuBtnPressed() throws IOException { public void menuBtnPressed() throws IOException {
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm()); sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
sound.play(); sound.play();
matchBrowserLobbyInterface.closeSocket(); httpMatchBrowserClient.interrupt();
loadScene("title.fxml"); loadScene("title.fxml");
} }
@ -148,6 +153,7 @@ public class LobbyController extends Controller {
} }
public void receiveMatchData(){ public void receiveMatchData(){
/*
matchBrowserLobbyInterface = new MatchBrowserLobbyInterface(); matchBrowserLobbyInterface = new MatchBrowserLobbyInterface();
try { try {
matchBrowserLobbyInterface.startReceivingHostData(new DatagramSocket(4941)); matchBrowserLobbyInterface.startReceivingHostData(new DatagramSocket(4941));
@ -160,17 +166,24 @@ public class LobbyController extends Controller {
matchBrowserLobbyInterface.addObserver(o); matchBrowserLobbyInterface.addObserver(o);
} catch (SocketException e) { } catch (SocketException e) {
System.err.println("Socket 4941 in use"); System.err.println("Socket 4941 in use");
} }*/
} }
/** /**
* Adds the games received from the server * Adds the games received from the server
*/ */
private void addServerGames() { private void addServerGames() {
connections.clear(); httpMatchBrowserClient.connections.addAll(customConnections);
connections.addAll(customConnections); httpMatchBrowserClient.connections.addListener(new ListChangeListener<RaceConnection>() {
@Override
public void onChanged(Change<? extends RaceConnection> c) {
refreshBtnPressed();
}
});
/*
for (HostGame game : matchBrowserLobbyInterface.getGames()) { for (HostGame game : matchBrowserLobbyInterface.getGames()) {
connections.add(new RaceConnection(game.getIp(), 4942, "Boat Game")); connections.add(new RaceConnection(game.getIp(), 4942, "Boat Game"));
} }*/
} }
} }

@ -429,12 +429,14 @@ public class RaceViewController extends Controller {
@Override @Override
public void handle(long now) { public void handle(long now) {
CompoundMark target = boat.getCurrentLeg().getEndCompoundMark(); CompoundMark target = boat.getCurrentLeg().getEndCompoundMark();
Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate()); if (target != null) {
Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate());
nextMarkArrow.setX(view3D.getPivot().getX()); nextMarkArrow.setX(view3D.getPivot().getX());
nextMarkArrow.setY(view3D.getPivot().getY()); nextMarkArrow.setY(view3D.getPivot().getY());
nextMarkArrow.setZ(view3D.getPivot().getZ() + 15); nextMarkArrow.setZ(view3D.getPivot().getZ() + 15);
nextMarkArrow.setHeading(headingToMark.degrees()); nextMarkArrow.setHeading(headingToMark.degrees());
}
} }
}; };
arrowToNextMark.start(); arrowToNextMark.start();

@ -8,6 +8,7 @@ import shared.dataInput.EmptyBoatDataSource;
import shared.dataInput.EmptyRaceDataSource; import shared.dataInput.EmptyRaceDataSource;
import shared.dataInput.EmptyRegattaDataSource; import shared.dataInput.EmptyRegattaDataSource;
import visualiser.gameController.ControllerClient; import visualiser.gameController.ControllerClient;
import visualiser.network.HttpMatchBrowserHost;
import visualiser.network.ServerConnection; import visualiser.network.ServerConnection;
import java.io.IOException; import java.io.IOException;
@ -115,5 +116,8 @@ public class VisualiserRaceEvent {
this.visualiserRaceServiceThread.interrupt(); this.visualiserRaceServiceThread.interrupt();
this.serverConnectionThread.interrupt(); this.serverConnectionThread.interrupt();
serverConnection.terminate(); serverConnection.terminate();
if (HttpMatchBrowserHost.httpMatchBrowserHost != null) {
HttpMatchBrowserHost.httpMatchBrowserHost.interrupt();
}
} }
} }

@ -94,7 +94,7 @@ public class VisualiserRaceState extends RaceState {
super.setRaceDataSource(raceDataSource); super.setRaceDataSource(raceDataSource);
if (getBoatDataSource() != null) { if (getBoatDataSource() != null) {
this.generateVisualiserBoats(this.boats, getBoatDataSource().getBoats(), raceDataSource.getParticipants(), unassignedColors); this.generateVisualiserBoats(this.boats, getBoatDataSource().getBoats(), getRaceDataSource().getParticipants(), unassignedColors);
} }
initialiseLegCompletionOrder(); initialiseLegCompletionOrder();
@ -109,7 +109,7 @@ public class VisualiserRaceState extends RaceState {
super.setBoatDataSource(boatDataSource); super.setBoatDataSource(boatDataSource);
if (getRaceDataSource() != null) { if (getRaceDataSource() != null) {
this.generateVisualiserBoats(this.boats, boatDataSource.getBoats(), getRaceDataSource().getParticipants(), unassignedColors); this.generateVisualiserBoats(this.boats, getBoatDataSource().getBoats(), getRaceDataSource().getParticipants(), unassignedColors);
} }
} }

@ -0,0 +1,42 @@
package visualiser.network;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.json.JSONArray;
import org.json.JSONObject;
import shared.utils.JsonReader;
import visualiser.model.RaceConnection;
import java.io.IOException;
/**
* Created by Gondr on 19/09/2017.
*/
public class HttpMatchBrowserClient extends Thread {
public ObservableList<RaceConnection> connections = FXCollections.observableArrayList();
/**
* Get all the matches that have been running in the past 5 seconds.
*/
@Override
public void run() {
while(!Thread.interrupted()) {
try {
JSONArray cons = JsonReader.readJsonFromUrlArray("http://api.umbrasheep.com/seng/get_matches/");
connections.clear();
for (int i = 0; i < cons.length(); i++) {
JSONObject con = (JSONObject) cons.get(i);
//using "ip_address" will give their public ip
connections.add(new RaceConnection((String) con.get("local_ip"), con.getInt("port"), "Boat Game"));
}
Thread.sleep(5000);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

@ -0,0 +1,115 @@
package visualiser.network;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;
/**
* Creates an Http connection that hosts a game
*/
public class HttpMatchBrowserHost extends Thread {
private HttpClient httpClient;
private List<NameValuePair> params;
public static HttpMatchBrowserHost httpMatchBrowserHost = null;
/**
* Constructor, this sends out the creation message of the race.
* the thread should be run as soon as possible as the race is only valid for 5 seconds
* until it requires a heartbeat from the start function.
* @throws IOException if the hosting url is unreachable.
*/
public HttpMatchBrowserHost() throws IOException {
httpMatchBrowserHost = this;
httpClient = HttpClients.createDefault();
Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
boolean matches = false;
String ip = "";
Pattern ipPattern = Pattern.compile("192.168.1.*");
while(e.hasMoreElements())
{
if (matches){
break;
}
NetworkInterface n = (NetworkInterface) e.nextElement();
Enumeration<InetAddress> ee = n.getInetAddresses();
while (ee.hasMoreElements())
{
InetAddress i = ee.nextElement();
matches = ipPattern.matcher(i.getHostAddress()).matches();
if (matches){
ip = i.getHostAddress();
//System.out.println(ip);
break;
}
}
}
// Request parameters and other properties.
params = new ArrayList<>(2);
params.add(new BasicNameValuePair("ip", ip));
params.add(new BasicNameValuePair("port", "4942"));
params.add(new BasicNameValuePair("magic", "Thomas and Seng"));
sendHttp("http://api.umbrasheep.com/seng/registermatch/");
}
/**
* Sends a post to a server.
* @param domain url of to send to
* @throws IOException if the url is unreachable.
*/
public void sendHttp(String domain) throws IOException {
HttpPost httppost = new HttpPost(domain);
httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
//Execute and get the response.
HttpResponse response = httpClient.execute(httppost);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
try {
// do something useful
} finally {
instream.close();
}
} else {
throw new IOException("No Response from Host");
}
}
/**
* THe host starts sending out heartbeat messages every 2 seconds.
*/
@Override
public void run() {
while(!Thread.interrupted()){
try {
sendHttp("http://api.umbrasheep.com/seng/keep_match_alive/");
Thread.sleep(2000);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

@ -26,8 +26,9 @@ public class MatchBrowserInterface {
public MatchBrowserInterface() { public MatchBrowserInterface() {
try {//132.181.16.13 is the ip of the CI as of 13/9/17 try {//132.181.16.13 is the ip of the CI as of 13/9/17
//this.IPAddress = InetAddress.getByName("132.181.16.13"); //InetAddress.getLocalHost();
//this.IPAddress = InetAddress.getByName("umbrasheep.com"); //InetAddress.getLocalHost(); //this.IPAddress = InetAddress.getByName("umbrasheep.com"); //InetAddress.getLocalHost();
this.IPAddress = InetAddress.getByName("132.181.16.13"); //InetAddress.getLocalHost(); this.IPAddress = InetAddress.getByName("191.101.233.116"); //InetAddress.getLocalHost();
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
e.printStackTrace(); e.printStackTrace();
} }

Loading…
Cancel
Save