FIx: Fixed Coverage Screenshots And htmlRepot.

This commit is contained in:
GabrieleRadice
2026-06-19 22:57:43 +02:00
parent a4dc8b2540
commit 0129efe400
316 changed files with 4566 additions and 4497 deletions
@@ -0,0 +1,526 @@
<!DOCTYPE html>
<html id="htmlId">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Coverage Report > GameEventProcessor</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</a>
</div>
<h1>Coverage Summary for Class: GameEventProcessor (it.polimi.ingsw.gc14)</h1>
<table class="coverageStats">
<tr>
<th class="name">Class</th>
<th class="coverageStat
">
Method, %
</th>
<th class="coverageStat
">
Branch, %
</th>
<th class="coverageStat
">
Line, %
</th>
</tr>
<tr>
<td class="name">GameEventProcessor</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/19)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/48)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/110)
</span>
</td>
</tr>
<tr>
<td class="name">GameEventProcessor$1</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/1)
</span>
</td>
</tr>
<tr>
<td class="name"><strong>Total</strong></td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/20)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/48)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/111)
</span>
</td>
</tr>
</table>
<br/>
<br/>
<pre>
<code class="sourceCode" id="sourceCode">&nbsp;package it.polimi.ingsw.gc14;
&nbsp;
&nbsp;import it.polimi.ingsw.gc14.Controller.GameController;
&nbsp;import it.polimi.ingsw.gc14.Model.Game;
&nbsp;import it.polimi.ingsw.gc14.Model.GamePackage.GameStages;
&nbsp;import it.polimi.ingsw.gc14.Network.CompositeClientBroadcaster;
&nbsp;import it.polimi.ingsw.gc14.Network.EventType;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkEvent;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkEvents.*;
&nbsp;
&nbsp;import java.util.ArrayList;
&nbsp;import java.util.List;
&nbsp;import java.util.Map;
&nbsp;import java.util.concurrent.*;
&nbsp;import java.util.stream.Collectors;
&nbsp;
&nbsp;/**
&nbsp; * Processes game events from the action queue and routes each one
&nbsp; * based on the current game state.
&nbsp; *
&nbsp; * &lt;p&gt;The three top-level states that determine routing are:
&nbsp; * &lt;ul&gt;
&nbsp; * &lt;li&gt;&lt;b&gt;Inactive&lt;/b&gt; no game model is present, or the game has ended.
&nbsp; * Only disconnection cleanup is performed.&lt;/li&gt;
&nbsp; * &lt;li&gt;&lt;b&gt;Suspended&lt;/b&gt; a forfeit timer is running because exactly one
&nbsp; * player remains online. Only reconnection events are accepted.&lt;/li&gt;
&nbsp; * &lt;li&gt;&lt;b&gt;Active&lt;/b&gt; normal gameplay; every event is applied, saved,
&nbsp; * and broadcast.&lt;/li&gt;
&nbsp; * &lt;/ul&gt;
&nbsp; *
&nbsp; * &lt;p&gt;This class is not thread-safe by itself: it relies on the caller
&nbsp; * (the game loop in {@code ServerLauncher}) to drive it from a single
&nbsp; * thread via {@link #doFirstEvent()}.
&nbsp; */
&nbsp;public class GameEventProcessor {
&nbsp; /** Queue from which incoming client events are consumed one at a time. */
&nbsp; private final BlockingQueue&lt;NetworkEvent&gt; actionQueue;
&nbsp; /** Server-side game controller; all model mutations go through this. */
&nbsp; private final GameController gameController;
&nbsp; /** Shared map tracking each player&#39;s online status ({@code true} = online). */
&nbsp; private final LimitedMap&lt;String, Boolean&gt; playerList;
&nbsp; /** Broadcaster used to push events and model snapshots to all connected clients. */
&nbsp; private final CompositeClientBroadcaster broadcaster;
&nbsp; /** Persists and deletes game save files. */
&nbsp; private final SaveManager saveManager;
&nbsp;
&nbsp; /** Single-thread executor used exclusively for the forfeit timer. Daemon so it does not block JVM shutdown. */
<b class="nc">&nbsp; private final ScheduledExecutorService timerExecutor =</b>
<b class="nc">&nbsp; Executors.newSingleThreadScheduledExecutor(r -&gt; {</b>
<b class="nc">&nbsp; Thread t = new Thread(r, &quot;forfeit-timer&quot;);</b>
<b class="nc">&nbsp; t.setDaemon(true);</b>
<b class="nc">&nbsp; return t;</b>
&nbsp; });
&nbsp;
&nbsp; /**
&nbsp; * Handle to the running forfeit timer, or {@code null} when no timer is active.
&nbsp; * A non-null value signals that the game is in the suspended state.
&nbsp; */
&nbsp; private ScheduledFuture&lt;?&gt; disconnectionTimer;
&nbsp;
&nbsp; /**
&nbsp; * Constructs a {@code GameEventProcessor} with all required dependencies.
&nbsp; *
&nbsp; * @param actionQueue the queue from which incoming events are consumed.
&nbsp; * @param gameController the server-side game controller.
&nbsp; * @param playerList the shared map tracking each player&#39;s online status.
&nbsp; * @param broadcaster the broadcaster used to notify all connected clients.
&nbsp; * @param saveManager the save manager used to persist the game state.
&nbsp; */
&nbsp; public GameEventProcessor(
&nbsp; BlockingQueue&lt;NetworkEvent&gt; actionQueue,
&nbsp; GameController gameController,
&nbsp; LimitedMap&lt;String, Boolean&gt; playerList,
&nbsp; CompositeClientBroadcaster broadcaster,
<b class="nc">&nbsp; SaveManager saveManager) {</b>
<b class="nc">&nbsp; this.actionQueue = actionQueue;</b>
<b class="nc">&nbsp; this.gameController = gameController;</b>
<b class="nc">&nbsp; this.playerList = playerList;</b>
<b class="nc">&nbsp; this.broadcaster = broadcaster;</b>
<b class="nc">&nbsp; this.saveManager = saveManager;</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Blocks until one event is available in the queue, then routes it to the
&nbsp; * appropriate handler based on the current game state.
&nbsp; *
&nbsp; * @throws InterruptedException if the thread is interrupted while waiting
&nbsp; * for the next event.
&nbsp; */
&nbsp; public void doFirstEvent() throws InterruptedException {
<b class="nc">&nbsp; NetworkEvent event = actionQueue.take();</b>
&nbsp;
<b class="nc">&nbsp; if (!isGameActive()) {</b>
<b class="nc">&nbsp; handleInactiveGame(event);</b>
<b class="nc">&nbsp; } else if (isSuspended()) {</b>
<b class="nc">&nbsp; handleSuspendedGame(event);</b>
&nbsp; } else {
<b class="nc">&nbsp; applyAndBroadcast(event);</b>
&nbsp; }
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Returns {@code true} when there is an ongoing game that has not yet ended.
&nbsp; */
&nbsp; private boolean isGameActive() {
<b class="nc">&nbsp; Game model = gameController.getModel();</b>
<b class="nc">&nbsp; return model != null</b>
<b class="nc">&nbsp; &amp;&amp; model.getCurrentState().getGameStage() != GameStages.ENDED;</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Returns {@code true} when the forfeit timer is running, meaning only one
&nbsp; * player is currently online and the game is waiting for a reconnection.
&nbsp; */
&nbsp; private boolean isSuspended() {
<b class="nc">&nbsp; return disconnectionTimer != null;</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Handles events that arrive when no active game exists (not yet started,
&nbsp; * or already ended). Only disconnection cleanup is relevant here.
&nbsp; *
&nbsp; * @param event the incoming event.
&nbsp; */
&nbsp; private void handleInactiveGame(NetworkEvent event) {
<b class="nc">&nbsp; if (event.getEventType() != EventType.DISCONNECTED_PLAYER) return;</b>
&nbsp;
<b class="nc">&nbsp; synchronized (gameController) {</b>
<b class="nc">&nbsp; playerList.remove(event.getUsername());</b>
<b class="nc">&nbsp; if (playerList.isEmpty()) {</b>
<b class="nc">&nbsp; gameController.setModel(null);</b>
<b class="nc">&nbsp; System.out.println(&quot;\n!!! Player list is now empty, ready for a new game init !!!\n&quot;);</b>
&nbsp; }
<b class="nc">&nbsp; }</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Routes events while the game is suspended waiting for a reconnection.
&nbsp; * &lt;ul&gt;
&nbsp; * &lt;li&gt;A second disconnection while suspended means no player remains
&nbsp; * online: the game is aborted entirely.&lt;/li&gt;
&nbsp; * &lt;li&gt;Any non-reconnection event is rejected with an error.&lt;/li&gt;
&nbsp; * &lt;li&gt;A reconnection event is allowed through to {@link #applyAndBroadcast}.&lt;/li&gt;
&nbsp; * &lt;/ul&gt;
&nbsp; *
&nbsp; * @param event the incoming event.
&nbsp; */
&nbsp; private void handleSuspendedGame(NetworkEvent event) {
<b class="nc">&nbsp; switch (event.getEventType()) {</b>
<b class="nc">&nbsp; case DISCONNECTED_PLAYER -&gt; abortGame();</b>
<b class="nc">&nbsp; case RECONNECT_PLAYER -&gt; applyAndBroadcast(event);</b>
<b class="nc">&nbsp; default -&gt; rejectEvent(event);</b>
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Cancels the forfeit timer, clears the player list, and resets the model.
&nbsp; * Called when the last remaining player disconnects while the game is suspended.
&nbsp; */
&nbsp; private void abortGame() {
<b class="nc">&nbsp; disconnectionTimer.cancel(true);</b>
<b class="nc">&nbsp; disconnectionTimer = null;</b>
<b class="nc">&nbsp; playerList.clear();</b>
<b class="nc">&nbsp; gameController.setModel(null);</b>
<b class="nc">&nbsp; System.out.println(&quot;\n!!! All players disconnected — game aborted, ready for a new game init !!!\n&quot;);</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Marks the event as an error and broadcasts it back to the requesting
&nbsp; * player. Used to reject actions that are not permitted in the current state.
&nbsp; *
&nbsp; * @param event the event to reject.
&nbsp; */
&nbsp; private void rejectEvent(NetworkEvent event) {
<b class="nc">&nbsp; event.setIsError(true);</b>
<b class="nc">&nbsp; broadcaster.notifyAll(event);</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Applies the event to the game controller, saves the updated state,
&nbsp; * and broadcasts the result to connected clients.
&nbsp; *
&nbsp; * &lt;p&gt;The entire method body is synchronized on {@code gameController} to
&nbsp; * prevent concurrent modification of the game model by the network threads.
&nbsp; *
&nbsp; * @param event the event to apply.
&nbsp; */
&nbsp; private void applyAndBroadcast(NetworkEvent event) {
<b class="nc">&nbsp; synchronized (gameController) {</b>
<b class="nc">&nbsp; int roundBefore = gameController.getModel().getCurrentState().getRound();</b>
&nbsp;
<b class="nc">&nbsp; event.setIsError(!event.apply(gameController));</b>
<b class="nc">&nbsp; Game game = gameController.getModel();</b>
&nbsp;
<b class="nc">&nbsp; if (event.isError()) {</b>
<b class="nc">&nbsp; broadcaster.notifyAll(event);</b>
<b class="nc">&nbsp; return;</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; cancelForfeitTimerIfReconnect(event);</b>
&nbsp;
<b class="nc">&nbsp; if (!saveManager.save(game)) {</b>
<b class="nc">&nbsp; System.out.println(&quot;\n!!! Save failed !!!\n&quot;);</b>
&nbsp; }
&nbsp; else {
<b class="nc">&nbsp; System.out.println(event.getUsername()+&quot;: &quot;+ event.getEventType() + &quot; save successful.&quot;);</b>
&nbsp; }
&nbsp;
&nbsp; // During the lobby phase a disconnection only removes the player
&nbsp; // from the list; no broadcast is needed.
<b class="nc">&nbsp; if (event.getEventType() == EventType.DISCONNECTED_PLAYER</b>
<b class="nc">&nbsp; &amp;&amp; game.getCurrentState().getGameStage() == GameStages.WAITING) {</b>
<b class="nc">&nbsp; playerList.remove(event.getUsername());</b>
<b class="nc">&nbsp; return;</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; enrichEvent(event, game);</b>
<b class="nc">&nbsp; startForfeitTimerIfNeeded(event, game);</b>
<b class="nc">&nbsp; broadcastResult(event, game, roundBefore);</b>
<b class="nc">&nbsp; }</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Cancels the forfeit timer if the event is a successful reconnection.
&nbsp; *
&nbsp; * @param event the event that was just successfully applied.
&nbsp; */
&nbsp; private void cancelForfeitTimerIfReconnect(NetworkEvent event) {
<b class="nc">&nbsp; if (event.getEventType() == EventType.RECONNECT_PLAYER</b>
&nbsp; &amp;&amp; disconnectionTimer != null
<b class="nc">&nbsp; &amp;&amp; !disconnectionTimer.isDone()) {</b>
<b class="nc">&nbsp; disconnectionTimer.cancel(false);</b>
<b class="nc">&nbsp; disconnectionTimer = null;</b>
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Populates the event with the current game state so that clients can
&nbsp; * update their mini-model after receiving it.
&nbsp; *
&nbsp; * &lt;p&gt;Each event subclass overrides {@link NetworkEvent#enrichWithGameState}
&nbsp; * to append any type-specific extra fields (available totems, etc.).
&nbsp; *
&nbsp; * @param event the event to enrich.
&nbsp; * @param game the current game model.
&nbsp; */
&nbsp; private void enrichEvent(NetworkEvent event, Game game) {
<b class="nc">&nbsp; event.enrichWithGameState(game, buildDisconnectedList(game));</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Schedules the 60-second forfeit timer if a disconnection has left
&nbsp; * exactly one player online.
&nbsp; *
&nbsp; * &lt;p&gt;If a previous timer is still pending it is canceled first to avoid
&nbsp; * duplicate timers.
&nbsp; *
&nbsp; * @param event the event that was just applied.
&nbsp; * @param game the current game model.
&nbsp; */
&nbsp; private void startForfeitTimerIfNeeded(NetworkEvent event, Game game) {
<b class="nc">&nbsp; if (event.getEventType() != EventType.DISCONNECTED_PLAYER) return;</b>
<b class="nc">&nbsp; if (game.getCurrentState().getGameStage() == GameStages.ENDED) return;</b>
<b class="nc">&nbsp; if (onlinePlayerCount() != 1) return;</b>
&nbsp;
<b class="nc">&nbsp; if (disconnectionTimer != null &amp;&amp; !disconnectionTimer.isDone()) {</b>
<b class="nc">&nbsp; disconnectionTimer.cancel(false);</b>
<b class="nc">&nbsp; System.out.println(&quot;Disconnection TIMER reset&quot;);</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; disconnectionTimer = timerExecutor.schedule(</b>
<b class="nc">&nbsp; () -&gt; endGameForfeit(game),</b>
&nbsp; 1, TimeUnit.MINUTES
&nbsp; );
<b class="nc">&nbsp; System.out.println(&quot;Disconnection TIMER started, 60 seconds from now...&quot; );</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Ends the game by forfeit when the timer expires without a reconnection.
&nbsp; * Broadcasts an {@link EndedGame} event, deletes the save, and resets state.
&nbsp; *
&nbsp; * @param game the game model captured when the timer was scheduled.
&nbsp; */
&nbsp; private void endGameForfeit(Game game) {
<b class="nc">&nbsp; synchronized (gameController) {</b>
<b class="nc">&nbsp; gameController.endGameForfeit();</b>
<b class="nc">&nbsp; EndedGame forfeitEnd = new EndedGame(</b>
<b class="nc">&nbsp; game.getSlotMap(), game.getOrderLogicCard(),</b>
<b class="nc">&nbsp; game.getCurrentState(), game.getPlayerStanding()</b>
&nbsp; );
<b class="nc">&nbsp; broadcaster.notifyAll(forfeitEnd);</b>
<b class="nc">&nbsp; System.out.println(&quot;Timer expired: no player reconnected in 60 s.&quot;);</b>
<b class="nc">&nbsp; removeOfflinePlayers();</b>
<b class="nc">&nbsp; if (!saveManager.delete()) {</b>
<b class="nc">&nbsp; System.out.println(&quot;\n!!! Couldn&#39;t delete save !!!\n&quot;);</b>
&nbsp; }
<b class="nc">&nbsp; disconnectionTimer = null;</b>
<b class="nc">&nbsp; }</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Determines what to broadcast after a successful event application:
&nbsp; * &lt;ul&gt;
&nbsp; * &lt;li&gt;If the round advanced, an {@link ApplyNextRound} event (with updated
&nbsp; * card lists) replaces the original event.&lt;/li&gt;
&nbsp; * &lt;li&gt;If the game has ended, an {@link EndedGame} event is sent and the
&nbsp; * save file is deleted.&lt;/li&gt;
&nbsp; * &lt;li&gt;Otherwise the original event is broadcast as-is.&lt;/li&gt;
&nbsp; * &lt;/ul&gt;
&nbsp; *
&nbsp; * @param event the event that was applied.
&nbsp; * @param game the current game model (post-apply).
&nbsp; * @param roundBefore the round number before the event was applied.
&nbsp; */
&nbsp; private void broadcastResult(NetworkEvent event, Game game, int roundBefore) {
<b class="nc">&nbsp; broadcaster.notifyAll(event);</b>
<b class="nc">&nbsp; if (game.getCurrentState().getRound() != roundBefore) {</b>
<b class="nc">&nbsp; ApplyNextRound nextRound = new ApplyNextRound(</b>
<b class="nc">&nbsp; game.getSlotMap(), game.getOrderLogicCard(), game.getCurrentState(),</b>
<b class="nc">&nbsp; game.getPlayers(),</b>
<b class="nc">&nbsp; game.getUpperListTribeCards(), game.getLowerListTribeCards(),</b>
<b class="nc">&nbsp; game.getUpperListBuilding(), game.getLowerListBuilding()</b>
&nbsp; );
<b class="nc">&nbsp; broadcaster.notifyAll(nextRound);</b>
<b class="nc">&nbsp; } else if (game.getCurrentState().getGameStage() == GameStages.ENDED) {</b>
<b class="nc">&nbsp; EndedGame endedGame = new EndedGame(</b>
<b class="nc">&nbsp; game.getSlotMap(), game.getOrderLogicCard(),</b>
<b class="nc">&nbsp; game.getCurrentState(), game.getPlayerStanding()</b>
&nbsp; );
<b class="nc">&nbsp; broadcaster.notifyAll(endedGame);</b>
<b class="nc">&nbsp; if (!saveManager.delete()) {</b>
<b class="nc">&nbsp; System.out.println(&quot;\n!!! Couldn&#39;t delete save !!!\n&quot;);</b>
&nbsp; }
<b class="nc">&nbsp; removeOfflinePlayers();</b>
&nbsp; }
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Returns the number of players currently marked as online in the player list.
&nbsp; */
&nbsp; private long onlinePlayerCount() {
<b class="nc">&nbsp; return playerList.values().stream().filter(v -&gt; v).count();</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Builds the list of usernames of players currently marked as disconnected
&nbsp; * in the game model.
&nbsp; *
&nbsp; * @param game the current game model.
&nbsp; * @return a new {@link ArrayList} of disconnected usernames.
&nbsp; */
&nbsp; private ArrayList&lt;String&gt; buildDisconnectedList(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;
&nbsp; /**
&nbsp; * Removes all offline entries (value {@code false}) from the player list.
&nbsp; * Online players remain until they disconnect naturally.
&nbsp; */
&nbsp; private void removeOfflinePlayers() {
<b class="nc">&nbsp; List&lt;String&gt; toRemove = playerList.entrySet().stream()</b>
<b class="nc">&nbsp; .filter(e -&gt; !e.getValue())</b>
<b class="nc">&nbsp; .map(Map.Entry::getKey)</b>
<b class="nc">&nbsp; .toList();</b>
<b class="nc">&nbsp; toRemove.forEach(playerList::remove);</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>