Coverage Summary for Class: RMIClient (it.polimi.ingsw.gc14.Network.RMI.Client)

Class Class, % Method, % Branch, % Line, %
RMIClient 0% (0/1) 0% (0/15) 0% (0/12) 0% (0/60)


 package it.polimi.ingsw.gc14.Network.RMI.Client;
 import java.rmi.RemoteException;
 import java.rmi.registry.LocateRegistry;
 import java.rmi.registry.Registry;
 import java.util.concurrent.*;
 
 import it.polimi.ingsw.gc14.Controller.ClientController;
 import it.polimi.ingsw.gc14.ErrorType;
 import it.polimi.ingsw.gc14.Network.IClient;
 import it.polimi.ingsw.gc14.Network.NetworkConfig;
 import it.polimi.ingsw.gc14.Network.RMI.Common.IGameServer;
 
 /**
  * Client RMI. Uses the methods exposed by the server RMI.
  */
 public class RMIClient  implements IClient {
     /** Hostname or IP address of the RMI server. */
     private final String host;
     /** Port of the RMI registry. */
     private final int port;
     /** Remote stub obtained from the RMI registry after a successful connection. */
     private IGameServer stub;
     /** Client-side controller that receives and applies incoming game events. */
     private final ClientController controller;
     /** Local IP address advertised to the RMI runtime so callbacks are routed correctly. */
     private final String myIp;
     /** Username of the currently connected player; set during {@link #connect}. */
     private String username;
     /** {@code true} while the heartbeat loop is active; set to {@code false} on disconnect. */
     private volatile boolean running = false;
 
     /** Scheduler that fires ping() every {@value NetworkConfig#KEEPALIVE_INTERVAL_MS} ms. */
     private ScheduledExecutorService pingSender;
     /**
      * Future executor used for timer Running
      */
     private ExecutorService executor;
 
     /**
      * Constructs an RMI client and initializes its connection parameters.
      *
      * @param controller the client controller associated with this RMI client.
      * @param host       the hostname or IP address of the RMI server.
      * @param port       the port used to connect to the RMI registry.
      * @param myIp       the IP address of the client.
      */
     public RMIClient(ClientController controller, String host, int port, String myIp) {
         this.controller = controller;
         this.host = host;
         this.port = port;
         this.myIp = myIp;
     }
 
 
     /**
      * Connects to the RMI server and starts the heartbeat loop.
      *
      * <p>Mirrors {@code TCPClient.connect()}: after a successful join the
      * heartbeat channel is opened (here: a scheduler is started instead of
      * opening a second socket).
      */
     @Override
     public ErrorType connect(String username, int proposedNPlayers) {
         try {
             System.setProperty("java.rmi.server.hostname", this.myIp);
             Registry registry = LocateRegistry.getRegistry(host, port);
             this.stub = (IGameServer) registry.lookup("RMIGameServer");
             this.username = username;
 
             ClientCallbackImpl callback = new ClientCallbackImpl(controller);
             ErrorType status = stub.joinGame(username, proposedNPlayers, callback);
             if(status==null)
             {
                 running = true;
                 startHeartbeat();
                 return null;
             }
             return status;
         } catch (Exception e) {
             e.printStackTrace();
             return ErrorType.SERVER_UNREACHABLE;
         }
     }
 
     // -------------------------------------------------------------------------
     // Heartbeat — mirrors TCPClient.heartbeatLoop()
     // -------------------------------------------------------------------------
 
     /**
      * Starts sending periodic pings to the server.
      *
      * <p>Mirrors the {@code ScheduledExecutorService} in
      * {@code TCPClient.heartbeatLoop()} that writes {@code PING} every 3 s.
      * On {@link RemoteException} the server is considered gone and
      * {@link #disconnect()} is called — mirrors the behavior on
      * {@code SocketTimeoutException} / {@code IOException} in the TCP version.
      */
     private void startHeartbeat() {
         pingSender = Executors.newSingleThreadScheduledExecutor(r -> {
             Thread t = new Thread(r, "rmi-heartbeat");
             t.setDaemon(true);
             return t;
         });
 
         executor = Executors.newSingleThreadExecutor();
         pingSender.scheduleAtFixedRate(() -> {
             Future<?> future = executor.submit(() -> {
                 try {
                     stub.ping(username);
                 } catch (RemoteException e) {
                     disconnect();
                     controller.getView().showError(ErrorType.SERVER_CRASHED,ErrorType.SERVER_CRASHED.toString());
                 }
             });
             try {
                 future.get(NetworkConfig.SILENCE_THRESHOLD_MS, TimeUnit.MILLISECONDS);
             } catch (TimeoutException e) {
                 future.cancel(true);
                 controller.getView().showError(ErrorType.SERVER_CRASHED,ErrorType.SERVER_CRASHED.toString());
                 disconnect();
             } catch (Exception e) {
                 disconnect();
                 controller.getView().showError(ErrorType.SERVER_CRASHED,ErrorType.SERVER_CRASHED.toString());
             }
         }, 0, NetworkConfig.KEEPALIVE_INTERVAL_MS, TimeUnit.MILLISECONDS);
     }
 
     /**
      * Tears down the connection.
      * Mirrors {@code TCPClient.disconnect()}: stops the heartbeat and notifies
      * the view.
      */
     private void disconnect() {
         if (!running) return;
         running = false;
         if (pingSender != null) pingSender.shutdownNow();
         if (executor != null) executor.shutdownNow();
         controller.setModel(null);
         controller.setClient(null);
     }
 
 
 
 
     /**
      * Requests to draw a tribe card from the upper list.
      * Creates a NetworkEvent and sends it through the network client.
      * @param playerUsername the name of the player performing the action
      * @param pos the index of the card to draw
      */
     public void drawUpperTribeCard(String playerUsername, int pos)  {
         try{
             stub.drawUpperTribeCard(playerUsername,pos);
         }
         catch (RemoteException e){
             System.out.println("Error during remote draw upper tribe card");
         }
     }
 
 
     /**
      * Requests to draw a tribe card from the lower list.
      * Creates a NetworkEvent and sends it through the network client.
      * @param playerUsername the name of the player performing the action
      * @param pos the index of the card to draw
      */
     public void drawLowerTribeCard(String playerUsername,int pos)  {
         try{
             stub.drawLowerTribeCard(playerUsername,pos);
         }
         catch (RemoteException e){
             System.out.println("Error during remote draw lower tribe card");
         }
     }
 
 
     /**
      * Requests to draw a building card from the upper list.
      * Creates a NetworkEvent and sends it through the network client.
      * @param playerUsername the name of the player performing the action
      * @param pos the index of the card to draw
      */
     public void drawUpperBuildingCard(String playerUsername,int pos)  {
         try{
             stub.drawUpperBuildingCard(playerUsername,pos);
         }
         catch (RemoteException e){
             System.out.println("Error during remote draw upper building card");
         }
     }
 
 
     /**
      * Requests to draw a building card from the lower list.
      * Creates a NetworkEvent and sends it through the network client.
      * @param playerUsername the name of the player performing the action
      * @param pos the index of the card to draw
      */
     public void drawLowerBuildingCard(String playerUsername,int pos)  {
         try{
             stub.drawLowerBuildingCard(playerUsername,pos);
         }
         catch (RemoteException e){
             System.out.println("Error during remote draw lower building card");
         }
     }
 
 
     /**
      * Requests to skip the turn.
      * This action is available only when the player cannot draw any tribe card, but still can buy some buildings.
      * @param playerUsername the name of the player performing the action
      */
     public void skipTurn(String playerUsername) {
         try{
             stub.skipTurn(playerUsername);
         }
         catch (RemoteException e){
             System.out.println("Error during remote skip turn");
         }
     }
     
 
 
 
 
     /**
      * Used to perform the slot choice action for the specified player at the specified position.
      * @param playerUsername the name of the player performing the action
      * @param pos the index of the selected slot
      */
     public void slotChoice(String playerUsername,int pos)  {
         try{
             stub.slotChoice(playerUsername,pos);
         }
         catch (RemoteException e){
             System.out.println("Error during remote slot choice");
         }
     }
     /**
      * Sends a totem choice to the server for the specified player via RMI.
      *
      * @param playerUsername the username of the player choosing the totem.
      * @param totem the name of the chosen totem.
      */
     public void totemChoice(String playerUsername,String totem)  {
        try{
            stub.totemChoice(playerUsername,totem);
        }
        catch (RemoteException e){
            System.out.println("Error during remote totem choice");
        }
     }
 
     /**
      * Notifies the server of a voluntary disconnection via RMI, then stops
      * the keep-alive scheduler.
      */
     public void notifyDisconnection() {
         running = false;
         if (pingSender != null) pingSender.shutdownNow();
         if (executor != null) executor.shutdownNow();
         try {
             stub.disconnectPlayer(username);
         } catch (RemoteException e) {
             System.out.println("Error during remote disconnection");
         }
     }
 
 
 }