diff --git a/matchBrowser/pom.xml b/matchBrowser/pom.xml
new file mode 100644
index 00000000..fb8cabe9
--- /dev/null
+++ b/matchBrowser/pom.xml
@@ -0,0 +1,82 @@
+
+
+
+ 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
+
+
+
+
+ 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..e189c052
--- /dev/null
+++ b/matchBrowser/src/main/java/app/Main.java
@@ -0,0 +1,7 @@
+package app;
+
+/**
+ * Used when starting the matchmaking browser
+ */
+public class Main {
+}
diff --git a/matchBrowser/src/main/java/model/MatchTable.java b/matchBrowser/src/main/java/model/MatchTable.java
new file mode 100644
index 00000000..d2798939
--- /dev/null
+++ b/matchBrowser/src/main/java/model/MatchTable.java
@@ -0,0 +1,34 @@
+package model;
+
+
+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(ArrayList newEntry) {
+ //create a key from the ip and port
+ TableKey entryKey = new TableKey((String) newEntry.get(0), (Integer) newEntry.get(1));
+
+ //get the rest of the entry and use it as the value
+ List entryItems = new ArrayList();
+ for(int i = 2; i getMatchTable() {
+ return 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..6b803fd9
--- /dev/null
+++ b/matchBrowser/src/main/java/model/TableKey.java
@@ -0,0 +1,30 @@
+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 == key.ip && port == key.port;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = port;
+ result = 31 * result + ip.hashCode();
+ return result;
+ }
+}
diff --git a/matchBrowser/src/main/java/networkInterface/InInterface.java b/matchBrowser/src/main/java/networkInterface/InInterface.java
new file mode 100644
index 00000000..5d729e08
--- /dev/null
+++ b/matchBrowser/src/main/java/networkInterface/InInterface.java
@@ -0,0 +1,46 @@
+package networkInterface;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * Holds the output for the network for
+ */
+public class InInterface {
+ private DatagramSocket serverSocket;
+ private byte[] receiveData = new byte[1024];
+ private byte[] sendData = new byte[1024];
+
+ public InInterface(){
+ try {
+ this.serverSocket = new DatagramSocket(3779);
+
+ this.run();
+ } catch (IOException e) {
+ System.err.println("Error listening on port: " + this.serverSocket.getLocalPort() + ".");
+ System.exit(-1);
+ }
+
+ }
+
+ private void run() throws IOException{
+ while(true) {
+ DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
+ serverSocket.receive(receivePacket);
+
+ //decode and update table
+
+ //client ip and port
+ InetAddress IPAddress = receivePacket.getAddress();
+ int port = receivePacket.getPort();
+
+
+
+// String capitalizedSentence = sentence.toUpperCase();
+// sendData = capitalizedSentence.getBytes();
+// DatagramPacket sendPacket =
+// new DatagramPacket(sendData, sendData.length, IPAddress, port);
+// serverSocket.send(sendPacket);
+ }
+ }
+}
diff --git a/matchBrowser/src/main/java/networkInterface/OutInterface.java b/matchBrowser/src/main/java/networkInterface/OutInterface.java
new file mode 100644
index 00000000..18746004
--- /dev/null
+++ b/matchBrowser/src/main/java/networkInterface/OutInterface.java
@@ -0,0 +1,7 @@
+package networkInterface;
+
+/**
+ * Holds the connection to the network for output
+ */
+public class OutInterface {
+}
diff --git a/matchBrowser/src/test/java/model/MatchTableTest.java b/matchBrowser/src/test/java/model/MatchTableTest.java
new file mode 100644
index 00000000..6c16cd9e
--- /dev/null
+++ b/matchBrowser/src/test/java/model/MatchTableTest.java
@@ -0,0 +1,27 @@
+package model;
+
+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() {
+ ArrayList entry = new ArrayList(Arrays.asList("127.0.0.1", 4942, 1, 1, 2, 6, 1));
+
+ testTable.addEntry(entry);
+
+ assertEquals(testTable.getMatchTable().get(new TableKey("127.0.0.1", 4942)), Arrays.asList(1, 1, 2, 6, 1));
+ }
+}
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..3991b83e
--- /dev/null
+++ b/racevisionGame/src/main/java/network/MessageDecoders/HostGameMessageDecoder.java
@@ -0,0 +1,63 @@
+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;
+
+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 = ipPart1 + "." + ipPart2 + "." + ipPart3 + "." + 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..95f5cf5c
--- /dev/null
+++ b/racevisionGame/src/main/java/network/MessageDecoders/HostedGamesRequestDecoder.java
@@ -0,0 +1,34 @@
+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+14)));
+ byteIndex += 14;
+ }
+
+ return new HostGamesRequest(knownGames);
+
+ } catch (Exception e) {
+ 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..0a5c9a08
--- /dev/null
+++ b/racevisionGame/src/main/java/network/MessageEncoders/HostGameMessageEncoder.java
@@ -0,0 +1,54 @@
+package network.MessageEncoders;
+
+import network.Exceptions.InvalidMessageException;
+import network.Messages.AC35Data;
+import network.Messages.HostGame;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.List;
+
+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(14);
+
+ 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..4b4b52d1
--- /dev/null
+++ b/racevisionGame/src/main/java/network/MessageEncoders/HostedGamesRequestEncoder.java
@@ -0,0 +1,44 @@
+package network.MessageEncoders;
+
+import network.Exceptions.InvalidMessageException;
+import network.Messages.AC35Data;
+import network.Messages.HostGame;
+import network.Messages.HostGamesRequest;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+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+14*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..d03292fe
--- /dev/null
+++ b/racevisionGame/src/main/java/network/Messages/HostGame.java
@@ -0,0 +1,79 @@
+package network.Messages;
+
+
+import network.Messages.Enums.MessageType;
+import network.Messages.Enums.RaceStatusEnum;
+
+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;
+ }
+}
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..4ecfc2d2
--- /dev/null
+++ b/racevisionGame/src/main/java/network/Messages/HostGamesRequest.java
@@ -0,0 +1,22 @@
+package network.Messages;
+
+import network.Messages.Enums.MessageType;
+
+import java.util.List;
+
+public class HostGamesRequest extends AC35Data{
+
+ private List knownGames;
+
+ /**
+ * Constructor
+ */
+ public HostGamesRequest(List knownGames) {
+ super(MessageType.HOSTED_GAMES_REQUEST);
+ this.knownGames = knownGames;
+ }
+
+ public List getKnownGames() {
+ return knownGames;
+ }
+}
diff --git a/racevisionGame/src/test/java/network/MessageDecoders/HostGameMessageDecoderTest.java b/racevisionGame/src/test/java/network/MessageDecoders/HostGameMessageDecoderTest.java
new file mode 100644
index 00000000..2d73241d
--- /dev/null
+++ b/racevisionGame/src/test/java/network/MessageDecoders/HostGameMessageDecoderTest.java
@@ -0,0 +1,36 @@
+package network.MessageDecoders;
+
+import network.MessageEncoders.HostGameMessageEncoder;
+import network.Messages.AC35Data;
+import network.Messages.Enums.RaceStatusEnum;
+import network.Messages.HostGame;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class HostGameMessageDecoderTest {
+
+ @Test
+ public void hostGameMessageDecoderTest() throws Exception {
+ HostGame testHost = new HostGame("127.0.0.1", 3779, (byte) 1, (byte) 2, RaceStatusEnum.PRESTART, (byte) 6, (byte) 2);
+
+
+ HostGameMessageEncoder encoder = new HostGameMessageEncoder();
+
+ byte[] encodedMessage = encoder.encode(testHost);
+
+ HostGameMessageDecoder decoder = new HostGameMessageDecoder();
+
+ HostGame decodedTest = (HostGame) decoder.decode(encodedMessage);
+
+ compareHostGameMessage(testHost, decodedTest);
+ }
+
+ public static void compareHostGameMessage(HostGame original, HostGame decoded) {
+ Assert.assertEquals(original.getIp(), decoded.getIp());
+ Assert.assertEquals(original.getPort(), decoded.getPort());
+ Assert.assertEquals(original.getSpeed(), decoded.getSpeed());
+ Assert.assertEquals(original.getStatus(), decoded.getStatus());
+ Assert.assertEquals(original.getRequiredNumPlayers(), decoded.getRequiredNumPlayers());
+ Assert.assertEquals(original.getCurrentNumPlayers(), decoded.getCurrentNumPlayers());
+ }
+}
diff --git a/racevisionGame/src/test/java/network/MessageDecoders/HostedGamesRequestDecoderTest.java b/racevisionGame/src/test/java/network/MessageDecoders/HostedGamesRequestDecoderTest.java
new file mode 100644
index 00000000..7567fe23
--- /dev/null
+++ b/racevisionGame/src/test/java/network/MessageDecoders/HostedGamesRequestDecoderTest.java
@@ -0,0 +1,38 @@
+package network.MessageDecoders;
+
+
+import network.MessageEncoders.HostGameMessageEncoder;
+import network.MessageEncoders.HostedGamesRequestEncoder;
+import network.Messages.Enums.RaceStatusEnum;
+import network.Messages.HostGame;
+import network.Messages.HostGamesRequest;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class HostedGamesRequestDecoderTest {
+ @Test
+ public void hostGamesRequestMessageDecoderTest() throws Exception {
+ HostGame testHostGame1 = new HostGame("127.0.0.1", 3779, (byte) 1, (byte) 2, RaceStatusEnum.PRESTART, (byte) 6, (byte) 2);
+ HostGame testHostGame2 = new HostGame("127.0.0.1", 3780, (byte) 1, (byte) 2, RaceStatusEnum.PRESTART, (byte) 6, (byte) 2);
+
+ List knownGames = Arrays.asList(testHostGame1, testHostGame2);
+
+ HostedGamesRequestEncoder encoder = new HostedGamesRequestEncoder();
+
+ byte[] encodedMessage = encoder.encode(new HostGamesRequest(knownGames));
+
+ HostedGamesRequestDecoder decoder = new HostedGamesRequestDecoder();
+
+ HostGamesRequest decodedTest = (HostGamesRequest) decoder.decode(encodedMessage);
+
+ compareHostGamesRequestMessage(knownGames, decodedTest.getKnownGames());
+ }
+
+ public static void compareHostGamesRequestMessage(List original, List decoded) {
+ Assert.assertEquals(original.get(0).getIp(), decoded.get(0).getIp());
+ Assert.assertEquals(original.get(1).getPort(), decoded.get(1).getPort());
+ }
+}