* Server: fixed that too much permanents or mana sources on battlefield can crush or slow down the server (#6938);

This commit is contained in:
Oleg Agafonov 2020-08-04 05:36:43 +04:00
parent de110a92dd
commit 3430013f8d
2 changed files with 61 additions and 16 deletions

View file

@ -343,4 +343,28 @@ public class ConvokeTest extends CardTestPlayerBaseWithAIHelps {
assertPermanentCount(playerA, "Hogaak, Arisen Necropolis", 1);
}
@Test
public void test_Mana_MemoryOverflow() {
// possible bug: convoke mana calculation can overflow server's memory (too much mana options from too much permanents)
// https://github.com/magefree/mage/issues/6938
// Create X 1/1 white Soldier creature tokens with lifelink.
// Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creatures color.)
addCard(Zone.HAND, playerA, "March of the Multitudes", 1); // {X}{G}{W}{W}
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 500);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "March of the Multitudes");
setChoice(playerA, "X=1");
addTarget(playerA, "Grizzly Bears"); // convoke pay
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
assertPermanentCount(playerA, "Soldier", 1);
}
}

View file

@ -1,30 +1,27 @@
package mage.abilities.mana;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.TapSourceCost;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ManaEvent;
import mage.players.Player;
import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*
* @author BetaSteward_at_googlemail.com
*
* <p>
* this class is used to build a list of all possible mana combinations it can
* be used to find all the ways to pay a mana cost or all the different mana
* combinations available to a player
*
* <p>
* TODO: Conditional Mana is not supported yet. The mana adding removes the
* condition of conditional mana
*
*/
public class ManaOptions extends ArrayList<Mana> {
@ -87,6 +84,8 @@ public class ManaOptions extends ArrayList<Mana> {
}
}
}
forceManaDeduplication();
}
private void addManaVariation(List<Mana> netManas, ActivatedManaAbilityImpl ability, Game game) {
@ -102,6 +101,8 @@ public class ManaOptions extends ArrayList<Mana> {
}
}
}
forceManaDeduplication();
}
private static List<List<Mana>> getSimulatedTriggeredManaFromPlayer(Game game, Ability ability) {
@ -189,9 +190,9 @@ public class ManaOptions extends ArrayList<Mana> {
if (startingMana.includesMana(manaCosts)) { // can pay the mana costs to use the ability
if (!subtractCostAddMana(manaCosts, triggeredManaVariation, ability.getCosts().isEmpty(), startingMana)) {
// the starting mana includes mana parts that the increased mana does not include, so add starting mana also as an option
add(prevMana);
}
wasUsable = true;
add(prevMana);
}
wasUsable = true;
} else {
// mana costs can't be paid so keep starting mana
add(prevMana);
@ -251,10 +252,13 @@ public class ManaOptions extends ArrayList<Mana> {
}
}
}
if (this.size() > 30 || replaces > 30) {
logger.trace("ManaOptionsCosts " + this.size() + " Ign:" + replaces + " => " + this.toString());
logger.trace("Abilities: " + abilities.toString());
}
forceManaDeduplication();
return wasUsable;
}
@ -302,6 +306,8 @@ public class ManaOptions extends ArrayList<Mana> {
}
}
}
forceManaDeduplication();
}
public void addMana(Mana addMana) {
@ -311,6 +317,8 @@ public class ManaOptions extends ArrayList<Mana> {
for (Mana mana : this) {
mana.add(addMana);
}
forceManaDeduplication();
}
public void addMana(ManaOptions options) {
@ -335,6 +343,17 @@ public class ManaOptions extends ArrayList<Mana> {
}
}
}
forceManaDeduplication();
}
private void forceManaDeduplication() {
// memory overflow protection - force de-duplication on too much mana sources
// bug example: https://github.com/magefree/mage/issues/6938
// use it after new mana adding
if (this.size() > 1000) {
this.removeDuplicated();
}
}
public ManaOptions copy() {
@ -408,14 +427,16 @@ public class ManaOptions extends ArrayList<Mana> {
}
}
forceManaDeduplication();
return oldManaWasReplaced;
}
/**
*
* @param number of generic mana
* @param number of generic mana
* @param manaAvailable
* @return
* @return
*/
public static List<Mana> getPossiblePayCombinations(int number, Mana manaAvailable) {
List<Mana> payCombinations = new ArrayList<>();