* Delay triggered abilities - fixed that copied spell with multiple delayed triggered abilities can freeze the game or missing from stack (#5437);

This commit is contained in:
Oleg Agafonov 2020-01-23 07:04:19 +04:00
parent 5aaec29361
commit 2ca1643b54
4 changed files with 53 additions and 28 deletions

View file

@ -13,14 +13,18 @@ import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.target.targetpointer.TargetPointer;
import mage.util.GameLog;
import org.apache.log4j.Logger;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author BetaSteward_at_googlemail.com
*/
public class CardsView extends LinkedHashMap<UUID, CardView> {
private static final Logger LOGGER = Logger.getLogger(CardsView.class);
public CardsView() {
}
@ -151,6 +155,11 @@ public class CardsView extends LinkedHashMap<UUID, CardView> {
this.put(ability.getId(), abilityView);
}
}
if (this.size() != abilities.size()) {
LOGGER.error("Can't translate abilities list to cards view (need " + abilities.size() + ", but get " + this.size() + "). Abilities:\n"
+ abilities.stream().map(a -> a.getClass().getSimpleName() + " - " + a.getRule()).collect(Collectors.joining("\n")));
}
}
public CardsView(Collection<? extends Ability> abilities, GameState state) {

View file

@ -2,11 +2,6 @@ package mage.view;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.costs.Cost;
import mage.cards.Card;
@ -30,6 +25,12 @@ import mage.players.Player;
import mage.watchers.common.CastSpellLastTurnWatcher;
import org.apache.log4j.Logger;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -124,7 +125,6 @@ public class GameView implements Serializable {
} else {
LOGGER.fatal("Designation object not found: " + object.getName() + ' ' + object.toString() + ' ' + object.getClass().toString());
}
} else if (object instanceof StackAbility) {
StackAbility stackAbility = ((StackAbility) object);
stackAbility.newId();

View file

@ -1,9 +1,5 @@
package mage.game;
import java.io.IOException;
import java.io.Serializable;
import java.util.*;
import java.util.Map.Entry;
import mage.MageException;
import mage.MageObject;
import mage.abilities.*;
@ -68,6 +64,11 @@ import mage.util.functions.ApplyToPermanent;
import mage.watchers.common.*;
import org.apache.log4j.Logger;
import java.io.IOException;
import java.io.Serializable;
import java.util.*;
import java.util.Map.Entry;
public abstract class GameImpl implements Game, Serializable {
private static final int ROLLBACK_TURNS_MAX = 4;
@ -515,6 +516,13 @@ public abstract class GameImpl implements Game, Serializable {
if (card == null) {
card = this.getMeldCard(cardId);
}
// copied cards removes, but delayed triggered possible from it, see https://github.com/magefree/mage/issues/5437
// TODO: remove that workround after LKI rework, see GameState.copyCard
if (card == null) {
card = (Card) state.getValue(GameState.COPIED_FROM_CARD_KEY + cardId.toString());
}
return card;
}
@ -1806,10 +1814,11 @@ public abstract class GameImpl implements Game, Serializable {
while (copiedCards.hasNext()) {
Card card = copiedCards.next();
if (card instanceof SplitCardHalf || card instanceof AdventureCardSpell) {
continue; // only the main card is moves, not the halves
continue; // only the main card is moves, not the halves (cause halfes is not copied - it uses original card -- TODO: need to fix (bugs with same card copy)?
}
Zone zone = state.getZone(card.getId());
if (zone != Zone.BATTLEFIELD && zone != Zone.STACK) {
// TODO: remember LKI of copied cards here after LKI rework
switch (zone) {
case GRAVEYARD:
for (Player player : getPlayers().values()) {
@ -2779,11 +2788,9 @@ public abstract class GameImpl implements Game, Serializable {
Map<UUID, CardState> lkiCardStateMap = lkiCardState.get(zone);
if (lkiCardStateMap != null) {
CardState cardState = lkiCardStateMap.get(objectId);
if (cardState != null) {
return cardState;
}
}
}
return null;
}

View file

@ -1,8 +1,5 @@
package mage.game;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
import mage.MageObject;
import mage.abilities.*;
import mage.abilities.effects.ContinuousEffect;
@ -37,6 +34,10 @@ import mage.util.ThreadLocalStringBuilder;
import mage.watchers.Watcher;
import mage.watchers.Watchers;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author BetaSteward_at_googlemail.com
* <p>
@ -49,6 +50,8 @@ public class GameState implements Serializable, Copyable<GameState> {
private static final ThreadLocalStringBuilder threadLocalBuilder = new ThreadLocalStringBuilder(1024);
public static final String COPIED_FROM_CARD_KEY = "CopiedFromCard";
private final Players players;
private final PlayerList playerList;
private UUID choosingPlayerId; // player that makes a choice at game start
@ -601,6 +604,7 @@ public class GameState implements Serializable, Copyable<GameState> {
// public void addMessage(String message) {
// this.messages.add(message);
// }
/**
* Returns a list of all players of the game ignoring range or if a player
* has lost or left the game.
@ -1165,7 +1169,7 @@ public class GameState implements Serializable, Copyable<GameState> {
copiedCards.put(copiedCard.getId(), copiedCard);
addCard(copiedCard);
if (copiedCard.isSplitCard()) {
Card leftCard = ((SplitCard) copiedCard).getLeftHalfCard();
Card leftCard = ((SplitCard) copiedCard).getLeftHalfCard(); // TODO: must be new ID (bugs with same card copy)?
copiedCards.put(leftCard.getId(), leftCard);
addCard(leftCard);
Card rightCard = ((SplitCard) copiedCard).getRightHalfCard();
@ -1177,6 +1181,11 @@ public class GameState implements Serializable, Copyable<GameState> {
copiedCards.put(spellCard.getId(), spellCard);
addCard(spellCard);
}
// copied cards removes from game after battlefield/stack leaves, so remember it here as workaround to fix freeze, see https://github.com/magefree/mage/issues/5437
// TODO: remove that workaround after LKI will be rewritten to support cross-steps/turns data transition and support copied cards
this.setValue(COPIED_FROM_CARD_KEY + copiedCard.getId(), cardToCopy.copy());
return copiedCard;
}