diff --git a/src/main/java/com/han/asd/DungeonGame.java b/src/main/java/com/han/asd/DungeonGame.java deleted file mode 100644 index 750c12cda3ce05ee703736a26afc12f306c57cd1..0000000000000000000000000000000000000000 --- a/src/main/java/com/han/asd/DungeonGame.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.han.asd; - -import com.han.asd.listener.ConnectionPacketListener; -import com.han.asd.listener.DisconnectPacketListener; -import com.han.asd.network.Network; -import com.han.asd.network.exceptions.ConnectionFailedException; -import com.han.asd.network.exceptions.DisconnectFailedException; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.net.SocketException; -import java.net.UnknownHostException; - -public class DungeonGame { - private static final Logger logger = LogManager.getLogger(DungeonGame.class); - private final Network network; - - public DungeonGame() throws UnknownHostException { - this.network = new Network(); - } - - public void createLobby() throws ConnectionFailedException, SocketException, UnknownHostException { - network.startLobby(Main.TEST_DUNGEON_PORT); - - network.getPacketDispatcher().register(new ConnectionPacketListener()); - network.getPacketDispatcher().register(new DisconnectPacketListener()); - - logger.info("Lobby created and started on port {}", Main.TEST_DUNGEON_PORT); - } - - public void stopLobby() { - network.stopLobby(); - logger.info("Lobby stopped"); - } - - public void discover() { - network.startDiscovery(Main.TEST_DUNGEON_PORT); - logger.info("Started discovery to join lobby"); - } - - public void joinLobby(String ip) throws ConnectionFailedException { - network.connectLobby(ip, Main.TEST_DUNGEON_PORT); - logger.info("Started discovery to join lobby"); - } - - public void leaveLobby() throws DisconnectFailedException { - network.disconnectLobby(); - logger.info("Left the lobby"); - } - - public Network getNetwork() { - return network; - } -} diff --git a/src/main/java/com/han/asd/Main.java b/src/main/java/com/han/asd/Main.java index 3cc9248575b773aedcad51e6bb264de1b554d521..7c694da7191d528f0a7ea82def325e90a52cae4d 100644 --- a/src/main/java/com/han/asd/Main.java +++ b/src/main/java/com/han/asd/Main.java @@ -1,47 +1,60 @@ package com.han.asd; -import com.han.asd.network.exceptions.ConnectionFailedException; -import com.han.asd.network.exceptions.DisconnectFailedException; -import com.han.asd.network.exceptions.NetworkConfigurationException; +import com.han.asd.api.network.discovery.Discovery; +import com.han.asd.api.network.exceptions.ConnectionFailedException; +import com.han.asd.api.network.exceptions.DisconnectFailedException; +import com.han.asd.api.network.exceptions.SendFailedException; +import com.han.asd.api.network.packet.Packet; +import com.han.asd.game.DungeonGame; +import com.han.asd.packets.ConnectionPacket; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.net.SocketException; -import java.net.UnknownHostException; +import java.io.IOException; public class Main { - public static final int TEST_DUNGEON_PORT = 8886; - public static final int TEST_DUNGEON_PORT_2 = 8887; private static final Logger logger = LogManager.getLogger(Main.class); - public static void main(String[] args) - throws InterruptedException, DisconnectFailedException, UnknownHostException, NetworkConfigurationException, ConnectionFailedException, SocketException { + public static void main(String[] args) throws IOException, ConnectionFailedException, InterruptedException, DisconnectFailedException, SendFailedException { logger.info("Starting DungeonGame application..."); + // Create a game (network for host) DungeonGame dungeonGame = new DungeonGame(); -// dungeonGame.createLobby(); -// logger.info("Lobby created successfully."); + dungeonGame.createGame(); - dungeonGame.discover(); - logger.info("Attempting to discover lobby(s)..."); + // Simulate wait time + Thread.sleep(3000); - Thread.sleep(1000*60); + // Discover hosts for 3 seconds + dungeonGame.discoverGames(3000); - if (!dungeonGame.getNetwork().getHosts().isEmpty()) { - logger.info("Lobby(s) have been found, joining the first one as a test."); + // TUI would print found games. + Discovery discovery = dungeonGame.getNetwork().getDiscovery(); + for (String host : discovery.getHosts()) { + logger.info("{}", host); + } - String lobbyIp = dungeonGame.getNetwork().getHosts().get(0); - dungeonGame.joinLobby(lobbyIp); + // Simulate joining a game + if(!discovery.getHosts().isEmpty()) { + String serverIpToJoin = discovery.getHosts().getFirst(); + if(serverIpToJoin != null) { + // Join the game (connect to a host) + dungeonGame.joinGame(serverIpToJoin); + } - Thread.sleep(3000); + // Simulate wait time + Thread.sleep(2000); - dungeonGame.leaveLobby(); - logger.info("Leaving the lobby..."); + // Leave the game (disconnect from host) + dungeonGame.leaveGame(); } else { - logger.info("No lobby(s) where found during the discovery."); + logger.info("No lobby found."); } -// dungeonGame.stopLobby(); -// logger.info("Lobby stopped. Exiting application."); + // Simulate wait time + Thread.sleep(2000); + + // Stop the game (network for host) + dungeonGame.stopGame(); } } diff --git a/src/main/java/com/han/asd/api/network/HostNetwork.java b/src/main/java/com/han/asd/api/network/HostNetwork.java new file mode 100644 index 0000000000000000000000000000000000000000..be148117b6b07efef5ded90af1a8dd20e5f428f5 --- /dev/null +++ b/src/main/java/com/han/asd/api/network/HostNetwork.java @@ -0,0 +1,28 @@ +package com.han.asd.api.network; + +import com.han.asd.api.network.exceptions.ConnectionFailedException; +import com.han.asd.api.network.server.TCPServer; +import com.han.asd.api.network.tick.TickManager; +import com.han.asd.api.threading.ThreadManager; + +public class HostNetwork { + private final TickManager tickManager; + private final ThreadManager threadManager; + private TCPServer hostServer; + + public HostNetwork(TickManager tickManager, ThreadManager threadManager) { + this.threadManager = threadManager; + this.tickManager = tickManager; + } + + public void startHost(int port) throws ConnectionFailedException { + hostServer = new TCPServer(threadManager, tickManager); + hostServer.start(port); + + threadManager.runOperation(hostServer); + } + + public void stopHost() { + threadManager.stopOperation(hostServer); + } +} diff --git a/src/main/java/com/han/asd/api/network/Network.java b/src/main/java/com/han/asd/api/network/Network.java new file mode 100644 index 0000000000000000000000000000000000000000..adc3f1acbf2eaf4a417714c54f3f8d1ab012c6b5 --- /dev/null +++ b/src/main/java/com/han/asd/api/network/Network.java @@ -0,0 +1,34 @@ +package com.han.asd.api.network; + +import com.han.asd.api.network.HostNetwork; +import com.han.asd.api.network.connection.ConnectionHandler; +import com.han.asd.api.network.connection.TCPConnection; +import com.han.asd.api.network.discovery.Discovery; +import com.han.asd.api.network.exceptions.ConnectionFailedException; +import com.han.asd.api.network.exceptions.DisconnectFailedException; +import com.han.asd.api.network.exceptions.SendFailedException; +import com.han.asd.api.network.packet.Packet; +import com.han.asd.api.network.packet.PacketDispatcher; +import com.han.asd.api.network.tick.TickManager; +import com.han.asd.api.threading.ThreadManager; +import com.han.asd.packets.ConnectionPacket; + +import java.net.Socket; + +public interface Network { + HostNetwork getHostNetwork(); + PacketDispatcher getPacketDispatcher(); + TickManager getTickManager(); + ThreadManager getThreadManager(); + ConnectionHandler getConnectionHandler(); + Discovery getDiscovery(); + int getDiscoveryServerPort(); + int getDiscoveryClientPort(); + String getDiscoveryAddress(); + int getTickCount(); + void startHost() throws ConnectionFailedException; + void stopHost(); + void discoverHosts(int timeout); + void connectHost(String address) throws ConnectionFailedException; + void disconnectHost() throws DisconnectFailedException; +} diff --git a/src/main/java/com/han/asd/api/network/connection/Connection.java b/src/main/java/com/han/asd/api/network/connection/Connection.java new file mode 100644 index 0000000000000000000000000000000000000000..c64a79ad80c7dfd547e30d6baf17b194944acbce --- /dev/null +++ b/src/main/java/com/han/asd/api/network/connection/Connection.java @@ -0,0 +1,13 @@ +package com.han.asd.api.network.connection; + +import com.han.asd.api.network.exceptions.PacketParsingException; +import com.han.asd.api.network.exceptions.ReceiveFailedException; +import com.han.asd.api.network.exceptions.SendFailedException; +import com.han.asd.api.network.packet.Packet; + +public interface Connection { + void send(Packet packet) throws SendFailedException; + Packet receive() throws PacketParsingException, ReceiveFailedException; + void close(); + boolean isConnected(); +} diff --git a/src/main/java/com/han/asd/api/network/connection/ConnectionHandler.java b/src/main/java/com/han/asd/api/network/connection/ConnectionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..99d6b7449ef623a9b99707e1ccff59c04663cb65 --- /dev/null +++ b/src/main/java/com/han/asd/api/network/connection/ConnectionHandler.java @@ -0,0 +1,59 @@ +package com.han.asd.api.network.connection; + +import com.han.asd.api.network.exceptions.PacketParsingException; +import com.han.asd.api.network.exceptions.ReceiveFailedException; +import com.han.asd.api.network.packet.Packet; +import com.han.asd.api.network.tick.TickManager; +import com.han.asd.api.threading.operation.Operation; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ConnectionHandler implements Operation { + private static final Logger logger = LogManager.getLogger(ConnectionHandler.class); + private final TickManager tickManager; + private final Connection connection; + + public ConnectionHandler(TickManager tickManager, Connection connection) { + this.tickManager = tickManager; + this.connection = connection; + } + + @Override + public void stop() { + connection.close(); + } + + @Override + public void execute() { + try { + while (!Thread.interrupted() && connection.isConnected()) { + Packet packet = connection.receive(); + if(packet != null) { + tickManager.getQueue().add(packet); + logger.info("Packet added to queue: {}", packet.getClass().getSimpleName()); + } else if(!connection.isConnected()) { + break; + } + Thread.sleep(1); + } + } catch (ReceiveFailedException | PacketParsingException e) { + logger.error("Invalid packet exception in connection handler", e); + throw new RuntimeException("InvalidPacketException occurred", e); + } catch (InterruptedException ignored) { + } + } + + @Override + public long getTimeout() { + return 1; + } + + @Override + public int getPriority() { + return 0; + } + + public Connection getConnection() { + return connection; + } +} diff --git a/src/main/java/com/han/asd/network/connection/TCPConnection.java b/src/main/java/com/han/asd/api/network/connection/TCPConnection.java similarity index 70% rename from src/main/java/com/han/asd/network/connection/TCPConnection.java rename to src/main/java/com/han/asd/api/network/connection/TCPConnection.java index 03b0c1ca091cd19458366e36b8b2379c00f20e2c..5492ea54b24f55477c2fb0c0221e58df14cd2901 100644 --- a/src/main/java/com/han/asd/network/connection/TCPConnection.java +++ b/src/main/java/com/han/asd/api/network/connection/TCPConnection.java @@ -1,13 +1,14 @@ -package com.han.asd.network.connection; +package com.han.asd.api.network.connection; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.han.asd.network.exceptions.PacketParsingException; -import com.han.asd.network.exceptions.ReceiveFailedException; -import com.han.asd.network.exceptions.SendFailedException; -import com.han.asd.network.packet.Packet; +import com.han.asd.api.network.exceptions.ConnectionFailedException; +import com.han.asd.api.network.exceptions.PacketParsingException; +import com.han.asd.api.network.exceptions.ReceiveFailedException; +import com.han.asd.api.network.exceptions.SendFailedException; +import com.han.asd.api.network.packet.Packet; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -16,20 +17,21 @@ import java.lang.reflect.Type; import java.net.Socket; public class TCPConnection implements Connection { - private static final Logger logger = LogManager.getLogger(TCPConnection.class); - private final Gson gson; private Socket socket; private BufferedReader reader; private BufferedWriter writer; - public TCPConnection(Socket socket) throws IOException { - this.socket = socket; - this.gson = new GsonBuilder().create(); - this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); - this.writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); - logger.info("TCP connection established with {}:{}", socket.getInetAddress().getHostAddress(), socket.getPort()); + public TCPConnection(Socket socket) throws ConnectionFailedException { + try { + this.socket = socket; + this.gson = new GsonBuilder().create(); + this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + this.writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); + } catch (IOException e) { + throw new ConnectionFailedException("Failed to open input/output stream", e); + } } @Override @@ -44,6 +46,7 @@ public class TCPConnection implements Connection { writer.write(rawPacket); writer.newLine(); writer.flush(); + logger.debug("Packet sent: {}", packet.getClass().getSimpleName()); } catch (IOException e) { logger.error("Error sending packet", e); @@ -52,8 +55,11 @@ public class TCPConnection implements Connection { } @Override - public Packet receive() throws PacketParsingException, ReceiveFailedException { + public Packet receive() throws PacketParsingException { try { + if(socket == null || socket.isClosed() || !reader.ready()) + return null; + String rawPacket = reader.readLine(); if (rawPacket == null) { throw new PacketParsingException("Raw packet is empty."); @@ -70,20 +76,18 @@ public class TCPConnection implements Connection { } catch (ClassNotFoundException | PacketParsingException e) { logger.error("Couldn't transform raw json into a valid packet.", e); throw new PacketParsingException("Couldn't transform raw json into a valid packet.", e); - } catch (IOException e) { - logger.error("Error receiving packet", e); - throw new ReceiveFailedException("Failed to receive TCP packet", e); + } catch (IOException ignored) { + //logger.error("Error receiving packet", e); + //throw new ReceiveFailedException("Failed to receive TCP packet", e); } + + return null; } @Override public void close() { try { - //socket.shutdownInput(); - //socket.shutdownOutput(); - if (reader != null) { - reader.reset(); reader.close(); } if (writer != null) { @@ -95,8 +99,6 @@ public class TCPConnection implements Connection { } catch (Exception ignored) { logger.error("Failed to drop connection"); } finally { - logger.info("TCP connection closed"); - socket = null; reader = null; writer = null; diff --git a/src/main/java/com/han/asd/network/connection/UDPConnection.java b/src/main/java/com/han/asd/api/network/connection/UDPConnection.java similarity index 90% rename from src/main/java/com/han/asd/network/connection/UDPConnection.java rename to src/main/java/com/han/asd/api/network/connection/UDPConnection.java index a39c0e9222e43a1c3fb76306d95525fe49fc48fe..93132012f56bcda5b67d66c23a0ed9480715b973 100644 --- a/src/main/java/com/han/asd/network/connection/UDPConnection.java +++ b/src/main/java/com/han/asd/api/network/connection/UDPConnection.java @@ -1,151 +1,155 @@ -package com.han.asd.network.connection; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.han.asd.network.exceptions.NetworkConfigurationException; -import com.han.asd.network.exceptions.PacketParsingException; -import com.han.asd.network.exceptions.ReceiveFailedException; -import com.han.asd.network.exceptions.SendFailedException; -import com.han.asd.network.packet.Packet; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.IOException; -import java.lang.reflect.Type; -import java.net.*; -import java.util.Collections; -import java.util.Enumeration; - -public class UDPConnection implements Connection { - private static final Logger logger = LogManager.getLogger(UDPConnection.class); - - private final Gson gson; - private final DatagramSocket datagramSocket; - private final InetAddress address; - private final int port; - private boolean connected; - - public UDPConnection(DatagramSocket datagramSocket, InetAddress address, int port) { - this.datagramSocket = datagramSocket; - this.gson = new GsonBuilder().create(); - this.address = address; - this.port = port; - this.connected = true; - } - - public UDPConnection(DatagramSocket datagramSocket) { - this(datagramSocket, datagramSocket.getInetAddress(), datagramSocket.getLocalPort()); - } - - public UDPConnection(InetAddress address, int port) throws SocketException { - this(new DatagramSocket(), address, port); - } - - public UDPConnection(int port) throws SocketException, NetworkConfigurationException { - this(getLocalBroadcastAddress(), port); - } - - public static InetAddress getLocalBroadcastAddress() throws NetworkConfigurationException { - try { - Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); - for (NetworkInterface netInt : Collections.list(networkInterfaces)) { - if (netInt.isLoopback() || !netInt.isUp()) - continue; - - for (InterfaceAddress interfaceAddress : netInt.getInterfaceAddresses()) { - InetAddress broadcast = interfaceAddress.getBroadcast(); - if (broadcast == null) - continue; - - if (interfaceAddress.getAddress().equals(InetAddress.getLocalHost())) { - return broadcast; - } - } - } - } catch (SocketException | UnknownHostException e) { - throw new NetworkConfigurationException("Socket initialization failed", e); - } - return null; - } - - public void broadcast(Packet packet) throws SendFailedException, NetworkConfigurationException { - InetAddress broadcastAddress = getLocalBroadcastAddress(); - if (broadcastAddress != null) { - send(broadcastAddress, packet); - logger.info("Broadcast packet sent"); - } else { - logger.error("Broadcast address not found"); - } - } - - @Override - public void send(Packet packet) throws SendFailedException { - send(address, packet); - } - - private void send(InetAddress address, Packet packet) throws SendFailedException { - try { - JsonObject wrapper = new JsonObject(); - wrapper.addProperty("type", packet.getClass().getName()); - wrapper.add("data", gson.toJsonTree(packet)); - - String rawPacket = wrapper.toString(); - byte[] buffer = rawPacket.getBytes(); - - DatagramPacket networkPacket = new DatagramPacket(buffer, buffer.length, address, port); - datagramSocket.send(networkPacket); - - logger.info("Packet sent to: {}", address.getHostAddress()); - } catch (IOException e) { - logger.error("Error sending packet", e); - throw new SendFailedException("Error sending packet", e); - } - } - - @Override - public Packet receive() throws PacketParsingException, ReceiveFailedException { - try { - byte[] buffer = new byte[1024]; - DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length); - datagramSocket.receive(datagramPacket); - - String rawPacket = new String(datagramPacket.getData(), 0, datagramPacket.getLength()); - if (rawPacket.isEmpty()) { - throw new PacketParsingException("Raw packet is empty."); - } - - JsonObject member = gson.fromJson(rawPacket, JsonObject.class); - JsonElement typeString = member.get("type"); - JsonElement data = member.get("data"); - - Type packetType = Class.forName(typeString.getAsString()); - Packet packet = gson.fromJson(data, packetType); - - logger.info("Packet received: {}", packet.getClass().getSimpleName()); - return packet; - } catch (ClassNotFoundException | PacketParsingException e) { - logger.error("Couldn't transform raw json into a valid packet.", e); - throw new PacketParsingException("Couldn't transform raw json into a valid packet.", e); - } catch (SocketException e) { - } catch (Exception e) { - logger.error("Error receiving packet"); - throw new ReceiveFailedException("Failed to receive UDP packet", e); - } - - return null; - } - - @Override - public void close() { - datagramSocket.close(); - connected = false; - logger.info("UDP connection closed"); - } - - @Override - public boolean isConnected() { - return connected; - } -} +package com.han.asd.api.network.connection; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.han.asd.api.network.exceptions.NetworkConfigurationException; +import com.han.asd.api.network.exceptions.PacketParsingException; +import com.han.asd.api.network.exceptions.ReceiveFailedException; +import com.han.asd.api.network.exceptions.SendFailedException; +import com.han.asd.api.network.packet.Packet; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.net.*; +import java.util.Collections; +import java.util.Enumeration; + +public class UDPConnection implements Connection { + private static final Logger logger = LogManager.getLogger(UDPConnection.class); + + private final Gson gson; + private final DatagramSocket datagramSocket; + private final InetAddress address; + private final int port; + private boolean connected; + + public UDPConnection(DatagramSocket datagramSocket, InetAddress address, int port) { + this.datagramSocket = datagramSocket; + this.gson = new GsonBuilder().create(); + this.address = address; + this.port = port; + this.connected = true; + } + + public UDPConnection(DatagramSocket datagramSocket) { + this(datagramSocket, datagramSocket.getInetAddress(), datagramSocket.getLocalPort()); + } + + public UDPConnection(InetAddress address, int port) throws SocketException { + this(new DatagramSocket(), address, port); + } + + public UDPConnection(int port) throws SocketException, NetworkConfigurationException { + this(getLocalBroadcastAddress(), port); + } + + public static InetAddress getLocalBroadcastAddress() throws NetworkConfigurationException { + try { + Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); + for (NetworkInterface netInt : Collections.list(networkInterfaces)) { + if (netInt.isLoopback() || !netInt.isUp()) + continue; + + for (InterfaceAddress interfaceAddress : netInt.getInterfaceAddresses()) { + InetAddress broadcast = interfaceAddress.getBroadcast(); + if (broadcast == null) + continue; + + if (interfaceAddress.getAddress().equals(InetAddress.getLocalHost())) { + return broadcast; + } + } + } + } catch (SocketException | UnknownHostException e) { + throw new NetworkConfigurationException("Socket initialization failed", e); + } + return null; + } + + public void broadcast(Packet packet) throws SendFailedException, NetworkConfigurationException { + InetAddress broadcastAddress = getLocalBroadcastAddress(); + if (broadcastAddress != null) { + send(broadcastAddress, packet); + logger.info("Broadcast packet sent"); + } else { + logger.error("Broadcast address not found"); + } + } + + @Override + public void send(Packet packet) throws SendFailedException { + send(address, packet); + } + + private void send(InetAddress address, Packet packet) throws SendFailedException { + try { + JsonObject wrapper = new JsonObject(); + wrapper.addProperty("type", packet.getClass().getName()); + wrapper.add("data", gson.toJsonTree(packet)); + + String rawPacket = wrapper.toString(); + byte[] buffer = rawPacket.getBytes(); + + DatagramPacket networkPacket = new DatagramPacket(buffer, buffer.length, address, port); + datagramSocket.send(networkPacket); + + logger.info("Packet sent to: {}", address.getHostAddress()); + } catch (IOException e) { + logger.error("Error sending packet", e); + throw new SendFailedException("Error sending packet", e); + } + } + + @Override + public Packet receive() throws PacketParsingException, ReceiveFailedException { + try { + byte[] buffer = new byte[1024]; + DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length); + datagramSocket.receive(datagramPacket); + + String rawPacket = new String(datagramPacket.getData(), 0, datagramPacket.getLength()); + if (rawPacket.isEmpty()) { + throw new PacketParsingException("Raw packet is empty."); + } + + JsonObject member = gson.fromJson(rawPacket, JsonObject.class); + JsonElement typeString = member.get("type"); + JsonElement data = member.get("data"); + + Type packetType = Class.forName(typeString.getAsString()); + Packet packet = gson.fromJson(data, packetType); + + logger.info("Packet received: {}", packet.getClass().getSimpleName()); + return packet; + } catch (ClassNotFoundException | PacketParsingException e) { + logger.error("Couldn't transform raw json into a valid packet.", e); + throw new PacketParsingException("Couldn't transform raw json into a valid packet.", e); + } catch (SocketException _) { + } catch (Exception e) { + logger.error("Error receiving packet"); + throw new ReceiveFailedException("Failed to receive UDP packet", e); + } + + return null; + } + + @Override + public void close() { + if (datagramSocket != null && !datagramSocket.isClosed()) { + datagramSocket.close(); + } + + connected = false; + + logger.info("UDP connection closed"); + } + + @Override + public boolean isConnected() { + return connected; + } +} diff --git a/src/main/java/com/han/asd/api/network/discovery/Discovery.java b/src/main/java/com/han/asd/api/network/discovery/Discovery.java new file mode 100644 index 0000000000000000000000000000000000000000..46863feaec15fe93cc5dfc6a39285f7dfd5fa7fd --- /dev/null +++ b/src/main/java/com/han/asd/api/network/discovery/Discovery.java @@ -0,0 +1,13 @@ +package com.han.asd.api.network.discovery; + +import com.han.asd.api.network.exceptions.ConnectionFailedException; +import com.han.asd.api.network.exceptions.SendFailedException; +import java.util.List; + +public interface Discovery { + void allowHostDiscovery(int port) throws ConnectionFailedException; + void disableHostDiscovery(); + void discoverHosts(int server_port, int port) throws ConnectionFailedException, SendFailedException; + void stopHostDiscovery(); + List<String> getHosts(); +} diff --git a/src/main/java/com/han/asd/api/network/discovery/LocalDiscovery.java b/src/main/java/com/han/asd/api/network/discovery/LocalDiscovery.java new file mode 100644 index 0000000000000000000000000000000000000000..acb5d701322bef0d05c1b7f79ec57d44b91f6f30 --- /dev/null +++ b/src/main/java/com/han/asd/api/network/discovery/LocalDiscovery.java @@ -0,0 +1,71 @@ +package com.han.asd.api.network.discovery; + +import com.han.asd.api.network.connection.UDPConnection; +import com.han.asd.api.network.exceptions.ConnectionFailedException; +import com.han.asd.api.network.exceptions.SendFailedException; +import com.han.asd.api.network.packet.Packet; +import com.han.asd.api.network.packets.DiscoveryPacket; +import com.han.asd.api.network.server.BaseUDPServer; +import com.han.asd.api.network.server.MulticastServer; +import com.han.asd.api.network.server.UDPServer; +import com.han.asd.api.network.tick.TickManager; +import com.han.asd.api.threading.ThreadManager; + +import java.io.IOException; +import java.net.InetAddress; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class LocalDiscovery implements Discovery { + private BaseUDPServer hostDiscoveryServer; + private BaseUDPServer discoverServer; + private final ThreadManager threadManager; + private final TickManager tickManager; + private final InetAddress mcastAddress; + private final List<String> hosts; + + public LocalDiscovery(ThreadManager threadManager, TickManager tickManager, InetAddress mcastAddress) { + this.threadManager = threadManager; + this.tickManager = tickManager; + this.mcastAddress = mcastAddress; + this.hosts = new CopyOnWriteArrayList<>(); + } + + @Override + public void allowHostDiscovery(int port) throws ConnectionFailedException { + hostDiscoveryServer = new MulticastServer(mcastAddress, tickManager); + hostDiscoveryServer.start(port); + threadManager.runOperation(hostDiscoveryServer); + } + + @Override + public void disableHostDiscovery() { + threadManager.stopOperation(hostDiscoveryServer); + } + + @Override + public void discoverHosts(int server_port, int port) throws ConnectionFailedException, SendFailedException { + try { + discoverServer = new UDPServer(tickManager); + discoverServer.start(port); + + threadManager.runOperation(discoverServer); + + Packet packet = new DiscoveryPacket(InetAddress.getLocalHost().getHostAddress()); + UDPConnection udpConnection = new UDPConnection(mcastAddress, server_port); + udpConnection.send(packet); + } catch (IOException e) { + throw new SendFailedException("Failed to send discover packet", e); + } + } + + @Override + public void stopHostDiscovery() { + threadManager.stopOperation(discoverServer); + } + + @Override + public List<String> getHosts() { + return hosts; + } +} diff --git a/src/main/java/com/han/asd/network/listeners/DiscoveryPacketListener.java b/src/main/java/com/han/asd/api/network/discovery/listeners/DiscoveryPacketListener.java similarity index 54% rename from src/main/java/com/han/asd/network/listeners/DiscoveryPacketListener.java rename to src/main/java/com/han/asd/api/network/discovery/listeners/DiscoveryPacketListener.java index abf8548d51b617da4a16783e296f444077774e2f..5e965ef1b9959761bbec6d929f3b19233035ef77 100644 --- a/src/main/java/com/han/asd/network/listeners/DiscoveryPacketListener.java +++ b/src/main/java/com/han/asd/api/network/discovery/listeners/DiscoveryPacketListener.java @@ -1,15 +1,14 @@ -package com.han.asd.network.listeners; - -import com.han.asd.Main; -import com.han.asd.network.Network; -import com.han.asd.network.connection.UDPConnection; -import com.han.asd.network.exceptions.SendFailedException; -import com.han.asd.network.packet.Packet; -import com.han.asd.network.packet.PacketHandler; -import com.han.asd.network.packet.PacketListener; -import com.han.asd.network.packet.PacketPriority; -import com.han.asd.network.packets.DiscoveryPacket; -import com.han.asd.network.packets.DiscoveryResponsePacket; +package com.han.asd.api.network.discovery.listeners; + +import com.han.asd.api.network.connection.UDPConnection; +import com.han.asd.api.network.exceptions.SendFailedException; +import com.han.asd.api.network.packet.Packet; +import com.han.asd.api.network.packet.PacketHandler; +import com.han.asd.api.network.packet.PacketListener; +import com.han.asd.api.network.packet.PacketPriority; +import com.han.asd.api.network.packets.DiscoveryPacket; +import com.han.asd.api.network.packets.DiscoveryResponsePacket; +import com.han.asd.network.LocalNetwork; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -20,12 +19,12 @@ import java.util.concurrent.atomic.AtomicBoolean; public class DiscoveryPacketListener implements PacketListener { private static final Logger logger = LogManager.getLogger(DiscoveryPacketListener.class); - private final Network network; private final AtomicBoolean busy; + private final LocalNetwork network; - public DiscoveryPacketListener(Network network) { - this.network = network; + public DiscoveryPacketListener(LocalNetwork network) { this.busy = new AtomicBoolean(false); + this.network = network; } @PacketHandler(priority = PacketPriority.MONITOR) @@ -34,18 +33,14 @@ public class DiscoveryPacketListener implements PacketListener { return; } -// String localAddresss = InetAddress.getLocalHost().getHostAddress(); -// if (discoveryPacket.getSourceAddress().equals(localAddresss)) -// return; - busy.set(true); - logger.info("Discovery packet received from: {} {}", discoveryPacket.getSourceAddress(), network.getPort()); + logger.info("Discovery packet received from: {}", discoveryPacket.getSourceAddress()); InetAddress targetAddress = InetAddress.getByName(discoveryPacket.getSourceAddress()); Packet packet = new DiscoveryResponsePacket(InetAddress.getLocalHost().getHostAddress()); - UDPConnection udpConnection = new UDPConnection(targetAddress, Main.TEST_DUNGEON_PORT_2); + UDPConnection udpConnection = new UDPConnection(targetAddress, network.getDiscoveryServerPort()); udpConnection.send(packet); busy.set(false); diff --git a/src/main/java/com/han/asd/network/listeners/DiscoveryResponsePacketListener.java b/src/main/java/com/han/asd/api/network/discovery/listeners/DiscoveryResponsePacketListener.java similarity index 51% rename from src/main/java/com/han/asd/network/listeners/DiscoveryResponsePacketListener.java rename to src/main/java/com/han/asd/api/network/discovery/listeners/DiscoveryResponsePacketListener.java index 2da70c74d41555d35d55040104deec97ba4be51c..0409f74391459d21c3b50f1b33c2fca2d3d929cf 100644 --- a/src/main/java/com/han/asd/network/listeners/DiscoveryResponsePacketListener.java +++ b/src/main/java/com/han/asd/api/network/discovery/listeners/DiscoveryResponsePacketListener.java @@ -1,24 +1,24 @@ -package com.han.asd.network.listeners; +package com.han.asd.api.network.discovery.listeners; -import com.han.asd.network.Network; -import com.han.asd.network.packet.PacketHandler; -import com.han.asd.network.packet.PacketListener; -import com.han.asd.network.packet.PacketPriority; -import com.han.asd.network.packets.DiscoveryResponsePacket; +import com.han.asd.api.network.packet.PacketHandler; +import com.han.asd.api.network.packet.PacketListener; +import com.han.asd.api.network.packet.PacketPriority; +import com.han.asd.api.network.packets.DiscoveryResponsePacket; +import com.han.asd.network.LocalNetwork; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class DiscoveryResponsePacketListener implements PacketListener { private static final Logger logger = LogManager.getLogger(DiscoveryResponsePacketListener.class); - private final Network network; + private final LocalNetwork network; - public DiscoveryResponsePacketListener(Network network) { + public DiscoveryResponsePacketListener(LocalNetwork network) { this.network = network; } @PacketHandler(priority = PacketPriority.MONITOR) public void onDiscoveryResponsePacket(DiscoveryResponsePacket discoveryPacket) { logger.info("Discovery response received from: {}", discoveryPacket.getDestinationAddress()); - network.getHosts().add(discoveryPacket.getDestinationAddress()); + network.getDiscovery().getHosts().add(discoveryPacket.getDestinationAddress()); } } diff --git a/src/main/java/com/han/asd/network/exceptions/ConnectionFailedException.java b/src/main/java/com/han/asd/api/network/exceptions/ConnectionFailedException.java similarity index 83% rename from src/main/java/com/han/asd/network/exceptions/ConnectionFailedException.java rename to src/main/java/com/han/asd/api/network/exceptions/ConnectionFailedException.java index a920bbacbda5f3dc0e3fb51d714b5451bf9df506..bab99c1fce0cdb43dcf8dbb3ab1529a0ba74d3b8 100644 --- a/src/main/java/com/han/asd/network/exceptions/ConnectionFailedException.java +++ b/src/main/java/com/han/asd/api/network/exceptions/ConnectionFailedException.java @@ -1,11 +1,11 @@ -package com.han.asd.network.exceptions; - -public class ConnectionFailedException extends Exception { - public ConnectionFailedException(String message) { - super(message); - } - - public ConnectionFailedException(String message, Throwable cause) { - super(message, cause); - } -} +package com.han.asd.api.network.exceptions; + +public class ConnectionFailedException extends Exception { + public ConnectionFailedException(String message) { + super(message); + } + + public ConnectionFailedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/han/asd/network/exceptions/DisconnectFailedException.java b/src/main/java/com/han/asd/api/network/exceptions/DisconnectFailedException.java similarity index 83% rename from src/main/java/com/han/asd/network/exceptions/DisconnectFailedException.java rename to src/main/java/com/han/asd/api/network/exceptions/DisconnectFailedException.java index 6995bc7106ea9a7a39bc87326c4c0fa170a07a66..a988bb7592119c18e3f177ff4959a87aebe3ba16 100644 --- a/src/main/java/com/han/asd/network/exceptions/DisconnectFailedException.java +++ b/src/main/java/com/han/asd/api/network/exceptions/DisconnectFailedException.java @@ -1,11 +1,11 @@ -package com.han.asd.network.exceptions; - -public class DisconnectFailedException extends Exception { - public DisconnectFailedException(String message) { - super(message); - } - - public DisconnectFailedException(String message, Throwable cause) { - super(message, cause); - } -} +package com.han.asd.api.network.exceptions; + +public class DisconnectFailedException extends Exception { + public DisconnectFailedException(String message) { + super(message); + } + + public DisconnectFailedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/han/asd/network/exceptions/DispatchPacketException.java b/src/main/java/com/han/asd/api/network/exceptions/DispatchPacketException.java similarity index 85% rename from src/main/java/com/han/asd/network/exceptions/DispatchPacketException.java rename to src/main/java/com/han/asd/api/network/exceptions/DispatchPacketException.java index 50f566139e01fff56a00a160f0f69d6332fc0e0b..bc82529a223c83be211a3f9aa08f53aa6d7dafe8 100644 --- a/src/main/java/com/han/asd/network/exceptions/DispatchPacketException.java +++ b/src/main/java/com/han/asd/api/network/exceptions/DispatchPacketException.java @@ -1,4 +1,4 @@ -package com.han.asd.network.exceptions; +package com.han.asd.api.network.exceptions; public class DispatchPacketException extends Exception { public DispatchPacketException(String errorMessage) { diff --git a/src/main/java/com/han/asd/network/exceptions/NetworkConfigurationException.java b/src/main/java/com/han/asd/api/network/exceptions/NetworkConfigurationException.java similarity index 84% rename from src/main/java/com/han/asd/network/exceptions/NetworkConfigurationException.java rename to src/main/java/com/han/asd/api/network/exceptions/NetworkConfigurationException.java index 14d2f4e76494c5ccebe9c7c2ac83d427c568ae3c..9b077ed1f9eb04819fcdec7a9a3d84490c45029c 100644 --- a/src/main/java/com/han/asd/network/exceptions/NetworkConfigurationException.java +++ b/src/main/java/com/han/asd/api/network/exceptions/NetworkConfigurationException.java @@ -1,11 +1,11 @@ -package com.han.asd.network.exceptions; - -public class NetworkConfigurationException extends Exception { - public NetworkConfigurationException(String errorMessage) { - super(errorMessage); - } - - public NetworkConfigurationException(String message, Throwable cause) { - super(message, cause); - } +package com.han.asd.api.network.exceptions; + +public class NetworkConfigurationException extends Exception { + public NetworkConfigurationException(String errorMessage) { + super(errorMessage); + } + + public NetworkConfigurationException(String message, Throwable cause) { + super(message, cause); + } } \ No newline at end of file diff --git a/src/main/java/com/han/asd/network/exceptions/PacketParsingException.java b/src/main/java/com/han/asd/api/network/exceptions/PacketParsingException.java similarity index 83% rename from src/main/java/com/han/asd/network/exceptions/PacketParsingException.java rename to src/main/java/com/han/asd/api/network/exceptions/PacketParsingException.java index 28273062e36ea9ae0c1e2d46dd015c77e24fb6cc..01b15bb0f4adfd2418708f36c3759d7d5da80f83 100644 --- a/src/main/java/com/han/asd/network/exceptions/PacketParsingException.java +++ b/src/main/java/com/han/asd/api/network/exceptions/PacketParsingException.java @@ -1,11 +1,11 @@ -package com.han.asd.network.exceptions; - -public class PacketParsingException extends Exception { - public PacketParsingException(String errorMessage) { - super(errorMessage); - } - - public PacketParsingException(String message, Throwable cause) { - super(message, cause); - } +package com.han.asd.api.network.exceptions; + +public class PacketParsingException extends Exception { + public PacketParsingException(String errorMessage) { + super(errorMessage); + } + + public PacketParsingException(String message, Throwable cause) { + super(message, cause); + } } \ No newline at end of file diff --git a/src/main/java/com/han/asd/network/exceptions/ReceiveFailedException.java b/src/main/java/com/han/asd/api/network/exceptions/ReceiveFailedException.java similarity index 83% rename from src/main/java/com/han/asd/network/exceptions/ReceiveFailedException.java rename to src/main/java/com/han/asd/api/network/exceptions/ReceiveFailedException.java index 3f0c3456f44bab2145d56dee39c239b85b32e86c..70f7c87ad992db08c68155f0beadf0460410a1b8 100644 --- a/src/main/java/com/han/asd/network/exceptions/ReceiveFailedException.java +++ b/src/main/java/com/han/asd/api/network/exceptions/ReceiveFailedException.java @@ -1,11 +1,11 @@ -package com.han.asd.network.exceptions; - -public class ReceiveFailedException extends Exception { - public ReceiveFailedException(String errorMessage) { - super(errorMessage); - } - - public ReceiveFailedException(String message, Throwable cause) { - super(message, cause); - } +package com.han.asd.api.network.exceptions; + +public class ReceiveFailedException extends Exception { + public ReceiveFailedException(String errorMessage) { + super(errorMessage); + } + + public ReceiveFailedException(String message, Throwable cause) { + super(message, cause); + } } \ No newline at end of file diff --git a/src/main/java/com/han/asd/network/exceptions/SendFailedException.java b/src/main/java/com/han/asd/api/network/exceptions/SendFailedException.java similarity index 83% rename from src/main/java/com/han/asd/network/exceptions/SendFailedException.java rename to src/main/java/com/han/asd/api/network/exceptions/SendFailedException.java index 1675d284554675973a55fba794a6e15b31055755..ce6076abffa3a1eb84c1478b824067398ba554fe 100644 --- a/src/main/java/com/han/asd/network/exceptions/SendFailedException.java +++ b/src/main/java/com/han/asd/api/network/exceptions/SendFailedException.java @@ -1,11 +1,11 @@ -package com.han.asd.network.exceptions; - -public class SendFailedException extends Exception { - public SendFailedException(String errorMessage) { - super(errorMessage); - } - - public SendFailedException(String message, Throwable cause) { - super(message, cause); - } +package com.han.asd.api.network.exceptions; + +public class SendFailedException extends Exception { + public SendFailedException(String errorMessage) { + super(errorMessage); + } + + public SendFailedException(String message, Throwable cause) { + super(message, cause); + } } \ No newline at end of file diff --git a/src/main/java/com/han/asd/network/packet/Packet.java b/src/main/java/com/han/asd/api/network/packet/Packet.java similarity index 61% rename from src/main/java/com/han/asd/network/packet/Packet.java rename to src/main/java/com/han/asd/api/network/packet/Packet.java index 22b65374a2ad35395ebf4a88135737a0a1a5c02a..0415a5f413a0e9d25953bd9a9a786a6632444373 100644 --- a/src/main/java/com/han/asd/network/packet/Packet.java +++ b/src/main/java/com/han/asd/api/network/packet/Packet.java @@ -1,23 +1,17 @@ -package com.han.asd.network.packet; +package com.han.asd.api.network.packet; import java.util.UUID; public abstract class Packet { private final long timestamp; - private static final UUID uuid = UUID.randomUUID(); public Packet() { this.timestamp = System.currentTimeMillis(); } - public long getTimestamp() { return timestamp; } - - public UUID getUuid() { - return uuid; - } } diff --git a/src/main/java/com/han/asd/network/packet/PacketDispatcher.java b/src/main/java/com/han/asd/api/network/packet/PacketDispatcher.java similarity index 96% rename from src/main/java/com/han/asd/network/packet/PacketDispatcher.java rename to src/main/java/com/han/asd/api/network/packet/PacketDispatcher.java index f5c5e45f4dca1d1d90b8e2c0508868a8b7b554e2..2d5b78427689c1bd0e9c0ccacb274c586daaadbc 100644 --- a/src/main/java/com/han/asd/network/packet/PacketDispatcher.java +++ b/src/main/java/com/han/asd/api/network/packet/PacketDispatcher.java @@ -1,6 +1,6 @@ -package com.han.asd.network.packet; +package com.han.asd.api.network.packet; -import com.han.asd.network.exceptions.DispatchPacketException; +import com.han.asd.api.network.exceptions.DispatchPacketException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/com/han/asd/network/packet/PacketHandler.java b/src/main/java/com/han/asd/api/network/packet/PacketHandler.java similarity index 89% rename from src/main/java/com/han/asd/network/packet/PacketHandler.java rename to src/main/java/com/han/asd/api/network/packet/PacketHandler.java index 686513a66243da6c248372e6004587b351eaa444..9dee0a3a54fb260be5dc4da73230559dceeaeba0 100644 --- a/src/main/java/com/han/asd/network/packet/PacketHandler.java +++ b/src/main/java/com/han/asd/api/network/packet/PacketHandler.java @@ -1,4 +1,4 @@ -package com.han.asd.network.packet; +package com.han.asd.api.network.packet; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/main/java/com/han/asd/api/network/packet/PacketListener.java b/src/main/java/com/han/asd/api/network/packet/PacketListener.java new file mode 100644 index 0000000000000000000000000000000000000000..0b29223f3b57b741a98c44b82277c9e70b1c2d46 --- /dev/null +++ b/src/main/java/com/han/asd/api/network/packet/PacketListener.java @@ -0,0 +1,4 @@ +package com.han.asd.api.network.packet; + +public interface PacketListener { +} \ No newline at end of file diff --git a/src/main/java/com/han/asd/network/packet/PacketListenerPriorityComparator.java b/src/main/java/com/han/asd/api/network/packet/PacketListenerPriorityComparator.java similarity index 69% rename from src/main/java/com/han/asd/network/packet/PacketListenerPriorityComparator.java rename to src/main/java/com/han/asd/api/network/packet/PacketListenerPriorityComparator.java index 03d97961b50a12b95e91774f9ff80a1ec80f34d0..4804aea99f42810cda0c697a97c421014c2fe41c 100644 --- a/src/main/java/com/han/asd/network/packet/PacketListenerPriorityComparator.java +++ b/src/main/java/com/han/asd/api/network/packet/PacketListenerPriorityComparator.java @@ -1,4 +1,4 @@ -package com.han.asd.network.packet; +package com.han.asd.api.network.packet; import java.lang.reflect.Method; import java.util.Comparator; @@ -6,6 +6,6 @@ import java.util.Comparator; public class PacketListenerPriorityComparator implements Comparator<Method> { @Override public int compare(Method one, Method two) { - return one.getAnnotation(PacketHandler.class).priority().getSlot() - two.getAnnotation(PacketHandler.class).priority().getSlot(); + return one.getAnnotation(PacketHandler.class).priority().getPriority() - two.getAnnotation(PacketHandler.class).priority().getPriority(); } } \ No newline at end of file diff --git a/src/main/java/com/han/asd/api/network/packet/PacketPriority.java b/src/main/java/com/han/asd/api/network/packet/PacketPriority.java new file mode 100644 index 0000000000000000000000000000000000000000..c0fa755a04c1685fbf4932665116f128f643df3f --- /dev/null +++ b/src/main/java/com/han/asd/api/network/packet/PacketPriority.java @@ -0,0 +1,20 @@ +package com.han.asd.api.network.packet; + +public enum PacketPriority { + LOWEST(0), + LOW(1), + NORMAL(2), + HIGH(3), + HIGHEST(4), + MONITOR(5); + + private final int priority; + + PacketPriority(int priority) { + this.priority = priority; + } + + public int getPriority() { + return priority; + } +} \ No newline at end of file diff --git a/src/main/java/com/han/asd/network/packets/DiscoveryPacket.java b/src/main/java/com/han/asd/api/network/packets/DiscoveryPacket.java similarity index 75% rename from src/main/java/com/han/asd/network/packets/DiscoveryPacket.java rename to src/main/java/com/han/asd/api/network/packets/DiscoveryPacket.java index aa092e81de64747d45e46c2afcbf482eae290aca..26b28dd8ec236df0a51a7088419769910f42d773 100644 --- a/src/main/java/com/han/asd/network/packets/DiscoveryPacket.java +++ b/src/main/java/com/han/asd/api/network/packets/DiscoveryPacket.java @@ -1,6 +1,6 @@ -package com.han.asd.network.packets; +package com.han.asd.api.network.packets; -import com.han.asd.network.packet.Packet; +import com.han.asd.api.network.packet.Packet; public class DiscoveryPacket extends Packet { private final String sourceAddress; diff --git a/src/main/java/com/han/asd/network/packets/DiscoveryResponsePacket.java b/src/main/java/com/han/asd/api/network/packets/DiscoveryResponsePacket.java similarity index 78% rename from src/main/java/com/han/asd/network/packets/DiscoveryResponsePacket.java rename to src/main/java/com/han/asd/api/network/packets/DiscoveryResponsePacket.java index 315f4aee935e7d8680cadbf9fb710b6fad82240a..875b8359a112e71306d29265bcd659e76aa2d71b 100644 --- a/src/main/java/com/han/asd/network/packets/DiscoveryResponsePacket.java +++ b/src/main/java/com/han/asd/api/network/packets/DiscoveryResponsePacket.java @@ -1,6 +1,6 @@ -package com.han.asd.network.packets; +package com.han.asd.api.network.packets; -import com.han.asd.network.packet.Packet; +import com.han.asd.api.network.packet.Packet; public class DiscoveryResponsePacket extends Packet { private final String destinationAddress; diff --git a/src/main/java/com/han/asd/api/network/packets/TimeSyncPacket.java b/src/main/java/com/han/asd/api/network/packets/TimeSyncPacket.java new file mode 100644 index 0000000000000000000000000000000000000000..ef4082b3d1153b16202f32bf5081131d6063f1e1 --- /dev/null +++ b/src/main/java/com/han/asd/api/network/packets/TimeSyncPacket.java @@ -0,0 +1,10 @@ +package com.han.asd.api.network.packets; + +import com.han.asd.api.network.packet.Packet; + +public class TimeSyncPacket extends Packet { + + public TimeSyncPacket() { + + } +} diff --git a/src/main/java/com/han/asd/api/network/server/BaseUDPServer.java b/src/main/java/com/han/asd/api/network/server/BaseUDPServer.java new file mode 100644 index 0000000000000000000000000000000000000000..9b3544004fc7360588187d77df70512d52ea824b --- /dev/null +++ b/src/main/java/com/han/asd/api/network/server/BaseUDPServer.java @@ -0,0 +1,51 @@ +package com.han.asd.api.network.server; + +import com.han.asd.api.network.connection.Connection; +import com.han.asd.api.network.connection.ConnectionHandler; +import com.han.asd.api.network.connection.UDPConnection; +import com.han.asd.api.network.exceptions.ConnectionFailedException; +import com.han.asd.api.network.tick.TickManager; + +import java.net.DatagramSocket; + +public abstract class BaseUDPServer implements Server { + private final TickManager tickManager; + private ConnectionHandler connectionHandler; + + public BaseUDPServer(TickManager tickManager) { + this.tickManager = tickManager; + } + + public void start(DatagramSocket datagramSocket) { + UDPConnection udpConnection = new UDPConnection(datagramSocket); + connectionHandler = new ConnectionHandler(tickManager, udpConnection); + } + + @Override + public abstract void start(int port) throws ConnectionFailedException; + + @Override + public void stop() { + connectionHandler.stop(); + } + + @Override + public void execute() { + connectionHandler.execute(); + } + + @Override + public long getTimeout() { + return 1; + } + + @Override + public int getPriority() { + return 0; + } + + @Override + public Connection getConnection() { + return connectionHandler.getConnection(); + } +} diff --git a/src/main/java/com/han/asd/network/server/MulticastServer.java b/src/main/java/com/han/asd/api/network/server/MulticastServer.java similarity index 52% rename from src/main/java/com/han/asd/network/server/MulticastServer.java rename to src/main/java/com/han/asd/api/network/server/MulticastServer.java index f34d2a2fc1f5d60170e29b84192dd13eef602605..dfa4567a8037388440b7528cb13c75408175ccbf 100644 --- a/src/main/java/com/han/asd/network/server/MulticastServer.java +++ b/src/main/java/com/han/asd/api/network/server/MulticastServer.java @@ -1,7 +1,9 @@ -package com.han.asd.network.server; +package com.han.asd.api.network.server; -import com.han.asd.network.packet.PacketDispatcher; +import com.han.asd.api.network.exceptions.ConnectionFailedException; +import com.han.asd.api.network.tick.TickManager; +import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.MulticastSocket; @@ -11,20 +13,20 @@ public class MulticastServer extends BaseUDPServer { private final String mcastAddr; - public MulticastServer(InetAddress mcastAddr, PacketDispatcher packetDispatcher) { - super(packetDispatcher); + public MulticastServer(InetAddress mcastAddr, TickManager tickManager) { + super(tickManager); this.mcastAddr = mcastAddr.getHostAddress(); } @Override - public void start(int port) { + public void start(int port) throws ConnectionFailedException { try { MulticastSocket multicastSocket = new MulticastSocket(port); InetAddress group = InetAddress.getByName(mcastAddr); multicastSocket.joinGroup(new InetSocketAddress(group, 0), NetworkInterface.getByIndex(0)); - start(port, multicastSocket); - } catch (Exception e) { - System.out.println("fout"); + start(multicastSocket); + } catch (IOException e) { + throw new ConnectionFailedException("Failed to start multicast server", e); } } } diff --git a/src/main/java/com/han/asd/api/network/server/Server.java b/src/main/java/com/han/asd/api/network/server/Server.java new file mode 100644 index 0000000000000000000000000000000000000000..e1ca59aeb8a8ef846bfea38771345f30aa08f54d --- /dev/null +++ b/src/main/java/com/han/asd/api/network/server/Server.java @@ -0,0 +1,11 @@ +package com.han.asd.api.network.server; + +import com.han.asd.api.network.connection.Connection; +import com.han.asd.api.network.exceptions.ConnectionFailedException; +import com.han.asd.api.threading.operation.Operation; + +public interface Server extends Operation { + void start(int port) throws ConnectionFailedException; + void stop(); + Connection getConnection(); +} diff --git a/src/main/java/com/han/asd/network/server/TCPServer.java b/src/main/java/com/han/asd/api/network/server/TCPServer.java similarity index 50% rename from src/main/java/com/han/asd/network/server/TCPServer.java rename to src/main/java/com/han/asd/api/network/server/TCPServer.java index 618ddc15a81d4c6ce570c8d2997065f2f3e85e85..9c0dd7370f934be25faeabc378f7bfafdb59c0ff 100644 --- a/src/main/java/com/han/asd/network/server/TCPServer.java +++ b/src/main/java/com/han/asd/api/network/server/TCPServer.java @@ -1,10 +1,11 @@ -package com.han.asd.network.server; +package com.han.asd.api.network.server; -import com.han.asd.network.connection.ConnectionHandler; -import com.han.asd.network.connection.TCPConnection; -import com.han.asd.network.exceptions.ConnectionFailedException; -import com.han.asd.network.packet.PacketDispatcher; -import com.han.asd.network.tick.TickManager; +import com.han.asd.api.network.connection.Connection; +import com.han.asd.api.network.connection.ConnectionHandler; +import com.han.asd.api.network.connection.TCPConnection; +import com.han.asd.api.network.exceptions.ConnectionFailedException; +import com.han.asd.api.network.tick.TickManager; +import com.han.asd.api.threading.ThreadManager; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -14,32 +15,32 @@ import java.net.Socket; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -public class TCPServer implements Server, Runnable { +public class TCPServer implements Server { private static final Logger logger = LogManager.getLogger(TCPServer.class); - private final List<ConnectionHandler> connections = new CopyOnWriteArrayList<>(); + private final List<ConnectionHandler> connectionHandlers = new CopyOnWriteArrayList<>(); private final TickManager tickManager; private ServerSocket serverSocket; + private ThreadManager threadManager; - public TCPServer(TickManager tickManager) { + public TCPServer(ThreadManager threadManager, TickManager tickManager) { this.tickManager = tickManager; + this.threadManager = threadManager; } @Override public void start(int port) throws ConnectionFailedException { try { this.serverSocket = new ServerSocket(port); - new Thread(this).start(); - logger.info("TCP server started on port: {}", port); } catch (IOException e) { - throw new ConnectionFailedException("TCP server failed to start on port: " + port, e); + throw new ConnectionFailedException("Failed to start TCP server"); } } @Override public void stop() { try { - for (ConnectionHandler connection : connections) { - connection.close(); + for (ConnectionHandler connectionHandler : connectionHandlers) { + connectionHandler.stop(); } if (!serverSocket.isClosed()) { @@ -52,25 +53,40 @@ public class TCPServer implements Server, Runnable { } @Override - public void run() { + public Connection getConnection() { + return null; + } + + public List<ConnectionHandler> getConnections() { + return connectionHandlers; + } + + @Override + public void execute() { try { - while (!serverSocket.isClosed()) { + while (!Thread.interrupted() && !serverSocket.isClosed()) { Socket socket = serverSocket.accept(); TCPConnection clientConnection = new TCPConnection(socket); if (clientConnection.isConnected()) { - ConnectionHandler connectionHandler = new ConnectionHandler(tickManager); - connectionHandler.start(clientConnection); - connections.add(connectionHandler); + ConnectionHandler connectionHandler = new ConnectionHandler(tickManager, clientConnection); + connectionHandlers.add(connectionHandler); + threadManager.runOperation(connectionHandler); logger.info("New TCP connection established"); } } - } catch (IOException e) { + } catch (IOException | ConnectionFailedException e) { logger.info("TCP server socket closed"); } } - public List<ConnectionHandler> getConnections() { - return connections; + @Override + public long getTimeout() { + return 1; + } + + @Override + public int getPriority() { + return 0; } } diff --git a/src/main/java/com/han/asd/api/network/server/UDPServer.java b/src/main/java/com/han/asd/api/network/server/UDPServer.java new file mode 100644 index 0000000000000000000000000000000000000000..d656c5d38ed0d9f29099b503e3c6c576b4bf3925 --- /dev/null +++ b/src/main/java/com/han/asd/api/network/server/UDPServer.java @@ -0,0 +1,26 @@ +package com.han.asd.api.network.server; + +import com.han.asd.api.network.exceptions.ConnectionFailedException; +import com.han.asd.api.network.tick.TickManager; + +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; + +public class UDPServer extends BaseUDPServer { + + public UDPServer(TickManager tickManager) { + super(tickManager); + } + + @Override + public void start(int port) throws ConnectionFailedException { + try { + DatagramSocket datagramSocket = new DatagramSocket(port, InetAddress.getLocalHost()); + start(datagramSocket); + } catch (UnknownHostException | SocketException e) { + throw new ConnectionFailedException("Failed to start UDP server", e); + } + } +} diff --git a/src/main/java/com/han/asd/api/network/tick/Tick.java b/src/main/java/com/han/asd/api/network/tick/Tick.java new file mode 100644 index 0000000000000000000000000000000000000000..9f0cc805ad92691e5962824da5873f567d902e13 --- /dev/null +++ b/src/main/java/com/han/asd/api/network/tick/Tick.java @@ -0,0 +1,29 @@ +package com.han.asd.api.network.tick; + +public class Tick { + + private long delta; + + public Tick(long delta) { + this.delta = delta; + } + + public long millisToNextSecond() { + long synchronizedSystemTime = System.currentTimeMillis() + this.delta; + return 1000 - (synchronizedSystemTime % 1000); + } + + public long calculateDelta(long clientTime) { + long serverTime = System.currentTimeMillis(); + long latency = (serverTime - clientTime) / 2; + return (serverTime - clientTime) + latency; + } + + public long getDelta() { + return delta; + } + + public void setDelta(long delta) { + this.delta = delta; + } +} diff --git a/src/main/java/com/han/asd/api/network/tick/TickManager.java b/src/main/java/com/han/asd/api/network/tick/TickManager.java new file mode 100644 index 0000000000000000000000000000000000000000..29a54f2c55bbb08ae1e1ce9dba74c0bab83905a3 --- /dev/null +++ b/src/main/java/com/han/asd/api/network/tick/TickManager.java @@ -0,0 +1,76 @@ +package com.han.asd.api.network.tick; + +import com.han.asd.api.network.exceptions.DispatchPacketException; +import com.han.asd.api.network.packet.Packet; +import com.han.asd.api.network.packet.PacketDispatcher; +import com.han.asd.api.threading.operation.Operation; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.concurrent.atomic.AtomicBoolean; + +public class TickManager implements Operation { + private static final Logger logger = LogManager.getLogger(TickManager.class); + private final Tick tick; + private final Queue<Packet> queue; + private final PacketDispatcher packetDispatcher; + private final int tickCount; + private final AtomicBoolean sync; + + public TickManager(PacketDispatcher packetDispatcher, int tickCount) { + this.tick = new Tick(0); + this.queue = new LinkedList<>(); + this.packetDispatcher = packetDispatcher; + this.tickCount = tickCount; + this.sync = new AtomicBoolean(false); + } + + public void sync() { + sync.set(true); + } + + public Queue<Packet> getQueue() { + return queue; + } + + @Override + public void execute() { + try { + while(!Thread.interrupted()) { + if (sync.get()) { + Thread.sleep(tick.millisToNextSecond()); + sync.set(false); + } + + Packet packet = queue.poll(); + if (packet == null) + continue; + + tick.setDelta(tick.calculateDelta(packet.getTimestamp())); + + packetDispatcher.dispatch(packet); + + Thread.sleep(1000 / tickCount); + } + } catch (InterruptedException | DispatchPacketException e) { + logger.error("Error dispatching TickManager queue: ", e); + } + } + + @Override + public long getTimeout() { + return 1; + } + + @Override + public int getPriority() { + return 0; + } + + @Override + public void stop() { + + } +} diff --git a/src/main/java/com/han/asd/api/network/tick/listeners/TimeSyncPacketListener.java b/src/main/java/com/han/asd/api/network/tick/listeners/TimeSyncPacketListener.java new file mode 100644 index 0000000000000000000000000000000000000000..223b62e31ca831933c5d50cbfe5b8bb29b2859b3 --- /dev/null +++ b/src/main/java/com/han/asd/api/network/tick/listeners/TimeSyncPacketListener.java @@ -0,0 +1,24 @@ +package com.han.asd.api.network.tick.listeners; + +import com.han.asd.api.network.packet.PacketHandler; +import com.han.asd.api.network.packet.PacketListener; +import com.han.asd.api.network.packet.PacketPriority; +import com.han.asd.api.network.packets.TimeSyncPacket; +import com.han.asd.network.LocalNetwork; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class TimeSyncPacketListener implements PacketListener { + private static final Logger logger = LogManager.getLogger(TimeSyncPacketListener.class); + private final LocalNetwork network; + + public TimeSyncPacketListener(LocalNetwork network) { + this.network = network; + } + + @PacketHandler(priority = PacketPriority.MONITOR) + public void onTimeSync(TimeSyncPacket timeSyncPacket) { + logger.info("Time sync packet received"); + network.getTickManager().sync(); + } +} diff --git a/src/main/java/com/han/asd/api/threading/ComparableFutureTask.java b/src/main/java/com/han/asd/api/threading/ComparableFutureTask.java new file mode 100644 index 0000000000000000000000000000000000000000..6bc79cf70919acb237b2fccba62f089fc02099de --- /dev/null +++ b/src/main/java/com/han/asd/api/threading/ComparableFutureTask.java @@ -0,0 +1,17 @@ +package com.han.asd.api.threading; + +import java.util.concurrent.FutureTask; + +public class ComparableFutureTask<V> extends FutureTask<V> implements Comparable<ComparableFutureTask<V>> { + private final int priority; + + public ComparableFutureTask(Runnable runnable, V result, int priority) { + super(runnable, result); + this.priority = priority; + } + + @Override + public int compareTo(ComparableFutureTask<V> o) { + return Integer.compare(priority, o.priority); + } +} \ No newline at end of file diff --git a/src/main/java/com/han/asd/api/threading/PriorityRunnable.java b/src/main/java/com/han/asd/api/threading/PriorityRunnable.java new file mode 100644 index 0000000000000000000000000000000000000000..e5b929fbb74b87bfc9cfa19332d4ef893831e019 --- /dev/null +++ b/src/main/java/com/han/asd/api/threading/PriorityRunnable.java @@ -0,0 +1,25 @@ +package com.han.asd.api.threading; + +public class PriorityRunnable implements Runnable, Comparable<PriorityRunnable> { + private final int priority; + private final Runnable task; + + public PriorityRunnable(int priority, Runnable task) { + this.priority = priority; + this.task = task; + } + + @Override + public void run() { + task.run(); + } + + @Override + public int compareTo(PriorityRunnable o) { + return Integer.compare(priority, o.priority); + } + + public int getPriority() { + return priority; + } +} \ No newline at end of file diff --git a/src/main/java/com/han/asd/api/threading/ThreadManager.java b/src/main/java/com/han/asd/api/threading/ThreadManager.java new file mode 100644 index 0000000000000000000000000000000000000000..63598c3ea50f2677fa7ba686ea9bf4e8752078f8 --- /dev/null +++ b/src/main/java/com/han/asd/api/threading/ThreadManager.java @@ -0,0 +1,81 @@ +package com.han.asd.api.threading; + +import com.han.asd.api.threading.operation.Operation; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Manages a thread pool and executes operations on it. + */ +public class ThreadManager { + private static final Logger logger = LogManager.getLogger(ThreadManager.class); + + private final ExecutorService executorService; + private final Map<Operation, Thread> threads; + + /** + * Creates a new thread manager with the specified number of threads. + * + * @param numberOfThreads The number of threads to create. + */ + public ThreadManager(int numberOfThreads) { + this.threads = new HashMap<>(); + this.executorService = Executors.newFixedThreadPool(numberOfThreads); + } + + /** + * Executes the specified operation on the thread pool. + * + * @param operation The operation to execute. + */ + public void runOperation(Operation operation) { + executorService.execute(() -> { + Thread t = Thread.currentThread(); + try { + logger.info("Executing operation on: " + t.getName()); + threads.put(operation, t); + operation.execute(); + } catch (Exception e) { + logger.info("An exception occurred in thread " + t.getName(), e); + } + }); + } + + /** + * Interrupts the specified operation on the thread pool. + * + * @param operation The operation to interrupt. + */ + public void stopOperation(Operation operation) { + Thread thread = threads.get(operation); + if (thread != null) { + thread.interrupt(); + threads.remove(operation, thread); + operation.stop(); + } + } + + /** + * Shuts down the thread pool. + */ + public void shutdown() { + executorService.shutdown(); + try { + // Wait a while for existing tasks to terminate. + if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { + executorService.shutdownNow(); + // Wait a while for tasks to respond to being canceled. + if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) + logger.error("Pool did not terminate"); + } + } catch (InterruptedException ie) { + executorService.shutdownNow(); + Thread.currentThread().interrupt(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/han/asd/api/threading/operation/Operation.java b/src/main/java/com/han/asd/api/threading/operation/Operation.java new file mode 100644 index 0000000000000000000000000000000000000000..155b4876f4e1cf9372e5456ab1d143898d35c146 --- /dev/null +++ b/src/main/java/com/han/asd/api/threading/operation/Operation.java @@ -0,0 +1,11 @@ +package com.han.asd.api.threading.operation; + +public interface Operation { + void execute(); + + long getTimeout(); + + int getPriority(); + + void stop(); +} \ No newline at end of file diff --git a/src/main/java/com/han/asd/game/DungeonGame.java b/src/main/java/com/han/asd/game/DungeonGame.java new file mode 100644 index 0000000000000000000000000000000000000000..230058bc93c7b36ecad15558ce194e4dc536285e --- /dev/null +++ b/src/main/java/com/han/asd/game/DungeonGame.java @@ -0,0 +1,54 @@ +package com.han.asd.game; + +import com.han.asd.api.network.exceptions.ConnectionFailedException; +import com.han.asd.api.network.exceptions.DisconnectFailedException; +import com.han.asd.listener.ConnectionPacketListener; +import com.han.asd.listener.DisconnectPacketListener; +import com.han.asd.network.LocalNetwork; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.net.UnknownHostException; + +public class DungeonGame { + private static final Logger logger = LogManager.getLogger(DungeonGame.class); + private final LocalNetwork network; + + public DungeonGame() throws UnknownHostException { + this.network = new LocalNetwork(); + } + + public void createGame() throws ConnectionFailedException { + network.startHost(); + + network.getPacketDispatcher().register(new ConnectionPacketListener()); + network.getPacketDispatcher().register(new DisconnectPacketListener()); + + logger.info("Lobby created and started"); + } + + public void stopGame() { + network.stopHost(); + logger.info("Lobby stopped"); + } + + public void discoverGames(int timeout) { + logger.info("Started discovery to join lobby"); + network.discoverHosts(timeout); + } + + public void joinGame(String ip) throws ConnectionFailedException { + logger.info("Trying to join lobby {}", ip); + network.connectHost(ip); + } + + public void leaveGame() throws DisconnectFailedException { + network.disconnectHost(); + logger.info("Left the lobby"); + } + + public LocalNetwork getNetwork() { + return network; + } +} diff --git a/src/main/java/com/han/asd/listener/ConnectionPacketListener.java b/src/main/java/com/han/asd/listener/ConnectionPacketListener.java index 5a45443873221fa34ef3befd61c1890a7f6ef189..c273af7ddca1c38d963c4058b592ee8f635b1144 100644 --- a/src/main/java/com/han/asd/listener/ConnectionPacketListener.java +++ b/src/main/java/com/han/asd/listener/ConnectionPacketListener.java @@ -1,8 +1,8 @@ package com.han.asd.listener; -import com.han.asd.network.packet.PacketHandler; -import com.han.asd.network.packet.PacketListener; -import com.han.asd.network.packets.ConnectionPacket; +import com.han.asd.api.network.packet.PacketHandler; +import com.han.asd.api.network.packet.PacketListener; +import com.han.asd.packets.ConnectionPacket; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/com/han/asd/listener/DisconnectPacketListener.java b/src/main/java/com/han/asd/listener/DisconnectPacketListener.java index fe00307ec8a393e3cc3691b0c1f5a5bf015b76e3..bbefe3e5380f004686285c1beedd16cafb7e1bab 100644 --- a/src/main/java/com/han/asd/listener/DisconnectPacketListener.java +++ b/src/main/java/com/han/asd/listener/DisconnectPacketListener.java @@ -1,8 +1,8 @@ package com.han.asd.listener; -import com.han.asd.network.packet.PacketHandler; -import com.han.asd.network.packet.PacketListener; -import com.han.asd.network.packets.DisconnectPacket; +import com.han.asd.api.network.packet.PacketHandler; +import com.han.asd.api.network.packet.PacketListener; +import com.han.asd.packets.DisconnectPacket; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/com/han/asd/network/LocalNetwork.java b/src/main/java/com/han/asd/network/LocalNetwork.java new file mode 100644 index 0000000000000000000000000000000000000000..e9dfbcdc077f56798fa3cc0a6ba99eb99550fdbd --- /dev/null +++ b/src/main/java/com/han/asd/network/LocalNetwork.java @@ -0,0 +1,176 @@ +package com.han.asd.network; + +import com.han.asd.api.network.HostNetwork; +import com.han.asd.api.network.Network; +import com.han.asd.api.network.connection.ConnectionHandler; +import com.han.asd.api.network.connection.TCPConnection; +import com.han.asd.api.network.discovery.Discovery; +import com.han.asd.api.network.discovery.LocalDiscovery; +import com.han.asd.api.network.exceptions.ConnectionFailedException; +import com.han.asd.api.network.exceptions.DisconnectFailedException; +import com.han.asd.api.network.exceptions.SendFailedException; +import com.han.asd.api.network.discovery.listeners.DiscoveryPacketListener; +import com.han.asd.api.network.discovery.listeners.DiscoveryResponsePacketListener; +import com.han.asd.api.network.packets.TimeSyncPacket; +import com.han.asd.api.network.tick.listeners.TimeSyncPacketListener; +import com.han.asd.api.network.packet.Packet; +import com.han.asd.api.network.packet.PacketDispatcher; +import com.han.asd.packets.ConnectionPacket; +import com.han.asd.packets.DisconnectPacket; +import com.han.asd.api.network.tick.TickManager; +import com.han.asd.api.threading.ThreadManager; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +public class LocalNetwork implements Network { + private static final Logger logger = LogManager.getLogger(LocalNetwork.class); + private final HostNetwork hostNetwork; + private final PacketDispatcher packetDispatcher; + private final TickManager tickManager; + private final ThreadManager threadManager; + private ConnectionHandler connectionHandler; + private final Discovery discovery; + + public LocalNetwork() throws UnknownHostException { + this.packetDispatcher = new PacketDispatcher(); + + packetDispatcher.register(new DiscoveryPacketListener(this)); + packetDispatcher.register(new DiscoveryResponsePacketListener(this)); + packetDispatcher.register(new TimeSyncPacketListener(this)); + + this.tickManager = new TickManager(packetDispatcher, getTickCount()); + this.threadManager = new ThreadManager(10); + threadManager.runOperation(tickManager); + + this.hostNetwork = new HostNetwork(tickManager, threadManager); + this.discovery = new LocalDiscovery(threadManager, tickManager, InetAddress.getByName(getDiscoveryAddress())); + + logger.info("Network initialized"); + } + + @Override + public void startHost() throws ConnectionFailedException { + int serverPort = getDiscoveryServerPort(); + discovery.allowHostDiscovery(serverPort); + logger.info("Discovery server started on port: {}", serverPort); + hostNetwork.startHost(serverPort); + logger.info("Lobby server started on port: {}", serverPort); + } + + @Override + public void stopHost() { + discovery.disableHostDiscovery(); + hostNetwork.stopHost(); + logger.info("Lobby stopped"); + } + + public void discoverHosts(int timeout) { + try { + discovery.discoverHosts(getDiscoveryServerPort(), getDiscoveryClientPort()); + Thread.sleep(timeout); + discovery.stopHostDiscovery(); + logger.info("Discovered hosts"); + } catch(SendFailedException | ConnectionFailedException e) { + logger.info("Discovery failed"); + } catch (InterruptedException _) { + } + } + + @Override + public void connectHost(String address) throws ConnectionFailedException { + try { + Socket socket = new Socket(address, getDiscoveryServerPort()); + if(socket.isConnected()) { + TCPConnection tcpConnection = new TCPConnection(socket); + + if (tcpConnection.isConnected()) { + connectionHandler = new ConnectionHandler(tickManager, tcpConnection); + threadManager.runOperation(connectionHandler); + + Packet connectionPacket = new ConnectionPacket("test"); + connectionHandler.getConnection().send(connectionPacket); + + Packet timePacket = new TimeSyncPacket(); + connectionHandler.getConnection().send(timePacket); + + logger.info("Connected to lobby at {}", address); + } + } + } catch (Exception e) { + logger.error("Couldn't connect to the specified lobby", e); + throw new ConnectionFailedException("Failed to connect to " + address, e); + } + } + + @Override + public void disconnectHost() throws DisconnectFailedException { + if (connectionHandler.getConnection() == null) { + logger.error("Error disconnecting from lobby"); + return; + } + + try { + Packet packet = new DisconnectPacket("test"); + connectionHandler.getConnection().send(packet); + + if (connectionHandler.getConnection().isConnected()) { + threadManager.stopOperation(connectionHandler); + logger.info("Disconnected from lobby"); + } + } catch (SendFailedException e) { + logger.error("Error disconnecting from lobby", e); + throw new DisconnectFailedException("Couldn't disconnect from the specified lobby", e); + } + } + + public Discovery getDiscovery() { + return discovery; + } + + @Override + public int getDiscoveryServerPort() { + return 8886; + } + + @Override + public int getDiscoveryClientPort() { + return 8887; + } + + @Override + public String getDiscoveryAddress() { + return "224.0.0.1"; + } + + @Override + public int getTickCount() { + return 20; + } + + @Override + public HostNetwork getHostNetwork() { + return hostNetwork; + } + + public PacketDispatcher getPacketDispatcher() { + return packetDispatcher; + } + + public TickManager getTickManager() { + return tickManager; + } + + @Override + public ThreadManager getThreadManager() { + return threadManager; + } + + @Override + public ConnectionHandler getConnectionHandler() { + return connectionHandler; + } +} diff --git a/src/main/java/com/han/asd/network/Network.java b/src/main/java/com/han/asd/network/Network.java deleted file mode 100644 index fcce4864d915e6d104c0228c821bae0e5e6755ae..0000000000000000000000000000000000000000 --- a/src/main/java/com/han/asd/network/Network.java +++ /dev/null @@ -1,154 +0,0 @@ -package com.han.asd.network; - -import com.han.asd.Main; -import com.han.asd.network.connection.ConnectionHandler; -import com.han.asd.network.connection.TCPConnection; -import com.han.asd.network.connection.UDPConnection; -import com.han.asd.network.exceptions.ConnectionFailedException; -import com.han.asd.network.exceptions.DisconnectFailedException; -import com.han.asd.network.exceptions.NetworkConfigurationException; -import com.han.asd.network.exceptions.SendFailedException; -import com.han.asd.network.listeners.DiscoveryPacketListener; -import com.han.asd.network.listeners.DiscoveryResponsePacketListener; -import com.han.asd.network.packet.Packet; -import com.han.asd.network.packet.PacketDispatcher; -import com.han.asd.network.packets.DisconnectPacket; -import com.han.asd.network.packets.DiscoveryPacket; -import com.han.asd.network.server.BaseUDPServer; -import com.han.asd.network.server.MulticastServer; -import com.han.asd.network.server.TCPServer; -import com.han.asd.network.server.UDPServer; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.net.InetAddress; -import java.net.Socket; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -public class Network { - private static final Logger logger = LogManager.getLogger(Network.class); - private final BaseUDPServer hostServer; - private final BaseUDPServer clientServer; - private final TCPServer tcpServer; - private final PacketDispatcher packetDispatcher; - private final InetAddress mcastAddress = InetAddress.getByName("224.0.0.1"); - private final List<String> hosts = new CopyOnWriteArrayList<>(); - private TCPConnection tcpConnection; - private int port; - - public Network() throws UnknownHostException { - packetDispatcher = new PacketDispatcher(); - packetDispatcher.register(new DiscoveryPacketListener(this)); - packetDispatcher.register(new DiscoveryResponsePacketListener(this)); - - hostServer = new MulticastServer(mcastAddress, packetDispatcher); - clientServer = new UDPServer(packetDispatcher); - tcpServer = new TCPServer(packetDispatcher); - - logger.info("Network initialized"); - } - - public void startLobby(int port) throws ConnectionFailedException, SocketException, UnknownHostException { - this.port = port; - - hostServer.start(port); - tcpServer.start(port); - - logger.info("Lobby started on port: {}", port); - } - - public void stopLobby() { - port = 0; - - hostServer.stop(); - tcpServer.stop(); - - logger.info("Lobby stopped"); - } - - public void startDiscovery(int port) { - this.port = port; - - try { - clientServer.start(Main.TEST_DUNGEON_PORT_2); - - Packet packet = new DiscoveryPacket(InetAddress.getLocalHost().getHostAddress()); - UDPConnection udpConnection = new UDPConnection(mcastAddress, port); - udpConnection.send(packet); - - logger.info("Discovery started"); - } catch (UnknownHostException | SocketException | SendFailedException e) { - logger.error("Failed to start discovery", e); - } - } - - public void stopDiscovery() { - clientServer.stop(); - logger.info("Discovery stopped"); - } - - public void connectLobby(String address, int port) throws ConnectionFailedException { - try { - Socket socket = new Socket(address, port); - tcpConnection = new TCPConnection(socket); - ConnectionHandler connectionHandler = new ConnectionHandler(packetDispatcher); - - if (tcpConnection.isConnected()) { - connectionHandler.start(tcpConnection); - logger.info("Connected to lobby at {}:{}", address, port); - } - } catch (Exception e) { - logger.error("Couldn't connect to the specified lobby", e); - throw new ConnectionFailedException("Failed to connect to " + address + ":" + port, e); - } - } - - public void disconnectLobby() throws DisconnectFailedException { - port = 0; - - if (tcpConnection == null) { - logger.error("Error disconnecting from lobby"); - return; - } - - try { - Packet packet = new DisconnectPacket("test"); - tcpConnection.send(packet); - - if (tcpConnection.isConnected()) { - tcpConnection.close(); - logger.info("Disconnected from lobby"); - } - } catch (SendFailedException e) { - logger.error("Error disconnecting from lobby", e); - throw new DisconnectFailedException("Couldn't disconnect from the specified lobby", e); - } - } - - public boolean isConnected() { - return tcpConnection != null; - } - - public TCPServer getTcpServer() { - return tcpServer; - } - - public TCPConnection getTcpConnection() { - return tcpConnection; - } - - public PacketDispatcher getPacketDispatcher() { - return packetDispatcher; - } - - public int getPort() { - return port; - } - - public List<String> getHosts() { - return hosts; - } -} diff --git a/src/main/java/com/han/asd/network/connection/Connection.java b/src/main/java/com/han/asd/network/connection/Connection.java deleted file mode 100644 index c9a20da0f84e0d59789eaea782b33a3002d37678..0000000000000000000000000000000000000000 --- a/src/main/java/com/han/asd/network/connection/Connection.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.han.asd.network.connection; - -import com.han.asd.network.exceptions.PacketParsingException; -import com.han.asd.network.exceptions.ReceiveFailedException; -import com.han.asd.network.exceptions.SendFailedException; -import com.han.asd.network.packet.Packet; - -public interface Connection { - void send(Packet packet) throws SendFailedException; - - Packet receive() throws PacketParsingException, ReceiveFailedException; - - void close(); - - boolean isConnected(); -} diff --git a/src/main/java/com/han/asd/network/connection/ConnectionHandler.java b/src/main/java/com/han/asd/network/connection/ConnectionHandler.java deleted file mode 100644 index b472a2d17f45e1c931f26aa3e80c0c0cc3b3dd52..0000000000000000000000000000000000000000 --- a/src/main/java/com/han/asd/network/connection/ConnectionHandler.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.han.asd.network.connection; - -import com.han.asd.network.exceptions.DispatchPacketException; -import com.han.asd.network.exceptions.PacketParsingException; -import com.han.asd.network.exceptions.ReceiveFailedException; -import com.han.asd.network.packet.Packet; -import com.han.asd.network.packet.PacketDispatcher; -import com.han.asd.network.tick.TickManager; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -public class ConnectionHandler implements Runnable { - private static final Logger logger = LogManager.getLogger(ConnectionHandler.class); - private static final ExecutorService executor = Executors.newFixedThreadPool(2); - private final TickManager tickManager; - private Connection connection; - - public ConnectionHandler(TickManager tickManager) { - this.tickManager = tickManager; - } - - public void start(Connection connection) { - this.connection = connection; - executor.execute(this); - logger.info("Connection handler started"); - } - - @Override - public void run() { - try { - while (connection.isConnected()) { - Packet packet = connection.receive(); - if (packet == null) { - logger.warn("Received null packet, skipping packet dispatch"); - continue; - } - tickManager.getQueue().add(packet); - logger.info("Packet dispatched: {}", packet.getClass().getSimpleName()); - } - } catch (ReceiveFailedException e) { - logger.error("Server exception in connection handler", e); - throw new RuntimeException("ServerException occurred", e); - } catch (PacketParsingException e) { - logger.error("Invalid packet exception in connection handler", e); - throw new RuntimeException("InvalidPacketException occurred", e); - } finally { - if (!connection.isConnected()) { - logger.info("Connection closed"); - } - } - } - - public ExecutorService getExecutor() { - return executor; - } - - public Connection getConnection() { - return connection; - } - - public void close() { - if (connection.isConnected()) { - connection.close(); - } - - executor.shutdown(); - } -} diff --git a/src/main/java/com/han/asd/network/packet/PacketListener.java b/src/main/java/com/han/asd/network/packet/PacketListener.java deleted file mode 100644 index 7df35385110c878c76d6599f3d9a455d385d543c..0000000000000000000000000000000000000000 --- a/src/main/java/com/han/asd/network/packet/PacketListener.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.han.asd.network.packet; - -public interface PacketListener { -} \ No newline at end of file diff --git a/src/main/java/com/han/asd/network/packet/PacketPriority.java b/src/main/java/com/han/asd/network/packet/PacketPriority.java deleted file mode 100644 index 0ea77c8f02381385eb6a8fcdc513a62e5dacb911..0000000000000000000000000000000000000000 --- a/src/main/java/com/han/asd/network/packet/PacketPriority.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.han.asd.network.packet; - -public enum PacketPriority { - LOWEST(0), - LOW(1), - NORMAL(2), - HIGH(3), - HIGHEST(4), - MONITOR(5); - - private final int slot; - - PacketPriority(int slot) { - this.slot = slot; - } - - public int getSlot() { - return slot; - } -} \ No newline at end of file diff --git a/src/main/java/com/han/asd/network/server/BaseUDPServer.java b/src/main/java/com/han/asd/network/server/BaseUDPServer.java deleted file mode 100644 index d64bbdf88623633abcb5a0b21a2327ebc1eec870..0000000000000000000000000000000000000000 --- a/src/main/java/com/han/asd/network/server/BaseUDPServer.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.han.asd.network.server; - -import com.han.asd.network.connection.ConnectionHandler; -import com.han.asd.network.connection.UDPConnection; -import com.han.asd.network.packet.PacketDispatcher; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.net.DatagramSocket; -import java.net.SocketException; -import java.net.UnknownHostException; - -public abstract class BaseUDPServer implements Server { - private static final Logger logger = LogManager.getLogger(BaseUDPServer.class); - private final PacketDispatcher packetDispatcher; - private DatagramSocket datagramSocket; - private UDPConnection udpConnection; - private ConnectionHandler connectionHandler; - - public BaseUDPServer(PacketDispatcher packetDispatcher) { - this.packetDispatcher = packetDispatcher; - } - - public void start(int port, DatagramSocket datagramSocket) throws SocketException { - udpConnection = new UDPConnection(datagramSocket); - - connectionHandler = new ConnectionHandler(packetDispatcher); - connectionHandler.start(udpConnection); - - this.datagramSocket = datagramSocket; - datagramSocket.setBroadcast(true); - - logger.info("UDP server started on port: {}", port); - } - - @Override - public abstract void start(int port) throws UnknownHostException, SocketException; - - @Override - public void stop() { - if (connectionHandler != null) { - connectionHandler.close(); - } - - if (udpConnection != null && udpConnection.isConnected()) { - udpConnection.close(); - } - - if (datagramSocket != null && !datagramSocket.isClosed()) { - datagramSocket.close(); - } - - logger.info("UDP server stopped"); - } -} diff --git a/src/main/java/com/han/asd/network/server/Server.java b/src/main/java/com/han/asd/network/server/Server.java deleted file mode 100644 index 77c14563bbc9a04f24b6f572ee1a3ab5ed503356..0000000000000000000000000000000000000000 --- a/src/main/java/com/han/asd/network/server/Server.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.han.asd.network.server; - -import com.han.asd.network.exceptions.ConnectionFailedException; -import com.han.asd.network.exceptions.NetworkConfigurationException; - -import java.net.SocketException; -import java.net.UnknownHostException; - -public interface Server { - void start(int port) throws ConnectionFailedException, NetworkConfigurationException, UnknownHostException, SocketException; - - void stop(); -} diff --git a/src/main/java/com/han/asd/network/server/UDPServer.java b/src/main/java/com/han/asd/network/server/UDPServer.java deleted file mode 100644 index d7e0cd68d67271c66862395b97527d4e8bcc97d8..0000000000000000000000000000000000000000 --- a/src/main/java/com/han/asd/network/server/UDPServer.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.han.asd.network.server; - -import com.han.asd.network.packet.PacketDispatcher; - -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.SocketException; -import java.net.UnknownHostException; - -public class UDPServer extends BaseUDPServer { - - public UDPServer(PacketDispatcher packetDispatcher) { - super(packetDispatcher); - } - - @Override - public void start(int port) throws UnknownHostException, SocketException { - DatagramSocket datagramSocket = new DatagramSocket(port, InetAddress.getLocalHost()); - start(port, datagramSocket); - } -} diff --git a/src/main/java/com/han/asd/network/tick/Tick.java b/src/main/java/com/han/asd/network/tick/Tick.java deleted file mode 100644 index 7b02096654f851411d51459ad2cee2740c34d5bd..0000000000000000000000000000000000000000 --- a/src/main/java/com/han/asd/network/tick/Tick.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.han.asd.network.tick; - -public class Tick { - - private long timestamp; - - public Tick(long timestamp) { - this.timestamp = timestamp; - } - - public long getTimestamp() { - return timestamp; - } - - public void setTimestamp(long timestamp) { - this.timestamp = timestamp; - } -} diff --git a/src/main/java/com/han/asd/network/tick/TickManager.java b/src/main/java/com/han/asd/network/tick/TickManager.java deleted file mode 100644 index b78edb53e903789d4bf74d343615c460bfc1157a..0000000000000000000000000000000000000000 --- a/src/main/java/com/han/asd/network/tick/TickManager.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.han.asd.network.tick; - -import com.han.asd.network.exceptions.DispatchPacketException; -import com.han.asd.network.packet.Packet; -import com.han.asd.network.packet.PacketDispatcher; - -import java.util.LinkedList; -import java.util.Queue; - -public class TickManager implements Runnable { - private Tick tick; - private Queue<Packet> queue; - private PacketDispatcher packetDispatcher; - - public TickManager(Tick tick, PacketDispatcher packetDispatcher) { - this.tick = tick; - this.queue = new LinkedList<>(); - this.packetDispatcher = packetDispatcher; - } - - @Override - public void run() { - try { - Packet packet = queue.poll(); - packetDispatcher.dispatch(packet); - Thread.sleep(tick.getTimestamp()); - } catch (InterruptedException | DispatchPacketException e) { - throw new RuntimeException(e); - } - } - - public Queue<Packet> getQueue() { - return queue; - } -} diff --git a/src/main/java/com/han/asd/network/packets/ConnectionPacket.java b/src/main/java/com/han/asd/packets/ConnectionPacket.java similarity index 77% rename from src/main/java/com/han/asd/network/packets/ConnectionPacket.java rename to src/main/java/com/han/asd/packets/ConnectionPacket.java index 88af1d22f5f9448cb6954947cd327cedef3fdb95..dbe11dca06a9572109264c0e06d0add9209e447f 100644 --- a/src/main/java/com/han/asd/network/packets/ConnectionPacket.java +++ b/src/main/java/com/han/asd/packets/ConnectionPacket.java @@ -1,6 +1,6 @@ -package com.han.asd.network.packets; +package com.han.asd.packets; -import com.han.asd.network.packet.Packet; +import com.han.asd.api.network.packet.Packet; public class ConnectionPacket extends Packet { private final String sourceAddress; diff --git a/src/main/java/com/han/asd/network/packets/DisconnectPacket.java b/src/main/java/com/han/asd/packets/DisconnectPacket.java similarity index 77% rename from src/main/java/com/han/asd/network/packets/DisconnectPacket.java rename to src/main/java/com/han/asd/packets/DisconnectPacket.java index 6485e9d1a0caa23cc20d406410bea5dac9ff04c2..0f3692acdfc8da13b581e285a4ddfacef729c083 100644 --- a/src/main/java/com/han/asd/network/packets/DisconnectPacket.java +++ b/src/main/java/com/han/asd/packets/DisconnectPacket.java @@ -1,6 +1,6 @@ -package com.han.asd.network.packets; +package com.han.asd.packets; -import com.han.asd.network.packet.Packet; +import com.han.asd.api.network.packet.Packet; public class DisconnectPacket extends Packet { private final String sourceAddress;