reconnect to games when client reconnects

This commit is contained in:
BetaSteward 2011-07-04 15:15:05 -04:00
parent 1a43757ed0
commit b4b02d0f68
5 changed files with 100 additions and 63 deletions

View file

@ -106,6 +106,7 @@ public class Session {
callbackHandler.handleCallbackOneway(new Callback(call)); callbackHandler.handleCallbackOneway(new Callback(call));
} catch (HandleCallbackException ex) { } catch (HandleCallbackException ex) {
logger.fatal("Session fireCallback error", ex); logger.fatal("Session fireCallback error", ex);
kill();
} }
} }

View file

@ -28,10 +28,14 @@
package mage.server; package mage.server;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
import mage.interfaces.callback.ClientCallback; import mage.interfaces.callback.ClientCallback;
import mage.server.game.GameManager;
import mage.server.game.GameSession;
import mage.view.TableClientMessage; import mage.view.TableClientMessage;
/** /**
@ -39,7 +43,7 @@ import mage.view.TableClientMessage;
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
public class User { public class User {
public enum UserState { public enum UserState {
Created, Connected, Disconnected, Reconnected; Created, Connected, Disconnected, Reconnected;
} }
@ -49,9 +53,8 @@ public class User {
private String sessionId = ""; private String sessionId = "";
private String host; private String host;
private Date connectionTime = new Date(); private Date connectionTime = new Date();
private Date lastActivity = new Date();
private UserState userState; private UserState userState;
private CountDownLatch connectionSignal = new CountDownLatch(1); private Map<UUID, GameSession> gameSessions = new HashMap<UUID, GameSession>();
public User(String userName, String host) { public User(String userName, String host) {
this.userName = userName; this.userName = userName;
@ -88,7 +91,7 @@ public class User {
} }
public boolean isConnected() { public boolean isConnected() {
return userState == UserState.Connected; return userState == UserState.Connected || userState == UserState.Reconnected;
} }
public Date getConnectionTime() { public Date getConnectionTime() {
@ -131,6 +134,19 @@ public class User {
} }
private void reconnect() { private void reconnect() {
for (Entry<UUID, GameSession> entry: gameSessions.entrySet()) {
gameStarted(entry.getValue().getGameId(), entry.getKey());
entry.getValue().init();
GameManager.getInstance().sendPlayerString(entry.getValue().getGameId(), userId, "");
}
} }
public void addGame(UUID playerId, GameSession gameSession) {
gameSessions.put(playerId, gameSession);
}
public void removeGame(UUID playerId) {
gameSessions.remove(playerId);
}
} }

View file

@ -62,6 +62,7 @@ import mage.game.events.TableEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.players.Player; import mage.players.Player;
import mage.server.ChatManager; import mage.server.ChatManager;
import mage.server.UserManager;
import mage.server.util.ThreadExecutor; import mage.server.util.ThreadExecutor;
import mage.sets.Sets; import mage.sets.Sets;
import mage.view.*; import mage.view.*;
@ -186,6 +187,7 @@ public class GameController implements GameCallback {
UUID playerId = userPlayerMap.get(userId); UUID playerId = userPlayerMap.get(userId);
GameSession gameSession = new GameSession(game, userId, playerId); GameSession gameSession = new GameSession(game, userId, playerId);
gameSessions.put(playerId, gameSession); gameSessions.put(playerId, gameSession);
UserManager.getInstance().getUser(userId).addGame(playerId, gameSession);
logger.info("player " + playerId + " has joined game " + game.getId()); logger.info("player " + playerId + " has joined game " + game.getId());
ChatManager.getInstance().broadcast(chatId, "", game.getPlayer(playerId).getName() + " has joined the game", MessageColor.BLACK); ChatManager.getInstance().broadcast(chatId, "", game.getPlayer(playerId).getName() + " has joined the game", MessageColor.BLACK);
checkStart(); checkStart();
@ -194,7 +196,7 @@ public class GameController implements GameCallback {
private synchronized void startGame() { private synchronized void startGame() {
if (gameFuture == null) { if (gameFuture == null) {
for (final Entry<UUID, GameSession> entry: gameSessions.entrySet()) { for (final Entry<UUID, GameSession> entry: gameSessions.entrySet()) {
if (!entry.getValue().init(getGameView(entry.getKey()))) { if (!entry.getValue().init()) {
logger.fatal("Unable to initialize client"); logger.fatal("Unable to initialize client");
//TODO: generate client error message //TODO: generate client error message
return; return;
@ -227,9 +229,9 @@ public class GameController implements GameCallback {
} }
public void watch(UUID userId) { public void watch(UUID userId) {
GameWatcher gameWatcher = new GameWatcher(userId, game.getId()); GameWatcher gameWatcher = new GameWatcher(userId, game);
watchers.put(userId, gameWatcher); watchers.put(userId, gameWatcher);
gameWatcher.init(getGameView()); gameWatcher.init();
ChatManager.getInstance().broadcast(chatId, "", " has started watching", MessageColor.BLACK); ChatManager.getInstance().broadcast(chatId, "", " has started watching", MessageColor.BLACK);
} }
@ -297,6 +299,7 @@ public class GameController implements GameCallback {
public void endGame(final String message) throws MageException { public void endGame(final String message) throws MageException {
for (final GameSession gameSession: gameSessions.values()) { for (final GameSession gameSession: gameSessions.values()) {
gameSession.gameOver(message); gameSession.gameOver(message);
gameSession.removeGame();
} }
for (final GameWatcher gameWatcher: watchers.values()) { for (final GameWatcher gameWatcher: watchers.values()) {
gameWatcher.gameOver(message); gameWatcher.gameOver(message);
@ -329,17 +332,17 @@ public class GameController implements GameCallback {
} }
private synchronized void updateGame() { private synchronized void updateGame() {
for (final Entry<UUID, GameSession> entry: gameSessions.entrySet()) { for (final GameSession gameSession: gameSessions.values()) {
entry.getValue().update(getGameView(entry.getKey())); gameSession.update();
} }
for (final GameWatcher gameWatcher: watchers.values()) { for (final GameWatcher gameWatcher: watchers.values()) {
gameWatcher.update(getGameView()); gameWatcher.update();
} }
} }
private synchronized void ask(UUID playerId, String question) throws MageException { private synchronized void ask(UUID playerId, String question) throws MageException {
if (gameSessions.containsKey(playerId)) if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).ask(question, getGameView(playerId)); gameSessions.get(playerId).ask(question);
informOthers(playerId); informOthers(playerId);
} }
@ -358,41 +361,41 @@ public class GameController implements GameCallback {
private synchronized void target(UUID playerId, String question, Cards cards, List<Permanent> perms, Set<UUID> targets, boolean required, Map<String, Serializable> options) throws MageException { private synchronized void target(UUID playerId, String question, Cards cards, List<Permanent> perms, Set<UUID> targets, boolean required, Map<String, Serializable> options) throws MageException {
if (gameSessions.containsKey(playerId)) { if (gameSessions.containsKey(playerId)) {
if (cards != null) if (cards != null)
gameSessions.get(playerId).target(question, new CardsView(cards.getCards(game)), targets, required, getGameView(playerId), options); gameSessions.get(playerId).target(question, new CardsView(cards.getCards(game)), targets, required, options);
else if (perms != null) { else if (perms != null) {
CardsView permsView = new CardsView(); CardsView permsView = new CardsView();
for (Permanent perm: perms) { for (Permanent perm: perms) {
permsView.put(perm.getId(), new PermanentView(perm, game.getCard(perm.getId()))); permsView.put(perm.getId(), new PermanentView(perm, game.getCard(perm.getId())));
} }
gameSessions.get(playerId).target(question, permsView, targets, required, getGameView(playerId), options); gameSessions.get(playerId).target(question, permsView, targets, required, options);
} }
else else
gameSessions.get(playerId).target(question, new CardsView(), targets, required, getGameView(playerId), options); gameSessions.get(playerId).target(question, new CardsView(), targets, required, options);
} }
informOthers(playerId); informOthers(playerId);
} }
private synchronized void target(UUID playerId, String question, Collection<? extends Ability> abilities, boolean required, Map<String, Serializable> options) throws MageException { private synchronized void target(UUID playerId, String question, Collection<? extends Ability> abilities, boolean required, Map<String, Serializable> options) throws MageException {
if (gameSessions.containsKey(playerId)) if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).target(question, new CardsView(abilities, game), null, required, getGameView(playerId), options); gameSessions.get(playerId).target(question, new CardsView(abilities, game), null, required, options);
informOthers(playerId); informOthers(playerId);
} }
private synchronized void select(UUID playerId, String message) throws MageException { private synchronized void select(UUID playerId, String message) throws MageException {
if (gameSessions.containsKey(playerId)) if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).select(message, getGameView(playerId)); gameSessions.get(playerId).select(message);
informOthers(playerId); informOthers(playerId);
} }
private synchronized void playMana(UUID playerId, String message) throws MageException { private synchronized void playMana(UUID playerId, String message) throws MageException {
if (gameSessions.containsKey(playerId)) if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).playMana(message, getGameView(playerId)); gameSessions.get(playerId).playMana(message);
informOthers(playerId); informOthers(playerId);
} }
private synchronized void playXMana(UUID playerId, String message) throws MageException { private synchronized void playXMana(UUID playerId, String message) throws MageException {
if (gameSessions.containsKey(playerId)) if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).playXMana(message, getGameView(playerId)); gameSessions.get(playerId).playXMana(message);
informOthers(playerId); informOthers(playerId);
} }
@ -417,11 +420,11 @@ public class GameController implements GameCallback {
final String message = "Waiting for " + game.getPlayer(playerId).getName(); final String message = "Waiting for " + game.getPlayer(playerId).getName();
for (final Entry<UUID, GameSession> entry: gameSessions.entrySet()) { for (final Entry<UUID, GameSession> entry: gameSessions.entrySet()) {
if (!entry.getKey().equals(playerId)) { if (!entry.getKey().equals(playerId)) {
entry.getValue().inform(message, getGameView(entry.getKey())); entry.getValue().inform(message);
} }
} }
for (final GameWatcher watcher: watchers.values()) { for (final GameWatcher watcher: watchers.values()) {
watcher.inform(message, getGameView()); watcher.inform(message);
} }
} }
@ -431,21 +434,8 @@ public class GameController implements GameCallback {
} }
} }
private GameView getGameView() {
return new GameView(game.getState(), game);
}
public GameView getGameView(UUID playerId) { public GameView getGameView(UUID playerId) {
GameView gameView = new GameView(game.getState(), game); return gameSessions.get(playerId).getGameView();
gameView.setHand(new CardsView(game.getPlayer(playerId).getHand().getCards(game)));
List<LookedAtView> list = new ArrayList<LookedAtView>();
for (Entry<String, Cards> entry : game.getState().getLookedAt(playerId).entrySet()) {
list.add(new LookedAtView(entry.getKey(), entry.getValue(), game));
}
gameView.setLookedAt(list);
return gameView;
} }
@Override @Override

View file

@ -29,12 +29,16 @@
package mage.server.game; package mage.server.game;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import mage.cards.Cards;
import mage.game.Game; import mage.game.Game;
import mage.interfaces.callback.ClientCallback; import mage.interfaces.callback.ClientCallback;
import mage.server.User; import mage.server.User;
@ -45,6 +49,7 @@ import mage.view.AbilityPickerView;
import mage.view.CardsView; import mage.view.CardsView;
import mage.view.GameClientMessage; import mage.view.GameClientMessage;
import mage.view.GameView; import mage.view.GameView;
import mage.view.LookedAtView;
/** /**
* *
@ -52,44 +57,42 @@ import mage.view.GameView;
*/ */
public class GameSession extends GameWatcher { public class GameSession extends GameWatcher {
private Game game;
private UUID playerId; private UUID playerId;
private ScheduledFuture<?> futureTimeout; private ScheduledFuture<?> futureTimeout;
protected static ScheduledExecutorService timeoutExecutor = ThreadExecutor.getInstance().getTimeoutExecutor(); protected static ScheduledExecutorService timeoutExecutor = ThreadExecutor.getInstance().getTimeoutExecutor();
public GameSession(Game game, UUID userId, UUID playerId) { public GameSession(Game game, UUID userId, UUID playerId) {
super(userId, game.getId()); super(userId, game);
this.game = game;
this.playerId = playerId; this.playerId = playerId;
} }
public void ask(final String question, final GameView gameView) { public void ask(final String question) {
if (!killed) { if (!killed) {
setupTimeout(); setupTimeout();
User user = UserManager.getInstance().getUser(userId); User user = UserManager.getInstance().getUser(userId);
if (user != null) { if (user != null) {
user.fireCallback(new ClientCallback("gameAsk", game.getId(), new GameClientMessage(gameView, question))); user.fireCallback(new ClientCallback("gameAsk", game.getId(), new GameClientMessage(getGameView(), question)));
} }
} }
} }
public void target(final String question, final CardsView cardView, final Set<UUID> targets, final boolean required, final GameView gameView, final Map<String, Serializable> options) { public void target(final String question, final CardsView cardView, final Set<UUID> targets, final boolean required, final Map<String, Serializable> options) {
if (!killed) { if (!killed) {
setupTimeout(); setupTimeout();
User user = UserManager.getInstance().getUser(userId); User user = UserManager.getInstance().getUser(userId);
if (user != null) { if (user != null) {
user.fireCallback(new ClientCallback("gameTarget", game.getId(), new GameClientMessage(gameView, question, cardView, targets, required, options))); user.fireCallback(new ClientCallback("gameTarget", game.getId(), new GameClientMessage(getGameView(), question, cardView, targets, required, options)));
} }
} }
} }
public void select(final String message, final GameView gameView) { public void select(final String message) {
if (!killed) { if (!killed) {
setupTimeout(); setupTimeout();
User user = UserManager.getInstance().getUser(userId); User user = UserManager.getInstance().getUser(userId);
if (user != null) { if (user != null) {
user.fireCallback(new ClientCallback("gameSelect", game.getId(), new GameClientMessage(gameView, message))); user.fireCallback(new ClientCallback("gameSelect", game.getId(), new GameClientMessage(getGameView(), message)));
} }
} }
} }
@ -114,22 +117,22 @@ public class GameSession extends GameWatcher {
} }
} }
public void playMana(final String message, final GameView gameView) { public void playMana(final String message) {
if (!killed) { if (!killed) {
setupTimeout(); setupTimeout();
User user = UserManager.getInstance().getUser(userId); User user = UserManager.getInstance().getUser(userId);
if (user != null) { if (user != null) {
user.fireCallback(new ClientCallback("gamePlayMana", game.getId(), new GameClientMessage(gameView, message))); user.fireCallback(new ClientCallback("gamePlayMana", game.getId(), new GameClientMessage(getGameView(), message)));
} }
} }
} }
public void playXMana(final String message, final GameView gameView) { public void playXMana(final String message) {
if (!killed) { if (!killed) {
setupTimeout(); setupTimeout();
User user = UserManager.getInstance().getUser(userId); User user = UserManager.getInstance().getUser(userId);
if (user != null) { if (user != null) {
user.fireCallback(new ClientCallback("gamePlayXMana", game.getId(), new GameClientMessage(gameView, message))); user.fireCallback(new ClientCallback("gamePlayXMana", game.getId(), new GameClientMessage(getGameView(), message)));
} }
} }
} }
@ -153,14 +156,13 @@ public class GameSession extends GameWatcher {
} }
} }
private synchronized void setupTimeout() { private synchronized void setupTimeout() {
cancelTimeout(); cancelTimeout();
futureTimeout = timeoutExecutor.schedule( futureTimeout = timeoutExecutor.schedule(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
GameManager.getInstance().timeout(gameId, userId); GameManager.getInstance().timeout(game.getId(), userId);
} }
}, },
ConfigSettings.getInstance().getMaxSecondsIdle(), TimeUnit.SECONDS ConfigSettings.getInstance().getMaxSecondsIdle(), TimeUnit.SECONDS
@ -192,4 +194,27 @@ public class GameSession extends GameWatcher {
cancelTimeout(); cancelTimeout();
game.getPlayer(playerId).setResponseInteger(data); game.getPlayer(playerId).setResponseInteger(data);
} }
@Override
public GameView getGameView() {
GameView gameView = new GameView(game.getState(), game);
gameView.setHand(new CardsView(game.getPlayer(playerId).getHand().getCards(game)));
List<LookedAtView> list = new ArrayList<LookedAtView>();
for (Entry<String, Cards> entry : game.getState().getLookedAt(playerId).entrySet()) {
list.add(new LookedAtView(entry.getKey(), entry.getValue(), game));
}
gameView.setLookedAt(list);
return gameView;
}
public void removeGame() {
UserManager.getInstance().getUser(userId).removeGame(playerId);
}
public UUID getGameId() {
return game.getId();
}
} }

