Added locking UI input, added guards to playAbility, playLand and discard to disallow their execution from EDT

merged trunk up to 20537
This commit is contained in:
Maxmtg
2013-03-22 09:18:04 +00:00
parent bdbd345ecd
commit 55edddf27e
21 changed files with 278 additions and 341 deletions

3
.gitattributes vendored
View File

@@ -13696,6 +13696,7 @@ src/main/java/forge/Command.java svneol=native#text/plain
src/main/java/forge/CommandList.java svneol=native#text/plain src/main/java/forge/CommandList.java svneol=native#text/plain
src/main/java/forge/Constant.java svneol=native#text/plain src/main/java/forge/Constant.java svneol=native#text/plain
src/main/java/forge/CounterType.java svneol=native#text/plain src/main/java/forge/CounterType.java svneol=native#text/plain
src/main/java/forge/FThreads.java -text
src/main/java/forge/GameEntity.java -text src/main/java/forge/GameEntity.java -text
src/main/java/forge/GameLog.java -text src/main/java/forge/GameLog.java -text
src/main/java/forge/ImageCache.java svneol=native#text/plain src/main/java/forge/ImageCache.java svneol=native#text/plain
@@ -14080,6 +14081,7 @@ src/main/java/forge/control/input/InputAttack.java svneol=native#text/plain
src/main/java/forge/control/input/InputBlock.java svneol=native#text/plain src/main/java/forge/control/input/InputBlock.java svneol=native#text/plain
src/main/java/forge/control/input/InputCleanup.java svneol=native#text/plain src/main/java/forge/control/input/InputCleanup.java svneol=native#text/plain
src/main/java/forge/control/input/InputControl.java svneol=native#text/plain src/main/java/forge/control/input/InputControl.java svneol=native#text/plain
src/main/java/forge/control/input/InputLockUI.java -text
src/main/java/forge/control/input/InputMulligan.java svneol=native#text/plain src/main/java/forge/control/input/InputMulligan.java svneol=native#text/plain
src/main/java/forge/control/input/InputPassPriority.java svneol=native#text/plain src/main/java/forge/control/input/InputPassPriority.java svneol=native#text/plain
src/main/java/forge/control/input/InputPayDiscardCost.java -text src/main/java/forge/control/input/InputPayDiscardCost.java -text
@@ -14520,7 +14522,6 @@ src/main/java/forge/util/MyRandom.java svneol=native#text/plain
src/main/java/forge/util/PredicateString.java -text src/main/java/forge/util/PredicateString.java -text
src/main/java/forge/util/ReflectionUtil.java -text src/main/java/forge/util/ReflectionUtil.java -text
src/main/java/forge/util/TextUtil.java -text src/main/java/forge/util/TextUtil.java -text
src/main/java/forge/util/ThreadUtil.java svneol=native#text/plain
src/main/java/forge/util/XmlUtil.java -text src/main/java/forge/util/XmlUtil.java -text
src/main/java/forge/util/package-info.java -text src/main/java/forge/util/package-info.java -text
src/main/java/forge/util/storage/IStorage.java -text src/main/java/forge/util/storage/IStorage.java -text

View File

@@ -0,0 +1,94 @@
package forge;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.SwingUtilities;
import forge.control.input.InputLockUI;
/**
* TODO: Write javadoc for this type.
*
*/
public class FThreads {
private final static ExecutorService threadPool = Executors.newCachedThreadPool();
public static ExecutorService getCachedPool() {
return threadPool;
}
// This pool is designed to parallel CPU or IO intensive tasks like parse cards or download images, assuming a load factor of 0.5
public final static ExecutorService getComputingPool(float loadFactor) {
return Executors.newFixedThreadPool((int)(Runtime.getRuntime().availableProcessors() / (1-loadFactor)));
}
public static boolean isMultiCoreSystem() {
return Runtime.getRuntime().availableProcessors() > 1;
}
/** Checks if calling method uses event dispatch thread.
* Exception thrown if method is on "wrong" thread.
* A boolean is passed to indicate if the method must be EDT or not.
*
* @param methodName   String, part of the custom exception message.
* @param mustBeEDT   boolean: true = exception if not EDT, false = exception if EDT
*/
public static void checkEDT(final String methodName, final boolean mustBeEDT) {
boolean isEDT = SwingUtilities.isEventDispatchThread();
if ( isEDT != mustBeEDT ) {
String modalOperator = mustBeEDT ? " must be" : " may not be";
throw new IllegalStateException( methodName + modalOperator + " accessed from the event dispatch thread.");
}
}
/**
* TODO: Write javadoc for this method.
* @param runnable
*/
public static void invokeInEDT(Runnable runnable) {
SwingUtilities.invokeLater(runnable);
}
/**
* Invoke the given Runnable in an Event Dispatch Thread and wait for it to
* finish; but <B>try to use SwingUtilities.invokeLater instead whenever
* feasible.</B>
*
* Exceptions generated by SwingUtilities.invokeAndWait (if used), are
* rethrown as RuntimeExceptions.
*
* @param proc
* the Runnable to run
* @see javax.swing.SwingUtilities#invokeLater(Runnable)
*/
public static void invokeInEDTAndWait(final Runnable proc) {
if (SwingUtilities.isEventDispatchThread()) {
// Just run in the current thread.
proc.run();
} else {
try {
SwingUtilities.invokeAndWait(proc);
} catch (final InterruptedException exn) {
throw new RuntimeException(exn);
} catch (final InvocationTargetException exn) {
throw new RuntimeException(exn);
}
}
}
public static void invokeInNewThread(Runnable proc) {
invokeInNewThread(proc, false);
}
private final static InputLockUI inpuptLock = new InputLockUI();
public static void invokeInNewThread(Runnable proc, boolean lockUI) {
getCachedPool().execute(proc);
if( lockUI ) {
Singletons.getModel().getMatch().getInput().setInput(inpuptLock);
}
}
}

