Files
2026-06-19 22:57:43 +02:00

336 lines
14 KiB
HTML

<!DOCTYPE html>
<html id="htmlId">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Coverage Report > ServerLauncher</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: ServerLauncher (it.polimi.ingsw.gc14)</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">ServerLauncher</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/24)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/71)
</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.MiniModel;
&nbsp;import it.polimi.ingsw.gc14.Model.Player;
&nbsp;import it.polimi.ingsw.gc14.Network.CompositeClientBroadcaster;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkEvent;
&nbsp;import it.polimi.ingsw.gc14.Network.RMI.Server.RMIServer;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkConfig;
&nbsp;import it.polimi.ingsw.gc14.Network.TCP.Server.TCPServer;
&nbsp;
&nbsp;import java.net.*;
&nbsp;import java.rmi.RemoteException;
&nbsp;import java.util.*;
&nbsp;import java.util.concurrent.*;
&nbsp;import java.util.stream.Collectors;
&nbsp;
&nbsp;/**
&nbsp; * Main server entry point.
&nbsp; *
&nbsp; * &lt;p&gt;Responsibilities of this class are intentionally limited to wiring:
&nbsp; * it creates all components, connects them together, restores a previously
&nbsp; * saved game if one exists, and starts the event-processing loop and the
&nbsp; * network servers.
&nbsp; *
&nbsp; * &lt;p&gt;All game-logic and event-routing decisions are delegated to
&nbsp; * {@link GameEventProcessor}; persistence is delegated to {@link SaveManager}.
&nbsp; *
&nbsp; * &lt;p&gt;The original {@code ServerLauncher} class is preserved and untouched.
&nbsp; * This class is a clean replacement that uses the new component structure.
&nbsp; */
&nbsp;public class ServerLauncher {
&nbsp;
&nbsp; /** Drives the main game-event loop. */
&nbsp; private final GameEventProcessor eventProcessor;
&nbsp;
&nbsp; /**
&nbsp; * Constructs a {@code ServerLauncherTest} and wires all components together.
&nbsp; *
&nbsp; * @param actionQueue the shared event queue.
&nbsp; * @param gameController the server-side game controller.
&nbsp; * @param playerList the shared player-status map.
&nbsp; * @param broadcaster the broadcaster used to reach all connected clients.
&nbsp; * @param saveManager the save manager used to persist game state.
&nbsp; */
&nbsp; public ServerLauncher(
&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.eventProcessor = new GameEventProcessor(</b>
&nbsp; actionQueue, gameController, playerList, broadcaster, saveManager);
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Initializes all server components, wires them together, restores a saved
&nbsp; * game if available, and starts the event-processing loop and both network
&nbsp; * servers.
&nbsp; *
&nbsp; * @param args command-line arguments (unused).
&nbsp; * @throws RemoteException if the RMI server cannot be created.
&nbsp; */
&nbsp; static void main(String[] args) throws RemoteException {
<b class="nc">&nbsp; LimitedMap&lt;String, Boolean&gt; playerList = new LimitedMap&lt;&gt;(5, () -&gt; {});</b>
<b class="nc">&nbsp; BlockingQueue&lt;NetworkEvent&gt; actionQueue = new LinkedBlockingQueue&lt;&gt;();</b>
<b class="nc">&nbsp; GameController gameController = new GameController();</b>
&nbsp;
&nbsp; String ip;
&nbsp; try {
<b class="nc">&nbsp; ip = chooseNetworkInterface(new Scanner(System.in));</b>
<b class="nc">&nbsp; System.out.println(ip);</b>
&nbsp; } catch (Exception e) {
<b class="nc">&nbsp; throw new RuntimeException(e);</b>
&nbsp; }
<b class="nc">&nbsp; System.setProperty(&quot;java.rmi.server.hostname&quot;, ip);</b>
&nbsp;
<b class="nc">&nbsp; RMIServer rmiServer = new RMIServer(gameController, NetworkConfig.RMI_PORT, actionQueue, playerList, ip);</b>
<b class="nc">&nbsp; TCPServer tcpServer = new TCPServer(gameController, NetworkConfig.TCP_PORT, NetworkConfig.HEARTBEAT_PORT, actionQueue, playerList);</b>
<b class="nc">&nbsp; CompositeClientBroadcaster broadcaster = new CompositeClientBroadcaster(rmiServer, tcpServer);</b>
<b class="nc">&nbsp; SaveManager saveManager = new SaveManager(ServerLauncher.class);</b>
&nbsp;
<b class="nc">&nbsp; restoreGameIfSaved(gameController, playerList, saveManager);</b>
&nbsp;
<b class="nc">&nbsp; ServerLauncher launcher = new ServerLauncher(</b>
&nbsp; actionQueue, gameController, playerList, broadcaster, saveManager);
&nbsp;
&nbsp; // When all required players have joined, broadcast the initial MiniModel.
<b class="nc">&nbsp; playerList.setAction(() -&gt; new Thread(() -&gt; {</b>
&nbsp; MiniModel miniModel;
<b class="nc">&nbsp; synchronized (gameController) {</b>
<b class="nc">&nbsp; Game game = gameController.getModel();</b>
<b class="nc">&nbsp; miniModel = new MiniModel(</b>
<b class="nc">&nbsp; game.getSlotMap(), game.getOrderLogicCard(), game.getCurrentState(),</b>
<b class="nc">&nbsp; game.getPlayers(), 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; 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; );
<b class="nc">&nbsp; }</b>
<b class="nc">&nbsp; broadcaster.notifyAll(miniModel);</b>
<b class="nc">&nbsp; }).start());</b>
&nbsp;
<b class="nc">&nbsp; new Thread(() -&gt; {</b>
<b class="nc">&nbsp; try { launcher.run(); }</b>
&nbsp; catch (Exception e) {
<b class="nc">&nbsp; System.out.println(&quot;Unexpected exception in game loop&quot;);</b>
<b class="nc">&nbsp; e.printStackTrace();</b>
&nbsp; }
<b class="nc">&nbsp; }).start();</b>
&nbsp;
<b class="nc">&nbsp; rmiServer.start();</b>
<b class="nc">&nbsp; new Thread(() -&gt; tcpServer.start()).start();</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Runs the event-processing loop until the thread is interrupted.
&nbsp; *
&nbsp; * &lt;p&gt;A {@link ConcurrentModificationException} is caught and logged rather
&nbsp; */
&nbsp; public void run() {
&nbsp; while (true) {
&nbsp; try {
<b class="nc">&nbsp; eventProcessor.doFirstEvent();</b>
&nbsp; } catch (InterruptedException e) {
<b class="nc">&nbsp; Thread.currentThread().interrupt();</b>
&nbsp; break;
&nbsp; }
&nbsp; }
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Attempts to restore a previously saved game.
&nbsp; *
&nbsp; * &lt;p&gt;The save is discarded if all but at most one player was offline at the
&nbsp; * time of the crash, since there would be nobody to resume the game with.
&nbsp; * Otherwise, the model is restored, the player-list limit is set, and each
&nbsp; * player who was offline at crash time is pre-populated as offline so the
&nbsp; * reconnection flow can handle them correctly.
&nbsp; *
&nbsp; * @param gameController the controller that will receive the restored model.
&nbsp; * @param playerList the player-status map to populate.
&nbsp; * @param saveManager the save manager to load from.
&nbsp; */
&nbsp; private static void restoreGameIfSaved(
&nbsp; GameController gameController,
&nbsp; LimitedMap&lt;String, Boolean&gt; playerList,
&nbsp; SaveManager saveManager) {
&nbsp;
<b class="nc">&nbsp; Game game = saveManager.load();</b>
<b class="nc">&nbsp; if (game == null) return;</b>
&nbsp;
<b class="nc">&nbsp; long disconnectedCount = game.getDisconnectedPlayers().entrySet().stream()</b>
<b class="nc">&nbsp; .filter(Map.Entry::getValue).count();</b>
&nbsp;
<b class="nc">&nbsp; if (disconnectedCount &gt;= game.getNPlayers() - 1) {</b>
<b class="nc">&nbsp; saveManager.delete();</b>
<b class="nc">&nbsp; System.out.println(&quot;Save discarded: too many players were offline at crash time.&quot;);</b>
&nbsp; return;
&nbsp; }
&nbsp;
<b class="nc">&nbsp; gameController.setModel(game);</b>
<b class="nc">&nbsp; playerList.setLimit(game.getNPlayers());</b>
&nbsp;
<b class="nc">&nbsp; for (Map.Entry&lt;Player, Boolean&gt; entry : game.getDisconnectedPlayers().entrySet()) {</b>
<b class="nc">&nbsp; if (entry.getValue()) {</b>
<b class="nc">&nbsp; playerList.put(entry.getKey().getUserName(), false);</b>
&nbsp; }
&nbsp; }
<b class="nc">&nbsp; System.out.println(&quot;Game restored from save (&quot; + game.getNPlayers() + &quot; players).&quot;);</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Lists active non-loopback IPv4 network interfaces and prompts the operator
&nbsp; * to choose one. If only one interface is available it is selected automatically.
&nbsp; *
&nbsp; * @param scanner the scanner used to read the operator&#39;s choice.
&nbsp; * @return the IPv4 address of the selected interface.
&nbsp; * @throws Exception if no valid network interface is available.
&nbsp; */
&nbsp; public static String chooseNetworkInterface(Scanner scanner) throws Exception {
<b class="nc">&nbsp; List&lt;String&gt; ips = new ArrayList&lt;&gt;();</b>
&nbsp;
<b class="nc">&nbsp; Enumeration&lt;NetworkInterface&gt; interfaces = NetworkInterface.getNetworkInterfaces();</b>
<b class="nc">&nbsp; while (interfaces.hasMoreElements()) {</b>
<b class="nc">&nbsp; NetworkInterface ni = interfaces.nextElement();</b>
<b class="nc">&nbsp; if (!ni.isUp() || ni.isLoopback() || ni.isVirtual()) continue;</b>
&nbsp;
<b class="nc">&nbsp; Enumeration&lt;InetAddress&gt; addresses = ni.getInetAddresses();</b>
<b class="nc">&nbsp; while (addresses.hasMoreElements()) {</b>
<b class="nc">&nbsp; InetAddress addr = addresses.nextElement();</b>
<b class="nc">&nbsp; if (addr instanceof Inet4Address) {</b>
<b class="nc">&nbsp; System.out.println(&quot;[&quot; + ips.size() + &quot;] &quot; + ni.getDisplayName()</b>
<b class="nc">&nbsp; + &quot; -&gt; &quot; + addr.getHostAddress());</b>
<b class="nc">&nbsp; ips.add(addr.getHostAddress());</b>
&nbsp; }
&nbsp; }
&nbsp; }
&nbsp;
<b class="nc">&nbsp; if (ips.isEmpty()) throw new Exception(&quot;No active network interface available&quot;);</b>
&nbsp;
<b class="nc">&nbsp; if (ips.size() == 1) {</b>
<b class="nc">&nbsp; System.out.println(&quot;One interface found, using: &quot; + ips.get(0));</b>
<b class="nc">&nbsp; return ips.get(0);</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; System.out.print(&quot;Choose interface: &quot;);</b>
<b class="nc">&nbsp; int choice = Integer.parseInt(scanner.nextLine().trim());</b>
<b class="nc">&nbsp; return ips.get(choice);</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>