diff --git a/matchBrowser/pom.xml b/matchBrowser/pom.xml
new file mode 100644
index 00000000..91581a28
--- /dev/null
+++ b/matchBrowser/pom.xml
@@ -0,0 +1,89 @@
+
+
+
+ team-7
+ seng302
+ 2.0
+
+ 4.0.0
+
+
+ jar
+ matchBrowser
+ matchBrowser
+ 2.0
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+
+ org.mockito
+ mockito-all
+ 1.9.5
+
+
+
+ org.testng
+ testng
+ 6.11
+ test
+
+
+
+ seng302
+ racevisionGame
+ 2.0
+
+
+
+
+
+ 1.8
+ 1.8
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.5.1
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.4.3
+
+
+
+
+ app.Main
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+
+
+
+
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/matchBrowser/src/main/java/app/Main.java b/matchBrowser/src/main/java/app/Main.java
new file mode 100644
index 00000000..0d858d6f
--- /dev/null
+++ b/matchBrowser/src/main/java/app/Main.java
@@ -0,0 +1,12 @@
+package app;
+
+import networkInterface.NetworkInterface;
+
+/**
+ * Used when starting the matchmaking browser
+ */
+public class Main {
+ public static void main(String[] args) {
+ NetworkInterface networkInterface = new NetworkInterface();
+ }
+}
diff --git a/matchBrowser/src/main/java/model/ClientAddress.java b/matchBrowser/src/main/java/model/ClientAddress.java
new file mode 100644
index 00000000..51c7249d
--- /dev/null
+++ b/matchBrowser/src/main/java/model/ClientAddress.java
@@ -0,0 +1,37 @@
+package model;
+
+public class ClientAddress {
+ private String ip;
+ private int port;
+
+ public ClientAddress(String ip, int port) {
+ this.ip = ip;
+ this.port = port;
+ }
+
+ public String getIp() {
+ return ip;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o != null && o instanceof ClientAddress && hashCode() == o.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = ip != null ? ip.hashCode() : 0;
+ result *= 31;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "{ip='" + ip + '\'' +
+ ", port=" + port+"}";
+ }
+}
diff --git a/matchBrowser/src/main/java/model/MatchTable.java b/matchBrowser/src/main/java/model/MatchTable.java
new file mode 100644
index 00000000..0e177cc7
--- /dev/null
+++ b/matchBrowser/src/main/java/model/MatchTable.java
@@ -0,0 +1,32 @@
+package model;
+
+
+import network.Messages.HostGame;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Holds a table object that stores current games
+ */
+public class MatchTable {
+ private HashMap matchTable;
+
+ public MatchTable() {
+ this.matchTable = new HashMap<>();
+ }
+
+ public void addEntry(ClientAddress address, HostGame newEntry) {
+ this.matchTable.put(address, newEntry);
+ }
+
+ public HashMap getMatchTable() {
+ return matchTable;
+ }
+
+ @Override
+ public String toString() {
+ return "MatchTable=" + matchTable;
+ }
+}
diff --git a/matchBrowser/src/main/java/model/TableKey.java b/matchBrowser/src/main/java/model/TableKey.java
new file mode 100644
index 00000000..3a8a85e9
--- /dev/null
+++ b/matchBrowser/src/main/java/model/TableKey.java
@@ -0,0 +1,37 @@
+package model;
+
+/**
+ * Used to create a key made of an ip and port.
+ */
+public class TableKey {
+
+ private final String ip;
+ private final int port;
+
+ public TableKey(String ip, int port) {
+ this.ip = ip;
+ this.port = port;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof TableKey)) return false;
+ TableKey key = (TableKey) o;
+ return ip.equals(key.ip) && port == key.port;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = port;
+ result = 31 * result + ip.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "[ip='" + ip + '\'' +
+ ", port=" + port +
+ ']';
+ }
+}
diff --git a/matchBrowser/src/main/java/networkInterface/NetworkInterface.java b/matchBrowser/src/main/java/networkInterface/NetworkInterface.java
new file mode 100644
index 00000000..ce211f68
--- /dev/null
+++ b/matchBrowser/src/main/java/networkInterface/NetworkInterface.java
@@ -0,0 +1,132 @@
+package networkInterface;
+
+import model.ClientAddress;
+import model.MatchTable;
+import network.BinaryMessageDecoder;
+import network.Exceptions.InvalidMessageException;
+import network.MessageDecoders.HostGameMessageDecoder;
+import network.MessageDecoders.HostedGamesRequestDecoder;
+import network.MessageEncoders.HostedGamesRequestEncoder;
+import network.Messages.Enums.MessageType;
+import network.Messages.HostGame;
+import network.Messages.HostGamesRequest;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.time.LocalDateTime;
+import java.util.*;
+
+/**
+ * Holds the output for the network for
+ */
+public class NetworkInterface {
+ private Timer scheduler;
+ private DatagramSocket serverSocket;
+ private byte[] receiveData = new byte[1024];
+
+ private Set clientsAddresses;
+ private MatchTable matchTable;
+
+
+ public NetworkInterface(){
+ this.clientsAddresses = new HashSet<>();
+ this.matchTable = new MatchTable();
+ this.scheduler = new Timer(true);
+ try {
+ this.serverSocket = new DatagramSocket(3779);
+
+ startBroadcast(5000);
+ scheduleFlush(70000);
+ this.run();
+ } catch (IOException e) {
+ System.err.println("Error listening on port: " + this.serverSocket.getLocalPort() + ".");
+ System.exit(-1);
+ }
+
+ }
+
+ /**
+ * Broadcasts match table to clients at a requested interval
+ * @param period interval to broadcast table
+ */
+ private void startBroadcast(int period) {
+ scheduler.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ List games = new ArrayList<>();
+
+ for(Map.Entry tableEntry: matchTable.getMatchTable().entrySet()) {
+ HostGame game = tableEntry.getValue();
+ if(game != null) {
+ games.add(game);
+ }
+ }
+
+ HostedGamesRequestEncoder encoder = new HostedGamesRequestEncoder();
+ try {
+ byte[] message = encoder.encode(new HostGamesRequest(games));
+ for(ClientAddress address: clientsAddresses) {
+ serverSocket.send(new DatagramPacket(message, message.length, InetAddress.getByName(address.getIp()), 4941));
+ }
+ } catch (InvalidMessageException | IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }, period, period);
+ }
+
+ /**
+ * Flushes the match table at a requested interval
+ * @param period interval to flush table
+ */
+ private void scheduleFlush(int period) {
+ scheduler.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ matchTable.getMatchTable().clear();
+ }
+ }, period, period);
+ }
+
+ private void run() throws IOException{
+ while(true) {
+ DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
+ serverSocket.receive(receivePacket);
+
+ BinaryMessageDecoder messageDecoder = new BinaryMessageDecoder(receivePacket.getData());
+ switch (MessageType.fromByte(messageDecoder.getHeaderMessageType())){
+ case HOST_GAME:
+ //decode and update table
+ HostGameMessageDecoder decoder = new HostGameMessageDecoder();
+ HostGame newKnownGame;
+ try{
+ newKnownGame = (HostGame) decoder.decode(messageDecoder.getMessageBody());
+ newKnownGame.setIp(receivePacket.getAddress().getHostAddress());
+ this.matchTable.addEntry(new ClientAddress(receivePacket.getAddress().getHostAddress(), receivePacket.getPort()), newKnownGame);
+
+ }catch (InvalidMessageException e){
+ System.out.println(e);
+ System.err.println("Message received that is not a hostedGame packet");
+ }
+ break;
+ case HOSTED_GAMES_REQUEST:
+ //update known clients
+ HostedGamesRequestDecoder decoder2 = new HostedGamesRequestDecoder();
+ HostGamesRequest newKnownGames;
+ try{
+ newKnownGames = (HostGamesRequest) decoder2.decode(messageDecoder.getMessageBody());
+ if (newKnownGames.getKnownGames().size() == 0){
+ //this is just an alert message with no content
+ clientsAddresses.add(new ClientAddress(receivePacket.getAddress().getHostAddress(), receivePacket.getPort()));
+ }
+ }catch (InvalidMessageException e){
+ System.out.println(e);
+ System.err.println("Message received that is not a hostedGamesRequest packet");
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/matchBrowser/src/test/java/model/MatchTableTest.java b/matchBrowser/src/test/java/model/MatchTableTest.java
new file mode 100644
index 00000000..530412f9
--- /dev/null
+++ b/matchBrowser/src/test/java/model/MatchTableTest.java
@@ -0,0 +1,29 @@
+package model;
+
+import network.Messages.Enums.RaceStatusEnum;
+import network.Messages.HostGame;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+
+public class MatchTableTest {
+ private MatchTable testTable;
+
+ @Before
+ public void setUp() {
+ testTable = new MatchTable();
+ }
+
+ @Test
+ public void testTable() {
+ HostGame entry = new HostGame("127.0.0.1", 4942, (byte)1, (byte)1, RaceStatusEnum.PRESTART, (byte)6, (byte)1);
+
+ testTable.addEntry(new ClientAddress("127.0.0.1", 3779), entry);
+
+ assertEquals(testTable.getMatchTable().get(new ClientAddress("127.0.0.1", 4942)), entry);
+ }
+}
diff --git a/pom.xml b/pom.xml
index ee4cba6f..5f38965a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,6 +10,7 @@
racevisionGame
dedicatedServer
+ matchBrowser
https://eng-git.canterbury.ac.nz/SENG302-2016/team-7
diff --git a/racevisionGame/src/main/java/mock/app/Event.java b/racevisionGame/src/main/java/mock/app/Event.java
index 70bcf177..4b8a6bc6 100644
--- a/racevisionGame/src/main/java/mock/app/Event.java
+++ b/racevisionGame/src/main/java/mock/app/Event.java
@@ -10,6 +10,8 @@ import mock.model.commandFactory.CompositeCommand;
import mock.model.wind.ShiftingWindGenerator;
import mock.model.wind.WindGenerator;
import mock.xml.RaceXMLCreator;
+import network.Messages.Enums.RaceStatusEnum;
+import network.Messages.HostGame;
import network.Messages.LatestMessages;
import shared.dataInput.*;
import shared.enums.XMLFileType;
@@ -20,7 +22,8 @@ import shared.exceptions.XMLReaderException;
import shared.model.Bearing;
import shared.model.Constants;
-import java.io.*;
+import java.io.IOException;
+import java.net.Inet4Address;
import java.nio.charset.StandardCharsets;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
@@ -105,6 +108,8 @@ public class Event {
boatsXMLFile = "mock/mockXML/boatsSinglePlayer.xml";
}
+ double windAngle = 300;
+
//Read XML files.
try {
//this.raceXML = RaceXMLCreator.alterRaceToWind(raceXMLFile, 90);
@@ -113,7 +118,7 @@ public class Event {
//this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8), 1000, 5000);
this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, -1, true);
} else {
- this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, 300, false);
+ this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, windAngle, false);
}
this.boatXML = XMLReader.readXMLFileToString(boatsXMLFile, StandardCharsets.UTF_8);
@@ -146,7 +151,7 @@ public class Event {
this.latestMessages = new LatestMessages();
WindGenerator windGenerator = new ShiftingWindGenerator(
- Bearing.fromDegrees(225),
+ Bearing.fromDegrees(windAngle),
12
);
@@ -225,4 +230,14 @@ public class Event {
return raceXML;
}
+ /**
+ * Creates the needed data type for a network packet
+ * @return hostGame Ac35DataType
+ * @throws IOException Inet4Address issue
+ */
+ public HostGame getHostedGameData() throws IOException{
+ String ip = Inet4Address.getLocalHost().getHostAddress();
+ return new HostGame(ip, 3779,(byte) 1,(byte) 1, RaceStatusEnum.PRESTART, (byte) 1, (byte) 1);
+ }
+
}
diff --git a/racevisionGame/src/main/java/mock/app/MockOutput.java b/racevisionGame/src/main/java/mock/app/MockOutput.java
index 023235cc..198dcc3f 100644
--- a/racevisionGame/src/main/java/mock/app/MockOutput.java
+++ b/racevisionGame/src/main/java/mock/app/MockOutput.java
@@ -60,7 +60,6 @@ public class MockOutput implements RunnableWithFramePeriod {
try {
Thread.sleep(500);
-
} catch (InterruptedException e) {
//If we get interrupted, exit the function.
Logger.getGlobal().log(Level.WARNING, "MockOutput.run().sleep(waitForXMLs) was interrupted on thread: " + Thread.currentThread(), e);
@@ -72,7 +71,6 @@ public class MockOutput implements RunnableWithFramePeriod {
}
}
-
long previousFrameTime = System.currentTimeMillis();
diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java
index 1d966cd4..41fbe4d8 100644
--- a/racevisionGame/src/main/java/mock/model/MockRace.java
+++ b/racevisionGame/src/main/java/mock/model/MockRace.java
@@ -129,6 +129,7 @@ public class MockRace extends RaceState {
this.boats.add(mockBoat);
getRaceDataSource().incrementSequenceNumber();
+
}
/**
diff --git a/racevisionGame/src/main/java/mock/model/RaceServer.java b/racevisionGame/src/main/java/mock/model/RaceServer.java
index c9575ce3..93a154b0 100644
--- a/racevisionGame/src/main/java/mock/model/RaceServer.java
+++ b/racevisionGame/src/main/java/mock/model/RaceServer.java
@@ -80,7 +80,6 @@ public class RaceServer {
updateXMLFiles();
}
-
/**
* Checks if the race/boat/regatta data sources have changed, and if they have, update their xml representations.
*/
diff --git a/racevisionGame/src/main/java/network/MessageDecoders/HostGameMessageDecoder.java b/racevisionGame/src/main/java/network/MessageDecoders/HostGameMessageDecoder.java
new file mode 100644
index 00000000..33d3a334
--- /dev/null
+++ b/racevisionGame/src/main/java/network/MessageDecoders/HostGameMessageDecoder.java
@@ -0,0 +1,64 @@
+package network.MessageDecoders;
+
+import network.Exceptions.InvalidMessageException;
+import network.Messages.AC35Data;
+import network.Messages.CourseWinds;
+import network.Messages.Enums.RaceStatusEnum;
+import network.Messages.HostGame;
+import network.Messages.RaceStatus;
+
+import java.util.Arrays;
+
+import static network.Utils.ByteConverter.bytesToInt;
+import static network.Utils.ByteConverter.bytesToLong;
+
+public class HostGameMessageDecoder implements MessageDecoder {
+
+ /**
+ * The encoded message.
+ */
+ private byte[] encodedMessage;
+
+ /**
+ * The decoded message.
+ */
+ private HostGame message;
+
+ /**
+ * Constructor
+ */
+ public HostGameMessageDecoder() {
+ }
+
+ @Override
+ public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException {
+ this.encodedMessage = encodedMessage;
+
+ try{
+ byte ipPart1 = encodedMessage[0];
+ byte ipPart2 = encodedMessage[1];
+ byte ipPart3 = encodedMessage[2];
+ byte ipPart4 = encodedMessage[3];
+ String ipString = bytesToLong(ipPart1) + "." + bytesToLong(ipPart2) + "." + bytesToLong(ipPart3) + "." + bytesToLong(ipPart4);
+// System.out.println(ipString);
+ int port = bytesToInt(Arrays.copyOfRange(encodedMessage, 4, 8));
+ byte map = encodedMessage[8];
+ byte speed = encodedMessage[9];
+ byte status = encodedMessage[10];
+ byte requiredNumPlayers = encodedMessage[11];
+ byte currentNumPlayers = encodedMessage[12];
+
+
+ message = new HostGame(ipString, port, map,
+ speed, RaceStatusEnum.fromByte(status),
+ requiredNumPlayers, currentNumPlayers);
+
+ return message;
+
+ } catch (Exception e) {
+ throw new InvalidMessageException("Could not decode Host game message.", e);
+ }
+ }
+
+
+}
diff --git a/racevisionGame/src/main/java/network/MessageDecoders/HostedGamesRequestDecoder.java b/racevisionGame/src/main/java/network/MessageDecoders/HostedGamesRequestDecoder.java
new file mode 100644
index 00000000..34e53748
--- /dev/null
+++ b/racevisionGame/src/main/java/network/MessageDecoders/HostedGamesRequestDecoder.java
@@ -0,0 +1,35 @@
+package network.MessageDecoders;
+
+import network.Exceptions.InvalidMessageException;
+import network.Messages.AC35Data;
+import network.Messages.HostGame;
+import network.Messages.HostGamesRequest;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static network.Utils.ByteConverter.bytesToInt;
+
+public class HostedGamesRequestDecoder implements MessageDecoder{
+ @Override
+ public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException {
+ try{
+ int numberOfGames = bytesToInt(Arrays.copyOfRange(encodedMessage, 0, 4));
+
+ HostGameMessageDecoder lineDecoder = new HostGameMessageDecoder();
+ List knownGames = new ArrayList<>();
+ int byteIndex = 4;
+ for (int i = 0; i < numberOfGames; i++){
+ knownGames.add((HostGame) lineDecoder.decode(Arrays.copyOfRange(encodedMessage, byteIndex, byteIndex+13)));
+ byteIndex += 13;
+ }
+
+ return new HostGamesRequest(knownGames);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new InvalidMessageException("Could not decode Host game message.", e);
+ }
+ }
+}
diff --git a/racevisionGame/src/main/java/network/MessageEncoders/HostGameMessageEncoder.java b/racevisionGame/src/main/java/network/MessageEncoders/HostGameMessageEncoder.java
new file mode 100644
index 00000000..8fbe30d2
--- /dev/null
+++ b/racevisionGame/src/main/java/network/MessageEncoders/HostGameMessageEncoder.java
@@ -0,0 +1,52 @@
+package network.MessageEncoders;
+
+import network.Exceptions.InvalidMessageException;
+import network.Messages.AC35Data;
+import network.Messages.HostGame;
+
+import java.nio.ByteBuffer;
+
+import static network.Utils.ByteConverter.intToBytes;
+
+
+public class HostGameMessageEncoder implements MessageEncoder{
+
+ /**
+ * Constructor
+ */
+ public HostGameMessageEncoder() {
+ }
+
+ @Override
+ public byte[] encode(AC35Data message) throws InvalidMessageException {
+ try{
+ //Downcast
+ HostGame hostGame = (HostGame) message;
+
+ ByteBuffer hostGameMessage = ByteBuffer.allocate(13);
+
+ ByteBuffer ipBytes = ByteBuffer.allocate(4);
+ String ip = hostGame.getIp();
+ String[] ipValues = ip.split("\\.");
+ for(String value:ipValues){
+ ipBytes.put(intToBytes(Integer.parseInt(value), 1)[0]);
+ }
+ byte raceStatus = hostGame.getStatus().getValue();
+
+ hostGameMessage.put(ipBytes.array());
+ hostGameMessage.put(intToBytes(hostGame.getPort()));
+ hostGameMessage.put(hostGame.getMap());
+ hostGameMessage.put(hostGame.getSpeed());
+ hostGameMessage.put(raceStatus);
+ hostGameMessage.put(hostGame.getRequiredNumPlayers());
+ hostGameMessage.put(hostGame.getCurrentNumPlayers());
+
+
+// System.out.println(hostGameMessage.array()[4]);
+ return hostGameMessage.array();
+
+ } catch (Exception e) {
+ throw new InvalidMessageException("Could not encode Host game message.", e);
+ }
+ }
+}
diff --git a/racevisionGame/src/main/java/network/MessageEncoders/HostedGamesRequestEncoder.java b/racevisionGame/src/main/java/network/MessageEncoders/HostedGamesRequestEncoder.java
new file mode 100644
index 00000000..80942a31
--- /dev/null
+++ b/racevisionGame/src/main/java/network/MessageEncoders/HostedGamesRequestEncoder.java
@@ -0,0 +1,43 @@
+package network.MessageEncoders;
+
+import network.Exceptions.InvalidMessageException;
+import network.Messages.AC35Data;
+import network.Messages.HostGame;
+import network.Messages.HostGamesRequest;
+
+import java.nio.ByteBuffer;
+
+import static network.Utils.ByteConverter.intToBytes;
+
+public class HostedGamesRequestEncoder implements MessageEncoder{
+
+ /**
+ * Constructor
+ */
+ public HostedGamesRequestEncoder() {
+ }
+
+ @Override
+ public byte[] encode(AC35Data message) throws InvalidMessageException {
+ try{
+ //Downcast
+ HostGamesRequest hostGamesRequest = (HostGamesRequest) message;
+
+ int numGames = hostGamesRequest.getKnownGames().size();
+
+ ByteBuffer hostedGamesRequestMessage = ByteBuffer.allocate(4+13*numGames);
+
+ hostedGamesRequestMessage.put(intToBytes(numGames));
+
+ HostGameMessageEncoder lineEncoder = new HostGameMessageEncoder();
+ for (HostGame line: hostGamesRequest.getKnownGames()) {
+ hostedGamesRequestMessage.put(lineEncoder.encode(line));
+ }
+
+ return hostedGamesRequestMessage.array();
+
+ }catch(Exception e){
+ throw new InvalidMessageException("Could not encode Host game message.", e);
+ }
+ }
+}
diff --git a/racevisionGame/src/main/java/network/Messages/Enums/MessageType.java b/racevisionGame/src/main/java/network/Messages/Enums/MessageType.java
index aed5d70a..0941fd08 100644
--- a/racevisionGame/src/main/java/network/Messages/Enums/MessageType.java
+++ b/racevisionGame/src/main/java/network/Messages/Enums/MessageType.java
@@ -39,6 +39,10 @@ public enum MessageType {
*/
ASSIGN_PLAYER_BOAT(121),
+ HOST_GAME(108),
+
+ HOSTED_GAMES_REQUEST(109),
+
NOTAMESSAGE(0);
diff --git a/racevisionGame/src/main/java/network/Messages/HostGame.java b/racevisionGame/src/main/java/network/Messages/HostGame.java
new file mode 100644
index 00000000..7fd5cb81
--- /dev/null
+++ b/racevisionGame/src/main/java/network/Messages/HostGame.java
@@ -0,0 +1,89 @@
+package network.Messages;
+
+
+import network.Messages.Enums.MessageType;
+import network.Messages.Enums.RaceStatusEnum;
+import network.Utils.ByteConverter;
+
+import java.nio.ByteBuffer;
+
+import static network.Utils.ByteConverter.intToBytes;
+
+public class HostGame extends AC35Data {
+
+ private String ip;
+ private int port;
+ private byte map;
+ private byte speed;
+ private RaceStatusEnum status;
+ private byte requiredNumPlayers;
+ private byte currentNumPlayers;
+
+ public HostGame(String ip, int port, byte map, byte speed,
+ RaceStatusEnum status, byte requiredNumPlayers,
+ byte currentNumPlayers) {
+ super(MessageType.HOST_GAME);
+ this.ip = ip;
+ this.port = port;
+ this.map = map;
+ this.speed = speed;
+ this.status = status;
+ this.requiredNumPlayers = requiredNumPlayers;
+ this.currentNumPlayers = currentNumPlayers;
+
+ }
+
+ /**
+ * @return the ip of host
+ */
+ public String getIp() {
+ return ip;
+ }
+
+ /**
+ * @return the port of host
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * @return the map index
+ */
+ public byte getMap() {
+ return map;
+ }
+
+ /**
+ * @return the speed value of game
+ */
+ public byte getSpeed() {
+ return speed;
+ }
+
+ /**
+ * @return the status of race
+ */
+ public RaceStatusEnum getStatus() {
+ return status;
+ }
+
+ /**
+ * @return required number of players
+ */
+ public byte getRequiredNumPlayers() {
+ return requiredNumPlayers;
+ }
+
+ /**
+ * @return current number of players
+ */
+ public byte getCurrentNumPlayers() {
+ return currentNumPlayers;
+ }
+
+ public void setIp(String ip) {
+ this.ip = ip;
+ }
+}
+
diff --git a/racevisionGame/src/main/java/network/Messages/HostGamesRequest.java b/racevisionGame/src/main/java/network/Messages/HostGamesRequest.java
new file mode 100644
index 00000000..080b4e3c
--- /dev/null
+++ b/racevisionGame/src/main/java/network/Messages/HostGamesRequest.java
@@ -0,0 +1,23 @@
+package network.Messages;
+
+import network.Messages.Enums.MessageType;
+
+import java.util.List;
+
+public class HostGamesRequest extends AC35Data{
+
+ private List knownGames;
+
+ /**
+ * Constructor
+ * @param knownGames games known by sender
+ */
+ public HostGamesRequest(List knownGames) {
+ super(MessageType.HOSTED_GAMES_REQUEST);
+ this.knownGames = knownGames;
+ }
+
+ public List getKnownGames() {
+ return knownGames;
+ }
+}
diff --git a/racevisionGame/src/main/java/visualiser/Controllers/HostGameController.java b/racevisionGame/src/main/java/visualiser/Controllers/HostGameController.java
index 6f82f87d..2e267031 100644
--- a/racevisionGame/src/main/java/visualiser/Controllers/HostGameController.java
+++ b/racevisionGame/src/main/java/visualiser/Controllers/HostGameController.java
@@ -9,7 +9,11 @@ import javafx.scene.image.ImageView;
import mock.app.Event;
import mock.exceptions.EventConstructionException;
import visualiser.app.App;
+import visualiser.app.MatchBrowserSingleton;
+import visualiser.network.MatchBrowserInterface;
+
import java.io.IOException;
+import java.net.DatagramSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
@@ -24,11 +28,17 @@ public class HostGameController extends Controller {
private @FXML ImageView mapImage;
private ArrayList listOfMaps;
private int currentMapIndex = 0;
+ private DatagramSocket udpSocket;
+ private MatchBrowserInterface matchBrowserInterface;
public void initialize() {
loadMaps();
+ this.udpSocket = MatchBrowserSingleton.getInstance().getUdpSocket();
+ this.matchBrowserInterface = MatchBrowserSingleton.getInstance().getMatchBrowserInterface();
}
+
+
/**
* Loads in the list of playable maps to be selected from.
*/
@@ -55,6 +65,8 @@ public class HostGameController extends Controller {
App.game = new Event(false, currentMapIndex);
App.gameType = currentMapIndex;
connectSocket("localhost", 4942);
+ alertMatchBrowser();
+
} catch (EventConstructionException e) {
Logger.getGlobal().log(Level.SEVERE, "Could not create Event.", e);
throw new RuntimeException(e);
@@ -63,19 +75,32 @@ public class HostGameController extends Controller {
}
}
+ /**
+ * Sends info to the match browser so clients can see it
+ */
+ public void alertMatchBrowser(){
+ try{
+ matchBrowserInterface.startSendingHostData(App.game.getHostedGameData(), udpSocket);
+ }catch (IOException e){
+ System.err.println("failed to send out hosted game info");
+ }
+ }
+
/**
* Connect to a socket
* @param address address of the server
* @param port port that the server is run off
+ * @throws IOException socket error
*/
public void connectSocket(String address, int port) throws IOException {
Socket socket = new Socket(address, port);
- RaceStartController rsc = (RaceStartController)loadScene("raceStart.fxml");
- rsc.enterLobby(socket, true);
+ InGameLobbyController iglc = (InGameLobbyController)loadScene("gameLobby.fxml");
+ iglc.enterGameLobby(socket, true);
}
/**
* Menu button pressed. Prompt alert then return to menu
+ * @throws IOException socket error
*/
public void menuBtnPressed() throws Exception {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
diff --git a/racevisionGame/src/main/java/visualiser/Controllers/InGameLobbyController.java b/racevisionGame/src/main/java/visualiser/Controllers/InGameLobbyController.java
new file mode 100644
index 00000000..3366b790
--- /dev/null
+++ b/racevisionGame/src/main/java/visualiser/Controllers/InGameLobbyController.java
@@ -0,0 +1,363 @@
+package visualiser.Controllers;
+
+import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
+import javafx.animation.AnimationTimer;
+import javafx.application.Platform;
+import javafx.collections.FXCollections;
+import javafx.collections.ListChangeListener;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.geometry.Insets;
+import javafx.scene.Node;
+import javafx.scene.control.Alert;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.Label;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.GridPane;
+import javafx.scene.shape.MeshView;
+import mock.app.Event;
+import network.Messages.Enums.RaceStatusEnum;
+import network.Messages.Enums.RequestToJoinEnum;
+import visualiser.app.App;
+import visualiser.gameController.ControllerClient;
+import visualiser.layout.SeaSurface;
+import visualiser.layout.Subject3D;
+import visualiser.layout.View3D;
+import visualiser.model.VisualiserBoat;
+import visualiser.model.VisualiserRaceEvent;
+import visualiser.model.VisualiserRaceState;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.URL;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Controller for Hosting a game.
+ */
+public class InGameLobbyController extends Controller {
+ @FXML
+ private ImageView imageView;
+
+ @FXML
+ GridPane playerContainer;
+
+
+ @FXML
+ private Label playerLabel;
+
+ @FXML
+ private Label playerLabel2;
+
+ @FXML
+ private Label playerLabel3;
+
+ @FXML
+ private Label playerLabel4;
+
+ @FXML
+ private Label playerLabel5;
+
+ @FXML
+ private Label playerLabel6;
+
+ @FXML
+ private Label countdownLabel;
+
+ @FXML
+ private AnchorPane countdownTenPane;
+
+ @FXML
+ private Label countdownTenText;
+
+ private Event game;
+
+ private View3D playerBoat;
+
+ private VisualiserRaceEvent visualiserRaceEvent;
+
+ private boolean isHost;
+
+ private ControllerClient controllerClient;
+
+ private ArrayList