Fixed connecting to hosts. Fixed issue34 and 35 to the point where they can be developed off of.

#refactor #bug #issue[34, 35]
This commit is contained in:
Calum
2017-08-02 00:26:57 +12:00
parent 908c0749cf
commit 87ef37a689
48 changed files with 355 additions and 319 deletions
@@ -9,7 +9,7 @@ import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import seng302.model.Player;
import seng302.model.Yacht;
import seng302.server.messages.BoatActionType;
import seng302.gameServer.server.messages.BoatActionType;
/**
* A Static class to hold information about the current state of the game (model)
@@ -5,8 +5,8 @@ import java.util.Stack;
import java.util.Timer;
import java.util.TimerTask;
import seng302.model.Player;
import seng302.server.messages.Heartbeat;
import seng302.server.messages.Message;
import seng302.gameServer.server.messages.Heartbeat;
import seng302.gameServer.server.messages.Message;
/**
* Send Heartbeat messages to connected player at a specified interval
@@ -2,7 +2,7 @@ package seng302.gameServer;
import java.util.Arrays;
import seng302.model.stream.packets.StreamPacket;
import seng302.server.messages.BoatActionType;
import seng302.gameServer.server.messages.BoatActionType;
public class ServerPacketParser {
@@ -25,16 +25,16 @@ import seng302.model.stream.packets.StreamPacket;
import seng302.model.stream.xml.generator.Race;
import seng302.model.stream.xml.generator.Regatta;
import seng302.utilities.XMLGenerator;
import seng302.server.messages.BoatActionType;
import seng302.server.messages.BoatLocationMessage;
import seng302.server.messages.BoatStatus;
import seng302.server.messages.BoatSubMessage;
import seng302.server.messages.Message;
import seng302.server.messages.RaceStatus;
import seng302.server.messages.RaceStatusMessage;
import seng302.server.messages.RaceType;
import seng302.server.messages.XMLMessage;
import seng302.server.messages.XMLMessageSubType;
import seng302.gameServer.server.messages.BoatActionType;
import seng302.gameServer.server.messages.BoatLocationMessage;
import seng302.gameServer.server.messages.BoatStatus;
import seng302.gameServer.server.messages.BoatSubMessage;
import seng302.gameServer.server.messages.Message;
import seng302.gameServer.server.messages.RaceStatus;
import seng302.gameServer.server.messages.RaceStatusMessage;
import seng302.gameServer.server.messages.RaceType;
import seng302.gameServer.server.messages.XMLMessage;
import seng302.gameServer.server.messages.XMLMessageSubType;
/**
* A class describing a single connection to a Client for the purposes of sending and receiving on
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
/**
* Created by kre39 on 12/07/17.
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
import java.util.HashMap;
import java.util.Map;
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
public class BoatLocationMessage extends Message {
private final int MESSAGE_SIZE = 56;
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
/**
* The current status of a boat
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
import java.nio.ByteBuffer;
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
/**
* Created by kre39 on 20/07/17.
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
public enum DeviceType {
UNKNOWN(0),
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
import java.nio.ByteBuffer;
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
public class Heartbeat extends Message {
private final int MESSAGE_SIZE = 4;
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
public class MarkRoundingMessage extends Message{
private final long MESSAGE_VERSION_NUMBER = 1;
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
/**
* Types of marks boats can round
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
/**
* Enum containing the types of messages
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
/**
* The types of race start status messages
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
public class RaceStartStatusMessage extends Message {
private final int MESSAGE_SIZE = 20;
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
/**
* The current status of the race
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
import java.util.List;
import java.util.zip.CRC32;
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
/**
* Enum containing the types of races
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
/**
* The status of a boat rounding a mark
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
/**
* The side the boat rounded the mark
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
public class XMLMessage extends Message{
private final MessageType MESSAGE_TYPE = MessageType.XML_MESSAGE;
@@ -1,4 +1,4 @@
package seng302.server.messages;
package seng302.gameServer.server.messages;
/**
* Enum containing the types of XML messages
@@ -1,4 +1,4 @@
package seng302.server.simulator;
package seng302.gameServer.server.simulator;
import seng302.model.GeoPoint;
import seng302.utilities.GeoUtility;
@@ -1,4 +1,4 @@
package seng302.server.simulator;
package seng302.gameServer.server.simulator;
import seng302.model.mark.CompoundMark;
@@ -1,4 +1,4 @@
package seng302.server.simulator;
package seng302.gameServer.server.simulator;
public enum RoundingType {
@@ -1,10 +1,10 @@
package seng302.server.simulator;
package seng302.gameServer.server.simulator;
import java.util.List;
import java.util.Observable;
import java.util.concurrent.ThreadLocalRandom;
import seng302.model.mark.Mark;
import seng302.server.simulator.parsers.RaceParser;
import seng302.gameServer.server.simulator.parsers.RaceParser;
import seng302.model.GeoPoint;
import seng302.utilities.GeoUtility;
@@ -1,4 +1,4 @@
package seng302.server.simulator.parsers;
package seng302.gameServer.server.simulator.parsers;
import org.w3c.dom.Document;
@@ -1,4 +1,4 @@
package seng302.server.simulator.parsers;
package seng302.gameServer.server.simulator.parsers;
import java.util.ArrayList;
import java.util.HashMap;
@@ -9,9 +9,9 @@ import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import seng302.model.mark.CompoundMark;
import seng302.server.simulator.Corner;
import seng302.gameServer.server.simulator.Corner;
import seng302.model.mark.Mark;
import seng302.server.simulator.RoundingType;
import seng302.gameServer.server.simulator.RoundingType;
/**
* Parses the race xml file to get course details
@@ -1,4 +1,4 @@
package seng302.server.simulator.parsers;
package seng302.gameServer.server.simulator.parsers;
import java.io.InputStream;
import java.io.StringReader;
@@ -1,4 +1,4 @@
package seng302.server.simulator.parsers;
package seng302.gameServer.server.simulator.parsers;
import java.util.ArrayList;
import java.util.List;
@@ -6,8 +6,8 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import seng302.server.simulator.Boat;
import seng302.server.simulator.Corner;
import seng302.gameServer.server.simulator.Boat;
import seng302.gameServer.server.simulator.Corner;
/**
* Parses the race xml file to get course details
@@ -9,7 +9,7 @@ import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import seng302.model.stream.xml.generator.Race;
import seng302.model.stream.xml.generator.Regatta;
import seng302.server.messages.XMLMessageSubType;
import seng302.gameServer.server.messages.XMLMessageSubType;
/**
* An XML generator to generate the Race, Boat, and Regatta XML dynamically
@@ -16,8 +16,8 @@ import javafx.application.Platform;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import seng302.model.stream.packets.StreamPacket;
import seng302.server.messages.BoatActionMessage;
import seng302.server.messages.Message;
import seng302.gameServer.server.messages.BoatActionMessage;
import seng302.gameServer.server.messages.Message;
/**
* A class describing a single connection to a Server for the purposes of sending and receiving on
@@ -130,7 +130,7 @@ public class ClientToServerThread implements Runnable {
} else {
streamPackets.add(new StreamPacket(type, payloadLength, timeStamp, payload));
for (ClientSocketListener csl : listeners)
Platform.runLater(csl::newPacket);
csl.newPacket();
}
} else {
clientLog("Packet has been dropped", 1);
@@ -147,6 +147,7 @@ public class ClientToServerThread implements Runnable {
clientLog(e.getMessage(), 1);
return;
}
// System.out.println("streamPackets = " + streamPackets.size());
}
closeSocket();
clientLog("Closed connection to Server", 0);
@@ -5,13 +5,13 @@ import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Map;
import java.util.TimeZone;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread;
import seng302.model.RaceState;
import seng302.model.Yacht;
@@ -22,8 +22,8 @@ import seng302.model.stream.parser.PositionUpdateData.DeviceType;
import seng302.model.stream.parser.RaceStatusData;
import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.stream.xml.parser.RegattaXMLData;
import seng302.server.messages.BoatActionMessage;
import seng302.server.messages.BoatActionType;
import seng302.gameServer.server.messages.BoatActionMessage;
import seng302.gameServer.server.messages.BoatActionType;
import seng302.utilities.StreamParser;
import seng302.utilities.XMLParser;
import seng302.visualiser.controllers.LobbyController;
@@ -62,12 +62,12 @@ public class GameClient {
ioe.printStackTrace();
System.out.println("Unable to connect to host...");
}
LobbyController lobbyController = loadLobby("/views/LobbyView.fxml");
socketThread.addStreamObserver(this::parsePackets);
LobbyController lobbyController = loadLobby();
lobbyController.setPlayerListSource(clientLobbyList);
lobbyController.disableReadyButton();
lobbyController.setTitle("Connected to host - IP : " + ipAddress + " Port : " + portNumber);
lobbyController.addCloseListener((exitCause) -> this.loadStartScreen());
socketThread.addStreamObserver(this::parsePackets);
}
public void runAsHost(String ipAddress, Integer portNumber) {
@@ -79,8 +79,8 @@ public class GameClient {
System.out.println("Unable to make local connection to host...");
}
socketThread.addStreamObserver(this::parsePackets);
LobbyController lobbyController = loadLobby("/views/LobbyView.fxml");
lobbyController.setPlayerListSource(GameState.getObservablePlayers());
LobbyController lobbyController = loadLobby();
lobbyController.setPlayerListSource(clientLobbyList);
lobbyController.setTitle("Hosting Lobby - IP : " + ipAddress + " Port : " + portNumber);
lobbyController.addCloseListener(exitCause -> {
if (exitCause == CloseStatus.READY) {
@@ -112,11 +112,10 @@ public class GameClient {
/**
* Loads a view of the lobby into the clients pane
*
* @param lobbyView fxml file for the desired lobby
* @return the lobby controller.
*/
private LobbyController loadLobby(String lobbyView) {
FXMLLoader fxmlLoader = new FXMLLoader(GameClient.class.getResource(lobbyView));
private LobbyController loadLobby() {
FXMLLoader fxmlLoader = new FXMLLoader(GameClient.class.getResource("/views/LobbyView.fxml"));
try {
holderPane.getChildren().clear();
holderPane.getChildren().add(fxmlLoader.load());
@@ -130,9 +129,11 @@ public class GameClient {
FXMLLoader fxmlLoader = new FXMLLoader(
RaceViewController.class.getResource("/views/RaceView.fxml"));
try {
Node node = fxmlLoader.load();
final Node node = fxmlLoader.load();
Platform.runLater(() -> {
holderPane.getChildren().clear();
holderPane.getChildren().add(node);
});
} catch (IOException e) {
e.printStackTrace();
}
@@ -173,13 +174,14 @@ public class GameClient {
break;
case BOAT_XML:
System.out.println("GOT SUM BOATS YAY :)");
allBoatsMap = XMLParser.parseBoats(
StreamParser.extractXmlMessage(packet)
);
clientLobbyList.clear();
allBoatsMap.forEach((id, boat) -> {
clientLobbyList.add(id + " " + boat.getBoatName());
System.out.println(id + " " + boat.getBoatName());
// System.out.println(id + " " + boat.getBoatName());
});
// startRaceIfAllDataReceived();
+42 -35
View File
@@ -1,11 +1,13 @@
package seng302.visualiser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.animation.AnimationTimer;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.geometry.Point2D;
import javafx.scene.Group;
@@ -70,12 +72,13 @@ public class GameView extends Pane {
private ImageView mapImage = new ImageView();
//FRAME RATE
private Double frameRate = 60.0;
private final long[] frameTimes = new long[30];
private int frameTimeIndex = 0;
private boolean arrayFilled = false;
private AnimationTimer timer;
private int NUM_SAMPLES = 10;
private final long[] frameTimes = new long[NUM_SAMPLES];
private Double frameRate = 60.0;
private int frameTimeIndex = 0;
private boolean arrayFilled = false;
private enum ScaleDirection {
HORIZONTAL,
@@ -98,9 +101,14 @@ public class GameView extends Pane {
}
private void initializeTimer () {
Arrays.fill(frameTimes, 1_000_000_000 / 60);
timer = new AnimationTimer() {
private long lastTime = 0;
private int FPSCount = 30;
private Double frameRate = 60.0;
private int index = 0;
private boolean arrayFilled = false;
private long sum = 1_000_000_000 / 3;
@Override
public void handle(long now) {
@@ -121,13 +129,15 @@ public class GameView extends Pane {
frameRate = 1_000_000_000.0 / elapsedNanosPerFrame;
if (FPSCount-- == 0) {
FPSCount = 30;
drawFps(frameRate.intValue());
drawFps(frameRate);
}
}
boatObjects.forEach((boat, boatObject) -> boatObject.updateLocation());
lastTime = now;
}
}
// Platform.runLater(() ->
// boatObjects.forEach((boat, boatObject) -> boatObject.updateLocation())
// );
}
};
}
@@ -175,10 +185,9 @@ public class GameView extends Pane {
*/
public void updateCourse(List<CompoundMark> newCourse, List<Corner> sequence) {
markerObjects = new HashMap<>();
final List<Gate> gates = new ArrayList<>();
Paint colour = Color.BLACK;
markers.getChildren().clear();
//Creates new markers
System.out.println(newCourse.size());
for (CompoundMark cMark : newCourse) {
//Set start and end colour
if (cMark.getId() == sequence.get(0).getCompoundMarkID()) {
@@ -189,26 +198,24 @@ public class GameView extends Pane {
//Create mark dots
for (Mark mark : cMark.getMarks()) {
makeAndBindMarker(mark, colour);
System.out.println("hi" + mark.getName());
}
//Create gate line
if (cMark.isGate()) {
for (int i = 1; i < cMark.getMarks().size(); i++) {
gates.add(
makeAndBindGate(
markerObjects.get(cMark.getSubMark(i)),
markerObjects.get(cMark.getSubMark(i+1)),
colour
)
);
}
}
colour = Color.BLACK;
System.out.println("cMark.toString() = " + cMark.toString());
}
//Set X,Y co-ordinates
//Scale race to markers if there is no border.
if (borderPoints == null) {
rescaleRace(new ArrayList<>(markerObjects.keySet()));
} else {
rescaleRace(new ArrayList<>(borderPoints));
}
//Move the Markers to initial position.
markerObjects.forEach(((mark, marker) -> {
@@ -216,7 +223,11 @@ public class GameView extends Pane {
marker.setCenterX(p2d.getX());
marker.setCenterY(p2d.getY());
}));
Platform.runLater(() -> {
markers.getChildren().clear();
markers.getChildren().addAll(gates);
markers.getChildren().addAll(markerObjects.values());
});
}
/**
@@ -241,8 +252,9 @@ public class GameView extends Pane {
* @param m1 The first Mark of the gate.
* @param m2 The second Mark of the gate.
* @param colour The desired colour of the gate.
* @return the new gate.
*/
private void makeAndBindGate(Marker m1, Marker m2, Paint colour) {
private Gate makeAndBindGate(Marker m1, Marker m2, Paint colour) {
Gate gate = new Gate(colour);
gate.startXProperty().bind(
m1.centerXProperty()
@@ -256,7 +268,7 @@ public class GameView extends Pane {
gate.endYProperty().bind(
m2.centerYProperty()
);
markers.getChildren().addAll(gate);
return gate;
}
/**
@@ -297,20 +309,22 @@ public class GameView extends Pane {
*/
public void setBoats(List<Yacht> yachts) {
BoatObject newBoat;
final List<Group> wakes = new ArrayList<>();
for (Yacht yacht : yachts) {
Paint colour = Colors.getColor();
newBoat = new BoatObject();
newBoat.setFill(colour);
boatObjects.put(yacht, newBoat);
createAndBindAnnotationBox(yacht, colour);
wakesGroup.getChildren().add(newBoat.getWake());
// wakesGroup.getChildren().add(newBoat.getWake());
wakes.add(newBoat.getWake());
boatObjectGroup.getChildren().add(newBoat);
trails.getChildren().add(newBoat.getTrail());
// TODO: 1/08/17 Make this less vile to look at.
yacht.addLocationListener((boat, lat, lon, heading, velocity) ->{
BoatObject bo = boatObjects.get(boat);
Point2D p2d = findScaledXY(lat, lon);
bo.moveTo(p2d.getX(), p2d.getY(), heading);
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity);
// annotations.get(boat).setLayoutX(p2d.getX());
// annotations.get(boat).setLayoutY(p2d.getY());
// annotations.get(boat).setLocation(100d, 100d);
@@ -323,24 +337,14 @@ public class GameView extends Pane {
});
}
annotationsGroup.getChildren().addAll(annotations.values());
Platform.runLater(() -> {
gameObjects.addAll(trails);
gameObjects.add(wakesGroup);
gameObjects.addAll(wakes);
gameObjects.addAll(annotationsGroup);
gameObjects.addAll(boatObjectGroup);
});
}
// private void updateBoatObject (Yacht correspondingYacht) {
// BoatObject bo = boatObjects.get(correspondingYacht);
// Point2D p2d = findScaledXY(lat, lon);
// bo.moveTo(p2d.getX(), p2d.getY(), heading);
// annotations.get(boat).moveTo(p2d.getX(), p2d.getY());
// bo.setTrajectory(
// heading,
// velocity,
// metersPerPixelX,
// metersPerPixelY);
// }
private void createAndBindAnnotationBox (Yacht yacht, Paint colour) {
AnnotationBox newAnnotation = new AnnotationBox();
newAnnotation.setFill(colour);
@@ -371,8 +375,8 @@ public class GameView extends Pane {
annotations.put(yacht, newAnnotation);
}
private void drawFps(int fps){
fpsDisplay.setText(String.format("%d FPS", fps));
private void drawFps(Double fps){
Platform.runLater(() -> fpsDisplay.setText(String.format("%d FPS", Math.round(fps))));
}
/**
@@ -383,9 +387,6 @@ public class GameView extends Pane {
private void findMinMaxPoint(List<GeoPoint> points) {
List<GeoPoint> sortedPoints = new ArrayList<>(points);
sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLat));
for (GeoPoint gp : sortedPoints) {
System.out.println(gp.getLat());
}
minLatPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng());
GeoPoint maxLat = sortedPoints.get(sortedPoints.size()-1);
maxLatPoint = new GeoPoint(maxLat.getLat(), maxLat.getLng());
@@ -572,5 +573,11 @@ public class GameView extends Pane {
playerYacht.getVelocityProperty(),
(velocity) -> String.format("Speed: %.2f ms", velocity.doubleValue())
);
Platform.runLater(() -> {
boatObjectGroup.getChildren().remove(boatObjects.get(playerYacht));
gameObjects.add(boatObjects.get(playerYacht));
annotationsGroup.getChildren().remove(annotations.get(playerYacht));
gameObjects.add(annotations.get(playerYacht));
});
}
}
@@ -3,6 +3,7 @@ package seng302.visualiser.controllers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
@@ -187,13 +188,12 @@ public class LobbyController {
lobbyListeners.add(listener);
}
// TODO: 1/08/17 could definitely do this in a cleaner way.
public void setPlayerListSource (ObservableList<String> players) {
this.players = players;
players.addListener((ListChangeListener<? super String>) (lcl) ->
initialiseListView()
Platform.runLater(this::initialiseListView)
);
initialiseListView();
Platform.runLater(this::initialiseListView);
}
public void disableReadyButton () {
@@ -3,14 +3,14 @@ package seng302.visualiser.controllers;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Point2D;
@@ -33,7 +33,6 @@ import javafx.scene.shape.Line;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
import javafx.util.StringConverter;
import seng302.model.RaceState;
import seng302.model.Yacht;
@@ -81,7 +80,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private RaceState raceState;
private Timeline timerTimeline;
private HashMap<Integer, Series<String, Double>> sparkLineData = new HashMap<>();
private Timer timer = new Timer();
private List<Series<String, Double>> sparkLineData = new ArrayList<>();
private ImportantAnnotationsState importantAnnotations;
public void initialize() {
@@ -112,9 +112,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
initialiseFPSCheckBox();
initialiseAnnotationSlider();
initialiseBoatSelectionComboBox();
initialiseSparkLine();
gameView = new GameView();
contentAnchorPane.getChildren().add(gameView);
Platform.runLater(() -> contentAnchorPane.getChildren().add(gameView));
gameView.setBoats(new ArrayList<>(participants.values()));
gameView.updateBorder(raceData.getCourseLimit());
gameView.updateCourse(
@@ -210,18 +211,15 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
* Used to add any new yachts into the race that may have started late or not have had data received yet
*/
private void updateSparkLine(){
// TODO: 2/08/17 there is about 0 chance of this working. Once we are keeping track of boat positions it can be fixed.
// Collect the racing yachts that aren't already in the chart
List<Yacht> sparkLineCandidates = new ArrayList<>();
// participants.forEach((id, yacht) ->{
// if (!sparkLineData.containsKey(id) && yacht.getPosition() != null && !yacht.getPosition().equals("-"))
// sparkLineCandidates.add(yacht);
// });
participants.forEach((id, yacht) -> sparkLineCandidates.add(yacht));
sparklineYAxis.setUpperBound(participants.size() + 1);
sparkLineData.clear();
List<Yacht> sparkLineCandidates = new ArrayList<>(participants.values());
// Create a new data series for new yachts
sparkLineCandidates.stream().filter(yacht -> yacht.getPositionInteger() != null).forEach(yacht -> {
sparkLineCandidates
.stream()
.filter(yacht -> yacht.getPositionInteger() != null)
.forEach(yacht -> {
Series<String, Double> yachtData = new Series<>();
yachtData.setName(yacht.getSourceId().toString());
yachtData.getData().add(
@@ -230,12 +228,11 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
1.0 + participants.size() - yacht.getPositionInteger()
)
);
sparkLineData.put(yacht.getSourceId(), yachtData);
sparkLineData.add(yachtData);
});
// Lambda function to sort the series in order of leg (later legs shown more to the right)
List<XYChart.Series<String, Double>> positions = new ArrayList<>(sparkLineData.values());
positions.sort((o1, o2) -> {
sparkLineData.sort((o1, o2) -> {
Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size()-1).getXValue());
Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size()-1).getXValue());
if (leg2 < leg1){
@@ -244,26 +241,30 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
return -1;
}
});
// Adds the new data series to the sparkline (and set the colour of the series)
raceSparkLine.setCreateSymbols(false);
positions
Platform.runLater(() -> {
sparkLineData
.stream()
.filter(spark -> !raceSparkLine.getData().contains(spark))
.forEach(spark -> {
raceSparkLine.getData().add(spark);
spark.getNode().lookup(".chart-series-line").setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName()));
});
});
}
private void initialiseSparkLine() {
sparklineYAxis.setUpperBound(participants.size() + 1);
raceSparkLine.setCreateSymbols(false);
}
/**
* Updates the yachts sparkline of the desired yacht and using the new leg number
* @param yacht The yacht to be updated on the sparkline
* @param legNumber the leg number that the position will be assigned to
*/
public void updateYachtPositionSparkline(Yacht yacht, Integer legNumber){
for (XYChart.Series<String, Double> positionData : sparkLineData.values()) {
void updateYachtPositionSparkline(Yacht yacht, Integer legNumber){
for (XYChart.Series<String, Double> positionData : sparkLineData) {
positionData.getData().add(
new Data<>(
Integer.toString(legNumber),
@@ -305,23 +306,16 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
* Updates of each of these attributes are called ONCE EACH SECOND
*/
private void initializeUpdateTimer() {
timerTimeline = new Timeline();
timerTimeline.setCycleCount(Timeline.INDEFINITE);
// Run timer update every second
timerTimeline.getKeyFrames().add(
new KeyFrame(Duration.seconds(1),
event -> {
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
updateRaceTime();
updateWindDirection();
updateOrder();
updateBoatSelectionComboBox();
updateSparkLine();
})
);
// Start the timer
timerTimeline.playFromStart();
}
}, 0, 1000);
}
/**
* Iterates over all corners until ones SeqID matches with the yachts current leg number.
@@ -331,6 +325,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
* @return The next Mark or null if none found
*/
private Mark getNextMark(BoatObject bg) {
// TODO: 1/08/17 Move to GameView
//
// Integer legNumber = bg.getYacht().getLegNumber();
// List<Corner> markSequence = courseData.getMarkSequence();
@@ -346,6 +341,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
// return courseData.getCompoundMarks().get(corner.getCompoundMarkID());
// }
// }
// return null;
return null;
}
@@ -376,13 +372,13 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
* section
*/
private void updateOrder() {
positionVbox.getChildren().clear();
// positionVbox.getChildren().removeAll();
// positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
// list of racing yacht id
List<Yacht> sorted = new ArrayList<>(participants.values());
sorted.sort(Comparator.comparingInt(Yacht::getPositionInteger));
List<Text> vboxEntries = new ArrayList<>();
for (Yacht yacht : sorted) {
// System.out.println("yacht == null " + String.valueOf(yacht == null));
@@ -390,17 +386,20 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
Text textToAdd = new Text(yacht.getPositionInteger() + ". " +
yacht.getShortName() + " (Finished)");
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
positionVbox.getChildren().add(textToAdd);
vboxEntries.add(textToAdd);
} else {
Text textToAdd = new Text(yacht.getPositionInteger() + ". " +
yacht.getShortName() + " ");
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
textToAdd.setStyle("");
positionVbox.getChildren().add(textToAdd);
vboxEntries.add(textToAdd);
}
// System.out.println("finished a loop :))))))))))))");
}
Platform.runLater(() ->
positionVbox.getChildren().setAll(vboxEntries)
);
// participants.forEach((id, yacht) ->{
// Text textToAdd = new Text(yacht.getPosition() + ". " +
// yacht.getShortName() + " ");
@@ -411,7 +410,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
}
// private void updateLaylines(BoatObject bg) {
private void updateLaylines(BoatObject bg) {
// TODO: 1/08/17 move to GameView
//
// Mark nextMark = getNextMark(bg);
// Boolean isUpwind = null;
@@ -472,7 +472,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
// }
// }
// }
// }
}
private Point2D getPointRotation(Point2D ref, Double distance, Double angle){
@@ -517,17 +517,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
});
}
/**
* Grabs the yachts currently in the race as from the StreamParser and sets them to be selectable
* in the yacht selection combo box
*/
private void updateBoatSelectionComboBox() {
ObservableList<Yacht> observableYachts = FXCollections.observableArrayList();
observableYachts.addAll(participants.values());
yachtSelectionComboBox.setItems(observableYachts);
}
/**
* Display the list of yachts in the order they finished the race
*/
@@ -2,6 +2,7 @@ package seng302.visualiser.fxObjects;
import java.util.HashMap;
import java.util.Map;
import javafx.application.Platform;
import javafx.beans.value.ObservableValue;
import javafx.scene.CacheHint;
import javafx.scene.Group;
@@ -13,7 +14,7 @@ import javafx.scene.text.Text;
/**
* Grouping of string objects over a semi transparent background.
*/
public class AnnotationBox extends Group{
public class AnnotationBox extends Group {
@FunctionalInterface
public interface AnnotationFormatter<T> {
@@ -41,7 +42,7 @@ public class AnnotationBox extends Group{
this.source = source;
this.format = formatter;
source.addListener((obs, oldVal, newVal) ->
text.setText(format.transformString(newVal))
Platform.runLater(() -> text.setText(format.transformString(newVal)))
);
}
@@ -79,6 +80,9 @@ public class AnnotationBox extends Group{
private Map<String, Annotation> annotationsByName = new HashMap<>();
/**
* Creates an empty annotation box. The box is offset from (0,0) by (17, -38).
*/
public AnnotationBox() {
this.setCache(true);
background.setX(BACKGROUND_X);
@@ -95,34 +99,51 @@ public class AnnotationBox extends Group{
this.getChildren().add(background);
}
/**
* Adds an annotation to the box. Use the name to reference the annotation for removal or\
* changing visibility.
* @param annotationName the name of the annotation.
* @param annotation the annotation.
*/
public void addAnnotation (String annotationName, Annotation annotation) {
annotationsByName.put(annotationName, annotation);
Platform.runLater(() -> {
this.getChildren().add(annotation.getText());
visibleAnnotations++;
update();
});
}
/**
* Adds an annotation with a constant text.
* @param annotationName The name of the annotation. Will be used to reference it later.
* @param annotationText The desired text.
*/
public void addAnnotation (String annotationName, String annotationText) {
Text text = getTextObject();
annotationsByName.put(annotationName, new Annotation(text, annotationText));
this.getChildren().add(text);
visibleAnnotations++;
update();
}
public <E> void addAnnotation (String annotationName, ObservableValue<E> observable) {
addAnnotation(annotationName, observable, E::toString);
addAnnotation(annotationName, new Annotation(text, annotationText));
}
/**
* Adds an annotation with the given name. The annotation will contain the value of the given
* ObservableValue. The formatter should return a String and takes an object of the same type as
* the ObservableValue as a parameter. The String is how you want the annotation to look.
* @param annotationName The annotation name.
* @param observable The observable value the annotation will display.
* @param formatter A formatting function for the observable value.
* @param <E> The type of ObservableValue.
*/
public <E> void addAnnotation (String annotationName, ObservableValue<E> observable,
AnnotationFormatter<E> formatter) {
Text newText = getTextObject();
annotationsByName.put(annotationName, new Annotation<>(newText, observable, formatter));
this.getChildren().add(newText);
visibleAnnotations++;
update();
addAnnotation(annotationName, new Annotation<>(newText, observable, formatter));
}
/**
* Sets the visibility of the annotation with the given name if it exists.
* @param annotationName The name of the annotation
* @param visibility the desired visibility
*/
public void setAnnotationVisibility (String annotationName, boolean visibility) {
if (annotationsByName.containsKey(annotationName)) {
Text textField = annotationsByName.get(annotationName).text;
@@ -138,68 +159,54 @@ public class AnnotationBox extends Group{
}
}
/**
* Removes the annotation with the given name if it exits.
* @param annotationName The name given when the annotation was created.
*/
public void removeAnnotation (String annotationName) {
if (annotationName.contains(annotationName)) {
Platform.runLater(() -> {
this.getChildren().remove(annotationsByName.remove(annotationName).getText());
annotationsByName.remove(annotationName);
visibleAnnotations--;
update();
});
annotationsByName.remove(annotationName);
}
}
/**
* Moves the annotation.
* @param x x location
* @param y y location
*/
public void setLocation (double x, double y) {
// double dx = x - this.getLayoutX();
// double dy = y - this.getLayoutY();
// this.relocate(x, y);
//// this.relocate(x, y);
// for (Node n : this.getChildren()) {
// n.relocate(
// n.getLayoutX() + dx,
// n.getLayoutY() + dy
// );
//// n.setLayoutX(n.getLayoutX() + dx);
//// n.setLayoutY(n.getLayoutY() + dy);
// }
// update();
this.relocate(x + BACKGROUND_X, y + BACKGROUND_Y);
// for (int i = 1; i <= visibleAnnotations; i++) {
// Text text = (Text) this.getChildren().get(i);
// if (text.visibleProperty().get()) {
// text.setLayoutX(x + X_OFFSET_TEXT);
// text.setLayoutY(y + Y_OFFSET_TEXT_INIT * Y_OFFSET_PER_TEXT * (i + 1));
// }
// }
// moveTexts(x, y);
// for (Node n : this.getChildren()) {
// n.relocate(x, y);
// }
Platform.runLater(()-> this.relocate(x + BACKGROUND_X, y + BACKGROUND_Y));
}
/**
* Changes the width of the annotation box. Default is 145.
* @param width new width.
*/
public void setWidth (double width) {
backgroundWidth = width;
background.setWidth(backgroundWidth);
Platform.runLater(() -> background.setWidth(backgroundWidth));
}
private void update () {
background.setVisible(visibleAnnotations != 0);
background.setHeight(Math.abs(BACKGROUND_X) + TEXT_BUFFER + BACKGROUND_H_PER_TEXT * visibleAnnotations);
// System.out.println("visibleAnnotations = " + visibleAnnotations);
for (int i = 1; i <= visibleAnnotations; i++) {
Text text = (Text) this.getChildren().get(i);
if (text.visibleProperty().get()) {
// System.out.println("AYY LMAO");
// System.out.println("text.getText() = " + text.getText());
//// System.out
//// .println("text.visibleProperty().get() = " + text.visibleProperty().get());
// System.out.println(text.getLayoutX());
// System.out.println(background.getLayoutX());
text.setX(X_OFFSET_TEXT);
text.setY(Y_OFFSET_TEXT_INIT + Y_OFFSET_PER_TEXT * i);
// });
}
}
}
/**
* Return a text object with caching and a color applied
*
* Returns a text object for an annotation.
* @return The text object
*/
private Text getTextObject() {
@@ -211,6 +218,10 @@ public class AnnotationBox extends Group{
return text;
}
/**
* Set the colour of the annotation box's border and text colour.
* @param value desired colour.
*/
public void setFill (Paint value) {
theme = value;
background.setStroke(theme);
@@ -1,13 +1,16 @@
package seng302.visualiser.fxObjects;
import java.util.ArrayList;
import javafx.application.Platform;
import javafx.geometry.Point2D;
import javafx.scene.CacheHint;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Polyline;
import javafx.scene.transform.Rotate;
/**
@@ -28,15 +31,16 @@ public class BoatObject extends Group {
private double yVelocity;
private double lastHeading;
//Graphical objects
private Group lineGroup = new Group();
private Polyline trail = new Polyline();
private Polygon boatPoly;
private Wake wake;
private Line leftLayLine;
private Line rightLayline;
private Double distanceTravelled = 0.0;
private double distanceTravelled, lastRotation;
private Point2D lastPoint;
private Paint colour = Color.BLACK;
private Boolean isSelected = true; //All boats are initialised as selected
private Boolean isSelected, destinationSet; //All boats are initialised as selected
private boolean isPlayer = false;
/**
* Creates a BoatGroup with the default triangular boat polygon.
@@ -55,7 +59,6 @@ public class BoatObject extends Group {
* polygon.
*/
public BoatObject(double... points) {
this.colour = colour;
initChildren(points);
}
@@ -66,7 +69,6 @@ public class BoatObject extends Group {
* polygon.
*/
private void initChildren(double... points) {
// this.colour = color;
boatPoly = new Polygon(points);
boatPoly.setFill(colour);
boatPoly.setFill(this.colour);
@@ -88,44 +90,54 @@ public class BoatObject extends Group {
leftLayLine = new Line();
rightLayline = new Line();
trail.getStrokeDashArray().setAll(5d, 10d);
trail.setCache(true);
wake = new Wake(0, -BOAT_HEIGHT);
wake.setVisible(true);
super.getChildren().addAll(boatPoly);//, annotationBox);
}
public void setFill (Paint value) {
this.colour = value;
boatPoly.setFill(colour);
trail.setStroke(colour);
}
/**
* Moves the boat and its children annotations from its current coordinates by specified
* amounts.
*
* @param dx The amount to move the X coordinate by
* @param dy The amount to move the Y coordinate by
*/
private void moveGroupBy(double dx, double dy) {
boatPoly.setLayoutX(boatPoly.getLayoutX() + dx);
boatPoly.setLayoutY(boatPoly.getLayoutY() + dy);
wake.setLayoutX(wake.getLayoutX() + dx);
wake.setLayoutY(wake.getLayoutY() + dy);
}
/**
* Moves the boat and its children annotations to coordinates specified
*
* @param x The X coordinate to move the boat to
* @param y The Y coordinate to move the boat to
*/
public void moveTo(double x, double y, double rotation) {
public void moveTo(double x, double y, double rotation, double velocity) {
Double dx = Math.abs(boatPoly.getLayoutX() - x);
Double dy = Math.abs(boatPoly.getLayoutY() - y);
Platform.runLater(() -> {
rotateTo(rotation);
boatPoly.setLayoutX(x);
boatPoly.setLayoutY(y);
wake.setLayoutX(x);
wake.setLayoutY(y);
wake.rotate(rotation);
});
wake.setRotation(rotation, velocity);
// rotateTo(rotation);
// boatPoly.setLayoutX(x);
// boatPoly.setLayoutY(y);
// wake.setLayoutX(x);
// wake.setLayoutY(y);
// wake.rotate(rotation);
// wake.setRotation(rotation, groundSpeed);
// isStopped = false;
// destinationSet = true;
lastRotation = rotation;
distanceTravelled += Math.sqrt((dx * dx) + (dy * dy));
if (distanceTravelled > 15 && isPlayer) {
distanceTravelled = 0d;
Platform.runLater(() -> trail.getPoints().addAll(x, y));
}
}
private void rotateTo(double rotation) {
@@ -133,31 +145,31 @@ public class BoatObject extends Group {
}
public void updateLocation() {
double dx = xVelocity / 60;
double dy = yVelocity / 60;
distanceTravelled += Math.abs(dx) + Math.abs(dy);
moveGroupBy(dx, dy);
if (distanceTravelled > 70) {
distanceTravelled = 0d;
if (lastPoint != null) {
Line l = new Line(
lastPoint.getX(),
lastPoint.getY(),
boatPoly.getLayoutX(),
boatPoly.getLayoutY()
);
l.getStrokeDashArray().setAll(3d, 7d);
l.setStroke(colour);
l.setCache(true);
l.setCacheHint(CacheHint.SPEED);
lineGroup.getChildren().add(l);
}
lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY());
}
wake.updatePosition();
// double dx = xVelocity / 60;
// double dy = yVelocity / 60;
//
// distanceTravelled += Math.abs(dx) + Math.abs(dy);
// moveGroupBy(dx, dy);
//
// if (distanceTravelled > 70) {
// distanceTravelled = 0d;
//
// if (lastPoint != null) {
// Line l = new Line(
// lastPoint.getX(),
// lastPoint.getY(),
// boatPoly.getLayoutX(),
// boatPoly.getLayoutY()
// );
// l.getStrokeDashArray().setAll(3d, 7d);
// l.setStroke(colour);
// l.setCache(true);
// l.setCacheHint(CacheHint.SPEED);
// lineGroup.getChildren().add(l);
// }
// lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY());
// }
// wake.updatePosition();
}
// /**
@@ -203,16 +215,16 @@ public class BoatObject extends Group {
public void setVisibility (boolean teamName, boolean velocity, boolean estTime, boolean legTime,
boolean trail, boolean wake) {
// boatAnnotations.setVisible(teamName, velocity, estTime, legTime);
this.wake.setVisible(wake);
this.lineGroup.setVisible(trail);
// this.wake.setVisible(wake);
this.trail.setVisible(trail);
}
public void setLineGroupVisible(Boolean visible) {
lineGroup.setVisible(visible);
trail.setVisible(visible);
}
public void setWakeVisible(Boolean visible) {
wake.setVisible(visible);
// wake.setVisible(visible);
}
public void setLayLinesVisible(Boolean visible) {
@@ -236,8 +248,8 @@ public class BoatObject extends Group {
return wake;
}
public Group getTrail() {
return lineGroup;
public Node getTrail() {
return trail;
}
public Double getBoatLayoutX() {
@@ -260,6 +272,7 @@ public class BoatObject extends Group {
);
boatPoly.setStroke(Color.BLACK);
boatPoly.setStrokeWidth(3);
isPlayer = true;
}
public void setTrajectory(double heading, double velocity) {
@@ -1,5 +1,6 @@
package seng302.visualiser.fxObjects;
import javafx.application.Platform;
import javafx.scene.CacheHint;
import javafx.scene.Group;
import javafx.scene.paint.Color;
@@ -41,7 +42,11 @@ public class Wake extends Group {
arc.setCache(true);
arc.setCacheHint(CacheHint.ROTATE);
arc.setType(ArcType.OPEN);
arc.setStroke(new Color(0.18, 0.7, 1.0, 1.0 + (-0.99 / numWakes * i)));
arc.setStroke(
new Color(
0.18, 0.7, 1.0, 1.0 + (-0.99 / numWakes * i)
)
);
arc.setStrokeWidth(3.0);
arc.setStrokeLineCap(StrokeLineCap.ROUND);
arc.setFill(new Color(0.0, 0.0, 0.0, 0.0));
@@ -55,7 +60,15 @@ public class Wake extends Group {
void setRotation (double rotation, double velocity) {
// if (Math.abs(rotations[0] - rotation) > 20) {
Platform.runLater(() -> {
rotate(rotation);
double rad = (14 / numWakes) + velocity;
for (Arc arc : arcs) {
arc.setRadiusX(rad);
arc.setRadiusY(rad);
rad += (14 / numWakes) + (velocity / 2.5);
}
});
// } else {
// rotations[0] = rotation;
// ((Rotate) arcs[0].getTransforms().get(0)).setAngle(rotation);
@@ -78,12 +91,12 @@ public class Wake extends Group {
// }
// }
double rad = (14 / numWakes) + velocity;
for (Arc arc : arcs) {
arc.setRadiusX(rad);
arc.setRadiusY(rad);
rad += (14 / numWakes) + (velocity / 2.5);
}
// double rad = (14 / numWakes) + velocity;
// for (Arc arc : arcs) {
// arc.setRadiusX(rad);
// arc.setRadiusY(rad);
// rad += (14 / numWakes) + (velocity / 2.5);
// }
}
/**
@@ -1,9 +1,9 @@
package seng302.server;
package seng302.gameServer.server;
import static junit.framework.TestCase.assertEquals;
import org.junit.Test;
import seng302.server.messages.BoatLocationMessage;
import seng302.gameServer.server.messages.BoatLocationMessage;
/**
* Test conversions used by the boat location messages
@@ -1,10 +1,10 @@
package seng302.server;
package seng302.gameServer.server;
import static junit.framework.TestCase.assertTrue;
import org.junit.Test;
import seng302.server.messages.Header;
import seng302.server.messages.MessageType;
import seng302.gameServer.server.messages.Header;
import seng302.gameServer.server.messages.MessageType;
/**
* Tests message header
@@ -1,12 +1,12 @@
package seng302.server;
package seng302.gameServer.server;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
import org.junit.Test;
import seng302.server.messages.Message;
import seng302.server.messages.XMLMessage;
import seng302.server.messages.XMLMessageSubType;
import seng302.gameServer.server.messages.Message;
import seng302.gameServer.server.messages.XMLMessage;
import seng302.gameServer.server.messages.XMLMessageSubType;
public class TestMessage {
private static int XML_MESSAGE_LEN = 14;
@@ -1,4 +1,4 @@
package seng302.server.simulator;
package seng302.gameServer.server.simulator;
import static org.junit.Assert.assertEquals;