From e17e9749d88111fe4c25ecd4cffaa8c7b32a27a0 Mon Sep 17 00:00:00 2001 From: Michael Rausch Date: Wed, 20 Sep 2017 20:26:14 +1200 Subject: [PATCH] Implemented server to manage a list of available servers on the internet. - Implemented a server manager that keeps track of servers & room codes, and removes old servers - Implemented queries to find a server with a specific room code - Implemented protocol to register servers #story[1281] --- src/main/java/seng302/App.java | 16 ++- .../seng302/gameServer/ServerAdvertiser.java | 6 +- .../seng302/gameServer/ServerDescription.java | 21 ++++ .../gameServer/messages/MessageType.java | 5 +- .../gameServer/messages/RoomCodeRequest.java | 24 ++++ .../messages/ServerRegistrationMessage.java | 40 +++++++ .../model/stream/packets/PacketType.java | 8 +- .../ReadableByteInputStream.java | 34 ++++++ .../serverRepository/ServerListing.java | 109 ++++++++++++++++++ .../ServerRepoStreamParser.java | 109 ++++++++++++++++++ .../serverRepository/ServerRepository.java | 83 +++++++++++++ .../ServerRepositoryClient.java | 54 +++++++++ .../seng302/serverRepository/ServerTable.java | 79 +++++++++++++ .../seng302/visualiser/ServerListener.java | 35 +++++- 14 files changed, 615 insertions(+), 8 deletions(-) create mode 100644 src/main/java/seng302/gameServer/messages/RoomCodeRequest.java create mode 100644 src/main/java/seng302/gameServer/messages/ServerRegistrationMessage.java create mode 100644 src/main/java/seng302/serverRepository/ReadableByteInputStream.java create mode 100644 src/main/java/seng302/serverRepository/ServerListing.java create mode 100644 src/main/java/seng302/serverRepository/ServerRepoStreamParser.java create mode 100644 src/main/java/seng302/serverRepository/ServerRepository.java create mode 100644 src/main/java/seng302/serverRepository/ServerRepositoryClient.java create mode 100644 src/main/java/seng302/serverRepository/ServerTable.java diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index 3c654f77..9616b7a0 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -10,11 +10,13 @@ import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import seng302.serverRepository.ServerRepository; import seng302.visualiser.controllers.ViewManager; public class App extends Application { private static Logger logger = LoggerFactory.getLogger(App.class); + private static boolean isRunningAsCache = false; public static void parseArgs(String[] args) throws ParseException { Options options = new Options(); @@ -25,9 +27,14 @@ public class App extends Application { .getLogger(Logger.ROOT_LOGGER_NAME); options.addOption("debugLevel", true, "Set the application debug level"); + options.addOption("runAsCache", false, "Run as a server cache for server discovery"); cmd = parser.parse(options, args); + if (cmd.hasOption("runAsCache")){ + isRunningAsCache = true; + } + if (cmd.hasOption("debugLevel")) { switch (cmd.getOptionValue("debugLevel")) { @@ -67,14 +74,19 @@ public class App extends Application { } - public static void main(String[] args) { + public static void main(String[] args) throws Exception { try { parseArgs(args); } catch (ParseException e) { logger.error("Could not parse command line arguments"); } - launch(args); + if (!isRunningAsCache){ + launch(args); + } + else{ + ServerRepository serverRepository = new ServerRepository(); + } } } diff --git a/src/main/java/seng302/gameServer/ServerAdvertiser.java b/src/main/java/seng302/gameServer/ServerAdvertiser.java index 3c5929c2..938ddd83 100644 --- a/src/main/java/seng302/gameServer/ServerAdvertiser.java +++ b/src/main/java/seng302/gameServer/ServerAdvertiser.java @@ -1,5 +1,9 @@ package seng302.gameServer; +import com.sun.org.apache.xpath.internal.operations.Bool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import javax.jmdns.JmDNS; import javax.jmdns.ServiceInfo; import java.io.IOException; @@ -27,7 +31,7 @@ public class ServerAdvertiser { */ private static String SERVICE = "_partyatsea"; private static String PROTOCOL = "_tcp"; - public static String SERVICE_TYPE = SERVICE + "." + PROTOCOL + ".local."; + public static String SERVICE_TYPE = SERVICE + "." + PROTOCOL + ".homekit.bonjour.michaelrausch.nz."; private static ServerAdvertiser instance = null; private static JmDNS jmdnsInstance = null; diff --git a/src/main/java/seng302/gameServer/ServerDescription.java b/src/main/java/seng302/gameServer/ServerDescription.java index 44fc1875..ed041720 100644 --- a/src/main/java/seng302/gameServer/ServerDescription.java +++ b/src/main/java/seng302/gameServer/ServerDescription.java @@ -7,6 +7,10 @@ public class ServerDescription { private String serverName; private String mapName; private Integer numPlayers; + private Long lastUpdated; + private Long lastRefreshed; + + private static Long EXPIRY_INTERVAL = 5000L; public ServerDescription(String serverName, String mapName, Integer numPlayers, Integer capacity, String address, Integer portNum){ this.serverName = serverName; @@ -15,6 +19,7 @@ public class ServerDescription { this.address = address; this.portNum = portNum; this.capacity = capacity; + lastUpdated = System.currentTimeMillis(); } @@ -80,4 +85,20 @@ public class ServerDescription { return this.getName().hashCode() + this.getAddress().hashCode() + this.portNumber().hashCode() + this.getMapName().hashCode(); } + + public Boolean hasExpired(){ + return System.currentTimeMillis() - lastUpdated > EXPIRY_INTERVAL; + } + + public Boolean serverShouldBeRemoved() { + if (lastRefreshed == null) return false; + + System.out.println("SBR" + (System.currentTimeMillis() - lastRefreshed > EXPIRY_INTERVAL)); + return System.currentTimeMillis() - lastRefreshed > EXPIRY_INTERVAL; + } + + public void hasBeenRefreshed(){ + System.out.println("Was refreshed"); + lastRefreshed = System.currentTimeMillis(); + } } diff --git a/src/main/java/seng302/gameServer/messages/MessageType.java b/src/main/java/seng302/gameServer/messages/MessageType.java index 9f0ddd4c..ff991c29 100644 --- a/src/main/java/seng302/gameServer/messages/MessageType.java +++ b/src/main/java/seng302/gameServer/messages/MessageType.java @@ -21,7 +21,10 @@ public enum MessageType { REGISTRATION_REQUEST(101), REGISTRATION_RESPONSE(102), CUSTOMIZATION_REQUEST(103), - CUSTOMIZATION_RESPONSE(104); + CUSTOMIZATION_RESPONSE(104), + REPO_REGISTRATION_REQUEST(201), + ROOM_CODE_REQUEST(202), + LOBBY_REQUEST(203); private int code; diff --git a/src/main/java/seng302/gameServer/messages/RoomCodeRequest.java b/src/main/java/seng302/gameServer/messages/RoomCodeRequest.java new file mode 100644 index 00000000..ecb24079 --- /dev/null +++ b/src/main/java/seng302/gameServer/messages/RoomCodeRequest.java @@ -0,0 +1,24 @@ +package seng302.gameServer.messages; + +public class RoomCodeRequest extends Message{ + private int size = 0; + + @Override + public int getSize() { + return size; + } + + public RoomCodeRequest(String roomCode){ + size = roomCode.length() + 6; + + setHeader(new Header(MessageType.ROOM_CODE_REQUEST, 0x01, (short)getSize())); + allocateBuffer(); + writeHeaderToBuffer(); + + putInt(roomCode.length(), 6); + putBytes(roomCode.getBytes()); + + writeCRC(); + rewind(); + } +} diff --git a/src/main/java/seng302/gameServer/messages/ServerRegistrationMessage.java b/src/main/java/seng302/gameServer/messages/ServerRegistrationMessage.java new file mode 100644 index 00000000..03b9a443 --- /dev/null +++ b/src/main/java/seng302/gameServer/messages/ServerRegistrationMessage.java @@ -0,0 +1,40 @@ +package seng302.gameServer.messages; + +public class ServerRegistrationMessage extends Message { + private int size; + @Override + public int getSize() { + return size; + } + + public ServerRegistrationMessage(String serverName, String mapName, String address, int port, int players, int capacity, String roomCode){ + size = serverName.getBytes().length + mapName.length() + address.length() + roomCode.length() + 36; + + setHeader(new Header(MessageType.REPO_REGISTRATION_REQUEST, 0x01, (short) getSize())); + allocateBuffer(); + writeHeaderToBuffer(); + + int nameLength = serverName.length(); + int mapNameLength = mapName.length(); + int addressLength = address.length(); + int roomCodeLength = roomCode.length(); + + // Put fields here + putInt(nameLength, 6); + putInt(mapNameLength, 6); + putInt(addressLength, 6); + putInt(roomCodeLength, 6); + + putInt(port, 4); + putInt(players, 4); + putInt(capacity, 4); + + putBytes(serverName.getBytes()); + putBytes(mapName.getBytes()); + putBytes(address.getBytes()); + putBytes(roomCode.getBytes()); + + writeCRC(); + rewind(); + } +} diff --git a/src/main/java/seng302/model/stream/packets/PacketType.java b/src/main/java/seng302/model/stream/packets/PacketType.java index baf2ff42..52ed23fd 100644 --- a/src/main/java/seng302/model/stream/packets/PacketType.java +++ b/src/main/java/seng302/model/stream/packets/PacketType.java @@ -20,7 +20,9 @@ public enum PacketType { RACE_REGISTRATION_REQUEST, RACE_REGISTRATION_RESPONSE, RACE_CUSTOMIZATION_REQUEST, - RACE_CUSTOMIZATION_RESPONSE; + RACE_CUSTOMIZATION_RESPONSE, + + SERVER_REGISTRATION, ROOM_CODE_REQUEST, LOBBY_REQUEST; public static PacketType assignPacketType(int packetType, byte[] payload){ switch(packetType){ @@ -65,6 +67,10 @@ public enum PacketType { return RACE_CUSTOMIZATION_REQUEST; case 104: return RACE_CUSTOMIZATION_RESPONSE; + case 201: + return SERVER_REGISTRATION; + case 202: + return ROOM_CODE_REQUEST; default: } return OTHER; diff --git a/src/main/java/seng302/serverRepository/ReadableByteInputStream.java b/src/main/java/seng302/serverRepository/ReadableByteInputStream.java new file mode 100644 index 00000000..4695d7e7 --- /dev/null +++ b/src/main/java/seng302/serverRepository/ReadableByteInputStream.java @@ -0,0 +1,34 @@ +package seng302.serverRepository; + +import java.io.InputStream; + +public class ReadableByteInputStream { + private InputStream is; + + public ReadableByteInputStream(InputStream is){ + this.is = is; + } + + public byte[] getBytes(int n) throws Exception { + byte[] bytes = new byte[n]; + for (int i = 0; i < n; i++) { + bytes[i] = (byte) readByte(); + } + return bytes; + } + + public void skipBytes(long n) throws Exception { + for (int i = 0; i < n; i++) { + readByte(); + } + } + + public int readByte() throws Exception { + int currentByte = is.read(); + + if (currentByte == -1) { + throw new Exception(); + } + return currentByte; + } +} diff --git a/src/main/java/seng302/serverRepository/ServerListing.java b/src/main/java/seng302/serverRepository/ServerListing.java new file mode 100644 index 00000000..58d223e6 --- /dev/null +++ b/src/main/java/seng302/serverRepository/ServerListing.java @@ -0,0 +1,109 @@ +package seng302.serverRepository; + +public class ServerListing { + private static final int SERVER_TTL_DEFAULT = 10; + + private String serverName = ""; + private String mapName = ""; + private String address = ""; + private int portNumber = 0; + private int capacity = 0; + private int players = 0; + private String roomCode = ""; + private int ttl = SERVER_TTL_DEFAULT; + + + public ServerListing(String serverName, String mapName, String address, int portNumber, int capacity){ + this.serverName = serverName; + this.mapName = mapName; + this.address = address; + this.portNumber = portNumber; + this.capacity = capacity; + } + + public ServerListing setNumberOfPlayers(int players){ + this.players = players; + return this; + } + + public ServerListing setRoomCode(String roomCode){ + this.roomCode = roomCode; + return this; + } + + public void refreshTtl(){ + ttl = SERVER_TTL_DEFAULT; + } + + public void decrementTtl(){ + ttl--; + } + + public boolean hasTtlExpired(){ + return ttl < 0; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!ServerListing.class.isAssignableFrom(obj.getClass())) { + return false; + } + + final ServerListing other = (ServerListing) obj; + + if (this.getPortNumber() != other.getPortNumber()){ + return false; + } + + if (!this.getMapName().equals(other.getMapName())){ + return false; + } + + if (!this.getServerName().equals(other.getServerName())){ + return false; + } + + if (this.getCapacity() != other.getCapacity()){ + return false; + } + + if (!this.getAddress().equals(other.getAddress())){ + return false; + } + + return true; + } + + @Override + public int hashCode() { + return this.getServerName().hashCode() + + this.getAddress().hashCode() + this.getMapName().hashCode(); + } + + public String getRoomCode() { + return roomCode; + } + + public int getPortNumber() { + return portNumber; + } + + public String getMapName() { + return mapName; + } + + public String getServerName() { + return serverName; + } + + public int getCapacity() { + return capacity; + } + + public String getAddress() { + return address; + } +} diff --git a/src/main/java/seng302/serverRepository/ServerRepoStreamParser.java b/src/main/java/seng302/serverRepository/ServerRepoStreamParser.java new file mode 100644 index 00000000..da7a5521 --- /dev/null +++ b/src/main/java/seng302/serverRepository/ServerRepoStreamParser.java @@ -0,0 +1,109 @@ +package seng302.serverRepository; + + +import seng302.gameServer.messages.Message; +import seng302.model.stream.packets.PacketType; + +import java.io.InputStream; +import java.util.Arrays; + +public class ServerRepoStreamParser { + private ReadableByteInputStream inputStream; + + private String roomCode; + private String mapName; + private ServerListing serverListing; + + public ServerRepoStreamParser(InputStream is){ + inputStream = new ReadableByteInputStream(is); + } + + public PacketType parse() throws Exception { + int sync1 = inputStream.readByte(); + int sync2 = inputStream.readByte(); + + PacketType packetType = null; + + if (sync1 == 0x47 && sync2 == 0x83) { + int type = inputStream.readByte(); + inputStream.skipBytes(10); + long payloadLength = Message.bytesToLong(inputStream.getBytes(2)); + byte[] payload = inputStream.getBytes((int) payloadLength); + inputStream.skipBytes(4); + + packetType = PacketType.assignPacketType(type, payload); + + switch (packetType) { + case ROOM_CODE_REQUEST: + roomCode = parseRoomCodeRequest(payload); + break; + + case LOBBY_REQUEST: + mapName = parseLobbyRequest(payload); + + case SERVER_REGISTRATION: + serverListing = parseServerRegistration(payload); + break; + } + + } + + return packetType; + } + private String parseLobbyRequest(byte[] payload) { + int mapNameLength = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 0 ,4)); + + return new String(Arrays.copyOfRange(payload, 4, 4+mapNameLength)); + } + + private String parseRoomCodeRequest(byte[] payload) { + int roomCodeLength = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 0 ,4)); + + return new String(Arrays.copyOfRange(payload, 4, 4+roomCodeLength)); + } + + public static ServerListing parseServerRegistration(byte[] payload) { + int nameLength = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 0, 6)); + int mapNameLength = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 6, 12)); + int addressLength = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 12, 18)); + int roomCodeLength = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 18, 24)); + + int portNumber = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 24, 28)); + int players = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 28, 32)); + int capacity = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 32, 36)); + + int currentPos = 36; + int nextPos = currentPos + nameLength; + String serverName = new String(Arrays.copyOfRange(payload, currentPos, nextPos)); + + currentPos = nextPos; + nextPos = currentPos + mapNameLength; + String mapName = new String(Arrays.copyOfRange(payload, currentPos, nextPos)); + + currentPos = nextPos; + nextPos = currentPos + addressLength; + String address = new String(Arrays.copyOfRange(payload, currentPos, nextPos)); + + currentPos = nextPos; + nextPos = currentPos + roomCodeLength; + String roomCode = new String(Arrays.copyOfRange(payload, currentPos, nextPos)); + + ServerListing serverListing = new ServerListing(serverName, mapName, address, portNumber, capacity); + serverListing.setNumberOfPlayers(players); + serverListing.setRoomCode(roomCode); + + return serverListing; + } + + public String getRoomCode() { + return roomCode; + } + + public String getMapName() { + return mapName; + } + + public ServerListing getServerListing() { + return serverListing; + } +} diff --git a/src/main/java/seng302/serverRepository/ServerRepository.java b/src/main/java/seng302/serverRepository/ServerRepository.java new file mode 100644 index 00000000..1770273b --- /dev/null +++ b/src/main/java/seng302/serverRepository/ServerRepository.java @@ -0,0 +1,83 @@ +package seng302.serverRepository; + +import seng302.gameServer.messages.Message; +import seng302.gameServer.messages.RoomCodeRequest; +import seng302.gameServer.messages.ServerRegistrationMessage; +import seng302.model.stream.packets.PacketType; + +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Timer; +import java.util.TimerTask; + +public class ServerRepository { + private ServerTable serverTable; + + public ServerRepository() throws Exception { + System.out.println(" -- Starting Server Repository -- "); + serverTable = new ServerTable(); + + ServerSocket serverSocket = new ServerSocket(9999); + + + // TODO Remove later, this is for testing + new Timer().schedule(new TimerTask() { + @Override + public void run() { + try { + new ServerRepositoryClient(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 5000); + + while (true){ + Socket clientSocket = serverSocket.accept(); + + parseRequest(clientSocket); + + clientSocket.close(); + } + } + + + private void parseRequest(Socket clientSocket) throws Exception { + ServerRepoStreamParser parser = new ServerRepoStreamParser(clientSocket.getInputStream()); + + if (clientSocket.isConnected() && !clientSocket.isClosed()){ + PacketType parsePacketResult = parser.parse(); + + switch (parsePacketResult){ + case SERVER_REGISTRATION: + ServerListing listing = parser.getServerListing(); + + if (!serverTable.getAllServers().contains(listing)){ + listing.setRoomCode(serverTable.getNextRoomCode().toString()); + } + + serverTable.addServer(listing); + + Message serverRegMessage = new RoomCodeRequest(listing.getRoomCode()); + clientSocket.getOutputStream().write(serverRegMessage.getBuffer()); + break; + + case ROOM_CODE_REQUEST: + String desiredRoomCode = parser.getRoomCode(); + + ServerListing serverListing = serverTable.getServerByRoomCode(desiredRoomCode); + Message response; + + if (serverListing != null){ + response = new ServerRegistrationMessage(serverListing.getServerName(), serverListing.getMapName(), serverListing.getAddress(), serverListing.getPortNumber(), 0, 0, desiredRoomCode); + } + else{ + response = new ServerRegistrationMessage("", "", "", 0, 0, 0, ""); + } + + clientSocket.getOutputStream().write(response.getBuffer()); + break; + } + } + } +} diff --git a/src/main/java/seng302/serverRepository/ServerRepositoryClient.java b/src/main/java/seng302/serverRepository/ServerRepositoryClient.java new file mode 100644 index 00000000..4eca3f54 --- /dev/null +++ b/src/main/java/seng302/serverRepository/ServerRepositoryClient.java @@ -0,0 +1,54 @@ +package seng302.serverRepository; + +import com.sun.xml.internal.ws.api.message.Packet; +import seng302.gameServer.messages.Message; +import seng302.gameServer.messages.ServerRegistrationMessage; +import seng302.model.stream.packets.PacketType; + +import java.io.IOException; +import java.net.Socket; +import java.util.Timer; +import java.util.TimerTask; + +public class ServerRepositoryClient { + + private String roomCode = "0"; + + public ServerRepositoryClient() throws Exception { + new Timer().schedule(new TimerTask() { + @Override + public void run() { + try { + sendUpdate(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 0, 5000); + } + + private void sendUpdate() throws Exception { + Socket socket = new Socket("localhost", 9999); + ServerRepoStreamParser parser = new ServerRepoStreamParser(socket.getInputStream()); + Message req = new ServerRegistrationMessage("asdf", "Asdf", "asdf", 6969, 1, 20, "4949"); + + socket.getOutputStream().write(req.getBuffer()); + + PacketType packetType = parser.parse(); + + if (packetType != PacketType.ROOM_CODE_REQUEST){ + return; + } + + String roomCode = parser.getRoomCode(); + + if (roomCode.equals("0")){ + return; + } + + this.roomCode = roomCode; + + socket.close(); + } +} + diff --git a/src/main/java/seng302/serverRepository/ServerTable.java b/src/main/java/seng302/serverRepository/ServerTable.java new file mode 100644 index 00000000..6f662b68 --- /dev/null +++ b/src/main/java/seng302/serverRepository/ServerTable.java @@ -0,0 +1,79 @@ +package seng302.serverRepository; + +import com.sun.corba.se.spi.activation.Server; + +import java.util.*; + +public class ServerTable { + private List servers; + private int lastRoomCode = 4020; + + public ServerTable(){ + servers = new ArrayList<>(); + + new Timer().schedule(new TimerTask() { + @Override + public void run() { + updateServers(); + } + }, 0, 1000); + + } + + private void updateServers() { + List serversToRemove = new ArrayList<>(); + + for (ServerListing server : servers){ + server.decrementTtl(); + + if (server.hasTtlExpired()){ + serversToRemove.add(server); + } + } + + for (ServerListing server : serversToRemove){ + System.out.println("Removing " + server.getServerName()); + servers.remove(server); + } + } + + public void addServer(ServerListing server){ + if (servers.contains(server)){ + updateTtlForServer(server); + return; + } + + servers.add(server); + } + + private void updateTtlForServer(ServerListing server) { + for (ServerListing serverListing : servers){ + if (server.equals(serverListing)){ + System.out.println("Refreshing TTL For " + server.getServerName()); + serverListing.refreshTtl(); + } + } + } + + public List getAllServers(){ + return Collections.unmodifiableList(servers); + } + + public ServerListing getServerByRoomCode(String roomCode){ + for (ServerListing serverListing : servers){ + if (serverListing.getRoomCode().equals(roomCode)){ + return serverListing; + } + } + + return null; + } + + public Integer getNextRoomCode(){ + System.out.println(lastRoomCode); + lastRoomCode += 1; + return lastRoomCode; + } + + +} diff --git a/src/main/java/seng302/visualiser/ServerListener.java b/src/main/java/seng302/visualiser/ServerListener.java index 625d088c..b6c31141 100644 --- a/src/main/java/seng302/visualiser/ServerListener.java +++ b/src/main/java/seng302/visualiser/ServerListener.java @@ -6,11 +6,10 @@ import seng302.gameServer.ServerDescription; import javax.jmdns.JmDNS; import javax.jmdns.ServiceEvent; import javax.jmdns.ServiceListener; +import javax.jmdns.impl.JmDNSImpl; import java.io.IOException; import java.net.InetAddress; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; +import java.util.*; import static seng302.gameServer.ServerAdvertiser.getLocalHostIp; @@ -18,6 +17,7 @@ import static seng302.gameServer.ServerAdvertiser.getLocalHostIp; * Listens for servers on the local network */ public class ServerListener{ + private static Integer SERVICE_REFRESH_INTERVAL = 5 * 1000; private static ServerListener instance; private ServerListenerDelegate delegate; private JmDNS jmdns = null; @@ -91,8 +91,16 @@ public class ServerListener{ private ServerListener() throws IOException { jmdns = JmDNS.create(InetAddress.getByName(getLocalHostIp())); + listener = new GameServeMonitor(); jmdns.addServiceListener(ServerAdvertiser.SERVICE_TYPE, listener); + + /*new Timer().schedule(new TimerTask() { + @Override + public void run() { + refresh(); + } + }, 50, SERVICE_REFRESH_INTERVAL);*/ } public static ServerListener getInstance() throws IOException { @@ -110,4 +118,25 @@ public class ServerListener{ public void setDelegate(ServerListenerDelegate delegate){ this.delegate = delegate; } + + public void refresh(){ + ArrayList servers = new ArrayList<>(listener.servers); + + for (ServerDescription serverDescription : servers){ + if (serverDescription.hasExpired()){ + jmdns.requestServiceInfo(ServerAdvertiser.SERVICE_TYPE, serverDescription.getName()); + } + else{ + serverDescription.hasBeenRefreshed(); + } + } + + for (ServerDescription server : servers){ + if (server.serverShouldBeRemoved()){ + listener.servers.remove(server); + delegate.serverRemoved(new ArrayList(listener.servers)); + } + } + + } }