Fix: Server Persistence

This commit is contained in:
rubenpirreram
2026-05-10 20:09:19 +02:00
parent 6491eb3a85
commit 8106adb0d0
3 changed files with 143 additions and 78 deletions
@@ -42,6 +42,7 @@ 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;
public RMIServer(GameController controller, int nPort, public RMIServer(GameController controller, int nPort,
BlockingQueue<NetworkEvent> actionQueue, BlockingQueue<NetworkEvent> actionQueue,
@@ -71,28 +72,51 @@ 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 (playerList.isEmpty()) { if(serverCrashed)
model = new Game(preferredInt); {
controller.setModel(model); if(controller.getModel().getPlayers().stream().anyMatch(p -> p.getUserName().equals(username))&& !playerList.containsKey(username)) {
playerList.setLimit(preferredInt); 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)) {
playerList.put(username, true);
clients.put(username, callback);
System.out.println("Reconnected player: " + username);
startWatchdog(username);
callback.onGameInit(model);
System.out.println("Model sent: " + username);
actionQueue.add(new ReconnectPlayer(username));
return true;
}
} }
if (controller.addPlayer(username)) { else
clients.put(username, callback); {
playerList.put(username, true); if (playerList.isEmpty()) {
startWatchdog(username); model = new Game(preferredInt);
System.out.println("Accepted player: " + username); controller.setModel(model);
return true; playerList.setLimit(preferredInt);
} }
// Reconnection: player was offline if (controller.addPlayer(username)) {
if (playerList.containsKey(username) && !playerList.get(username)) { clients.put(username, callback);
playerList.put(username, true); playerList.put(username, true);
clients.put(username, callback); startWatchdog(username);
System.out.println("Reconnected player: " + username); System.out.println("Accepted player: " + username);
startWatchdog(username); return true;
callback.onGameInit(model); }
System.out.println("Model sent: " + username); // Reconnection: player was offline
actionQueue.add(new ReconnectPlayer(username)); if (playerList.containsKey(username) && !playerList.get(username)) {
return true; playerList.put(username, true);
clients.put(username, callback);
System.out.println("Reconnected player: " + username);
startWatchdog(username);
callback.onGameInit(model);
System.out.println("Model sent: " + username);
actionQueue.add(new ReconnectPlayer(username));
return true;
}
} }
return false; return false;
} }
@@ -263,7 +287,8 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
* Starts the RMI server. * Starts the RMI server.
* @return true if the server starts successfully, false otherwise * @return true if the server starts successfully, false otherwise
*/ */
public boolean start() { public boolean start(boolean serverCrashed) {
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);
@@ -35,6 +35,7 @@ public class TCPServer {
LimitedMap<String, Boolean> playerList; LimitedMap<String, Boolean> playerList;
List<ClientHandler> clientHandlers; List<ClientHandler> clientHandlers;
boolean serverCrashed;
// Mappa temporanea: username → ClientHandler // Mappa temporanea: username → ClientHandler
// Serve per associare il socket heartbeat al giusto ClientHandler // Serve per associare il socket heartbeat al giusto ClientHandler
private final Map<String, ClientHandler> pendingHeartbeat = new ConcurrentHashMap<>(); private final Map<String, ClientHandler> pendingHeartbeat = new ConcurrentHashMap<>();
@@ -50,7 +51,8 @@ public class TCPServer {
this.clientHandlers = new ArrayList<>(); this.clientHandlers = new ArrayList<>();
} }
public void start() { public void start(boolean serverCrashed) {
this.serverCrashed = serverCrashed;
try { try {
socketTCP = new ServerSocket(port); socketTCP = new ServerSocket(port);
heartbeatSocketTCP = new ServerSocket(heartbeatPort); heartbeatSocketTCP = new ServerSocket(heartbeatPort);
@@ -90,64 +92,96 @@ public class TCPServer {
} }
synchronized (controller) { synchronized (controller) {
if (playerList.isEmpty()) {
if(controller.getModel() == null){ String username = eventAddPlayer.getUsername();
if(serverCrashed){
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++;
}
else if(playerList.containsKey(username) && !playerList.get(username)){
// riconnessione
playerList.put(username, true);
System.out.println("Reconnected player: " + username);
ClientHandler handler = new ClientHandler(
username, clientSocket, clientSend, clientReceive,
clientHandlers, playerList, actionQueue
);
clientSocket.getOutputStream().write(1);
pendingHeartbeat.put(username, handler);
handler.notifyModel(controller.getModel());
Thread thread = new Thread(handler);
thread.start();
clientHandlers.add(handler);
connectedPlayers++;
actionQueue.add(new ReconnectPlayer(username));
}
else
{
clientSocket.getOutputStream().write(-1);
clientSocket.close();
System.out.println("Player could not be added. Connection terminated.");
}
}
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());
} }
else if (controller.addPlayer(username)) {
{ // nuovo giocatore
for(Player p:controller.getModel().getPlayers()) { playerList.put(username, true);
playerList.put(p.getUserName(),false); System.out.println("Accepted 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++;
// metti in attesa del socket heartbeat
}
else if(playerList.containsKey(username) && !playerList.get(username)){
// riconnessione
playerList.put(username, true);
System.out.println("Reconnected player: " + username);
ClientHandler handler = new ClientHandler(
username, clientSocket, clientSend, clientReceive,
clientHandlers, playerList, actionQueue
);
clientSocket.getOutputStream().write(1);
pendingHeartbeat.put(username, handler);
handler.notifyModel(controller.getModel());
Thread thread = new Thread(handler);
thread.start();
clientHandlers.add(handler);
connectedPlayers++;
actionQueue.add(new ReconnectPlayer(username));
}
else{
clientSocket.getOutputStream().write(-1);
clientSocket.close();
System.out.println("Player could not be added. Connection terminated.");
} }
} }
String username = eventAddPlayer.getUsername();
if (controller.addPlayer(username)) {
// nuovo giocatore
playerList.put(username, true);
System.out.println("Accepted 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++;
// metti in attesa del socket heartbeat
}
else if(playerList.containsKey(username) && !playerList.get(username)){
// riconnessione
playerList.put(username, true);
System.out.println("Reconnected player: " + username);
ClientHandler handler = new ClientHandler(
username, clientSocket, clientSend, clientReceive,
clientHandlers, playerList, actionQueue
);
clientSocket.getOutputStream().write(1);
pendingHeartbeat.put(username, handler);
handler.notifyModel(controller.getModel());
Thread thread = new Thread(handler);
thread.start();
clientHandlers.add(handler);
connectedPlayers++;
actionQueue.add(new ReconnectPlayer(username));
}
else{
clientSocket.getOutputStream().write(-1);
clientSocket.close();
System.out.println("Player could not be added. Connection terminated.");
}
} }
@@ -4,6 +4,7 @@ package it.polimi.ingsw.gc14;
import it.polimi.ingsw.gc14.Controller.GameController; import it.polimi.ingsw.gc14.Controller.GameController;
import it.polimi.ingsw.gc14.Model.Game; import it.polimi.ingsw.gc14.Model.Game;
import it.polimi.ingsw.gc14.Model.GamePackage.GameStages; import it.polimi.ingsw.gc14.Model.GamePackage.GameStages;
import it.polimi.ingsw.gc14.Model.Player;
import it.polimi.ingsw.gc14.Network.NetworkEvent; import it.polimi.ingsw.gc14.Network.NetworkEvent;
import it.polimi.ingsw.gc14.Network.NetworkEvents.DisconnectedPlayer; import it.polimi.ingsw.gc14.Network.NetworkEvents.DisconnectedPlayer;
import it.polimi.ingsw.gc14.Network.RMI.Server.RMIServer; import it.polimi.ingsw.gc14.Network.RMI.Server.RMIServer;
@@ -126,7 +127,7 @@ 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));
} catch (Exception e) { } catch (Exception e) {
@@ -136,7 +137,12 @@ public class ServerLauncher {
RMIServer serverRMI = new RMIServer(gameController, 1099, actionQueue, playerList,IP); RMIServer serverRMI = new RMIServer(gameController, 1099, actionQueue, playerList,IP);
TCPServer serverTCP = new TCPServer(gameController, 8080, 8081,actionQueue, playerList); TCPServer serverTCP = new TCPServer(gameController, 8080, 8081,actionQueue, playerList);
ServerLauncher launcher = new ServerLauncher(actionQueue, gameController, serverRMI, serverTCP); ServerLauncher launcher = new ServerLauncher(actionQueue, gameController, serverRMI, serverTCP);
if(gameController.getModel() != null){
playerList.setLimit(gameController.getModel().getNPlayers());
serverCrashed = true;
} else {
serverCrashed = false;
}
playerList.setAction(()->{ playerList.setAction(()->{
new Thread(()->{ new Thread(()->{
try { try {
@@ -149,8 +155,8 @@ public class ServerLauncher {
}).start(); }).start();
}); });
serverRMI.start(); serverRMI.start(serverCrashed);
new Thread(()->{serverTCP.start();}).start(); new Thread(()->{serverTCP.start(serverCrashed);}).start();
} }