diff --git a/.gitattributes b/.gitattributes index 86d563d23e1..8ab81a83e3e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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/Constant.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/GameLog.java -text 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/InputCleanup.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/InputPassPriority.java svneol=native#text/plain 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/ReflectionUtil.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/package-info.java -text src/main/java/forge/util/storage/IStorage.java -text diff --git a/src/main/java/forge/FThreads.java b/src/main/java/forge/FThreads.java new file mode 100644 index 00000000000..16e77d3143a --- /dev/null +++ b/src/main/java/forge/FThreads.java @@ -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 try to use SwingUtilities.invokeLater instead whenever + * feasible. + * + * 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); + } + } + +} diff --git a/src/main/java/forge/StaticEffects.java b/src/main/java/forge/StaticEffects.java index 407e92a32c4..c7dc9ab4b45 100644 --- a/src/main/java/forge/StaticEffects.java +++ b/src/main/java/forge/StaticEffects.java @@ -153,13 +153,21 @@ public class StaticEffects { } if (params.containsKey("AddColor")) { - addColors = CardUtil.getShortColorsString(new ArrayList(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(Arrays.asList(colors.split(" & ")))); + } } if (params.containsKey("SetColor")) { - addColors = CardUtil.getShortColorsString(new ArrayList(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(Arrays.asList(colors.split(" & ")))); + } } // modify players diff --git a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java index 6ee5b378bdc..81f23bd9e65 100644 --- a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java +++ b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java @@ -927,7 +927,7 @@ public class ChangeZoneAi extends SpellAbilityAi { } 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 Target tgt = sa.getTarget(); diff --git a/src/main/java/forge/card/ability/effects/DiscardEffect.java b/src/main/java/forge/card/ability/effects/DiscardEffect.java index d15ab566e7f..4e2d5d8254b 100644 --- a/src/main/java/forge/card/ability/effects/DiscardEffect.java +++ b/src/main/java/forge/card/ability/effects/DiscardEffect.java @@ -165,7 +165,7 @@ public class DiscardEffect extends RevealEffectBase { } else if (mode.equals("RevealYouChoose") || mode.equals("RevealOppChoose") || mode.equals("TgtChoose")) { // Is Reveal you choose right? I think the wrong player is // being used? - List dPHand = p.getCardsIn(ZoneType.Hand); + List dPHand = new ArrayList(p.getCardsIn(ZoneType.Hand)); if (dPHand.isEmpty()) continue; // for loop over players diff --git a/src/main/java/forge/card/cardfactory/CardFactoryArtifacts.java b/src/main/java/forge/card/cardfactory/CardFactoryArtifacts.java index 4c2bcda640d..6c653ac621f 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryArtifacts.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryArtifacts.java @@ -5,18 +5,11 @@ import java.util.List; import forge.Card; -import forge.Singletons; import forge.card.cost.Cost; import forge.card.spellability.AbilityActivated; import forge.card.spellability.Target; -import forge.control.input.Input; -import forge.control.input.InputSelectManyCards; import forge.game.player.Player; -import forge.game.zone.PlayerZone; -import forge.game.zone.Zone; import forge.game.zone.ZoneType; -import forge.gui.GuiChoose; -import forge.gui.match.CMatchUI; /** * TODO: Write javadoc for this type. @@ -102,96 +95,5 @@ class CardFactoryArtifacts { ab1.setStackDescription(sb.toString()); card.addSpellAbility(ab1); } // *************** 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 ************************** } } diff --git a/src/main/java/forge/card/cardfactory/CardStorageReader.java b/src/main/java/forge/card/cardfactory/CardStorageReader.java index e75d1b2fc24..d284f15e64b 100644 --- a/src/main/java/forge/card/cardfactory/CardStorageReader.java +++ b/src/main/java/forge/card/cardfactory/CardStorageReader.java @@ -39,9 +39,9 @@ import javax.swing.SwingUtilities; import org.apache.commons.lang.time.StopWatch; +import forge.FThreads; import forge.card.CardRules; import forge.card.CardRulesReader; -import forge.control.FControl; import forge.error.BugReporter; import forge.gui.toolbox.FProgressBar; import forge.util.FileUtil; @@ -62,7 +62,7 @@ public class CardStorageReader { /** Default charset when loading from files. */ 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 CountDownLatch cdl = new CountDownLatch(NUMBER_OF_PARTS); @@ -209,7 +209,7 @@ public class CardStorageReader { try { if ( useThreadPool ) { - final ExecutorService executor = FControl.getComputingPool(0.5f); + final ExecutorService executor = FThreads.getComputingPool(0.5f); final List>> parts = executor.invokeAll(tasks); executor.shutdown(); cdl.await(); diff --git a/src/main/java/forge/control/FControl.java b/src/main/java/forge/control/FControl.java index c12b26f0b37..270a82a9648 100644 --- a/src/main/java/forge/control/FControl.java +++ b/src/main/java/forge/control/FControl.java @@ -25,9 +25,6 @@ import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - import javax.swing.ImageIcon; import javax.swing.JLayeredPane; import javax.swing.SwingUtilities; @@ -85,8 +82,6 @@ public enum FControl { DRAFTING_PROCESS } - private final ExecutorService threadPool = Executors.newCachedThreadPool(); - private final SoundSystem soundSystem = new SoundSystem(); @@ -317,21 +312,4 @@ public enum FControl { public SoundSystem getSoundSystem() { 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; - } } diff --git a/src/main/java/forge/control/input/InputLockUI.java b/src/main/java/forge/control/input/InputLockUI.java new file mode 100644 index 00000000000..cd76856a653 --- /dev/null +++ b/src/main/java/forge/control/input/InputLockUI.java @@ -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..."); + } + +} diff --git a/src/main/java/forge/control/input/InputPassPriority.java b/src/main/java/forge/control/input/InputPassPriority.java index 1647022f8cd..6c1e26116b6 100644 --- a/src/main/java/forge/control/input/InputPassPriority.java +++ b/src/main/java/forge/control/input/InputPassPriority.java @@ -18,10 +18,10 @@ package forge.control.input; import forge.Card; +import forge.FThreads; import forge.Singletons; import forge.card.spellability.SpellAbility; import forge.control.FControl; -import forge.game.GameState; import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.gui.GuiDisplayUtil; @@ -80,11 +80,17 @@ public class InputPassPriority extends Input { /** {@inheritDoc} */ @Override public final void selectCard(final Card card) { - Player player = Singletons.getControl().getPlayer(); - GameState game = Singletons.getModel().getGame(); - SpellAbility ab = player.getController().getAbilityToPlay(game.getAbilitesOfCard(card, player)); + final Player player = Singletons.getControl().getPlayer(); + final SpellAbility ab = player.getController().getAbilityToPlay(player.getGame().getAbilitesOfCard(card, player)); if ( null != ab) { - player.playSpellAbility(card, ab); + Runnable execAbility = new Runnable() { + @Override + public void run() { + player.playSpellAbility(card, ab); + } + }; + + FThreads.invokeInNewThread(execAbility, true); } else { SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE); diff --git a/src/main/java/forge/game/GameActionPlay.java b/src/main/java/forge/game/GameActionPlay.java index 4de5f530440..8ccfcd9275c 100644 --- a/src/main/java/forge/game/GameActionPlay.java +++ b/src/main/java/forge/game/GameActionPlay.java @@ -10,6 +10,7 @@ import forge.CardCharacteristicName; import forge.CardColor; import forge.CardLists; import forge.CardPredicates; +import forge.FThreads; import forge.card.MagicColor; import forge.card.ability.AbilityUtils; import forge.card.ability.ApiType; @@ -358,6 +359,7 @@ public class GameActionPlay { * a {@link forge.card.spellability.SpellAbility} object. */ public final void playSpellAbility(SpellAbility sa, Player activator) { + FThreads.checkEDT("Player.playSpellAbility", false); sa.setActivatingPlayer(activator); final Card source = sa.getSourceCard(); diff --git a/src/main/java/forge/game/ai/AiController.java b/src/main/java/forge/game/ai/AiController.java index fac40936c61..4b36151e2dd 100644 --- a/src/main/java/forge/game/ai/AiController.java +++ b/src/main/java/forge/game/ai/AiController.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import com.esotericsoftware.minlog.Log; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; @@ -44,6 +45,7 @@ import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellPermanent; import forge.game.GameActionUtil; import forge.game.GameState; +import forge.game.phase.PhaseType; import forge.game.player.AIPlayer; import forge.game.player.Player; import forge.game.zone.ZoneType; @@ -762,5 +764,97 @@ public class AiController { } 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 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 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 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 ); + } + } diff --git a/src/main/java/forge/game/ai/AiInputCommon.java b/src/main/java/forge/game/ai/AiInputCommon.java index a2f23c97f38..338f7a1ced2 100644 --- a/src/main/java/forge/game/ai/AiInputCommon.java +++ b/src/main/java/forge/game/ai/AiInputCommon.java @@ -17,18 +17,8 @@ */ package forge.game.ai; -import java.util.List; - -import com.esotericsoftware.minlog.Log; - -import forge.Card; -import forge.card.spellability.SpellAbility; +import forge.FThreads; 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; /** *

