diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 00000000..e7bedf33 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/racevisionGame/pom.xml b/racevisionGame/pom.xml index efc88c09..f77e5ea3 100644 --- a/racevisionGame/pom.xml +++ b/racevisionGame/pom.xml @@ -104,7 +104,7 @@ - mock.app.App + visualiser.app.App ${maven.compiler.source} ${maven.compiler.target} diff --git a/racevisionGame/src/main/java/mock/app/App.java b/racevisionGame/src/main/java/mock/app/App.java deleted file mode 100644 index 06f628d5..00000000 --- a/racevisionGame/src/main/java/mock/app/App.java +++ /dev/null @@ -1,54 +0,0 @@ -package mock.app; - - -import javafx.application.Application; -import javafx.stage.Stage; -import mock.dataInput.PolarParser; -import mock.model.Polars; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import shared.dataInput.XMLReader; -import shared.enums.XMLFileType; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerException; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; - -public class App extends Application { - - /** - * Entry point for running the programme - * - * @param args for starting the programme - */ - public static void main(String[] args) { - launch(args); - } - - @Override - public void start(Stage primaryStage) { - try { - Polars boatPolars = PolarParser.parse("mock/polars/acc_polars.csv"); - - String regattaXML = XMLReader.readXMLFileToString("mock/mockXML/regattaTest.xml", StandardCharsets.UTF_8); - String raceXML = XMLReader.readXMLFileToString("mock/mockXML/raceTest.xml", StandardCharsets.UTF_8); - String boatXML = XMLReader.readXMLFileToString("mock/mockXML/boatTest.xml", StandardCharsets.UTF_8); - - Event raceEvent = new Event(raceXML, regattaXML, boatXML, XMLFileType.Contents, boatPolars); - raceEvent.start(); - - } catch (Exception e) { - //Catch all exceptions, print, and exit. - e.printStackTrace(); - System.exit(1); - } - } - - - -} diff --git a/racevisionGame/src/main/java/mock/app/ConnectionAcceptor.java b/racevisionGame/src/main/java/mock/app/ConnectionAcceptor.java new file mode 100644 index 00000000..97a974a9 --- /dev/null +++ b/racevisionGame/src/main/java/mock/app/ConnectionAcceptor.java @@ -0,0 +1,214 @@ +package mock.app; + +import network.Messages.Enums.XMLMessageType; +import network.Messages.LatestMessages; +import network.Messages.XMLMessage; +import org.mockito.Mock; +import visualiser.gameController.ControllerServer; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Array; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; + +/** + * Connection acceptor for multiple clients + */ +public class ConnectionAcceptor implements Runnable { + + + /** + * Port to expose server on. + */ + private int serverPort = 4942; + /** + * Socket used to listen for clients on. + */ + private ServerSocket serverSocket; + //mock outputs + private ArrayBlockingQueue mockOutputList = new ArrayBlockingQueue<>(16, true); + //latest messages + private LatestMessages latestMessages; + //Acknowledgement number for packets + private int ackNumber = 0; + //race xml sequence number + private short raceXMLSequenceNumber; + //boat xml sequence number + private short boatXMLSequenceNumber; + //regatta xml sequence number + private short regattaXMLSequenceNumber; + + /** + * Connection Acceptor Constructor + * @param latestMessages Latest messages to be sent + * @throws IOException if a server socket cannot be instantiated. + */ + public ConnectionAcceptor(LatestMessages latestMessages) throws IOException { + + this.latestMessages =latestMessages; + this.serverSocket = new ServerSocket(serverPort); + CheckClientConnection checkClientConnection = new CheckClientConnection(mockOutputList); + new Thread(checkClientConnection).start(); + } + + public String getAddress() throws UnknownHostException { + return InetAddress.getLocalHost().getHostAddress(); + } + + public int getServerPort() { + return serverPort; + } + + /** + * Run the Acceptor + */ + @Override + public void run() { + while(true){//should be connections not filled up + try { + System.out.println("Waiting for a connection...");//TEMP DEBUG REMOVE + Socket mockSocket = serverSocket.accept(); + DataOutputStream outToVisualiser = new DataOutputStream(mockSocket.getOutputStream()); + MockOutput mockOutput = new MockOutput(latestMessages, outToVisualiser); + ControllerServer controllerServer = new ControllerServer(mockSocket); + new Thread(mockOutput).start(); + new Thread(controllerServer).start(); + mockOutputList.add(mockOutput); + System.out.println(String.format("%d number of Visualisers Connected.", mockOutputList.size())); + } catch (IOException e) { + e.printStackTrace(); + } + + } + } + + /** + * Nested class to remove disconnected clients + */ + class CheckClientConnection implements Runnable{ + + private ArrayBlockingQueue mocks; + + /** + * Constructor + * @param mocks Mocks "connected" + */ + public CheckClientConnection(ArrayBlockingQueue mocks){ + this.mocks = mocks; + } + + /** + * Run the remover. + */ + @Override + public void run() { + double timeSinceLastHeartBeat = System.currentTimeMillis(); + while(true) { + //System.out.println(mocks.size());//used to see current amount of visualisers connected. + ArrayBlockingQueue m = new ArrayBlockingQueue(16, true, mocks); + for (MockOutput mo : m) { + try { + mo.sendHeartBeat(); + } catch (IOException e) { + mocks.remove(mo); + } + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + + /** + * Sets the Race XML to send. + * @param raceXml XML to send to the Client. + */ + public void setRaceXml(String raceXml) { + //Create the message. + XMLMessage message = this.createXMLMessage(raceXml, XMLMessageType.RACE); + + //Place it in LatestMessages. + this.latestMessages.setRaceXMLMessage(message); + } + + /** + * Sets the Regatta XMl to send. + * @param regattaXml XML to send to Client. + */ + public void setRegattaXml(String regattaXml) { + //Create the message. + XMLMessage message = this.createXMLMessage(regattaXml, XMLMessageType.REGATTA); + + //Place it in LatestMessages. + this.latestMessages.setRegattaXMLMessage(message); + } + + /** + * Sets the Boats XML to send. + * @param boatsXml XMl to send to the Client. + */ + public void setBoatsXml(String boatsXml) { + //Create the message. + XMLMessage message = this.createXMLMessage(boatsXml, XMLMessageType.BOAT); + + //Place it in LatestMessages. + this.latestMessages.setBoatXMLMessage(message); + } + + /** + * Creates an XMLMessage of a specified subtype using the xml contents string. + * @param xmlString The contents of the xml file. + * @param messageType The subtype of xml message (race, regatta, boat). + * @return The created XMLMessage object. + */ + private XMLMessage createXMLMessage(String xmlString, XMLMessageType messageType) { + + //Get the correct sequence number to use, and increment it. + short sequenceNumber = 0; + if (messageType == XMLMessageType.RACE) { + sequenceNumber = this.raceXMLSequenceNumber; + this.raceXMLSequenceNumber++; + + } else if (messageType == XMLMessageType.BOAT) { + sequenceNumber = this.boatXMLSequenceNumber; + this.boatXMLSequenceNumber++; + + } else if (messageType == XMLMessageType.REGATTA) { + sequenceNumber = this.regattaXMLSequenceNumber; + this.regattaXMLSequenceNumber++; + + } + + //Create the message. + XMLMessage message = new XMLMessage( + XMLMessage.currentVersionNumber, + getNextAckNumber(), + System.currentTimeMillis(), + messageType, + sequenceNumber, + xmlString); + + + return message; + } + + /** + * Increments the ackNumber value, and returns it. + * @return Incremented ackNumber. + */ + private int getNextAckNumber(){ + this.ackNumber++; + + return this.ackNumber; + } + +} diff --git a/racevisionGame/src/main/java/mock/app/Event.java b/racevisionGame/src/main/java/mock/app/Event.java index 080e8405..c94a691f 100644 --- a/racevisionGame/src/main/java/mock/app/Event.java +++ b/racevisionGame/src/main/java/mock/app/Event.java @@ -1,5 +1,6 @@ package mock.app; +import mock.dataInput.PolarParser; import mock.model.MockRace; import mock.model.Polars; import network.Messages.LatestMessages; @@ -11,7 +12,10 @@ import shared.exceptions.InvalidRegattaDataException; import shared.exceptions.XMLReaderException; import shared.model.Constants; +import javax.xml.transform.TransformerException; import java.io.IOException; +import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -20,6 +24,7 @@ import java.time.format.DateTimeFormatter; * A Race Event, this holds all of the race's information as well as handling the connection to its clients. */ public class Event { + private static Event theEvent = new Event(); private String raceXML; private String regattaXML; @@ -29,38 +34,43 @@ public class Event { private Polars boatPolars; - private MockOutput mockOutput; + private ConnectionAcceptor mockOutput; private LatestMessages latestMessages; - /** * Constructs an event, using various XML files. - * @param raceXML The race.xml file. - * @param regattaXML The regatta.xml file. - * @param boatXML The boat.xml file. - * @param type How to read the file - e.g., load as resource. - * @param boatPolars polars that the boat uses */ - public Event(String raceXML, String regattaXML, String boatXML, XMLFileType type, Polars boatPolars) { - - this.raceXML = getRaceXMLAtCurrentTime(raceXML); - this.boatXML = boatXML; - this.regattaXML = regattaXML; - - this.xmlFileType = type; + private Event() { + try { + this.raceXML = getRaceXMLAtCurrentTime(XMLReader.readXMLFileToString("mock/mockXML/raceTest.xml", StandardCharsets.UTF_8)); + this.boatXML = XMLReader.readXMLFileToString("mock/mockXML/boatsSinglePlayer.xml", StandardCharsets.UTF_8); + this.regattaXML = XMLReader.readXMLFileToString("mock/mockXML/regattaTest.xml", StandardCharsets.UTF_8); + this.xmlFileType = XMLFileType.Contents; - this.boatPolars = boatPolars; + this.boatPolars = PolarParser.parse("mock/polars/acc_polars.csv"); - this.latestMessages = new LatestMessages(); + this.latestMessages = new LatestMessages(); + this.mockOutput = new ConnectionAcceptor(latestMessages); + } + catch (IOException e) { + e.printStackTrace(); + } catch (XMLReaderException e) { + e.printStackTrace(); + } catch (TransformerException e) { + e.printStackTrace(); + } + } + public static Event getEvent() { + return theEvent; + } - try { - this.mockOutput = new MockOutput(this.latestMessages); - new Thread(mockOutput).start(); + public String getAddress() throws UnknownHostException { + return mockOutput.getAddress(); + } - } catch (IOException e) { - e.printStackTrace(); - } + public int getPort() { + return mockOutput.getServerPort(); } /** @@ -71,6 +81,7 @@ public class Event { * @throws InvalidRegattaDataException Thrown if the regatta xml file cannot be parsed. */ public void start() throws InvalidRaceDataException, XMLReaderException, InvalidBoatDataException, InvalidRegattaDataException { + new Thread(mockOutput).start(); sendXMLs(); @@ -83,7 +94,6 @@ public class Event { MockRace newRace = new MockRace(boatDataSource, raceDataSource, regattaDataSource, this.latestMessages, this.boatPolars, Constants.RaceTimeScale); new Thread(newRace).start(); - } /** @@ -120,5 +130,4 @@ public class Event { return raceXML; } - } diff --git a/racevisionGame/src/main/java/mock/app/MockOutput.java b/racevisionGame/src/main/java/mock/app/MockOutput.java index e217accf..12eaf807 100644 --- a/racevisionGame/src/main/java/mock/app/MockOutput.java +++ b/racevisionGame/src/main/java/mock/app/MockOutput.java @@ -29,18 +29,6 @@ public class MockOutput implements Runnable */ private double heartbeatPeriod = 5.0; - /** - * Port to expose server on. - */ - private int serverPort = 4942; - /** - * Socket used to listen for clients on. - */ - private ServerSocket serverSocket; - /** - * Socket used to communicate with a client. - */ - private Socket mockSocket; /** * Output stream which wraps around mockSocket outstream. */ @@ -60,22 +48,6 @@ public class MockOutput implements Runnable */ private int ackNumber = 1; - - /** - * Sequence number for race xml messages. - */ - private short raceXMLSequenceNumber = 1; - - /** - * Sequence number for boat xml messages. - */ - private short boatXMLSequenceNumber = 1; - - /** - * Sequence number for regatta xml messages. - */ - private short regattaXMLSequenceNumber = 1; - /** * Sequence number for heartbeat messages. */ @@ -86,13 +58,14 @@ public class MockOutput implements Runnable /** * Ctor. - * @param latestMessages The collection of messages to send to connected clients. + * * @throws IOException if server socket cannot be opened. */ - public MockOutput(LatestMessages latestMessages) throws IOException { + public MockOutput(LatestMessages latestMessages, DataOutputStream outToVisualiser) throws IOException { + + this.outToVisualiser = outToVisualiser; this.lastHeartbeatTime = System.currentTimeMillis(); - this.serverSocket = new ServerSocket(serverPort); this.latestMessages = latestMessages; @@ -155,44 +128,6 @@ public class MockOutput implements Runnable } - - /** - * Creates an XMLMessage of a specified subtype using the xml contents string. - * @param xmlString The contents of the xml file. - * @param messageType The subtype of xml message (race, regatta, boat). - * @return The created XMLMessage object. - */ - private XMLMessage createXMLMessage(String xmlString, XMLMessageType messageType) { - - //Get the correct sequence number to use, and increment it. - short sequenceNumber = 0; - if (messageType == XMLMessageType.RACE) { - sequenceNumber = this.raceXMLSequenceNumber; - this.raceXMLSequenceNumber++; - - } else if (messageType == XMLMessageType.BOAT) { - sequenceNumber = this.boatXMLSequenceNumber; - this.boatXMLSequenceNumber++; - - } else if (messageType == XMLMessageType.REGATTA) { - sequenceNumber = this.regattaXMLSequenceNumber; - this.regattaXMLSequenceNumber++; - - } - - //Create the message. - XMLMessage message = new XMLMessage( - XMLMessage.currentVersionNumber, - getNextAckNumber(), - System.currentTimeMillis(), - messageType, - sequenceNumber, - xmlString ); - - - return message; - } - /** * Encodes/serialises a XMLMessage message, and returns it. * @param xmlMessage The XMLMessage message to serialise. @@ -266,6 +201,17 @@ public class MockOutput implements Runnable } + /** + * Sends a heartbeat + * @throws IOException if the socket is no longer open at both ends the heartbeat returns an error. + */ + public void sendHeartBeat() throws IOException { + //Sends a heartbeat every so often. + if (timeSinceHeartbeat() >= heartbeatPeriod) { + outToVisualiser.write(parseHeartbeat(createHeartbeatMessage())); + lastHeartbeatTime = System.currentTimeMillis(); + } + } /** * Sending loop of the Server @@ -274,11 +220,6 @@ public class MockOutput implements Runnable try { while (!stop){ - //Wait for a client to connect. - System.out.println("Waiting for a connection...");//TEMP DEBUG REMOVE - mockSocket = serverSocket.accept(); - - outToVisualiser = new DataOutputStream(mockSocket.getOutputStream()); //Wait until all of the xml files have been set. if (!this.latestMessages.hasAllXMLMessages()) { @@ -307,12 +248,6 @@ public class MockOutput implements Runnable long minimumFramePeriod = 16; if (framePeriod >= minimumFramePeriod) { - //Sends a heartbeat every so often. - if (timeSinceHeartbeat() >= heartbeatPeriod) { - outToVisualiser.write(parseHeartbeat(createHeartbeatMessage())); - lastHeartbeatTime = System.currentTimeMillis(); - } - //Send XML messages. if (!sentXMLs) { //Serialise them. @@ -385,47 +320,4 @@ public class MockOutput implements Runnable stop = true; } - /** - * Sets the Race XML to send. - * @param raceXml XML to send to the Client. - */ - public void setRaceXml(String raceXml) { - //Create the message. - XMLMessage message = this.createXMLMessage(raceXml, XMLMessageType.RACE); - - //Place it in LatestMessages. - this.latestMessages.setRaceXMLMessage(message); - } - - /** - * Sets the Regatta XMl to send. - * @param regattaXml XML to send to Client. - */ - public void setRegattaXml(String regattaXml) { - //Create the message. - XMLMessage message = this.createXMLMessage(regattaXml, XMLMessageType.REGATTA); - - //Place it in LatestMessages. - this.latestMessages.setRegattaXMLMessage(message); - } - - /** - * Sets the Boats XML to send. - * @param boatsXml XMl to send to the Client. - */ - public void setBoatsXml(String boatsXml) { - //Create the message. - XMLMessage message = this.createXMLMessage(boatsXml, XMLMessageType.BOAT); - - //Place it in LatestMessages. - this.latestMessages.setBoatXMLMessage(message); - } - - - public static void main(String argv[]) throws Exception - { - MockOutput client = new MockOutput(new LatestMessages()); - client.run(); - } - } diff --git a/racevisionGame/src/main/java/network/BinaryMessageDecoder.java b/racevisionGame/src/main/java/network/BinaryMessageDecoder.java index 1c34ca85..ecc6b2f3 100644 --- a/racevisionGame/src/main/java/network/BinaryMessageDecoder.java +++ b/racevisionGame/src/main/java/network/BinaryMessageDecoder.java @@ -193,6 +193,10 @@ public class BinaryMessageDecoder { AverageWindDecoder awDecoder = new AverageWindDecoder(messageBody); return awDecoder.getAverageWind(); + case BOATACTION: + BoatActionDecoder baDecoder = new BoatActionDecoder(messageBody); + return new BoatAction(baDecoder.getBoatAction()); + default: //System.out.println("Broken Message!"); //throw new InvalidMessageException("Broken message! Did not recognise message type: " + headerMessageType + "."); diff --git a/racevisionGame/src/main/java/network/BinaryMessageEncoder.java b/racevisionGame/src/main/java/network/BinaryMessageEncoder.java index f7dc12a7..ced31af1 100644 --- a/racevisionGame/src/main/java/network/BinaryMessageEncoder.java +++ b/racevisionGame/src/main/java/network/BinaryMessageEncoder.java @@ -94,6 +94,15 @@ public class BinaryMessageEncoder { this.fullMessage = tempFullMessageByteBuffer.array(); } + /** + * Construct a binary message from message type and message body. + * @param headerMessageType of message + * @param messageBody of message + */ + public BinaryMessageEncoder(MessageType headerMessageType, byte[] messageBody) { + this(headerMessageType, System.currentTimeMillis(), 69, (short)messageBody.length, messageBody); + } + /** * Returns the full encoded message. This includes the header, body, and CRC. * @return Full encoded message. diff --git a/racevisionGame/src/main/java/network/MessageDecoders/BoatActionDecoder.java b/racevisionGame/src/main/java/network/MessageDecoders/BoatActionDecoder.java new file mode 100644 index 00000000..bf2076b5 --- /dev/null +++ b/racevisionGame/src/main/java/network/MessageDecoders/BoatActionDecoder.java @@ -0,0 +1,20 @@ +package network.MessageDecoders; + +import network.Messages.Enums.BoatActionEnum; + +import java.util.Arrays; + +public class BoatActionDecoder { + byte byteBoatAction; + BoatActionEnum boatAction; + + public BoatActionDecoder(byte[] encodedBoatAction) { + byteBoatAction = encodedBoatAction[0]; + + boatAction = BoatActionEnum.fromByte(byteBoatAction); + } + + public BoatActionEnum getBoatAction() { + return boatAction; + } +} \ No newline at end of file diff --git a/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java b/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java index 0808d16d..4c57cf0c 100644 --- a/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java +++ b/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java @@ -339,4 +339,11 @@ public class RaceVisionByteEncoder { return result.array(); } + public static byte[] boatActionMessage(BoatAction boatAction){ + ByteBuffer boatActionMessage = ByteBuffer.allocate(1); + boatActionMessage.put(intToBytes(boatAction.getBoatAction(), 1)); + byte [] result = boatActionMessage.array(); + return result; + } + } diff --git a/racevisionGame/src/main/java/network/Messages/BoatAction.java b/racevisionGame/src/main/java/network/Messages/BoatAction.java new file mode 100644 index 00000000..d20943a5 --- /dev/null +++ b/racevisionGame/src/main/java/network/Messages/BoatAction.java @@ -0,0 +1,22 @@ +package network.Messages; + +import network.Messages.Enums.BoatActionEnum; +import network.Messages.Enums.MessageType; + +/** + * Created by David on 10/07/2017. + */ +public class BoatAction extends AC35Data { + + private byte boatAction; + + public BoatAction(BoatActionEnum boatAction){ + super(MessageType.BOATACTION); + this.boatAction = boatAction.getValue(); + } + + public byte getBoatAction() { + return boatAction; + } + +} \ No newline at end of file diff --git a/racevisionGame/src/main/java/network/Messages/Enums/BoatActionEnum.java b/racevisionGame/src/main/java/network/Messages/Enums/BoatActionEnum.java new file mode 100644 index 00000000..84f6e0fd --- /dev/null +++ b/racevisionGame/src/main/java/network/Messages/Enums/BoatActionEnum.java @@ -0,0 +1,71 @@ +package network.Messages.Enums; + +import java.util.HashMap; +import java.util.Map; + +/** + * Boat actions + */ +public enum BoatActionEnum { + NOT_A_STATUS(-1), + AUTO_PILOT(1), + SAILS_IN(2), + SAILS_OUT(3), + TACK_GYBE(4), + UPWIND(5), + DOWNWIND(6); + + private byte value; + + /** + * Ctor. Creates a BoatActionEnum from a given primitive integer value, cast to a byte. + * @param value Integer, which is cast to byte, to construct from. + */ + private BoatActionEnum(int value) { + this.value = (byte) value; + } + + /** + * Returns the primitive value of the enum. + * @return Primitive value of the enum. + */ + public byte getValue() { + return value; + } + + + /** + * Stores a mapping between Byte values and BoatActionEnum values. + */ + private static final Map byteToStatusMap = new HashMap<>(); + + + /* + Static initialization block. Initializes the byteToStatusMap. + */ + static { + for (BoatActionEnum type : BoatActionEnum.values()) { + BoatActionEnum.byteToStatusMap.put(type.value, type); + } + } + + + /** + * Returns the enumeration value which corresponds to a given byte value. + * @param boatActionEnum Byte value to convert to a BoatActionEnum value. + * @return The BoatActionEnum value which corresponds to the given byte value. + */ + public static BoatActionEnum fromByte(byte boatActionEnum) { + //Gets the corresponding MessageType from the map. + BoatActionEnum type = BoatActionEnum.byteToStatusMap.get(boatActionEnum); + + if (type == null) { + //If the byte value wasn't found, return the NOT_A_STATUS boatActionEnum. + return BoatActionEnum.NOT_A_STATUS; + } else { + //Otherwise, return the boatActionEnum. + return type; + } + + } +} \ No newline at end of file diff --git a/racevisionGame/src/main/java/network/Messages/Enums/MessageType.java b/racevisionGame/src/main/java/network/Messages/Enums/MessageType.java index 6b8fd775..086673f5 100644 --- a/racevisionGame/src/main/java/network/Messages/Enums/MessageType.java +++ b/racevisionGame/src/main/java/network/Messages/Enums/MessageType.java @@ -19,6 +19,7 @@ public enum MessageType { MARKROUNDING(38), COURSEWIND(44), AVGWIND(47), + BOATACTION(100), NOTAMESSAGE(0); ///Primitive value of the enum. diff --git a/racevisionGame/src/main/java/network/Messages/LatestMessages.java b/racevisionGame/src/main/java/network/Messages/LatestMessages.java index c0da436b..f35fc52e 100644 --- a/racevisionGame/src/main/java/network/Messages/LatestMessages.java +++ b/racevisionGame/src/main/java/network/Messages/LatestMessages.java @@ -289,8 +289,7 @@ public class LatestMessages extends Observable { * @return True if race, boat, and regatta have an xml message, false otherwise. */ public boolean hasAllXMLMessages() { - - if ((this.regattaXMLMessage == null) || (this.boatXMLMessage == null) || (this.raceXMLMessage == null)) { + if (this.regattaXMLMessage == null || this.boatXMLMessage == null || this.raceXMLMessage == null) { return false; } else { diff --git a/racevisionGame/src/main/java/visualiser/Controllers/ConnectionController.java b/racevisionGame/src/main/java/visualiser/Controllers/ConnectionController.java index 7dacbd5e..92ca8809 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/ConnectionController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/ConnectionController.java @@ -8,11 +8,17 @@ import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane; +import mock.app.Event; +import shared.exceptions.InvalidBoatDataException; +import shared.exceptions.InvalidRaceDataException; +import shared.exceptions.InvalidRegattaDataException; +import shared.exceptions.XMLReaderException; import visualiser.model.RaceConnection; import java.io.IOException; import java.net.Socket; import java.net.URL; +import java.net.UnknownHostException; import java.util.ResourceBundle; /** @@ -37,19 +43,24 @@ public class ConnectionController extends Controller { private ObservableList connections; + + /** + * Represents whether the client is currently hosting a game already - this is to ensure they don't launch multiple servers. + */ + private boolean currentlyHostingGame = false; + + @Override public void initialize(URL location, ResourceBundle resources) { // TODO - replace with config file connections = FXCollections.observableArrayList(); - connections.add(new RaceConnection("livedata.americascup.com", 4941)); - connections.add(new RaceConnection("localhost", 4942)); connectionTable.setItems(connections); hostnameColumn.setCellValueFactory(cellData -> cellData.getValue().hostnameProperty()); statusColumn.setCellValueFactory(cellData -> cellData.getValue().statusProperty()); connectionTable.getSelectionModel().selectedItemProperty().addListener((obs, prev, curr) -> { - if (curr != null && ((RaceConnection)curr).check()) connectButton.setDisable(false); + if (curr != null && curr.check()) connectButton.setDisable(false); else connectButton.setDisable(true); }); connectButton.setDisable(true); @@ -75,6 +86,7 @@ public class ConnectionController extends Controller { try{ RaceConnection connection = connectionTable.getSelectionModel().getSelectedItem(); Socket socket = new Socket(connection.getHostname(), connection.getPort()); + socket.setKeepAlive(true); connectionWrapper.setVisible(false); parent.enterLobby(socket); } catch (IOException e) { /* Never reached */ } @@ -92,6 +104,34 @@ public class ConnectionController extends Controller { }catch(NumberFormatException e){ System.err.println("Port number entered is not a number"); } + } + /** + * Sets up a new host + */ + public void addLocal() { + try { + //We don't want to host more than one game. + if (!currentlyHostingGame) { + Event game = Event.getEvent(); + urlField.textProperty().set(game.getAddress()); + portField.textProperty().set(Integer.toString(game.getPort())); + + game.start(); + addConnection(); + + currentlyHostingGame = true; + } + } catch (InvalidRaceDataException e) { + e.printStackTrace(); + } catch (XMLReaderException e) { + e.printStackTrace(); + } catch (InvalidBoatDataException e) { + e.printStackTrace(); + } catch (InvalidRegattaDataException e) { + e.printStackTrace(); + } catch (UnknownHostException e) { + e.printStackTrace(); + } } } diff --git a/racevisionGame/src/main/java/visualiser/Controllers/MainController.java b/racevisionGame/src/main/java/visualiser/Controllers/MainController.java index 262c3c8a..d258e695 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/MainController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/MainController.java @@ -4,6 +4,7 @@ import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.layout.AnchorPane; import visualiser.app.VisualiserInput; +import visualiser.gameController.ControllerClient; import visualiser.model.VisualiserBoat; import visualiser.model.VisualiserRace; @@ -36,8 +37,8 @@ public class MainController extends Controller { * @param visualiserInput The object used to read packets from the race server. * @param visualiserRace The object modelling the race. */ - public void beginRace(VisualiserInput visualiserInput, VisualiserRace visualiserRace) { - raceController.startRace(visualiserInput, visualiserRace); + public void beginRace(VisualiserInput visualiserInput, VisualiserRace visualiserRace, ControllerClient controllerClient) { + raceController.startRace(visualiserInput, visualiserRace, controllerClient); } /** diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index ffcfcb54..a15e826c 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -5,9 +5,12 @@ import javafx.animation.AnimationTimer; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.scene.chart.LineChart; import javafx.scene.control.*; +import javafx.scene.control.Label; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; @@ -16,8 +19,13 @@ import javafx.util.Callback; import network.Messages.Enums.RaceStatusEnum; import shared.model.Leg; import visualiser.app.VisualiserInput; +import visualiser.gameController.ControllerClient; +import visualiser.gameController.Keys.ControlKey; +import visualiser.gameController.Keys.KeyFactory; import visualiser.model.*; +import java.awt.*; +import java.io.IOException; import java.net.URL; import java.text.DecimalFormat; import java.util.ResourceBundle; @@ -52,6 +60,11 @@ public class RaceController extends Controller { */ private Sparkline sparkline; + /** + * Service for sending keystrokes to server + */ + private ControllerClient controllerClient; + @FXML private GridPane canvasBase; @FXML private Pane arrow; @@ -79,6 +92,22 @@ public class RaceController extends Controller { @Override public void initialize(URL location, ResourceBundle resources) { + KeyFactory keyFactory = KeyFactory.getFactory(); + + // Initialise keyboard handler + race.addEventFilter(KeyEvent.KEY_PRESSED, event -> { + String codeString = event.getCode().toString(); + ControlKey controlKey = keyFactory.getKey(codeString); + if(controlKey != null) { + try { + controllerClient.sendKey(controlKey); + controlKey.onAction(); // Change key state if applicable + event.consume(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); } @@ -278,8 +307,6 @@ public class RaceController extends Controller { //Add to scene. canvasBase.getChildren().add(0, raceCanvas); - - } @@ -312,10 +339,11 @@ public class RaceController extends Controller { * @param visualiserInput Object used to read packets from server. * @param visualiserRace Object modelling the race. */ - public void startRace(VisualiserInput visualiserInput, VisualiserRace visualiserRace) { + public void startRace(VisualiserInput visualiserInput, VisualiserRace visualiserRace, ControllerClient controllerClient) { this.visualiserInput = visualiserInput; this.visualiserRace = visualiserRace; + this.controllerClient = controllerClient; initialiseRace(); @@ -345,7 +373,6 @@ public class RaceController extends Controller { } - /** * Timer which monitors the race. */ diff --git a/racevisionGame/src/main/java/visualiser/Controllers/StartController.java b/racevisionGame/src/main/java/visualiser/Controllers/StartController.java index 2735023e..8db4ec60 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/StartController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/StartController.java @@ -19,6 +19,7 @@ import shared.exceptions.InvalidRaceDataException; import shared.exceptions.InvalidRegattaDataException; import shared.exceptions.XMLReaderException; import visualiser.app.VisualiserInput; +import visualiser.gameController.ControllerClient; import visualiser.model.VisualiserBoat; import visualiser.model.VisualiserRace; @@ -76,6 +77,8 @@ public class StartController extends Controller implements Observer { */ private VisualiserRace visualiserRace; + private ControllerClient controllerClient; + /** * An array of colors used to assign colors to each boat - passed in to the VisualiserRace constructor. */ @@ -258,7 +261,7 @@ public class StartController extends Controller implements Observer { startWrapper.setVisible(false); start.setVisible(false); - parent.beginRace(visualiserInput, visualiserRace); + parent.beginRace(visualiserInput, visualiserRace, controllerClient); } } }.start(); @@ -308,6 +311,8 @@ public class StartController extends Controller implements Observer { try { //Begin reading packets from the socket/server. this.visualiserInput = new VisualiserInput(socket); + //Send controller input to server + this.controllerClient = new ControllerClient(socket); //Store a reference to latestMessages so that we can observe it. LatestMessages latestMessages = this.visualiserInput.getLatestMessages(); latestMessages.addObserver(this); diff --git a/racevisionGame/src/main/java/visualiser/app/App.java b/racevisionGame/src/main/java/visualiser/app/App.java index 6e379d2b..9a86cfd4 100644 --- a/racevisionGame/src/main/java/visualiser/app/App.java +++ b/racevisionGame/src/main/java/visualiser/app/App.java @@ -31,6 +31,7 @@ public class App extends Application { FXMLLoader loader = new FXMLLoader(getClass().getResource("/visualiser/scenes/main.fxml")); Parent root = loader.load(); Scene scene = new Scene(root, 1200, 800); + stage.setScene(scene); stage.setTitle("RaceVision - Team 7"); stage.show(); diff --git a/racevisionGame/src/main/java/visualiser/app/VisualiserInput.java b/racevisionGame/src/main/java/visualiser/app/VisualiserInput.java index d86e0224..0b8102b5 100644 --- a/racevisionGame/src/main/java/visualiser/app/VisualiserInput.java +++ b/racevisionGame/src/main/java/visualiser/app/VisualiserInput.java @@ -20,6 +20,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ArrayBlockingQueue; import static network.Utils.ByteConverter.bytesToShort; @@ -372,6 +373,4 @@ public class VisualiserInput implements Runnable { } } - - } diff --git a/racevisionGame/src/main/java/visualiser/gameController/ControllerClient.java b/racevisionGame/src/main/java/visualiser/gameController/ControllerClient.java new file mode 100644 index 00000000..eb46d361 --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/gameController/ControllerClient.java @@ -0,0 +1,68 @@ +package visualiser.gameController; + +import network.BinaryMessageEncoder; +import network.MessageEncoders.RaceVisionByteEncoder; +import network.Messages.BoatAction; +import network.Messages.Enums.BoatActionEnum; +import network.Messages.Enums.MessageType; +import visualiser.gameController.Keys.ControlKey; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.net.SocketException; +import java.nio.ByteBuffer; + +/** + * Basic service for sending key presses to game server + */ +public class ControllerClient { + /** + * Socket to server + */ + Socket socket; + + /** + * Output stream wrapper for socket to server + */ + DataOutputStream outputStream; + + /** + * Initialise controller client with live socket. + * @param socket to server + */ + public ControllerClient(Socket socket) { + this.socket = socket; + + try { + this.outputStream = new DataOutputStream(socket.getOutputStream()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Send a keypress to server + * @param key to send + * @throws IOException if socket write fails + */ + public void sendKey(ControlKey key) throws IOException { + int protocolCode = key.getProtocolCode(); + if(protocolCode > -1) { + + byte[] bytes = new byte[4]; + ByteBuffer.wrap(bytes).putInt(key.getProtocolCode()); + BoatActionEnum boatActionEnum = BoatActionEnum.fromByte(bytes[3]); + + BoatAction boatAction = new BoatAction(boatActionEnum); + + byte[] encodedBoatAction = RaceVisionByteEncoder.boatActionMessage(boatAction); + + BinaryMessageEncoder binaryMessage = new BinaryMessageEncoder(MessageType.BOATACTION, System.currentTimeMillis(), 0, + (short) encodedBoatAction.length, encodedBoatAction); + + System.out.println("Sending out key: " + boatActionEnum); + outputStream.write(binaryMessage.getFullMessage()); + } + } +} diff --git a/racevisionGame/src/main/java/visualiser/gameController/ControllerServer.java b/racevisionGame/src/main/java/visualiser/gameController/ControllerServer.java new file mode 100644 index 00000000..fb6a257b --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/gameController/ControllerServer.java @@ -0,0 +1,59 @@ +package visualiser.gameController; + +import network.BinaryMessageDecoder; +import network.MessageDecoders.BoatActionDecoder; +import network.Messages.Enums.BoatActionEnum; +import visualiser.gameController.Keys.ControlKey; +import visualiser.gameController.Keys.KeyFactory; + +import java.io.DataInputStream; +import java.io.IOException; +import java.net.Socket; + +/** + * Service for dispatching key press data to race from client + */ +public class ControllerServer implements Runnable { + /** + * Socket to client + */ + private Socket socket; + /** + * Wrapper for input from client + */ + private DataInputStream inputStream; + + /** + * Initialise server-side controller with live client socket + * @param socket to client + */ + public ControllerServer(Socket socket) { + this.socket = socket; + try { + this.inputStream = new DataInputStream(this.socket.getInputStream()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Wait for controller key input from client and loop. + */ + @Override + public void run() { + while(true) { + byte[] message = new byte[20]; + try { + if (inputStream.available() > 0) { + inputStream.read(message); + BinaryMessageDecoder encodedMessage = new BinaryMessageDecoder(message); + BoatActionDecoder boatActionDecoder = new BoatActionDecoder(encodedMessage.getMessageBody()); + BoatActionEnum decodedMessage = boatActionDecoder.getBoatAction(); + System.out.println("Received key: " + decodedMessage); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} diff --git a/racevisionGame/src/main/java/visualiser/gameController/InputChecker.java b/racevisionGame/src/main/java/visualiser/gameController/InputChecker.java new file mode 100644 index 00000000..34a7d544 --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/gameController/InputChecker.java @@ -0,0 +1,83 @@ +package visualiser.gameController; + +import javafx.animation.AnimationTimer; +import javafx.scene.Scene; +import visualiser.gameController.Keys.ControlKey; +import visualiser.gameController.Keys.KeyFactory; + +import java.util.HashMap; + +import static javafx.application.Application.launch; + +/** + * Class for checking what keys are currently being used + */ +public class InputChecker { + private HashMap currentlyActiveKeys = new HashMap<>(); + + /** + * Controller loop that detects key presses that runs parallel to the main scene. + * @param scene Scene the controller is to run in parallel with. + */ + public void runWithScene(Scene scene){ + KeyFactory keyFactory = KeyFactory.getFactory(); + + scene.setOnKeyPressed(event -> { + String codeString = event.getCode().toString(); + if (!currentlyActiveKeys.containsKey(codeString)) { + ControlKey controlKey = keyFactory.getKey(codeString); + if (controlKey != null) { + controlKey.onAction(); + System.out.println(controlKey.toString() + " is Pressed."); + } + currentlyActiveKeys.put(codeString, true); + } + }); + + scene.setOnKeyReleased(event -> { + String codeString = event.getCode().toString(); + ControlKey controlKey = keyFactory.getKey(codeString); + if (controlKey != null) { + controlKey.onRelease(); + System.out.println(controlKey.toString() + " is Released."); + } + currentlyActiveKeys.remove(event.getCode().toString()); + }); + + new AnimationTimer() { + @Override + public void handle(long now) { + for (String key: currentlyActiveKeys.keySet()){ + ControlKey controlKey = keyFactory.getKey(key); + if (controlKey != null){ + controlKey.onHold(); + System.out.println(controlKey.toString() + " is Held."); + } + } +// for (String key : InputKeys.stringKeysMap.keySet()){ +// if (removeActiveKey(key)) { +// System.out.println(key); +// } +// } + + } + }.start(); + } + + /** + * removes a key from the active dictionary + * @param codeString string of the key press to remove + * @return whether or not the key has been removed or not. + */ + private boolean removeActiveKey(String codeString) { + Boolean isActive = currentlyActiveKeys.get(codeString); + + if (isActive != null && isActive) { + currentlyActiveKeys.put(codeString, false); + return true; + } else { + return false; + } + } + +} diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/ControlKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/ControlKey.java new file mode 100644 index 00000000..ad8a559a --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/ControlKey.java @@ -0,0 +1,58 @@ +package visualiser.gameController.Keys; + +import javafx.scene.input.KeyCode; + +/** + * Key for the controller, part of the abstract factory KeyFactory + */ +public abstract class ControlKey { + + private String name; + protected int protocolCode; + + /** + * Constructor for key state with specified protocol code + * @param name of action + * @param protocolCode -1 if not sent + */ + public ControlKey(String name, int protocolCode) { + this.name = name; + this.protocolCode = protocolCode; + } + + /** + * Constructor for key state not sent over network + * @param name name of the key + */ + public ControlKey(String name){ + this.name = name; + this.protocolCode = -1; + } + + public int getProtocolCode() { + return protocolCode; + } + + /** + * To String method + * @return returns the name of the key + */ + public String toString(){ + return this.name; + } + + /** + * What this key should do when the command is issued for it to do its job. + */ + public abstract void onAction();//may want to make it take in a visualiser and stuff in the future. + + /** + * What to do when the key is held + */ + public abstract void onHold(); + + /** + * What to do when the key is released. + */ + public abstract void onRelease(); +} diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/DownWindKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/DownWindKey.java new file mode 100644 index 00000000..6d929ca1 --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/DownWindKey.java @@ -0,0 +1,31 @@ +package visualiser.gameController.Keys; + +/** + * Key to send downwind packet to server + */ +public class DownWindKey extends ControlKey { + + /** + * Constructor for Control + * @param name name of the key + * + */ + public DownWindKey(String name) { + super(name, 6); + } + + @Override + public void onAction() { + + } + + @Override + public void onHold() { + + } + + @Override + public void onRelease() { + + } +} diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/KeyFactory.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/KeyFactory.java new file mode 100644 index 00000000..ef1368f0 --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/KeyFactory.java @@ -0,0 +1,51 @@ +package visualiser.gameController.Keys; + +import java.util.HashMap; +import java.util.Map; + +/** + * Factory for creating Keys, these could be predefined in the future. + */ +public class KeyFactory { + /** + * Retrieve command given key + */ + private Map keyState; + + /** + * Singleton instance to enforce consistent key state + */ + private static KeyFactory theFactory = new KeyFactory(); + + /** + * Singleton constructor for key state, set up initial state of each action. + */ + private KeyFactory() { + this.keyState = new HashMap<>(); + keyState.put("Z", new ZoomInKey("Zoom In")); + keyState.put("X", new ZoomOutKey("Zoom Out")); + keyState.put("SPACE", new VMGKey("VMG")); + keyState.put("SHIFT", new SailsToggleKey("Toggle Sails")); + keyState.put("ENTER", new TackGybeKey("Tack/Gybe")); + keyState.put("PAGE_UP", new UpWindKey("Upwind")); + keyState.put("PAGE_DOWN", new DownWindKey("Downwind")); + } + + /** + * Get singleton instance of KeyFactory to interact with key state + * @return automatically constructed KeyFactory + */ + public static KeyFactory getFactory() { + return theFactory; + } + + /** + * Get the Control Key in charge of a key press + * @param key key pressed (String value of KeyCode) + * @return the Control Key behaviour of the key pressed. + */ + public ControlKey getKey(String key){ + return keyState.get(key); + } + +} diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/SailsToggleKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/SailsToggleKey.java new file mode 100644 index 00000000..bc9b81a6 --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/SailsToggleKey.java @@ -0,0 +1,34 @@ +package visualiser.gameController.Keys; + +/** + * Key to toggle the sails + */ +public class SailsToggleKey extends ControlKey { + + /** + * Constructor for Control + * @param name name of the key + * + */ + public SailsToggleKey(String name) { + super(name, 2); + } + + /** + * Toggle command associated with sails key + */ + @Override + public void onAction() { + protocolCode = protocolCode == 2? 3 : 2; + } + + @Override + public void onHold() { + + } + + @Override + public void onRelease() { + + } +} diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/TackGybeKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/TackGybeKey.java new file mode 100644 index 00000000..cf9a0699 --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/TackGybeKey.java @@ -0,0 +1,31 @@ +package visualiser.gameController.Keys; + +/** + * key to toggle between tacking and gybing + */ +public class TackGybeKey extends ControlKey { + + /** + * Constructor for Control + * @param name name of the key + * + */ + public TackGybeKey(String name) { + super(name, 4); + } + + @Override + public void onAction() { + + } + + @Override + public void onHold() { + + } + + @Override + public void onRelease() { + + } +} diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/UpWindKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/UpWindKey.java new file mode 100644 index 00000000..85f7fc4b --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/UpWindKey.java @@ -0,0 +1,31 @@ +package visualiser.gameController.Keys; + +/** + * Key to go upwind + */ +public class UpWindKey extends ControlKey { + + /** + * Constructor for Control + * @param name name of the key + * + */ + public UpWindKey(String name) { + super(name, 5); + } + + @Override + public void onAction() { + + } + + @Override + public void onHold() { + + } + + @Override + public void onRelease() { + + } +} diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/VMGKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/VMGKey.java new file mode 100644 index 00000000..e6d82ba7 --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/VMGKey.java @@ -0,0 +1,33 @@ +package visualiser.gameController.Keys; + +import javafx.scene.input.KeyCode; + +/** + * Key to trigger auto VMG + */ +public class VMGKey extends ControlKey{ + + /** + * Constructor for Control + * + * @param name name of the key + */ + public VMGKey(String name) { + super(name, 1); + } + + @Override + public void onAction() { + + } + + @Override + public void onHold() { + + } + + @Override + public void onRelease() { + + } +} diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/ZoomInKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/ZoomInKey.java new file mode 100644 index 00000000..35b6ce94 --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/ZoomInKey.java @@ -0,0 +1,28 @@ +package visualiser.gameController.Keys; + +import javafx.scene.input.KeyCode; + +/** + * key to zoom into the game + */ +public class ZoomInKey extends ControlKey { + + public ZoomInKey(String name) { + super(name); + } + + @Override + public void onAction() { + + } + + @Override + public void onHold() { + + } + + @Override + public void onRelease() { + + } +} diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/ZoomOutKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/ZoomOutKey.java new file mode 100644 index 00000000..6da2210c --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/ZoomOutKey.java @@ -0,0 +1,31 @@ +package visualiser.gameController.Keys; + +/** + * Key to zoom out of the game. + */ +public class ZoomOutKey extends ControlKey{ + + /** + * Constructor for Control + * @param name name of the key + * + */ + public ZoomOutKey(String name) { + super(name); + } + + @Override + public void onAction() { + + } + + @Override + public void onHold() { + + } + + @Override + public void onRelease() { + + } +} diff --git a/racevisionGame/src/main/java/visualiser/model/RaceConnection.java b/racevisionGame/src/main/java/visualiser/model/RaceConnection.java index 848b747e..036e1f2a 100644 --- a/racevisionGame/src/main/java/visualiser/model/RaceConnection.java +++ b/racevisionGame/src/main/java/visualiser/model/RaceConnection.java @@ -15,6 +15,11 @@ public class RaceConnection { private final int port; private final StringProperty status; + /** + * Constructor for remote host connections. + * @param hostname URL for remote host + * @param port port for game feed + */ public RaceConnection(String hostname, int port) { this.hostname = new SimpleStringProperty(hostname); this.port = port; @@ -33,6 +38,10 @@ public class RaceConnection { try (Socket s = new Socket()){ s.connect(i, 750);//TODO this should be at least a second or two, once moved to its own thread status.set("Ready"); + s.shutdownInput(); + s.shutdownOutput(); + s.close(); + //System.out.println(String.valueOf(s.isClosed())); return true; } catch (IOException e) {} diff --git a/racevisionGame/src/main/resources/mock/mockXML/boatTest.xml b/racevisionGame/src/main/resources/mock/mockXML/boatTest.xml index 84f911bc..b43abe7f 100644 --- a/racevisionGame/src/main/resources/mock/mockXML/boatTest.xml +++ b/racevisionGame/src/main/resources/mock/mockXML/boatTest.xml @@ -33,23 +33,8 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/racevisionGame/src/main/resources/mock/mockXML/boatsSinglePlayer.xml b/racevisionGame/src/main/resources/mock/mockXML/boatsSinglePlayer.xml new file mode 100644 index 00000000..98058f3d --- /dev/null +++ b/racevisionGame/src/main/resources/mock/mockXML/boatsSinglePlayer.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/racevisionGame/src/main/resources/mock/mockXML/raceTest.xml b/racevisionGame/src/main/resources/mock/mockXML/raceTest.xml index 63fac32d..83e36f85 100644 --- a/racevisionGame/src/main/resources/mock/mockXML/raceTest.xml +++ b/racevisionGame/src/main/resources/mock/mockXML/raceTest.xml @@ -5,12 +5,7 @@ CREATION_TIME - - - - - diff --git a/racevisionGame/src/main/resources/visualiser/scenes/connect.fxml b/racevisionGame/src/main/resources/visualiser/scenes/connect.fxml index 32384999..254ddb8a 100644 --- a/racevisionGame/src/main/resources/visualiser/scenes/connect.fxml +++ b/racevisionGame/src/main/resources/visualiser/scenes/connect.fxml @@ -1,15 +1,25 @@ - - - + + + + + + + + + + - + + - - + + + + @@ -18,42 +28,43 @@ - + - + - - -