diff --git a/src/main/java/it/polimi/ingsw/gc14/Model/MiniModel.java b/src/main/java/it/polimi/ingsw/gc14/Model/MiniModel.java index 9aef2c0..653c90d 100644 --- a/src/main/java/it/polimi/ingsw/gc14/Model/MiniModel.java +++ b/src/main/java/it/polimi/ingsw/gc14/Model/MiniModel.java @@ -1,37 +1,86 @@ package it.polimi.ingsw.gc14.Model; -import it.polimi.ingsw.gc14.Model.Cards.BuildingCard; -import it.polimi.ingsw.gc14.Model.Cards.TribeCard; -import it.polimi.ingsw.gc14.Model.Cards.TribeCards.Character; import it.polimi.ingsw.gc14.Model.GamePackage.Board; import it.polimi.ingsw.gc14.Model.GamePackage.CurrentState; - import java.io.Serializable; import java.util.*; +/** + * Lightweight representation of the game model shared with clients. + * + *
The mini model contains only the information required by the client-side
+ * view and controllers to render the current match state and process incoming
+ * network updates.
+ */
public class MiniModel implements Serializable {
+ /**
+ * Current game board.
+ */
public Board board;
+
+ /**
+ * Map associating each occupied slot with the corresponding player.
+ */
public Map Each player is indexed by username.
+ *
+ * @param players the list of players to store.
+ */
public void setPlayers(List The server also manages a dedicated heartbeat channel used
+ * to detect disconnected clients and associate each heartbeat
+ * connection with the corresponding {@link ClientHandler}.
*/
public class TCPServer {
+ /**
+ * Main TCP port used for standard client-server communication.
+ */
int port;
- int heartbeatPort; // ← nuova porta
+ /**
+ * TCP port dedicated to heartbeat communication.
+ */
+ int heartbeatPort;
+
+ /**
+ * Number of players that have successfully connected.
+ */
int connectedPlayers;
- ServerSocket socketTCP;
- ServerSocket heartbeatSocketTCP; // ← nuovo ServerSocket
+ /**
+ * Main server socket used to accept client connections.
+ */
+ ServerSocket socketTCP;
+
+ /**
+ * Server socket used to accept heartbeat connections.
+ */
+ ServerSocket heartbeatSocketTCP;
+
+ /**
+ * Game controller used to manage the server-side game logic.
+ */
final GameController controller;
+
+ /**
+ * Queue containing network events received from clients.
+ */
BlockingQueue The method opens both the main TCP server socket and the dedicated
+ * heartbeat server socket. It then starts a separate thread for heartbeat
+ * connections and continuously waits for new players or reconnecting clients.
+ *
+ * When a new connection is received, the first event must be an
+ * {@link AddPlayer} request. Depending on the current server state,
+ * the connection is handled either as a new player joining the game
+ * 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) {
this.serverCrashed = serverCrashed;
+
try {
socketTCP = new ServerSocket(port);
heartbeatSocketTCP = new ServerSocket(heartbeatPort);
@@ -68,16 +147,19 @@ public class TCPServer {
System.out.println("TCP server started on port: " + port);
System.out.println("Heartbeat server started on port: " + heartbeatPort);
- // Thread separato per accettare le connessioni heartbeat
new Thread(this::acceptHeartbeat, "heartbeat-acceptor").start();
- // Loop principale — invariato nella logica, cambia solo la creazione del ClientHandler
while (true) {
try {
Socket clientSocket = socketTCP.accept();
- ObjectOutputStream clientSend = new ObjectOutputStream(clientSocket.getOutputStream());
- ObjectInputStream clientReceive = new ObjectInputStream(clientSocket.getInputStream());
+
+ ObjectOutputStream clientSend =
+ new ObjectOutputStream(clientSocket.getOutputStream());
+ ObjectInputStream clientReceive =
+ new ObjectInputStream(clientSocket.getInputStream());
+
NetworkEvent event = (NetworkEvent) clientReceive.readObject();
+
if (!(event.getEventType() == EventType.ADD_PLAYER)) {
clientSocket.getOutputStream().write(-1);
clientSocket.close();
@@ -87,136 +169,195 @@ public class TCPServer {
AddPlayer eventAddPlayer = (AddPlayer) event;
- if (eventAddPlayer.getProposedNPlayer() < 2 || eventAddPlayer.getProposedNPlayer() > 5) {
+ if (eventAddPlayer.getProposedNPlayer() < 2
+ || eventAddPlayer.getProposedNPlayer() > 5) {
clientSocket.getOutputStream().write(-1);
clientSocket.close();
System.out.println("Invalid parameters. Connection terminated.");
continue;
}
- synchronized (controller) {
+ synchronized (controller) {
+ String username = eventAddPlayer.getUsername();
- 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);
+ if (serverCrashed) {
+ if (controller.getModel().getPlayers().stream()
+ .anyMatch(p -> p.getUserName().equals(username))
+ && !playerList.containsKey(username)) {
- ClientHandler handler = new ClientHandler(
- username, clientSocket, clientSend, clientReceive,
- clientHandlers, playerList, actionQueue
- );
- clientSocket.getOutputStream().write(1);
- pendingHeartbeat.put(username, handler);
- Game game = controller.getModel();
- handler.notifyMiniModel(new MiniModel(game.getBoard(),game.getSlotMap(), game.orderLogicCard,game.getCurrentState(),game.getPlayers(),game.getAvailableTotems()));
- 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());
- controller.setModel(model);
- playerList.setLimit(eventAddPlayer.getProposedNPlayer());
- }
- 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);
+ 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);
- Game game = controller.getModel();
- handler.notifyMiniModel(new MiniModel(game.getBoard(),game.getSlotMap(), game.orderLogicCard,game.getCurrentState(),game.getPlayers(),game.getAvailableTotems()));
- 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.");
- }
- }
+ 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();
- }
- catch(IOException | ClassNotFoundException e){
+ clientHandlers.add(handler);
+ connectedPlayers++;
+
+ } else if (playerList.containsKey(username)
+ && !playerList.get(username)) {
+
+ 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);
+
+ Game game = controller.getModel();
+ handler.notifyMiniModel(new MiniModel(
+ game.getBoard(),
+ game.getSlotMap(),
+ game.orderLogicCard,
+ game.getCurrentState(),
+ game.getPlayers(),
+ game.getAvailableTotems()
+ ));
+
+ 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());
+ controller.setModel(model);
+ playerList.setLimit(eventAddPlayer.getProposedNPlayer());
+ }
+
+ if (controller.addPlayer(username)) {
+ 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++;
+
+ } else if (playerList.containsKey(username)
+ && !playerList.get(username)) {
+
+ 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);
+
+ Game game = controller.getModel();
+ handler.notifyMiniModel(new MiniModel(
+ game.getBoard(),
+ game.getSlotMap(),
+ game.orderLogicCard,
+ game.getCurrentState(),
+ game.getPlayers(),
+ game.getAvailableTotems()
+ ));
+
+ 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.");
+ }
+ }
+ }
+
+ } catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
/**
- * Accetta connessioni sul socket heartbeat e le associa al ClientHandler giusto.
- * Il client manda subito il proprio username per identificarsi.
+ * Accepts heartbeat connections and associates them with the correct client handler.
+ *
+ * Each client immediately sends its username on the heartbeat channel.
+ * The method uses that username to retrieve the pending {@link ClientHandler}
+ * and starts a dedicated {@link HeartbeatHandler}. If no pending handler is found,
+ * the heartbeat socket is closed.
*/
private void acceptHeartbeat() {
while (true) {
try {
Socket hbSocket = heartbeatSocketTCP.accept();
- ObjectInputStream hbIn = new ObjectInputStream(hbSocket.getInputStream());
+ ObjectInputStream hbIn =
+ new ObjectInputStream(hbSocket.getInputStream());
- // il client manda subito il suo username
String username = (String) hbIn.readObject();
ClientHandler handler = pendingHeartbeat.remove(username);
+
if (handler != null) {
- HeartbeatHandler hb = new HeartbeatHandler(username, hbSocket, handler);
+ HeartbeatHandler hb =
+ new HeartbeatHandler(username, hbSocket, handler);
+
new Thread(hb, "heartbeat-" + username).start();
System.out.println("Heartbeat connected for: " + username);
} else {
- System.out.println("No pending handler for: " + username + ", closing heartbeat.");
+ System.out.println(
+ "No pending handler for: " + username + ", closing heartbeat."
+ );
hbSocket.close();
}
@@ -226,13 +367,27 @@ public class TCPServer {
}
}
+ /**
+ * Notifies connected TCP clients of a new network event.
+ *
+ * If the event does not represent an error, it is sent to all connected clients.
+ * If it represents an error, it is sent only to the client that requested the action.
+ *
+ * @param event the network event to send to the clients.
+ */
public void notifyAll(NetworkEvent event) {
clientHandlers.forEach(h -> {
- if (!event.getIsError() || event.getUsername().equals(h.getUsername()))
+ if (!event.getIsError() || event.getUsername().equals(h.getUsername())) {
h.notifyEvent(event);
+ }
});
}
+ /**
+ * Notifies all connected TCP clients of a new game model.
+ *
+ * @param model the updated mini model to send to the clients.
+ */
public void notifyAll(MiniModel model) {
clientHandlers.forEach(h -> h.notifyMiniModel(model));
}