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