Files
Progetto-ingegneria-del-sof…/deliverables/Coverage/htmReport/ns-11/sources/source-3.html
T
2026-06-19 22:57:43 +02:00

439 lines
20 KiB
HTML

<!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/30)
</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 final int port;
&nbsp;
&nbsp; /**
&nbsp; * TCP port dedicated to heartbeat communication.
&nbsp; */
&nbsp; private final 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 final BlockingQueue&lt;NetworkEvent&gt; actionQueue;
&nbsp;
&nbsp; /**
&nbsp; * Map storing the online/offline status of connected players.
&nbsp; */
&nbsp; private final LimitedMap&lt;String, Boolean&gt; playerList;
&nbsp;
&nbsp; /**
&nbsp; * List of active TCP client handlers.
&nbsp; */
&nbsp; private final 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;TCP 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 || controller.getModel().getCurrentState().getGameStage().equals(GameStages.WAITING))) {</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.INVALID_USERNAME);</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-19 22:53</div>
</div>
</body>
</html>