From 48d7afa375b4f10bbb2d0ef6fa8582f710c7ca96 Mon Sep 17 00:00:00 2001 From: BetaSteward Date: Wed, 24 Aug 2011 12:59:51 -0400 Subject: [PATCH] catch spell and activated ability errors and rollback state - show error dialog on client --- .../src/main/java/mage/client/MageFrame.java | 18 ++++ .../client/remote/CallbackClientImpl.java | 5 +- .../java/mage/server/game/GameController.java | 5 +- .../mage/abilities/keyword/KickerAbility.java | 6 +- Mage/src/mage/game/Game.java | 7 +- Mage/src/mage/game/GameImpl.java | 100 +++++++++++------- Mage/src/mage/players/PlayerImpl.java | 41 +++---- 7 files changed, 114 insertions(+), 68 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 87dc8b40702..186ccb9a107 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -106,6 +106,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { private static Session session; private ConnectDialog connectDialog; + private ErrorDialog errorDialog; private static CallbackClient callbackClient; private static Preferences prefs = Preferences.userNodeForPackage(MageFrame.class); private JLabel title; @@ -188,6 +189,8 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { callbackClient = new CallbackClientImpl(this); connectDialog = new ConnectDialog(); desktopPane.add(connectDialog, JLayeredPane.POPUP_LAYER); + errorDialog = new ErrorDialog(); + desktopPane.add(errorDialog, JLayeredPane.POPUP_LAYER); ui.addComponent(MageComponents.DESKTOP_PANE, desktopPane); try { @@ -834,6 +837,21 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { } } + public void showErrorDialog(final String title, final String message) { + if (SwingUtilities.isEventDispatchThread()) { + errorDialog.showDialog(title, message); + } + else { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + errorDialog.showDialog(title, message); + } + }); + } + + } + public void showCollectionViewer() { this.collectionViewerPane.setVisible(true); setActive(collectionViewerPane); diff --git a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java index 819537c8384..ac2411c51ca 100644 --- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java +++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java @@ -141,10 +141,7 @@ public class CallbackClientImpl implements CallbackClient { } } else if (callback.getMethod().equals("gameError")) { - GamePanel panel = frame.getGame(callback.getObjectId()); - if (panel != null) { - panel.modalMessage((String) callback.getData()); - } + frame.showErrorDialog("Game Error", (String) callback.getData()); } else if (callback.getMethod().equals("gameAsk")) { GameClientMessage message = (GameClientMessage) callback.getData(); diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index 4095563bcf4..3d752db72d4 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -60,12 +60,12 @@ import mage.game.events.TableEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.server.ChatManager; +import mage.server.Main; import mage.server.UserManager; import mage.server.util.SystemUtil; import mage.server.util.Splitter; import mage.server.util.ThreadExecutor; import mage.sets.Sets; -import mage.sets.alarareborn.EnlistedWurm; import mage.view.*; import mage.view.ChatMessage.MessageColor; import org.apache.log4j.Logger; @@ -506,8 +506,9 @@ public class GameController implements GameCallback { } private void error(String message) { + String msg = message + "\nServer version: " + Main.getVersion().toString(); for (final Entry entry: gameSessions.entrySet()) { - entry.getValue().gameError(message); + entry.getValue().gameError(msg); } } diff --git a/Mage/src/mage/abilities/keyword/KickerAbility.java b/Mage/src/mage/abilities/keyword/KickerAbility.java index 41e77037fb1..bf2d71a148a 100644 --- a/Mage/src/mage/abilities/keyword/KickerAbility.java +++ b/Mage/src/mage/abilities/keyword/KickerAbility.java @@ -68,13 +68,13 @@ public class KickerAbility extends StaticAbility { // replace by card name or just plain "this" message = message.replace("{this}", card == null ? "this" : card.getName()); if (player.chooseUse(getEffects().get(0).getOutcome(), message, game)) { - game.bookmarkState(); + int bookmark = game.bookmarkState(); if (super.activate(game, noMana)) { - game.removeLastBookmark(); + game.removeBookmark(bookmark); kicked = true; } else { - game.restoreState(); + game.restoreState(bookmark); kicked = false; } return kicked; diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index 0e2f05114dd..3aeaced2dcf 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -42,7 +42,6 @@ import mage.MageItem; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.Modes; import mage.abilities.TriggeredAbilities; import mage.abilities.TriggeredAbility; import mage.abilities.effects.ContinuousEffect; @@ -158,9 +157,9 @@ public interface Game extends MageItem, Serializable { //game transaction methods public void saveState(); - public void bookmarkState(); - public void restoreState(); - public void removeLastBookmark(); + public int bookmarkState(); + public void restoreState(int bookmark); + public void removeBookmark(int bookmark); // game options public void setGameOptions(GameOptions options); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 99b22f14fdd..832f2c64aaf 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -73,6 +73,7 @@ public abstract class GameImpl> implements Game, Serializa private static FilterAura filterAura = new FilterAura(); private static FilterEquipment filterEquipment = new FilterEquipment(); private static FilterFortification filterFortification = new FilterFortification(); + private static Random rnd = new Random(); private transient Stack savedStates = new Stack(); private transient Object customData; @@ -296,28 +297,37 @@ public abstract class GameImpl> implements Game, Serializa } @Override - public void bookmarkState() { + public int bookmarkState() { if (!simulation) { saveState(); if (logger.isDebugEnabled()) logger.debug("Bookmarking state: " + gameStates.getSize()); savedStates.push(gameStates.getSize() - 1); + return savedStates.size(); + } + return 0; + } + + @Override + public void restoreState(int bookmark) { + if (!simulation) { + if (bookmark != 0) { + int stateNum = savedStates.get(bookmark - 1); + removeBookmark(bookmark); + GameState restore = gameStates.rollback(stateNum); + if (restore != null) + state.restore(restore); + } } } @Override - public void restoreState() { + public void removeBookmark(int bookmark) { if (!simulation) { - GameState restore = gameStates.rollback(savedStates.pop()); - if (restore != null) - state.restore(restore); - } - } - - @Override - public void removeLastBookmark() { - if (!simulation) { - savedStates.pop(); + if (bookmark != 0) { + while (savedStates.size() > bookmark) + savedStates.pop(); + } } } @@ -453,7 +463,7 @@ public abstract class GameImpl> implements Game, Serializa protected UUID pickChoosingPlayer() { UUID[] players = getPlayers().keySet().toArray(new UUID[0]); - UUID playerId = players[new Random().nextInt(players.length)]; + UUID playerId = players[rnd.nextInt(players.length)]; fireInformEvent(state.getPlayer(playerId).getName() + " won the toss"); return playerId; } @@ -502,37 +512,55 @@ public abstract class GameImpl> implements Game, Serializa @Override public void playPriority(UUID activePlayerId) { + int bookmark = 0; try { while (!isGameOver()) { state.getPlayers().resetPassed(); state.getPlayerList().setCurrent(activePlayerId); Player player; while (!isGameOver()) { - player = getPlayer(state.getPlayerList().get()); - state.setPriorityPlayerId(player.getId()); - while (!player.isPassed() && !player.hasLost() && !player.hasLeft() && !isGameOver()) { - checkStateAndTriggered(); - if (isGameOver()) return; - // resetPassed should be called if player performs any action - player.priority(this); - if (isGameOver()) return; - applyEffects(); - } - if (isGameOver()) return; - if (allPassed()) { - if (!state.getStack().isEmpty()) { - //20091005 - 115.4 - state.getStack().resolve(this); - applyEffects(); - state.getPlayers().resetPassed(); - fireUpdatePlayersEvent(); - state.getRevealed().reset(); - break; - } else - return; - } + try { + if (bookmark == 0) + bookmark = bookmarkState(); + player = getPlayer(state.getPlayerList().get()); + state.setPriorityPlayerId(player.getId()); + while (!player.isPassed() && !player.hasLost() && !player.hasLeft() && !isGameOver()) { + checkStateAndTriggered(); + if (isGameOver()) return; + // resetPassed should be called if player performs any action + player.priority(this); + if (isGameOver()) return; + applyEffects(); + } + if (isGameOver()) return; + if (allPassed()) { + if (!state.getStack().isEmpty()) { + //20091005 - 115.4 + state.getStack().resolve(this); + applyEffects(); + state.getPlayers().resetPassed(); + fireUpdatePlayersEvent(); + state.getRevealed().reset(); + break; + } else { + removeBookmark(bookmark); + return; + } + } + } + catch (Exception ex) { + logger.fatal("Game exception ", ex); + this.fireErrorEvent("Game exception occurred: " + ex.getMessage() + " - " + ex.getStackTrace()[0]); + restoreState(bookmark); + bookmark = 0; + continue; + } +// removeBookmark(bookmark); +// bookmark = 0; state.getPlayerList().getNext(); } + removeBookmark(bookmark); + bookmark = 0; } } catch (Exception ex) { logger.fatal("Game exception ", ex); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 0ce2d4672e3..cb58df490c3 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -73,6 +73,8 @@ import mage.watchers.common.BloodthirstWatcher; public abstract class PlayerImpl> implements Player, Serializable { + private static Random rnd = new Random(); + protected boolean abort; protected final UUID playerId; protected String name; @@ -436,7 +438,7 @@ public abstract class PlayerImpl> implements Player, Ser Card card = game.getCard(ability.getSourceId()); if (card != null) { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getId(), ability.getSourceId(), playerId))) { - game.bookmarkState(); + int bookmark = game.bookmarkState(); card.cast(game, game.getZone(card.getId()), ability, playerId); removeFromHand(card, game); Ability spellAbility = game.getStack().getSpell(ability.getId()).getSpellAbility(); @@ -446,10 +448,10 @@ public abstract class PlayerImpl> implements Player, Ser } game.fireEvent(GameEvent.getEvent(GameEvent.EventType.SPELL_CAST, spellAbility.getId(), playerId)); game.fireInformEvent(name + " casts " + card.getName()); - game.removeLastBookmark(); + game.removeBookmark(bookmark); return true; } - game.restoreState(); + game.restoreState(bookmark); } } return false; @@ -459,45 +461,45 @@ public abstract class PlayerImpl> implements Player, Ser public boolean playLand(Card card, Game game) { //20091005 - 305.1 if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), playerId))) { - game.bookmarkState(); + int bookmark = game.bookmarkState(); removeFromHand(card, game); if (card.putOntoBattlefield(game, Zone.HAND, null, playerId)) { landsPlayed++; game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), playerId)); game.fireInformEvent(name + " plays " + card.getName()); - game.removeLastBookmark(); + game.removeBookmark(bookmark); return true; } - game.restoreState(); + game.restoreState(bookmark); } return false; } protected boolean playManaAbility(ManaAbility ability, Game game) { - game.bookmarkState(); + int bookmark = game.bookmarkState(); if (ability.activate(game, false)) { ability.resolve(game); - game.removeLastBookmark(); + game.removeBookmark(bookmark); return true; } - game.restoreState(); + game.restoreState(bookmark); return false; } protected boolean playAbility(ActivatedAbility ability, Game game) { //20091005 - 602.2a if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.ACTIVATE_ABILITY, ability.getId(), ability.getSourceId(), playerId))) { - game.bookmarkState(); + int bookmark = game.bookmarkState(); ability.newId(); game.getStack().push(new StackAbility(ability, playerId)); String message = ability.getActivatedMessage(game); if (ability.activate(game, false)) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.ACTIVATED_ABILITY, ability.getId(), ability.getSourceId(), playerId)); game.fireInformEvent(name + message); - game.removeLastBookmark(); + game.removeBookmark(bookmark); return true; } - game.restoreState(); + game.restoreState(bookmark); } return false; } @@ -505,16 +507,16 @@ public abstract class PlayerImpl> implements Player, Ser protected boolean specialAction(SpecialAction action, Game game) { //20091005 - 114 if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.ACTIVATE_ABILITY, action.getSourceId(), action.getId(), playerId))) { - game.bookmarkState(); + int bookmark = game.bookmarkState(); if (action.activate(game, false)) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.ACTIVATED_ABILITY, action.getSourceId(), action.getId(), playerId)); game.fireInformEvent(name + action.getActivatedMessage(game)); if (action.resolve(game)) { - game.removeLastBookmark(); + game.removeBookmark(bookmark); return true; } } - game.restoreState(); + game.restoreState(bookmark); } return false; } @@ -553,16 +555,16 @@ public abstract class PlayerImpl> implements Player, Ser @Override public boolean triggerAbility(TriggeredAbility source, Game game) { //20091005 - 603.3c, 603.3d - game.bookmarkState(); + int bookmark = game.bookmarkState(); TriggeredAbility ability = (TriggeredAbility) source.copy(); if (ability.getTargets().canChoose(ability.getSourceId(), playerId, game)) { game.getStack().push(new StackAbility(ability, playerId)); if (ability.activate(game, false)) { - game.removeLastBookmark(); + game.removeBookmark(bookmark); return true; } } - game.restoreState(); + game.restoreState(bookmark); return false; } @@ -796,6 +798,7 @@ public abstract class PlayerImpl> implements Player, Ser this.inRange = player.getInRange(); this.landsPlayed = player.getLandsPlayed(); this.name = player.getName(); + this.passed = player.isPassed(); } @Override @@ -932,7 +935,7 @@ public abstract class PlayerImpl> implements Player, Ser */ @Override public boolean flipCoin(Game game) { - boolean result = new Random().nextBoolean(); + boolean result = rnd.nextBoolean(); game.informPlayers("[Flip a coin] " + getName() + (result ? " won." : " lost.")); return result; }