From 99e2b67e042c4b720eb38804524b5bc767337f41 Mon Sep 17 00:00:00 2001 From: drdev Date: Wed, 15 Oct 2014 16:07:54 +0000 Subject: [PATCH] Prevent concurrent modification exception when moving token to a zone besides the graveyard --- .../src/main/java/forge/util/FCollection.java | 6 ++++ .../main/java/forge/util/FCollectionView.java | 1 + .../src/main/java/forge/game/GameAction.java | 34 ++++++++++++++----- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/forge-core/src/main/java/forge/util/FCollection.java b/forge-core/src/main/java/forge/util/FCollection.java index 4ca1b95f9ef..bc73dc81bcf 100644 --- a/forge-core/src/main/java/forge/util/FCollection.java +++ b/forge-core/src/main/java/forge/util/FCollection.java @@ -251,4 +251,10 @@ public class FCollection implements List, Set, FCollectionView, Clon } return subList; } + + @Override + public Iterable threadSafeIterator() { + //create a new linked list for iterating to make it thread safe and avoid concurrent modification exceptions + return new LinkedList(list); + } } diff --git a/forge-core/src/main/java/forge/util/FCollectionView.java b/forge-core/src/main/java/forge/util/FCollectionView.java index f877bfcd2c6..24515260300 100644 --- a/forge-core/src/main/java/forge/util/FCollectionView.java +++ b/forge-core/src/main/java/forge/util/FCollectionView.java @@ -13,4 +13,5 @@ public interface FCollectionView extends Iterable { int lastIndexOf(Object o); boolean contains(Object o); List subList(int fromIndex, int toIndex); + Iterable threadSafeIterator(); } \ No newline at end of file diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 1abd0efc673..5d89886f955 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -675,23 +675,32 @@ public class GameAction { if (zt == ZoneType.Battlefield) { continue; } - for (Card c : p.getCardsIn(zt)) { + Iterable cards = p.getCardsIn(zt).threadSafeIterator(); + for (Card c : cards) { // If a token is in a zone other than the battlefield, it ceases to exist. checkAgain |= stateBasedAction704_5d(c); } } } - List noRegCreats = new ArrayList(); - List desCreats = new ArrayList(); - for (Card c : game.getCardsIn(ZoneType.Battlefield)) { + Iterable cards = game.getCardsIn(ZoneType.Battlefield).threadSafeIterator(); + List noRegCreats = null; + List desCreats = null; + for (Card c : cards) { if (c.isCreature()) { // Rule 704.5f - Put into grave (no regeneration) for toughness <= 0 if (c.getNetToughness() <= 0) { + if (noRegCreats == null) { + noRegCreats = new LinkedList(); + } noRegCreats.add(c); checkAgain = true; - } else if (c.hasKeyword("CARDNAME can't be destroyed by lethal damage unless lethal damage dealt by a single source is marked on it.")) { + } + else if (c.hasKeyword("CARDNAME can't be destroyed by lethal damage unless lethal damage dealt by a single source is marked on it.")) { for (final Integer dmg : c.getReceivedDamageFromThisTurn().values()) { if (c.getNetToughness() <= dmg.intValue()) { + if (desCreats == null) { + desCreats = new LinkedList(); + } desCreats.add(c); checkAgain = true; break; @@ -700,6 +709,9 @@ public class GameAction { } // Rule 704.5g - Destroy due to lethal damage else if (c.getNetToughness() <= c.getDamage()) { + if (desCreats == null) { + desCreats = new LinkedList(); + } desCreats.add(c); checkAgain = true; } @@ -721,11 +733,15 @@ public class GameAction { } } - for (Card c : noRegCreats) { - sacrificeDestroy(c); + if (noRegCreats != null) { + for (Card c : noRegCreats) { + sacrificeDestroy(c); + } } - for (Card c : desCreats) { - destroy(c, null); + if (desCreats != null) { + for (Card c : desCreats) { + destroy(c, null); + } } if (game.getTriggerHandler().runWaitingTriggers()) {