Add: complete JavaDoc for MiniModel and TCPServer
This commit is contained in:
@@ -1,37 +1,86 @@
|
|||||||
package it.polimi.ingsw.gc14.Model;
|
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.Board;
|
||||||
import it.polimi.ingsw.gc14.Model.GamePackage.CurrentState;
|
import it.polimi.ingsw.gc14.Model.GamePackage.CurrentState;
|
||||||
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lightweight representation of the game model shared with clients.
|
||||||
|
*
|
||||||
|
* <p>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 {
|
public class MiniModel implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current game board.
|
||||||
|
*/
|
||||||
public Board board;
|
public Board board;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map associating each occupied slot with the corresponding player.
|
||||||
|
*/
|
||||||
public Map<Slot, Player> slotPlayerMap;
|
public Map<Slot, Player> slotPlayerMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Card used to manage the player turn order.
|
||||||
|
*/
|
||||||
public OrderLogicCard orderLogicCard;
|
public OrderLogicCard orderLogicCard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current state of the game.
|
||||||
|
*/
|
||||||
public CurrentState currentState;
|
public CurrentState currentState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of players indexed by username.
|
||||||
|
*/
|
||||||
public Map<String, Player> players;
|
public Map<String, Player> players;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of totems still available for selection.
|
||||||
|
*/
|
||||||
public List<Totems> availableTotems;
|
public List<Totems> availableTotems;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Final standing of the players at the end of the game.
|
||||||
|
*/
|
||||||
public List<Player> standingPlayers;
|
public List<Player> standingPlayers;
|
||||||
|
|
||||||
// --- Costruttori ---
|
/**
|
||||||
|
* Constructs an empty mini model.
|
||||||
|
*/
|
||||||
public MiniModel() {
|
public MiniModel() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a mini model containing only the game board.
|
||||||
|
*
|
||||||
|
* @param board the current game board.
|
||||||
|
*/
|
||||||
public MiniModel(Board board) {
|
public MiniModel(Board board) {
|
||||||
this.board = board;
|
this.board = board;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MiniModel(Board board, Map<Slot, Player> slotPlayerMap, OrderLogicCard orderLogicCard,
|
/**
|
||||||
CurrentState currentState, List<Player> players, List<Totems> availableTotems) {
|
* Constructs a complete mini model from the current server-side game data.
|
||||||
|
*
|
||||||
|
* @param board the current game board.
|
||||||
|
* @param slotPlayerMap the map associating occupied slots with players.
|
||||||
|
* @param orderLogicCard the card managing player turn order.
|
||||||
|
* @param currentState the current state of the game.
|
||||||
|
* @param players the list of players in the match.
|
||||||
|
* @param availableTotems the list of totems still available for selection.
|
||||||
|
*/
|
||||||
|
public MiniModel(Board board,
|
||||||
|
Map<Slot, Player> slotPlayerMap,
|
||||||
|
OrderLogicCard orderLogicCard,
|
||||||
|
CurrentState currentState,
|
||||||
|
List<Player> players,
|
||||||
|
List<Totems> availableTotems) {
|
||||||
this.board = board;
|
this.board = board;
|
||||||
this.slotPlayerMap = slotPlayerMap;
|
this.slotPlayerMap = slotPlayerMap;
|
||||||
this.orderLogicCard = orderLogicCard;
|
this.orderLogicCard = orderLogicCard;
|
||||||
@@ -42,43 +91,90 @@ public class MiniModel implements Serializable {
|
|||||||
setPlayers(players);
|
setPlayers(players);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
// --- Setter ---
|
* Sets the current game board.
|
||||||
|
*
|
||||||
|
* @param board the board to assign.
|
||||||
|
*/
|
||||||
public void setBoard(Board board) {
|
public void setBoard(Board board) {
|
||||||
this.board = board;
|
this.board = board;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list of totems still available for selection.
|
||||||
|
*
|
||||||
|
* @param availableTotems the available totems.
|
||||||
|
*/
|
||||||
public void setAvailableTotems(List<Totems> availableTotems) {
|
public void setAvailableTotems(List<Totems> availableTotems) {
|
||||||
this.availableTotems = availableTotems;
|
this.availableTotems = availableTotems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the map associating occupied slots with players.
|
||||||
|
*
|
||||||
|
* @param slotPlayerMap the slot-player map to assign.
|
||||||
|
*/
|
||||||
public void setSlotPlayerMap(Map<Slot, Player> slotPlayerMap) {
|
public void setSlotPlayerMap(Map<Slot, Player> slotPlayerMap) {
|
||||||
this.slotPlayerMap = slotPlayerMap;
|
this.slotPlayerMap = slotPlayerMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the card used to manage the player turn order.
|
||||||
|
*
|
||||||
|
* @param orderLogicCard the order logic card to assign.
|
||||||
|
*/
|
||||||
public void setOrderLogicCard(OrderLogicCard orderLogicCard) {
|
public void setOrderLogicCard(OrderLogicCard orderLogicCard) {
|
||||||
this.orderLogicCard = orderLogicCard;
|
this.orderLogicCard = orderLogicCard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current state of the game.
|
||||||
|
*
|
||||||
|
* @param currentState the current game state.
|
||||||
|
*/
|
||||||
public void setCurrentState(CurrentState currentState) {
|
public void setCurrentState(CurrentState currentState) {
|
||||||
this.currentState = currentState;
|
this.currentState = currentState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces or updates the players stored in the mini model.
|
||||||
|
*
|
||||||
|
* <p>Each player is indexed by username.
|
||||||
|
*
|
||||||
|
* @param players the list of players to store.
|
||||||
|
*/
|
||||||
public void setPlayers(List<Player> players) {
|
public void setPlayers(List<Player> players) {
|
||||||
for (Player player : players) {
|
for (Player player : players) {
|
||||||
this.players.put(player.getUserName(), player);
|
this.players.put(player.getUserName(), player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds or updates a single player in the mini model.
|
||||||
|
*
|
||||||
|
* @param player the player to store.
|
||||||
|
*/
|
||||||
public void setPlayer(Player player) {
|
public void setPlayer(Player player) {
|
||||||
players.put(player.getUserName(), player);
|
players.put(player.getUserName(), player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the final standing of the players.
|
||||||
|
*
|
||||||
|
* @param standingPlayers the final ordered list of players.
|
||||||
|
*/
|
||||||
public void setStandingPlayers(ArrayList<Player> standingPlayers) {
|
public void setStandingPlayers(ArrayList<Player> standingPlayers) {
|
||||||
this.standingPlayers = standingPlayers;
|
this.standingPlayers = standingPlayers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the position of the player with the specified username
|
||||||
|
* within the player collection.
|
||||||
|
*
|
||||||
|
* @param username the username of the player to search for.
|
||||||
|
* @return the zero-based position of the player, or {@code -1}
|
||||||
|
* if no matching username is found.
|
||||||
|
*/
|
||||||
public int getPositionByUsername(String username) {
|
public int getPositionByUsername(String username) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Player player : players.values()) {
|
for (Player player : players.values()) {
|
||||||
|
|||||||
@@ -18,33 +18,96 @@ import java.util.concurrent.BlockingQueue;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server TCP. Accepts connections and manages all client handlers.
|
* TCP server responsible for accepting client connections,
|
||||||
|
* handling player registration and reconnection, and sending
|
||||||
|
* game updates to connected clients.
|
||||||
|
*
|
||||||
|
* <p>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 {
|
public class TCPServer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main TCP port used for standard client-server communication.
|
||||||
|
*/
|
||||||
int port;
|
int port;
|
||||||
int heartbeatPort; // ← nuova porta
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TCP port dedicated to heartbeat communication.
|
||||||
|
*/
|
||||||
|
int heartbeatPort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of players that have successfully connected.
|
||||||
|
*/
|
||||||
int connectedPlayers;
|
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;
|
final GameController controller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue containing network events received from clients.
|
||||||
|
*/
|
||||||
BlockingQueue<NetworkEvent> actionQueue;
|
BlockingQueue<NetworkEvent> actionQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map storing the online/offline status of connected players.
|
||||||
|
*/
|
||||||
LimitedMap<String, Boolean> playerList;
|
LimitedMap<String, Boolean> playerList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of active TCP client handlers.
|
||||||
|
*/
|
||||||
List<ClientHandler> clientHandlers;
|
List<ClientHandler> clientHandlers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag indicating whether the server is recovering from a previous crash.
|
||||||
|
*/
|
||||||
boolean serverCrashed;
|
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) {
|
public void setServerCrashed(boolean serverCrashed) {
|
||||||
this.serverCrashed = serverCrashed;
|
this.serverCrashed = serverCrashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mappa temporanea: username → ClientHandler
|
/**
|
||||||
// Serve per associare il socket heartbeat al giusto ClientHandler
|
* Temporary map associating each username with the corresponding
|
||||||
|
* {@link ClientHandler} waiting for its heartbeat connection.
|
||||||
|
*/
|
||||||
private final Map<String, ClientHandler> pendingHeartbeat = new ConcurrentHashMap<>();
|
private final Map<String, ClientHandler> pendingHeartbeat = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public TCPServer(GameController controller, int port, int heartbeatPort,
|
/**
|
||||||
BlockingQueue<NetworkEvent> actionQueue, LimitedMap<String, Boolean> playerList) {
|
* Constructs a TCP server with the required game and network components.
|
||||||
|
*
|
||||||
|
* @param controller the game controller used to manage the game logic.
|
||||||
|
* @param port the main TCP port used for client communication.
|
||||||
|
* @param heartbeatPort the TCP port dedicated to heartbeat connections.
|
||||||
|
* @param actionQueue the queue containing incoming network events.
|
||||||
|
* @param playerList the map storing the connection status of the players.
|
||||||
|
*/
|
||||||
|
public TCPServer(GameController controller,
|
||||||
|
int port,
|
||||||
|
int heartbeatPort,
|
||||||
|
BlockingQueue<NetworkEvent> actionQueue,
|
||||||
|
LimitedMap<String, Boolean> playerList) {
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.heartbeatPort = heartbeatPort;
|
this.heartbeatPort = heartbeatPort;
|
||||||
this.connectedPlayers = 0;
|
this.connectedPlayers = 0;
|
||||||
@@ -54,8 +117,24 @@ public class TCPServer {
|
|||||||
this.clientHandlers = new ArrayList<>();
|
this.clientHandlers = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the TCP server and begins accepting client connections.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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) {
|
public void start(boolean serverCrashed) {
|
||||||
this.serverCrashed = serverCrashed;
|
this.serverCrashed = serverCrashed;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
socketTCP = new ServerSocket(port);
|
socketTCP = new ServerSocket(port);
|
||||||
heartbeatSocketTCP = new ServerSocket(heartbeatPort);
|
heartbeatSocketTCP = new ServerSocket(heartbeatPort);
|
||||||
@@ -68,16 +147,19 @@ public class TCPServer {
|
|||||||
System.out.println("TCP server started on port: " + port);
|
System.out.println("TCP server started on port: " + port);
|
||||||
System.out.println("Heartbeat server started on port: " + heartbeatPort);
|
System.out.println("Heartbeat server started on port: " + heartbeatPort);
|
||||||
|
|
||||||
// Thread separato per accettare le connessioni heartbeat
|
|
||||||
new Thread(this::acceptHeartbeat, "heartbeat-acceptor").start();
|
new Thread(this::acceptHeartbeat, "heartbeat-acceptor").start();
|
||||||
|
|
||||||
// Loop principale — invariato nella logica, cambia solo la creazione del ClientHandler
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
Socket clientSocket = socketTCP.accept();
|
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();
|
NetworkEvent event = (NetworkEvent) clientReceive.readObject();
|
||||||
|
|
||||||
if (!(event.getEventType() == EventType.ADD_PLAYER)) {
|
if (!(event.getEventType() == EventType.ADD_PLAYER)) {
|
||||||
clientSocket.getOutputStream().write(-1);
|
clientSocket.getOutputStream().write(-1);
|
||||||
clientSocket.close();
|
clientSocket.close();
|
||||||
@@ -87,136 +169,195 @@ public class TCPServer {
|
|||||||
|
|
||||||
AddPlayer eventAddPlayer = (AddPlayer) event;
|
AddPlayer eventAddPlayer = (AddPlayer) event;
|
||||||
|
|
||||||
if (eventAddPlayer.getProposedNPlayer() < 2 || eventAddPlayer.getProposedNPlayer() > 5) {
|
if (eventAddPlayer.getProposedNPlayer() < 2
|
||||||
|
|| eventAddPlayer.getProposedNPlayer() > 5) {
|
||||||
clientSocket.getOutputStream().write(-1);
|
clientSocket.getOutputStream().write(-1);
|
||||||
clientSocket.close();
|
clientSocket.close();
|
||||||
System.out.println("Invalid parameters. Connection terminated.");
|
System.out.println("Invalid parameters. Connection terminated.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (controller) {
|
synchronized (controller) {
|
||||||
|
String username = eventAddPlayer.getUsername();
|
||||||
|
|
||||||
String username = eventAddPlayer.getUsername();
|
if (serverCrashed) {
|
||||||
if(serverCrashed){
|
if (controller.getModel().getPlayers().stream()
|
||||||
if(controller.getModel().getPlayers().stream().anyMatch(p -> p.getUserName().equals(username))&& !playerList.containsKey(username)){
|
.anyMatch(p -> p.getUserName().equals(username))
|
||||||
playerList.put(username, true);
|
&& !playerList.containsKey(username)) {
|
||||||
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(
|
playerList.put(username, true);
|
||||||
username, clientSocket, clientSend, clientReceive,
|
System.out.println("(After crash)Reconnected player: " + username);
|
||||||
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);
|
|
||||||
|
|
||||||
ClientHandler handler = new ClientHandler(
|
ClientHandler handler = new ClientHandler(
|
||||||
username, clientSocket, clientSend, clientReceive,
|
username,
|
||||||
clientHandlers, playerList, actionQueue
|
clientSocket,
|
||||||
);
|
clientSend,
|
||||||
clientSocket.getOutputStream().write(1);
|
clientReceive,
|
||||||
pendingHeartbeat.put(username, handler);
|
clientHandlers,
|
||||||
Game game = controller.getModel();
|
playerList,
|
||||||
handler.notifyMiniModel(new MiniModel(game.getBoard(),game.getSlotMap(), game.orderLogicCard,game.getCurrentState(),game.getPlayers(),game.getAvailableTotems()));
|
actionQueue
|
||||||
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.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
clientSocket.getOutputStream().write(1);
|
||||||
|
pendingHeartbeat.put(username, handler);
|
||||||
|
|
||||||
|
Thread thread = new Thread(handler);
|
||||||
|
thread.start();
|
||||||
|
|
||||||
}
|
clientHandlers.add(handler);
|
||||||
catch(IOException | ClassNotFoundException e){
|
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();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accetta connessioni sul socket heartbeat e le associa al ClientHandler giusto.
|
* Accepts heartbeat connections and associates them with the correct client handler.
|
||||||
* Il client manda subito il proprio username per identificarsi.
|
*
|
||||||
|
* <p>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() {
|
private void acceptHeartbeat() {
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
Socket hbSocket = heartbeatSocketTCP.accept();
|
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();
|
String username = (String) hbIn.readObject();
|
||||||
|
|
||||||
ClientHandler handler = pendingHeartbeat.remove(username);
|
ClientHandler handler = pendingHeartbeat.remove(username);
|
||||||
|
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
HeartbeatHandler hb = new HeartbeatHandler(username, hbSocket, handler);
|
HeartbeatHandler hb =
|
||||||
|
new HeartbeatHandler(username, hbSocket, handler);
|
||||||
|
|
||||||
new Thread(hb, "heartbeat-" + username).start();
|
new Thread(hb, "heartbeat-" + username).start();
|
||||||
System.out.println("Heartbeat connected for: " + username);
|
System.out.println("Heartbeat connected for: " + username);
|
||||||
} else {
|
} else {
|
||||||
System.out.println("No pending handler for: " + username + ", closing heartbeat.");
|
System.out.println(
|
||||||
|
"No pending handler for: " + username + ", closing heartbeat."
|
||||||
|
);
|
||||||
hbSocket.close();
|
hbSocket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,13 +367,27 @@ public class TCPServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies connected TCP clients of a new network event.
|
||||||
|
*
|
||||||
|
* <p>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) {
|
public void notifyAll(NetworkEvent event) {
|
||||||
clientHandlers.forEach(h -> {
|
clientHandlers.forEach(h -> {
|
||||||
if (!event.getIsError() || event.getUsername().equals(h.getUsername()))
|
if (!event.getIsError() || event.getUsername().equals(h.getUsername())) {
|
||||||
h.notifyEvent(event);
|
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) {
|
public void notifyAll(MiniModel model) {
|
||||||
clientHandlers.forEach(h -> h.notifyMiniModel(model));
|
clientHandlers.forEach(h -> h.notifyMiniModel(model));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user