- Huge update to network play

- Updated protocol: now used everywhere for type checking
  - No more locks between server and client: client no longer expects any replies
  - More code reuse
  - Fixed issue with location of multiple abilities selection popup menu
  - Improved stability; actual games should now be possible
- Other updates
  - Mobile version player panel now uses same code as desktop
  - Remove automatic "view all cards" cheat in hotseat (no longer necessary)
  - Code cleanup
This commit is contained in:
elcnesh
2015-04-26 10:54:55 +00:00
parent 5c89e1a9ce
commit addcece582
48 changed files with 694 additions and 856 deletions

3
.gitattributes vendored
View File

@@ -17715,6 +17715,8 @@ forge-gui/src/main/java/forge/model/MultipleForgeJarsFoundError.java -text
forge-gui/src/main/java/forge/model/UnOpenedMeta.java -text forge-gui/src/main/java/forge/model/UnOpenedMeta.java -text
forge-gui/src/main/java/forge/model/package-info.java svneol=native#text/plain forge-gui/src/main/java/forge/model/package-info.java svneol=native#text/plain
forge-gui/src/main/java/forge/net/GameProtocol.java -text forge-gui/src/main/java/forge/net/GameProtocol.java -text
forge-gui/src/main/java/forge/net/GameProtocolHandler.java -text
forge-gui/src/main/java/forge/net/GameProtocolSender.java -text
forge-gui/src/main/java/forge/net/IRemote.java -text forge-gui/src/main/java/forge/net/IRemote.java -text
forge-gui/src/main/java/forge/net/ReplyPool.java -text forge-gui/src/main/java/forge/net/ReplyPool.java -text
forge-gui/src/main/java/forge/net/client/ClientGameLobby.java -text forge-gui/src/main/java/forge/net/client/ClientGameLobby.java -text
@@ -17735,6 +17737,7 @@ forge-gui/src/main/java/forge/net/event/UpdateLobbyPlayerEvent.java -text
forge-gui/src/main/java/forge/net/event/package-info.java -text forge-gui/src/main/java/forge/net/event/package-info.java -text
forge-gui/src/main/java/forge/net/package-info.java -text forge-gui/src/main/java/forge/net/package-info.java -text
forge-gui/src/main/java/forge/net/server/FServerManager.java -text forge-gui/src/main/java/forge/net/server/FServerManager.java -text
forge-gui/src/main/java/forge/net/server/GameServerHandler.java -text
forge-gui/src/main/java/forge/net/server/IToClient.java -text forge-gui/src/main/java/forge/net/server/IToClient.java -text
forge-gui/src/main/java/forge/net/server/NetGuiGame.java -text forge-gui/src/main/java/forge/net/server/NetGuiGame.java -text
forge-gui/src/main/java/forge/net/server/RemoteClient.java -text forge-gui/src/main/java/forge/net/server/RemoteClient.java -text

View File

