mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28:00 +00:00
- 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:
@@ -126,7 +126,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
}
|
||||
synchronized (zonesUpdate) {
|
||||
if (!zonesUpdate.isEmpty()) {
|
||||
matchController.updateZones(zonesUpdate);
|
||||
// Copy to prevent concurrency issues
|
||||
matchController.updateZones(new PlayerZoneUpdates(zonesUpdate));
|
||||
zonesUpdate.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,7 @@ public class WatchLocalGame extends PlayerControllerHuman {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryUndoLastAction() {
|
||||
return false;
|
||||
public void undoLastAction() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,18 +62,15 @@ public class WatchLocalGame extends PlayerControllerHuman {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passPriority() {
|
||||
return false;
|
||||
public void passPriority() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passPriorityUntilEndOfTurn() {
|
||||
return false;
|
||||
public void passPriorityUntilEndOfTurn() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useMana(final byte mana) {
|
||||
return false;
|
||||
public void useMana(final byte mana) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,15 +18,15 @@ public interface IGameController {
|
||||
|
||||
void alphaStrike();
|
||||
|
||||
boolean useMana(byte color);
|
||||
void useMana(byte color);
|
||||
|
||||
void selectButtonOk();
|
||||
|
||||
void selectButtonCancel();
|
||||
|
||||
boolean passPriority();
|
||||
void passPriority();
|
||||
|
||||
boolean passPriorityUntilEndOfTurn();
|
||||
void passPriorityUntilEndOfTurn();
|
||||
|
||||
void selectPlayer(PlayerView playerView, ITriggerEvent triggerEvent);
|
||||
|
||||
@@ -35,7 +35,7 @@ public interface IGameController {
|
||||
|
||||
void selectAbility(SpellAbilityView sa);
|
||||
|
||||
boolean tryUndoLastAction();
|
||||
void undoLastAction();
|
||||
|
||||
IDevModeCheats cheat();
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import forge.game.player.PlayerView;
|
||||
import forge.game.spellability.SpellAbilityView;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.item.PaperCard;
|
||||
import forge.match.MatchButtonType;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.trackable.TrackableCollection;
|
||||
import forge.util.ITriggerEvent;
|
||||
@@ -31,10 +30,8 @@ public interface IGuiGame {
|
||||
void afterGameEnd();
|
||||
void showCombat();
|
||||
void showPromptMessage(PlayerView playerView, String message);
|
||||
boolean stopAtPhase(PlayerView playerTurn, PhaseType phase);
|
||||
IButton getBtnOK(PlayerView playerView);
|
||||
IButton getBtnCancel(PlayerView playerView);
|
||||
void focusButton(MatchButtonType button);
|
||||
void updateButtons(PlayerView owner, boolean okEnabled, boolean cancelEnabled, boolean focusOk);
|
||||
void updateButtons(PlayerView owner, String label1, String label2, boolean enable1, boolean enable2, boolean focus1);
|
||||
void flashIncorrectAction();
|
||||
void updatePhase();
|
||||
void updateTurn(PlayerView player);
|
||||
@@ -52,11 +49,8 @@ public interface IGuiGame {
|
||||
void updateManaPool(Iterable<PlayerView> manaPoolUpdate);
|
||||
void updateLives(Iterable<PlayerView> livesUpdate);
|
||||
void setPanelSelection(CardView hostCard);
|
||||
SpellAbilityView getAbilityToPlay(List<SpellAbilityView> abilities,
|
||||
ITriggerEvent triggerEvent);
|
||||
Map<CardView, Integer> assignDamage(CardView attacker,
|
||||
List<CardView> blockers, int damage, GameEntityView defender,
|
||||
boolean overrideOrder);
|
||||
SpellAbilityView getAbilityToPlay(CardView hostCard, List<SpellAbilityView> abilities, ITriggerEvent triggerEvent);
|
||||
Map<CardView, Integer> assignDamage(CardView attacker, List<CardView> blockers, int damage, GameEntityView defender, boolean overrideOrder);
|
||||
|
||||
void message(String message);
|
||||
void message(String message, String title);
|
||||
@@ -163,10 +157,6 @@ public interface IGuiGame {
|
||||
void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi);
|
||||
boolean openZones(Collection<ZoneType> zones, Map<PlayerView, Object> players);
|
||||
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 setUsedToPay(CardView card, boolean value);
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import forge.game.GameView;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.card.CardView.CardStateView;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.interfaces.IButton;
|
||||
import forge.interfaces.IGameController;
|
||||
import forge.interfaces.IGuiGame;
|
||||
import forge.interfaces.IMayViewCards;
|
||||
@@ -195,20 +194,7 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateButtons(final PlayerView owner, final String okLabel, final String cancelLabel, final boolean okEnabled, final boolean cancelEnabled, final boolean focusOk) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
public abstract void updateButtons(PlayerView owner, String label1, String label2, boolean enable1, boolean enable2, boolean focus1);
|
||||
|
||||
// Auto-yield and other input-related code
|
||||
|
||||
|
||||
@@ -193,11 +193,6 @@ public class HostedMatch {
|
||||
gui.setGameController(null, humanController);
|
||||
|
||||
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
|
||||
|
||||
@@ -129,7 +129,7 @@ public class InputPassPriority extends InputSyncronizedBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
final SpellAbility ability = getController().getAbilityToPlay(abilities, triggerEvent);
|
||||
final SpellAbility ability = getController().getAbilityToPlay(card, abilities, triggerEvent);
|
||||
if (ability != null) {
|
||||
chosenSa = new ArrayList<SpellAbility>();
|
||||
chosenSa.add(ability);
|
||||
|
||||
@@ -153,14 +153,12 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
return abilities;
|
||||
}
|
||||
|
||||
public boolean useManaFromPool(byte colorCode) {
|
||||
public void useManaFromPool(byte colorCode) {
|
||||
// find the matching mana in pool.
|
||||
if (player.getManaPool().tryPayCostWithColor(colorCode, saPaidFor, manaCost)) {
|
||||
onManaAbilityPaid();
|
||||
showMessage();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean activateManaAbility(final Card card, ManaCostBeingPaid manaCost) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import java.util.Map;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import forge.UiCommand;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.deck.CardPool;
|
||||
import forge.game.GameEntityView;
|
||||
@@ -18,11 +17,12 @@ import forge.game.phase.PhaseType;
|
||||
import forge.game.player.DelayedReveal;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.game.spellability.SpellAbilityView;
|
||||
import forge.interfaces.IButton;
|
||||
import forge.interfaces.IGameController;
|
||||
import forge.interfaces.IGuiGame;
|
||||
import forge.match.MatchButtonType;
|
||||
import forge.match.NextGameDecision;
|
||||
import forge.trackable.TrackableCollection;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.ReflectionUtil;
|
||||
|
||||
public final class GameProtocol {
|
||||
|
||||
@@ -36,101 +36,146 @@ public final class GameProtocol {
|
||||
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.
|
||||
*/
|
||||
public enum ProtocolMethod {
|
||||
setGameView(Void.TYPE, GameView.class),
|
||||
openView(Void.TYPE, TrackableCollection.class),
|
||||
afterGameEnd(),
|
||||
showCombat(),
|
||||
showPromptMessage(Void.TYPE, PlayerView.class, String.class),
|
||||
stopAtPhase(Boolean.TYPE, PlayerView.class, PhaseType.class),
|
||||
focusButton(Void.TYPE, MatchButtonType.class),
|
||||
flashIncorrectAction(),
|
||||
updatePhase(),
|
||||
updateTurn(Void.TYPE, PlayerView.class),
|
||||
updatePlayerControl(),
|
||||
enableOverlay(),
|
||||
disableOverlay(),
|
||||
finishGame(),
|
||||
showManaPool(Void.TYPE, PlayerView.class),
|
||||
hideManaPool(Void.TYPE, PlayerView.class),
|
||||
updateStack(),
|
||||
updateZones(Void.TYPE, Iterable.class),
|
||||
updateCards(Void.TYPE, Iterable.class),
|
||||
updateManaPool(Void.TYPE, Iterable.class),
|
||||
updateLives(Void.TYPE, Iterable.class),
|
||||
setPanelSelection(Void.TYPE, CardView.class),
|
||||
getAbilityToPlay(SpellAbilityView.class, List.class, ITriggerEvent.class),
|
||||
assignDamage(Map.class, CardView.class, List.class, Integer.TYPE, GameEntityView.class, Boolean.TYPE),
|
||||
message(Void.TYPE, String.class, String.class),
|
||||
showErrorDialog(Void.TYPE, String.class, String.class),
|
||||
showConfirmDialog(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),
|
||||
showCardOptionDialog(Integer.TYPE, CardView.class, String.class, String.class, FSkinProp.class, String.class, Array.class),
|
||||
showInputDialog(String.class, String.class, String.class, FSkinProp.class, String.class, Array.class),
|
||||
confirm(Boolean.TYPE, CardView.class, String.class, Boolean.TYPE, Array.class),
|
||||
getChoices(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),
|
||||
sideboard(List.class, CardPool.class, CardPool.class),
|
||||
chooseSingleEntityForEffect(GameEntityView.class, String.class, TrackableCollection.class, DelayedReveal.class, Boolean.TYPE),
|
||||
setCard(Void.TYPE, CardView.class),
|
||||
// Server -> Client
|
||||
setGameView (Mode.SERVER, Void.TYPE, GameView.class),
|
||||
openView (Mode.SERVER, Void.TYPE, TrackableCollection/*PlayerView*/.class),
|
||||
afterGameEnd (Mode.SERVER),
|
||||
showCombat (Mode.SERVER),
|
||||
showPromptMessage (Mode.SERVER, Void.TYPE, PlayerView.class, String.class),
|
||||
updateButtons (Mode.SERVER, Void.TYPE, PlayerView.class, String.class, String.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE),
|
||||
flashIncorrectAction(Mode.SERVER),
|
||||
updatePhase (Mode.SERVER),
|
||||
updateTurn (Mode.SERVER, Void.TYPE, PlayerView.class),
|
||||
updatePlayerControl (Mode.SERVER),
|
||||
enableOverlay (Mode.SERVER),
|
||||
disableOverlay (Mode.SERVER),
|
||||
finishGame (Mode.SERVER),
|
||||
showManaPool (Mode.SERVER, Object.class, PlayerView.class),
|
||||
hideManaPool (Mode.SERVER, Void.TYPE, PlayerView.class),
|
||||
updateStack (Mode.SERVER),
|
||||
updateZones (Mode.SERVER, Void.TYPE, Iterable/*PlayerZoneUpdate*/.class),
|
||||
updateCards (Mode.SERVER, Void.TYPE, Iterable/*CardView*/.class),
|
||||
updateManaPool (Mode.SERVER, Void.TYPE, Iterable/*PlayerView*/.class),
|
||||
updateLives (Mode.SERVER, Void.TYPE, Iterable/*PlayerView*/.class),
|
||||
setPanelSelection (Mode.SERVER, Void.TYPE, CardView.class),
|
||||
getAbilityToPlay (Mode.SERVER, SpellAbilityView.class, CardView.class, List/*SpellAbilityView*/.class, ITriggerEvent.class),
|
||||
assignDamage (Mode.SERVER, Map.class, CardView.class, List/*CardView*/.class, Integer.TYPE, GameEntityView.class, Boolean.TYPE),
|
||||
message (Mode.SERVER, Void.TYPE, String.class, String.class),
|
||||
showErrorDialog (Mode.SERVER, Void.TYPE, String.class, String.class),
|
||||
showConfirmDialog (Mode.SERVER, Boolean.TYPE, String.class, String.class, String.class, String.class, Boolean.TYPE),
|
||||
showOptionDialog (Mode.SERVER, Integer.TYPE, String.class, String.class, FSkinProp.class, Array/*String*/.class, Integer.TYPE),
|
||||
showCardOptionDialog(Mode.SERVER, Integer.TYPE, CardView.class, String.class, String.class, FSkinProp.class, String.class, Array/*String*/.class),
|
||||
showInputDialog (Mode.SERVER, String.class, String.class, String.class, FSkinProp.class, String.class, Array/*String*/.class),
|
||||
confirm (Mode.SERVER, Boolean.TYPE, CardView.class, String.class, Boolean.TYPE, Array/*String*/.class),
|
||||
getChoices (Mode.SERVER, List.class, String.class, Integer.TYPE, Integer.TYPE, Collection.class, Object.class, Function.class),
|
||||
order (Mode.SERVER, List.class, String.class, String.class, Integer.TYPE, Integer.TYPE, List.class, List.class, CardView.class, Boolean.TYPE),
|
||||
sideboard (Mode.SERVER, List.class, CardPool.class, CardPool.class),
|
||||
chooseSingleEntityForEffect(Mode.SERVER, GameEntityView.class, String.class, TrackableCollection.class, DelayedReveal.class, Boolean.TYPE),
|
||||
setCard (Mode.SERVER, Void.TYPE, CardView.class),
|
||||
// TODO case "setPlayerAvatar":
|
||||
openZones(Boolean.TYPE, Collection.class, Map.class),
|
||||
restoreOldZones(Void.TYPE, Map.class),
|
||||
isUiSetToSkipPhase(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);
|
||||
openZones (Mode.SERVER, Boolean.TYPE, Collection/*ZoneType*/.class, Map/*PlayerView,Object*/.class),
|
||||
restoreOldZones (Mode.SERVER, Void.TYPE, Map/*PlayerView,Object*/.class),
|
||||
isUiSetToSkipPhase (Mode.SERVER, Boolean.TYPE, PlayerView.class, PhaseType.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<?>[] args;
|
||||
|
||||
private ProtocolMethod() {
|
||||
this(Void.TYPE);
|
||||
private ProtocolMethod(final Mode mode) {
|
||||
this(mode, Void.TYPE);
|
||||
}
|
||||
private ProtocolMethod(final Class<?> returnType) {
|
||||
this(returnType, (Class<?>[]) null);
|
||||
private ProtocolMethod(final Mode mode, final Class<?> returnType) {
|
||||
this(mode, returnType, (Class<?>[]) null);
|
||||
}
|
||||
@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.args = args == null ? new Class<?>[] {} : args;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
try {
|
||||
final String name;
|
||||
final Class<?> toCall;
|
||||
if (name().startsWith("btn_")) {
|
||||
name = name().substring("btn_".length());
|
||||
toCall = IButton.class;
|
||||
} else {
|
||||
name = name();
|
||||
toCall = IGuiGame.class;
|
||||
}
|
||||
|
||||
final Method candidate = toCall.getMethod(name, args);
|
||||
if (!candidate.getReturnType().equals(returnType)) {
|
||||
final Class<?> toCall = mode.toInvoke;
|
||||
final Method candidate = toCall.getMethod(name(), args);
|
||||
// Don't check Client return values for now as some use void
|
||||
// and a default return value, to improve performance
|
||||
if (mode == Mode.SERVER && !candidate.getReturnType().equals(returnType)) {
|
||||
throw new NoSuchMethodException(String.format("Wrong return type for method %s", name()));
|
||||
}
|
||||
return candidate;
|
||||
} catch (final NoSuchMethodException | SecurityException e) {
|
||||
System.err.println(String.format("Warning: class contains no method named %s", name()));
|
||||
return null;
|
||||
System.err.println(String.format("Warning: class contains no accessible method named %s", name()));
|
||||
return getMethodNoArgs();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean invokeOnButton() {
|
||||
return name().startsWith("btn_");
|
||||
private Method getMethodNoArgs() {
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
96
forge-gui/src/main/java/forge/net/GameProtocolHandler.java
Normal file
96
forge-gui/src/main/java/forge/net/GameProtocolHandler.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
32
forge-gui/src/main/java/forge/net/GameProtocolSender.java
Normal file
32
forge-gui/src/main/java/forge/net/GameProtocolSender.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,18 @@
|
||||
package forge.net.client;
|
||||
|
||||
import forge.FThreads;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.interfaces.IGuiGame;
|
||||
import forge.match.MatchButtonType;
|
||||
import forge.model.FModel;
|
||||
import forge.net.GameProtocol;
|
||||
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.ReplyEvent;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.trackable.TrackableCollection;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
class GameClientHandler extends ChannelInboundHandlerAdapter {
|
||||
final class GameClientHandler extends GameProtocolHandler<IGuiGame> {
|
||||
private final FGameClient client;
|
||||
private final IGuiGame gui;
|
||||
|
||||
@@ -28,93 +20,43 @@ class GameClientHandler extends ChannelInboundHandlerAdapter {
|
||||
* Creates a client-side game handler.
|
||||
*/
|
||||
public GameClientHandler(final FGameClient client) {
|
||||
super(true);
|
||||
this.client = client;
|
||||
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
|
||||
public void channelActive(final ChannelHandlerContext ctx) {
|
||||
// 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])));
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package forge.net.client;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.player.PlayerView;
|
||||
@@ -9,90 +8,77 @@ import forge.game.spellability.SpellAbilityView;
|
||||
import forge.interfaces.IDevModeCheats;
|
||||
import forge.interfaces.IGameController;
|
||||
import forge.match.NextGameDecision;
|
||||
import forge.net.event.GuiGameEvent;
|
||||
import forge.net.GameProtocol.ProtocolMethod;
|
||||
import forge.net.GameProtocolSender;
|
||||
import forge.util.ITriggerEvent;
|
||||
|
||||
public class NetGameController implements IGameController {
|
||||
|
||||
private final IToServer server;
|
||||
private final GameProtocolSender sender;
|
||||
public NetGameController(final IToServer server) {
|
||||
this.server = server;
|
||||
this.sender = new GameProtocolSender(server);
|
||||
}
|
||||
|
||||
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 void send(final ProtocolMethod method, final Object... args) {
|
||||
sender.send(method, args);
|
||||
}
|
||||
|
||||
private void send(final String method, final Object... args) {
|
||||
server.send(new GuiGameEvent(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;
|
||||
private <T> T sendAndWait(final ProtocolMethod method, final Object... args) {
|
||||
return sender.sendAndWait(method, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useMana(final byte color) {
|
||||
return sendAndWait(methodName(), color);
|
||||
public void useMana(final byte color) {
|
||||
send(ProtocolMethod.useMana, Byte.valueOf(color));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryUndoLastAction() {
|
||||
return sendAndWait(methodName());
|
||||
public void undoLastAction() {
|
||||
send(ProtocolMethod.undoLastAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectPlayer(final PlayerView playerView, final ITriggerEvent triggerEvent) {
|
||||
send(methodName(), playerView, triggerEvent);
|
||||
send(ProtocolMethod.selectPlayer, playerView, triggerEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public void selectButtonOk() {
|
||||
send(methodName());
|
||||
send(ProtocolMethod.selectButtonOk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectButtonCancel() {
|
||||
send(methodName());
|
||||
send(ProtocolMethod.selectButtonCancel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectAbility(final SpellAbilityView sa) {
|
||||
send(methodName(), sa);
|
||||
send(ProtocolMethod.selectAbility, sa);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passPriorityUntilEndOfTurn() {
|
||||
return sendAndWait(methodName());
|
||||
public void passPriorityUntilEndOfTurn() {
|
||||
send(ProtocolMethod.passPriorityUntilEndOfTurn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passPriority() {
|
||||
return sendAndWait(methodName());
|
||||
public void passPriority() {
|
||||
send(ProtocolMethod.passPriority);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextGameDecision(final NextGameDecision decision) {
|
||||
send(methodName(), decision);
|
||||
send(ProtocolMethod.nextGameDecision, decision);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,31 +88,33 @@ public class NetGameController implements IGameController {
|
||||
}
|
||||
|
||||
public String getActivateDescription(final CardView card) {
|
||||
return sendAndWait(methodName(), card);
|
||||
return sendAndWait(ProtocolMethod.getActivateDescription, card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void concede() {
|
||||
send(methodName());
|
||||
send(ProtocolMethod.concede);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDevModeCheats cheat() {
|
||||
// No cheating in network games!
|
||||
return IDevModeCheats.NO_CHEAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlayUnlimitedLands() {
|
||||
// Don't do this over network
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alphaStrike() {
|
||||
send(methodName());
|
||||
send(ProtocolMethod.alphaStrike);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reorderHand(CardView card, int index) {
|
||||
send(methodName(), card, index);
|
||||
send(ProtocolMethod.reorderHand, card, Integer.valueOf(index));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package forge.net.event;
|
||||
|
||||
import forge.net.GameProtocol.ProtocolMethod;
|
||||
import forge.net.server.RemoteClient;
|
||||
|
||||
public final class GuiGameEvent implements IdentifiableNetEvent {
|
||||
@@ -7,10 +8,10 @@ public final class GuiGameEvent implements IdentifiableNetEvent {
|
||||
private static int staticId = 0;
|
||||
|
||||
private final int id;
|
||||
private final String method;
|
||||
private final ProtocolMethod method;
|
||||
private final Object[] objects;
|
||||
|
||||
public GuiGameEvent(final String method, final Object ... objects) {
|
||||
public GuiGameEvent(final ProtocolMethod method, final Object ... objects) {
|
||||
this.id = staticId++;
|
||||
this.method = method;
|
||||
this.objects = objects == null ? new Object[0] : objects;
|
||||
@@ -30,7 +31,7 @@ public final class GuiGameEvent implements IdentifiableNetEvent {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
public ProtocolMethod getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +1,19 @@
|
||||
package forge.net.server;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.GuiBase;
|
||||
import forge.LobbyPlayer;
|
||||
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.IGuiGame;
|
||||
import forge.interfaces.ILobbyListener;
|
||||
import forge.match.LobbySlot;
|
||||
import forge.match.LobbySlotType;
|
||||
import forge.match.NextGameDecision;
|
||||
import forge.net.event.GuiGameEvent;
|
||||
import forge.net.event.LobbyUpdateEvent;
|
||||
import forge.net.event.LoginEvent;
|
||||
import forge.net.event.LogoutEvent;
|
||||
import forge.net.event.MessageEvent;
|
||||
import forge.net.event.NetEvent;
|
||||
import forge.net.event.ReplyEvent;
|
||||
import forge.net.event.UpdateLobbyPlayerEvent;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.ITriggerEvent;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
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.LoggingHandler;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.log4j.PropertyConfigurator;
|
||||
@@ -79,6 +68,16 @@ public final class 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}.
|
||||
*
|
||||
@@ -126,7 +125,7 @@ public final class FServerManager {
|
||||
|
||||
}
|
||||
}).start();
|
||||
//mapNatPort(port);
|
||||
mapNatPort(port);
|
||||
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
||||
isHosting = true;
|
||||
} 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 {
|
||||
@Override
|
||||
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,12 @@
|
||||
package forge.net.server;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.LobbyPlayer;
|
||||
import forge.UiCommand;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.deck.CardPool;
|
||||
import forge.game.GameEntityView;
|
||||
@@ -22,54 +18,31 @@ import forge.game.player.IHasIcon;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.game.spellability.SpellAbilityView;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.interfaces.IButton;
|
||||
import forge.item.PaperCard;
|
||||
import forge.match.AbstractGuiGame;
|
||||
import forge.match.MatchButtonType;
|
||||
import forge.net.event.GuiGameEvent;
|
||||
import forge.net.GameProtocol.ProtocolMethod;
|
||||
import forge.net.GameProtocolSender;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.trackable.TrackableCollection;
|
||||
import forge.util.ITriggerEvent;
|
||||
|
||||
public class NetGuiGame extends AbstractGuiGame {
|
||||
|
||||
private final IToClient client;
|
||||
private final Map<MatchButtonType, Map<PlayerView, NetButton>> btns = new EnumMap<MatchButtonType, Map<PlayerView,NetButton>>(MatchButtonType.class);
|
||||
private final GameProtocolSender sender;
|
||||
public NetGuiGame(final IToClient client) {
|
||||
this.client = client;
|
||||
for (final MatchButtonType type : MatchButtonType.values()) {
|
||||
btns.put(type, Maps.<PlayerView, NetButton>newHashMap());
|
||||
}
|
||||
this.sender = new GameProtocolSender(client);
|
||||
}
|
||||
|
||||
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 void send(final ProtocolMethod method, final Object... args) {
|
||||
sender.send(method, args);
|
||||
}
|
||||
|
||||
private void send(final String method, final Object... args) {
|
||||
client.send(new GuiGameEvent(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 <T> T sendAndWait(final ProtocolMethod method, final Object... args) {
|
||||
return sender.sendAndWait(method, args);
|
||||
}
|
||||
|
||||
private void updateGameView() {
|
||||
send("setGameView", getGameView());
|
||||
send(ProtocolMethod.setGameView, getGameView());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,227 +53,206 @@ public class NetGuiGame extends AbstractGuiGame {
|
||||
|
||||
@Override
|
||||
public void openView(final TrackableCollection<PlayerView> myPlayers) {
|
||||
for (final MatchButtonType type : MatchButtonType.values()) {
|
||||
btns.get(type).clear();
|
||||
for (final PlayerView player : myPlayers) {
|
||||
btns.get(type).put(player, new NetButton(player, type));
|
||||
}
|
||||
}
|
||||
send(methodName(), myPlayers);
|
||||
send(ProtocolMethod.openView, myPlayers);
|
||||
updateGameView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterGameEnd() {
|
||||
send(methodName());
|
||||
send(ProtocolMethod.afterGameEnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showCombat() {
|
||||
send(methodName());
|
||||
send(ProtocolMethod.showCombat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showPromptMessage(final PlayerView playerView, final String message) {
|
||||
updateGameView();
|
||||
send(methodName(), playerView, message);
|
||||
send(ProtocolMethod.showPromptMessage, playerView, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stopAtPhase(final PlayerView playerTurn, final PhaseType phase) {
|
||||
return sendAndWait(methodName(), playerTurn, phase);
|
||||
}
|
||||
|
||||
@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);
|
||||
public void updateButtons(final PlayerView owner, final String label1, final String label2, final boolean enable1, final boolean enable2, final boolean focus1) {
|
||||
send(ProtocolMethod.updateButtons, owner, label1, label2, enable1, enable2, focus1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flashIncorrectAction() {
|
||||
send(methodName());
|
||||
send(ProtocolMethod.flashIncorrectAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePhase() {
|
||||
updateGameView();
|
||||
send(methodName());
|
||||
send(ProtocolMethod.updatePhase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTurn(final PlayerView player) {
|
||||
updateGameView();
|
||||
send(methodName(), player);
|
||||
send(ProtocolMethod.updateTurn, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePlayerControl() {
|
||||
updateGameView();
|
||||
send(methodName());
|
||||
send(ProtocolMethod.updatePlayerControl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableOverlay() {
|
||||
send(methodName());
|
||||
send(ProtocolMethod.enableOverlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableOverlay() {
|
||||
send(methodName());
|
||||
send(ProtocolMethod.disableOverlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishGame() {
|
||||
send(methodName());
|
||||
send(ProtocolMethod.finishGame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object showManaPool(final PlayerView player) {
|
||||
send(methodName(), player);
|
||||
send(ProtocolMethod.showManaPool, player);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideManaPool(final PlayerView player, final Object zoneToRestore) {
|
||||
send(methodName(), player, zoneToRestore);
|
||||
send(ProtocolMethod.hideManaPool, player, zoneToRestore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStack() {
|
||||
updateGameView();
|
||||
send(methodName());
|
||||
send(ProtocolMethod.updateStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateZones(final Iterable<PlayerZoneUpdate> zonesToUpdate) {
|
||||
updateGameView();
|
||||
send(methodName(), zonesToUpdate);
|
||||
send(ProtocolMethod.updateZones, zonesToUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCards(final Iterable<CardView> cards) {
|
||||
updateGameView();
|
||||
send(methodName(), cards);
|
||||
send(ProtocolMethod.updateCards, cards);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateManaPool(final Iterable<PlayerView> manaPoolUpdate) {
|
||||
updateGameView();
|
||||
send(methodName(), manaPoolUpdate);
|
||||
send(ProtocolMethod.updateManaPool, manaPoolUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLives(final Iterable<PlayerView> livesUpdate) {
|
||||
updateGameView();
|
||||
send(methodName(), livesUpdate);
|
||||
send(ProtocolMethod.updateLives, livesUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPanelSelection(final CardView hostCard) {
|
||||
updateGameView();
|
||||
send(methodName(), hostCard);
|
||||
send(ProtocolMethod.setPanelSelection, hostCard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellAbilityView getAbilityToPlay(final List<SpellAbilityView> abilities, final ITriggerEvent triggerEvent) {
|
||||
return sendAndWait(methodName(), abilities, triggerEvent);
|
||||
public SpellAbilityView getAbilityToPlay(final CardView hostCard, final List<SpellAbilityView> abilities, final ITriggerEvent triggerEvent) {
|
||||
return sendAndWait(ProtocolMethod.getAbilityToPlay, hostCard, abilities, triggerEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public void message(final String message, final String title) {
|
||||
send(methodName(), message, title);
|
||||
send(ProtocolMethod.message, message, title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showErrorDialog(final String message, final String title) {
|
||||
send(methodName(), message, title);
|
||||
send(ProtocolMethod.showErrorDialog, message, title);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
public List<PaperCard> sideboard(final CardPool sideboard, final CardPool main) {
|
||||
return sendAndWait(methodName(), sideboard, main);
|
||||
return sendAndWait(ProtocolMethod.sideboard, sideboard, main);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public void setCard(final CardView card) {
|
||||
updateGameView();
|
||||
send(methodName(), card);
|
||||
send(ProtocolMethod.setCard, card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi) {
|
||||
public void setPlayerAvatar(final LobbyPlayer player, final IHasIcon ihi) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean openZones(final Collection<ZoneType> zones, final Map<PlayerView, Object> players) {
|
||||
updateGameView();
|
||||
return sendAndWait(methodName(), zones, players);
|
||||
return sendAndWait(ProtocolMethod.openZones, zones, players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreOldZones(final Map<PlayerView, Object> playersToRestoreZonesFor) {
|
||||
send(methodName(), playersToRestoreZonesFor);
|
||||
send(ProtocolMethod.restoreOldZones, playersToRestoreZonesFor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUiSetToSkipPhase(final PlayerView playerTurn, final PhaseType phase) {
|
||||
return sendAndWait(methodName(), playerTurn, phase);
|
||||
return sendAndWait(ProtocolMethod.isUiSetToSkipPhase, playerTurn, phase);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -308,71 +260,4 @@ public class NetGuiGame extends AbstractGuiGame {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public final class RemoteClient implements IToClient {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public void setReply(final int id, final Object reply) {
|
||||
replies.complete(id, reply);
|
||||
ReplyPool getReplyPool() {
|
||||
return replies;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ public class HumanPlay {
|
||||
return 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) {
|
||||
|
||||
@@ -219,8 +219,8 @@ public class PlayerControllerHuman
|
||||
/**
|
||||
* 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) {
|
||||
final SpellAbilityView resultView = getGui().getAbilityToPlay(SpellAbilityView.getCollection(abilities), triggerEvent);
|
||||
public SpellAbility getAbilityToPlay(final Card hostCard, final List<SpellAbility> abilities, final ITriggerEvent triggerEvent) {
|
||||
final SpellAbilityView resultView = getGui().getAbilityToPlay(CardView.get(hostCard), SpellAbilityView.getCollection(abilities), triggerEvent);
|
||||
return getGame().getSpellAbility(resultView);
|
||||
}
|
||||
|
||||
@@ -1304,6 +1304,12 @@ public class PlayerControllerHuman
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void undoLastAction() {
|
||||
tryUndoLastAction();
|
||||
}
|
||||
|
||||
public boolean tryUndoLastAction() {
|
||||
if (!canUndoLastAction()) {
|
||||
return false;
|
||||
@@ -1333,37 +1339,33 @@ public class PlayerControllerHuman
|
||||
}
|
||||
}
|
||||
|
||||
public boolean passPriority() {
|
||||
return passPriority(false);
|
||||
public void passPriority() {
|
||||
passPriority(false);
|
||||
}
|
||||
public boolean passPriorityUntilEndOfTurn() {
|
||||
return passPriority(true);
|
||||
public void passPriorityUntilEndOfTurn() {
|
||||
passPriority(true);
|
||||
}
|
||||
private boolean passPriority(final boolean passUntilEndOfTurn) {
|
||||
private void passPriority(final boolean passUntilEndOfTurn) {
|
||||
final Input inp = inputProxy.getInput();
|
||||
if (inp instanceof InputPassPriority) {
|
||||
if (passUntilEndOfTurn) {
|
||||
autoPassUntilEndOfTurn();
|
||||
}
|
||||
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();
|
||||
if (input instanceof InputPayMana) {
|
||||
return ((InputPayMana) input).useManaFromPool(mana);
|
||||
((InputPayMana) input).useManaFromPool(mana);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void selectPlayer(final PlayerView playerView, final ITriggerEvent triggerEvent) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.player;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -11,11 +12,21 @@ import forge.game.player.PlayerView;
|
||||
public class PlayerZoneUpdates implements Iterable<PlayerZoneUpdate>, Serializable {
|
||||
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() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*
|
||||
* @param other
|
||||
* the {@link PlayerZoneUpdates} to copy.
|
||||
*/
|
||||
public PlayerZoneUpdates(final PlayerZoneUpdates other) {
|
||||
this.updates.putAll(other.updates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<PlayerZoneUpdate> iterator() {
|
||||
return updates.values().iterator();
|
||||
|
||||
Reference in New Issue
Block a user