diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index 018e6b4a1a3..e908f8898bb 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -66,6 +66,7 @@ public class Session { private final Date timeConnected; private boolean isAdmin = false; private final AsynchInvokerCallbackHandler callbackHandler; + private boolean error = false; private final ReentrantLock lock; @@ -370,10 +371,14 @@ public class Session { } public void fireCallback(final ClientCallback call) { + if (error) { + return; + } try { call.setMessageId(messageId++); callbackHandler.handleCallbackOneway(new Callback(call)); } catch (HandleCallbackException ex) { + error = true; // to reduce repeated SESSION CALLBACK EXCEPTION UserManager.instance.getUser(userId).ifPresent(user -> { user.setUserState(User.UserState.Disconnected); logger.warn("SESSION CALLBACK EXCEPTION - " + user.getName() + " userId " + userId + " - cause: " + getBasicCause(ex).toString()); diff --git a/Mage.Server/src/main/java/mage/server/User.java b/Mage.Server/src/main/java/mage/server/User.java index e1f0abd8fd5..37da6692ba5 100644 --- a/Mage.Server/src/main/java/mage/server/User.java +++ b/Mage.Server/src/main/java/mage/server/User.java @@ -358,14 +358,16 @@ public class User { for (Entry entry : tables.entrySet()) { ccJoinedTable(entry.getValue().getRoomId(), entry.getValue().getId(), entry.getValue().isTournament()); } - for (Entry entry : userTournaments.entrySet()) { - TournamentController tournamentController = TournamentManager.instance.getTournamentController(entry.getValue()); + for (Iterator> iterator = userTournaments.entrySet().iterator(); iterator.hasNext();) { + Entry next = iterator.next(); + TournamentController tournamentController = TournamentManager.instance.getTournamentController(next.getValue()); if (tournamentController != null) { - ccTournamentStarted(entry.getValue(), entry.getKey()); - tournamentController.rejoin(entry.getKey()); + ccTournamentStarted(next.getValue(), next.getKey()); + tournamentController.rejoin(next.getKey()); + } else { + iterator.remove(); // tournament has ended meanwhile } } - for (Entry entry : gameSessions.entrySet()) { ccGameStarted(entry.getValue().getGameId(), entry.getKey()); entry.getValue().init(); diff --git a/Mage.Sets/src/mage/cards/e/EternalDominion.java b/Mage.Sets/src/mage/cards/e/EternalDominion.java index 9ae0fd09d98..7c7c777d81e 100644 --- a/Mage.Sets/src/mage/cards/e/EternalDominion.java +++ b/Mage.Sets/src/mage/cards/e/EternalDominion.java @@ -53,14 +53,15 @@ import mage.target.common.TargetOpponent; public class EternalDominion extends CardImpl { public EternalDominion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{7}{U}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{7}{U}{U}{U}"); // Search target opponent's library for an artifact, creature, enchantment, or land card. // Put that card onto the battlefield under your control. Then that player shuffles his or her library. this.getSpellAbility().addEffect(new EternalDominionEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); - // Epic + // Epic (For the rest of the game, you can't cast spells. At the beginning of each of your upkeeps + // for the rest of the game, copy this spell except for its epic ability. If the spell has targets, you may choose new targets for the copy) this.getSpellAbility().addEffect(new EpicEffect()); } diff --git a/Mage.Sets/src/mage/cards/p/PathOfMettle.java b/Mage.Sets/src/mage/cards/p/PathOfMettle.java index c8242bb6c2b..93bd1198ed2 100644 --- a/Mage.Sets/src/mage/cards/p/PathOfMettle.java +++ b/Mage.Sets/src/mage/cards/p/PathOfMettle.java @@ -126,13 +126,15 @@ class PathOfMettleTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { int attackerCount = 0; - if (game.getCombat().getAttackingPlayerId().equals(getControllerId())) { - for (UUID attacker : game.getCombat().getAttackers()) { - if (filter.match(game.getPermanent(attacker), game)) { - attackerCount++; + if (game.getCombat() != null) { + if (getControllerId().equals(game.getCombat().getAttackingPlayerId())) { + for (UUID attacker : game.getCombat().getAttackers()) { + if (filter.match(game.getPermanent(attacker), game)) { + attackerCount++; + } } + return attackerCount >= 2; } - return attackerCount >= 2; } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/AttachEffect.java b/Mage/src/main/java/mage/abilities/effects/common/AttachEffect.java index efe93399ea8..9716c790aa7 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/AttachEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/AttachEffect.java @@ -74,7 +74,7 @@ public class AttachEffect extends OneShotEffect { if (player != null) { return player.addAttachment(source.getSourceId(), game); } - if (source.getTargets().get(0) instanceof TargetCard) { // e.g. Spellweaver Volute + if (!source.getTargets().isEmpty() && source.getTargets().get(0) instanceof TargetCard) { // e.g. Spellweaver Volute Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null) { return card.addAttachment(source.getSourceId(), game); diff --git a/Mage/src/main/java/mage/game/match/MatchImpl.java b/Mage/src/main/java/mage/game/match/MatchImpl.java index 961996df324..caccd3e463c 100644 --- a/Mage/src/main/java/mage/game/match/MatchImpl.java +++ b/Mage/src/main/java/mage/game/match/MatchImpl.java @@ -433,7 +433,9 @@ public abstract class MatchImpl implements Match { sb.append(" QUITTED"); } sb.append("
"); - sb.append("DeckHash: ").append(mp.getDeck().getDeckHashCode()).append("
"); + if (mp.getDeck() != null) { + sb.append("DeckHash: ").append(mp.getDeck().getDeckHashCode()).append("
"); + } } if (getDraws() > 0) { sb.append(" Draws: ").append(getDraws()).append("
"); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index a0723b0ba5f..b28ba370b57 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -53,7 +53,6 @@ import mage.cards.Card; import mage.cards.Cards; import mage.cards.CardsImpl; import mage.cards.SplitCard; -import mage.cards.SplitCardHalf; import mage.cards.decks.Deck; import mage.choices.ChoiceImpl; import mage.constants.*; @@ -858,9 +857,11 @@ public abstract class PlayerImpl implements Player, Serializable { Cards cards = new CardsImpl(cardsToLibrary); // prevent possible ConcurrentModificationException if (!anyOrder) { while (!cards.isEmpty()) { - UUID cardId = cards.getRandom(game).getId(); - cards.remove(cardId); - moveObjectToLibrary(cardId, source == null ? null : source.getSourceId(), game, false, false); + Card card = cards.getRandom(game); + if (card != null) { + cards.remove(card); + moveObjectToLibrary(card.getId(), source == null ? null : source.getSourceId(), game, false, false); + } } } else { TargetCard target = new TargetCard(Zone.ALL, new FilterCard("card to put on the bottom of your library (last one chosen will be bottommost)")); @@ -1866,7 +1867,7 @@ public abstract class PlayerImpl implements Player, Serializable { player.gainLife(actualDamage, game); } // Unstable ability - Earl of Squirrel - if (sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) { + if (sourceAbilities != null && sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) { Player player = game.getPlayer(sourceControllerId); new SquirrelToken().putOntoBattlefield(actualDamage, game, sourceId, player.getId()); }