@@ -79,7 +79,7 @@ public class PlayerControllerAi extends PlayerController {
brains.setUseSimulation(value); brains.setUseSimulation(value);
} }
public SpellAbility getAbilityToPlay(List<SpellAbility> abilities, ITriggerEvent triggerEvent) { public SpellAbility getAbilityToPlay(Card hostCard, List<SpellAbility> abilities, ITriggerEvent triggerEvent) {
if (abilities.size() == 0) { if (abilities.size() == 0) {
return null; return null;
} }

View File

@@ -2,4 +2,6 @@ package forge.util;
public interface ITriggerEvent { public interface ITriggerEvent {
int getButton(); int getButton();
int getX();
int getY();
} }

View File

@@ -1,8 +1,11 @@
package forge.util; package forge.util;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import org.apache.commons.lang3.reflect.TypeUtils;
/** /**
* Static utilities related to reflection. * Static utilities related to reflection.
* *
@@ -62,4 +65,10 @@ public final class ReflectionUtil {
return null; return null;
} }
public static boolean isInstance(final Object obj, final Class<?> type) {
if (Array.class.equals(type)) {
return obj.getClass().isArray();
}
return TypeUtils.isInstance(obj, type);
}
} }

View File

@@ -26,10 +26,6 @@ import forge.util.FCollectionView;
public class GameView extends TrackableObject { public class GameView extends TrackableObject {
private static final long serialVersionUID = 8522884512960961528L; private static final long serialVersionUID = 8522884512960961528L;
/*private final TrackableIndex<CardView> cards = new TrackableIndex<CardView>();
private final TrackableIndex<PlayerView> players = new TrackableIndex<PlayerView>();
private final TrackableIndex<SpellAbilityView> spellAbilities = new TrackableIndex<SpellAbilityView>();
private final TrackableIndex<StackItemView> stackItems = new TrackableIndex<StackItemView>();*/
private final TrackableCollection<PlayerView> players; private final TrackableCollection<PlayerView> players;
private CombatView combatView; private CombatView combatView;
private final transient Game game; //TODO: Remove this when possible before network support added private final transient Game game; //TODO: Remove this when possible before network support added

View File

@@ -720,7 +720,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (sas.isEmpty()) { if (sas.isEmpty()) {
continue; continue;
} }
SpellAbility tgtSA = decider.getController().getAbilityToPlay(sas); SpellAbility tgtSA = decider.getController().getAbilityToPlay(tgtCard, sas);
if (!decider.getController().confirmAction(tgtSA, null, "Do you want to play " + tgtCard + "?")) { if (!decider.getController().confirmAction(tgtSA, null, "Do you want to play " + tgtCard + "?")) {
continue; continue;
} }

View File

@@ -1,13 +1,18 @@
package forge.game.ability.effects; package forge.game.ability.effects;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
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;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.StaticData; import forge.StaticData;
import forge.card.CardStateName;
import forge.card.CardRulesPredicates; import forge.card.CardRulesPredicates;
import forge.card.CardStateName;
import forge.game.Game; import forge.game.Game;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect; import forge.game.ability.SpellAbilityEffect;
@@ -23,11 +28,6 @@ import forge.item.PaperCard;
import forge.util.Aggregates; import forge.util.Aggregates;
import forge.util.Lang; import forge.util.Lang;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
public class PlayEffect extends SpellAbilityEffect { public class PlayEffect extends SpellAbilityEffect {
@Override @Override
protected String getStackDescription(SpellAbility sa) { protected String getStackDescription(SpellAbility sa) {
@@ -160,13 +160,13 @@ public class PlayEffect extends SpellAbilityEffect {
} }
Card original = tgtCard; Card original = tgtCard;
if (sa.hasParam("CopyCard")) { if (sa.hasParam("CopyCard")) {
Zone zone = tgtCard.getZone(); final Zone zone = tgtCard.getZone();
tgtCard = Card.fromPaperCard(tgtCard.getPaperCard(), sa.getActivatingPlayer()); tgtCard = Card.fromPaperCard(tgtCard.getPaperCard(), sa.getActivatingPlayer());
tgtCard.setToken(true); tgtCard.setToken(true);
tgtCard.setZone(zone); tgtCard.setZone(zone);
if (zone != null) { if (zone != null) {
zone.add(tgtCard); zone.add(tgtCard);
} }
if (useEncoded) { if (useEncoded) {
@@ -197,7 +197,7 @@ public class PlayEffect extends SpellAbilityEffect {
} }
// only one mode can be used // only one mode can be used
SpellAbility tgtSA = sa.getActivatingPlayer().getController().getAbilityToPlay(sas); SpellAbility tgtSA = sa.getActivatingPlayer().getController().getAbilityToPlay(tgtCard, sas);
boolean noManaCost = sa.hasParam("WithoutManaCost"); boolean noManaCost = sa.hasParam("WithoutManaCost");
if (noManaCost) { if (noManaCost) {
tgtSA = tgtSA.copyWithNoManaCost(); tgtSA = tgtSA.copyWithNoManaCost();

View File

@@ -1,5 +1,6 @@
package forge.game.player; package forge.game.player;
import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import forge.game.card.Card; import forge.game.card.Card;
@@ -10,7 +11,9 @@ import forge.game.zone.ZoneType;
* Stores information to reveal cards after a delay unless those cards can be * Stores information to reveal cards after a delay unless those cards can be
* revealed in the same dialog as cards being selected * revealed in the same dialog as cards being selected
*/ */
public class DelayedReveal { public class DelayedReveal implements Serializable {
private static final long serialVersionUID = 5516713460440436615L;
private final Collection<CardView> cards; private final Collection<CardView> cards;
private final ZoneType zone; private final ZoneType zone;
private final PlayerView owner; private final PlayerView owner;

View File

@@ -90,8 +90,8 @@ public abstract class PlayerController {
public Player getPlayer() { return player; } public Player getPlayer() { return player; }
public LobbyPlayer getLobbyPlayer() { return lobbyPlayer; } public LobbyPlayer getLobbyPlayer() { return lobbyPlayer; }
public final SpellAbility getAbilityToPlay(List<SpellAbility> abilities) { return getAbilityToPlay(abilities, null); } public final SpellAbility getAbilityToPlay(final Card hostCard, final List<SpellAbility> abilities) { return getAbilityToPlay(hostCard, abilities, null); }
public abstract SpellAbility getAbilityToPlay(List<SpellAbility> abilities, ITriggerEvent triggerEvent); public abstract SpellAbility getAbilityToPlay(Card hostCard, List<SpellAbility> abilities, ITriggerEvent triggerEvent);
//public abstract void playFromSuspend(Card c); //public abstract void playFromSuspend(Card c);
public abstract void playSpellAbilityForFree(SpellAbility copySA, boolean mayChoseNewTargets); public abstract void playSpellAbilityForFree(SpellAbility copySA, boolean mayChoseNewTargets);

View File

@@ -375,6 +375,8 @@ public class PlayerView extends GameEntityView {
} }
public String getDetails() { public String getDetails() {
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
builder.append(getName());
builder.append('\n');
for (final String detailsPart : getDetailsList()) { for (final String detailsPart : getDetailsList()) {
builder.append(detailsPart); builder.append(detailsPart);
builder.append('\n'); builder.append('\n');

View File

@@ -17,8 +17,8 @@
*/ */
package forge.screens.match; package forge.screens.match;
import java.awt.Component;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -32,11 +32,10 @@ import java.util.concurrent.atomic.AtomicReference;
import javax.swing.JMenu; import javax.swing.JMenu;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import forge.FThreads; import forge.FThreads;
@@ -56,7 +55,6 @@ import forge.game.combat.CombatView;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal; import forge.game.player.DelayedReveal;
import forge.game.player.IHasIcon; import forge.game.player.IHasIcon;
import forge.game.player.Player;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView; import forge.game.spellability.SpellAbilityView;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -72,11 +70,9 @@ import forge.gui.framework.IVDoc;
import forge.gui.framework.SDisplayUtil; import forge.gui.framework.SDisplayUtil;
import forge.gui.framework.SLayoutIO; import forge.gui.framework.SLayoutIO;
import forge.gui.framework.VEmptyDoc; import forge.gui.framework.VEmptyDoc;
import forge.interfaces.IButton;
import forge.item.InventoryItem; import forge.item.InventoryItem;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.match.AbstractGuiGame; import forge.match.AbstractGuiGame;
import forge.match.MatchButtonType;
import forge.menus.IMenuProvider; import forge.menus.IMenuProvider;
import forge.model.FModel; import forge.model.FModel;
import forge.player.PlayerZoneUpdate; import forge.player.PlayerZoneUpdate;
@@ -97,7 +93,6 @@ import forge.toolbox.FButton;
import forge.toolbox.FOptionPane; import forge.toolbox.FOptionPane;
import forge.toolbox.FSkin; import forge.toolbox.FSkin;
import forge.toolbox.FSkin.SkinImage; import forge.toolbox.FSkin.SkinImage;
import forge.toolbox.MouseTriggerEvent;
import forge.toolbox.special.PhaseIndicator; import forge.toolbox.special.PhaseIndicator;
import forge.toolbox.special.PhaseLabel; import forge.toolbox.special.PhaseLabel;
import forge.trackable.TrackableCollection; import forge.trackable.TrackableCollection;
@@ -149,13 +144,13 @@ public final class CMatchUI
Singletons.getView().getLpnDocument().add(targetingOverlay.getPanel(), FView.TARGETING_LAYER); Singletons.getView().getLpnDocument().add(targetingOverlay.getPanel(), FView.TARGETING_LAYER);
targetingOverlay.getPanel().setSize(Singletons.getControl().getDisplaySize()); targetingOverlay.getPanel().setSize(Singletons.getControl().getDisplaySize());
this.myDocs = new EnumMap<EDocID, IVDoc<? extends ICDoc>>(EDocID.class); this.myDocs = new EnumMap<EDocID, IVDoc<? extends ICDoc>>(EDocID.class);
this.myDocs.put(EDocID.CARD_PICTURE, getCDetailPicture().getCPicture().getView()); this.myDocs.put(EDocID.CARD_PICTURE, cDetailPicture.getCPicture().getView());
this.myDocs.put(EDocID.CARD_DETAIL, getCDetailPicture().getCDetail().getView()); this.myDocs.put(EDocID.CARD_DETAIL, cDetailPicture.getCDetail().getView());
this.myDocs.put(EDocID.CARD_ANTES, getCAntes().getView()); this.myDocs.put(EDocID.CARD_ANTES, cAntes.getView());
this.myDocs.put(EDocID.REPORT_MESSAGE, getCPrompt().getView()); this.myDocs.put(EDocID.REPORT_MESSAGE, getCPrompt().getView());
this.myDocs.put(EDocID.REPORT_STACK, getCStack().getView()); this.myDocs.put(EDocID.REPORT_STACK, getCStack().getView());
this.myDocs.put(EDocID.REPORT_COMBAT, getCCombat().getView()); this.myDocs.put(EDocID.REPORT_COMBAT, cCombat.getView());
this.myDocs.put(EDocID.REPORT_LOG, getCLog().getView()); this.myDocs.put(EDocID.REPORT_LOG, cLog.getView());
this.myDocs.put(EDocID.DEV_MODE, getCDev().getView()); this.myDocs.put(EDocID.DEV_MODE, getCDev().getView());
this.myDocs.put(EDocID.BUTTON_DOCK, getCDock().getView());; this.myDocs.put(EDocID.BUTTON_DOCK, getCDock().getView());;
} }
@@ -212,25 +207,13 @@ public final class CMatchUI
getCDev().update(); getCDev().update();
} }
public CAntes getCAntes() {
return cAntes;
}
public CCombat getCCombat() {
return cCombat;
}
public CDetailPicture getCDetailPicture() {
return cDetailPicture;
}
public CDev getCDev() { public CDev getCDev() {
return cDev; return cDev;
} }
public CDock getCDock() { public CDock getCDock() {
return cDock; return cDock;
} }
public CLog getCLog() { CPrompt getCPrompt() {
return cLog;
}
public CPrompt getCPrompt() {
return cPrompt; return cPrompt;
} }
public CStack getCStack() { public CStack getCStack() {
@@ -338,24 +321,6 @@ public final class CMatchUI
return idx < 0 || idx >= allHands.size() ? null : allHands.get(idx); return idx < 0 || idx >= allHands.size() ? null : allHands.get(idx);
} }
/**
* Checks if game control should stop at a phase, for either a forced
* programmatic stop, or a user-induced phase toggle.
*
* @param turn
* the {@link Player} at whose phase might be stopped.
* @param phase
* the {@link PhaseType} at which might be stopped.
* @return boolean whether the current GUI calls for a stop at the specified
* phase of the specified player.
*/
@Override
public final boolean stopAtPhase(final PlayerView turn, final PhaseType phase) {
final VField vf = getFieldViewFor(turn);
final PhaseLabel label = vf.getPhaseIndicator().getLabelFor(phase);
return label == null || label.getEnabled();
}
@Override @Override
public void setCard(final CardView c) { public void setCard(final CardView c) {
this.setCard(c, false); this.setCard(c, false);
@@ -395,8 +360,8 @@ public final class CMatchUI
SDisplayUtil.showTab(selectedDocBeforeCombat); SDisplayUtil.showTab(selectedDocBeforeCombat);
selectedDocBeforeCombat = null; selectedDocBeforeCombat = null;
} }
getCCombat().setModel(combat); cCombat.setModel(combat);
getCCombat().update(); cCombat.update();
} // showCombat(CombatView) } // showCombat(CombatView)
@Override @Override
@@ -548,26 +513,60 @@ public final class CMatchUI
return panels; return panels;
} }
@Override /**
public IButton getBtnOK(final PlayerView playerView) { * Find the card panel belonging to a card, bringing up the corresponding
return view.getBtnOK(); * window or tab if necessary.
*
* @param card
* the {@link CardView} to find a panel for.
* @return a {@link CardPanel}, or {@code null} if no corresponding panel is
* found.
*/
private CardPanel findCardPanel(final CardView card) {
final int id = card.getId();
switch (card.getZone()) {
case Battlefield:
for (final VField f : view.getFieldViews()) {
final CardPanel panel = f.getTabletop().getCardPanel(id);
if (panel != null) {
SDisplayUtil.showTab(f);
return panel;
}
}
break;
case Hand:
for (final VHand h : view.getHands()) {
final CardPanel panel = h.getHandArea().getCardPanel(id);
if (panel != null) {
SDisplayUtil.showTab(h);
return panel;
}
}
break;
case Command:
case Exile:
case Graveyard:
case Library:
return FloatingCardArea.getCardPanel(this, card);
default:
break;
}
return null;
} }
@Override public void updateButtons(final PlayerView owner, final String label1, final String label2, final boolean enable1, final boolean enable2, final boolean focus1) {
public IButton getBtnCancel(final PlayerView playerView) { final FButton btn1 = view.getBtnOK(), btn2 = view.getBtnCancel();
return view.getBtnCancel(); btn1.setText(label1);
} btn2.setText(label2);
btn1.setEnabled(enable1);
btn2.setEnabled(enable2);
@Override final FButton toFocus = enable1 && focus1 ? btn1 : (enable2 ? btn2 : null);
public void focusButton(final MatchButtonType button) {
// ensure we don't steal focus from an overlay // ensure we don't steal focus from an overlay
if (!SOverlayUtils.overlayHasFocus()) { if (toFocus != null && !SOverlayUtils.overlayHasFocus()) {
FThreads.invokeInEdtLater(new Runnable() { FThreads.invokeInEdtLater(new Runnable() {
@Override public final void run() { @Override public final void run() {
final FButton btn = button == MatchButtonType.OK toFocus.requestFocusInWindow();
? getCPrompt().getView().getBtnOK()
: getCPrompt().getView().getBtnCancel();
btn.requestFocusInWindow();
} }
}); });
} }
@@ -671,7 +670,7 @@ public final class CMatchUI
} }
@Override @Override
public SpellAbilityView getAbilityToPlay(final List<SpellAbilityView> abilities, final ITriggerEvent triggerEvent) { public SpellAbilityView getAbilityToPlay(final CardView hostCard, final List<SpellAbilityView> abilities, final ITriggerEvent triggerEvent) {
if (triggerEvent == null) { if (triggerEvent == null) {
if (abilities.isEmpty()) { if (abilities.isEmpty()) {
return null; return null;
@@ -696,19 +695,20 @@ public final class CMatchUI
final JPopupMenu menu = new JPopupMenu("Abilities"); final JPopupMenu menu = new JPopupMenu("Abilities");
boolean enabled; boolean enabled;
boolean hasEnabled = false; int firstEnabled = -1;
int shortcut = KeyEvent.VK_1; //use number keys as shortcuts for abilities 1-9 int shortcut = KeyEvent.VK_1; //use number keys as shortcuts for abilities 1-9
int index = 0;
for (final SpellAbilityView ab : abilities) { for (final SpellAbilityView ab : abilities) {
enabled = ab.canPlay(); enabled = ab.canPlay();
if (enabled) { if (enabled && firstEnabled < 0) {
hasEnabled = true; firstEnabled = index;
} }
GuiUtils.addMenuItem(menu, FSkin.encodeSymbols(ab.toString(), true), GuiUtils.addMenuItem(menu, FSkin.encodeSymbols(ab.toString(), true),
shortcut > 0 ? KeyStroke.getKeyStroke(shortcut, 0) : null, shortcut > 0 ? KeyStroke.getKeyStroke(shortcut, 0) : null,
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
cPrompt.selectAbility(ab); getGameController().selectAbility(ab);
} }
}, enabled); }, enabled);
if (shortcut > 0) { if (shortcut > 0) {
@@ -717,15 +717,38 @@ public final class CMatchUI
shortcut = 0; //stop adding shortcuts after 9 shortcut = 0; //stop adding shortcuts after 9
} }
} }
index++;
} }
if (hasEnabled) { //only show menu if at least one ability can be played
SwingUtilities.invokeLater(new Runnable() { //use invoke later to ensure first ability selected by default if (firstEnabled >= 0) { //only show menu if at least one ability can be played
public void run() { final CardPanel panel = findCardPanel(hostCard);
MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, menu.getSubElements()[0]}); final Component menuParent;
final int x, y;
if (panel == null) {
// Fall back to showing in VPrompt if no panel can be found
menuParent = getCPrompt().getView().getTarMessage();
x = 0;
y = 0;
SDisplayUtil.showTab(getCPrompt().getView());
} else {
final ZoneType zone = hostCard.getZone();
if (ImmutableList.of(ZoneType.Command, ZoneType.Exile, ZoneType.Graveyard, ZoneType.Library).contains(zone)) {
FloatingCardArea.show(this, hostCard.getController(), zone);
}
menuParent = panel.getParent();
x = triggerEvent.getX();
y = triggerEvent.getY();
}
menu.show(menuParent, x, y);
final int _firstEnabled = firstEnabled;
SwingUtilities.invokeLater(new Runnable() { //use invoke later to ensure first enabled ability selected by default
@Override public final void run() {
for (int i = 0; i <= _firstEnabled; i++) {
menu.dispatchEvent(new KeyEvent(menu, KeyEvent.KEY_PRESSED, 0, 0, KeyEvent.VK_DOWN, KeyEvent.CHAR_UNDEFINED));
}
} }
}); });
MouseEvent mouseEvent = ((MouseTriggerEvent)triggerEvent).getMouseEvent();
menu.show(mouseEvent.getComponent(), mouseEvent.getX(), mouseEvent.getY());
} }
return null; //delay ability until choice made return null; //delay ability until choice made
@@ -772,7 +795,7 @@ public final class CMatchUI
@Override @Override
public void openView(final TrackableCollection<PlayerView> myPlayers) { public void openView(final TrackableCollection<PlayerView> myPlayers) {
final GameView gameView = getGameView(); final GameView gameView = getGameView();
gameView.getGameLog().addObserver(getCLog()); gameView.getGameLog().addObserver(cLog);
// Sort players // Sort players
FCollectionView<PlayerView> players = gameView.getPlayers(); FCollectionView<PlayerView> players = gameView.getPlayers();

View File

@@ -32,6 +32,6 @@ public class ZoneAction extends ForgeAction {
@Override @Override
public void actionPerformed(final ActionEvent e) { public void actionPerformed(final ActionEvent e) {
FloatingCardArea.show(matchUI, player, zone); FloatingCardArea.showOrHide(matchUI, player, zone);
} }
} }

View File

@@ -61,7 +61,7 @@ public class CDock implements ICDoc {
* End turn. * End turn.
*/ */
public void endTurn() { public void endTurn() {
matchUI.getCPrompt().passPriorityUntilEndOfTurn(); matchUI.getGameController().passPriorityUntilEndOfTurn();
} }
/** /**

View File

@@ -47,7 +47,7 @@ public class CField implements ICDoc {
private final MouseListener madAvatar = new MouseAdapter() { private final MouseListener madAvatar = new MouseAdapter() {
@Override @Override
public void mousePressed(final MouseEvent e) { public void mousePressed(final MouseEvent e) {
matchUI.getCPrompt().selectPlayer(player, new MouseTriggerEvent(e)); matchUI.getGameController().selectPlayer(player, new MouseTriggerEvent(e));
} }
}; };
@@ -71,9 +71,11 @@ public class CField implements ICDoc {
final ZoneAction commandAction = new ZoneAction(matchUI, player, ZoneType.Command, MatchConstants.HUMANCOMMAND); final ZoneAction commandAction = new ZoneAction(matchUI, player, ZoneType.Command, MatchConstants.HUMANCOMMAND);
final Function<Byte, Boolean> manaAction = new Function<Byte, Boolean>() { final Function<Byte, Boolean> manaAction = new Function<Byte, Boolean>() {
public Boolean apply(Byte colorCode) { public Boolean apply(final Byte colorCode) {
if (CField.this.player.isLobbyPlayer(Singletons.getControl().getGuiPlayer())) { if (CField.this.player.isLobbyPlayer(Singletons.getControl().getGuiPlayer())) {
return matchUI.getGameController().useMana(colorCode.byteValue()); final int oldMana = player.getMana(colorCode);
matchUI.getGameController().useMana(colorCode.byteValue());
return oldMana != player.getMana(colorCode);
} }
return false; return false;
} }

View File

@@ -23,22 +23,17 @@ import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter; import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.FocusListener; import java.awt.event.FocusListener;
import java.util.List;
import javax.swing.JButton; import javax.swing.JButton;
import forge.FThreads; import forge.FThreads;
import forge.UiCommand; import forge.UiCommand;
import forge.game.GameView; import forge.game.GameView;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView;
import forge.gui.framework.ICDoc; import forge.gui.framework.ICDoc;
import forge.gui.framework.SDisplayUtil; import forge.gui.framework.SDisplayUtil;
import forge.screens.match.CMatchUI; import forge.screens.match.CMatchUI;
import forge.screens.match.views.VPrompt; import forge.screens.match.views.VPrompt;
import forge.toolbox.FSkin; import forge.toolbox.FSkin;
import forge.util.ITriggerEvent;
/** /**
* Controls the prompt panel in the match UI. * Controls the prompt panel in the match UI.
@@ -96,34 +91,14 @@ public class CPrompt implements ICDoc {
_initButton(view.getBtnOK(), actOK); _initButton(view.getBtnOK(), actOK);
} }
public void selectButtonOk() { private void selectButtonOk() {
matchUI.getGameController().selectButtonOk(); matchUI.getGameController().selectButtonOk();
} }
public void selectButtonCancel() { private void selectButtonCancel() {
matchUI.getGameController().selectButtonCancel(); matchUI.getGameController().selectButtonCancel();
} }
public boolean passPriority() {
return matchUI.getGameController().passPriority();
}
public boolean passPriorityUntilEndOfTurn() {
return matchUI.getGameController().passPriorityUntilEndOfTurn();
}
public void selectPlayer(final PlayerView playerView, final ITriggerEvent triggerEvent) {
matchUI.getGameController().selectPlayer(playerView, triggerEvent);
}
public boolean selectCard(final CardView cardView, final List<CardView> otherCardViewsToSelect, final ITriggerEvent triggerEvent) {
return matchUI.getGameController().selectCard(cardView, otherCardViewsToSelect, triggerEvent);
}
public void selectAbility(final SpellAbilityView sa) {
matchUI.getGameController().selectAbility(sa);
}
public void setMessage(String s0) { public void setMessage(String s0) {
view.getTarMessage().setText(FSkin.encodeSymbols(s0, false)); view.getTarMessage().setText(FSkin.encodeSymbols(s0, false));
} }

View File

@@ -85,7 +85,7 @@ public final class GameMenu {
return new ActionListener() { return new ActionListener() {
@Override @Override
public void actionPerformed(final ActionEvent e) { public void actionPerformed(final ActionEvent e) {
matchUI.getGameController().tryUndoLastAction(); matchUI.getGameController().undoLastAction();
} }
}; };
} }

View File

@@ -69,7 +69,7 @@ public class VStack implements IVDoc<CStack> {
// Other fields // Other fields
private final AbilityMenu abilityMenu = new AbilityMenu(); private final AbilityMenu abilityMenu = new AbilityMenu();
private static StackInstanceTextArea hoveredItem; private StackInstanceTextArea hoveredItem;
public StackInstanceTextArea getHoveredItem() { public StackInstanceTextArea getHoveredItem() {
return hoveredItem; return hoveredItem;
@@ -267,7 +267,7 @@ public class VStack implements IVDoc<CStack> {
controller.getMatchUI().setShouldAutoYield(key, !autoYield); controller.getMatchUI().setShouldAutoYield(key, !autoYield);
if (!autoYield && controller.getMatchUI().getGameView().peekStack() == item) { if (!autoYield && controller.getMatchUI().getGameView().peekStack() == item) {
//auto-pass priority if ability is on top of stack //auto-pass priority if ability is on top of stack
controller.getMatchUI().getCPrompt().passPriority(); controller.getMatchUI().getGameController().passPriority();
} }
} }
}); });

View File

@@ -17,6 +17,24 @@
*/ */
package forge.toolbox; package forge.toolbox;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import forge.UiCommand; import forge.UiCommand;
import forge.assets.FSkinProp; import forge.assets.FSkinProp;
import forge.gui.framework.ILocalRepaint; import forge.gui.framework.ILocalRepaint;
@@ -25,11 +43,6 @@ import forge.toolbox.FSkin.Colors;
import forge.toolbox.FSkin.SkinImage; import forge.toolbox.FSkin.SkinImage;
import forge.toolbox.FSkin.SkinnedButton; import forge.toolbox.FSkin.SkinnedButton;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/** /**
* The core JButton used throughout the Forge project. Follows skin font and * The core JButton used throughout the Forge project. Follows skin font and
* theme button styling. * theme button styling.

View File

@@ -7,20 +7,28 @@ import forge.util.ITriggerEvent;
//MouseEvent wrapper used for passing trigger to input classes //MouseEvent wrapper used for passing trigger to input classes
public class MouseTriggerEvent implements ITriggerEvent, Serializable { public class MouseTriggerEvent implements ITriggerEvent, Serializable {
private static final long serialVersionUID = -4746127117012991732L; private static final long serialVersionUID = -5440485066050000298L;
private final MouseEvent event; private final int button, x, y;
public MouseTriggerEvent(MouseEvent event0) { public MouseTriggerEvent(final MouseEvent event) {
event = event0; this.button = event.getButton();
this.x = event.getX();
this.y = event.getY();
} }
@Override @Override
public int getButton() { public int getButton() {
return event.getButton(); return button;
} }
public MouseEvent getMouseEvent() { @Override
return event; public int getX() {
return x;
}
@Override
public int getY() {
return y;
} }
} }

View File

@@ -26,6 +26,7 @@ import java.awt.event.MouseEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.swing.ScrollPaneConstants; import javax.swing.ScrollPaneConstants;
import javax.swing.Timer; import javax.swing.Timer;
@@ -55,12 +56,20 @@ public class FloatingCardArea extends CardArea {
private static final String COORD_DELIM = ","; private static final String COORD_DELIM = ",";
private static final ForgePreferences prefs = FModel.getPreferences(); private static final ForgePreferences prefs = FModel.getPreferences();
private static final HashMap<Integer, FloatingCardArea> floatingAreas = new HashMap<Integer, FloatingCardArea>(); private static final Map<Integer, FloatingCardArea> floatingAreas = new HashMap<Integer, FloatingCardArea>();
private static int getKey(final PlayerView player, final ZoneType zone) { private static int getKey(final PlayerView player, final ZoneType zone) {
return 40 * player.getId() + zone.hashCode(); return 40 * player.getId() + zone.hashCode();
} }
public static void showOrHide(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) {
final FloatingCardArea cardArea = _init(matchUI, player, zone);
cardArea.showOrHideWindow();
}
public static void show(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) { public static void show(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) {
final FloatingCardArea cardArea = _init(matchUI, player, zone);
cardArea.showWindow();
}
private static FloatingCardArea _init(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) {
final int key = getKey(player, zone); final int key = getKey(player, zone);
FloatingCardArea cardArea = floatingAreas.get(key); FloatingCardArea cardArea = floatingAreas.get(key);
if (cardArea == null || cardArea.getMatchUI() != matchUI) { if (cardArea == null || cardArea.getMatchUI() != matchUI) {
@@ -69,7 +78,11 @@ public class FloatingCardArea extends CardArea {
} else { } else {
cardArea.setPlayer(player); //ensure player is updated if needed cardArea.setPlayer(player); //ensure player is updated if needed
} }
cardArea.showWindow(); return cardArea;
}
public static CardPanel getCardPanel(final CMatchUI matchUI, final CardView card) {
final FloatingCardArea window = _init(matchUI, card.getController(), card.getZone());
return window.getCardPanel(card.getId());
} }
public static void refresh(final PlayerView player, final ZoneType zone) { public static void refresh(final PlayerView player, final ZoneType zone) {
FloatingCardArea cardArea = floatingAreas.get(getKey(player, zone)); FloatingCardArea cardArea = floatingAreas.get(getKey(player, zone));
@@ -188,16 +201,22 @@ public class FloatingCardArea extends CardArea {
} }
private void showWindow() { private void showWindow() {
onShow();
window.setVisible(true);
}
private void showOrHideWindow() {
onShow();
window.setVisible(!window.isVisible());
}
private void onShow() {
if (!hasBeenShown) { if (!hasBeenShown) {
loadLocation(); loadLocation();
window.getTitleBar().addMouseListener(new FMouseAdapter() { window.getTitleBar().addMouseListener(new FMouseAdapter() {
@Override @Override public final void onLeftDoubleClick(final MouseEvent e) {
public void onLeftDoubleClick(MouseEvent e) {
window.setVisible(false); //hide window if titlebar double-clicked window.setVisible(false); //hide window if titlebar double-clicked
} }
}); });
} }
window.setVisible(!window.isVisible());
} }
private void loadLocation() { private void loadLocation() {
@@ -251,7 +270,7 @@ public class FloatingCardArea extends CardArea {
window.setSize(mainFrame.getWidth() / 5, mainFrame.getHeight() / 2); window.setSize(mainFrame.getWidth() / 5, mainFrame.getHeight() / 2);
} }
public void refresh() { private void refresh() {
if (!window.isVisible()) { return; } //don't refresh while window hidden if (!window.isVisible()) { return; } //don't refresh while window hidden
List<CardPanel> cardPanels = new ArrayList<CardPanel>(); List<CardPanel> cardPanels = new ArrayList<CardPanel>();
@@ -292,7 +311,7 @@ public class FloatingCardArea extends CardArea {
} }
} }
final Timer layoutTimer = new Timer(250, new ActionListener() { private final Timer layoutTimer = new Timer(250, new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent arg0) { public void actionPerformed(ActionEvent arg0) {
layoutTimer.stop(); layoutTimer.stop();
@@ -311,12 +330,12 @@ public class FloatingCardArea extends CardArea {
} }
@Override @Override
public final void mouseLeftClicked(final CardPanel panel, final MouseEvent evt) { public final void mouseLeftClicked(final CardPanel panel, final MouseEvent evt) {
getMatchUI().getCPrompt().selectCard(panel.getCard(), null, new MouseTriggerEvent(evt)); getMatchUI().getGameController().selectCard(panel.getCard(), null, new MouseTriggerEvent(evt));
super.mouseLeftClicked(panel, evt); super.mouseLeftClicked(panel, evt);
} }
@Override @Override
public final void mouseRightClicked(final CardPanel panel, final MouseEvent evt) { public final void mouseRightClicked(final CardPanel panel, final MouseEvent evt) {
getMatchUI().getCPrompt().selectCard(panel.getCard(), null, new MouseTriggerEvent(evt)); getMatchUI().getGameController().selectCard(panel.getCard(), null, new MouseTriggerEvent(evt));
super.mouseRightClicked(panel, evt); super.mouseRightClicked(panel, evt);
} }
} }

View File

@@ -60,14 +60,14 @@ public class HandArea extends CardArea {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void mouseLeftClicked(final CardPanel panel, final MouseEvent evt) { public final void mouseLeftClicked(final CardPanel panel, final MouseEvent evt) {
getMatchUI().getCPrompt().selectCard(panel.getCard(), null, new MouseTriggerEvent(evt)); getMatchUI().getGameController().selectCard(panel.getCard(), null, new MouseTriggerEvent(evt));
super.mouseLeftClicked(panel, evt); super.mouseLeftClicked(panel, evt);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void mouseRightClicked(final CardPanel panel, final MouseEvent evt) { public final void mouseRightClicked(final CardPanel panel, final MouseEvent evt) {
getMatchUI().getCPrompt().selectCard(panel.getCard(), null, new MouseTriggerEvent(evt)); getMatchUI().getGameController().selectCard(panel.getCard(), null, new MouseTriggerEvent(evt));
super.mouseRightClicked(panel, evt); super.mouseRightClicked(panel, evt);
} }
} }

View File

@@ -543,7 +543,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
} }
} }
} }
if (getMatchUI().getCPrompt().selectCard(panel.getCard(), otherCardViewsToSelect, triggerEvent)) { if (getMatchUI().getGameController().selectCard(panel.getCard(), otherCardViewsToSelect, triggerEvent)) {
return true; return true;
} }
//if panel can't do anything with card selection, try selecting previous panel in stack //if panel can't do anything with card selection, try selecting previous panel in stack

View File

@@ -473,8 +473,9 @@ public class PlayerControllerForTests extends PlayerController {
} }
@Override @Override
public SpellAbility getAbilityToPlay(List<SpellAbility> abilities, ITriggerEvent triggerEvent) { public SpellAbility getAbilityToPlay(Card hostCard, List<SpellAbility> abilities, ITriggerEvent triggerEvent) {
return getAbilityToPlay(abilities); // Isn't this a method invocation loop? --elcnesh
return getAbilityToPlay(hostCard, abilities);
} }
@Override @Override

View File

@@ -34,11 +34,9 @@ import forge.game.player.IHasIcon;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView; import forge.game.spellability.SpellAbilityView;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.interfaces.IButton;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.match.AbstractGuiGame; import forge.match.AbstractGuiGame;
import forge.match.HostedMatch; import forge.match.HostedMatch;
import forge.match.MatchButtonType;
import forge.model.FModel; import forge.model.FModel;
import forge.player.PlayerZoneUpdate; import forge.player.PlayerZoneUpdate;
import forge.properties.ForgePreferences; import forge.properties.ForgePreferences;
@@ -50,6 +48,7 @@ import forge.screens.match.views.VPlayerPanel;
import forge.screens.match.views.VPlayerPanel.InfoTab; import forge.screens.match.views.VPlayerPanel.InfoTab;
import forge.screens.match.views.VPrompt; import forge.screens.match.views.VPrompt;
import forge.screens.match.winlose.ViewWinLose; import forge.screens.match.winlose.ViewWinLose;
import forge.toolbox.FButton;
import forge.toolbox.FDisplayObject; import forge.toolbox.FDisplayObject;
import forge.toolbox.FOptionPane; import forge.toolbox.FOptionPane;
import forge.trackable.TrackableCollection; import forge.trackable.TrackableCollection;
@@ -142,24 +141,18 @@ public class MatchController extends AbstractGuiGame {
Forge.openScreen(view); Forge.openScreen(view);
} }
@Override
public IButton getBtnOK(PlayerView player) {
return view.getPrompt(player).getBtnOk();
}
@Override
public IButton getBtnCancel(PlayerView player) {
return view.getPrompt(player).getBtnCancel();
}
@Override @Override
public void showPromptMessage(final PlayerView player, final String message) { public void showPromptMessage(final PlayerView player, final String message) {
view.getPrompt(player).setMessage(message); view.getPrompt(player).setMessage(message);
} }
@Override public void updateButtons(final PlayerView owner, final String label1, final String label2, final boolean enable1, final boolean enable2, final boolean focus1) {
public void focusButton(final MatchButtonType button) { final VPrompt prompt = view.getPrompt(owner);
//not needed for mobile game final FButton btn1 = prompt.getBtnOk(), btn2 = prompt.getBtnCancel();
btn1.setText(label1);
btn2.setText(label2);
btn1.setEnabled(enable1);
btn2.setEnabled(enable2);
} }
@Override @Override
@@ -224,7 +217,7 @@ public class MatchController extends AbstractGuiGame {
} }
@Override @Override
public SpellAbilityView getAbilityToPlay(List<SpellAbilityView> abilities, ITriggerEvent triggerEvent) { public SpellAbilityView getAbilityToPlay(final CardView hostCard, final List<SpellAbilityView> abilities, final ITriggerEvent triggerEvent) {
if (abilities.isEmpty()) { if (abilities.isEmpty()) {
return null; return null;
} }
@@ -320,11 +313,6 @@ public class MatchController extends AbstractGuiGame {
} }
} }
@Override
public boolean stopAtPhase(PlayerView turn, PhaseType phase) {
return view.stopAtPhase(turn, phase);
}
@Override @Override
public void updateZones(Iterable<PlayerZoneUpdate> zonesToUpdate) { public void updateZones(Iterable<PlayerZoneUpdate> zonesToUpdate) {
view.updateZones(zonesToUpdate); view.updateZones(zonesToUpdate);

View File

@@ -384,7 +384,7 @@ public class MatchScreen extends FScreen {
break; break;
case Keys.Z: //undo on Ctrl+Z case Keys.Z: //undo on Ctrl+Z
if (KeyInputAdapter.isCtrlKeyDown()) { if (KeyInputAdapter.isCtrlKeyDown()) {
getGameController().tryUndoLastAction(); getGameController().undoLastAction();
return true; return true;
} }
break; break;

View File

@@ -91,7 +91,13 @@ public class VManaPool extends VDisplayArea {
public boolean flick(float x, float y) { public boolean flick(float x, float y) {
if (player.isLobbyPlayer(GamePlayerUtil.getGuiPlayer())) { if (player.isLobbyPlayer(GamePlayerUtil.getGuiPlayer())) {
//on two finger tap, keep using mana until it runs out or no longer can be put towards the cost //on two finger tap, keep using mana until it runs out or no longer can be put towards the cost
while (MatchController.instance.getGameController().useMana(colorCode)) {} int oldMana, newMana = player.getMana(colorCode);
do {
oldMana = newMana;
MatchController.instance.getGameController().useMana(colorCode);
newMana = player.getMana(colorCode);
}
while (oldMana != newMana);
} }
return true; return true;
} }

View File

@@ -5,11 +5,8 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;
import forge.Graphics; import forge.Graphics;
import forge.assets.FImage; import forge.assets.FImage;
import forge.assets.FSkinFont; import forge.assets.FSkinFont;
import forge.game.card.CardView;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.menu.FDropDown; import forge.menu.FDropDown;
import forge.model.FModel;
import forge.properties.ForgePreferences.FPref;
import forge.screens.match.MatchController; import forge.screens.match.MatchController;
import forge.toolbox.FContainer; import forge.toolbox.FContainer;
import forge.toolbox.FDisplayObject; import forge.toolbox.FDisplayObject;
@@ -65,33 +62,7 @@ public class VPlayers extends FDropDown {
g.drawImage(avatarImage, x, y, h, h); g.drawImage(avatarImage, x, y, h, h);
x += h + PADDING; x += h + PADDING;
StringBuilder builder = new StringBuilder(); g.drawText(player.getDetails(), FONT, FList.FORE_COLOR, x, y, getWidth() - PADDING - x, h, true, HAlignment.LEFT, true);
builder.append(player.getName());
builder.append("\nLife: " + String.valueOf(player.getLife()));
builder.append(" | Poison counters: " + String.valueOf(player.getPoisonCounters()));
builder.append(" | Maximum hand size: " + String.valueOf(player.getMaxHandSize()));
builder.append(" | Cards drawn this turn: " + String.valueOf(player.getNumDrawnThisTurn()));
builder.append(" | Damage Prevention: " + String.valueOf(player.getPreventNextDamage()));
if (!player.getKeywords().isEmpty()) {
builder.append(" | " + player.getKeywords().toString());
}
if (FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE)) {
Iterable<CardView> list = player.getAnte();
builder.append(" | Ante'd: ");
boolean needDelim = false;
for (CardView cv : list) {
if (needDelim) {
builder.append(", ");
}
else { needDelim = true; }
builder.append(cv);
}
}
if (MatchController.instance.getGameView().isCommander()) {
builder.append(" | " + player.getCommanderInfo());
}
g.drawText(builder.toString(), FONT, FList.FORE_COLOR, x, y, getWidth() - PADDING - x, h, true, HAlignment.LEFT, true);
} }
@Override @Override

View File

@@ -126,7 +126,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
} }
synchronized (zonesUpdate) { synchronized (zonesUpdate) {
if (!zonesUpdate.isEmpty()) { if (!zonesUpdate.isEmpty()) {
matchController.updateZones(zonesUpdate); // Copy to prevent concurrency issues
matchController.updateZones(new PlayerZoneUpdates(zonesUpdate));
zonesUpdate.clear(); zonesUpdate.clear();
} }
} }

View File

@@ -32,8 +32,7 @@ public class WatchLocalGame extends PlayerControllerHuman {
} }
@Override @Override
public boolean tryUndoLastAction() { public void undoLastAction() {
return false;
} }
@Override @Override
@@ -63,18 +62,15 @@ public class WatchLocalGame extends PlayerControllerHuman {
} }
@Override @Override
public boolean passPriority() { public void passPriority() {
return false;
} }
@Override @Override
public boolean passPriorityUntilEndOfTurn() { public void passPriorityUntilEndOfTurn() {
return false;
} }
@Override @Override
public boolean useMana(final byte mana) { public void useMana(final byte mana) {
return false;
} }
@Override @Override

View File

@@ -18,15 +18,15 @@ public interface IGameController {
void alphaStrike(); void alphaStrike();
boolean useMana(byte color); void useMana(byte color);
void selectButtonOk(); void selectButtonOk();
void selectButtonCancel(); void selectButtonCancel();
boolean passPriority(); void passPriority();
boolean passPriorityUntilEndOfTurn(); void passPriorityUntilEndOfTurn();
void selectPlayer(PlayerView playerView, ITriggerEvent triggerEvent); void selectPlayer(PlayerView playerView, ITriggerEvent triggerEvent);
@@ -35,7 +35,7 @@ public interface IGameController {
void selectAbility(SpellAbilityView sa); void selectAbility(SpellAbilityView sa);
boolean tryUndoLastAction(); void undoLastAction();
IDevModeCheats cheat(); IDevModeCheats cheat();

View File

@@ -19,7 +19,6 @@ import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView; import forge.game.spellability.SpellAbilityView;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.match.MatchButtonType;
import forge.player.PlayerZoneUpdate; import forge.player.PlayerZoneUpdate;
import forge.trackable.TrackableCollection; import forge.trackable.TrackableCollection;
import forge.util.ITriggerEvent; import forge.util.ITriggerEvent;
@@ -31,10 +30,8 @@ public interface IGuiGame {
void afterGameEnd(); void afterGameEnd();
void showCombat(); void showCombat();
void showPromptMessage(PlayerView playerView, String message); void showPromptMessage(PlayerView playerView, String message);
boolean stopAtPhase(PlayerView playerTurn, PhaseType phase); void updateButtons(PlayerView owner, boolean okEnabled, boolean cancelEnabled, boolean focusOk);
IButton getBtnOK(PlayerView playerView); void updateButtons(PlayerView owner, String label1, String label2, boolean enable1, boolean enable2, boolean focus1);
IButton getBtnCancel(PlayerView playerView);
void focusButton(MatchButtonType button);
void flashIncorrectAction(); void flashIncorrectAction();
void updatePhase(); void updatePhase();
void updateTurn(PlayerView player); void updateTurn(PlayerView player);
@@ -52,11 +49,8 @@ public interface IGuiGame {
void updateManaPool(Iterable<PlayerView> manaPoolUpdate); void updateManaPool(Iterable<PlayerView> manaPoolUpdate);
void updateLives(Iterable<PlayerView> livesUpdate); void updateLives(Iterable<PlayerView> livesUpdate);
void setPanelSelection(CardView hostCard); void setPanelSelection(CardView hostCard);
SpellAbilityView getAbilityToPlay(List<SpellAbilityView> abilities, SpellAbilityView getAbilityToPlay(CardView hostCard, List<SpellAbilityView> abilities, ITriggerEvent triggerEvent);
ITriggerEvent triggerEvent); Map<CardView, Integer> assignDamage(CardView attacker, List<CardView> blockers, int damage, GameEntityView defender, boolean overrideOrder);
Map<CardView, Integer> assignDamage(CardView attacker,
List<CardView> blockers, int damage, GameEntityView defender,
boolean overrideOrder);
void message(String message); void message(String message);
void message(String message, String title); void message(String message, String title);
@@ -163,10 +157,6 @@ public interface IGuiGame {
void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi); void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi);
boolean openZones(Collection<ZoneType> zones, Map<PlayerView, Object> players); boolean openZones(Collection<ZoneType> zones, Map<PlayerView, Object> players);
void restoreOldZones(Map<PlayerView, Object> playersToRestoreZonesFor); void restoreOldZones(Map<PlayerView, Object> playersToRestoreZonesFor);
void updateButtons(PlayerView owner, boolean okEnabled,
boolean cancelEnabled, boolean focusOk);
void updateButtons(PlayerView owner, String okLabel, String cancelLabel,
boolean okEnabled, boolean cancelEnabled, boolean focusOk);
void setHighlighted(PlayerView pv, boolean b); void setHighlighted(PlayerView pv, boolean b);
void setUsedToPay(CardView card, boolean value); void setUsedToPay(CardView card, boolean value);

View File

@@ -24,7 +24,6 @@ import forge.game.GameView;
import forge.game.card.CardView; import forge.game.card.CardView;
import forge.game.card.CardView.CardStateView; import forge.game.card.CardView.CardStateView;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.interfaces.IButton;
import forge.interfaces.IGameController; import forge.interfaces.IGameController;
import forge.interfaces.IGuiGame; import forge.interfaces.IGuiGame;
import forge.interfaces.IMayViewCards; import forge.interfaces.IMayViewCards;
@@ -195,20 +194,7 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
} }
@Override @Override
public void updateButtons(final PlayerView owner, final String okLabel, final String cancelLabel, final boolean okEnabled, final boolean cancelEnabled, final boolean focusOk) { public abstract void updateButtons(PlayerView owner, String label1, String label2, boolean enable1, boolean enable2, boolean focus1);
final IButton btnOk = getBtnOK(owner);
final IButton btnCancel = getBtnCancel(owner);
btnOk.setText(okLabel);
btnCancel.setText(cancelLabel);
btnOk.setEnabled(okEnabled);
btnCancel.setEnabled(cancelEnabled);
if (okEnabled && focusOk) {
focusButton(MatchButtonType.OK);
} else if (cancelEnabled) {
focusButton(MatchButtonType.CANCEL);
}
}
// Auto-yield and other input-related code // Auto-yield and other input-related code

View File

@@ -193,11 +193,6 @@ public class HostedMatch {
gui.setGameController(null, humanController); gui.setGameController(null, humanController);
gui.openView(null); gui.openView(null);
} else if (humanCount == players.size()) {
//if there are no AI's, allow all players to see all cards (hotseat mode).
for (final PlayerControllerHuman humanController : humanControllers) {
humanController.setMayLookAtAllCards(true);
}
} }
//prompt user for player one name if needed //prompt user for player one name if needed

View File

@@ -129,7 +129,7 @@ public class InputPassPriority extends InputSyncronizedBase {
return false; return false;
} }
final SpellAbility ability = getController().getAbilityToPlay(abilities, triggerEvent); final SpellAbility ability = getController().getAbilityToPlay(card, abilities, triggerEvent);
if (ability != null) { if (ability != null) {
chosenSa = new ArrayList<SpellAbility>(); chosenSa = new ArrayList<SpellAbility>();
chosenSa.add(ability); chosenSa.add(ability);

View File

@@ -153,14 +153,12 @@ public abstract class InputPayMana extends InputSyncronizedBase {
return abilities; return abilities;
} }
public boolean useManaFromPool(byte colorCode) { public void useManaFromPool(byte colorCode) {
// find the matching mana in pool. // find the matching mana in pool.
if (player.getManaPool().tryPayCostWithColor(colorCode, saPaidFor, manaCost)) { if (player.getManaPool().tryPayCostWithColor(colorCode, saPaidFor, manaCost)) {
onManaAbilityPaid(); onManaAbilityPaid();
showMessage(); showMessage();
return true;
} }
return false;
} }
protected boolean activateManaAbility(final Card card, ManaCostBeingPaid manaCost) { protected boolean activateManaAbility(final Card card, ManaCostBeingPaid manaCost) {

View File

@@ -8,7 +8,6 @@ import java.util.Map;
import com.google.common.base.Function; import com.google.common.base.Function;
import forge.UiCommand;
import forge.assets.FSkinProp; import forge.assets.FSkinProp;
import forge.deck.CardPool; import forge.deck.CardPool;
import forge.game.GameEntityView; import forge.game.GameEntityView;
@@ -18,11 +17,12 @@ import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal; import forge.game.player.DelayedReveal;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView; import forge.game.spellability.SpellAbilityView;
import forge.interfaces.IButton; import forge.interfaces.IGameController;
import forge.interfaces.IGuiGame; import forge.interfaces.IGuiGame;
import forge.match.MatchButtonType; import forge.match.NextGameDecision;
import forge.trackable.TrackableCollection; import forge.trackable.TrackableCollection;
import forge.util.ITriggerEvent; import forge.util.ITriggerEvent;
import forge.util.ReflectionUtil;
public final class GameProtocol { public final class GameProtocol {
@@ -36,101 +36,146 @@ public final class GameProtocol {
return ProtocolMethod.valueOf(name); return ProtocolMethod.valueOf(name);
} }
private enum Mode {
SERVER(IGuiGame.class),
CLIENT(IGameController.class);
private final Class<?> toInvoke;
private Mode(final Class<?> toInvoke) {
this.toInvoke = toInvoke;
}
};
/** /**
* The methods that can be sent through this protocol. * The methods that can be sent through this protocol.
*/ */
public enum ProtocolMethod { public enum ProtocolMethod {
setGameView(Void.TYPE, GameView.class), // Server -> Client
openView(Void.TYPE, TrackableCollection.class), setGameView (Mode.SERVER, Void.TYPE, GameView.class),
afterGameEnd(), openView (Mode.SERVER, Void.TYPE, TrackableCollection/*PlayerView*/.class),
showCombat(), afterGameEnd (Mode.SERVER),
showPromptMessage(Void.TYPE, PlayerView.class, String.class), showCombat (Mode.SERVER),
stopAtPhase(Boolean.TYPE, PlayerView.class, PhaseType.class), showPromptMessage (Mode.SERVER, Void.TYPE, PlayerView.class, String.class),
focusButton(Void.TYPE, MatchButtonType.class), updateButtons (Mode.SERVER, Void.TYPE, PlayerView.class, String.class, String.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE),
flashIncorrectAction(), flashIncorrectAction(Mode.SERVER),
updatePhase(), updatePhase (Mode.SERVER),
updateTurn(Void.TYPE, PlayerView.class), updateTurn (Mode.SERVER, Void.TYPE, PlayerView.class),
updatePlayerControl(), updatePlayerControl (Mode.SERVER),
enableOverlay(), enableOverlay (Mode.SERVER),
disableOverlay(), disableOverlay (Mode.SERVER),
finishGame(), finishGame (Mode.SERVER),
showManaPool(Void.TYPE, PlayerView.class), showManaPool (Mode.SERVER, Object.class, PlayerView.class),
hideManaPool(Void.TYPE, PlayerView.class), hideManaPool (Mode.SERVER, Void.TYPE, PlayerView.class),
updateStack(), updateStack (Mode.SERVER),
updateZones(Void.TYPE, Iterable.class), updateZones (Mode.SERVER, Void.TYPE, Iterable/*PlayerZoneUpdate*/.class),
updateCards(Void.TYPE, Iterable.class), updateCards (Mode.SERVER, Void.TYPE, Iterable/*CardView*/.class),
updateManaPool(Void.TYPE, Iterable.class), updateManaPool (Mode.SERVER, Void.TYPE, Iterable/*PlayerView*/.class),
updateLives(Void.TYPE, Iterable.class), updateLives (Mode.SERVER, Void.TYPE, Iterable/*PlayerView*/.class),
setPanelSelection(Void.TYPE, CardView.class), setPanelSelection (Mode.SERVER, Void.TYPE, CardView.class),
getAbilityToPlay(SpellAbilityView.class, List.class, ITriggerEvent.class), getAbilityToPlay (Mode.SERVER, SpellAbilityView.class, CardView.class, List/*SpellAbilityView*/.class, ITriggerEvent.class),
assignDamage(Map.class, CardView.class, List.class, Integer.TYPE, GameEntityView.class, Boolean.TYPE), assignDamage (Mode.SERVER, Map.class, CardView.class, List/*CardView*/.class, Integer.TYPE, GameEntityView.class, Boolean.TYPE),
message(Void.TYPE, String.class, String.class), message (Mode.SERVER, Void.TYPE, String.class, String.class),
showErrorDialog(Void.TYPE, String.class, String.class), showErrorDialog (Mode.SERVER, Void.TYPE, String.class, String.class),
showConfirmDialog(Boolean.TYPE, String.class, String.class, String.class, String.class, Boolean.TYPE), showConfirmDialog (Mode.SERVER, Boolean.TYPE, String.class, String.class, String.class, String.class, Boolean.TYPE),
showOptionDialog(Integer.TYPE, String.class, String.class, FSkinProp.class, Array.class, Integer.TYPE), showOptionDialog (Mode.SERVER, Integer.TYPE, String.class, String.class, FSkinProp.class, Array/*String*/.class, Integer.TYPE),
showCardOptionDialog(Integer.TYPE, CardView.class, String.class, String.class, FSkinProp.class, String.class, Array.class), showCardOptionDialog(Mode.SERVER, Integer.TYPE, CardView.class, String.class, String.class, FSkinProp.class, String.class, Array/*String*/.class),
showInputDialog(String.class, String.class, String.class, FSkinProp.class, String.class, Array.class), showInputDialog (Mode.SERVER, String.class, String.class, String.class, FSkinProp.class, String.class, Array/*String*/.class),
confirm(Boolean.TYPE, CardView.class, String.class, Boolean.TYPE, Array.class), confirm (Mode.SERVER, Boolean.TYPE, CardView.class, String.class, Boolean.TYPE, Array/*String*/.class),
getChoices(List.class, String.class, Integer.TYPE, Integer.TYPE, Collection.class, Object.class, Function.class), getChoices (Mode.SERVER, List.class, String.class, Integer.TYPE, Integer.TYPE, Collection.class, Object.class, Function.class),
order(List.class, String.class, String.class, Integer.TYPE, Integer.TYPE, List.class, List.class, CardView.class, Boolean.TYPE), order (Mode.SERVER, List.class, String.class, String.class, Integer.TYPE, Integer.TYPE, List.class, List.class, CardView.class, Boolean.TYPE),
sideboard(List.class, CardPool.class, CardPool.class), sideboard (Mode.SERVER, List.class, CardPool.class, CardPool.class),
chooseSingleEntityForEffect(GameEntityView.class, String.class, TrackableCollection.class, DelayedReveal.class, Boolean.TYPE), chooseSingleEntityForEffect(Mode.SERVER, GameEntityView.class, String.class, TrackableCollection.class, DelayedReveal.class, Boolean.TYPE),
setCard(Void.TYPE, CardView.class), setCard (Mode.SERVER, Void.TYPE, CardView.class),
// TODO case "setPlayerAvatar": // TODO case "setPlayerAvatar":
openZones(Boolean.TYPE, Collection.class, Map.class), openZones (Mode.SERVER, Boolean.TYPE, Collection/*ZoneType*/.class, Map/*PlayerView,Object*/.class),
restoreOldZones(Void.TYPE, Map.class), restoreOldZones (Mode.SERVER, Void.TYPE, Map/*PlayerView,Object*/.class),
isUiSetToSkipPhase(Boolean.TYPE, PlayerView.class, PhaseType.class), isUiSetToSkipPhase (Mode.SERVER, Boolean.TYPE, PlayerView.class, PhaseType.class),
// BUTTONS
btn_setEnabled(Void.TYPE, Boolean.TYPE),
btn_setVisible(Void.TYPE, Boolean.TYPE),
btn_setText(Void.TYPE, String.class),
btn_isSelected(Boolean.TYPE),
btn_setSelected(Void.TYPE, Boolean.TYPE),
btn_requestFocusInWindows(Boolean.TYPE),
btn_setCommand(Void.TYPE, UiCommand.class),
btn_setImage(Void.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE),
btn_setTextImage(Void.TYPE, FSkinProp.class);
// Client -> Server
// Note: these should all return void, to avoid awkward situations in
// which client and server wait for one another's response and block
// the threads that're supposed to give that response
useMana (Mode.CLIENT, Void.TYPE, Byte.TYPE),
undoLastAction (Mode.CLIENT, Void.TYPE, Boolean.TYPE),
selectPlayer (Mode.CLIENT, Void.TYPE, PlayerView.class, ITriggerEvent.class),
selectCard (Mode.CLIENT, Void.TYPE, CardView.class, List.class, ITriggerEvent.class),
selectButtonOk (Mode.CLIENT),
selectButtonCancel (Mode.CLIENT),
selectAbility (Mode.CLIENT, Void.TYPE, SpellAbilityView.class),
passPriorityUntilEndOfTurn(Mode.CLIENT),
passPriority (Mode.CLIENT),
nextGameDecision (Mode.CLIENT, Void.TYPE, NextGameDecision.class),
getActivateDescription (Mode.CLIENT, Void.TYPE, String.class, CardView.class),
concede (Mode.CLIENT),
alphaStrike (Mode.CLIENT),
reorderHand (Mode.CLIENT, Void.TYPE, CardView.class, Integer.TYPE);
private final Mode mode;
private final Class<?> returnType; private final Class<?> returnType;
private final Class<?>[] args; private final Class<?>[] args;
private ProtocolMethod() { private ProtocolMethod(final Mode mode) {
this(Void.TYPE); this(mode, Void.TYPE);
} }
private ProtocolMethod(final Class<?> returnType) { private ProtocolMethod(final Mode mode, final Class<?> returnType) {
this(returnType, (Class<?>[]) null); this(mode, returnType, (Class<?>[]) null);
} }
@SafeVarargs @SafeVarargs
private ProtocolMethod(final Class<?> returnType, final Class<?> ... args) { private ProtocolMethod(final Mode mode, final Class<?> returnType, final Class<?> ... args) {
this.mode = mode;
this.returnType = returnType; this.returnType = returnType;
this.args = args == null ? new Class<?>[] {} : args; this.args = args == null ? new Class<?>[] {} : args;
} }
public Method getMethod() { public Method getMethod() {
try { try {
final String name; final Class<?> toCall = mode.toInvoke;
final Class<?> toCall; final Method candidate = toCall.getMethod(name(), args);
if (name().startsWith("btn_")) { // Don't check Client return values for now as some use void
name = name().substring("btn_".length()); // and a default return value, to improve performance
toCall = IButton.class; if (mode == Mode.SERVER && !candidate.getReturnType().equals(returnType)) {
} else {
name = name();
toCall = IGuiGame.class;
}
final Method candidate = toCall.getMethod(name, args);
if (!candidate.getReturnType().equals(returnType)) {
throw new NoSuchMethodException(String.format("Wrong return type for method %s", name())); throw new NoSuchMethodException(String.format("Wrong return type for method %s", name()));
} }
return candidate; return candidate;
} catch (final NoSuchMethodException | SecurityException e) { } catch (final NoSuchMethodException | SecurityException e) {
System.err.println(String.format("Warning: class contains no method named %s", name())); System.err.println(String.format("Warning: class contains no accessible method named %s", name()));
return null; return getMethodNoArgs();
} }
} }
public boolean invokeOnButton() { private Method getMethodNoArgs() {
return name().startsWith("btn_"); try {
return mode.toInvoke.getMethod(name(), (Class<?>[]) null);
} catch (final NoSuchMethodException | SecurityException e) {
System.err.println(String.format("Warning: class contains no accessible arg-less method named %s", name()));
return null;
}
}
public Class<?> getReturnType() {
return returnType;
}
public Class<?>[] getArgTypes() {
return args;
}
public void checkArgs(final Object[] args) {
for (int iArg = 0; iArg < args.length; iArg++) {
final Object arg = args[iArg];
final Class<?> type = this.args[iArg];
if (!ReflectionUtil.isInstance(arg, type)) {
throw new InternalError(String.format("Protocol method %s: illegal argument (%d) of type %s, %s expected", name(), iArg, arg.getClass().getName(), type.getName()));
}
}
}
public void checkReturnValue(final Object value) {
if (returnType.equals(Void.TYPE)) {
// If void is expected, any return value is fine
return;
}
if (!ReflectionUtil.isInstance(value, returnType)) {
throw new IllegalStateException(String.format("Protocol method %s: illegal return object type %s returned by client, expected %s", name(), value.getClass().getName(), getReturnType().getName()));
}
} }
} }
} }

View File

@@ -0,0 +1,96 @@
package forge.net;
import forge.FThreads;
import forge.net.GameProtocol.ProtocolMethod;
import forge.net.event.GuiGameEvent;
import forge.net.event.ReplyEvent;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public abstract class GameProtocolHandler<T> extends ChannelInboundHandlerAdapter {
private final boolean runInEdt;
protected GameProtocolHandler(final boolean runInEdt) {
this.runInEdt = runInEdt;
}
protected abstract ReplyPool getReplyPool(ChannelHandlerContext ctx);
protected abstract IRemote getRemote(ChannelHandlerContext ctx);
protected abstract T getToInvoke(ChannelHandlerContext ctx);
protected abstract void beforeCall(ProtocolMethod protocolMethod, Object[] args);
@Override
public final void channelRead(final ChannelHandlerContext ctx, final Object msg) {
System.out.println("Received: " + msg);
if (msg instanceof ReplyEvent) {
final ReplyEvent event = (ReplyEvent) msg;
getReplyPool(ctx).complete(event.getIndex(), event.getReply());
} else if (msg instanceof GuiGameEvent) {
final GuiGameEvent event = (GuiGameEvent) msg;
final ProtocolMethod protocolMethod = event.getMethod();
final String methodName = protocolMethod.name();
final Method method = protocolMethod.getMethod();
if (method == null) {
throw new IllegalStateException(String.format("Method %s not found", protocolMethod.name()));
}
final Object[] args = event.getObjects();
protocolMethod.checkArgs(args);
final Object toInvoke = getToInvoke(ctx);
// Pre-call actions
beforeCall(protocolMethod, args);
final Class<?> returnType = protocolMethod.getReturnType();
final Runnable toRun = new Runnable() {
@Override public final void run() {
if (returnType.equals(Void.TYPE)) {
try {
method.invoke(toInvoke, args);
} catch (final IllegalAccessException | IllegalArgumentException e) {
System.err.println(String.format("Unknown protocol method %s with %d args", methodName, args == null ? 0 : args.length));
} catch (final InvocationTargetException e) {
throw new RuntimeException(e);
}
} else {
Serializable reply = null;
try {
final Object theReply = method.invoke(toInvoke, args);
if (theReply instanceof Serializable) {
protocolMethod.checkReturnValue(theReply);
reply = (Serializable) theReply;
} else if (theReply != null) {
System.err.println(String.format("Non-serializable return type %s for method %s, returning null", returnType.getName(), methodName));
}
} catch (final IllegalAccessException | IllegalArgumentException e) {
System.err.println(String.format("Unknown protocol method %s with %d args, replying with null", methodName, args == null ? 0 : args.length));
} catch (final InvocationTargetException e) {
throw new RuntimeException(e);
}
getRemote(ctx).send(new ReplyEvent(event.getId(), reply));
}
}
};
if (runInEdt) {
FThreads.invokeInEdtNowOrLater(toRun);
} else {
FThreads.invokeInBackgroundThread(toRun);
}
}
}
@Override
public final void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}

View File

@@ -0,0 +1,32 @@
package forge.net;
import java.util.concurrent.TimeoutException;
import forge.net.GameProtocol.ProtocolMethod;
import forge.net.event.GuiGameEvent;
public final class GameProtocolSender {
private final IRemote remote;
public GameProtocolSender(final IRemote remote) {
this.remote = remote;
}
public void send(final ProtocolMethod method, final Object... args) {
method.checkArgs(args);
remote.send(new GuiGameEvent(method, args));
}
@SuppressWarnings("unchecked")
public <T> T sendAndWait(final ProtocolMethod method, final Object... args) {
method.checkArgs(args);
try {
final Object returned = remote.sendAndWait(new GuiGameEvent(method, args));
method.checkReturnValue(returned);
return (T) returned;
} catch (final TimeoutException e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -1,26 +1,18 @@
package forge.net.client; package forge.net.client;
import forge.FThreads; import io.netty.channel.ChannelHandlerContext;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.interfaces.IGuiGame; import forge.interfaces.IGuiGame;
import forge.match.MatchButtonType;
import forge.model.FModel; import forge.model.FModel;
import forge.net.GameProtocol;
import forge.net.GameProtocol.ProtocolMethod; import forge.net.GameProtocol.ProtocolMethod;
import forge.net.event.GuiGameEvent; import forge.net.GameProtocolHandler;
import forge.net.IRemote;
import forge.net.ReplyPool;
import forge.net.event.LoginEvent; import forge.net.event.LoginEvent;
import forge.net.event.ReplyEvent;
import forge.properties.ForgePreferences.FPref; import forge.properties.ForgePreferences.FPref;
import forge.trackable.TrackableCollection; import forge.trackable.TrackableCollection;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.io.Serializable; final class GameClientHandler extends GameProtocolHandler<IGuiGame> {
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
class GameClientHandler extends ChannelInboundHandlerAdapter {
private final FGameClient client; private final FGameClient client;
private final IGuiGame gui; private final IGuiGame gui;
@@ -28,93 +20,43 @@ class GameClientHandler extends ChannelInboundHandlerAdapter {
* Creates a client-side game handler. * Creates a client-side game handler.
*/ */
public GameClientHandler(final FGameClient client) { public GameClientHandler(final FGameClient client) {
super(true);
this.client = client; this.client = client;
this.gui = client.getGui(); this.gui = client.getGui();
} }
@Override
protected ReplyPool getReplyPool(final ChannelHandlerContext ctx) {
return client.getReplyPool();
}
@Override
protected IRemote getRemote(final ChannelHandlerContext ctx) {
return client;
}
@Override
protected IGuiGame getToInvoke(final ChannelHandlerContext ctx) {
return gui;
}
@SuppressWarnings("unchecked")
@Override
protected void beforeCall(final ProtocolMethod protocolMethod, final Object[] args) {
switch (protocolMethod) {
case openView:
final TrackableCollection<PlayerView> myPlayers = (TrackableCollection<PlayerView>) args[0];
client.setGameControllers(myPlayers);
break;
default:
break;
}
}
@Override @Override
public void channelActive(final ChannelHandlerContext ctx) { public void channelActive(final ChannelHandlerContext ctx) {
// Don't use send() here, as this.channel is not yet set! // Don't use send() here, as this.channel is not yet set!
ctx.channel().writeAndFlush(new LoginEvent(FModel.getPreferences().getPref(FPref.PLAYER_NAME), Integer.parseInt(FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",")[0]))); ctx.channel().writeAndFlush(new LoginEvent(FModel.getPreferences().getPref(FPref.PLAYER_NAME), Integer.parseInt(FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",")[0])));
} }
@SuppressWarnings("unchecked")
@Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) {
System.out.println("Client received: " + msg);
if (msg instanceof ReplyEvent) {
final ReplyEvent event = (ReplyEvent) msg;
client.getReplyPool().complete(event.getIndex(), event.getReply());
} else if (msg instanceof GuiGameEvent) {
final GuiGameEvent event = (GuiGameEvent) msg;
final String methodName = event.getMethod();
final Object[] originalArgs = event.getObjects();
final ProtocolMethod protocolMethod = GameProtocol.getProtocolMethod(methodName);
if (protocolMethod == null) {
throw new IllegalStateException(String.format("Protocol method %s unknown", methodName));
}
final Method method = protocolMethod.getMethod();
if (method == null) {
throw new IllegalStateException(String.format("Method %s not found", protocolMethod.name()));
}
final Object toInvoke;
final Object[] args;
if (protocolMethod.invokeOnButton()) {
toInvoke = originalArgs[1] == MatchButtonType.OK ? gui.getBtnOK((PlayerView) originalArgs[0]) : gui.getBtnCancel((PlayerView) originalArgs[0]);
// Remove the player and button type from the args passed to the method
args = Arrays.copyOfRange(originalArgs, 2, originalArgs.length);
} else {
toInvoke = gui;
args = originalArgs;
}
// Pre-call actions
switch (protocolMethod) {
case openView:
final TrackableCollection<PlayerView> myPlayers = (TrackableCollection<PlayerView>) args[0];
client.setGameControllers(myPlayers);
break;
default:
break;
}
final Class<?> returnType = method.getReturnType();
if (returnType.equals(Void.TYPE)) {
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public final void run() {
try {
method.invoke(toInvoke, args);
} catch (final IllegalAccessException | IllegalArgumentException e) {
System.err.println(String.format("Unknown protocol method %s with %d args", methodName, args == null ? 0 : args.length));
} catch (final InvocationTargetException e) {
throw new RuntimeException(e);
}
}
});
} else {
Serializable reply = null;
try {
final Object theReply = method.invoke(toInvoke, args);
if (theReply instanceof Serializable) {
reply = (Serializable) method.invoke(toInvoke, args);
} else {
System.err.println(String.format("Non-serializable return type %s for method %s", returnType.getName(), methodName));
}
} catch (final IllegalAccessException | IllegalArgumentException e) {
System.err.println(String.format("Unknown protocol method %s with %d args, replying with null", methodName, args == null ? 0 : args.length));
} catch (final InvocationTargetException e) {
throw new RuntimeException(e);
}
client.send(new ReplyEvent(event.getId(), reply));
}
}
}
@Override
public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) {
cause.printStackTrace();
ctx.close();
}
} }

View File

@@ -1,7 +1,6 @@
package forge.net.client; package forge.net.client;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeoutException;
import forge.game.card.CardView; import forge.game.card.CardView;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
@@ -9,90 +8,77 @@ import forge.game.spellability.SpellAbilityView;
import forge.interfaces.IDevModeCheats; import forge.interfaces.IDevModeCheats;
import forge.interfaces.IGameController; import forge.interfaces.IGameController;
import forge.match.NextGameDecision; import forge.match.NextGameDecision;
import forge.net.event.GuiGameEvent; import forge.net.GameProtocol.ProtocolMethod;
import forge.net.GameProtocolSender;
import forge.util.ITriggerEvent; import forge.util.ITriggerEvent;
public class NetGameController implements IGameController { public class NetGameController implements IGameController {
private final IToServer server; private final GameProtocolSender sender;
public NetGameController(final IToServer server) { public NetGameController(final IToServer server) {
this.server = server; this.sender = new GameProtocolSender(server);
} }
private String methodName() { private void send(final ProtocolMethod method, final Object... args) {
boolean passedFirst = false; sender.send(method, args);
for (final StackTraceElement ste : Thread.currentThread().getStackTrace()) {
if (ste.getClassName() == getClass().getName()) {
if (passedFirst) {
return ste.getMethodName();
}
passedFirst = true;
}
}
return null;
} }
private void send(final String method, final Object... args) { private <T> T sendAndWait(final ProtocolMethod method, final Object... args) {
server.send(new GuiGameEvent(method, args)); return sender.sendAndWait(method, args);
}
@SuppressWarnings("unchecked")
private <T> T sendAndWait(final String method, final Object... args) {
try {
return (T) server.sendAndWait(new GuiGameEvent(method, args));
} catch (final TimeoutException e) {
e.printStackTrace();
}
return null;
} }
@Override @Override
public boolean useMana(final byte color) { public void useMana(final byte color) {
return sendAndWait(methodName(), color); send(ProtocolMethod.useMana, Byte.valueOf(color));
} }
@Override @Override
public boolean tryUndoLastAction() { public void undoLastAction() {
return sendAndWait(methodName()); send(ProtocolMethod.undoLastAction);
} }
@Override @Override
public void selectPlayer(final PlayerView playerView, final ITriggerEvent triggerEvent) { public void selectPlayer(final PlayerView playerView, final ITriggerEvent triggerEvent) {
send(methodName(), playerView, triggerEvent); send(ProtocolMethod.selectPlayer, playerView, triggerEvent);
} }
@Override @Override
public boolean selectCard(final CardView cardView, final List<CardView> otherCardViewsToSelect, final ITriggerEvent triggerEvent) { public boolean selectCard(final CardView cardView, final List<CardView> otherCardViewsToSelect, final ITriggerEvent triggerEvent) {
return sendAndWait(methodName(), cardView, otherCardViewsToSelect, triggerEvent); send(ProtocolMethod.selectCard, cardView, otherCardViewsToSelect, triggerEvent);
// Difference from local games! Always consider a card as successfully selected,
// to avoid blocks where server and client wait for each other to respond.
// Some cost in functionality but a huge gain in stability & speed.
return true;
} }
@Override @Override
public void selectButtonOk() { public void selectButtonOk() {
send(methodName()); send(ProtocolMethod.selectButtonOk);
} }
@Override @Override
public void selectButtonCancel() { public void selectButtonCancel() {
send(methodName()); send(ProtocolMethod.selectButtonCancel);
} }
@Override @Override
public void selectAbility(final SpellAbilityView sa) { public void selectAbility(final SpellAbilityView sa) {
send(methodName(), sa); send(ProtocolMethod.selectAbility, sa);
} }
@Override @Override
public boolean passPriorityUntilEndOfTurn() { public void passPriorityUntilEndOfTurn() {
return sendAndWait(methodName()); send(ProtocolMethod.passPriorityUntilEndOfTurn);
} }
@Override @Override
public boolean passPriority() { public void passPriority() {
return sendAndWait(methodName()); send(ProtocolMethod.passPriority);
} }
@Override @Override
public void nextGameDecision(final NextGameDecision decision) { public void nextGameDecision(final NextGameDecision decision) {
send(methodName(), decision); send(ProtocolMethod.nextGameDecision, decision);
} }
@Override @Override
@@ -102,31 +88,33 @@ public class NetGameController implements IGameController {
} }
public String getActivateDescription(final CardView card) { public String getActivateDescription(final CardView card) {
return sendAndWait(methodName(), card); return sendAndWait(ProtocolMethod.getActivateDescription, card);
} }
@Override @Override
public void concede() { public void concede() {
send(methodName()); send(ProtocolMethod.concede);
} }
@Override @Override
public IDevModeCheats cheat() { public IDevModeCheats cheat() {
// No cheating in network games!
return IDevModeCheats.NO_CHEAT; return IDevModeCheats.NO_CHEAT;
} }
@Override @Override
public boolean canPlayUnlimitedLands() { public boolean canPlayUnlimitedLands() {
// Don't do this over network
return false; return false;
} }
@Override @Override
public void alphaStrike() { public void alphaStrike() {
send(methodName()); send(ProtocolMethod.alphaStrike);
} }
@Override @Override
public void reorderHand(CardView card, int index) { public void reorderHand(CardView card, int index) {
send(methodName(), card, index); send(ProtocolMethod.reorderHand, card, Integer.valueOf(index));
} }
} }

View File

@@ -1,5 +1,6 @@
package forge.net.event; package forge.net.event;
import forge.net.GameProtocol.ProtocolMethod;
import forge.net.server.RemoteClient; import forge.net.server.RemoteClient;
public final class GuiGameEvent implements IdentifiableNetEvent { public final class GuiGameEvent implements IdentifiableNetEvent {
@@ -7,10 +8,10 @@ public final class GuiGameEvent implements IdentifiableNetEvent {
private static int staticId = 0; private static int staticId = 0;
private final int id; private final int id;
private final String method; private final ProtocolMethod method;
private final Object[] objects; private final Object[] objects;
public GuiGameEvent(final String method, final Object ... objects) { public GuiGameEvent(final ProtocolMethod method, final Object ... objects) {
this.id = staticId++; this.id = staticId++;
this.method = method; this.method = method;
this.objects = objects == null ? new Object[0] : objects; this.objects = objects == null ? new Object[0] : objects;
@@ -30,7 +31,7 @@ public final class GuiGameEvent implements IdentifiableNetEvent {
return id; return id;
} }
public String getMethod() { public ProtocolMethod getMethod() {
return method; return method;
} }

View File

@@ -1,28 +1,19 @@
package forge.net.server; package forge.net.server;
import forge.FThreads;
import forge.GuiBase; import forge.GuiBase;
import forge.LobbyPlayer;
import forge.game.GameView; import forge.game.GameView;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView;
import forge.interfaces.IGameController; import forge.interfaces.IGameController;
import forge.interfaces.IGuiGame; import forge.interfaces.IGuiGame;
import forge.interfaces.ILobbyListener; import forge.interfaces.ILobbyListener;
import forge.match.LobbySlot; import forge.match.LobbySlot;
import forge.match.LobbySlotType; import forge.match.LobbySlotType;
import forge.match.NextGameDecision;
import forge.net.event.GuiGameEvent;
import forge.net.event.LobbyUpdateEvent; import forge.net.event.LobbyUpdateEvent;
import forge.net.event.LoginEvent; import forge.net.event.LoginEvent;
import forge.net.event.LogoutEvent; import forge.net.event.LogoutEvent;
import forge.net.event.MessageEvent; import forge.net.event.MessageEvent;
import forge.net.event.NetEvent; import forge.net.event.NetEvent;
import forge.net.event.ReplyEvent;
import forge.net.event.UpdateLobbyPlayerEvent; import forge.net.event.UpdateLobbyPlayerEvent;
import forge.properties.ForgeConstants; import forge.properties.ForgeConstants;
import forge.util.ITriggerEvent;
import io.netty.bootstrap.ServerBootstrap; import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
@@ -40,12 +31,10 @@ import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler; import io.netty.handler.logging.LoggingHandler;
import java.io.Serializable;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.PropertyConfigurator;
@@ -79,6 +68,16 @@ public final class FServerManager {
private FServerManager() { private FServerManager() {
} }
RemoteClient getClient(final Channel ch) {
return clients.get(ch);
}
GameView getGameView() {
return localLobby.getGameView();
}
IGameController getController(final int index) {
return localLobby.getController(index);
}
/** /**
* Get the singleton instance of {@link FServerManager}. * Get the singleton instance of {@link FServerManager}.
* *
@@ -126,7 +125,7 @@ public final class FServerManager {
} }
}).start(); }).start();
//mapNatPort(port); mapNatPort(port);
Runtime.getRuntime().addShutdownHook(shutdownHook); Runtime.getRuntime().addShutdownHook(shutdownHook);
isHosting = true; isHosting = true;
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
@@ -229,197 +228,6 @@ public final class FServerManager {
} }
} }
private class GameServerHandler extends ChannelInboundHandlerAdapter {
@SuppressWarnings("unchecked")
@Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) {
System.out.println("Server received: " + msg);
final RemoteClient client = clients.get(ctx.channel());
if (msg instanceof ReplyEvent) {
client.setReply(((ReplyEvent) msg).getIndex(), ((ReplyEvent) msg).getReply());
} else if (msg instanceof GuiGameEvent) {
final GuiGameEvent event = (GuiGameEvent) msg;
final GameView gameView = localLobby.getGameView();
final IGameController controller = localLobby.getController(client.getIndex());
final Object[] args = event.getObjects();
FThreads.invokeInBackgroundThread(new Runnable() {
@Override public final void run() {
Serializable reply = null;
boolean doReply = false;
switch (event.getMethod()) {
// From GameController
case "useMana":
controller.useMana((byte) args[0]);
break;
case "tryUndoLastAction":
reply = controller.tryUndoLastAction();
doReply = true;
break;
case "selectPlayer":
controller.selectPlayer((PlayerView) args[0], (ITriggerEvent) args[1]);
break;
case "selectCard":
reply = controller.selectCard((CardView) args[0], (List<CardView>) args[1], (ITriggerEvent) args[2]);
doReply = true;
break;
case "selectButtonOk":
controller.selectButtonOk();
break;
case "selectButtonCancel":
controller.selectButtonCancel();
break;
case "selectAbility":
controller.selectAbility((SpellAbilityView) args[0]);
break;
case "passPriorityUntilEndOfTurn":
reply = controller.passPriorityUntilEndOfTurn();
doReply = true;
break;
case "passPriority":
reply = controller.passPriority();
doReply = true;
break;
case "nextGameDecision":
controller.nextGameDecision((NextGameDecision) args[0]);
break;
case "mayLookAtAllCards":
reply = controller.mayLookAtAllCards();
doReply = true;
break;
case "getActivateDescription":
reply = controller.getActivateDescription((CardView) args[0]);
doReply = true;
break;
case "concede":
controller.concede();
break;
case "alphaStrike":
controller.alphaStrike();
break;
// From GameView
case "getPlayers":
reply = (Serializable) gameView.getPlayers();
doReply = true;
break;
case "getTitle":
reply = gameView.getTitle();
doReply = true;
break;
case "isCommander":
reply = gameView.isCommander();
doReply = true;
break;
case "getGameType":
reply = gameView.getGameType();
doReply = true;
break;
case "getPoisonCountersToLose":
reply = gameView.getPoisonCountersToLose();
doReply = true;
break;
case "getNumGamesInMatch":
reply = gameView.getNumGamesInMatch();
doReply = true;
break;
case "getTurn":
reply = gameView.getTurn();
doReply = true;
break;
case "getPhase":
reply = gameView.getPhase();
doReply = true;
break;
case "getPlayerTurn":
reply = gameView.getPlayerTurn();
doReply = true;
break;
case "getStack":
reply = (Serializable) gameView.getStack();
doReply = true;
break;
case "peekStack":
reply = gameView.peekStack();
doReply = true;
break;
case "getStormCount":
reply = gameView.getStormCount();
doReply = true;
break;
case "isFirstGameInMatch":
reply = gameView.isFirstGameInMatch();
doReply = true;
break;
case "getNumPlayedGamesInMatch":
reply = gameView.getNumPlayedGamesInMatch();
doReply = true;
break;
case "isGameOver":
reply = gameView.isGameOver();
doReply = true;
break;
case "isMatchOver":
reply = gameView.isMatchOver();
doReply = true;
break;
case "getWinningTeam":
reply = gameView.getWinningTeam();
doReply = true;
break;
case "getGameLog":
reply = gameView.getGameLog();
doReply = true;
break;
case "getCombat":
reply = gameView.getCombat();
doReply = true;
break;
case "isMatchWonBy":
reply = gameView.isMatchWonBy((LobbyPlayer) args[0]);
doReply = true;
break;
case "getOutcomesOfMatch":
reply = (Serializable) gameView.getOutcomesOfMatch();
doReply = true;
break;
// TODO case "getWinningPlayer":
case "isWinner":
reply = gameView.isWinner((LobbyPlayer) args[0]);
doReply = true;
break;
case "getGamesWonBy":
reply = gameView.getGamesWonBy((LobbyPlayer) args[0]);
doReply = true;
break;
case "getDeck":
reply = gameView.getDeck((String) args[0]);
doReply = true;
break;
case "getAnteResult":
reply = gameView.getAnteResult((PlayerView) args[0]);
doReply = true;
break;
default:
System.err.println(String.format("Unknown incoming client command %s", event.getMethod()));
break;
}
if (doReply) {
client.send(new ReplyEvent(event.getId(), reply));
}
}
});
}
}
@Override
public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
private class RegisterClientHandler extends ChannelInboundHandlerAdapter { private class RegisterClientHandler extends ChannelInboundHandlerAdapter {
@Override @Override
public void channelActive(final ChannelHandlerContext ctx) throws Exception { public void channelActive(final ChannelHandlerContext ctx) throws Exception {

View File

@@ -0,0 +1,41 @@
package forge.net.server;
import io.netty.channel.ChannelHandlerContext;
import forge.interfaces.IGameController;
import forge.net.GameProtocol.ProtocolMethod;
import forge.net.GameProtocolHandler;
import forge.net.IRemote;
import forge.net.ReplyPool;
final class GameServerHandler extends GameProtocolHandler<IGameController> {
private final FServerManager server = FServerManager.getInstance();
GameServerHandler() {
super(false);
}
private RemoteClient getClient(final ChannelHandlerContext ctx) {
return server.getClient(ctx.channel());
}
@Override
protected ReplyPool getReplyPool(final ChannelHandlerContext ctx) {
return getClient(ctx).getReplyPool();
}
@Override
protected IRemote getRemote(final ChannelHandlerContext ctx) {
return getClient(ctx);
}
@Override
protected IGameController getToInvoke(final ChannelHandlerContext ctx) {
return server.getController(getClient(ctx).getIndex());
}
@Override
protected void beforeCall(final ProtocolMethod protocolMethod, final Object[] args) {
// Nothing needs to be done here
}
}

View File

@@ -1,16 +1,12 @@
package forge.net.server; package forge.net.server;
import java.util.Collection; import java.util.Collection;
import java.util.EnumMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeoutException;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.Maps;
import forge.LobbyPlayer; import forge.LobbyPlayer;
import forge.UiCommand;
import forge.assets.FSkinProp; import forge.assets.FSkinProp;
import forge.deck.CardPool; import forge.deck.CardPool;
import forge.game.GameEntityView; import forge.game.GameEntityView;
@@ -22,54 +18,31 @@ import forge.game.player.IHasIcon;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView; import forge.game.spellability.SpellAbilityView;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.interfaces.IButton;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.match.AbstractGuiGame; import forge.match.AbstractGuiGame;
import forge.match.MatchButtonType; import forge.net.GameProtocol.ProtocolMethod;
import forge.net.event.GuiGameEvent; import forge.net.GameProtocolSender;
import forge.player.PlayerZoneUpdate; import forge.player.PlayerZoneUpdate;
import forge.trackable.TrackableCollection; import forge.trackable.TrackableCollection;
import forge.util.ITriggerEvent; import forge.util.ITriggerEvent;
public class NetGuiGame extends AbstractGuiGame { public class NetGuiGame extends AbstractGuiGame {
private final IToClient client; private final GameProtocolSender sender;
private final Map<MatchButtonType, Map<PlayerView, NetButton>> btns = new EnumMap<MatchButtonType, Map<PlayerView,NetButton>>(MatchButtonType.class);
public NetGuiGame(final IToClient client) { public NetGuiGame(final IToClient client) {
this.client = client; this.sender = new GameProtocolSender(client);
for (final MatchButtonType type : MatchButtonType.values()) {
btns.put(type, Maps.<PlayerView, NetButton>newHashMap());
}
} }
private String methodName() { private void send(final ProtocolMethod method, final Object... args) {
boolean passedFirst = false; sender.send(method, args);
for (final StackTraceElement ste : Thread.currentThread().getStackTrace()) {
if (ste.getClassName() == getClass().getName()) {
if (passedFirst) {
return ste.getMethodName();
}
passedFirst = true;
}
}
return null;
} }
private void send(final String method, final Object... args) { private <T> T sendAndWait(final ProtocolMethod method, final Object... args) {
client.send(new GuiGameEvent(method, args)); return sender.sendAndWait(method, args);
}
@SuppressWarnings("unchecked")
private <T> T sendAndWait(final String method, final Object... args) {
try {
return (T) client.sendAndWait(new GuiGameEvent(method, args));
} catch (final TimeoutException e) {
e.printStackTrace();
}
return null;
} }
private void updateGameView() { private void updateGameView() {
send("setGameView", getGameView()); send(ProtocolMethod.setGameView, getGameView());
} }
@Override @Override
@@ -80,227 +53,206 @@ public class NetGuiGame extends AbstractGuiGame {
@Override @Override
public void openView(final TrackableCollection<PlayerView> myPlayers) { public void openView(final TrackableCollection<PlayerView> myPlayers) {
for (final MatchButtonType type : MatchButtonType.values()) { send(ProtocolMethod.openView, myPlayers);
btns.get(type).clear();
for (final PlayerView player : myPlayers) {
btns.get(type).put(player, new NetButton(player, type));
}
}
send(methodName(), myPlayers);
updateGameView(); updateGameView();
} }
@Override @Override
public void afterGameEnd() { public void afterGameEnd() {
send(methodName()); send(ProtocolMethod.afterGameEnd);
} }
@Override @Override
public void showCombat() { public void showCombat() {
send(methodName()); send(ProtocolMethod.showCombat);
} }
@Override @Override
public void showPromptMessage(final PlayerView playerView, final String message) { public void showPromptMessage(final PlayerView playerView, final String message) {
updateGameView(); updateGameView();
send(methodName(), playerView, message); send(ProtocolMethod.showPromptMessage, playerView, message);
} }
@Override @Override
public boolean stopAtPhase(final PlayerView playerTurn, final PhaseType phase) { public void updateButtons(final PlayerView owner, final String label1, final String label2, final boolean enable1, final boolean enable2, final boolean focus1) {
return sendAndWait(methodName(), playerTurn, phase); send(ProtocolMethod.updateButtons, owner, label1, label2, enable1, enable2, focus1);
}
@Override
public IButton getBtnOK(final PlayerView playerView) {
return btns.get(MatchButtonType.OK).get(playerView);
}
@Override
public IButton getBtnCancel(final PlayerView playerView) {
return btns.get(MatchButtonType.CANCEL).get(playerView);
}
@Override
public void focusButton(final MatchButtonType button) {
send(methodName(), button);
} }
@Override @Override
public void flashIncorrectAction() { public void flashIncorrectAction() {
send(methodName()); send(ProtocolMethod.flashIncorrectAction);
} }
@Override @Override
public void updatePhase() { public void updatePhase() {
updateGameView(); updateGameView();
send(methodName()); send(ProtocolMethod.updatePhase);
} }
@Override @Override
public void updateTurn(final PlayerView player) { public void updateTurn(final PlayerView player) {
updateGameView(); updateGameView();
send(methodName(), player); send(ProtocolMethod.updateTurn, player);
} }
@Override @Override
public void updatePlayerControl() { public void updatePlayerControl() {
updateGameView(); updateGameView();
send(methodName()); send(ProtocolMethod.updatePlayerControl);
} }
@Override @Override
public void enableOverlay() { public void enableOverlay() {
send(methodName()); send(ProtocolMethod.enableOverlay);
} }
@Override @Override
public void disableOverlay() { public void disableOverlay() {
send(methodName()); send(ProtocolMethod.disableOverlay);
} }
@Override @Override
public void finishGame() { public void finishGame() {
send(methodName()); send(ProtocolMethod.finishGame);
} }
@Override @Override
public Object showManaPool(final PlayerView player) { public Object showManaPool(final PlayerView player) {
send(methodName(), player); send(ProtocolMethod.showManaPool, player);
return null; return null;
} }
@Override @Override
public void hideManaPool(final PlayerView player, final Object zoneToRestore) { public void hideManaPool(final PlayerView player, final Object zoneToRestore) {
send(methodName(), player, zoneToRestore); send(ProtocolMethod.hideManaPool, player, zoneToRestore);
} }
@Override @Override
public void updateStack() { public void updateStack() {
updateGameView(); updateGameView();
send(methodName()); send(ProtocolMethod.updateStack);
} }
@Override @Override
public void updateZones(final Iterable<PlayerZoneUpdate> zonesToUpdate) { public void updateZones(final Iterable<PlayerZoneUpdate> zonesToUpdate) {
updateGameView(); updateGameView();
send(methodName(), zonesToUpdate); send(ProtocolMethod.updateZones, zonesToUpdate);
} }
@Override @Override
public void updateCards(final Iterable<CardView> cards) { public void updateCards(final Iterable<CardView> cards) {
updateGameView(); updateGameView();
send(methodName(), cards); send(ProtocolMethod.updateCards, cards);
} }
@Override @Override
public void updateManaPool(final Iterable<PlayerView> manaPoolUpdate) { public void updateManaPool(final Iterable<PlayerView> manaPoolUpdate) {
updateGameView(); updateGameView();
send(methodName(), manaPoolUpdate); send(ProtocolMethod.updateManaPool, manaPoolUpdate);
} }
@Override @Override
public void updateLives(final Iterable<PlayerView> livesUpdate) { public void updateLives(final Iterable<PlayerView> livesUpdate) {
updateGameView(); updateGameView();
send(methodName(), livesUpdate); send(ProtocolMethod.updateLives, livesUpdate);
} }
@Override @Override
public void setPanelSelection(final CardView hostCard) { public void setPanelSelection(final CardView hostCard) {
updateGameView(); updateGameView();
send(methodName(), hostCard); send(ProtocolMethod.setPanelSelection, hostCard);
} }
@Override @Override
public SpellAbilityView getAbilityToPlay(final List<SpellAbilityView> abilities, final ITriggerEvent triggerEvent) { public SpellAbilityView getAbilityToPlay(final CardView hostCard, final List<SpellAbilityView> abilities, final ITriggerEvent triggerEvent) {
return sendAndWait(methodName(), abilities, triggerEvent); return sendAndWait(ProtocolMethod.getAbilityToPlay, hostCard, abilities, triggerEvent);
} }
@Override @Override
public Map<CardView, Integer> assignDamage(final CardView attacker, final List<CardView> blockers, final int damage, final GameEntityView defender, final boolean overrideOrder) { public Map<CardView, Integer> assignDamage(final CardView attacker, final List<CardView> blockers, final int damage, final GameEntityView defender, final boolean overrideOrder) {
return sendAndWait(methodName(), attacker, blockers, damage, defender, overrideOrder); return sendAndWait(ProtocolMethod.assignDamage, attacker, blockers, damage, defender, overrideOrder);
} }
@Override @Override
public void message(final String message, final String title) { public void message(final String message, final String title) {
send(methodName(), message, title); send(ProtocolMethod.message, message, title);
} }
@Override @Override
public void showErrorDialog(final String message, final String title) { public void showErrorDialog(final String message, final String title) {
send(methodName(), message, title); send(ProtocolMethod.showErrorDialog, message, title);
} }
@Override @Override
public boolean showConfirmDialog(final String message, final String title, final String yesButtonText, final String noButtonText, final boolean defaultYes) { public boolean showConfirmDialog(final String message, final String title, final String yesButtonText, final String noButtonText, final boolean defaultYes) {
return sendAndWait(methodName(), message, title, yesButtonText, noButtonText, defaultYes); return sendAndWait(ProtocolMethod.showConfirmDialog, message, title, yesButtonText, noButtonText, defaultYes);
} }
@Override @Override
public int showOptionDialog(final String message, final String title, final FSkinProp icon, final String[] options, final int defaultOption) { public int showOptionDialog(final String message, final String title, final FSkinProp icon, final String[] options, final int defaultOption) {
return sendAndWait(methodName(), message, title, icon, options, defaultOption); return sendAndWait(ProtocolMethod.showOptionDialog, message, title, icon, options, defaultOption);
} }
@Override @Override
public int showCardOptionDialog(final CardView card, final String message, final String title, final FSkinProp icon, final String[] options, final int defaultOption) { public int showCardOptionDialog(final CardView card, final String message, final String title, final FSkinProp icon, final String[] options, final int defaultOption) {
return sendAndWait(methodName(), card, message, title, icon, options, defaultOption); return sendAndWait(ProtocolMethod.showCardOptionDialog, card, message, title, icon, options, defaultOption);
} }
@Override @Override
public String showInputDialog(final String message, final String title, final FSkinProp icon, final String initialInput, final String[] inputOptions) { public String showInputDialog(final String message, final String title, final FSkinProp icon, final String initialInput, final String[] inputOptions) {
return sendAndWait(methodName(), message, title, icon, initialInput, inputOptions); return sendAndWait(ProtocolMethod.showInputDialog, message, title, icon, initialInput, inputOptions);
} }
@Override @Override
public boolean confirm(final CardView c, final String question, final boolean defaultIsYes, final String[] options) { public boolean confirm(final CardView c, final String question, final boolean defaultIsYes, final String[] options) {
return sendAndWait(methodName(), c, question, defaultIsYes, options); return sendAndWait(ProtocolMethod.confirm, c, question, defaultIsYes, options);
} }
@Override @Override
public <T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices, final T selected, final Function<T, String> display) { public <T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices, final T selected, final Function<T, String> display) {
return sendAndWait(methodName(), message, min, max, choices, selected, display); return sendAndWait(ProtocolMethod.getChoices, message, min, max, choices, selected, display);
} }
@Override @Override
public <T> List<T> order(final String title, final String top, final int remainingObjectsMin, final int remainingObjectsMax, final List<T> sourceChoices, final List<T> destChoices, final CardView referenceCard, final boolean sideboardingMode) { public <T> List<T> order(final String title, final String top, final int remainingObjectsMin, final int remainingObjectsMax, final List<T> sourceChoices, final List<T> destChoices, final CardView referenceCard, final boolean sideboardingMode) {
return sendAndWait(methodName(), title, top, remainingObjectsMin, remainingObjectsMax, sourceChoices, destChoices, referenceCard, sideboardingMode); return sendAndWait(ProtocolMethod.order, title, top, remainingObjectsMin, remainingObjectsMax, sourceChoices, destChoices, referenceCard, sideboardingMode);
} }
@Override @Override
public List<PaperCard> sideboard(final CardPool sideboard, final CardPool main) { public List<PaperCard> sideboard(final CardPool sideboard, final CardPool main) {
return sendAndWait(methodName(), sideboard, main); return sendAndWait(ProtocolMethod.sideboard, sideboard, main);
} }
@Override @Override
public GameEntityView chooseSingleEntityForEffect(final String title, final Collection<? extends GameEntityView> optionList, final DelayedReveal delayedReveal, final boolean isOptional) { public GameEntityView chooseSingleEntityForEffect(final String title, final Collection<? extends GameEntityView> optionList, final DelayedReveal delayedReveal, final boolean isOptional) {
return sendAndWait(methodName(), title, optionList, delayedReveal, isOptional); return sendAndWait(ProtocolMethod.chooseSingleEntityForEffect, title, optionList, delayedReveal, isOptional);
} }
@Override @Override
public void setCard(final CardView card) { public void setCard(final CardView card) {
updateGameView(); updateGameView();
send(methodName(), card); send(ProtocolMethod.setCard, card);
} }
@Override @Override
public void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi) { public void setPlayerAvatar(final LobbyPlayer player, final IHasIcon ihi) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }
@Override @Override
public boolean openZones(final Collection<ZoneType> zones, final Map<PlayerView, Object> players) { public boolean openZones(final Collection<ZoneType> zones, final Map<PlayerView, Object> players) {
updateGameView(); updateGameView();
return sendAndWait(methodName(), zones, players); return sendAndWait(ProtocolMethod.openZones, zones, players);
} }
@Override @Override
public void restoreOldZones(final Map<PlayerView, Object> playersToRestoreZonesFor) { public void restoreOldZones(final Map<PlayerView, Object> playersToRestoreZonesFor) {
send(methodName(), playersToRestoreZonesFor); send(ProtocolMethod.restoreOldZones, playersToRestoreZonesFor);
} }
@Override @Override
public boolean isUiSetToSkipPhase(final PlayerView playerTurn, final PhaseType phase) { public boolean isUiSetToSkipPhase(final PlayerView playerTurn, final PhaseType phase) {
return sendAndWait(methodName(), playerTurn, phase); return sendAndWait(ProtocolMethod.isUiSetToSkipPhase, playerTurn, phase);
} }
@Override @Override
@@ -308,71 +260,4 @@ public class NetGuiGame extends AbstractGuiGame {
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }
private final class NetButton implements IButton {
private String methodName() {
boolean passedFirst = false;
for (final StackTraceElement ste : Thread.currentThread().getStackTrace()) {
if (ste.getClassName() == getClass().getName()) {
if (passedFirst) {
return ste.getMethodName();
}
passedFirst = true;
}
}
return null;
}
private final PlayerView playerView;
private final MatchButtonType type;
private NetButton(final PlayerView playerView, final MatchButtonType type) {
this.playerView = playerView;
this.type = type;
}
@Override
public void setEnabled(final boolean b0) {
send("btn_" + methodName(), playerView, type, b0);
}
@Override
public void setVisible(final boolean b0) {
send("btn_" + methodName(), playerView, type, b0);
}
@Override
public void setText(final String text0) {
send("btn_" + methodName(), playerView, type, text0);
}
@Override
public boolean isSelected() {
return sendAndWait("btn_" + methodName(), playerView, type);
}
@Override
public void setSelected(final boolean b0) {
send("btn_" + methodName(), playerView, type, b0);
}
@Override
public boolean requestFocusInWindow() {
return sendAndWait("btn_" + methodName(), playerView, type);
}
@Override
public void setCommand(final UiCommand command0) {
send("btn_" + methodName(), playerView, type, command0);
}
@Override
public void setImage(final FSkinProp color) {
send("btn_" + methodName(), playerView, type, color);
}
@Override
public void setTextColor(final int r, final int g, final int b) {
send("btn_" + methodName(), playerView, type, r, g, b);
}
}
} }

View File

@@ -46,7 +46,7 @@ public final class RemoteClient implements IToClient {
this.index = index; this.index = index;
} }
public void setReply(final int id, final Object reply) { ReplyPool getReplyPool() {
replies.complete(id, reply); return replies;
} }
} }

View File

@@ -144,7 +144,7 @@ public class HumanPlay {
return original; return original;
} }
final List<SpellAbility> abilities = GameActionUtil.getOptionalCosts(original); final List<SpellAbility> abilities = GameActionUtil.getOptionalCosts(original);
return p.getController().getAbilityToPlay(abilities); return p.getController().getAbilityToPlay(original.getHostCard(), abilities);
} }
private static boolean payManaCostIfNeeded(final PlayerControllerHuman controller, final Player p, final SpellAbility sa) { private static boolean payManaCostIfNeeded(final PlayerControllerHuman controller, final Player p, final SpellAbility sa) {

View File

@@ -219,8 +219,8 @@ public class PlayerControllerHuman
/** /**
* Uses GUI to learn which spell the player (human in our case) would like to play * Uses GUI to learn which spell the player (human in our case) would like to play
*/ */
public SpellAbility getAbilityToPlay(final List<SpellAbility> abilities, final ITriggerEvent triggerEvent) { public SpellAbility getAbilityToPlay(final Card hostCard, final List<SpellAbility> abilities, final ITriggerEvent triggerEvent) {
final SpellAbilityView resultView = getGui().getAbilityToPlay(SpellAbilityView.getCollection(abilities), triggerEvent); final SpellAbilityView resultView = getGui().getAbilityToPlay(CardView.get(hostCard), SpellAbilityView.getCollection(abilities), triggerEvent);
return getGame().getSpellAbility(resultView); return getGame().getSpellAbility(resultView);
} }
@@ -1304,6 +1304,12 @@ public class PlayerControllerHuman
return true; return true;
} }
@Override
public void undoLastAction() {
tryUndoLastAction();
}
public boolean tryUndoLastAction() { public boolean tryUndoLastAction() {
if (!canUndoLastAction()) { if (!canUndoLastAction()) {
return false; return false;
@@ -1333,37 +1339,33 @@ public class PlayerControllerHuman
} }
} }
public boolean passPriority() { public void passPriority() {
return passPriority(false); passPriority(false);
} }
public boolean passPriorityUntilEndOfTurn() { public void passPriorityUntilEndOfTurn() {
return passPriority(true); passPriority(true);
} }
private boolean passPriority(final boolean passUntilEndOfTurn) { private void passPriority(final boolean passUntilEndOfTurn) {
final Input inp = inputProxy.getInput(); final Input inp = inputProxy.getInput();
if (inp instanceof InputPassPriority) { if (inp instanceof InputPassPriority) {
if (passUntilEndOfTurn) { if (passUntilEndOfTurn) {
autoPassUntilEndOfTurn(); autoPassUntilEndOfTurn();
} }
inp.selectButtonOK(); inp.selectButtonOK();
return true; } else {
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public final void run() {
getGui().message("Cannot pass priority at this time.");
}
});
} }
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
getGui().message("Cannot pass priority at this time.");
}
});
return false;
} }
public boolean useMana(final byte mana) { public void useMana(final byte mana) {
final Input input = inputQueue.getInput(); final Input input = inputQueue.getInput();
if (input instanceof InputPayMana) { if (input instanceof InputPayMana) {
return ((InputPayMana) input).useManaFromPool(mana); ((InputPayMana) input).useManaFromPool(mana);
} }
return false;
} }
public void selectPlayer(final PlayerView playerView, final ITriggerEvent triggerEvent) { public void selectPlayer(final PlayerView playerView, final ITriggerEvent triggerEvent) {

View File

@@ -1,6 +1,7 @@
package forge.player; package forge.player;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
@@ -11,11 +12,21 @@ import forge.game.player.PlayerView;
public class PlayerZoneUpdates implements Iterable<PlayerZoneUpdate>, Serializable { public class PlayerZoneUpdates implements Iterable<PlayerZoneUpdate>, Serializable {
private static final long serialVersionUID = 7023549243041119023L; private static final long serialVersionUID = 7023549243041119023L;
private final Map<PlayerView, PlayerZoneUpdate> updates = Maps.newHashMap(); private final Map<PlayerView, PlayerZoneUpdate> updates = Collections.synchronizedMap(Maps.<PlayerView, PlayerZoneUpdate>newHashMap());
public PlayerZoneUpdates() { public PlayerZoneUpdates() {
} }
/**
* Copy constructor.
*
* @param other
* the {@link PlayerZoneUpdates} to copy.
*/
public PlayerZoneUpdates(final PlayerZoneUpdates other) {
this.updates.putAll(other.updates);
}
@Override @Override
public Iterator<PlayerZoneUpdate> iterator() { public Iterator<PlayerZoneUpdate> iterator() {
return updates.values().iterator(); return updates.values().iterator();