diff --git a/pom.xml b/pom.xml index 3aa99728..c2b04417 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,3 @@ - 4.0.0 @@ -77,6 +76,13 @@ 1.8.0 + + + javax.jmdns + jmdns + 3.4.2 + + @@ -159,4 +165,12 @@ + + + + Homer-Core + Homer-core-repo + https://nexus.arcsmed.at/content/repositories/homer.core + + \ No newline at end of file diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index 7739e99b..57e27ed8 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -8,14 +8,13 @@ import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.stage.Stage; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import seng302.visualiser.controllers.ViewManager; +import seng302.gameServer.ServerAdvertiser; + +import java.io.IOException; public class App extends Application { @@ -86,6 +85,21 @@ public class App extends Application { primaryStage.setScene(scene); primaryStage.show(); + primaryStage.setOnCloseRequest(e -> { +// ClientPacketParser.appClose(); +// ClientPacketParser.appClose(); + + + try { + ServerAdvertiser.getInstance().unregister(); + } catch (IOException e1) { + logger.warn("Could not un-register game"); + } + + System.exit(0); + }); + +// ClientState.primaryStage = primaryStage; primaryStage.setOnCloseRequest(e -> System.exit(0)); } @@ -94,6 +108,8 @@ public class App extends Application { parseArgs(args); } catch (ParseException e) { logger.error("Could not parse command line arguments"); + } catch (IOException e) { + e.printStackTrace(); } launch(args); diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 0b36ab16..ebe4abbe 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -1,37 +1,22 @@ package seng302.gameServer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; import javafx.scene.paint.Color; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.xml.sax.InputSource; -import seng302.gameServer.messages.BoatAction; -import seng302.gameServer.messages.BoatStatus; -import seng302.gameServer.messages.CustomizeRequestType; -import seng302.gameServer.messages.MarkRoundingMessage; -import seng302.gameServer.messages.MarkType; -import seng302.gameServer.messages.Message; -import seng302.gameServer.messages.RoundingBoatStatus; -import seng302.gameServer.messages.YachtEventCodeMessage; -import seng302.model.GeoPoint; -import seng302.model.Limit; -import seng302.model.Player; -import seng302.model.PolarTable; -import seng302.model.ServerYacht; +import seng302.gameServer.messages.*; +import seng302.model.*; import seng302.model.mark.CompoundMark; import seng302.model.mark.Mark; import seng302.model.mark.MarkOrder; import seng302.utilities.GeoUtility; import seng302.utilities.XMLParser; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.util.*; + /** * A Static class to hold information about the current state of the game (model) * Also contains logic for updating itself on regular time intervals on its own thread @@ -699,4 +684,11 @@ public class GameState implements Runnable { public static void resetCustomizationFlag() { customizationFlag = false; } + + public static Integer getSpacesLeft(){ + Integer numberOfPlayers = GameState.getPlayers().size(); + Integer maxPlayers = GameState.MAX_PLAYERS; + + return maxPlayers - numberOfPlayers - 1; + } } diff --git a/src/main/java/seng302/gameServer/MainServerThread.java b/src/main/java/seng302/gameServer/MainServerThread.java index 83fb304c..0aa4b3a6 100644 --- a/src/main/java/seng302/gameServer/MainServerThread.java +++ b/src/main/java/seng302/gameServer/MainServerThread.java @@ -1,14 +1,26 @@ package seng302.gameServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; import seng302.gameServer.messages.*; import seng302.model.GeoPoint; import seng302.model.Player; import seng302.model.PolarTable; import seng302.model.ServerYacht; import seng302.model.mark.CompoundMark; +import seng302.model.stream.xml.parser.RegattaXMLData; import seng302.utilities.GeoUtility; +import seng302.utilities.XMLGenerator; +import seng302.utilities.XMLParser; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; +import java.io.StringReader; import java.net.ServerSocket; import java.time.LocalDateTime; import java.util.*; @@ -37,6 +49,42 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { private ServerSocket serverSocket = null; private ArrayList serverToClientThreads = new ArrayList<>(); + private Logger logger = LoggerFactory.getLogger(MainServerThread.class); + + private void startAdvertisingServer(){ + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db; + Document doc; + XMLGenerator generator = new XMLGenerator(); + + try { + db = dbf.newDocumentBuilder(); + String regatta = generator.getRegattaAsXml(); + StringReader stringReader = new StringReader(regatta); + InputSource is = new InputSource(stringReader); + doc = db.parse(is); + } catch (ParserConfigurationException | IOException | SAXException e) { + logger.warn("Couldn't load race regatta"); + return; + } + + RegattaXMLData regattaXMLData = XMLParser.parseRegatta(doc); + + Integer spacesLeft = GameState.getSpacesLeft(); + + // No spaces left on server + if (spacesLeft < 1){ + return; + } + + // Start advertising server + try{ + ServerAdvertiser.getInstance().setMapName(regattaXMLData.getCourseName()).setSpacesLeft(spacesLeft); + ServerAdvertiser.getInstance().registerGame(PORT, regattaXMLData.getRegattaName()); + } catch (IOException e) { + logger.warn("Could not register server"); + } + } public MainServerThread() { new GameState("localhost"); @@ -45,6 +93,9 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { } catch (IOException e) { serverLog("IO error in server thread handler upon trying to make new server socket", 0); } + + startAdvertisingServer(); + PolarTable.parsePolarFile(getClass().getResourceAsStream("/config/acc_polars.csv")); GameState.addMarkPassListener(this::broadcastMessage); terminated = false; @@ -176,6 +227,12 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { } }); serverToClientThread.addDisconnectListener(this::clientDisconnected); + + try { + ServerAdvertiser.getInstance().setSpacesLeft(GameState.getSpacesLeft()); + } catch (IOException e) { + logger.warn("Couldn't update advertisement"); + } } /** @@ -202,10 +259,23 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { } } serverToClientThreads.remove(closedConnection); + + try { + ServerAdvertiser.getInstance().setSpacesLeft(GameState.getSpacesLeft()); + } catch (IOException e) { + logger.warn("Couldn't update advertisement"); + } + closedConnection.terminate(); } public void startGame() { + try { + ServerAdvertiser.getInstance().unregister(); + } catch (IOException e) { + logger.warn("Error unregistered server"); + } + initialiseBoatPositions(); Timer t = new Timer(); diff --git a/src/main/java/seng302/gameServer/ServerAdvertiser.java b/src/main/java/seng302/gameServer/ServerAdvertiser.java new file mode 100644 index 00000000..0b69b5a4 --- /dev/null +++ b/src/main/java/seng302/gameServer/ServerAdvertiser.java @@ -0,0 +1,156 @@ +package seng302.gameServer; + +import javax.jmdns.JmDNS; +import javax.jmdns.ServiceInfo; +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * Advertises the game server on the local network + */ +public class ServerAdvertiser { + /* + Our service name & protocol + + This must be in the format _Service._Proto.Name as per http://www.ietf.org/rfc/rfc2782.txt + Where Service is unique on the network, and protocol is usually _tcp. + + The pseudo-domain 'local.' must end in a full-stop. This is used to indicate that + the lookup should be performed using an IP multicast query on the local IP network. + + Read this before changing any of the following values + https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/NetServices/Articles/domainnames.html#//apple_ref/doc/uid/TP40002460-SW1 + */ + private static String SERVICE = "_partyatsea"; + private static String PROTOCOL = "_tcp"; + public static String SERVICE_TYPE = SERVICE + "." + PROTOCOL + ".local."; + + private static ServerAdvertiser instance = null; + private static JmDNS jmdnsInstance = null; + private ServiceInfo serviceInfo; // Note: Whenever this is changed, our service will be re-registered on the network. + + private Hashtable props; + + private ServerAdvertiser() throws IOException{ + jmdnsInstance = JmDNS.create(InetAddress.getByName(getLocalHostIp())); + + props = new Hashtable<>(); + props.put("map", ""); + props.put("spacesLeft", "0"); + } + + /** + * Get an instance of the ServerAdvertiser, create an instance if there isn't already one + * @return A ServerAdvertiser Instance + * @throws IOException If there was an exception creating the instance + */ + public static ServerAdvertiser getInstance() throws IOException { + if (instance == null){ + instance = new ServerAdvertiser(); + } + + return instance; + } + + /** + * Set the map name and broadcast an update on the network + * @param mapName The new map name + * @return The current ServerAdvertiser instance + */ + public ServerAdvertiser setMapName(String mapName){ + props.replace("map", mapName); + + if (serviceInfo != null){ + serviceInfo.setText(props); + } + + return instance; + } + + /** + * Set the spaces left on the server and broadcast an update on the network + * @param spacesLeft The number of spaces left on the server + * @return The current ServerAdvertiser instance + */ + public ServerAdvertiser setSpacesLeft(Integer spacesLeft){ + props.replace("spacesLeft", spacesLeft.toString()); + + if (serviceInfo != null){ + serviceInfo.setText(props); + } + + return instance; + } + + /** + * Register this service on the network + * + * Note: other parameters (map name/spaces left etc) are set after the + * service has been registered + * @param portNo The servers port number + * @param serverName The servers name + */ + public void registerGame(Integer portNo, String serverName) { + + serviceInfo = ServiceInfo.create(SERVICE_TYPE, serverName, portNo, 0, 0, props); + + new java.util.Timer().schedule( + new java.util.TimerTask() { + @Override + public void run() { + try { + jmdnsInstance.registerService(serviceInfo); + } catch (IOException e) { + System.out.println("Failed"); + } + } + }, 0); + } + + /** + * Unregister the service + */ + public void unregister(){ + if (serviceInfo != null) + jmdnsInstance.unregisterService(serviceInfo); + } + + /** + * Gets the local host ip address. + * + * @return the localhost ip address + */ + public static String getLocalHostIp() { + String ipAddress = null; + try { + Enumeration e = NetworkInterface.getNetworkInterfaces(); + while (e.hasMoreElements()) { + NetworkInterface ni = e.nextElement(); + if (ni.isLoopback()) + continue; + if(ni.isPointToPoint()) + continue; + if(ni.isVirtual()) + continue; + + Enumeration addresses = ni.getInetAddresses(); + while(addresses.hasMoreElements()) { + InetAddress address = addresses.nextElement(); + if(address instanceof Inet4Address) { // skip all ipv6 + ipAddress = address.getHostAddress(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + if (ipAddress == null) { + System.out.println("[HOST] Cannot obtain local host ip address."); + } + return ipAddress; + } +} diff --git a/src/main/java/seng302/gameServer/ServerDescription.java b/src/main/java/seng302/gameServer/ServerDescription.java new file mode 100644 index 00000000..02440382 --- /dev/null +++ b/src/main/java/seng302/gameServer/ServerDescription.java @@ -0,0 +1,73 @@ +package seng302.gameServer; + +public class ServerDescription { + private String address; + private Integer portNum; + private String serverName; + private String mapName; + private Integer spacesLeft; + + public ServerDescription(String serverName, String mapName, Integer spacesLeft, String address, Integer portNum){ + this.serverName = serverName; + this.mapName = mapName; + this.spacesLeft = spacesLeft; + this.address = address; + this.portNum = portNum; + } + + + public String getName() { + return serverName; + } + + public String getMapName() { + return mapName; + } + + public Integer portNumber() { + return portNum; + } + + public String getAddress(){ + return address; + } + + public Integer spacesLeft() { + return spacesLeft; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!ServerDescription.class.isAssignableFrom(obj.getClass())) { + return false; + } + final ServerDescription other = (ServerDescription) obj; + + if (!this.getAddress().equals(other.getAddress()) ) { + return false; + } + + if (!this.portNumber().equals(other.portNumber())){ + return false; + } + + if (!this.getMapName().equals(other.getMapName())){ + return false; + } + + if (!this.getName().equals(other.getName())){ + return false; + } + + return true; + } + + @Override + public int hashCode() { + return this.getName().hashCode() + this.getAddress().hashCode() + + this.portNumber().hashCode() + this.getMapName().hashCode(); + } +} diff --git a/src/main/java/seng302/gameServer/ServerToClientThread.java b/src/main/java/seng302/gameServer/ServerToClientThread.java index 3a9b4057..82e481ce 100644 --- a/src/main/java/seng302/gameServer/ServerToClientThread.java +++ b/src/main/java/seng302/gameServer/ServerToClientThread.java @@ -1,59 +1,24 @@ package seng302.gameServer; -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import seng302.gameServer.messages.*; +import seng302.model.Player; +import seng302.model.ServerYacht; +import seng302.model.stream.packets.PacketType; +import seng302.model.stream.packets.StreamPacket; +import seng302.model.stream.xml.generator.Race; +import seng302.utilities.XMLGenerator; + +import java.io.*; import java.net.Socket; import java.net.SocketException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Observable; -import java.util.Observer; +import java.util.*; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; import java.util.zip.CRC32; import java.util.zip.Checksum; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import seng302.gameServer.messages.BoatAction; -import seng302.gameServer.messages.BoatLocationMessage; -import seng302.gameServer.messages.ClientType; -import seng302.gameServer.messages.CustomizeRequestType; -import seng302.gameServer.messages.Message; -import seng302.gameServer.messages.RegistrationResponseMessage; -import seng302.gameServer.messages.RegistrationResponseStatus; -import seng302.gameServer.messages.XMLMessage; -import seng302.gameServer.messages.XMLMessageSubType; -import seng302.gameServer.messages.YachtEventCodeMessage; -import seng302.gameServer.messages.YachtEventCodeMessage; -import seng302.model.Player; -import seng302.model.ServerYacht; -import seng302.model.stream.packets.PacketType; -import seng302.model.stream.packets.StreamPacket; -import seng302.model.stream.xml.generator.Race; -import seng302.model.stream.xml.generator.Regatta; -import seng302.utilities.XMLGenerator; -import seng302.gameServer.messages.BoatAction; -import seng302.gameServer.messages.BoatLocationMessage; -import seng302.gameServer.messages.ClientType; -import seng302.gameServer.messages.Message; -import seng302.gameServer.messages.RegistrationResponseMessage; -import seng302.gameServer.messages.RegistrationResponseStatus; -import seng302.gameServer.messages.XMLMessage; -import seng302.gameServer.messages.XMLMessageSubType; -import seng302.gameServer.messages.YachtEventCodeMessage; -import seng302.model.Player; -import seng302.model.ServerYacht; -import seng302.model.stream.packets.PacketType; -import seng302.model.stream.packets.StreamPacket; -import seng302.model.stream.xml.generator.Race; -import seng302.model.stream.xml.generator.Regatta; -import seng302.utilities.XMLGenerator; /** * A class describing a single connection to a Client for the purposes of sending and receiving on @@ -258,12 +223,6 @@ public class ServerToClientThread implements Runnable, Observer { race.addBoat(yacht); } - //@TODO calculate lat/lng values - xml.setRegatta( - new Regatta( - "Party Parrot Test Server", "Bermuda Test Course", - 57.6679590, 11.8503233) - ); xml.setRace(race); XMLMessage xmlMessage; diff --git a/src/main/java/seng302/gameServer/messages/MarkRoundingMessage.java b/src/main/java/seng302/gameServer/messages/MarkRoundingMessage.java index c32a6927..b1276814 100644 --- a/src/main/java/seng302/gameServer/messages/MarkRoundingMessage.java +++ b/src/main/java/seng302/gameServer/messages/MarkRoundingMessage.java @@ -22,6 +22,7 @@ public class MarkRoundingMessage extends Message{ * @param roundingBoatStatus roundingBoatStatus * @param roundingSide roundingSide * @param markId markId + * @param markType . */ public MarkRoundingMessage(int ackNumber, int raceId, int sourceId, RoundingBoatStatus roundingBoatStatus, RoundingSide roundingSide, MarkType markType, int markId) { diff --git a/src/main/java/seng302/model/ServerYacht.java b/src/main/java/seng302/model/ServerYacht.java index 64143023..859db93c 100644 --- a/src/main/java/seng302/model/ServerYacht.java +++ b/src/main/java/seng302/model/ServerYacht.java @@ -1,8 +1,5 @@ package seng302.model; -import java.util.HashMap; -import java.util.Observable; -import java.util.Observer; import javafx.scene.paint.Color; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,6 +8,10 @@ import seng302.gameServer.messages.BoatStatus; import seng302.model.mark.Mark; import seng302.utilities.GeoUtility; +import java.util.HashMap; +import java.util.Observable; +import java.util.Observer; + /** * Yacht class for the racing boat.

