Partial implementation client resilience
This commit is contained in:
@@ -31,8 +31,11 @@ public class GameController {
|
||||
public GameController() {
|
||||
}
|
||||
//TODO
|
||||
public boolean SkipNotConnectedPlayer(Player player)
|
||||
public boolean SkipNotConnectedPlayer(String username)
|
||||
{
|
||||
Player player= model.getPlayerByUsername(username);
|
||||
if(player==null)
|
||||
return false;
|
||||
return model.SkipNotConnectedPlayer(player);
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -48,8 +48,14 @@ public class LimitedMap<K, V> implements Map<K, V> {
|
||||
*/
|
||||
@Override
|
||||
public synchronized V put(K key, V value) {
|
||||
boolean added = true;
|
||||
if(map.size()==limit) {
|
||||
if(!map.containsKey(key))
|
||||
return null;
|
||||
added = false;
|
||||
}
|
||||
V result = map.put(key, value);
|
||||
if (map.size() >= limit) {
|
||||
if (map.size() >= limit && added) {
|
||||
action.run();
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -13,7 +13,7 @@ public enum EventType {
|
||||
DRAW_LOWER_BUILD,
|
||||
PICK_OPTIONAL_TRIBE,
|
||||
PICK_OPTIONAL_BUILD,
|
||||
SKIP_UPPER,
|
||||
SKIP_LOWER,
|
||||
SKIP_NO_DRAWABLE,
|
||||
SKIP_PLAYER_DISCONNECTED,
|
||||
NO_OPTIONAL_CARD
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class SkipNoDrawable extends NetworkEvent implements Serializable{
|
||||
* @param username the name of the player requesting the event
|
||||
*/
|
||||
public SkipNoDrawable(String username){
|
||||
super(username, EventType.SKIP_LOWER, false);
|
||||
super(username, EventType.SKIP_PLAYER_DISCONNECTED, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package it.polimi.ingsw.gc14.Network.NetworkEvents;
|
||||
|
||||
import it.polimi.ingsw.gc14.Controller.GameController;
|
||||
import it.polimi.ingsw.gc14.Network.EventType;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* NetworkEvent to avoid drawing a card from the lower card list
|
||||
*/
|
||||
public class SkipPlayerDisconnected extends NetworkEvent implements Serializable{
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
* Initializes all the attributes.
|
||||
* @param username the name of the player requesting the event
|
||||
*/
|
||||
public SkipPlayerDisconnected(String username){
|
||||
super(username, EventType.SKIP_PLAYER_DISCONNECTED, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param gameController the Game Controller on which to apply the event
|
||||
* @return true if the player could skipTheTurn, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean apply(GameController gameController){
|
||||
return gameController.SkipNotConnectedPlayer(username);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,13 +72,15 @@ public class TCPClient implements IClient {
|
||||
communicationSocket = new Socket(hostname, mainPort);
|
||||
socketSend = new ObjectOutputStream(communicationSocket.getOutputStream());
|
||||
socketReceive = new ObjectInputStream(communicationSocket.getInputStream());
|
||||
doEvent(new AddPlayer(user, proposedNPlayers));
|
||||
|
||||
if (communicationSocket.getInputStream().read() == -1) {
|
||||
NetworkEvent event= new AddPlayer(user, proposedNPlayers);
|
||||
System.out.println("Sending event: " + event);
|
||||
socketSend.writeObject(event);
|
||||
int read= communicationSocket.getInputStream().read();
|
||||
if ( read== -1) {
|
||||
System.out.println("Could not connect to server");
|
||||
return false;
|
||||
}
|
||||
|
||||
new Thread(this::receiveMessage, "tcp-reader").start();
|
||||
// Socket heartbeat
|
||||
this.heartbeatSocket = new Socket(hostname, heartbeatPort);
|
||||
this.heartbeatOut =heartbeatSocket.getOutputStream() ;
|
||||
@@ -89,7 +91,7 @@ public class TCPClient implements IClient {
|
||||
heartbeatOut.flush();
|
||||
|
||||
running = true;
|
||||
new Thread(this::receiveMessage, "tcp-reader").start();
|
||||
|
||||
new Thread(this::heartbeatLoop, "heartbeat").start();
|
||||
return true;
|
||||
|
||||
@@ -157,7 +159,6 @@ public class TCPClient implements IClient {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
|
||||
if (read instanceof NetworkEvent event) { //TODO: avoid instanceof
|
||||
if (event.getIsError()) {
|
||||
controller.view.showError(event.toString());
|
||||
@@ -165,6 +166,7 @@ public class TCPClient implements IClient {
|
||||
event.apply(controller.localController);
|
||||
controller.view.render();
|
||||
}
|
||||
|
||||
} else if (read instanceof Game model) {
|
||||
controller.setModel(model);
|
||||
controller.view.render();
|
||||
@@ -279,7 +281,10 @@ public class TCPClient implements IClient {
|
||||
*/
|
||||
private void doEvent(NetworkEvent event) {
|
||||
try {
|
||||
socketSend.writeObject(event);
|
||||
synchronized (socketSend) {
|
||||
System.out.println("Sending event: " + event);
|
||||
socketSend.writeObject(event);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package it.polimi.ingsw.gc14.Network.TCP.Server;
|
||||
import it.polimi.ingsw.gc14.LimitedMap;
|
||||
import it.polimi.ingsw.gc14.Model.Game;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvents.SkipPlayerDisconnected;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
@@ -23,6 +24,7 @@ public class ClientHandler implements Runnable {
|
||||
|
||||
private boolean running ;
|
||||
|
||||
private Game game;
|
||||
/**
|
||||
* Returns the username associated with this client.
|
||||
*
|
||||
@@ -70,6 +72,7 @@ public class ClientHandler implements Runnable {
|
||||
this.out = out;
|
||||
this.clientHandlers = clientHandlers;
|
||||
this.actionQueue = actionQueue;
|
||||
this.limitedMap = playersMap;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,9 +85,11 @@ public class ClientHandler implements Runnable {
|
||||
try {
|
||||
running = true;
|
||||
while (running) {
|
||||
NetworkEvent event = (NetworkEvent) in.readObject();
|
||||
if (!actionQueue.add(event)) {
|
||||
System.out.println("Error inserting action into queue");
|
||||
synchronized (out){
|
||||
NetworkEvent event = (NetworkEvent) in.readObject();
|
||||
if (!actionQueue.add(event)) {
|
||||
System.out.println("Error inserting action into queue");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
@@ -116,6 +121,7 @@ public class ClientHandler implements Runnable {
|
||||
* @param game The current state of the game to send to the client.
|
||||
*/
|
||||
public synchronized void notifyModel(Game game) {
|
||||
this.game = game;
|
||||
try {
|
||||
out.writeObject(game);
|
||||
} catch (IOException e) {
|
||||
@@ -124,9 +130,13 @@ public class ClientHandler implements Runnable {
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (!running) return;
|
||||
running = false;
|
||||
clientHandlers.remove(this);
|
||||
limitedMap.put(username, false);
|
||||
if(this.game!=null && this.game.getCurrentState().getCurrentPlayer().getUserName().equals(username)) {
|
||||
actionQueue.add(new SkipPlayerDisconnected(username));
|
||||
}
|
||||
System.out.println("Disconnected player: " + username);
|
||||
try { clientSocket.close(); } catch (IOException ignored) {}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class HeartbeatHandler implements Runnable {
|
||||
@@ -66,10 +67,10 @@ public class HeartbeatHandler implements Runnable {
|
||||
|
||||
|
||||
private void disconnect() {
|
||||
if (!running) return;
|
||||
running = false;
|
||||
watchdog.shutdownNow();
|
||||
mainHandler.disconnect(); // disconnette anche il socket principale
|
||||
System.out.println("Disconnected: " + username);
|
||||
try { socket.close(); } catch (IOException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ public class TCPServer {
|
||||
ObjectOutputStream clientSend = new ObjectOutputStream(clientSocket.getOutputStream());
|
||||
ObjectInputStream clientReceive = new ObjectInputStream(clientSocket.getInputStream());
|
||||
NetworkEvent event = (NetworkEvent) clientReceive.readObject();
|
||||
|
||||
if (!(event.getEventType() == EventType.ADD_PLAYER)) {
|
||||
clientSocket.getOutputStream().write(-1);
|
||||
clientSocket.close();
|
||||
@@ -88,7 +87,7 @@ public class TCPServer {
|
||||
continue;
|
||||
}
|
||||
|
||||
synchronized (controller) {
|
||||
|
||||
if (playerList.isEmpty()) {
|
||||
Game model = new Game(eventAddPlayer.getProposedNPlayer());
|
||||
controller.setModel(model);
|
||||
@@ -100,35 +99,40 @@ public class TCPServer {
|
||||
if (controller.addPlayer(username)) {
|
||||
// nuovo giocatore
|
||||
playerList.put(username, true);
|
||||
clientSocket.getOutputStream().write(1);
|
||||
System.out.println("Accepted player: " + username);
|
||||
|
||||
ClientHandler handler = new ClientHandler(
|
||||
username, clientSocket, clientSend, clientReceive,
|
||||
clientHandlers,playerList, actionQueue
|
||||
);
|
||||
clientSocket.getOutputStream().write(1);
|
||||
pendingHeartbeat.put(username, handler);
|
||||
Thread thread = new Thread(handler);
|
||||
thread.start();
|
||||
clientHandlers.add(handler);
|
||||
connectedPlayers++;
|
||||
|
||||
// metti in attesa del socket heartbeat
|
||||
pendingHeartbeat.put(username, handler);
|
||||
|
||||
|
||||
} else if (playerList.containsKey(username) && !playerList.get(username)) {
|
||||
// riconnessione
|
||||
playerList.put(username, true);
|
||||
clientSocket.getOutputStream().write(1);
|
||||
System.out.println("Reconnected player: " + username);
|
||||
|
||||
ClientHandler handler = new ClientHandler(
|
||||
username, clientSocket, clientSend, clientReceive,
|
||||
clientHandlers, playerList, actionQueue
|
||||
);
|
||||
handler.notifyModel(controller.getModel());
|
||||
clientSocket.getOutputStream().write(1);
|
||||
pendingHeartbeat.put(username, handler);
|
||||
synchronized (controller) {
|
||||
handler.notifyModel(controller.getModel());
|
||||
}
|
||||
Thread thread = new Thread(handler);
|
||||
thread.start();
|
||||
clientHandlers.add(handler);
|
||||
connectedPlayers++;
|
||||
|
||||
pendingHeartbeat.put(username, handler);
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
@@ -136,7 +140,7 @@ public class TCPServer {
|
||||
clientSocket.close();
|
||||
System.out.println("Player could not be added. Connection terminated.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -3,8 +3,10 @@ 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.Player;
|
||||
import it.polimi.ingsw.gc14.Network.ClientPlayer;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvents.SkipPlayerDisconnected;
|
||||
import it.polimi.ingsw.gc14.Network.RMI.Server.RMIServer;
|
||||
import it.polimi.ingsw.gc14.Network.TCP.Server.TCPServer;
|
||||
import it.polimi.ingsw.gc14.View.TUI.TUI;
|
||||
@@ -85,13 +87,16 @@ public class ServerLauncher {
|
||||
* @throws RemoteException if an RMI error occurs
|
||||
*/
|
||||
public boolean doFirstEvent() throws InterruptedException, RemoteException {
|
||||
NetworkEvent event = actionQueue.take();
|
||||
event.setIsError(!event.apply(gameController));
|
||||
|
||||
serverRMI.notifyAll(event);
|
||||
serverTCP.notifyAll(event);
|
||||
|
||||
return !event.getIsError();
|
||||
synchronized (gameController) {
|
||||
NetworkEvent event = actionQueue.take();
|
||||
event.setIsError(!event.apply(gameController));
|
||||
serverRMI.notifyAll(event);
|
||||
serverTCP.notifyAll(event);
|
||||
if (!playerList.get(gameController.getModel().getCurrentState().getCurrentPlayer().getUserName())) {
|
||||
actionQueue.offer(new SkipPlayerDisconnected(gameController.getModel().getCurrentState().getCurrentPlayer().getUserName()));
|
||||
}
|
||||
return !event.getIsError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user