Add: Added Coverage Screenshots And htlmReport.
This commit is contained in:
@@ -0,0 +1,523 @@
|
||||
|
||||
|
||||
|
||||
<!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/107)
|
||||
</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/108)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<pre>
|
||||
<code class="sourceCode" id="sourceCode"> package it.polimi.ingsw.gc14;
|
||||
|
||||
import it.polimi.ingsw.gc14.Controller.GameController;
|
||||
import it.polimi.ingsw.gc14.Model.Game;
|
||||
import it.polimi.ingsw.gc14.Model.GamePackage.GameStages;
|
||||
import it.polimi.ingsw.gc14.Network.CompositeClientBroadcaster;
|
||||
import it.polimi.ingsw.gc14.Network.EventType;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvents.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Processes game events from the action queue and routes each one
|
||||
* based on the current game state.
|
||||
*
|
||||
* <p>The three top-level states that determine routing are:
|
||||
* <ul>
|
||||
* <li><b>Inactive</b> – no game model is present, or the game has ended.
|
||||
* Only disconnection clean-up is performed.</li>
|
||||
* <li><b>Suspended</b> – a forfeit timer is running because exactly one
|
||||
* player remains online. Only reconnection events are accepted.</li>
|
||||
* <li><b>Active</b> – normal gameplay; every event is applied, saved,
|
||||
* and broadcast.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>This class is not thread-safe by itself: it relies on the caller
|
||||
* (the game loop in {@code ServerLauncher}) to drive it from a single
|
||||
* thread via {@link #doFirstEvent()}.
|
||||
*/
|
||||
public class GameEventProcessor {
|
||||
|
||||
private final BlockingQueue<NetworkEvent> actionQueue;
|
||||
private final GameController gameController;
|
||||
private final LimitedMap<String, Boolean> playerList;
|
||||
private final CompositeClientBroadcaster broadcaster;
|
||||
private final SaveManager saveManager;
|
||||
|
||||
/** Single-thread executor used exclusively for the forfeit timer. Daemon so it does not block JVM shutdown. */
|
||||
<b class="nc"> private final ScheduledExecutorService timerExecutor =</b>
|
||||
<b class="nc"> Executors.newSingleThreadScheduledExecutor(r -> {</b>
|
||||
<b class="nc"> Thread t = new Thread(r, "forfeit-timer");</b>
|
||||
<b class="nc"> t.setDaemon(true);</b>
|
||||
<b class="nc"> return t;</b>
|
||||
});
|
||||
|
||||
/**
|
||||
* Handle to the running forfeit timer, or {@code null} when no timer is active.
|
||||
* A non-null value signals that the game is in the {@em suspended} state.
|
||||
*/
|
||||
private ScheduledFuture<?> disconnectionTimer;
|
||||
|
||||
/**
|
||||
* Constructs a {@code GameEventProcessor} with all required dependencies.
|
||||
*
|
||||
* @param actionQueue the queue from which incoming events are consumed.
|
||||
* @param gameController the server-side game controller.
|
||||
* @param playerList the shared map tracking each player's online status.
|
||||
* @param broadcaster the broadcaster used to notify all connected clients.
|
||||
* @param saveManager the save manager used to persist the game state.
|
||||
*/
|
||||
public GameEventProcessor(
|
||||
BlockingQueue<NetworkEvent> actionQueue,
|
||||
GameController gameController,
|
||||
LimitedMap<String, Boolean> playerList,
|
||||
CompositeClientBroadcaster broadcaster,
|
||||
<b class="nc"> SaveManager saveManager) {</b>
|
||||
<b class="nc"> this.actionQueue = actionQueue;</b>
|
||||
<b class="nc"> this.gameController = gameController;</b>
|
||||
<b class="nc"> this.playerList = playerList;</b>
|
||||
<b class="nc"> this.broadcaster = broadcaster;</b>
|
||||
<b class="nc"> this.saveManager = saveManager;</b>
|
||||
}
|
||||
|
||||
// ── Public entry point ────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Blocks until one event is available in the queue, then routes it to the
|
||||
* appropriate handler based on the current game state.
|
||||
*
|
||||
* @throws InterruptedException if the thread is interrupted while waiting
|
||||
* for the next event.
|
||||
*/
|
||||
public void doFirstEvent() throws InterruptedException {
|
||||
<b class="nc"> NetworkEvent event = actionQueue.take();</b>
|
||||
|
||||
<b class="nc"> if (!isGameActive()) {</b>
|
||||
<b class="nc"> handleInactiveGame(event);</b>
|
||||
<b class="nc"> } else if (isSuspended()) {</b>
|
||||
<b class="nc"> handleSuspendedGame(event);</b>
|
||||
} else {
|
||||
<b class="nc"> applyAndBroadcast(event);</b>
|
||||
}
|
||||
}
|
||||
|
||||
// ── State guards ──────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Returns {@code true} when there is an ongoing game that has not yet ended.
|
||||
*/
|
||||
private boolean isGameActive() {
|
||||
<b class="nc"> Game model = gameController.getModel();</b>
|
||||
<b class="nc"> return model != null</b>
|
||||
<b class="nc"> && model.getCurrentState().getGameStage() != GameStages.ENDED;</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} when the forfeit timer is running, meaning only one
|
||||
* player is currently online and the game is waiting for a reconnection.
|
||||
*/
|
||||
private boolean isSuspended() {
|
||||
<b class="nc"> return disconnectionTimer != null;</b>
|
||||
}
|
||||
|
||||
// ── Inactive game path ────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Handles events that arrive when no active game exists (not yet started,
|
||||
* or already ended). Only disconnection clean-up is relevant here.
|
||||
*
|
||||
* @param event the incoming event.
|
||||
*/
|
||||
private void handleInactiveGame(NetworkEvent event) {
|
||||
<b class="nc"> if (event.getEventType() != EventType.DISCONNECTED_PLAYER) return;</b>
|
||||
|
||||
<b class="nc"> synchronized (gameController) {</b>
|
||||
<b class="nc"> playerList.remove(event.getUsername());</b>
|
||||
<b class="nc"> if (playerList.isEmpty()) {</b>
|
||||
<b class="nc"> gameController.setModel(null);</b>
|
||||
<b class="nc"> System.out.println("\n!!! Player list is now empty, ready for a new game init !!!\n");</b>
|
||||
}
|
||||
<b class="nc"> }</b>
|
||||
}
|
||||
|
||||
// ── Suspended game path ───────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Routes events while the game is suspended waiting for a reconnection.
|
||||
* <ul>
|
||||
* <li>A second disconnection while suspended means no player remains
|
||||
* online: the game is aborted entirely.</li>
|
||||
* <li>Any non-reconnection event is rejected with an error.</li>
|
||||
* <li>A reconnection event is allowed through to {@link #applyAndBroadcast}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param event the incoming event.
|
||||
*/
|
||||
private void handleSuspendedGame(NetworkEvent event) {
|
||||
<b class="nc"> switch (event.getEventType()) {</b>
|
||||
<b class="nc"> case DISCONNECTED_PLAYER -> abortGame();</b>
|
||||
<b class="nc"> case RECONNECT_PLAYER -> applyAndBroadcast(event);</b>
|
||||
<b class="nc"> default -> rejectEvent(event);</b>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the forfeit timer, clears the player list, and resets the model.
|
||||
* Called when the last remaining player disconnects while the game is suspended.
|
||||
*/
|
||||
private void abortGame() {
|
||||
<b class="nc"> disconnectionTimer.cancel(true);</b>
|
||||
<b class="nc"> disconnectionTimer = null;</b>
|
||||
<b class="nc"> playerList.clear();</b>
|
||||
<b class="nc"> gameController.setModel(null);</b>
|
||||
<b class="nc"> System.out.println("\n!!! All players disconnected — game aborted, ready for a new game init !!!\n");</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the event as an error and broadcasts it back to the requesting
|
||||
* player. Used to reject actions that are not permitted in the current state.
|
||||
*
|
||||
* @param event the event to reject.
|
||||
*/
|
||||
private void rejectEvent(NetworkEvent event) {
|
||||
<b class="nc"> event.setIsError(true);</b>
|
||||
<b class="nc"> broadcaster.notifyAll(event);</b>
|
||||
}
|
||||
|
||||
// ── Active game path ──────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Applies the event to the game controller, saves the updated state,
|
||||
* and broadcasts the result to connected clients.
|
||||
*
|
||||
* <p>The entire method body is synchronized on {@code gameController} to
|
||||
* prevent concurrent modification of the game model by the network threads.
|
||||
*
|
||||
* @param event the event to apply.
|
||||
*/
|
||||
private void applyAndBroadcast(NetworkEvent event) {
|
||||
<b class="nc"> synchronized (gameController) {</b>
|
||||
<b class="nc"> int roundBefore = gameController.getModel().getCurrentState().getRound();</b>
|
||||
|
||||
<b class="nc"> event.setIsError(!event.apply(gameController));</b>
|
||||
<b class="nc"> Game game = gameController.getModel();</b>
|
||||
|
||||
<b class="nc"> if (event.isError()) {</b>
|
||||
<b class="nc"> broadcaster.notifyAll(event);</b>
|
||||
<b class="nc"> return;</b>
|
||||
}
|
||||
|
||||
<b class="nc"> cancelForfeitTimerIfReconnect(event);</b>
|
||||
|
||||
<b class="nc"> if (!saveManager.save(game)) {</b>
|
||||
<b class="nc"> System.out.println("\n!!! Save failed !!!\n");</b>
|
||||
}
|
||||
|
||||
// During the lobby phase a disconnection only removes the player
|
||||
// from the list; no broadcast is needed.
|
||||
<b class="nc"> if (event.getEventType() == EventType.DISCONNECTED_PLAYER</b>
|
||||
<b class="nc"> && game.getCurrentState().getGameStage() == GameStages.WAITING) {</b>
|
||||
<b class="nc"> playerList.remove(event.getUsername());</b>
|
||||
<b class="nc"> return;</b>
|
||||
}
|
||||
|
||||
<b class="nc"> enrichEvent(event, game);</b>
|
||||
<b class="nc"> startForfeitTimerIfNeeded(event, game);</b>
|
||||
<b class="nc"> broadcastResult(event, game, roundBefore);</b>
|
||||
<b class="nc"> }</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the forfeit timer if the event is a successful reconnection.
|
||||
*
|
||||
* @param event the event that was just successfully applied.
|
||||
*/
|
||||
private void cancelForfeitTimerIfReconnect(NetworkEvent event) {
|
||||
<b class="nc"> if (event.getEventType() == EventType.RECONNECT_PLAYER</b>
|
||||
&& disconnectionTimer != null
|
||||
<b class="nc"> && !disconnectionTimer.isDone()) {</b>
|
||||
<b class="nc"> disconnectionTimer.cancel(false);</b>
|
||||
<b class="nc"> disconnectionTimer = null;</b>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the event with the current game state so that clients can
|
||||
* update their mini-model after receiving it.
|
||||
*
|
||||
* <p>Each event subclass overrides {@link NetworkEvent#enrichWithGameState}
|
||||
* to append any type-specific extra fields (available totems, etc.).
|
||||
*
|
||||
* @param event the event to enrich.
|
||||
* @param game the current game model.
|
||||
*/
|
||||
private void enrichEvent(NetworkEvent event, Game game) {
|
||||
<b class="nc"> event.enrichWithGameState(game, buildDisconnectedList(game));</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the 60-second forfeit timer if a disconnection has left
|
||||
* exactly one player online.
|
||||
*
|
||||
* <p>If a previous timer is still pending it is cancelled first to avoid
|
||||
* duplicate timers.
|
||||
*
|
||||
* @param event the event that was just applied.
|
||||
* @param game the current game model.
|
||||
*/
|
||||
private void startForfeitTimerIfNeeded(NetworkEvent event, Game game) {
|
||||
<b class="nc"> if (event.getEventType() != EventType.DISCONNECTED_PLAYER) return;</b>
|
||||
<b class="nc"> if (game.getCurrentState().getGameStage() == GameStages.ENDED) return;</b>
|
||||
<b class="nc"> if (onlinePlayerCount() != 1) return;</b>
|
||||
|
||||
<b class="nc"> if (disconnectionTimer != null && !disconnectionTimer.isDone()) {</b>
|
||||
<b class="nc"> disconnectionTimer.cancel(false);</b>
|
||||
}
|
||||
|
||||
<b class="nc"> disconnectionTimer = timerExecutor.schedule(</b>
|
||||
<b class="nc"> () -> endGameForfeit(game),</b>
|
||||
1, TimeUnit.MINUTES
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the game by forfeit when the timer expires without a reconnection.
|
||||
* Broadcasts an {@link EndedGame} event, deletes the save, and resets state.
|
||||
*
|
||||
* @param game the game model captured when the timer was scheduled.
|
||||
*/
|
||||
private void endGameForfeit(Game game) {
|
||||
<b class="nc"> synchronized (gameController) {</b>
|
||||
<b class="nc"> gameController.endGameForfeit();</b>
|
||||
<b class="nc"> EndedGame forfeitEnd = new EndedGame(</b>
|
||||
<b class="nc"> game.getSlotMap(), game.getOrderLogicCard(),</b>
|
||||
<b class="nc"> game.getCurrentState(), game.getPlayerStanding()</b>
|
||||
);
|
||||
<b class="nc"> broadcaster.notifyAll(forfeitEnd);</b>
|
||||
<b class="nc"> System.out.println("Timer expired: no player reconnected in 60 s.");</b>
|
||||
<b class="nc"> removeOfflinePlayers();</b>
|
||||
<b class="nc"> if (!saveManager.delete()) {</b>
|
||||
<b class="nc"> System.out.println("\n!!! Couldn't delete save !!!\n");</b>
|
||||
}
|
||||
<b class="nc"> disconnectionTimer = null;</b>
|
||||
<b class="nc"> }</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines what to broadcast after a successful event application:
|
||||
* <ul>
|
||||
* <li>If the round advanced, an {@link ApplyNextRound} event (with updated
|
||||
* card lists) replaces the original event.</li>
|
||||
* <li>If the game has ended, an {@link EndedGame} event is sent and the
|
||||
* save file is deleted.</li>
|
||||
* <li>Otherwise the original event is broadcast as-is.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param event the event that was applied.
|
||||
* @param game the current game model (post-apply).
|
||||
* @param roundBefore the round number before the event was applied.
|
||||
*/
|
||||
private void broadcastResult(NetworkEvent event, Game game, int roundBefore) {
|
||||
<b class="nc"> broadcaster.notifyAll(event);</b>
|
||||
<b class="nc"> if (game.getCurrentState().getRound() != roundBefore) {</b>
|
||||
<b class="nc"> ApplyNextRound nextRound = new ApplyNextRound(</b>
|
||||
<b class="nc"> game.getSlotMap(), game.getOrderLogicCard(), game.getCurrentState(),</b>
|
||||
<b class="nc"> game.getPlayers(),</b>
|
||||
<b class="nc"> game.getUpperListTribeCards(), game.getLowerListTribeCards(),</b>
|
||||
<b class="nc"> game.getUpperListBuilding(), game.getLowerListBuilding()</b>
|
||||
);
|
||||
<b class="nc"> broadcaster.notifyAll(nextRound);</b>
|
||||
<b class="nc"> } else if (game.getCurrentState().getGameStage() == GameStages.ENDED) {</b>
|
||||
<b class="nc"> EndedGame endedGame = new EndedGame(</b>
|
||||
<b class="nc"> game.getSlotMap(), game.getOrderLogicCard(),</b>
|
||||
<b class="nc"> game.getCurrentState(), game.getPlayerStanding()</b>
|
||||
);
|
||||
<b class="nc"> broadcaster.notifyAll(endedGame);</b>
|
||||
<b class="nc"> if (!saveManager.delete()) {</b>
|
||||
<b class="nc"> System.out.println("\n!!! Couldn't delete save !!!\n");</b>
|
||||
}
|
||||
<b class="nc"> removeOfflinePlayers();</b>
|
||||
}
|
||||
}
|
||||
|
||||
// ── Utilities ─────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Returns the number of players currently marked as online in the player list.
|
||||
*/
|
||||
private long onlinePlayerCount() {
|
||||
<b class="nc"> return playerList.values().stream().filter(v -> v).count();</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the list of usernames of players currently marked as disconnected
|
||||
* in the game model.
|
||||
*
|
||||
* @param game the current game model.
|
||||
* @return a new {@link ArrayList} of disconnected usernames.
|
||||
*/
|
||||
private ArrayList<String> buildDisconnectedList(Game game) {
|
||||
<b class="nc"> return game.getDisconnectedPlayers().entrySet().stream()</b>
|
||||
<b class="nc"> .filter(Map.Entry::getValue)</b>
|
||||
<b class="nc"> .map(e -> e.getKey().getUserName())</b>
|
||||
<b class="nc"> .collect(Collectors.toCollection(ArrayList::new));</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all offline entries (value {@code false}) from the player list.
|
||||
* Online players remain until they disconnect naturally.
|
||||
*/
|
||||
private void removeOfflinePlayers() {
|
||||
<b class="nc"> List<String> toRemove = playerList.entrySet().stream()</b>
|
||||
<b class="nc"> .filter(e -> !e.getValue())</b>
|
||||
<b class="nc"> .map(Map.Entry::getKey)</b>
|
||||
<b class="nc"> .toList();</b>
|
||||
<b class="nc"> toRemove.forEach(playerList::remove);</b>
|
||||
}
|
||||
}
|
||||
</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>
|
||||
Reference in New Issue
Block a user