Class created to store more variables (eg. boat statuses) * compared to the XMLParser boat class, also done outside Boat class because some old variables are @@ -123,6 +124,7 @@ public class ServerYacht extends Observable { /** * Swaps the boats direction from one side of the wind to the other. + * @param windDirection . */ public void tackGybe(Double windDirection) { if (isAuto) { diff --git a/src/main/java/seng302/utilities/XMLGenerator.java b/src/main/java/seng302/utilities/XMLGenerator.java index 7fcc8efd..6c478af2 100644 --- a/src/main/java/seng302/utilities/XMLGenerator.java +++ b/src/main/java/seng302/utilities/XMLGenerator.java @@ -3,13 +3,15 @@ package seng302.utilities; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; +import seng302.gameServer.messages.XMLMessageSubType; +import seng302.model.stream.xml.generator.Race; +import seng302.model.stream.xml.generator.Regatta; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; -import seng302.model.stream.xml.generator.Race; -import seng302.model.stream.xml.generator.Regatta; -import seng302.gameServer.messages.XMLMessageSubType; +import java.util.Random; /** * An XML generator to generate the Race, Boat, and Regatta XML dynamically @@ -20,9 +22,14 @@ public class XMLGenerator { private static final String BOATS_TEMPLATE_NAME = "boats.ftlh"; private static final String RACE_TEMPLATE_NAME = "race.ftlh"; private Configuration configuration; - private Regatta regatta; + private Regatta regatta = null; private Race race; + public static Regatta DEFAULT_REGATTA = new Regatta("Party Parrot Test Server " + new Random().nextInt(100), + "Bermuda Test Course", + 57.6679590, + 11.8503233); + /** * Set up a configuration instance for Apache Freemake */ @@ -106,7 +113,7 @@ public class XMLGenerator { public String getRegattaAsXml(){ String result = null; - if (regatta == null) return null; + if (regatta == null) regatta = DEFAULT_REGATTA; try { result = parseToXmlString(REGATTA_TEMPLATE_NAME, XMLMessageSubType.REGATTA); diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index 443342de..8c6b7274 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -142,6 +142,7 @@ public class GameClient { } private void loadStartScreen() { + socketThread.setSocketToClose(); if (server != null) { server.terminate(); diff --git a/src/main/java/seng302/visualiser/ServerListener.java b/src/main/java/seng302/visualiser/ServerListener.java new file mode 100644 index 00000000..34b76fdb --- /dev/null +++ b/src/main/java/seng302/visualiser/ServerListener.java @@ -0,0 +1,113 @@ +package seng302.visualiser; + +import seng302.gameServer.ServerAdvertiser; +import seng302.gameServer.ServerDescription; + +import javax.jmdns.JmDNS; +import javax.jmdns.ServiceEvent; +import javax.jmdns.ServiceListener; +import java.io.IOException; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +import static seng302.gameServer.ServerAdvertiser.getLocalHostIp; + +/** + * Listens for servers on the local network + */ +public class ServerListener{ + private static ServerListener instance; + private ServerListenerDelegate delegate; + private JmDNS jmdns = null; + GameServeMonitor listener; + + private class GameServeMonitor implements ServiceListener { + private Set servers; + + GameServeMonitor(){ + servers = new HashSet<>(); + } + + /** + * A Service has been detected but not resolved + * @param event The ServiceEvent + */ + @Override + public void serviceAdded(ServiceEvent event) { + } + + /** + * A Service has been removed / unregistered + * @param event The ServiceEvent + */ + @Override + public void serviceRemoved(ServiceEvent event) { + String serverName = event.getInfo().getName(); + + ServerDescription toRemove = null; + + for (ServerDescription server : servers){ + if (server.getName().equals(serverName)){ + toRemove = server; + } + } + + if (toRemove != null){ + servers.remove(toRemove); + } + + delegate.serverRemoved(new ArrayList(servers)); + + // Get all other servers with the same name to respond if they are up + jmdns.requestServiceInfo(ServerAdvertiser.SERVICE_TYPE, serverName); + + } + + /** + * A Service has been added and resolved + * @param event The ServiceEvent + */ + @Override + public void serviceResolved(ServiceEvent event) { + String address = event.getInfo().getServer(); + Integer portNum = event.getInfo().getPort(); + + String serverName = event.getInfo().getName(); + String mapName = event.getInfo().getPropertyString("map"); + Integer spacesLeft = Integer.parseInt(event.getInfo().getPropertyString("spacesLeft")); + + ServerDescription serverDescription = new ServerDescription(serverName, mapName, spacesLeft, address, portNum); + + servers.remove(serverDescription); + servers.add(serverDescription); + + delegate.serverDetected(serverDescription, new ArrayList(servers)); + } + } + + private ServerListener() throws IOException { + jmdns = JmDNS.create(InetAddress.getByName(getLocalHostIp())); + listener = new GameServeMonitor(); + jmdns.addServiceListener(ServerAdvertiser.SERVICE_TYPE, listener); + } + + public static ServerListener getInstance() throws IOException { + if (instance == null){ + instance = new ServerListener(); + } + + return instance; + } + + /** + * Set the delegate to handle events + * @param delegate . + */ + public void setDelegate(ServerListenerDelegate delegate){ + this.delegate = delegate; + } + + +} diff --git a/src/main/java/seng302/visualiser/ServerListenerDelegate.java b/src/main/java/seng302/visualiser/ServerListenerDelegate.java new file mode 100644 index 00000000..15400a86 --- /dev/null +++ b/src/main/java/seng302/visualiser/ServerListenerDelegate.java @@ -0,0 +1,10 @@ +package seng302.visualiser; + +import seng302.gameServer.ServerDescription; + +import java.util.List; + +public interface ServerListenerDelegate { + void serverRemoved(List currentServers); + void serverDetected(ServerDescription serverDescription, List servers); +} diff --git a/src/main/java/seng302/visualiser/controllers/StartScreenController.java b/src/main/java/seng302/visualiser/controllers/StartScreenController.java index eced061e..af1f4c3c 100644 --- a/src/main/java/seng302/visualiser/controllers/StartScreenController.java +++ b/src/main/java/seng302/visualiser/controllers/StartScreenController.java @@ -5,6 +5,7 @@ import com.jfoenix.controls.JFXButton; import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; +import javafx.application.Platform; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; @@ -14,6 +15,26 @@ import javafx.scene.effect.DropShadow; import javafx.scene.paint.Color; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.ListView; +import javafx.scene.control.TextField; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.GridPane; +import seng302.gameServer.ServerAdvertiser; +import seng302.gameServer.ServerDescription; +import seng302.visualiser.GameClient; +import seng302.visualiser.ServerListener; +import seng302.visualiser.ServerListenerDelegate; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.URL; +import java.util.Enumeration; +import java.util.List; +import java.util.ResourceBundle; public class StartScreenController implements Initializable { @@ -26,6 +47,38 @@ public class StartScreenController implements Initializable { private Node serverList; private Logger logger = LoggerFactory.getLogger(StartScreenController.class); + public void initialize(URL url, ResourceBundle resourceBundle) { +// gameClient = new GameClient(holder); + + try { + ServerListener.getInstance().setDelegate(this); + } catch (IOException e) { + e.printStackTrace(); + } + + joinLobbyButton.setOnAction(event -> joinLobbyClicked()); + } +// +// /** +// * Loads the fxml content into the parent pane +// * @param jfxUrl +// * @return the controller of the fxml +// */ +// private Object setContentPane(String jfxUrl) { +// try { +// AnchorPane contentPane = (AnchorPane) startScreen2.getParent(); +// contentPane.getChildren().removeAll(); +// contentPane.getChildren().clear(); +// contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString()); +// FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(jfxUrl)); +// contentPane.getChildren().addAll((Pane) fxmlLoader.load()); +// +// return fxmlLoader.getController(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// return null; +// } private void setInitialDropShadow(){ DropShadow dropShadow = new DropShadow(); diff --git a/src/main/java/seng302/visualiser/fxObjects/BoatObject.java b/src/main/java/seng302/visualiser/fxObjects/BoatObject.java index a86a3ac8..ccabe35d 100644 --- a/src/main/java/seng302/visualiser/fxObjects/BoatObject.java +++ b/src/main/java/seng302/visualiser/fxObjects/BoatObject.java @@ -1,7 +1,5 @@ package seng302.visualiser.fxObjects; -import java.util.ArrayList; -import java.util.List; import javafx.application.Platform; import javafx.geometry.Point2D; import javafx.scene.Group; @@ -14,6 +12,9 @@ import javafx.scene.shape.Polyline; import javafx.scene.shape.StrokeLineCap; import javafx.scene.transform.Rotate; +import java.util.ArrayList; +import java.util.List; + /** * BoatGroup is a javafx group that by default contains a graphical objects for representing a 2 * dimensional boat. It contains a single polygon for the boat, a group of lines to show it's path, @@ -130,6 +131,7 @@ public class BoatObject extends Group { * @param rotation The rotation by which the boat moves * @param velocity The velocity the boat is moving * @param sailIn Boolean to toggle sail state. + * @param windDir . */ public void moveTo(double x, double y, double rotation, double velocity, Boolean sailIn, double windDir) { Double dx = Math.abs(boatPoly.getLayoutX() - x); diff --git a/src/test/java/seng302/gameServer/server/TestServerDesc.java b/src/test/java/seng302/gameServer/server/TestServerDesc.java new file mode 100644 index 00000000..8ee50255 --- /dev/null +++ b/src/test/java/seng302/gameServer/server/TestServerDesc.java @@ -0,0 +1,48 @@ +package seng302.gameServer.server; + +import org.junit.Test; +import seng302.gameServer.ServerDescription; + +import static org.junit.Assert.assertTrue; + +public class TestServerDesc { + @Test + public void testEquals(){ + ServerDescription one = new ServerDescription("a", "b", 10, "asd", 1234); + ServerDescription two = new ServerDescription("a", "b", 10, "asd", 1234); + + assertTrue(one.equals(two)); + } + + @Test + public void testNotEqualName(){ + ServerDescription one = new ServerDescription("a", "b", 10, "asd", 1234); + ServerDescription two = new ServerDescription("a2", "b", 10, "asd", 1234); + + assertTrue(!one.equals(two)); + } + + @Test + public void testNotEqualMap(){ + ServerDescription one = new ServerDescription("a", "b", 10, "asd", 1234); + ServerDescription two = new ServerDescription("a", "ba", 10, "asd", 1234); + + assertTrue(!one.equals(two)); + } + + @Test + public void testNotEqualPort(){ + ServerDescription one = new ServerDescription("a", "b", 10, "asd", 1234); + ServerDescription two = new ServerDescription("a", "b", 10, "asd", 12341); + + assertTrue(!one.equals(two)); + } + + @Test + public void testNotEqualAddress(){ + ServerDescription one = new ServerDescription("a", "b", 10, "as1d", 1234); + ServerDescription two = new ServerDescription("a", "b", 10, "asd", 1234); + + assertTrue(!one.equals(two)); + } +}