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