View File

@@ -153,13 +153,21 @@ public class StaticEffects {
} }
if (params.containsKey("AddColor")) { if (params.containsKey("AddColor")) {
addColors = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(params.get("AddColor").split( final String colors = params.get("AddColor");
" & ")))); if (colors.equals("ChosenColor")) {
addColors = CardUtil.getShortColorsString(se.getSource().getChosenColor());
} else {
addColors = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(colors.split(" & "))));
}
} }
if (params.containsKey("SetColor")) { if (params.containsKey("SetColor")) {
addColors = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(params.get("SetColor").split( final String colors = params.get("SetColor");
" & ")))); if (colors.equals("ChosenColor")) {
addColors = CardUtil.getShortColorsString(se.getSource().getChosenColor());
} else {
addColors = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(colors.split(" & "))));
}
} }
// modify players // modify players

View File

@@ -927,7 +927,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
} }
final Card source = sa.getSourceCard(); final Card source = sa.getSourceCard();
final ZoneType origin = ZoneType.smartValueOf(sa.getParam("Origin")); final ZoneType origin = ZoneType.listValueOf(sa.getParam("Origin")).get(0);
final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination")); final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
final Target tgt = sa.getTarget(); final Target tgt = sa.getTarget();

View File

@@ -165,7 +165,7 @@ public class DiscardEffect extends RevealEffectBase {
} else if (mode.equals("RevealYouChoose") || mode.equals("RevealOppChoose") || mode.equals("TgtChoose")) { } else if (mode.equals("RevealYouChoose") || mode.equals("RevealOppChoose") || mode.equals("TgtChoose")) {
// Is Reveal you choose right? I think the wrong player is // Is Reveal you choose right? I think the wrong player is
// being used? // being used?
List<Card> dPHand = p.getCardsIn(ZoneType.Hand); List<Card> dPHand = new ArrayList<Card>(p.getCardsIn(ZoneType.Hand));
if (dPHand.isEmpty()) if (dPHand.isEmpty())
continue; // for loop over players continue; // for loop over players

View File

@@ -5,18 +5,11 @@ import java.util.List;
import forge.Card; import forge.Card;
import forge.Singletons;
import forge.card.cost.Cost; import forge.card.cost.Cost;
import forge.card.spellability.AbilityActivated; import forge.card.spellability.AbilityActivated;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.control.input.Input;
import forge.control.input.InputSelectManyCards;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.PlayerZone;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
import forge.gui.match.CMatchUI;
/** /**
* TODO: Write javadoc for this type. * TODO: Write javadoc for this type.
@@ -102,96 +95,5 @@ class CardFactoryArtifacts {
ab1.setStackDescription(sb.toString()); ab1.setStackDescription(sb.toString());
card.addSpellAbility(ab1); card.addSpellAbility(ab1);
} // *************** END ************ END ************************** } // *************** END ************ END **************************
// *************** START *********** START **************************
else if (cardName.equals("Scroll Rack")) {
class AbilityScrollRack extends AbilityActivated {
public AbilityScrollRack(final Card ca, final Cost co, final Target t) {
super(ca, co, t);
}
@Override
public AbilityActivated getCopy() {
AbilityActivated res = new AbilityScrollRack(getSourceCard(),
getPayCosts(), getTarget() == null ? null : new Target(getTarget()));
CardFactory.copySpellAbility(this, res);
return res;
}
private static final long serialVersionUID = -5588587187720068547L;
@Override
public void resolve() {
// not implemented for compy
if (card.getController().isHuman()) {
InputSelectManyCards inp = new InputSelectManyCards(0, Integer.MAX_VALUE) {
private static final long serialVersionUID = 806464726820739922L;
@Override
protected boolean isValidChoice(Card c) {
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
return zone.is(ZoneType.Hand) && c.getController() == card.getController();
}
/* (non-Javadoc)
* @see forge.control.input.InputSelectManyCards#onDone()
*/
@Override
protected Input onDone() {
for (final Card c : selected) {
Singletons.getModel().getGame().getAction().exile(c);
}
// Put that many cards from the top of your
// library into your hand.
// Ruling: This is not a draw...
final PlayerZone lib = card.getController().getZone(ZoneType.Library);
int numCards = 0;
while ((lib.size() > 0) && (numCards < selected.size())) {
Singletons.getModel().getGame().getAction().moveToHand(lib.get(0));
numCards++;
}
final StringBuilder sb = new StringBuilder();
sb.append(card.getName()).append(" - Returning cards to top of library.");
CMatchUI.SINGLETON_INSTANCE.showMessage(sb.toString());
// Then look at the exiled cards and put them on
// top of your library in any order.
while (selected.size() > 0) {
final Card c1 = GuiChoose.one("Put a card on top of your library.", selected);
Singletons.getModel().getGame().getAction().moveToLibrary(c1);
selected.remove(c1);
}
return null; }
};
inp.setMessage(card.getName() + " - Exile cards from hand. Currently, %d selected. (Press OK when done.)");
Singletons.getModel().getMatch().getInput().setInput(inp);
}
}
@Override
public boolean canPlayAI() {
return false;
}
}
final Cost abCost = new Cost(card, "1 T", true);
final AbilityActivated ability = new AbilityScrollRack(card, abCost, null);
final StringBuilder sbDesc = new StringBuilder();
sbDesc.append(abCost);
sbDesc.append("Exile any number of cards from your hand face down. Put that many cards ");
sbDesc.append("from the top of your library into your hand. Then look at the exiled cards ");
sbDesc.append("and put them on top of your library in any order.");
ability.setDescription(sbDesc.toString());
final StringBuilder sbStack = new StringBuilder();
sbStack.append(cardName).append(" - exile any number of cards from your hand.");
ability.setStackDescription(sbStack.toString());
card.addSpellAbility(ability);
} // *************** END ************ END **************************
} }
} }

