mirror of
https://github.com/correl/mage.git
synced 2024-12-24 11:50:45 +00:00
* Server: fixed that too much permanents or mana sources on battlefield can crush or slow down the server (#6938);
This commit is contained in:
parent
de110a92dd
commit
3430013f8d
2 changed files with 61 additions and 16 deletions
|
@ -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 creature’s 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);
|
||||
}
|
||||
}
|
|
@ -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<>();
|
||||
|
|
Loading…
Reference in a new issue