Add: Added Coverage Screenshots And htlmReport.

This commit is contained in:
GabrieleRadice
2026-06-19 21:50:38 +02:00
parent 6aca188aa3
commit d78b8bbd93
316 changed files with 86329 additions and 0 deletions
@@ -0,0 +1,267 @@
<!DOCTYPE html>
<html id="htmlId">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Coverage Report > ClientHandler</title>
<style type="text/css">
@import "../../css/coverage.css";
@import "../../css/idea.min.css";
</style>
<script type="text/javascript" src="../../js/highlight.min.js"></script>
<script type="text/javascript" src="../../js/highlightjs-line-numbers.min.js"></script>
</head>
<body>
<div class="content">
<div class="breadCrumbs">
Current scope: <a href="../../index.html">all classes</a>
<span class="separator">|</span>
<a href="../index.html">it.polimi.ingsw.gc14.Network.TCP.Server</a>
</div>
<h1>Coverage Summary for Class: ClientHandler (it.polimi.ingsw.gc14.Network.TCP.Server)</h1>
<table class="coverageStats">
<tr>
<th class="name">Class</th>
<th class="coverageStat
">
Class, %
</th>
<th class="coverageStat
">
Method, %
</th>
<th class="coverageStat
">
Branch, %
</th>
<th class="coverageStat
">
Line, %
</th>
</tr>
<tr>
<td class="name">ClientHandler</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/1)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/6)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/14)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/32)
</span>
</td>
</tr>
</table>
<br/>
<br/>
<pre>
<code class="sourceCode" id="sourceCode">&nbsp;package it.polimi.ingsw.gc14.Network.TCP.Server;
&nbsp;
&nbsp;import it.polimi.ingsw.gc14.LimitedMap;
&nbsp;import it.polimi.ingsw.gc14.Model.Game;
&nbsp;import it.polimi.ingsw.gc14.Model.MiniModel;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkEvent;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkEvents.DisconnectedPlayer;
&nbsp;
&nbsp;import java.io.*;
&nbsp;import java.net.*;
&nbsp;import java.util.List;
&nbsp;import java.util.concurrent.BlockingQueue;
&nbsp;
&nbsp;/**
&nbsp; * Handles the TCP connection with a single client.
&nbsp; * Each instance runs on a dedicated thread and is responsible for receiving {@link NetworkEvent} from its client and adding them
&nbsp; * to the {@link #actionQueue}.
&nbsp; * It is also responsible to send events and game model updates back to the client.
&nbsp; */
&nbsp;public class ClientHandler implements Runnable {
&nbsp; /**
&nbsp; * The username of the connected client on this handler
&nbsp; */
&nbsp; private final String username;
&nbsp;
&nbsp; private volatile boolean running;
&nbsp;
&nbsp; /**
&nbsp; * Returns the username associated with this client.
&nbsp; *
&nbsp; * @return the username associated with this client.
&nbsp; */
&nbsp; public String getUsername() {
<b class="nc">&nbsp; return username;</b>
&nbsp; }
&nbsp; /** The TCP socket */
&nbsp; private Socket clientSocket;
&nbsp;
&nbsp; /** Input stream used to receive objects from the client */
&nbsp; private ObjectInputStream in;
&nbsp;
&nbsp; /** Output stream used to send objects to the client */
&nbsp; private ObjectOutputStream out;
&nbsp;
&nbsp; /**
&nbsp; * Shared list of all client handlers.
&nbsp; * This handler removes itself from the list when disconnected.
&nbsp; */
&nbsp; private List&lt;ClientHandler&gt; clientHandlers;
&nbsp;
&nbsp; /** Maps each connected player&#39;s username to their connection state (true = connected). */
&nbsp; private LimitedMap&lt;String, Boolean&gt; limitedMap;
&nbsp;
&nbsp; /** Queue containing the events to be applied to the game model */
&nbsp; private BlockingQueue&lt;NetworkEvent&gt; actionQueue;
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Class constructor that initializes the attributes.
&nbsp; *
&nbsp; * @param username the username associated with the client.
&nbsp; * @param clientSocket the socket representing the client&#39;s TCP connection.
&nbsp; * @param out the output stream used to send data to the client.
&nbsp; * @param in the input stream used to receive data from the client.
&nbsp; * @param clientHandlers the shared list of all active client handlers.
&nbsp; * @param playersMap the shared map containing the connected players.
&nbsp; * @param actionQueue the queue containing incoming events.
&nbsp; */
<b class="nc">&nbsp; public ClientHandler(String username, Socket clientSocket, ObjectOutputStream out, ObjectInputStream in, List&lt;ClientHandler&gt; clientHandlers, LimitedMap&lt;String,Boolean&gt; playersMap, BlockingQueue&lt;NetworkEvent&gt; actionQueue) {</b>
<b class="nc">&nbsp; this.username=username;</b>
<b class="nc">&nbsp; this.clientSocket = clientSocket;</b>
<b class="nc">&nbsp; this.in = in;</b>
<b class="nc">&nbsp; this.out = out;</b>
<b class="nc">&nbsp; this.clientHandlers = clientHandlers;</b>
<b class="nc">&nbsp; this.actionQueue = actionQueue;</b>
<b class="nc">&nbsp; this.limitedMap = playersMap;</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Listens for incoming {@link NetworkEvent}s from the client and adds them to the {@link #actionQueue}.
&nbsp; * Upon disconnection, this handler removes itself from {@link #clientHandlers}.
&nbsp; */
&nbsp; @Override
&nbsp; public void run() {
<b class="nc">&nbsp; running = true;</b>
&nbsp; try {
<b class="nc">&nbsp; while (running) {</b>
<b class="nc">&nbsp; NetworkEvent event = (NetworkEvent) in.readObject();</b>
<b class="nc">&nbsp; if (!actionQueue.offer(event)) {</b>
<b class="nc">&nbsp; System.out.println(&quot;Error inserting action into queue&quot;);</b>
&nbsp; }
&nbsp; }
&nbsp; } catch (IOException e) {
<b class="nc">&nbsp; System.err.println(&quot;Error in incoming connection/disconnection: &quot; + username);</b>
&nbsp; } catch (ClassNotFoundException e) {
<b class="nc">&nbsp; System.err.println(&quot;Class not found: &quot; + username);</b>
&nbsp; } finally {
<b class="nc">&nbsp; if (running) disconnect();</b>
&nbsp; }
&nbsp; }
&nbsp;
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Sends a {@link NetworkEvent} to the client.
&nbsp; * @param event The network event to send to the client.
&nbsp; */
&nbsp; public synchronized void notifyEvent(NetworkEvent event) {
&nbsp; try {
<b class="nc">&nbsp; out.reset();</b>
<b class="nc">&nbsp; out.writeObject(event);</b>
&nbsp; } catch (IOException e) {
<b class="nc">&nbsp; e.printStackTrace();</b>
<b class="nc">&nbsp; disconnect();</b>
&nbsp; }
&nbsp;
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Sends the current game model to this client.
&nbsp; * @param game The current state of the game to send to the client.
&nbsp; */
&nbsp; public synchronized void notifyMiniModel(MiniModel game) {
&nbsp; try {
<b class="nc">&nbsp; out.reset();</b>
<b class="nc">&nbsp; out.writeObject(game);</b>
&nbsp; } catch (IOException e) {
<b class="nc">&nbsp; e.printStackTrace();</b>
<b class="nc">&nbsp; disconnect();</b>
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Disconnects the client from the server.
&nbsp; *
&nbsp; * &lt;p&gt;The client handler is stopped and removed from the list of active handlers.
&nbsp; * The player is marked as disconnected, a {@link DisconnectedPlayer} event is
&nbsp; * added to the action queue, and the associated socket is closed.
&nbsp; */
&nbsp; public synchronized void disconnect() {
<b class="nc">&nbsp; if (!running) return;</b>
<b class="nc">&nbsp; running = false;</b>
<b class="nc">&nbsp; clientHandlers.remove(this);</b>
<b class="nc">&nbsp; limitedMap.put(username, false);</b>
<b class="nc">&nbsp; actionQueue.add(new DisconnectedPlayer(username));</b>
<b class="nc">&nbsp; System.out.println(&quot;Disconnected player: &quot; + username);</b>
<b class="nc">&nbsp; try { clientSocket.close(); } catch (IOException ignored) {}</b>
&nbsp; }
&nbsp;}
</code>
</pre>
</div>
<script type="text/javascript">
(function() {
var msie = false, msie9 = false;
/*@cc_on
msie = true;
@if (@_jscript_version >= 9)
msie9 = true;
@end
@*/
if (!msie || msie && msie9) {
hljs.highlightAll()
hljs.initLineNumbersOnLoad();
}
})();
</script>
<div class="footer">
<div style="float:right;">generated on 2026-06-14 21:53</div>
</div>
</body>
</html>
@@ -0,0 +1,247 @@
<!DOCTYPE html>
<html id="htmlId">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Coverage Report > HeartbeatHandler</title>
<style type="text/css">
@import "../../css/coverage.css";
@import "../../css/idea.min.css";
</style>
<script type="text/javascript" src="../../js/highlight.min.js"></script>
<script type="text/javascript" src="../../js/highlightjs-line-numbers.min.js"></script>
</head>
<body>
<div class="content">
<div class="breadCrumbs">
Current scope: <a href="../../index.html">all classes</a>
<span class="separator">|</span>
<a href="../index.html">it.polimi.ingsw.gc14.Network.TCP.Server</a>
</div>
<h1>Coverage Summary for Class: HeartbeatHandler (it.polimi.ingsw.gc14.Network.TCP.Server)</h1>
<table class="coverageStats">
<tr>
<th class="name">Class</th>
<th class="coverageStat
">
Class, %
</th>
<th class="coverageStat
">
Method, %
</th>
<th class="coverageStat
">
Branch, %
</th>
<th class="coverageStat
">
Line, %
</th>
</tr>
<tr>
<td class="name">HeartbeatHandler</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/1)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/5)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/8)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/29)
</span>
</td>
</tr>
</table>
<br/>
<br/>
<pre>
<code class="sourceCode" id="sourceCode">&nbsp;package it.polimi.ingsw.gc14.Network.TCP.Server;
&nbsp;
&nbsp;import java.io.*;
&nbsp;import java.net.*;
&nbsp;import java.util.concurrent.Executors;
&nbsp;import java.util.concurrent.ScheduledExecutorService;
&nbsp;import java.util.concurrent.TimeUnit;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkConfig;
&nbsp;
&nbsp;/**
&nbsp; * Handles the heartbeat communication associated with a TCP client.
&nbsp; *
&nbsp; * &lt;p&gt;This component listens for heartbeat {@code PING} messages from the client,
&nbsp; * replies with {@code PONG} messages, and periodically checks whether the client
&nbsp; * has remained silent for too long. If a timeout or communication error occurs,
&nbsp; * both the heartbeat connection and the main client connection are disconnected.
&nbsp; */
&nbsp;public class HeartbeatHandler implements Runnable {
&nbsp;
&nbsp; private final String username;
&nbsp; private final Socket socket;
&nbsp; private final InputStream in;
&nbsp; private final OutputStream out;
&nbsp;
&nbsp; /**
&nbsp; * Byte value representing a heartbeat ping message.
&nbsp; */
&nbsp; private static final int PING = 1;
&nbsp;
&nbsp; /**
&nbsp; * Byte value representing a heartbeat pong response.
&nbsp; */
&nbsp; private static final int PONG = 2;
&nbsp;
&nbsp; /**
&nbsp; * Main client handler associated with this heartbeat connection.
&nbsp; */
&nbsp; private final ClientHandler mainHandler;
&nbsp;
&nbsp; /**
&nbsp; * Timestamp of the last received heartbeat message.
&nbsp; */
<b class="nc">&nbsp; private volatile long lastReceivedTime = System.currentTimeMillis();</b>
&nbsp;
&nbsp; /**
&nbsp; * Indicates whether the heartbeat handler is still active.
&nbsp; */
<b class="nc">&nbsp; private volatile boolean running = true;</b>
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Executor used to periodically check heartbeat timeouts.
&nbsp; */
<b class="nc">&nbsp; private final ScheduledExecutorService watchdog =</b>
<b class="nc">&nbsp; Executors.newSingleThreadScheduledExecutor();</b>
&nbsp;
&nbsp; /**
&nbsp; * Constructs a heartbeat handler for the specified client.
&nbsp; *
&nbsp; * @param username the username of the connected client.
&nbsp; * @param socket the socket dedicated to heartbeat communication.
&nbsp; * @param mainHandler the main client handler associated with the same player.
&nbsp; * @throws IOException if the input or output stream cannot be obtained from the socket.
&nbsp; */
&nbsp; public HeartbeatHandler(String username, Socket socket, ClientHandler mainHandler)
<b class="nc">&nbsp; throws IOException {</b>
<b class="nc">&nbsp; this.username = username;</b>
<b class="nc">&nbsp; this.socket = socket;</b>
<b class="nc">&nbsp; this.mainHandler = mainHandler;</b>
<b class="nc">&nbsp; this.in = socket.getInputStream();</b>
<b class="nc">&nbsp; this.out = socket.getOutputStream();</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Starts the heartbeat listening loop.
&nbsp; *
&nbsp; * &lt;p&gt;The method activates the watchdog task, then waits for incoming heartbeat
&nbsp; * messages. When a {@code PING} is received, the last-received timestamp is
&nbsp; * updated and a {@code PONG} response is sent back to the client.
&nbsp; * If the stream is closed or an I/O error occurs, the client is disconnected.
&nbsp; */
&nbsp; @Override
&nbsp; public void run() {
<b class="nc">&nbsp; startWatchdog();</b>
&nbsp; try {
<b class="nc">&nbsp; while (running) {</b>
<b class="nc">&nbsp; int b = in.read(); // blocks until a byte arrives</b>
<b class="nc">&nbsp; if (b == -1) {</b>
<b class="nc">&nbsp; disconnect();</b>
&nbsp; break; // stream closed
&nbsp; }
<b class="nc">&nbsp; if (b == PING) {</b>
<b class="nc">&nbsp; lastReceivedTime = System.currentTimeMillis();</b>
<b class="nc">&nbsp; out.write(PONG);</b>
<b class="nc">&nbsp; out.flush();</b>
&nbsp; }
&nbsp; }
&nbsp; } catch (IOException e) {
<b class="nc">&nbsp; disconnect();</b>
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Starts the periodic watchdog task that detects heartbeat timeouts.
&nbsp; *
&nbsp; * &lt;p&gt;If no heartbeat message is received within the configured silence threshold,
&nbsp; * the associated client is disconnected.
&nbsp; */
&nbsp; private void startWatchdog() {
<b class="nc">&nbsp; watchdog.scheduleAtFixedRate(() -&gt; {</b>
<b class="nc">&nbsp; if (System.currentTimeMillis() - lastReceivedTime &gt; NetworkConfig.SILENCE_THRESHOLD_MS) {</b>
<b class="nc">&nbsp; System.out.println(&quot;Heartbeat timeout: &quot; + username);</b>
<b class="nc">&nbsp; disconnect();</b>
&nbsp; }
&nbsp; }, 1, 1, TimeUnit.SECONDS);
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Disconnects the heartbeat channel and the associated main client connection.
&nbsp; *
&nbsp; * &lt;p&gt;The handler is stopped, the watchdog task is terminated, the main
&nbsp; * {@link ClientHandler} is disconnected, and the heartbeat socket is closed.
&nbsp; */
&nbsp; private synchronized void disconnect() {
<b class="nc">&nbsp; running = false;</b>
<b class="nc">&nbsp; watchdog.shutdownNow();</b>
<b class="nc">&nbsp; mainHandler.disconnect(); // disconnette anche il socket principale</b>
<b class="nc">&nbsp; System.out.println(&quot;Disconnected: &quot; + username);</b>
<b class="nc">&nbsp; try { socket.close(); } catch (IOException ignored) {}</b>
&nbsp; }
&nbsp;}
</code>
</pre>
</div>
<script type="text/javascript">
(function() {
var msie = false, msie9 = false;
/*@cc_on
msie = true;
@if (@_jscript_version >= 9)
msie9 = true;
@end
@*/
if (!msie || msie && msie9) {
hljs.highlightAll()
hljs.initLineNumbersOnLoad();
}
})();
</script>
<div class="footer">
<div style="float:right;">generated on 2026-06-14 21:53</div>
</div>
</body>
</html>
@@ -0,0 +1,438 @@
<!DOCTYPE html>
<html id="htmlId">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Coverage Report > TCPServer</title>
<style type="text/css">
@import "../../css/coverage.css";
@import "../../css/idea.min.css";
</style>
<script type="text/javascript" src="../../js/highlight.min.js"></script>
<script type="text/javascript" src="../../js/highlightjs-line-numbers.min.js"></script>
</head>
<body>
<div class="content">
<div class="breadCrumbs">
Current scope: <a href="../../index.html">all classes</a>
<span class="separator">|</span>
<a href="../index.html">it.polimi.ingsw.gc14.Network.TCP.Server</a>
</div>
<h1>Coverage Summary for Class: TCPServer (it.polimi.ingsw.gc14.Network.TCP.Server)</h1>
<table class="coverageStats">
<tr>
<th class="name">Class</th>
<th class="coverageStat
">
Class, %
</th>
<th class="coverageStat
">
Method, %
</th>
<th class="coverageStat
">
Branch, %
</th>
<th class="coverageStat
">
Line, %
</th>
</tr>
<tr>
<td class="name">TCPServer</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/1)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/8)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/28)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/105)
</span>
</td>
</tr>
</table>
<br/>
<br/>
<pre>
<code class="sourceCode" id="sourceCode">&nbsp;package it.polimi.ingsw.gc14.Network.TCP.Server;
&nbsp;
&nbsp;import it.polimi.ingsw.gc14.Controller.GameController;
&nbsp;import it.polimi.ingsw.gc14.ErrorType;
&nbsp;import it.polimi.ingsw.gc14.LimitedMap;
&nbsp;import it.polimi.ingsw.gc14.Model.Game;
&nbsp;import it.polimi.ingsw.gc14.Model.GamePackage.GameStages;
&nbsp;import it.polimi.ingsw.gc14.Model.MiniModel;
&nbsp;import it.polimi.ingsw.gc14.Network.EventType;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkConfig;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkEvent;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkEvents.AddPlayer;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkEvents.ReconnectPlayer;
&nbsp;
&nbsp;import java.io.*;
&nbsp;import java.net.*;
&nbsp;import java.util.ArrayList;
&nbsp;import java.util.Map;
&nbsp;import java.util.concurrent.CopyOnWriteArrayList;
&nbsp;import java.util.concurrent.BlockingQueue;
&nbsp;import java.util.concurrent.ConcurrentHashMap;
&nbsp;import java.util.stream.Collectors;
&nbsp;
&nbsp;/**
&nbsp; * TCP server responsible for accepting client connections,
&nbsp; * handling player registration and reconnection, and sending
&nbsp; * game updates to connected clients.
&nbsp; *
&nbsp; * &lt;p&gt;The server also manages a dedicated heartbeat channel used
&nbsp; * to detect disconnected clients and associate each heartbeat
&nbsp; * connection with the corresponding {@link ClientHandler}.
&nbsp; */
&nbsp;public class TCPServer {
&nbsp;
&nbsp; /**
&nbsp; * Main TCP port used for standard client-server communication.
&nbsp; */
&nbsp; private int port;
&nbsp;
&nbsp; /**
&nbsp; * TCP port dedicated to heartbeat communication.
&nbsp; */
&nbsp; private int heartbeatPort;
&nbsp;
&nbsp; /**
&nbsp; * Main server socket used to accept client connections.
&nbsp; */
&nbsp; private ServerSocket socketTCP;
&nbsp;
&nbsp; /**
&nbsp; * Server socket used to accept heartbeat connections.
&nbsp; */
&nbsp; private ServerSocket heartbeatSocketTCP;
&nbsp;
&nbsp; /**
&nbsp; * Game controller used to manage the server-side game logic.
&nbsp; */
&nbsp; private final GameController controller;
&nbsp;
&nbsp; /**
&nbsp; * Queue containing network events received from clients.
&nbsp; */
&nbsp; private BlockingQueue&lt;NetworkEvent&gt; actionQueue;
&nbsp;
&nbsp; /**
&nbsp; * Map storing the online/offline status of connected players.
&nbsp; */
&nbsp; private LimitedMap&lt;String, Boolean&gt; playerList;
&nbsp;
&nbsp; /**
&nbsp; * List of active TCP client handlers.
&nbsp; */
&nbsp; private CopyOnWriteArrayList&lt;ClientHandler&gt; clientHandlers;
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Temporary map associating each username with the corresponding
&nbsp; * {@link ClientHandler} waiting for its heartbeat connection.
&nbsp; */
<b class="nc">&nbsp; private final Map&lt;String, ClientHandler&gt; pendingHeartbeat = new ConcurrentHashMap&lt;&gt;();</b>
&nbsp;
&nbsp; /**
&nbsp; * Constructs a TCP server with the required game and network components.
&nbsp; *
&nbsp; * @param controller the game controller used to manage the game logic.
&nbsp; * @param port the main TCP port used for client communication.
&nbsp; * @param heartbeatPort the TCP port dedicated to heartbeat connections.
&nbsp; * @param actionQueue the queue containing incoming network events.
&nbsp; * @param playerList the map storing the connection status of the players.
&nbsp; */
&nbsp; public TCPServer(GameController controller,
&nbsp; int port,
&nbsp; int heartbeatPort,
&nbsp; BlockingQueue&lt;NetworkEvent&gt; actionQueue,
<b class="nc">&nbsp; LimitedMap&lt;String, Boolean&gt; playerList) {</b>
<b class="nc">&nbsp; this.port = port;</b>
<b class="nc">&nbsp; this.heartbeatPort = heartbeatPort;</b>
<b class="nc">&nbsp; this.controller = controller;</b>
<b class="nc">&nbsp; this.actionQueue = actionQueue;</b>
<b class="nc">&nbsp; this.playerList = playerList;</b>
<b class="nc">&nbsp; this.clientHandlers = new CopyOnWriteArrayList&lt;&gt;();</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Starts the TCP server and begins accepting client connections.
&nbsp; *
&nbsp; * &lt;p&gt;The method opens both the main TCP server socket and the dedicated
&nbsp; * heartbeat server socket. It then starts a separate thread for heartbeat
&nbsp; * connections and continuously waits for new players or reconnecting clients.
&nbsp; *
&nbsp; * &lt;p&gt;When a new connection is received, the first event must be an
&nbsp; * {@link AddPlayer} request. Depending on the current server state,
&nbsp; * the connection is handled either as a new player joining the game
&nbsp; * or as a reconnection attempt.
&nbsp; *
&nbsp; */
&nbsp; public void start() {
&nbsp; try {
<b class="nc">&nbsp; socketTCP = new ServerSocket(port);</b>
<b class="nc">&nbsp; heartbeatSocketTCP = new ServerSocket(heartbeatPort);</b>
&nbsp; } catch (IOException e) {
<b class="nc">&nbsp; System.out.println(&quot;Could not start TCP server&quot;);</b>
<b class="nc">&nbsp; e.printStackTrace();</b>
&nbsp; return;
&nbsp; }
&nbsp;
<b class="nc">&nbsp; System.out.println(&quot;TCP server started on port: &quot; + port);</b>
<b class="nc">&nbsp; System.out.println(&quot;Heartbeat server started on port: &quot; + heartbeatPort);</b>
&nbsp;
<b class="nc">&nbsp; new Thread(this::acceptHeartbeat, &quot;heartbeat-acceptor&quot;).start();</b>
&nbsp;
&nbsp; while (true) {
&nbsp; try {
<b class="nc">&nbsp; Socket clientSocket = socketTCP.accept();</b>
&nbsp;
<b class="nc">&nbsp; ObjectOutputStream clientSend = new ObjectOutputStream(clientSocket.getOutputStream());</b>
<b class="nc">&nbsp; clientSend.flush();</b>
<b class="nc">&nbsp; ObjectInputStream clientReceive = new ObjectInputStream(clientSocket.getInputStream());</b>
&nbsp;
<b class="nc">&nbsp; NetworkEvent event = (NetworkEvent) clientReceive.readObject();</b>
&nbsp;
<b class="nc">&nbsp; if (!(event.getEventType() == EventType.ADD_PLAYER)) {</b>
&nbsp; clientSocket.close();
<b class="nc">&nbsp; System.out.println(&quot;Invalid parameters. Connection terminated.&quot;);</b>
&nbsp; continue;
&nbsp; }
&nbsp;
<b class="nc">&nbsp; AddPlayer eventAddPlayer = (AddPlayer) event;</b>
&nbsp;
<b class="nc">&nbsp; if (eventAddPlayer.getProposedNPlayer() &lt; NetworkConfig.MIN_PLAYERS</b>
<b class="nc">&nbsp; || eventAddPlayer.getProposedNPlayer() &gt; NetworkConfig.MAX_PLAYERS) {</b>
<b class="nc">&nbsp; eventAddPlayer.setErrorType(ErrorType.WRONG_PLAYER_NUMBER);</b>
<b class="nc">&nbsp; eventAddPlayer.setIsError(true);</b>
<b class="nc">&nbsp; clientSend.writeObject(eventAddPlayer);</b>
&nbsp; clientSocket.close();
<b class="nc">&nbsp; System.out.println(&quot;Invalid parameters. Connection terminated.&quot;);</b>
&nbsp; continue;
&nbsp; }
&nbsp;
<b class="nc">&nbsp; synchronized (controller) {</b>
<b class="nc">&nbsp; String username = eventAddPlayer.getUsername();</b>
&nbsp; //reconnect players after a server crash
<b class="nc">&nbsp; if (playerList.isEmpty()&amp;&amp; controller.getModel()==null) {</b>
<b class="nc">&nbsp; Game model = new Game(eventAddPlayer.getProposedNPlayer());</b>
<b class="nc">&nbsp; controller.setModel(model);</b>
<b class="nc">&nbsp; playerList.setLimit(eventAddPlayer.getProposedNPlayer());</b>
<b class="nc">&nbsp; System.out.println(&quot;Game Created With :&quot;+eventAddPlayer.getProposedNPlayer()+&quot; Players&quot;);</b>
&nbsp; }
<b class="nc">&nbsp; if(controller.getModel().getCurrentState().getGameStage()!= GameStages.WAITING ) {</b>
<b class="nc">&nbsp; if (controller.getModel().getPlayers().stream().noneMatch(p -&gt; p.getUserName().equals(username))|| controller.getModel().getCurrentState().getGameStage()==GameStages.ENDED) {</b>
<b class="nc">&nbsp; eventAddPlayer.setErrorType(ErrorType.GAME_ALREADY_STARTED);</b>
<b class="nc">&nbsp; eventAddPlayer.setIsError(true);</b>
<b class="nc">&nbsp; clientSend.writeObject(eventAddPlayer);</b>
&nbsp; clientSocket.close();
<b class="nc">&nbsp; System.out.println(&quot;Invalid parameters. Connection terminated.&quot;);</b>
<b class="nc">&nbsp; continue;</b>
&nbsp; }
&nbsp; }
&nbsp; else {
<b class="nc">&nbsp; if (controller.addPlayer(username)) {</b>
<b class="nc">&nbsp; playerList.put(username, true);</b>
<b class="nc">&nbsp; System.out.println(&quot;Accepted player: &quot; + username);</b>
<b class="nc">&nbsp; clientSend.writeObject(eventAddPlayer);</b>
<b class="nc">&nbsp; ClientHandler handler = createAndRegisterHandler(</b>
&nbsp; username, clientSocket, clientSend, clientReceive);
<b class="nc">&nbsp; new Thread(handler).start();</b>
<b class="nc">&nbsp; continue;</b>
&nbsp; } else {
<b class="nc">&nbsp; eventAddPlayer.setErrorType(ErrorType.USERNAME_ALREADY_USED);</b>
<b class="nc">&nbsp; eventAddPlayer.setIsError(true);</b>
<b class="nc">&nbsp; clientSend.writeObject(eventAddPlayer);</b>
&nbsp; clientSocket.close();
<b class="nc">&nbsp; System.out.println(&quot;Invalid parameters. Connection terminated.&quot;);</b>
<b class="nc">&nbsp; continue;</b>
&nbsp; }
&nbsp; }
&nbsp;
<b class="nc">&nbsp; if (!playerList.containsKey(username)) {</b>
<b class="nc">&nbsp; playerList.put(username, true);</b>
<b class="nc">&nbsp; System.out.println(&quot;(After crash)Reconnected player: &quot; + username);</b>
<b class="nc">&nbsp; clientSend.writeObject(eventAddPlayer);</b>
<b class="nc">&nbsp; ClientHandler handler = createAndRegisterHandler(</b>
&nbsp; username, clientSocket, clientSend, clientReceive);
<b class="nc">&nbsp; new Thread(handler).start();</b>
<b class="nc">&nbsp; } else if (!playerList.get(username)) {</b>
&nbsp; // reconnect a previously disconnected player
<b class="nc">&nbsp; playerList.put(username, true);</b>
<b class="nc">&nbsp; System.out.println(&quot;Reconnected player: &quot; + username);</b>
<b class="nc">&nbsp; clientSend.writeObject(eventAddPlayer);</b>
<b class="nc">&nbsp; ClientHandler handler = createAndRegisterHandler(</b>
&nbsp; username, clientSocket, clientSend, clientReceive);
<b class="nc">&nbsp; Game game = controller.getModel();</b>
<b class="nc">&nbsp; handler.notifyMiniModel(new MiniModel(</b>
<b class="nc">&nbsp; game.getSlotMap(), game.getOrderLogicCard(),</b>
<b class="nc">&nbsp; game.getCurrentState(), game.getPlayers(),</b>
<b class="nc">&nbsp; game.getAvailableTotems(),</b>
<b class="nc">&nbsp; game.getUpperListTribeCards(), game.getLowerListTribeCards(),</b>
<b class="nc">&nbsp; game.getUpperListBuilding(), game.getLowerListBuilding(),</b>
<b class="nc">&nbsp; disconnectedUsernames(game)));</b>
<b class="nc">&nbsp; new Thread(handler).start();</b>
<b class="nc">&nbsp; actionQueue.add(new ReconnectPlayer(username));</b>
&nbsp; } else {
<b class="nc">&nbsp; eventAddPlayer.setErrorType(ErrorType.USER_ALREADY_CONNECTED);</b>
<b class="nc">&nbsp; eventAddPlayer.setIsError(true);</b>
<b class="nc">&nbsp; clientSend.writeObject(eventAddPlayer);</b>
&nbsp; clientSocket.close();
<b class="nc">&nbsp; System.out.println(&quot;Invalid parameters. Connection terminated.&quot;);</b>
&nbsp; }
<b class="nc">&nbsp; }</b>
&nbsp; } catch (IOException | ClassNotFoundException e) {
<b class="nc">&nbsp; e.printStackTrace();</b>
&nbsp; }
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Accepts heartbeat connections and associates them with the correct client handler.
&nbsp; *
&nbsp; * &lt;p&gt;Each client immediately sends its username on the heartbeat channel.
&nbsp; * The method uses that username to retrieve the pending {@link ClientHandler}
&nbsp; * and starts a dedicated {@link HeartbeatHandler}. If no pending handler is found,
&nbsp; * the heartbeat socket is closed.
&nbsp; */
&nbsp; private void acceptHeartbeat() {
&nbsp; while (true) {
&nbsp; try {
<b class="nc">&nbsp; Socket hbSocket = heartbeatSocketTCP.accept();</b>
<b class="nc">&nbsp; ObjectInputStream hbIn =</b>
<b class="nc">&nbsp; new ObjectInputStream(hbSocket.getInputStream());</b>
&nbsp;
<b class="nc">&nbsp; String username = (String) hbIn.readObject();</b>
&nbsp;
<b class="nc">&nbsp; ClientHandler handler = pendingHeartbeat.remove(username);</b>
&nbsp;
<b class="nc">&nbsp; if (handler != null) {</b>
<b class="nc">&nbsp; HeartbeatHandler hb =</b>
&nbsp; new HeartbeatHandler(username, hbSocket, handler);
&nbsp;
<b class="nc">&nbsp; new Thread(hb, &quot;heartbeat-&quot; + username).start();</b>
<b class="nc">&nbsp; System.out.println(&quot;Heartbeat connected for: &quot; + username);</b>
&nbsp; } else {
<b class="nc">&nbsp; System.out.println(</b>
&nbsp; &quot;No pending handler for: &quot; + username + &quot;, closing heartbeat.&quot;
&nbsp; );
&nbsp; hbSocket.close();
&nbsp; }
&nbsp;
&nbsp; } catch (IOException | ClassNotFoundException e) {
<b class="nc">&nbsp; e.printStackTrace();</b>
&nbsp; }
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Notifies connected TCP clients of a new network event.
&nbsp; *
&nbsp; * &lt;p&gt;If the event does not represent an error, it is sent to all connected clients.
&nbsp; * If it represents an error, it is sent only to the client that requested the action.
&nbsp; *
&nbsp; * @param event the network event to send to the clients.
&nbsp; */
&nbsp; public void notifyAll(NetworkEvent event) {
<b class="nc">&nbsp; clientHandlers.forEach(h -&gt; {</b>
<b class="nc">&nbsp; if (!event.isError() || event.getUsername().equals(h.getUsername())) {</b>
<b class="nc">&nbsp; h.notifyEvent(event);</b>
&nbsp; }
&nbsp; });
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Notifies all connected TCP clients of a new game model.
&nbsp; *
&nbsp; * @param model the updated mini model to send to the clients.
&nbsp; */
&nbsp; public void notifyAll(MiniModel model) {
<b class="nc">&nbsp; clientHandlers.forEach(h -&gt; h.notifyMiniModel(model));</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Creates a {@link ClientHandler}, registers it in {@code pendingHeartbeat}
&nbsp; * and {@code clientHandlers}, but does NOT start its thread — callers start
&nbsp; * the thread after any extra setup (e.g. sending a model snapshot).
&nbsp; */
&nbsp; private ClientHandler createAndRegisterHandler(String username,
&nbsp; Socket socket,
&nbsp; ObjectOutputStream out,
&nbsp; ObjectInputStream in) {
<b class="nc">&nbsp; ClientHandler handler = new ClientHandler(</b>
&nbsp; username, socket, out, in, clientHandlers, playerList, actionQueue);
<b class="nc">&nbsp; pendingHeartbeat.put(username, handler);</b>
<b class="nc">&nbsp; clientHandlers.add(handler);</b>
<b class="nc">&nbsp; return handler;</b>
&nbsp; }
&nbsp;
&nbsp; /** Returns usernames of players currently marked as disconnected in the given game. */
&nbsp; private static ArrayList&lt;String&gt; disconnectedUsernames(Game game) {
<b class="nc">&nbsp; return game.getDisconnectedPlayers().entrySet().stream()</b>
<b class="nc">&nbsp; .filter(Map.Entry::getValue)</b>
<b class="nc">&nbsp; .map(e -&gt; e.getKey().getUserName())</b>
<b class="nc">&nbsp; .collect(Collectors.toCollection(ArrayList::new));</b>
&nbsp; }
&nbsp;}
</code>
</pre>
</div>
<script type="text/javascript">
(function() {
var msie = false, msie9 = false;
/*@cc_on
msie = true;
@if (@_jscript_version >= 9)
msie9 = true;
@end
@*/
if (!msie || msie && msie9) {
hljs.highlightAll()
hljs.initLineNumbersOnLoad();
}
})();
</script>
<div class="footer">
<div style="float:right;">generated on 2026-06-14 21:53</div>
</div>
</body>
</html>