View File

@@ -39,9 +39,9 @@ import javax.swing.SwingUtilities;
import org.apache.commons.lang.time.StopWatch; import org.apache.commons.lang.time.StopWatch;
import forge.FThreads;
import forge.card.CardRules; import forge.card.CardRules;
import forge.card.CardRulesReader; import forge.card.CardRulesReader;
import forge.control.FControl;
import forge.error.BugReporter; import forge.error.BugReporter;
import forge.gui.toolbox.FProgressBar; import forge.gui.toolbox.FProgressBar;
import forge.util.FileUtil; import forge.util.FileUtil;
@@ -62,7 +62,7 @@ public class CardStorageReader {
/** Default charset when loading from files. */ /** Default charset when loading from files. */
public static final String DEFAULT_CHARSET_NAME = "US-ASCII"; public static final String DEFAULT_CHARSET_NAME = "US-ASCII";
final private boolean useThreadPool = FControl.isMultiCoreSystem(); final private boolean useThreadPool = FThreads.isMultiCoreSystem();
final private int NUMBER_OF_PARTS = 25; final private int NUMBER_OF_PARTS = 25;
final private CountDownLatch cdl = new CountDownLatch(NUMBER_OF_PARTS); final private CountDownLatch cdl = new CountDownLatch(NUMBER_OF_PARTS);
@@ -209,7 +209,7 @@ public class CardStorageReader {
try { try {
if ( useThreadPool ) { if ( useThreadPool ) {
final ExecutorService executor = FControl.getComputingPool(0.5f); final ExecutorService executor = FThreads.getComputingPool(0.5f);
final List<Future<List<CardRules>>> parts = executor.invokeAll(tasks); final List<Future<List<CardRules>>> parts = executor.invokeAll(tasks);
executor.shutdown(); executor.shutdown();
cdl.await(); cdl.await();

View File

@@ -25,9 +25,6 @@ import java.awt.event.WindowEvent;
import java.awt.event.WindowListener; import java.awt.event.WindowListener;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JLayeredPane; import javax.swing.JLayeredPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
@@ -85,8 +82,6 @@ public enum FControl {
DRAFTING_PROCESS DRAFTING_PROCESS
} }
private final ExecutorService threadPool = Executors.newCachedThreadPool();
private final SoundSystem soundSystem = new SoundSystem(); private final SoundSystem soundSystem = new SoundSystem();
@@ -317,21 +312,4 @@ public enum FControl {
public SoundSystem getSoundSystem() { public SoundSystem getSoundSystem() {
return soundSystem; return soundSystem;
} }
public ExecutorService getThreadPool() {
return threadPool;
}
// This pool is designed to parallel CPU or IO intensive tasks like parse cards or download images, assuming a load factor of 0.5
public final static ExecutorService getComputingPool(float loadFactor) {
return Executors.newFixedThreadPool((int)(Runtime.getRuntime().availableProcessors() / (1-loadFactor)));
}
/**
* TODO: Write javadoc for this method.
* @return
*/
public static boolean isMultiCoreSystem() {
return Runtime.getRuntime().availableProcessors() > 1;
}
} }

View File

@@ -0,0 +1,18 @@
package forge.control.input;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil;
/**
* TODO: Write javadoc for this type.
*
*/
public class InputLockUI extends Input {
private static final long serialVersionUID = 5777143577098597374L;
public void showMessage() {
ButtonUtil.disableAll();
CMatchUI.SINGLETON_INSTANCE.showMessage("Waiting for actions...");
}
}

View File

@@ -18,10 +18,10 @@
package forge.control.input; package forge.control.input;
import forge.Card; import forge.Card;
import forge.FThreads;
import forge.Singletons; import forge.Singletons;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.FControl; import forge.control.FControl;
import forge.game.GameState;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.gui.GuiDisplayUtil; import forge.gui.GuiDisplayUtil;
@@ -80,11 +80,17 @@ public class InputPassPriority extends Input {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void selectCard(final Card card) { public final void selectCard(final Card card) {
Player player = Singletons.getControl().getPlayer(); final Player player = Singletons.getControl().getPlayer();
GameState game = Singletons.getModel().getGame(); final SpellAbility ab = player.getController().getAbilityToPlay(player.getGame().getAbilitesOfCard(card, player));
SpellAbility ab = player.getController().getAbilityToPlay(game.getAbilitesOfCard(card, player));
if ( null != ab) { if ( null != ab) {
player.playSpellAbility(card, ab); Runnable execAbility = new Runnable() {
@Override
public void run() {
player.playSpellAbility(card, ab);
}
};
FThreads.invokeInNewThread(execAbility, true);
} }
else { else {
SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE); SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE);

View File

@@ -10,6 +10,7 @@ import forge.CardCharacteristicName;
import forge.CardColor; import forge.CardColor;
import forge.CardLists; import forge.CardLists;
import forge.CardPredicates; import forge.CardPredicates;
import forge.FThreads;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.ability.ApiType; import forge.card.ability.ApiType;
@@ -358,6 +359,7 @@ public class GameActionPlay {
* a {@link forge.card.spellability.SpellAbility} object. * a {@link forge.card.spellability.SpellAbility} object.
*/ */
public final void playSpellAbility(SpellAbility sa, Player activator) { public final void playSpellAbility(SpellAbility sa, Player activator) {
FThreads.checkEDT("Player.playSpellAbility", false);
sa.setActivatingPlayer(activator); sa.setActivatingPlayer(activator);
final Card source = sa.getSourceCard(); final Card source = sa.getSourceCard();

View File

@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import com.esotericsoftware.minlog.Log;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@@ -44,6 +45,7 @@ import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellPermanent; import forge.card.spellability.SpellPermanent;
import forge.game.GameActionUtil; import forge.game.GameActionUtil;
import forge.game.GameState; import forge.game.GameState;
import forge.game.phase.PhaseType;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -762,5 +764,97 @@ public class AiController {
} }
return null; return null;
} }
public void onPriorityRecieved() {
final PhaseType phase = game.getPhaseHandler().getPhase();
if (game.getStack().size() > 0) {
playSpellAbilities(game);
} else {
switch(phase) {
case CLEANUP:
if ( game.getPhaseHandler().getPlayerTurn() == player ) {
final int size = player.getCardsIn(ZoneType.Hand).size();
if (!player.isUnlimitedHandSize()) {
int max = Math.min(player.getZone(ZoneType.Hand).size(), size - player.getMaxHandSize());
final List<Card> toDiscard = player.getAi().getCardsToDiscard(max, (String[])null, null);
for (int i = 0; i < toDiscard.size(); i++) {
player.discard(toDiscard.get(i), null);
}
game.getStack().chooseOrderOfSimultaneousStackEntryAll();
}
}
break;
case COMBAT_DECLARE_ATTACKERS:
declareAttackers();
break;
case MAIN1:
case MAIN2:
Log.debug("Computer " + phase.toString());
playLands();
// fall through is intended
default:
playSpellAbilities(game);
break;
}
}
player.getController().passPriority();
}
private void declareAttackers() {
// 12/2/10(sol) the decision making here has moved to getAttackers()
game.setCombat(new AiAttackController(player, player.getOpponent()).getAttackers());
final List<Card> att = game.getCombat().getAttackers();
if (!att.isEmpty()) {
game.getPhaseHandler().setCombat(true);
}
for (final Card element : att) {
// tapping of attackers happens after Propaganda is paid for
final StringBuilder sb = new StringBuilder();
sb.append("Computer just assigned ").append(element.getName()).append(" as an attacker.");
Log.debug(sb.toString());
}
player.getZone(ZoneType.Battlefield).updateObservers();
game.getPhaseHandler().setPlayersPriorityPermission(false);
// ai is about to attack, cancel all phase skipping
for (Player p : game.getPlayers()) {
p.getController().autoPassCancel();
}
}
private void playLands() {
final Player player = getPlayer();
List<Card> landsWannaPlay = getLandsToPlay();
while(landsWannaPlay != null && !landsWannaPlay.isEmpty() && player.canPlayLand(null)) {
Card land = chooseBestLandToPlay(landsWannaPlay);
landsWannaPlay.remove(land);
player.playLand(land);
game.getPhaseHandler().setPriority(player);
}
}
private void playSpellAbilities(final GameState game)
{
SpellAbility sa;
do {
sa = getSpellAbilityToPlay();
if ( sa == null ) break;
//System.out.println("Playing sa: " + sa);
if (!ComputerUtil.handlePlayingSpellAbility(player, sa, game)) {
break;
}
} while ( sa != null );
}
} }

View File

@@ -17,18 +17,8 @@
*/ */
package forge.game.ai; package forge.game.ai;
import java.util.List; import forge.FThreads;
import com.esotericsoftware.minlog.Log;
import forge.Card;
import forge.card.spellability.SpellAbility;
import forge.control.input.Input; import forge.control.input.Input;
import forge.game.GameState;
import forge.game.phase.PhaseType;
import forge.game.player.AIPlayer;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
/** /**
* <p> * <p>
@@ -43,8 +33,6 @@ public class AiInputCommon extends Input {
private static final long serialVersionUID = -3091338639571662216L; private static final long serialVersionUID = -3091338639571662216L;
private final AiController computer; private final AiController computer;
private final AIPlayer player;
private final GameState game;
/** /**
* <p> * <p>
@@ -56,15 +44,13 @@ public class AiInputCommon extends Input {
*/ */
public AiInputCommon(final AiController iComputer) { public AiInputCommon(final AiController iComputer) {
this.computer = iComputer; this.computer = iComputer;
player = computer.getPlayer();
this.game = computer.getGame();
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void showMessage() { public final void showMessage() {
// should not think when the game is over // should not think when the game is over
if (game.isGameOver()) { if (computer.getGame().isGameOver()) {
return; return;
} }
@@ -76,100 +62,19 @@ public class AiInputCommon extends Input {
* \"Detailed Error Trace\" to the Forge forum."); * \"Detailed Error Trace\" to the Forge forum.");
*/ */
final PhaseType phase = game.getPhaseHandler().getPhase(); FThreads.invokeInNewThread(aiActions, true);
if (game.getStack().size() > 0) {
playSpellAbilities(game);
} else {
switch(phase) {
case CLEANUP:
if ( game.getPhaseHandler().getPlayerTurn() == player ) {
final int size = player.getCardsIn(ZoneType.Hand).size();
if (!player.isUnlimitedHandSize()) {
int max = Math.min(player.getZone(ZoneType.Hand).size(), size - player.getMaxHandSize());
final List<Card> toDiscard = player.getAi().getCardsToDiscard(max, (String[])null, null);
for (int i = 0; i < toDiscard.size(); i++) {
player.discard(toDiscard.get(i), null);
}
game.getStack().chooseOrderOfSimultaneousStackEntryAll();
}
}
break;
case COMBAT_DECLARE_ATTACKERS:
declareAttackers();
break;
case MAIN1:
case MAIN2:
Log.debug("Computer " + phase.toString());
playLands();
// fall through is intended
default:
playSpellAbilities(game);
break;
}
}
player.getController().passPriority();
} // getMessage(); } // getMessage();
/** final Runnable aiActions = new Runnable() {
* TODO: Write javadoc for this method.
*/
private void declareAttackers() {
// 12/2/10(sol) the decision making here has moved to getAttackers()
game.setCombat(new AiAttackController(player, player.getOpponent()).getAttackers());
final List<Card> att = game.getCombat().getAttackers(); @Override
if (!att.isEmpty()) { public void run() {
game.getPhaseHandler().setCombat(true); computer.onPriorityRecieved();
} }
};
for (final Card element : att) {
// tapping of attackers happens after Propaganda is paid for
final StringBuilder sb = new StringBuilder();
sb.append("Computer just assigned ").append(element.getName()).append(" as an attacker.");
Log.debug(sb.toString());
}
player.getZone(ZoneType.Battlefield).updateObservers();
game.getPhaseHandler().setPlayersPriorityPermission(false);
// ai is about to attack, cancel all phase skipping
for (Player p : game.getPlayers()) {
p.getController().autoPassCancel();
}
}
/**
* TODO: Write javadoc for this method.
*/
private void playLands() {
final Player player = computer.getPlayer();
List<Card> landsWannaPlay = computer.getLandsToPlay();
while(landsWannaPlay != null && !landsWannaPlay.isEmpty() && player.canPlayLand(null)) {
Card land = computer.chooseBestLandToPlay(landsWannaPlay);
landsWannaPlay.remove(land);
player.playLand(land);
game.getPhaseHandler().setPriority(player);
}
}
protected void playSpellAbilities(final GameState game)
{
SpellAbility sa;
do {
sa = computer.getSpellAbilityToPlay();
if ( sa == null ) break;
//System.out.println("Playing sa: " + sa);
if (!ComputerUtil.handlePlayingSpellAbility(player, sa, game)) {
break;
}
} while ( sa != null );
}
/* (non-Javadoc) /* (non-Javadoc)

View File

@@ -39,6 +39,7 @@ import forge.CardPredicates.Presets;
import forge.CardUtil; import forge.CardUtil;
import forge.Constant.Preferences; import forge.Constant.Preferences;
import forge.CounterType; import forge.CounterType;
import forge.FThreads;
import forge.GameEntity; import forge.GameEntity;
import forge.Singletons; import forge.Singletons;
import forge.card.ability.AbilityFactory; import forge.card.ability.AbilityFactory;
@@ -1623,6 +1624,7 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
* a {@link forge.card.spellability.SpellAbility} object. * a {@link forge.card.spellability.SpellAbility} object.
*/ */
protected final void doDiscard(final Card c, final SpellAbility sa) { protected final void doDiscard(final Card c, final SpellAbility sa) {
FThreads.checkEDT("Player.doDiscard", false);
// TODO: This line should be moved inside CostPayment somehow // TODO: This line should be moved inside CostPayment somehow
/*if (sa != null) { /*if (sa != null) {
sa.addCostToHashList(c, "Discarded"); sa.addCostToHashList(c, "Discarded");
@@ -1845,6 +1847,7 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
* a {@link forge.Card} object. * a {@link forge.Card} object.
*/ */
public final void playLand(final Card land) { public final void playLand(final Card land) {
FThreads.checkEDT("Player.playSpellAbility", false);
if (this.canPlayLand(land)) { if (this.canPlayLand(land)) {
land.setController(this, 0); land.setController(this, 0);
game.getAction().moveTo(this.getZone(ZoneType.Battlefield), land); game.getAction().moveTo(this.getZone(ZoneType.Battlefield), land);

View File

@@ -31,7 +31,6 @@ import java.util.List;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import forge.Card; import forge.Card;
import forge.gui.match.VMatchUI; import forge.gui.match.VMatchUI;
@@ -89,26 +88,6 @@ public final class GuiUtils {
return ttf; return ttf;
} }
/** Checks if calling method uses event dispatch thread.
* Exception thrown if method is on "wrong" thread.
* A boolean is passed to indicate if the method must be EDT or not.
*
* @param methodName &emsp; String, part of the custom exception message.
* @param mustBeEDT &emsp; boolean: true = exception if not EDT, false = exception if EDT
*/
public static void checkEDT(final String methodName, final boolean mustBeEDT) {
boolean isEDT = SwingUtilities.isEventDispatchThread();
if (!isEDT && mustBeEDT) {
throw new IllegalStateException(
methodName + " must be accessed from the event dispatch thread.");
}
else if (isEDT && !mustBeEDT) {
throw new IllegalStateException(
methodName + " may not be accessed from the event dispatch thread.");
}
}
/** /**
* Clear all visually highlighted card panels on the battlefield. * Clear all visually highlighted card panels on the battlefield.
*/ */

View File

@@ -21,6 +21,7 @@ import java.util.Observable;
import java.util.Observer; import java.util.Observer;
import forge.Card; import forge.Card;
import forge.FThreads;
import forge.Singletons; import forge.Singletons;
import forge.control.input.Input; import forge.control.input.Input;
import forge.game.player.Player; import forge.game.player.Player;
@@ -58,7 +59,17 @@ public class InputProxy implements Observer {
public final synchronized void setInput(final Input in) { public final synchronized void setInput(final Input in) {
valid = true; valid = true;
this.input = in; this.input = in;
this.input.showMessage(); // this call may invalidate the input by the time it returns
if ( null == input ) {
throw new NullPointerException("input is null");
}
FThreads.invokeInEDT(new Runnable() {
@Override
public void run() {
InputProxy.this.input.showMessage(); // this call may invalidate the input by the time it returns
}
});
} }
/** /**

View File

@@ -47,7 +47,7 @@ public enum CPicture implements ICDoc {
*/ */
public void showCard(final Card c) { public void showCard(final Card c) {
canFlip = c != null && (c.isDoubleFaced() || c.isFlipCard()); canFlip = c != null && (c.isDoubleFaced() || c.isFlipCard());
this.currentCard = c; currentCard = c;
flipped = canFlip && (c.getCurState() == CardCharacteristicName.Transformed || flipped = canFlip && (c.getCurState() == CardCharacteristicName.Transformed ||
c.getCurState() == CardCharacteristicName.Flipped); c.getCurState() == CardCharacteristicName.Flipped);
VPicture.SINGLETON_INSTANCE.getLblFlipcard().setVisible(canFlip); VPicture.SINGLETON_INSTANCE.getLblFlipcard().setVisible(canFlip);
@@ -60,13 +60,15 @@ public enum CPicture implements ICDoc {
return; return;
} }
this.currentCard = null; canFlip = false;
flipped = false;
currentCard = null;
VPicture.SINGLETON_INSTANCE.getLblFlipcard().setVisible(false); VPicture.SINGLETON_INSTANCE.getLblFlipcard().setVisible(false);
VPicture.SINGLETON_INSTANCE.getPnlPicture().setCard(item); VPicture.SINGLETON_INSTANCE.getPnlPicture().setCard(item);
} }
public Card getCurrentCard() { public Card getCurrentCard() {
return this.currentCard; return currentCard;
} }
@Override @Override
@@ -100,7 +102,8 @@ public enum CPicture implements ICDoc {
} else if (currentCard.isFlipCard()) { } else if (currentCard.isFlipCard()) {
newState = CardCharacteristicName.Flipped; newState = CardCharacteristicName.Flipped;
} else { } else {
// if this is hit, then there is a misalignment between this method and the showCard method above // if this is hit, then then showCard has been modified to handle additional types, but
// this function is missing an else if statement above
throw new RuntimeException("unhandled flippable card"); throw new RuntimeException("unhandled flippable card");
} }
} else { } else {

View File

@@ -5,7 +5,7 @@ import java.util.Date;
import javax.swing.JProgressBar; import javax.swing.JProgressBar;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import forge.gui.GuiUtils; import forge.FThreads;
/** /**
* A simple progress bar component using the Forge skin. * A simple progress bar component using the Forge skin.
@@ -37,7 +37,7 @@ public class FProgressBar extends JProgressBar {
* @param s0 &emsp; A description to prepend before statistics. * @param s0 &emsp; A description to prepend before statistics.
*/ */
public void setDescription(final String s0) { public void setDescription(final String s0) {
GuiUtils.checkEDT("FProgressBar$setDescription", true); FThreads.checkEDT("FProgressBar$setDescription", true);
this.desc = s0; this.desc = s0;
this.setString(s0); this.setString(s0);
} }
@@ -77,7 +77,7 @@ public class FProgressBar extends JProgressBar {
/** Resets the various values required for this class. Must be called from EDT. */ /** Resets the various values required for this class. Must be called from EDT. */
public void reset() { public void reset() {
GuiUtils.checkEDT("FProgressBar$reset", true); FThreads.checkEDT("FProgressBar$reset", true);
this.setIndeterminate(true); this.setIndeterminate(true);
this.setValue(0); this.setValue(0);
this.tempVal = 0; this.tempVal = 0;

View File

@@ -37,6 +37,7 @@ import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.LineBorder; import javax.swing.border.LineBorder;
import forge.FThreads;
import forge.gui.GuiUtils; import forge.gui.GuiUtils;
import forge.view.FView; import forge.view.FView;
@@ -419,7 +420,7 @@ public enum FSkin {
*/ */
public static void loadLight(final String skinName) { public static void loadLight(final String skinName) {
// No need for this method to be loaded while on the EDT. // No need for this method to be loaded while on the EDT.
GuiUtils.checkEDT("FSkin$constructor", false); FThreads.checkEDT("FSkin$constructor", false);
// Non-default (preferred) skin name and dir. // Non-default (preferred) skin name and dir.
FSkin.preferredName = skinName.toLowerCase().replace(' ', '_'); FSkin.preferredName = skinName.toLowerCase().replace(' ', '_');
@@ -479,7 +480,7 @@ public enum FSkin {
*/ */
public static void loadFull() { public static void loadFull() {
// No need for this method to be loaded while on the EDT. // No need for this method to be loaded while on the EDT.
GuiUtils.checkEDT("FSkin$load", false); FThreads.checkEDT("FSkin$load", false);
// Preferred skin name must be called via loadLight() method, // Preferred skin name must be called via loadLight() method,
// which does some cleanup and init work. // which does some cleanup and init work.

View File

@@ -27,6 +27,7 @@ import java.util.List;
import forge.Constant; import forge.Constant;
import forge.Constant.Preferences; import forge.Constant.Preferences;
import forge.FThreads;
import forge.card.BoosterData; import forge.card.BoosterData;
import forge.card.CardBlock; import forge.card.CardBlock;
import forge.card.CardRulesReader; import forge.card.CardRulesReader;
@@ -43,7 +44,6 @@ import forge.game.MatchController;
import forge.game.limited.GauntletMini; import forge.game.limited.GauntletMini;
import forge.game.player.LobbyPlayer; import forge.game.player.LobbyPlayer;
import forge.gauntlet.GauntletData; import forge.gauntlet.GauntletData;
import forge.gui.GuiUtils;
import forge.item.CardDb; import forge.item.CardDb;
import forge.properties.ForgePreferences; import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref; import forge.properties.ForgePreferences.FPref;
@@ -161,7 +161,7 @@ public enum FModel {
this.loadDynamicGamedata(); this.loadDynamicGamedata();
// Loads all cards (using progress bar). // Loads all cards (using progress bar).
GuiUtils.checkEDT("CardFactory$constructor", false); FThreads.checkEDT("CardFactory$constructor", false);
final CardStorageReader reader = new CardStorageReader(NewConstants.CARD_DATA_DIR, true); final CardStorageReader reader = new CardStorageReader(NewConstants.CARD_DATA_DIR, true);
try { try {
// this fills in our map of card names to Card instances. // this fills in our map of card names to Card instances.

View File

@@ -1,68 +0,0 @@
/*
* The files in the directory "net/slightlymagic/braids" and in all subdirectories of it (the "Files") are
* Copyright 2011 Braids Cabal-Conjurer. They are available under either Forge's
* main license (the GNU Public License; see LICENSE.txt in Forge's top directory)
* or under the Apache License, as explained below.
*
* The Files are additionally licensed under the Apache License, Version 2.0 (the
* "Apache License"); you may not use the files in this directory except in
* compliance with one of its two licenses. You may obtain a copy of the Apache
* License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Apache License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License for the specific language governing permissions and
* limitations under the Apache License.
*
*/
package forge.util;
import java.lang.reflect.InvocationTargetException;
import javax.swing.SwingUtilities;
/**
* Some general-purpose functions.
*/
public final class ThreadUtil {
/**
* Invoke the given Runnable in an Event Dispatch Thread and wait for it to
* finish; but <B>try to use SwingUtilities.invokeLater instead whenever
* feasible.</B>
*
* Exceptions generated by SwingUtilities.invokeAndWait (if used), are
* rethrown as RuntimeExceptions.
*
* @param proc
* the Runnable to run
* @see javax.swing.SwingUtilities#invokeLater(Runnable)
*/
public static void invokeInEventDispatchThreadAndWait(final Runnable proc) {
// by
// Braids
// on
// 8/18/11
// 11:19
// PM
if (SwingUtilities.isEventDispatchThread()) {
// Just run in the current thread.
proc.run();
} else {
try {
SwingUtilities.invokeAndWait(proc);
} catch (final InterruptedException exn) {
throw new RuntimeException(exn);
// 11:19 PM
} catch (final InvocationTargetException exn) {
throw new RuntimeException(exn);
// 11:19 PM
}
}
}
}