diff --git a/src/main/java/it/polimi/ingsw/gc14/Network/RMI/Server/RMIServer.java b/src/main/java/it/polimi/ingsw/gc14/Network/RMI/Server/RMIServer.java index 3578350..05152c1 100644 --- a/src/main/java/it/polimi/ingsw/gc14/Network/RMI/Server/RMIServer.java +++ b/src/main/java/it/polimi/ingsw/gc14/Network/RMI/Server/RMIServer.java @@ -122,7 +122,7 @@ public class RMIServer implements IGameServer { /** - * Sends a model game to all RMI clients. + * Sends a game model to all RMI clients. * @param model The desired model */ public void notifyAll(Game model) throws RemoteException { diff --git a/src/main/java/it/polimi/ingsw/gc14/Network/TCP/Client/TCPClient.java b/src/main/java/it/polimi/ingsw/gc14/Network/TCP/Client/TCPClient.java index 9a39808..c69734e 100644 --- a/src/main/java/it/polimi/ingsw/gc14/Network/TCP/Client/TCPClient.java +++ b/src/main/java/it/polimi/ingsw/gc14/Network/TCP/Client/TCPClient.java @@ -8,78 +8,111 @@ import it.polimi.ingsw.gc14.Network.NetworkEvents.AddPlayer; import java.io.*; import java.net.*; -public class TCPClient implements Serializable{ - Socket communicationSocket = null; +/** + * Client TCP. Sends and receives messages with the TCP server. + */ +public class TCPClient { + + /** Socket TCP */ + Socket communicationSocket; + + /** Input stream used receive objects from the server */ ObjectInputStream socketReceive; + + /** Output stream used to send objects to the server */ ObjectOutputStream socketSend; + /** Client game's controller */ GameController controller; + + /** IP address of the server to connect to */ String hostname; + + /** TCP port */ int port; - public TCPClient(GameController controller, String hostname, int port){ + + /** + * Class constructor that initializes the attributes. + * @param controller The game controller + * @param hostname The IP address of the server + * @param port The TCP port of the server + */ + public TCPClient(GameController controller, String hostname, int port) { this.controller = controller; this.hostname = hostname; this.port = port; } - public boolean start(String user, int players){ - try{ + + /** + * Starts the TCP connection with the server. + * Sends an {@link AddPlayer} event, if the server responds with {@code -1}, the connection is refused and the method returns {@code false}. + * Otherwise, a listener thread is started. + * @param user The username of the player + * @param proposedNPlayers The desired number of players for the game + * @return true if the connection is successful, false otherwise. + */ + public boolean start(String user, int proposedNPlayers) { + try { communicationSocket = new Socket(hostname, port); socketSend = new ObjectOutputStream(communicationSocket.getOutputStream()); socketReceive = new ObjectInputStream(communicationSocket.getInputStream()); - socketSend.writeObject(new AddPlayer(user, players)); - if(communicationSocket.getInputStream().read() == -1){ + sendEvent(new AddPlayer(user, proposedNPlayers)); + if (communicationSocket.getInputStream().read() == -1) { + System.out.println("Could not connect to server"); return false; - } - else{ - Thread listener = new Thread(() -> ReceiveMessage()); + } else { + Thread listener = new Thread(() -> receiveMessage()); listener.start(); return true; } - } - catch(Exception e){ + } catch (IOException e) { + e.printStackTrace(); return false; } } - private void ReceiveMessage(){ - while(true){ - try{ - Object read = socketReceive.readObject(); - if (read instanceof NetworkEvent) { //TODO non fare con instanceof - NetworkEvent event = (NetworkEvent) read; - if(event.getIsError()) { - System.out.println(event.toString()); + /** + * Listens continuously for incoming objects from the server. + * - If the received object is a {@link NetworkEvent} flagged as an error, it is printed. + * - If the received object is a valid {@link NetworkEvent}, it is applied to the game controller. + * - If the received object is a {@link Game} model, the controller's model is set. + */ + private void receiveMessage() { + while (true) { + try { + Object read = socketReceive.readObject(); + if (read instanceof NetworkEvent event) { //TODO: avoid instanceof + if (event.getIsError()) { + System.out.println(event); } else { event.apply(controller); - //clientController.view.update(); TODO + //clientController.view.update(); TODO } + } else if (read instanceof Game model) { + controller.setModel(model); } - else if (read instanceof Game) { - controller.setModel((Game) read); - } - - } - catch(IOException e){ + } catch (IOException e) { e.printStackTrace(); - } - catch(ClassNotFoundException e){ + } catch (ClassNotFoundException e) { throw new RuntimeException(e); } - return; } } - private void SendEvent(NetworkEvent event){ - try{ + + /** + * Sends a {@link NetworkEvent} to the server. + * @param event The NetworkEvent to send. + */ + private void sendEvent(NetworkEvent event) { + try { socketSend.writeObject(event); - } - catch (IOException e) { + } catch (IOException e) { e.printStackTrace(); } } - -} +} \ No newline at end of file diff --git a/src/main/java/it/polimi/ingsw/gc14/Network/TCP/Server/ClientHandler.java b/src/main/java/it/polimi/ingsw/gc14/Network/TCP/Server/ClientHandler.java index a989868..07864f0 100644 --- a/src/main/java/it/polimi/ingsw/gc14/Network/TCP/Server/ClientHandler.java +++ b/src/main/java/it/polimi/ingsw/gc14/Network/TCP/Server/ClientHandler.java @@ -1,89 +1,105 @@ package it.polimi.ingsw.gc14.Network.TCP.Server; -import it.polimi.ingsw.gc14.Controller.GameController; import it.polimi.ingsw.gc14.Model.Game; import it.polimi.ingsw.gc14.Network.NetworkEvent; -import it.polimi.ingsw.gc14.Network.EventType; import java.io.*; import java.net.*; import java.util.List; import java.util.concurrent.BlockingQueue; +/** + * Handles the TCP connection with a single client. + * Each instance runs on a dedicated thread and is responsible for receiving {@link NetworkEvent} from its client and adding them + * to the {@link #actionQueue}. + * It is also responsible to send events and game model updates back to the client. + */ public class ClientHandler implements Runnable { + + /** The TCP socket */ private Socket clientSocket; - private TCPServer server; - public ObjectInputStream in = null; - public ObjectOutputStream out = null; + + /** Input stream used to receive objects from the client */ + public ObjectInputStream in; + + /** Output stream used to send objects to the client */ + public ObjectOutputStream out; + + /** + * Shared list of all client handlers. + * This handler removes itself from the list when disconnected. + */ List clientHandlers; + + /** Queue containing the events to be applied to the game model */ BlockingQueue actionQueue; - private EventType eventType; - public Socket getClientSocket() { - return clientSocket; - } + /** + * Class constructor that initializes the attributes. + * @param clientSocket The socket representing the client's TCP connection + * @param clientHandlers The shared list of all active client handlers + * @param actionQueue The queue containing incoming events + */ public ClientHandler(Socket clientSocket, List clientHandlers, BlockingQueue actionQueue) { this.clientSocket = clientSocket; this.clientHandlers = clientHandlers; this.actionQueue = actionQueue; } + + /** + * Listens for incoming {@link NetworkEvent}s from the client and adds them to the {@link #actionQueue}. + * Upon disconnection, this handler removes itself from {@link #clientHandlers}. + */ @Override - public void run(){ - clientLoop(); - } - - private void clientLoop(){ - try{ - NetworkEvent input = null; - synchronized(in){ - in = new ObjectInputStream(clientSocket.getInputStream()); - } - while(true){ - try{ - input = (NetworkEvent) (in.readObject()); - if(!actionQueue.add(input)){ - System.out.println("An error occurred in inserting an action into queue"); - } - } - catch(java.io.IOException e){ - e.printStackTrace(); - } - catch (ClassNotFoundException e){ - throw new RuntimeException(e); + public void run() { + try { + in = new ObjectInputStream(clientSocket.getInputStream()); + out = new ObjectOutputStream(clientSocket.getOutputStream()); + while (true) { + NetworkEvent event = (NetworkEvent) in.readObject(); + if (!actionQueue.add(event)) { + System.out.println("Error inserting action into queue"); } } - } - catch (IOException e) { + } catch (IOException e) { + clientHandlers.remove(this); e.printStackTrace(); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); } } - public void notifyEvent(NetworkEvent event){ - synchronized(out){ - try{ + + /** + * Sends a {@link NetworkEvent} to the client. + * @param event The network event to send to the client. + */ + public void notifyEvent(NetworkEvent event) { + synchronized (out) { + try { out = new ObjectOutputStream(clientSocket.getOutputStream()); out.writeObject(event); - } - catch(IOException e){ + } catch (IOException e) { e.printStackTrace(); } } } - public void notifyModel(Game game){ - synchronized(out){ - try{ + + /** + * Sends the current game model to this client. + * @param game The current state of the game to send to the client. + */ + public void notifyModel(Game game) { + synchronized (out) { + try { out = new ObjectOutputStream(clientSocket.getOutputStream()); out.writeObject(game); - } - catch(IOException e){ + } catch (IOException e) { e.printStackTrace(); } } } - -} - - +} \ No newline at end of file diff --git a/src/main/java/it/polimi/ingsw/gc14/Network/TCP/Server/TCPServer.java b/src/main/java/it/polimi/ingsw/gc14/Network/TCP/Server/TCPServer.java index fc86f84..16c4cfb 100644 --- a/src/main/java/it/polimi/ingsw/gc14/Network/TCP/Server/TCPServer.java +++ b/src/main/java/it/polimi/ingsw/gc14/Network/TCP/Server/TCPServer.java @@ -3,6 +3,7 @@ package it.polimi.ingsw.gc14.Network.TCP.Server; import it.polimi.ingsw.gc14.Controller.GameController; import it.polimi.ingsw.gc14.LimitedList; import it.polimi.ingsw.gc14.Model.Game; +import it.polimi.ingsw.gc14.Network.EventType; import it.polimi.ingsw.gc14.Network.NetworkEvent; import it.polimi.ingsw.gc14.Network.NetworkEvents.AddPlayer; @@ -12,71 +13,120 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingQueue; - +/** + * Server TCP. Accepts connections and manages all client handlers. + */ public class TCPServer { - int port = -1; - int ConnectedPlayers = 0; - ServerSocket serverTCP = null; - GameController gameController; + + /** TCP port */ + int port; + + /** Number of currently connected clients */ + int ConnectedPlayers; + + /** Socket TCP */ + ServerSocket socketTCP; + + /** Server game's controller */ + GameController controller; + + /** Queue containing the events to be applied to the game model */ BlockingQueue actionQueue; + + /** + * List containing the usernames of joined players. + * {@link LimitedList}'s limit defines at which size the list calls its action. The limit can be set using {@link LimitedList#setLimit(int)}. + */ private LimitedList playerList; + + /** List containing all client's handlers */ private List clientHandlers; - - private int getConnectedPlayers(){ - return ConnectedPlayers; + /** + * Class constructor that initializes the attributes. + * @param controller The game controller + * @param port The TCP port + * @param actionQueue The action queue + * @param playerList The player's usernames list + */ + public TCPServer(GameController controller, int port, BlockingQueue actionQueue, LimitedList playerList){ + this.port = port; + this.ConnectedPlayers = 0; + this.socketTCP = null; + this.controller = controller; + this.actionQueue = actionQueue; + this.playerList = playerList; + this.clientHandlers = new ArrayList<>(); } + + /** + * Starts the TCP server. + * If the first event is not AddPlayer, the request is rejected. + * If the desired number of player is invalid, the request is rejected. + * + * If this is the first player to connect, a new game model is created and passed to the controller. Additionally, the playerList's limit is set. + * If the controller successfully adds the player, the username is added to {@link #playerList} and the handler is added to {@link #clientHandlers}. + * + * If any error occurs, the server sends -1 back to the client. Otherwise, it sends 1. + */ public void start(){ - clientHandlers = new ArrayList<>(); try{ - serverTCP = new ServerSocket(port); + socketTCP = new ServerSocket(port); } catch (IOException e){ - System.out.println("Could not listen on port: " + port); + System.out.println("Could not start the server TCP on port: " + port); e.printStackTrace(); return; } - System.out.println("Listening on port: " + port); + System.out.println("Server TCP started on port: " + port); + Socket clientSocket; while(true){ - Socket clientSocket = null; try{ - clientSocket = serverTCP.accept(); + clientSocket = socketTCP.accept(); ObjectInputStream clientSocketObj = new ObjectInputStream(clientSocket.getInputStream()); NetworkEvent event = (NetworkEvent) clientSocketObj.readObject(); - synchronized (gameController){ - if(!(event instanceof AddPlayer) || (gameController.getModel() != null && gameController.getModel().getCurrentPlayerNumber() >= gameController.getModel().getNPlayers())){ - clientSocket.getOutputStream().write((int)(-1)); + if(!(event.getEventType() == EventType.ADD_PLAYER)){ + clientSocket.getOutputStream().write((int)(-1)); + clientSocket.close(); + System.out.println("Invalid parameters. Connection terminated.\n"); + } + else{ + AddPlayer eventAddPlayer = (AddPlayer) event; + if (eventAddPlayer.getProposedNPlayer() < 2 || eventAddPlayer.getProposedNPlayer() > 5) { + clientSocket.getOutputStream().write((int) (-1)); clientSocket.close(); System.out.println("Invalid parameters. Connection terminated.\n"); } - else{ - synchronized (gameController) { - AddPlayer addPlayer = (AddPlayer) event; - if(playerList.isEmpty() && (addPlayer.getProposedNPlayer() < 2 || addPlayer.getProposedNPlayer() > 5)){ - clientSocket.getOutputStream().write((int)(-1)); - clientSocket.close(); - System.out.println("Invalid parameters. Connection terminated.\n"); - } - else if(gameController.addPlayer(addPlayer.getUsername())){ - if(playerList.isEmpty()){ - Game game = new Game(addPlayer.getProposedNPlayer()); - gameController.setModel(game); - playerList.setLimit(addPlayer.getProposedNPlayer()); - } - playerList.add(addPlayer.getUsername()); - clientSocket.getOutputStream().write((int)(1)); - } + synchronized (controller) { + if (playerList.isEmpty()){ + Game model = new Game(eventAddPlayer.getProposedNPlayer()); + controller.setModel(model); + playerList.setLimit(eventAddPlayer.getProposedNPlayer()); } + if (controller.addPlayer(eventAddPlayer.getUsername())) { + playerList.add(eventAddPlayer.getUsername()); + clientSocket.getOutputStream().write((int) (1)); + System.out.println("Accepted player: " + eventAddPlayer.getUsername()); + ClientHandler clientHandler = new ClientHandler(clientSocket, clientHandlers, actionQueue); + clientHandlers.add(clientHandler); + ConnectedPlayers++; + + Thread t = new Thread(clientHandler); + t.start(); + } else { + clientSocket.getOutputStream().write((int) (-1)); + clientSocket.close(); + System.out.println("Player could not be added. Connection terminated.\n"); + } } } - // gestione di ADD_PLAYER } catch(IOException e){ e.printStackTrace(); @@ -84,44 +134,17 @@ public class TCPServer { catch(ClassNotFoundException e){ throw new RuntimeException(e); } - - System.out.println("Accepted player: " + gameController.getModel().getPlayerByUsername(clientSocket.getInetAddress().toString())); - - ConnectedPlayers++; - ClientHandler clientHandler = new ClientHandler(clientSocket, clientHandlers, actionQueue); - clientHandlers.add(clientHandler); - - //Sending model to clients - if(ConnectedPlayers == gameController.getModel().getNPlayers()){ - for (ClientHandler handler : clientHandlers) { - try { - synchronized(handler.out){ - ObjectOutputStream socketTx = new ObjectOutputStream(handler.getClientSocket().getOutputStream()); - socketTx.writeObject(gameController.getModel()); - } - } - catch(IOException e){ - e.printStackTrace(); - } - } - } - - Thread t = new Thread(clientHandler); - t.start(); } } - public TCPServer(GameController gameController, int port, BlockingQueue actionQueue, LimitedList players){ - this.port = port; - this.gameController = gameController; - this.actionQueue = actionQueue; - this.playerList = players; - } + /** Sends an action to all TCP clients */ public void notifyAll(NetworkEvent event){ clientHandlers.forEach((x) -> x.notifyEvent(event)); } + + /** Sends a game model to all TCP clients */ public void notifyAll(Game model){ clientHandlers.forEach((x) -> x.notifyModel(model)); }