Fixed: new game after server crash

This commit is contained in:
rubenpirreram
2026-05-26 17:49:47 +02:00
parent b900deadd8
commit bbeec46ca2
3 changed files with 76 additions and 131 deletions
@@ -47,16 +47,6 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
BlockingQueue<NetworkEvent> actionQueue; BlockingQueue<NetworkEvent> actionQueue;
private LimitedMap<String, Boolean> playerList; private LimitedMap<String, Boolean> playerList;
private boolean serverCrashed;
/**
* Sets whether the server is recovering from a previous crash.
*
* @param serverCrashed {@code true} if the server is in crash-recovery mode,
*/
public void setServerCrashed(boolean serverCrashed) {
this.serverCrashed = serverCrashed;
}
/** /**
* Constructs an RMI server with the required game and network components. * Constructs an RMI server with the required game and network components.
@@ -96,19 +86,8 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
if (preferredInt < 2 || preferredInt > 5) return false; if (preferredInt < 2 || preferredInt > 5) return false;
synchronized (controller) { synchronized (controller) {
if(serverCrashed)
{ if (playerList.isEmpty() && controller.getModel()==null) {
if(controller.getModel().getPlayers().stream().anyMatch(p -> p.getUserName().equals(username))&& !playerList.containsKey(username)) {
clients.put(username, callback);
playerList.put(username, true);
startWatchdog(username);
System.out.println("(After crash)Reconnected player: " + username);
return true;
}
}
else
{
if (playerList.isEmpty()) {
controller.setModel(new Game(preferredInt)); controller.setModel(new Game(preferredInt));
playerList.setLimit(preferredInt); playerList.setLimit(preferredInt);
System.out.println("Game Created With :"+preferredInt+" Players"); System.out.println("Game Created With :"+preferredInt+" Players");
@@ -120,6 +99,12 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
System.out.println("Accepted player: " + username); System.out.println("Accepted player: " + username);
return true; return true;
} }
if(controller.getModel().getPlayers().stream().anyMatch(p -> p.getUserName().equals(username))&& !playerList.containsKey(username)) {
clients.put(username, callback);
playerList.put(username, true);
startWatchdog(username);
System.out.println("(After crash)Reconnected player: " + username);
return true;
} }
if (playerList.containsKey(username) && !playerList.get(username)) { if (playerList.containsKey(username) && !playerList.get(username)) {
playerList.put(username, true); playerList.put(username, true);
@@ -279,13 +264,10 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
* on the specified port, and registers this server instance under the * on the specified port, and registers this server instance under the
* {@code RMIGameServer} name. * {@code RMIGameServer} name.
* *
* @param serverCrashed {@code true} if the server is being restarted after a crash,
* {@code false} otherwise.
* @return {@code true} if the server starts successfully, * @return {@code true} if the server starts successfully,
* {@code false} otherwise. * {@code false} otherwise.
*/ */
public boolean start(boolean serverCrashed) { public boolean start() {
this.serverCrashed = serverCrashed;
try { try {
System.setProperty("java.rmi.server.hostname", host); System.setProperty("java.rmi.server.hostname", host);
registry = LocateRegistry.createRegistry(nPort); registry = LocateRegistry.createRegistry(nPort);
@@ -73,20 +73,6 @@ public class TCPServer {
*/ */
List<ClientHandler> clientHandlers; List<ClientHandler> clientHandlers;
/**
* Flag indicating whether the server is recovering from a previous crash.
*/
boolean serverCrashed;
/**
* Sets whether the server is recovering from a previous crash.
*
* @param serverCrashed {@code true} if the server is in crash-recovery mode,
* {@code false} otherwise.
*/
public void setServerCrashed(boolean serverCrashed) {
this.serverCrashed = serverCrashed;
}
/** /**
* Temporary map associating each username with the corresponding * Temporary map associating each username with the corresponding
@@ -129,12 +115,8 @@ public class TCPServer {
* the connection is handled either as a new player joining the game * the connection is handled either as a new player joining the game
* or as a reconnection attempt. * or as a reconnection attempt.
* *
* @param serverCrashed {@code true} if the server is being restarted after a crash,
* {@code false} otherwise.
*/ */
public void start(boolean serverCrashed) { public void start() {
this.serverCrashed = serverCrashed;
try { try {
socketTCP = new ServerSocket(port); socketTCP = new ServerSocket(port);
heartbeatSocketTCP = new ServerSocket(heartbeatPort); heartbeatSocketTCP = new ServerSocket(heartbeatPort);
@@ -180,39 +162,7 @@ public class TCPServer {
synchronized (controller) { synchronized (controller) {
String username = eventAddPlayer.getUsername(); String username = eventAddPlayer.getUsername();
//reconnect players after a server crash //reconnect players after a server crash
if (serverCrashed) { if (playerList.isEmpty()&& controller.getModel()==null) {
if (controller.getModel().getPlayers().stream()
.anyMatch(p -> p.getUserName().equals(username))
&& !playerList.containsKey(username)) {
playerList.put(username, true);
System.out.println("(After crash)Reconnected player: " + username);
ClientHandler handler = new ClientHandler(
username,
clientSocket,
clientSend,
clientReceive,
clientHandlers,
playerList,
actionQueue
);
clientSocket.getOutputStream().write(1);
pendingHeartbeat.put(username, handler);
Thread thread = new Thread(handler);
thread.start();
clientHandlers.add(handler);
connectedPlayers++;
continue;
}
}
//manages a new player adding
else {
if (playerList.isEmpty()) {
Game model = new Game(eventAddPlayer.getProposedNPlayer()); Game model = new Game(eventAddPlayer.getProposedNPlayer());
controller.setModel(model); controller.setModel(model);
playerList.setLimit(eventAddPlayer.getProposedNPlayer()); playerList.setLimit(eventAddPlayer.getProposedNPlayer());
@@ -244,6 +194,32 @@ public class TCPServer {
continue; continue;
} }
if (controller.getModel().getPlayers().stream()
.anyMatch(p -> p.getUserName().equals(username))
&& !playerList.containsKey(username)) {
playerList.put(username, true);
System.out.println("(After crash)Reconnected player: " + username);
ClientHandler handler = new ClientHandler(
username,
clientSocket,
clientSend,
clientReceive,
clientHandlers,
playerList,
actionQueue
);
clientSocket.getOutputStream().write(1);
pendingHeartbeat.put(username, handler);
Thread thread = new Thread(handler);
thread.start();
clientHandlers.add(handler);
connectedPlayers++;
continue;
} }
//reconnect a previously disconnected player //reconnect a previously disconnected player
if (playerList.containsKey(username) if (playerList.containsKey(username)
@@ -288,8 +264,6 @@ public class TCPServer {
System.out.println("Player could not be added. Connection terminated."); System.out.println("Player could not be added. Connection terminated.");
} }
} }
} catch (IOException | ClassNotFoundException e) { } catch (IOException | ClassNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -186,8 +186,6 @@ public class ServerLauncher {
if(!entry.getValue()) if(!entry.getValue())
playerList.remove(entry.getKey()); playerList.remove(entry.getKey());
} }
serverRMI.setServerCrashed(false);
serverTCP.setServerCrashed(false);
} }
//notify the event //notify the event
else { else {
@@ -206,14 +204,16 @@ public class ServerLauncher {
// removes disconneted players when the game is ended // removes disconneted players when the game is ended
if(event.getEventType().equals(EventType.DISCONNECTED_PLAYER)) if(event.getEventType().equals(EventType.DISCONNECTED_PLAYER))
{ {
synchronized (gameController) {
playerList.remove(event.getUsername()); playerList.remove(event.getUsername());
if(playerList.isEmpty()) if (playerList.isEmpty()) {
{
gameController.setModel(null); gameController.setModel(null);
System.out.println("\n!!! Player list is now empty, ready for a new game init !!!\n"); System.out.println("\n!!! Player list is now empty, ready for a new game init !!!\n");
} }
return true; return true;
} }
}
return false; return false;
} }
@@ -245,7 +245,6 @@ public class ServerLauncher {
BlockingQueue<NetworkEvent> actionQueue = new LinkedBlockingQueue<>(); BlockingQueue<NetworkEvent> actionQueue = new LinkedBlockingQueue<>();
GameController gameController = new GameController(); GameController gameController = new GameController();
String IP; String IP;
boolean serverCrashed;
try { try {
IP=chooseNetworkInterface(new Scanner(System.in)); IP=chooseNetworkInterface(new Scanner(System.in));
System.out.println(IP); System.out.println(IP);
@@ -266,10 +265,6 @@ public class ServerLauncher {
playerList.put(entry.getKey().getUserName(),false); playerList.put(entry.getKey().getUserName(),false);
} }
} }
serverCrashed = true;
} else {
serverCrashed = false;
} }
playerList.setAction(()->{ playerList.setAction(()->{
new Thread(()->{ new Thread(()->{
@@ -281,8 +276,6 @@ public class ServerLauncher {
} }
serverRMI.notifyAll(miniModel); serverRMI.notifyAll(miniModel);
serverTCP.notifyAll(miniModel); serverTCP.notifyAll(miniModel);
view = new TUI(miniModel);
view.render();
}).start(); }).start();
}); });
new Thread(()-> { new Thread(()-> {
@@ -295,8 +288,8 @@ public class ServerLauncher {
} }
}).start(); }).start();
serverRMI.start(serverCrashed); serverRMI.start();
new Thread(()->{serverTCP.start(serverCrashed);}).start(); new Thread(()->{serverTCP.start();}).start();
System.out.println("Server RMI: "+System.getProperty("java.rmi.server.hostname")); System.out.println("Server RMI: "+System.getProperty("java.rmi.server.hostname"));
} }
@@ -314,10 +307,6 @@ public class ServerLauncher {
while (true) { while (true) {
try{ try{
this.doFirstEvent(); this.doFirstEvent();
// if(view!=null)
// {
// view.fullRender();
// }
} }
catch(InterruptedException e){ catch(InterruptedException e){
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();