mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
[Simulated AI] Fix memory leak caused by SAs making it into the wrong game's spabCache.
The problem was that during copying of SAs, the spabCache was not updated correctly due to the code being in the wrong order. A further problem was that activatingPlayer was not being correctly set on the new abilities, so you would end up referencing a Player from a different Game object from an SA that was in a spabCache, resulting in many previously-simulated Game objects being leaked through this reference chain. This change fixes both of those problems and also adds a validation function that checks the contents of the spabCache at every game copy.
This commit is contained in:
@@ -108,7 +108,18 @@ public class GameCopier {
|
||||
((PlayerZoneBattlefield) p.getZone(ZoneType.Battlefield)).setTriggers(true);
|
||||
}
|
||||
newGame.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
|
||||
|
||||
for (Card c : newGame.getCardsInGame()) {
|
||||
for (SpellAbility sa : c.getSpellAbilities()) {
|
||||
Player activatingPlayer = sa.getActivatingPlayer();
|
||||
if (activatingPlayer != null && activatingPlayer.getGame() != newGame) {
|
||||
sa.setActivatingPlayer(gameObjectMap.map(activatingPlayer));
|
||||
}
|
||||
}
|
||||
}
|
||||
origGame.validateSpabCache();
|
||||
newGame.validateSpabCache();
|
||||
|
||||
// Undo effects first before calculating them below, to avoid them applying twice.
|
||||
for (StaticEffect effect : origGame.getStaticEffects().getEffects()) {
|
||||
effect.removeMapped(gameObjectMap);
|
||||
|
||||
@@ -199,6 +199,16 @@ public class Game {
|
||||
public void removeSpellAbility(SpellAbility spellAbility) {
|
||||
spabCache.remove(spellAbility.getId());
|
||||
}
|
||||
public void validateSpabCache() {
|
||||
for (SpellAbility sa : spabCache.getValues()) {
|
||||
if (sa.getHostCard() != null && sa.getHostCard().getGame() != this) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if (sa.getActivatingPlayer() != null && sa.getActivatingPlayer().getGame() != this) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Game(List<RegisteredPlayer> players0, GameRules rules0, Match match0) { /* no more zones to map here */
|
||||
rules = rules0;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.game;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@@ -40,4 +41,8 @@ public class GameEntityCache<Entity extends IIdentifiable, View extends Trackabl
|
||||
public void clear() {
|
||||
entityCache.clear();
|
||||
}
|
||||
|
||||
public Collection<Entity> getValues() {
|
||||
return entityCache.values();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,9 +203,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
@Override
|
||||
public void setHostCard(final Card c) {
|
||||
if (hostCard == c) { return; }
|
||||
super.setHostCard(c);
|
||||
Game oldGame = hostCard != null ? hostCard.getGame() : null;
|
||||
Game newGame = c != null ? c.getGame() : null;
|
||||
super.setHostCard(c);
|
||||
if (oldGame != newGame) {
|
||||
if (oldGame != null) { oldGame.removeSpellAbility(this); }
|
||||
if (newGame != null) { newGame.addSpellAbility(this); }
|
||||
|
||||
Reference in New Issue
Block a user