From f9772732c4bcdd58d424f518efcfff9c438fe800 Mon Sep 17 00:00:00 2001 From: Hanno Fleischer Date: Wed, 11 Dec 2024 19:10:46 +0100 Subject: [PATCH] added better network support and disconnecting doesnt clos the client who hosts the server now --- .../src/main/java/pp/mdga/client/MdgaApp.java | 5 +- .../pp/mdga/client/dialog/NetworkDialog.java | 21 ++---- .../pp/mdga/client/server/MdgaServer.java | 74 +++++++++++++------ .../java/pp/mdga/client/CeremonyState.java | 9 +++ .../java/pp/mdga/client/ClientGameLogic.java | 4 +- .../main/java/pp/mdga/client/ClientState.java | 7 +- .../mdga/client/dialogstate/LobbyState.java | 6 +- .../mdga/server/automaton/CeremonyState.java | 2 + .../pp/mdga/server/automaton/ServerState.java | 2 +- 9 files changed, 83 insertions(+), 47 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java index 1e1570c6..586c9049 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java @@ -339,7 +339,10 @@ public void afterGameCleanup() { MainView main = (MainView) mainView; main.getJoinDialog().disconnect(); - main.getHostDialog().shutdownServer(); + System.out.println("Disconnecting from server..." + clientGameLogic.isHost()); + if (clientGameLogic.isHost()) { + main.getHostDialog().shutdownServer(); + } ceremonyView.afterGameCleanup(); } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/NetworkDialog.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/NetworkDialog.java index 6597435d..a068107c 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/NetworkDialog.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/NetworkDialog.java @@ -98,27 +98,16 @@ protected void startServer() { */ public void shutdownServer() { + serverInstance.shutdown(); + + // Wait for the server to shut down try { - Thread.sleep(1000); + serverThread.join(); // Wait for the server thread to finish } catch (InterruptedException e) { Thread.currentThread().interrupt(); - System.err.println("Thread was interrupted: " + e.getMessage()); } - if (serverInstance != null) { - serverInstance.shutdown(); - serverInstance = null; - } - - if (serverThread != null && serverThread.isAlive()) { - serverThread.interrupt(); - try { - serverThread.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - serverThread = null; - } + System.out.println("Server shutdown successfully."); } /** diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java index 9c1ac177..6ce8fd7b 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java @@ -15,6 +15,9 @@ import java.io.IOException; import java.lang.System.Logger; import java.lang.System.Logger.Level; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Map; import java.util.UUID; import java.util.concurrent.BlockingQueue; @@ -31,6 +34,7 @@ public class MdgaServer implements MessageListener, Connection private static int port; private final ServerGameLogic logic; private final BlockingQueue pendingMessages = new LinkedBlockingQueue<>(); + private volatile boolean running = true; static { // Configure logging @@ -59,17 +63,19 @@ public MdgaServer(int port) { */ public void run() { startServer(); - while (true) + while (running) { processNextMessage(); + } + shutdownServerResources(); } /** - * + * Starts the server and initializes listeners. */ private void startServer() { try { - LOGGER.log(Level.INFO, "Starting server..."); //NON-NLS - + LOGGER.log(Level.INFO, "Starting server..."); + unlockSerializers();//NON-NLS myServer = Network.createServer(port); initializeSerializables(); myServer.start(); @@ -77,20 +83,22 @@ private void startServer() { LOGGER.log(Level.INFO, "Server started: {0}", myServer.isRunning()); //NON-NLS } catch (IOException e) { LOGGER.log(Level.ERROR, "Couldn't start server: {0}", e.getMessage()); //NON-NLS - exit(1); + exit(); } } private void processNextMessage() { try { - pendingMessages.take().process(logic); + ReceivedMessage message = pendingMessages.take(); // This is a blocking call + message.process(logic); } catch (InterruptedException ex) { - LOGGER.log(Level.INFO, "Interrupted while waiting for messages"); //NON-NLS + LOGGER.log(Level.INFO, "Server thread interrupted, shutting down..."); //NON-NLS Thread.currentThread().interrupt(); } } private void initializeSerializables() { + Serializer.registerClass(UUID.class, new UUIDSerializer()); Serializer.registerClass(AnimationEndMessage.class); Serializer.registerClass(ClientStartGameMessage.class); @@ -262,12 +270,12 @@ public void handleDisconnect(int id) { this.logic.received(new DisconnectedMessage(), id); } - public void exit(int exitValue) { //NON-NLS - LOGGER.log(Level.INFO, "close request"); //NON-NLS - if (myServer != null) - for (HostedConnection client : myServer.getConnections()) //NON-NLS - if (client != null) client.close("Game over"); //NON-NLS - System.exit(exitValue); + /** + * Stops the server thread gracefully. + */ + public void exit() { + LOGGER.log(Level.INFO, "Requesting server shutdown"); //NON-NLS + running = false; } /** @@ -309,7 +317,11 @@ public void broadcast(ServerMessage message) { */ @Override public void disconnectClient(int id) { - this.myServer.getConnection(id).close(""); + if (myServer.getConnection(id) != null) { + this.myServer.getConnection(id).close(""); + } else { + LOGGER.log(Level.ERROR, "no connection with id={0}", id); //NON-NLS + } } /** @@ -319,13 +331,33 @@ public void disconnectClient(int id) { */ @Override public void shutdown() { - for (HostedConnection client : this.myServer.getConnections()) { - if (client != null) { - client.close("Host closed the server."); - } - } + this.exit(); + } - this.myServer.close(); - this.exit(0); + /** + * Gracefully shutdown server resources like connections and sockets. + */ + private void shutdownServerResources() { + LOGGER.log(Level.INFO, "Shutting down server resources"); //NON-NLS + if (myServer != null && myServer.isRunning()) { + for (HostedConnection client : myServer.getConnections()) { + if (client != null) client.close("Server shutting down."); + } + myServer.close(); + } + } + + /** + * This method will be used to unlock the Serializer registry. + */ + private static void unlockSerializers() { + try { + Field lockField = Serializer.class.getDeclaredField("locked"); + lockField.setAccessible(true); + lockField.setBoolean(null, false); // Unlock the Serializer registry + } catch (NoSuchFieldException | IllegalAccessException e) { + System.err.println("Failed to unlock the Serializer registry: " + e.getMessage()); + e.printStackTrace(); + } } } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java index f8060676..e24158b6 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java @@ -1,8 +1,12 @@ package pp.mdga.client; +import pp.mdga.Resources; import pp.mdga.client.ceremonystate.CeremonyStates; import pp.mdga.client.ceremonystate.PodiumState; import pp.mdga.client.ceremonystate.StatisticsState; +import pp.mdga.message.server.ShutdownMessage; +import pp.mdga.notification.InfoNotification; +import pp.mdga.notification.StartDialogNotification; public class CeremonyState extends ClientState { @@ -77,6 +81,11 @@ public CeremonyStates getState() { return currentState; } + @Override + public void received(ShutdownMessage msg){ + logic.addNotification(new InfoNotification(Resources.stringLookup("server.shutdown"))); + } + /** * this method is used to parse the selectNext from the clientGameLogic */ diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java index f32d8603..10e23896 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java @@ -401,9 +401,7 @@ public void received(ServerStartGameMessage msg) { */ @Override public void received(ShutdownMessage msg) { - addNotification(new InfoNotification(Resources.stringLookup("server.shutdown"))); - addNotification(new StartDialogNotification()); - setState(dialogsState); + state.received(msg); } /** diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java index bfea68f1..6546c7cc 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java @@ -1,9 +1,12 @@ package pp.mdga.client; +import pp.mdga.Resources; import pp.mdga.game.BonusCard; import pp.mdga.game.Color; import pp.mdga.game.Piece; import pp.mdga.message.server.*; +import pp.mdga.notification.InfoNotification; +import pp.mdga.notification.StartDialogNotification; import java.lang.System.Logger.Level; @@ -161,7 +164,9 @@ public void received(ServerStartGameMessage msg) { @Override public void received(ShutdownMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); + logic.addNotification(new InfoNotification(Resources.stringLookup("server.shutdown"))); + logic.addNotification(new StartDialogNotification()); + logic.setState(logic.getDialogs()); } @Override diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogstate/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogstate/LobbyState.java index a7c07a31..360ebba0 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogstate/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogstate/LobbyState.java @@ -141,11 +141,9 @@ public void received(LobbyPlayerJoinedMessage msg) { System.out.println(msg.getId()); logic.setOwnPlayerId(msg.getId()); } - if (msg.isHost() && msg.getId() == logic.getOwnPlayerId()) { - logic.setHost(true); + if (msg.getPlayer().getColor() != Color.NONE){ + logic.addNotification(new TskSelectNotification(msg.getPlayer().getColor(), msg.getPlayer().getName(), msg.getPlayer().getName().equals(logic.getOwnPlayerName()))); } - - logic.addNotification(new TskSelectNotification(msg.getPlayer().getColor(), msg.getPlayer().getName(), msg.getPlayer().getName().equals(logic.getOwnPlayerName()))); logic.getGame().getPlayers().put(msg.getId(), msg.getPlayer()); } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/CeremonyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/CeremonyState.java index 3f444ad1..b68c0823 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/CeremonyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/CeremonyState.java @@ -2,6 +2,7 @@ import pp.mdga.game.Color; import pp.mdga.message.server.CeremonyMessage; +import pp.mdga.message.server.ShutdownMessage; import pp.mdga.server.ServerGameLogic; /** @@ -59,6 +60,7 @@ private CeremonyMessage createCeremonyMessage() { public void enter() { LOGGER.log(System.Logger.Level.DEBUG, "Entered CeremonyState state."); logic.getServerSender().broadcast(createCeremonyMessage()); + logic.getServerSender().shutdown(); } /** diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/ServerState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/ServerState.java index 46879df1..517fc84b 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/ServerState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/ServerState.java @@ -86,9 +86,9 @@ public void received(LeaveGameMessage msg, int from) { this.logic.getServerSender().broadcast(new ShutdownMessage()); this.logic.getServerSender().shutdown(); } + this.logic.getServerSender().disconnectClient(from); this.logic.getGame().removePlayer(from); this.logic.getServerSender().broadcast(new LobbyPlayerLeaveMessage(from)); - this.logic.getServerSender().disconnectClient(from); } /**