@@ -43,8 +33,6 @@ public class AiInputCommon extends Input { private static final long serialVersionUID = -3091338639571662216L; private final AiController computer; - private final AIPlayer player; - private final GameState game; /** *

@@ -56,15 +44,13 @@ public class AiInputCommon extends Input { */ public AiInputCommon(final AiController iComputer) { this.computer = iComputer; - player = computer.getPlayer(); - this.game = computer.getGame(); } /** {@inheritDoc} */ @Override public final void showMessage() { // should not think when the game is over - if (game.isGameOver()) { + if (computer.getGame().isGameOver()) { return; } @@ -76,100 +62,19 @@ public class AiInputCommon extends Input { * \"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 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(); - /** - * 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 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(); - } - } - - /** - * TODO: Write javadoc for this method. - */ - private void playLands() { - final Player player = computer.getPlayer(); - List landsWannaPlay = computer.getLandsToPlay(); + final Runnable aiActions = new Runnable() { - while(landsWannaPlay != null && !landsWannaPlay.isEmpty() && player.canPlayLand(null)) { - Card land = computer.chooseBestLandToPlay(landsWannaPlay); - landsWannaPlay.remove(land); - player.playLand(land); - game.getPhaseHandler().setPriority(player); + @Override + public void run() { + computer.onPriorityRecieved(); } - } + }; + - 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) diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index 049922c5b02..9c7b0edd5ae 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -39,6 +39,7 @@ import forge.CardPredicates.Presets; import forge.CardUtil; import forge.Constant.Preferences; import forge.CounterType; +import forge.FThreads; import forge.GameEntity; import forge.Singletons; import forge.card.ability.AbilityFactory; @@ -1623,6 +1624,7 @@ public abstract class Player extends GameEntity implements Comparable { * a {@link forge.card.spellability.SpellAbility} object. */ protected final void doDiscard(final Card c, final SpellAbility sa) { + FThreads.checkEDT("Player.doDiscard", false); // TODO: This line should be moved inside CostPayment somehow /*if (sa != null) { sa.addCostToHashList(c, "Discarded"); @@ -1845,6 +1847,7 @@ public abstract class Player extends GameEntity implements Comparable { * a {@link forge.Card} object. */ public final void playLand(final Card land) { + FThreads.checkEDT("Player.playSpellAbility", false); if (this.canPlayLand(land)) { land.setController(this, 0); game.getAction().moveTo(this.getZone(ZoneType.Battlefield), land); diff --git a/src/main/java/forge/gui/GuiUtils.java b/src/main/java/forge/gui/GuiUtils.java index c93d287bff5..e8662c774cd 100644 --- a/src/main/java/forge/gui/GuiUtils.java +++ b/src/main/java/forge/gui/GuiUtils.java @@ -31,7 +31,6 @@ import java.util.List; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.KeyStroke; -import javax.swing.SwingUtilities; import forge.Card; import forge.gui.match.VMatchUI; @@ -89,26 +88,6 @@ public final class GuiUtils { 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   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) { - 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. */ diff --git a/src/main/java/forge/gui/InputProxy.java b/src/main/java/forge/gui/InputProxy.java index cef0ac2573b..6ec76d739f2 100644 --- a/src/main/java/forge/gui/InputProxy.java +++ b/src/main/java/forge/gui/InputProxy.java @@ -21,6 +21,7 @@ import java.util.Observable; import java.util.Observer; import forge.Card; +import forge.FThreads; import forge.Singletons; import forge.control.input.Input; import forge.game.player.Player; @@ -58,7 +59,17 @@ public class InputProxy implements Observer { public final synchronized void setInput(final Input in) { valid = true; 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 + } + }); } /** diff --git a/src/main/java/forge/gui/match/controllers/CPicture.java b/src/main/java/forge/gui/match/controllers/CPicture.java index 368bcad6e90..8dae6eb2f74 100644 --- a/src/main/java/forge/gui/match/controllers/CPicture.java +++ b/src/main/java/forge/gui/match/controllers/CPicture.java @@ -47,7 +47,7 @@ public enum CPicture implements ICDoc { */ public void showCard(final Card c) { canFlip = c != null && (c.isDoubleFaced() || c.isFlipCard()); - this.currentCard = c; + currentCard = c; flipped = canFlip && (c.getCurState() == CardCharacteristicName.Transformed || c.getCurState() == CardCharacteristicName.Flipped); VPicture.SINGLETON_INSTANCE.getLblFlipcard().setVisible(canFlip); @@ -59,14 +59,16 @@ public enum CPicture implements ICDoc { showCard(((IPaperCard)item).getMatchingForgeCard()); return; } - - this.currentCard = null; + + canFlip = false; + flipped = false; + currentCard = null; VPicture.SINGLETON_INSTANCE.getLblFlipcard().setVisible(false); VPicture.SINGLETON_INSTANCE.getPnlPicture().setCard(item); } public Card getCurrentCard() { - return this.currentCard; + return currentCard; } @Override @@ -100,7 +102,8 @@ public enum CPicture implements ICDoc { } else if (currentCard.isFlipCard()) { newState = CardCharacteristicName.Flipped; } 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"); } } else { diff --git a/src/main/java/forge/gui/toolbox/FProgressBar.java b/src/main/java/forge/gui/toolbox/FProgressBar.java index bff0f133454..22015d5d7e0 100644 --- a/src/main/java/forge/gui/toolbox/FProgressBar.java +++ b/src/main/java/forge/gui/toolbox/FProgressBar.java @@ -5,7 +5,7 @@ import java.util.Date; import javax.swing.JProgressBar; import javax.swing.SwingUtilities; -import forge.gui.GuiUtils; +import forge.FThreads; /** * A simple progress bar component using the Forge skin. @@ -37,7 +37,7 @@ public class FProgressBar extends JProgressBar { * @param s0   A description to prepend before statistics. */ public void setDescription(final String s0) { - GuiUtils.checkEDT("FProgressBar$setDescription", true); + FThreads.checkEDT("FProgressBar$setDescription", true); this.desc = 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. */ public void reset() { - GuiUtils.checkEDT("FProgressBar$reset", true); + FThreads.checkEDT("FProgressBar$reset", true); this.setIndeterminate(true); this.setValue(0); this.tempVal = 0; diff --git a/src/main/java/forge/gui/toolbox/FSkin.java b/src/main/java/forge/gui/toolbox/FSkin.java index 12d707febad..88f723e3f15 100644 --- a/src/main/java/forge/gui/toolbox/FSkin.java +++ b/src/main/java/forge/gui/toolbox/FSkin.java @@ -37,6 +37,7 @@ import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.LineBorder; +import forge.FThreads; import forge.gui.GuiUtils; import forge.view.FView; @@ -419,7 +420,7 @@ public enum FSkin { */ public static void loadLight(final String skinName) { // 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. FSkin.preferredName = skinName.toLowerCase().replace(' ', '_'); @@ -479,7 +480,7 @@ public enum FSkin { */ public static void loadFull() { // 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, // which does some cleanup and init work. diff --git a/src/main/java/forge/model/FModel.java b/src/main/java/forge/model/FModel.java index 56a9f31bd40..5a40205d8e5 100644 --- a/src/main/java/forge/model/FModel.java +++ b/src/main/java/forge/model/FModel.java @@ -27,6 +27,7 @@ import java.util.List; import forge.Constant; import forge.Constant.Preferences; +import forge.FThreads; import forge.card.BoosterData; import forge.card.CardBlock; import forge.card.CardRulesReader; @@ -43,7 +44,6 @@ import forge.game.MatchController; import forge.game.limited.GauntletMini; import forge.game.player.LobbyPlayer; import forge.gauntlet.GauntletData; -import forge.gui.GuiUtils; import forge.item.CardDb; import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; @@ -161,7 +161,7 @@ public enum FModel { this.loadDynamicGamedata(); // 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); try { // this fills in our map of card names to Card instances. diff --git a/src/main/java/forge/util/ThreadUtil.java b/src/main/java/forge/util/ThreadUtil.java deleted file mode 100644 index 4abc7967ae9..00000000000 --- a/src/main/java/forge/util/ThreadUtil.java +++ /dev/null @@ -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 try to use SwingUtilities.invokeLater instead whenever - * feasible. - * - * 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 - } - } - } - - -}