View file

@ -30,6 +30,7 @@ package mage.server.game;
import java.rmi.RemoteException; import java.rmi.RemoteException;
import java.util.UUID; import java.util.UUID;
import mage.game.Game;
import mage.interfaces.callback.ClientCallback; import mage.interfaces.callback.ClientCallback;
import mage.server.User; import mage.server.User;
import mage.server.UserManager; import mage.server.UserManager;
@ -46,39 +47,39 @@ public class GameWatcher {
protected final static Logger logger = Logger.getLogger(GameWatcher.class); protected final static Logger logger = Logger.getLogger(GameWatcher.class);
protected UUID userId; protected UUID userId;
protected UUID gameId; protected Game game;
protected boolean killed = false; protected boolean killed = false;
public GameWatcher(UUID userId, UUID gameId) { public GameWatcher(UUID userId, Game game) {
this.userId = userId; this.userId = userId;
this.gameId = gameId; this.game = game;
} }
public boolean init(final GameView gameView) { public boolean init() {
if (!killed) { if (!killed) {
User user = UserManager.getInstance().getUser(userId); User user = UserManager.getInstance().getUser(userId);
if (user != null) { if (user != null) {
user.fireCallback(new ClientCallback("gameInit", gameId, gameView)); user.fireCallback(new ClientCallback("gameInit", game.getId(), getGameView()));
return true; return true;
} }
} }
return false; return false;
} }
public void update(final GameView gameView) { public void update() {
if (!killed) { if (!killed) {
User user = UserManager.getInstance().getUser(userId); User user = UserManager.getInstance().getUser(userId);
if (user != null) { if (user != null) {
user.fireCallback(new ClientCallback("gameUpdate", gameId, gameView)); user.fireCallback(new ClientCallback("gameUpdate", game.getId(), getGameView()));
} }
} }
} }
public void inform(final String message, final GameView gameView) { public void inform(final String message) {
if (!killed) { if (!killed) {
User user = UserManager.getInstance().getUser(userId); User user = UserManager.getInstance().getUser(userId);
if (user != null) { if (user != null) {
user.fireCallback(new ClientCallback("gameInform", gameId, new GameClientMessage(gameView, message))); user.fireCallback(new ClientCallback("gameInform", game.getId(), new GameClientMessage(getGameView(), message)));
} }
} }
} }
@ -87,7 +88,7 @@ public class GameWatcher {
if (!killed) { if (!killed) {
User user = UserManager.getInstance().getUser(userId); User user = UserManager.getInstance().getUser(userId);
if (user != null) { if (user != null) {
user.fireCallback(new ClientCallback("gameOver", gameId, message)); user.fireCallback(new ClientCallback("gameOver", game.getId(), message));
} }
} }
} }
@ -96,18 +97,22 @@ public class GameWatcher {
if (!killed) { if (!killed) {
User user = UserManager.getInstance().getUser(userId); User user = UserManager.getInstance().getUser(userId);
if (user != null) { if (user != null) {
user.fireCallback(new ClientCallback("gameError", gameId, message)); user.fireCallback(new ClientCallback("gameError", game.getId(), message));
} }
} }
} }
protected void handleRemoteException(RemoteException ex) { protected void handleRemoteException(RemoteException ex) {
logger.fatal("GameWatcher error", ex); logger.fatal("GameWatcher error", ex);
GameManager.getInstance().kill(gameId, userId); GameManager.getInstance().kill(game.getId(), userId);
} }
public void setKilled() { public void setKilled() {
killed = true; killed = true;
} }
public GameView getGameView() {
return new GameView(game.getState(), game);
}
} }