Refactor lobby and add network support.

- Lobby code has been revised completely to support network lobbies;
  code has been separated into model and view parts.
- Added preliminary network game support; most of it should be working
  but hasn't been tested thoroughly.
- Fixed issue where controlling another player wouldn't be recognised
  by the GUI.
- Fixed issue where hand panels wouldn't display anymore.
- Minor fixes/cleanup.
This commit is contained in:
elcnesh
2015-03-14 10:53:09 +00:00
parent a73b67dc04
commit b98684877d
105 changed files with 3089 additions and 1888 deletions

View File

@@ -242,6 +242,9 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
if (ev.player.getGame().isGameOver()) {
return null;
}
if (ev.newController instanceof PlayerControllerHuman) {
matchController.setGameController(PlayerView.get(ev.player), (PlayerControllerHuman) ev.newController);
}
needPlayerControlUpdate = true;
return processEvent();
}

View File

@@ -9,7 +9,7 @@ import forge.LobbyPlayer;
import forge.game.Game;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityView;
import forge.interfaces.IDevModeCheats;
import forge.interfaces.IGuiGame;
import forge.match.input.Input;
@@ -90,7 +90,7 @@ public class WatchLocalGame extends PlayerControllerHuman {
}
@Override
public void selectAbility(final SpellAbility sa) {
public void selectAbility(final SpellAbilityView sa) {
}
@Override

View File

@@ -4,11 +4,8 @@ import forge.UiCommand;
import forge.assets.FSkinProp;
public interface IButton {
boolean isEnabled();
void setEnabled(boolean b0);
boolean isVisible();
void setVisible(boolean b0);
String getText();
void setText(String text0);
boolean isSelected();
void setSelected(boolean b0);

View File

@@ -4,7 +4,7 @@ import java.util.List;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityView;
import forge.match.NextGameDecision;
import forge.util.ITriggerEvent;
@@ -33,7 +33,7 @@ public interface IGameController {
boolean selectCard(CardView cardView,
List<CardView> otherCardViewsToSelect, ITriggerEvent triggerEvent);
void selectAbility(SpellAbility sa);
void selectAbility(SpellAbilityView sa);
boolean tryUndoLastAction();

View File

@@ -51,5 +51,4 @@ public interface IGuiBase {
void showBazaar();
IGuiGame getNewGuiGame();
HostedMatch hostMatch();
void netMessage(String origin, String message);
}

View File

@@ -11,7 +11,6 @@ import com.google.common.base.Function;
import forge.LobbyPlayer;
import forge.assets.FSkinProp;
import forge.deck.CardPool;
import forge.game.GameEntity;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.CardView;
@@ -19,25 +18,24 @@ import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
import forge.game.player.IHasIcon;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityView;
import forge.game.zone.ZoneType;
import forge.item.PaperCard;
import forge.player.PlayerControllerHuman;
import forge.util.FCollectionView;
import forge.match.MatchButtonType;
import forge.trackable.TrackableCollection;
import forge.util.ITriggerEvent;
public interface IGuiGame {
void setGameView(GameView gameView);
void setGameController(PlayerView player, IGameController gameController);
boolean resetForNewGame();
void openView(Iterable<PlayerView> myPlayers);
void openView(TrackableCollection<PlayerView> myPlayers);
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(IButton button);
void focusButton(MatchButtonType button);
void flashIncorrectAction();
void updatePhase();
void updateTurn(PlayerView player);
@@ -54,8 +52,7 @@ public interface IGuiGame {
void updateManaPool(Iterable<PlayerView> manaPoolUpdate);
void updateLives(Iterable<PlayerView> livesUpdate);
void setPanelSelection(CardView hostCard);
void hear(LobbyPlayer player, String message);
SpellAbility getAbilityToPlay(List<SpellAbility> abilities,
SpellAbilityView getAbilityToPlay(List<SpellAbilityView> abilities,
ITriggerEvent triggerEvent);
Map<CardView, Integer> assignDamage(CardView attacker,
List<CardView> blockers, int damage, GameEntityView defender,
@@ -161,9 +158,8 @@ public interface IGuiGame {
List<PaperCard> sideboard(CardPool sideboard, CardPool main);
GameEntityView chooseSingleEntityForEffect(String title,
FCollectionView<? extends GameEntity> optionList,
DelayedReveal delayedReveal, boolean isOptional,
PlayerControllerHuman controller);
Collection<? extends GameEntityView> optionList,
DelayedReveal delayedReveal, boolean isOptional);
void setCard(CardView card);
void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi);
boolean openZones(Collection<ZoneType> zones, Map<PlayerView, Object> players);

View File

@@ -1,10 +1,10 @@
package forge.interfaces;
import forge.net.game.LobbyState;
import forge.match.GameLobby;
import forge.net.game.server.RemoteClient;
public interface ILobby {
LobbyState getState();
GameLobby getState();
int login(RemoteClient client);
void logout(RemoteClient client);
}

View File

@@ -0,0 +1,8 @@
package forge.interfaces;
import forge.match.GameLobby.GameLobbyData;
public interface ILobbyListener {
void message(String source, String message);
void update(GameLobbyData state, int slot);
}

View File

@@ -1,7 +1,7 @@
package forge.interfaces;
import forge.net.game.LobbyState.LobbyPlayerData;
import forge.net.game.UpdateLobbyPlayerEvent;
public interface IPlayerChangeListener {
void update(LobbyPlayerData data);
void update(int index, UpdateLobbyPlayerEvent event);
}

View File

@@ -0,0 +1,5 @@
package forge.interfaces;
public interface IUpdateable {
void update();
}

View File

@@ -210,9 +210,9 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
btnOk.setEnabled(okEnabled);
btnCancel.setEnabled(cancelEnabled);
if (okEnabled && focusOk) {
focusButton(btnOk);
focusButton(MatchButtonType.OK);
} else if (cancelEnabled) {
focusButton(btnCancel);
focusButton(MatchButtonType.CANCEL);
}
}

View File

@@ -0,0 +1,442 @@
package forge.match;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.primitives.Ints;
import forge.AIOption;
import forge.GuiBase;
import forge.LobbyPlayer;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckFormat;
import forge.deck.DeckSection;
import forge.game.GameType;
import forge.game.GameView;
import forge.game.player.Player;
import forge.game.player.RegisteredPlayer;
import forge.interfaces.IGameController;
import forge.interfaces.IGuiGame;
import forge.interfaces.IUpdateable;
import forge.item.PaperCard;
import forge.model.FModel;
import forge.net.game.LobbySlotType;
import forge.net.game.UpdateLobbyPlayerEvent;
import forge.player.GamePlayerUtil;
import forge.properties.ForgePreferences.FPref;
import forge.util.NameGenerator;
import forge.util.gui.SOptionPane;
public abstract class GameLobby {
private final static int MAX_PLAYERS = 8;
private GameLobbyData data = new GameLobbyData();
private int lastArchenemy = 0;
private IUpdateable listener;
private final boolean allowNetworking;
private HostedMatch hostedMatch;
private final Map<LobbySlot, IGameController> gameControllers = Maps.newHashMap();
protected GameLobby(final boolean allowNetworking) {
this.allowNetworking = allowNetworking;
}
public final boolean isAllowNetworking() {
return allowNetworking;
}
public void setListener(final IUpdateable listener) {
this.listener = listener;
}
public GameLobbyData getData() {
return data;
}
public void setData(final GameLobbyData data) {
this.data = data;
updateView();
}
public GameType getGameType() {
return data.currentGameMode;
}
public void setGameType(final GameType type) {
data.currentGameMode = type;
updateView();
}
public boolean hasVariant(final GameType variant) {
return data.appliedVariants.contains(variant);
}
public int getNumberOfSlots() {
return data.slots.size();
}
public LobbySlot getSlot(final int index) {
if (index < 0 || index >= getNumberOfSlots()) {
return null;
}
return data.slots.get(index);
}
public void applyToSlot(final int index, final UpdateLobbyPlayerEvent event) {
final LobbySlot slot = getSlot(index);
if (slot == null) {
throw new NullPointerException();
}
final int nSlots = getNumberOfSlots();
final boolean triesToChangeArchenemy = event.getArchenemy() != null;
final boolean archenemyRemoved = triesToChangeArchenemy && !event.getArchenemy().booleanValue();
final boolean hasArchenemyChanged = triesToChangeArchenemy && slot.isArchenemy() != event.getArchenemy().booleanValue();
slot.apply(event);
// Change archenemy teams
if (hasVariant(GameType.Archenemy) && hasArchenemyChanged) {
final int newArchenemy = archenemyRemoved ? lastArchenemy : index;
if (archenemyRemoved) {
lastArchenemy = index;
}
for (int otherIndex = 0; otherIndex < nSlots; otherIndex++) {
final LobbySlot otherSlot = getSlot(otherIndex);
final boolean becomesArchenemy = otherIndex == newArchenemy;
if (!archenemyRemoved && otherSlot.isArchenemy() && !becomesArchenemy) {
lastArchenemy = otherIndex;
}
otherSlot.setIsArchenemy(becomesArchenemy);
}
updateView();
}
}
public IGameController getController(final int index) {
return gameControllers.get(getSlot(index));
}
public GameView getGameView() {
return hostedMatch.getGameView();
}
public abstract boolean hasControl();
public abstract boolean mayEdit(final int index);
public abstract boolean mayControl(final int index);
public abstract boolean mayRemove(final int index);
public abstract IGuiGame getGui(final int index);
public void addSlot() {
final int newIndex = getNumberOfSlots();
addSlot(new LobbySlot(allowNetworking ? LobbySlotType.OPEN : LobbySlotType.LOCAL, null, newIndex, newIndex, false, Collections.<AIOption>emptySet()));
}
protected final void addSlot(final LobbySlot slot) {
if (data.slots.size() >= MAX_PLAYERS) {
return;
}
data.slots.add(slot);
if (StringUtils.isEmpty(slot.getName())) {
slot.setName(randomName());
}
if (data.slots.size() == 1) {
// If first slot, make archenemy
slot.setIsArchenemy(true);
lastArchenemy = 0;
}
updateView();
}
private String randomName() {
final List<String> names = Lists.newArrayListWithCapacity(MAX_PLAYERS);
for (final LobbySlot slot : data.slots) {
names.add(slot.getName());
}
return NameGenerator.getRandomName("Any", "Any", names);
}
protected final String localName() {
return FModel.getPreferences().getPref(FPref.PLAYER_NAME);
}
protected final int[] localAvatarIndices() {
final String[] sAvatars = FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",");
final int[] result = new int[sAvatars.length];
for (int i = 0; i < sAvatars.length; i++) {
final Integer val = Ints.tryParse(sAvatars[i]);
result[i] = val == null ? -1 : val.intValue();
}
return result;
}
public void removeSlot(final int index) {
if (index < 0 || index >= data.slots.size()) {
return;
}
if (getSlot(index).isArchenemy()) {
getSlot(lastArchenemy).setIsArchenemy(true);
// Should actually be a stack here, but that's rather involved for
// such a nonimportant feature
lastArchenemy = 0;
} else if (lastArchenemy == index) {
lastArchenemy = 0;
} else {
lastArchenemy--;
}
data.slots.remove(index);
updateView();
}
public void applyVariant(final GameType variant) {
data.currentGameMode = variant;
data.appliedVariants.add(variant);
//ensure other necessary variants are unchecked
switch (variant) {
case Archenemy:
data.appliedVariants.remove(GameType.ArchenemyRumble);
break;
case ArchenemyRumble:
data.appliedVariants.remove(GameType.Archenemy);
break;
case Commander:
data.appliedVariants.remove(GameType.TinyLeaders);
data.appliedVariants.remove(GameType.MomirBasic);
break;
case TinyLeaders:
data.appliedVariants.remove(GameType.Commander);
data.appliedVariants.remove(GameType.MomirBasic);
break;
case Vanguard:
data.appliedVariants.remove(GameType.MomirBasic);
break;
case MomirBasic:
data.appliedVariants.remove(GameType.Commander);
data.appliedVariants.remove(GameType.Vanguard);
break;
default:
break;
}
updateView();
}
public void removeVariant(final GameType variant) {
data.appliedVariants.remove(variant);
if (data.appliedVariants.isEmpty()) {
data.appliedVariants.add(GameType.Constructed);
}
updateView();
}
private boolean isEnoughTeams() {
int lastTeam = -1;
final boolean useArchenemyTeams = data.appliedVariants.contains(GameType.Archenemy);
for (final LobbySlot slot : data.slots) {
final int team = useArchenemyTeams ? (slot.isArchenemy() ? 0 : 1) : slot.getTeam();
if (lastTeam == -1) {
lastTeam = team;
} else if (lastTeam != team) {
return true;
}
}
return false;
}
protected final void updateView() {
if (listener != null) {
listener.update();
}
}
/** Starts a match with the applied variants. */
public void startGame() {
if (!isEnoughTeams()) {
SOptionPane.showMessageDialog("There are not enough teams! Please adjust team allocations.");
return;
}
final List<LobbySlot> activeSlots = Lists.newArrayListWithCapacity(getNumberOfSlots());
for (final LobbySlot slot : data.getSlots()) {
if (slot.getType() != LobbySlotType.OPEN) {
activeSlots.add(slot);
}
}
for (final LobbySlot slot : activeSlots) {
if (slot.getDeck() == null) {
SOptionPane.showMessageDialog(String.format("Please specify a deck for %s", slot.getName()));
return;
}
}
final Set<GameType> variantTypes = data.appliedVariants;
GameType autoGenerateVariant = null;
boolean isCommanderMatch = false;
boolean isTinyLeadersMatch = false;
if (!variantTypes.isEmpty()) {
isTinyLeadersMatch = variantTypes.contains(GameType.TinyLeaders);
isCommanderMatch = isTinyLeadersMatch || variantTypes.contains(GameType.Commander);
if (!isCommanderMatch) {
for (final GameType variant : variantTypes) {
if (variant.isAutoGenerated()) {
autoGenerateVariant = variant;
break;
}
}
}
}
boolean checkLegality = FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY);
//Auto-generated decks don't need to be checked here
//Commander deck replaces regular deck and is checked later
if (checkLegality && autoGenerateVariant == null && !isCommanderMatch) {
for (final LobbySlot slot : activeSlots) {
final String name = slot.getName();
String errMsg = GameType.Constructed.getDeckFormat().getDeckConformanceProblem(slot.getDeck());
if (null != errMsg) {
SOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Deck");
return;
}
}
}
final List<RegisteredPlayer> players = new ArrayList<RegisteredPlayer>();
final Map<RegisteredPlayer, IGuiGame> guis = Maps.newHashMap();
final Map<RegisteredPlayer, LobbySlot> playerToSlot = Maps.newHashMap();
boolean hasNameBeenSet = false;
for (final LobbySlot slot : activeSlots) {
final IGuiGame gui = getGui(data.slots.indexOf(slot));
final String name = slot.getName();
final int avatar = slot.getAvatarIndex();
final int team = slot.getTeam();
final boolean isArchenemy = slot.isArchenemy();
final Set<AIOption> aiOptions = slot.getAiOptions();
final boolean isAI = slot.getType() == LobbySlotType.AI;
final LobbyPlayer lobbyPlayer;
if (isAI) {
lobbyPlayer = GamePlayerUtil.createAiPlayer(name, avatar, aiOptions);
} else {
boolean setNameNow = false;
if (!hasNameBeenSet && slot.getType() == LobbySlotType.LOCAL) {
setNameNow = true;
hasNameBeenSet = true;
}
lobbyPlayer = GamePlayerUtil.getGuiPlayer(name, setNameNow);
}
Deck deck = slot.getDeck();
RegisteredPlayer rp = new RegisteredPlayer(deck);
if (variantTypes.isEmpty()) {
rp.setTeamNumber(team);
players.add(rp.setPlayer(lobbyPlayer));
} else {
PaperCard vanguardAvatar = null;
if (isCommanderMatch) {
final GameType commanderGameType = isTinyLeadersMatch ? GameType.TinyLeaders : GameType.Commander;
if (checkLegality) {
String errMsg = commanderGameType.getDeckFormat().getDeckConformanceProblem(deck);
if (null != errMsg) {
SOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid " + commanderGameType + " Deck");
return;
}
}
} else if (autoGenerateVariant != null) {
deck = autoGenerateVariant.autoGenerateDeck(rp);
final CardPool avatarPool = deck.get(DeckSection.Avatar);
if (avatarPool != null) {
vanguardAvatar = avatarPool.get(0);
}
}
// Initialise variables for other variants
deck = deck == null ? rp.getDeck() : deck;
Iterable<PaperCard> schemes = null;
Iterable<PaperCard> planes = null;
//Archenemy
if (variantTypes.contains(GameType.ArchenemyRumble)
|| (variantTypes.contains(GameType.Archenemy) && isArchenemy)) {
final CardPool schemePool = deck.get(DeckSection.Schemes);
if (checkLegality) {
String errMsg = DeckFormat.getSchemeSectionConformanceProblem(schemePool);
if (null != errMsg) {
SOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Scheme Deck");
return;
}
}
schemes = schemePool.toFlatList();
}
//Planechase
if (variantTypes.contains(GameType.Planechase)) {
final CardPool planePool = deck.get(DeckSection.Planes);
if (checkLegality) {
String errMsg = DeckFormat.getPlaneSectionConformanceProblem(planePool);
if (null != errMsg) {
SOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Planar Deck");
return;
}
}
planes = planePool.toFlatList();
}
//Vanguard
if (variantTypes.contains(GameType.Vanguard)) {
if (vanguardAvatar == null) { //ERROR! null if avatar deselected on list
SOptionPane.showMessageDialog("No Vanguard avatar selected for " + name
+ ". Please choose one or disable the Vanguard variant");
return;
}
}
rp = RegisteredPlayer.forVariants(variantTypes, deck, schemes, isArchenemy, planes, vanguardAvatar);
rp.setTeamNumber(team);
players.add(rp.setPlayer(lobbyPlayer));
}
if (!isAI) {
guis.put(rp, gui);
}
playerToSlot.put(rp, slot);
}
hostedMatch = GuiBase.getInterface().hostMatch();
hostedMatch.startMatch(GameType.Constructed, variantTypes, players, guis);
for (final Player p : hostedMatch.getGame().getPlayers()) {
final LobbySlot slot = playerToSlot.get(p.getRegisteredPlayer());
if (p.getController() instanceof IGameController) {
gameControllers.put(slot, (IGameController) p.getController());
}
}
}
public final static class GameLobbyData implements Serializable {
private static final long serialVersionUID = -9038854736144187540L;
private transient GameType currentGameMode = GameType.Constructed;
private final Set<GameType> appliedVariants = EnumSet.of(GameType.Constructed);
private final List<LobbySlot> slots = Lists.newArrayList();
public GameType getCurrentGameMode() {
return currentGameMode;
}
public void setCurrentGameMode(GameType currentGameMode) {
this.currentGameMode = currentGameMode;
}
public Set<GameType> getAppliedVariants() {
return Collections.unmodifiableSet(appliedVariants);
}
public List<LobbySlot> getSlots() {
return Collections.unmodifiableList(slots);
}
}
}

View File

@@ -44,8 +44,8 @@ import forge.properties.ForgePreferences.FPref;
import forge.quest.QuestController;
import forge.sound.MusicPlaylist;
import forge.sound.SoundSystem;
import forge.trackable.TrackableCollection;
import forge.util.CollectionSuppliers;
import forge.util.FCollection;
import forge.util.GuiDisplayUtil;
import forge.util.NameGenerator;
import forge.util.maps.HashMapOfLists;
@@ -137,8 +137,6 @@ public class HostedMatch {
game.subscribeToEvents(SoundSystem.instance);
game.subscribeToEvents(visitor);
final String[] indices = FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",");
// Instantiate all required field slots (user at 0)
final List<Player> sortedPlayers = Lists.newArrayList(game.getRegisteredPlayers());
Collections.sort(sortedPlayers, new Comparator<Player>() {
@@ -150,18 +148,23 @@ public class HostedMatch {
}
});
final String[] avatarIndices = FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",");
final GameView gameView = getGameView();
int i = 0;
int avatarIndex = 0;
humanCount = 0;
final MapOfLists<IGuiGame, PlayerView> playersPerGui = new HashMapOfLists<IGuiGame, PlayerView>(CollectionSuppliers.<PlayerView>arrayLists());
for (final Player p : sortedPlayers) {
if (i < indices.length) {
avatarIndex = Integer.parseInt(indices[i]);
i++;
for (int iPlayer = 0; iPlayer < sortedPlayers.size(); iPlayer++) {
final RegisteredPlayer rp = match.getPlayers().get(iPlayer);
final Player p = sortedPlayers.get(iPlayer);
p.getLobbyPlayer().setAvatarIndex(rp.getPlayer().getAvatarIndex());
if (p.getLobbyPlayer().getAvatarIndex() == -1) {
if (iPlayer < avatarIndices.length) {
p.getLobbyPlayer().setAvatarIndex(Integer.parseInt(avatarIndices[iPlayer]));
} else {
p.getLobbyPlayer().setAvatarIndex(0);
}
}
p.getLobbyPlayer().setAvatarIndex(avatarIndex);
p.updateAvatar();
if (p.getController() instanceof PlayerControllerHuman) {
@@ -177,8 +180,9 @@ public class HostedMatch {
humanCount++;
}
}
for (final Entry<IGuiGame, Collection<PlayerView>> e : playersPerGui.entrySet()) {
e.getKey().openView(new FCollection<PlayerView>(e.getValue()));
e.getKey().openView(new TrackableCollection<PlayerView>(e.getValue()));
}
if (humanCount == 0) { //watch game but do not participate

View File

@@ -0,0 +1,109 @@
package forge.match;
import java.io.Serializable;
import java.util.Set;
import com.google.common.collect.ImmutableSet;
import forge.AIOption;
import forge.deck.Deck;
import forge.net.game.LobbySlotType;
import forge.net.game.UpdateLobbyPlayerEvent;
public final class LobbySlot implements Serializable {
private static final long serialVersionUID = 6918205436608794289L;
private LobbySlotType type;
private String name;
private int avatarIndex;
private int team;
private boolean isArchenemy;
private Deck deck;
private ImmutableSet<AIOption> aiOptions;
public LobbySlot(final LobbySlotType type, final String name, final int avatarIndex, final int team, final boolean isArchenemy, final Set<AIOption> aiOptions) {
this.type = type;
this.name = name;
this.avatarIndex = avatarIndex;
this.team = team;
this.isArchenemy = isArchenemy;
this.setAiOptions(aiOptions);
}
void apply(final UpdateLobbyPlayerEvent data) {
if (data.getType() != null) {
setType(data.getType());
}
if (data.getName() != null) {
setName(data.getName());
}
if (data.getAvatarIndex() != -1) {
setAvatarIndex(data.getAvatarIndex());
}
if (data.getTeam() != -1) {
setTeam(data.getTeam());
}
if (data.getArchenemy() != null) {
setIsArchenemy(data.getArchenemy().booleanValue());
}
if (data.getAiOptions() != null) {
setAiOptions(data.getAiOptions());
}
if (data.getDeck() != null) {
setDeck(data.getDeck());
} else if (getDeck() != null && data.getSection() != null && data.getCards() != null) {
getDeck().putSection(data.getSection(), data.getCards());
}
}
public LobbySlotType getType() {
return type;
}
public void setType(final LobbySlotType type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public int getAvatarIndex() {
return avatarIndex;
}
public void setAvatarIndex(final int avatarIndex) {
this.avatarIndex = avatarIndex;
}
public int getTeam() {
return team;
}
public void setTeam(final int team) {
this.team = team;
}
public boolean isArchenemy() {
return isArchenemy;
}
public void setIsArchenemy(final boolean isArchenemy) {
this.isArchenemy = isArchenemy;
}
public Deck getDeck() {
return deck;
}
public void setDeck(final Deck deck) {
this.deck = deck;
}
public ImmutableSet<AIOption> getAiOptions() {
return aiOptions;
}
public void setAiOptions(final Set<AIOption> aiOptions) {
this.aiOptions = aiOptions == null ? ImmutableSet.<AIOption>of() : ImmutableSet.copyOf(aiOptions);
}
}

View File

@@ -0,0 +1,44 @@
package forge.match;
import java.util.Collections;
import forge.AIOption;
import forge.GuiBase;
import forge.interfaces.IGuiGame;
import forge.net.game.LobbySlotType;
public final class LocalLobby extends GameLobby {
public LocalLobby() {
super(false);
final String humanName = localName();
final int[] avatarIndices = localAvatarIndices();
final LobbySlot slot0 = new LobbySlot(LobbySlotType.LOCAL, humanName, avatarIndices[0], 0, true, Collections.<AIOption>emptySet());
addSlot(slot0);
final LobbySlot slot1 = new LobbySlot(LobbySlotType.AI, null, avatarIndices[1], 1, false, Collections.<AIOption>emptySet());
addSlot(slot1);
}
@Override public boolean hasControl() {
return true;
}
@Override public boolean mayEdit(final int index) {
return true;
}
@Override public boolean mayControl(final int index) {
return true;
}
@Override public boolean mayRemove(final int index) {
return index >= 2;
}
@Override public IGuiGame getGui(final int index) {
return GuiBase.getInterface().getNewGuiGame();
}
}

View File

@@ -0,0 +1,6 @@
package forge.match;
public enum MatchButtonType {
OK,
CANCEL;
}

View File

@@ -1,415 +0,0 @@
package forge.match;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function;
import forge.LobbyPlayer;
import forge.UiCommand;
import forge.assets.FSkinProp;
import forge.deck.CardPool;
import forge.game.GameEntity;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.CardView;
import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
import forge.game.player.IHasIcon;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.interfaces.IButton;
import forge.item.PaperCard;
import forge.net.game.GuiGameEvent;
import forge.net.game.server.IToClient;
import forge.player.PlayerControllerHuman;
import forge.trackable.TrackableObject;
import forge.util.FCollectionView;
import forge.util.ITriggerEvent;
public class NetGuiGame extends AbstractGuiGame {
private final IToClient client;
public NetGuiGame(final IToClient client) {
this.client = client;
}
private void send(final String method) {
send(method, Collections.<TrackableObject>emptySet());
}
private void send(final String method, final TrackableObject object) {
send(method, Collections.singleton(object));
}
private void send(final String method, final Iterable<? extends TrackableObject> objects) {
client.send(new GuiGameEvent(method, objects));
}
@Override
public void setGameView(final GameView gameView) {
super.setGameView(gameView);
send("setGameView", gameView);
}
@Override
public boolean resetForNewGame() {
send("resetForNewGame");
return true;
}
@Override
public void openView(final Iterable<PlayerView> myPlayers) {
send("openView", myPlayers);
}
@Override
public void afterGameEnd() {
send("afterGameEnd");
}
@Override
public void showCombat() {
send("showCombat");
}
@Override
public void showPromptMessage(PlayerView playerView, String message) {
// TODO Auto-generated method stub
}
@Override
public boolean stopAtPhase(PlayerView playerTurn, PhaseType phase) {
// TODO Auto-generated method stub
return false;
}
@Override
public IButton getBtnOK(final PlayerView playerView) {
return new NetButton(playerView, "OK");
}
@Override
public IButton getBtnCancel(final PlayerView playerView) {
return new NetButton(playerView, "Cancel");
}
@Override
public void focusButton(IButton button) {
// TODO Auto-generated method stub
}
@Override
public void flashIncorrectAction() {
// TODO Auto-generated method stub
}
@Override
public void updatePhase() {
// TODO Auto-generated method stub
}
@Override
public void updateTurn(PlayerView player) {
// TODO Auto-generated method stub
}
@Override
public void updatePlayerControl() {
// TODO Auto-generated method stub
}
@Override
public void enableOverlay() {
// TODO Auto-generated method stub
}
@Override
public void disableOverlay() {
// TODO Auto-generated method stub
}
@Override
public void finishGame() {
// TODO Auto-generated method stub
}
@Override
public Object showManaPool(PlayerView player) {
// TODO Auto-generated method stub
return null;
}
@Override
public void hideManaPool(PlayerView player, Object zoneToRestore) {
// TODO Auto-generated method stub
}
@Override
public void updateStack() {
// TODO Auto-generated method stub
}
@Override
public void updateZones(List<Pair<PlayerView, ZoneType>> zonesToUpdate) {
// TODO Auto-generated method stub
}
@Override
public void updateSingleCard(CardView card) {
// TODO Auto-generated method stub
}
@Override
public void updateManaPool(Iterable<PlayerView> manaPoolUpdate) {
// TODO Auto-generated method stub
}
@Override
public void updateLives(Iterable<PlayerView> livesUpdate) {
// TODO Auto-generated method stub
}
@Override
public void setPanelSelection(CardView hostCard) {
// TODO Auto-generated method stub
}
@Override
public void hear(LobbyPlayer player, String message) {
// TODO Auto-generated method stub
}
@Override
public SpellAbility getAbilityToPlay(List<SpellAbility> abilities,
ITriggerEvent triggerEvent) {
// TODO Auto-generated method stub
return null;
}
@Override
public Map<CardView, Integer> assignDamage(CardView attacker,
List<CardView> blockers, int damage, GameEntityView defender,
boolean overrideOrder) {
// TODO Auto-generated method stub
return null;
}
@Override
public void message(String message, String title) {
// TODO Auto-generated method stub
}
@Override
public void showErrorDialog(String message, String title) {
// TODO Auto-generated method stub
}
@Override
public boolean showConfirmDialog(String message, String title,
String yesButtonText, String noButtonText, boolean defaultYes) {
// TODO Auto-generated method stub
return false;
}
@Override
public int showOptionDialog(String message, String title, FSkinProp icon,
String[] options, int defaultOption) {
// TODO Auto-generated method stub
return 0;
}
@Override
public int showCardOptionDialog(CardView card, String message,
String title, FSkinProp icon, String[] options, int defaultOption) {
// TODO Auto-generated method stub
return 0;
}
@Override
public String showInputDialog(String message, String title, FSkinProp icon,
String initialInput, String[] inputOptions) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean confirm(CardView c, String question, boolean defaultIsYes,
String[] options) {
// TODO Auto-generated method stub
return false;
}
@Override
public <T> List<T> getChoices(String message, int min, int max,
Collection<T> choices, T selected, Function<T, String> display) {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> List<T> order(String title, String top, int remainingObjectsMin,
int remainingObjectsMax, List<T> sourceChoices,
List<T> destChoices, CardView referenceCard,
boolean sideboardingMode) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<PaperCard> sideboard(CardPool sideboard, CardPool main) {
// TODO Auto-generated method stub
return null;
}
@Override
public GameEntityView chooseSingleEntityForEffect(String title,
FCollectionView<? extends GameEntity> optionList,
DelayedReveal delayedReveal, boolean isOptional,
PlayerControllerHuman controller) {
// TODO Auto-generated method stub
return null;
}
@Override
public void setCard(CardView card) {
// TODO Auto-generated method stub
}
@Override
public void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi) {
// TODO Auto-generated method stub
}
@Override
public boolean openZones(Collection<ZoneType> zones,
Map<PlayerView, Object> players) {
// TODO Auto-generated method stub
return false;
}
@Override
public void restoreOldZones(Map<PlayerView, Object> playersToRestoreZonesFor) {
// TODO Auto-generated method stub
}
@Override
public boolean isUiSetToSkipPhase(PlayerView playerTurn, PhaseType phase) {
// TODO Auto-generated method stub
return false;
}
@Override
protected void updateCurrentPlayer(PlayerView player) {
// TODO Auto-generated method stub
}
private final static class NetButton implements IButton {
private final PlayerView playerView;
private final String button;
private NetButton(final PlayerView playerView, final String button) {
this.playerView = playerView;
this.button = button;
}
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setEnabled(boolean b0) {
// TODO Auto-generated method stub
}
@Override
public boolean isVisible() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setVisible(boolean b0) {
// TODO Auto-generated method stub
}
@Override
public String getText() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setText(String text0) {
// TODO Auto-generated method stub
}
@Override
public boolean isSelected() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setSelected(boolean b0) {
// TODO Auto-generated method stub
}
@Override
public boolean requestFocusInWindow() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setCommand(UiCommand command0) {
// TODO Auto-generated method stub
}
@Override
public void setTextColor(FSkinProp color) {
// TODO Auto-generated method stub
}
@Override
public void setTextColor(int r, int g, int b) {
// TODO Auto-generated method stub
}
}
}

View File

@@ -83,6 +83,11 @@ public class InputConfirmMulligan extends InputSyncronizedBase {
showMessage(sb.toString());
}
@Override
protected final boolean allowAwaitNextInput() {
return true;
}
/** {@inheritDoc} */
@Override
protected final void onOk() {

View File

@@ -125,13 +125,13 @@ public class InputPassPriority extends InputSyncronizedBase {
@Override
protected boolean onCardSelected(final Card card, final List<Card> otherCardsToSelect, final ITriggerEvent triggerEvent) {
//remove unplayable unless triggerEvent specified, in which case unplayable may be shown as disabled options
List<SpellAbility> abilities = card.getAllPossibleAbilities(getController().getPlayer(), triggerEvent == null);
if (abilities.isEmpty()) {
List<SpellAbility> abilities = card.getAllPossibleAbilities(getController().getPlayer(), triggerEvent == null);
if (abilities.isEmpty()) {
return false;
}
}
final SpellAbility ability = getController().getAbilityToPlay(abilities, triggerEvent);
if (ability != null) {
if (ability != null) {
chosenSa = new ArrayList<SpellAbility>();
chosenSa.add(ability);
if (otherCardsToSelect != null && ability.isManaAbility()) {

View File

@@ -149,7 +149,7 @@ public class InputProxy implements Observer {
}
public final void selectAbility(final SpellAbility sa) {
final Input inp = getInput();
final Input inp = getInput();
if (inp != null) {
if (sa != null) {
inp.selectAbility(sa);

View File

@@ -0,0 +1,36 @@
package forge.net;
import forge.interfaces.IGuiGame;
import forge.match.GameLobby;
public final class ClientGameLobby extends GameLobby {
private int localPlayer = -1;
public ClientGameLobby() {
super(true);
}
public void setLocalPlayer(final int index) {
this.localPlayer = index;
}
@Override public boolean hasControl() {
return false;
}
@Override public boolean mayEdit(final int index) {
return index == localPlayer;
}
@Override public boolean mayControl(final int index) {
return false;
}
@Override public boolean mayRemove(final int index) {
return false;
}
@Override public IGuiGame getGui(final int index) {
return null;
}
}

View File

@@ -15,22 +15,44 @@ import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import forge.FThreads;
import forge.UiCommand;
import forge.assets.FSkinProp;
import forge.deck.CardPool;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.CardView;
import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView;
import forge.game.zone.ZoneType;
import forge.interfaces.IButton;
import forge.interfaces.IGuiGame;
import forge.interfaces.ILobbyListener;
import forge.match.MatchButtonType;
import forge.model.FModel;
import forge.net.game.GuiGameEvent;
import forge.net.game.LobbyUpdateEvent;
import forge.net.game.IdentifiableNetEvent;
import forge.net.game.LoginEvent;
import forge.net.game.MessageEvent;
import forge.net.game.NetEvent;
import forge.net.game.client.ILobbyListener;
import forge.net.game.ReplyEvent;
import forge.net.game.client.IToServer;
import forge.properties.ForgePreferences.FPref;
import forge.trackable.TrackableCollection;
import forge.util.ITriggerEvent;
public class FGameClient implements IToServer {
private final IGuiGame clientGui;
@@ -39,6 +61,7 @@ public class FGameClient implements IToServer {
}
private final List<ILobbyListener> lobbyListeners = Lists.newArrayList();
private final ReplyPool replies = new ReplyPool();
static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));
@@ -81,14 +104,32 @@ public class FGameClient implements IToServer {
}
}
@Override
public void send(final NetEvent event) {
System.out.println("Client sent " + event);
channel.writeAndFlush(event);
}
@Override
public Object sendAndWait(final IdentifiableNetEvent event) throws TimeoutException {
replies.initialize(event.getId());
send(event);
// Wait for reply
return replies.get(event.getId());
}
public void addLobbyListener(final ILobbyListener listener) {
lobbyListeners.add(listener);
}
private void setGameControllers(final Iterable<PlayerView> myPlayers) {
for (final PlayerView p : myPlayers) {
clientGui.setGameController(p, new NetGameController(this));
}
}
private class GameClientHandler extends ChannelInboundHandlerAdapter {
/**
* Creates a client-side handler.
@@ -98,24 +139,214 @@ public class FGameClient implements IToServer {
@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)));
// 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 GuiGameEvent) {
if (msg instanceof ReplyEvent) {
final ReplyEvent event = (ReplyEvent) msg;
replies.complete(event.getIndex(), event.getReply());
} else if (msg instanceof GuiGameEvent) {
final GuiGameEvent event = (GuiGameEvent) msg;
final Object[] args = event.getObjects();
Serializable reply = null;
boolean doReply = false;
final IButton btn;
if (args.length >= 2 && args[0] instanceof PlayerView && args[1] instanceof MatchButtonType) {
btn = args[1] == MatchButtonType.OK ? clientGui.getBtnOK((PlayerView) args[0]) : clientGui.getBtnCancel((PlayerView) args[0]);
} else {
btn = null;
}
switch (event.getMethod()) {
case "setGameView":
clientGui.setGameView((GameView) event.getObject());
clientGui.setGameView((GameView) args[0]);
break;
case "openView":
clientGui.openView((Iterable<PlayerView>) event.getObjects());
default:
final TrackableCollection<PlayerView> myPlayers = (TrackableCollection<PlayerView>) args[0];
setGameControllers(myPlayers);
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public final void run() {
//clientGui.setGameView(new NetGameView(FGameClient.this));
clientGui.openView(myPlayers);
}
});
break;
case "afterGameEnd":
clientGui.afterGameEnd();
break;
case "showCombat":
clientGui.showCombat();
break;
case "showPromptMessage":
clientGui.showPromptMessage((PlayerView) args[0], (String) args[1]);
break;
case "stopAtPhase":
reply = clientGui.stopAtPhase((PlayerView) args[0], (PhaseType) args[1]);
doReply = true;
break;
case "focusButton":
clientGui.focusButton((MatchButtonType) args[0]);
break;
case "flashIncorrectAction":
clientGui.flashIncorrectAction();
break;
case "updatePhase":
clientGui.updatePhase();
break;
case "updateTurn":
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public final void run() {
clientGui.updateTurn((PlayerView) args[0]);
}
});
break;
case "udpdatePlayerControl":
clientGui.updatePlayerControl();
break;
case "enableOverlay":
clientGui.enableOverlay();
break;
case "disbleOverlay":
clientGui.disableOverlay();
break;
case "finishGame":
clientGui.finishGame();
break;
case "showManaPool":
clientGui.showManaPool((PlayerView) args[0]);
break;
case "hideManaPool":
clientGui.hideManaPool((PlayerView) args[0], args[1]);
break;
case "updateStack":
clientGui.updateStack();
break;
case "updateZones":
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public final void run() {
clientGui.updateZones((List<Pair<PlayerView, ZoneType>>) args[0]);
}
});
break;
case "updateSingleCard":
clientGui.updateSingleCard((CardView) args[0]);
break;
case "updateManaPool":
clientGui.updateManaPool((Iterable<PlayerView>) args[0]);
break;
case "updateLives":
clientGui.updateLives((Iterable<PlayerView>) args[0]);
break;
case "setPanelSelection":
clientGui.setPanelSelection((CardView) args[0]);
break;
case "getAbilityToPlay":
reply = clientGui.getAbilityToPlay((List<SpellAbilityView>) args[0], (ITriggerEvent) args[1]);
doReply = true;
break;
case "assignDamage":
reply = (Serializable) clientGui.assignDamage((CardView) args[0], (List<CardView>) args[1], (int) args[2], (GameEntityView) args[3], (boolean) args[4]);
doReply = true;
break;
case "message":
clientGui.message((String) args[0], (String) args[1]);
break;
case "showErrorDialog":
clientGui.showErrorDialog((String) args[0], (String) args[1]);
break;
case "showConfirmDialog":
reply = clientGui.showConfirmDialog((String) args[0], (String) args[1], (String) args[2], (String) args[3], (boolean) args[4]);
doReply = true;
break;
case "showOptionDialog":
reply = clientGui.showOptionDialog((String) args[0], (String) args[1], (FSkinProp) args[2], (String[]) args[3], (int) args[4]);
doReply = true;
break;
case "showCardOptionDialog":
reply = clientGui.showCardOptionDialog((CardView) args[0], (String) args[1], (String) args[2], (FSkinProp) args[3], (String[]) args[4], (int) args[5]);
doReply = true;
break;
case "showInputDialog":
reply = clientGui.showInputDialog((String) args[0], (String) args[1], (FSkinProp) args[2], (String) args[3], (String[]) args[4]);
doReply = true;
break;
case "confirm":
reply = clientGui.confirm((CardView) args[0], (String) args[1], (boolean) args[2], (String[]) args[3]);
doReply = true;
break;
case "getChoices":
reply = (Serializable) clientGui.getChoices((String) args[0], (int) args[1], (int) args[2], (Collection<Object>) args[3], args[4], (Function<Object, String>) args[5]);
doReply = true;
break;
case "order":
reply = (Serializable) clientGui.order((String) args[0], (String) args[1], (int) args[2], (int) args[3], (List<Object>) args[4], (List<Object>) args[5], (CardView) args[6], (boolean) args[7]);
doReply = true;
break;
case "sideboard":
reply = (Serializable) clientGui.sideboard((CardPool) args[0], (CardPool) args[1]);
doReply = true;
break;
case "chooseSingleEntityForEffect":
reply = clientGui.chooseSingleEntityForEffect((String) args[0], (TrackableCollection<GameEntityView>) args[1], (DelayedReveal) args[2], (boolean) args[3]);
doReply = true;
break;
case "setCard":
clientGui.setCard((CardView) args[0]);
break;
// TODO case "setPlayerAvatar":
case "openZones":
reply = clientGui.openZones((Collection<ZoneType>) args[0], (Map<PlayerView, Object>) args[1]);
doReply = true;
break;
case "restoreOldZones":
clientGui.restoreOldZones((Map<PlayerView, Object>) args[0]);
break;
case "isUiSetToSkipPhase":
reply = clientGui.isUiSetToSkipPhase((PlayerView) args[0], (PhaseType) args[1]);
doReply = true;
break;
// BUTTONS
case "btn_setEnabled":
btn.setEnabled((boolean) args[2]);
break;
case "btn_setVisible":
btn.setVisible((boolean) args[2]);
break;
case "btn_setText":
btn.setText((String) args[2]);
break;
case "btn_isSelected":
reply = btn.isSelected();
doReply = true;
break;
case "btn_setSelected":
btn.setSelected((boolean) args[2]);
break;
case "btn_requestFocusInWindows":
reply = btn.requestFocusInWindow();
doReply = true;
break;
case "btn_setCommand":
btn.setCommand((UiCommand) args[2]);
break;
case "btn_setTextColor":
if (args.length == 3) {
btn.setTextColor((FSkinProp) args[2]);
} else {
btn.setTextColor((int) args[2], (int) args[3], (int) args[4]);
}
default:
System.err.println("Unsupported game event " + event.getMethod());
break;
}
if (doReply) {
send(new ReplyEvent(event.getId(), reply));
}
}
}
@@ -145,7 +376,8 @@ public class FGameClient implements IToServer {
public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
if (msg instanceof LobbyUpdateEvent) {
for (final ILobbyListener listener : lobbyListeners) {
listener.update(((LobbyUpdateEvent) msg).getState());
final LobbyUpdateEvent event = (LobbyUpdateEvent) msg;
listener.update(event.getState(), event.getSlot());
}
}
super.channelRead(ctx, msg);

View File

@@ -1,15 +1,27 @@
package forge.net;
import forge.game.GameRules;
import forge.interfaces.ILobby;
import forge.net.game.LobbyState;
import forge.net.game.LobbyUpdateEvent;
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.NextGameDecision;
import forge.net.game.GuiGameEvent;
import forge.net.game.LobbySlotType;
import forge.net.game.LoginEvent;
import forge.net.game.LogoutEvent;
import forge.net.game.MessageEvent;
import forge.net.game.NetEvent;
import forge.net.game.RegisterDeckEvent;
import forge.net.game.ReplyEvent;
import forge.net.game.UpdateLobbyPlayerEvent;
import forge.net.game.server.RemoteClient;
import forge.util.ITriggerEvent;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
@@ -27,8 +39,14 @@ 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.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
public final class FServerManager {
@@ -36,18 +54,13 @@ public final class FServerManager {
private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
private final EventLoopGroup workerGroup = new NioEventLoopGroup();
private final Map<Integer, NetGame> games = Maps.newTreeMap();
private int id = 0;
private final Map<Channel, RemoteClient> clients = Maps.newTreeMap();
private ILobby localLobby;
private ServerGameLobby localLobby;
private ILobbyListener lobbyListener;
private FServerManager() {
}
private int nextId() {
return id++;
}
public static FServerManager getInstance() {
if (instance == null) {
instance = new FServerManager();
@@ -62,15 +75,14 @@ public final class FServerManager {
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
@Override public final void initChannel(final SocketChannel ch) {
final ChannelPipeline p = ch.pipeline();
p.addLast(
new ObjectEncoder(),
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
new MessageHandler(),
new RegisterClientHandler(),
new ToLobbyListenersHandler(),
new LobbyInputHandler(),
new DeregisterClientHandler(),
new GameServerHandler());
}
@@ -101,29 +113,53 @@ public final class FServerManager {
}
public void broadcast(final NetEvent event) {
for (final RemoteClient client : clients.values()) {
broadcastTo(event, clients.values());
}
public void broadcastExcept(final NetEvent event, final RemoteClient notTo) {
broadcastExcept(event, Collections.singleton(notTo));
}
public void broadcastExcept(final NetEvent event, final Collection<RemoteClient> notTo) {
broadcastTo(event, Iterables.filter(clients.values(), Predicates.not(Predicates.in(notTo))));
}
private void broadcastTo(final NetEvent event, final Iterable<RemoteClient> to) {
for (final RemoteClient client : to) {
event.updateForClient(client);
client.send(event);
}
}
public void setLobby(final ILobby lobby) {
public void setLobby(final ServerGameLobby lobby) {
this.localLobby = lobby;
}
public NetGame hostGame(final GameRules rules) {
final int id = nextId();
final NetGame game = new NetGame(rules);
games.put(id, game);
return game;
public void setLobbyListener(final ILobbyListener listener) {
this.lobbyListener = listener;
}
public void updateLobbyState() {
final LobbyState state = localLobby.getState();
final LobbyUpdateEvent event = new LobbyUpdateEvent(state);
final LobbyUpdateEvent event = new LobbyUpdateEvent(localLobby.getData());
broadcast(event);
}
public void updateSlot(final int index, final UpdateLobbyPlayerEvent event) {
localLobby.applyToSlot(index, event);
}
public IGuiGame getGui(final int index) {
final LobbySlot slot = localLobby.getSlot(index);
final LobbySlotType type = slot.getType();
if (type == LobbySlotType.LOCAL) {
return GuiBase.getInterface().getNewGuiGame();
} else if (type == LobbySlotType.REMOTE) {
for (final RemoteClient client : clients.values()) {
if (client.getIndex() == index) {
return new NetGuiGame(client);
}
}
}
return null;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
@@ -141,9 +177,187 @@ 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
@@ -158,8 +372,7 @@ public final class FServerManager {
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
final RemoteClient client = new RemoteClient(ctx.channel());
clients.put(ctx.channel(), client);
games.get(0).addClient(client);
System.out.println("User connected to server at " + ctx.channel().remoteAddress());
System.out.println("Client connected to server at " + ctx.channel().remoteAddress());
updateLobbyState();
super.channelActive(ctx);
}
@@ -168,47 +381,45 @@ public final class FServerManager {
public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
final RemoteClient client = clients.get(ctx.channel());
if (msg instanceof LoginEvent) {
client.setUsername(((LoginEvent) msg).getUsername());
final String username = ((LoginEvent) msg).getUsername();
client.setUsername(username);
broadcast(new MessageEvent(null, String.format("%s joined the room", username)));
updateLobbyState();
} else if (msg instanceof RegisterDeckEvent) {
games.get(0).registerDeck(client, ((RegisterDeckEvent) msg).getDeck());
} else if (msg instanceof UpdateLobbyPlayerEvent) {
localLobby.applyToSlot(client.getIndex(), (UpdateLobbyPlayerEvent) msg);
}
super.channelRead(ctx, msg);
}
}
private class ToLobbyListenersHandler extends ChannelInboundHandlerAdapter {
private class LobbyInputHandler extends ChannelInboundHandlerAdapter {
@Override public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
final RemoteClient client = clients.get(ctx.channel());
if (msg instanceof LoginEvent) {
final LoginEvent event = (LoginEvent) msg;
final int index = localLobby.login(client);
final int index = localLobby.connectPlayer(event.getUsername(), event.getAvatarIndex());
if (index == -1) {
ctx.close();
} else {
client.setIndex(index);
broadcast(event);
updateLobbyState();
}
} else if (msg instanceof UpdateLobbyPlayerEvent) {
updateSlot(client.getIndex(), (UpdateLobbyPlayerEvent) msg);
} else if (msg instanceof MessageEvent) {
final MessageEvent event = (MessageEvent) msg;
broadcast(event);
lobbyListener.message(event.getSource(), event.getMessage());
}
super.channelRead(ctx, msg);
}
@Override public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
final RemoteClient client = clients.get(ctx.channel());
localLobby.logout(client);
updateLobbyState();
super.channelInactive(ctx);
}
}
private class DeregisterClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
final RemoteClient client = clients.remove(ctx.channel());
// TODO remove client from games
localLobby.disconnectPlayer(client.getIndex());
broadcast(new LogoutEvent(client.getUsername()));
super.channelInactive(ctx);
}

View File

@@ -0,0 +1,28 @@
package forge.net;
import forge.match.GameLobby.GameLobbyData;
import forge.net.game.NetEvent;
import forge.net.game.server.RemoteClient;
public class LobbyUpdateEvent implements NetEvent {
private static final long serialVersionUID = 7114918637727047985L;
private final GameLobbyData state;
private int slot;
public LobbyUpdateEvent(final GameLobbyData state) {
this.state = state;
}
@Override
public void updateForClient(final RemoteClient client) {
this.slot = client.getIndex();
}
public GameLobbyData getState() {
return state;
}
public int getSlot() {
return slot;
}
}

View File

@@ -1,69 +0,0 @@
package forge.net;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.ai.LobbyPlayerAi;
import forge.deck.Deck;
import forge.game.GameRules;
import forge.game.player.RegisteredPlayer;
import forge.interfaces.IGuiGame;
import forge.match.HostedMatch;
import forge.match.NetGuiGame;
import forge.net.game.server.RemoteClient;
import forge.player.LobbyPlayerHuman;
public final class NetGame {
private final Map<RemoteClient, NetPlayer> clients = Maps.newHashMap();
private final GameRules rules;
private final HostedMatch match = new HostedMatch();
public NetGame(final GameRules rules) {
this.rules = rules;
}
public void addClient(final RemoteClient client) {
clients.put(client, new NetPlayer(client, new NetGuiGame(client)));
}
public void startMatch() {
final List<RegisteredPlayer> registeredPlayers = Lists.newArrayListWithCapacity(clients.size());
final Map<RegisteredPlayer, IGuiGame> guis = Maps.newHashMap();
for (final NetPlayer np : clients.values()) {
if (np.player == null) {
System.err.println("No deck registered for player " + np.client.getUsername());
return;
}
registeredPlayers.add(np.player);
guis.put(np.player, np.gui);
}
// DEBUG
if (registeredPlayers.size() == 1) {
RegisteredPlayer r = new RegisteredPlayer(new Deck());
registeredPlayers.add(r);
r.setPlayer(new LobbyPlayerAi("AI", new HashMap<String, String>()));
}
match.startMatch(rules, null, registeredPlayers, guis);
}
public void registerDeck(final RemoteClient client, final Deck deck) {
final RegisteredPlayer r = new RegisteredPlayer(deck);
clients.get(client).player = r;
r.setPlayer(new LobbyPlayerHuman(client.getUsername()));
}
private static final class NetPlayer {
private final RemoteClient client;
private RegisteredPlayer player = null;
private final IGuiGame gui;
private NetPlayer(final RemoteClient client, final IGuiGame gui) {
this.client = client;
this.gui = gui;
}
}
}

View File

@@ -0,0 +1,129 @@
package forge.net;
import java.util.List;
import java.util.concurrent.TimeoutException;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView;
import forge.interfaces.IDevModeCheats;
import forge.interfaces.IGameController;
import forge.match.NextGameDecision;
import forge.net.game.GuiGameEvent;
import forge.net.game.client.IToServer;
import forge.util.ITriggerEvent;
public class NetGameController implements IGameController {
private final IToServer server;
public NetGameController(final IToServer server) {
this.server = 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 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;
}
@Override
public boolean useMana(final byte color) {
return sendAndWait(methodName(), color);
}
@Override
public boolean tryUndoLastAction() {
return sendAndWait(methodName());
}
@Override
public void selectPlayer(final PlayerView playerView, final ITriggerEvent triggerEvent) {
send(methodName(), playerView, triggerEvent);
}
@Override
public boolean selectCard(final CardView cardView, final List<CardView> otherCardViewsToSelect, final ITriggerEvent triggerEvent) {
return sendAndWait(methodName(), cardView, otherCardViewsToSelect, triggerEvent);
}
@Override
public void selectButtonOk() {
send(methodName());
}
@Override
public void selectButtonCancel() {
send(methodName());
}
@Override
public void selectAbility(final SpellAbilityView sa) {
send(methodName(), sa);
}
@Override
public boolean passPriorityUntilEndOfTurn() {
return sendAndWait(methodName());
}
@Override
public boolean passPriority() {
return sendAndWait(methodName());
}
@Override
public void nextGameDecision(final NextGameDecision decision) {
send(methodName(), decision);
}
@Override
public boolean mayLookAtAllCards() {
// Don't do this over network
return false;
}
public String getActivateDescription(final CardView card) {
return sendAndWait(methodName(), card);
}
@Override
public void concede() {
send(methodName());
}
@Override
public IDevModeCheats cheat() {
return IDevModeCheats.NO_CHEAT;
}
@Override
public boolean canPlayUnlimitedLands() {
return false;
}
@Override
public void alphaStrike() {
send(methodName());
}
}

View File

@@ -0,0 +1,380 @@
package forge.net;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import org.apache.commons.lang3.tuple.Pair;
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;
import forge.game.GameView;
import forge.game.card.CardView;
import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
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.game.GuiGameEvent;
import forge.net.game.server.IToClient;
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);
public NetGuiGame(final IToClient client) {
this.client = client;
for (final MatchButtonType type : MatchButtonType.values()) {
btns.put(type, Maps.<PlayerView, NetButton>newHashMap());
}
}
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 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 void updateGameView() {
send("setGameView", getGameView());
}
@Override
public void setGameView(final GameView gameView) {
super.setGameView(gameView);
updateGameView();
}
@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);
updateGameView();
}
@Override
public void afterGameEnd() {
send(methodName());
}
@Override
public void showCombat() {
send(methodName());
}
@Override
public void showPromptMessage(final PlayerView playerView, final String message) {
updateGameView();
send(methodName(), 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);
}
@Override
public void flashIncorrectAction() {
send(methodName());
}
@Override
public void updatePhase() {
updateGameView();
send(methodName());
}
@Override
public void updateTurn(final PlayerView player) {
updateGameView();
send(methodName(), player);
}
@Override
public void updatePlayerControl() {
updateGameView();
send(methodName());
}
@Override
public void enableOverlay() {
send(methodName());
}
@Override
public void disableOverlay() {
send(methodName());
}
@Override
public void finishGame() {
send(methodName());
}
@Override
public Object showManaPool(final PlayerView player) {
send(methodName(), player);
return null;
}
@Override
public void hideManaPool(final PlayerView player, final Object zoneToRestore) {
send(methodName(), player, zoneToRestore);
}
@Override
public void updateStack() {
updateGameView();
send(methodName());
}
@Override
public void updateZones(final List<Pair<PlayerView, ZoneType>> zonesToUpdate) {
updateGameView();
send(methodName(), zonesToUpdate);
}
@Override
public void updateSingleCard(final CardView card) {
updateGameView();
send(methodName(), card);
}
@Override
public void updateManaPool(final Iterable<PlayerView> manaPoolUpdate) {
updateGameView();
send(methodName(), manaPoolUpdate);
}
@Override
public void updateLives(final Iterable<PlayerView> livesUpdate) {
updateGameView();
send(methodName(), livesUpdate);
}
@Override
public void setPanelSelection(final CardView hostCard) {
updateGameView();
send(methodName(), hostCard);
}
@Override
public SpellAbilityView getAbilityToPlay(final List<SpellAbilityView> abilities, final ITriggerEvent triggerEvent) {
return sendAndWait(methodName(), 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);
}
@Override
public void message(final String message, final String title) {
send(methodName(), message, title);
}
@Override
public void showErrorDialog(final String message, final String title) {
send(methodName(), 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);
}
@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);
}
@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);
}
@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);
}
@Override
public boolean confirm(final CardView c, final String question, final boolean defaultIsYes, final String[] options) {
return sendAndWait(methodName(), 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);
}
@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);
}
@Override
public List<PaperCard> sideboard(final CardPool sideboard, final CardPool main) {
return sendAndWait(methodName(), 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);
}
@Override
public void setCard(final CardView card) {
updateGameView();
send(methodName(), card);
}
@Override
public void setPlayerAvatar(LobbyPlayer player, 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);
}
@Override
public void restoreOldZones(final Map<PlayerView, Object> playersToRestoreZonesFor) {
send(methodName(), playersToRestoreZonesFor);
}
@Override
public boolean isUiSetToSkipPhase(final PlayerView playerTurn, final PhaseType phase) {
return sendAndWait(methodName(), playerTurn, phase);
}
@Override
protected void updateCurrentPlayer(final PlayerView player) {
// 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 setTextColor(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

@@ -0,0 +1,65 @@
package forge.net;
import java.util.Collections;
import org.apache.commons.lang3.StringUtils;
import forge.AIOption;
import forge.interfaces.IGuiGame;
import forge.match.GameLobby;
import forge.match.LobbySlot;
import forge.net.game.LobbySlotType;
public final class ServerGameLobby extends GameLobby {
public ServerGameLobby() {
super(true);
addSlot(new LobbySlot(LobbySlotType.LOCAL, localName(), localAvatarIndices()[0], 0, true, Collections.<AIOption>emptySet()));
addSlot(new LobbySlot(LobbySlotType.OPEN, null, -1, 1, false, Collections.<AIOption>emptySet()));
}
public int connectPlayer(final String name, final int avatarIndex) {
final int nSlots = getNumberOfSlots();
for (int index = 0; index < nSlots; index++) {
final LobbySlot slot = getSlot(index);
if (slot.getType() == LobbySlotType.OPEN) {
connectPlayer(name, avatarIndex, slot);
return index;
}
}
return -1;
}
private void connectPlayer(final String name, final int avatarIndex, final LobbySlot slot) {
slot.setType(LobbySlotType.REMOTE);
slot.setName(name);
slot.setAvatarIndex(avatarIndex);
updateView();
}
public void disconnectPlayer(final int index) {
final LobbySlot slot = getSlot(index);
slot.setType(LobbySlotType.OPEN);
slot.setName(StringUtils.EMPTY);
updateView();
}
@Override public boolean hasControl() {
return true;
}
@Override public boolean mayEdit(final int index) {
final LobbySlotType type = getSlot(index).getType();
return type != LobbySlotType.REMOTE && type != LobbySlotType.OPEN;
}
@Override public boolean mayControl(final int index) {
return getSlot(index).getType() != LobbySlotType.REMOTE;
}
@Override public boolean mayRemove(final int index) {
return true;
}
@Override public IGuiGame getGui(final int index) {
return FServerManager.getInstance().getGui(index);
}
}

View File

@@ -1,7 +1,10 @@
package forge.player;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import forge.AIOption;
import forge.GuiBase;
import forge.LobbyPlayer;
import forge.ai.AiProfileUtil;
@@ -13,8 +16,6 @@ import forge.util.GuiDisplayUtil;
import forge.util.MyRandom;
import forge.util.gui.SOptionPane;
import org.apache.commons.lang3.StringUtils;
public final class GamePlayerUtil {
private GamePlayerUtil() { };
@@ -22,8 +23,8 @@ public final class GamePlayerUtil {
public static final LobbyPlayer getGuiPlayer() {
return guiPlayer;
}
public static final LobbyPlayer getGuiPlayer(String name, int index) {
if (index == 0) {
public static final LobbyPlayer getGuiPlayer(final String name, final boolean writePref) {
if (writePref) {
if (!name.equals(guiPlayer.getName())) {
guiPlayer.setName(name);
FModel.getPreferences().setPref(FPref.PLAYER_NAME, name);
@@ -49,7 +50,7 @@ public final class GamePlayerUtil {
public final static LobbyPlayer createAiPlayer(String name, int avatarIndex) {
return createAiPlayer(name, avatarIndex, null);
}
public final static LobbyPlayer createAiPlayer(String name, int avatarIndex, Map<String, String> options) {
public final static LobbyPlayer createAiPlayer(String name, int avatarIndex, Set<AIOption> options) {
LobbyPlayerAi player = new LobbyPlayerAi(name, options);
// TODO: implement specific AI profiles for quest mode.

View File

@@ -1,128 +0,0 @@
package forge.player;
import java.util.Collections;
import java.util.List;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.interfaces.IDevModeCheats;
import forge.interfaces.IGameController;
import forge.match.NextGameDecision;
import forge.net.game.client.IToServer;
import forge.trackable.TrackableObject;
import forge.util.ITriggerEvent;
public class NetGameController implements IGameController {
private final IToServer server;
public NetGameController(final IToServer server) {
this.server = server;
}
private void send(final String method) {
send(method, Collections.<TrackableObject>emptySet());
}
private void send(final String method, final TrackableObject object) {
send(method, Collections.singleton(object));
}
private void send(final String method, final Iterable<? extends TrackableObject> objects) {
//server.send(new (method, objects));
}
@Override
public boolean mayLookAtAllCards() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean canPlayUnlimitedLands() {
// TODO Auto-generated method stub
return false;
}
@Override
public void concede() {
send("concede");
}
@Override
public void alphaStrike() {
send("alphaStrike");
}
@Override
public boolean useMana(byte color) {
// TODO Auto-generated method stub
return false;
}
@Override
public void selectButtonOk() {
// TODO Auto-generated method stub
}
@Override
public void selectButtonCancel() {
// TODO Auto-generated method stub
}
@Override
public boolean passPriority() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean passPriorityUntilEndOfTurn() {
// TODO Auto-generated method stub
return false;
}
@Override
public void selectPlayer(PlayerView playerView, ITriggerEvent triggerEvent) {
// TODO Auto-generated method stub
}
@Override
public boolean selectCard(CardView cardView,
List<CardView> otherCardViewsToSelect, ITriggerEvent triggerEvent) {
// TODO Auto-generated method stub
return false;
}
@Override
public void selectAbility(SpellAbility sa) {
// TODO Auto-generated method stub
}
@Override
public boolean tryUndoLastAction() {
// TODO Auto-generated method stub
return false;
}
@Override
public IDevModeCheats cheat() {
// TODO Auto-generated method stub
return null;
}
@Override
public void nextGameDecision(NextGameDecision decision) {
// TODO Auto-generated method stub
}
@Override
public String getActivateDescription(CardView card) {
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -5,6 +5,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -74,6 +75,7 @@ import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.SpellAbilityView;
import forge.game.spellability.TargetChoices;
import forge.game.trigger.Trigger;
import forge.game.trigger.WrappedAbility;
@@ -217,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) {
return getGui().getAbilityToPlay(abilities, triggerEvent);
//return HostedMatch.getController().getAbilityToPlay(abilities, triggerEvent);
final SpellAbilityView resultView = getGui().getAbilityToPlay(SpellAbilityView.getCollection(abilities), triggerEvent);
return getGame().getSpellAbility(resultView);
}
@Override
@@ -389,13 +391,13 @@ public class PlayerControllerHuman
// Human is supposed to read the message and understand from it what to choose
if (optionList.isEmpty()) {
if (delayedReveal != null) {
delayedReveal.reveal(this);
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
return null;
}
if (!isOptional && optionList.size() == 1) {
if (delayedReveal != null) {
delayedReveal.reveal(this);
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
return Iterables.getFirst(optionList, null);
}
@@ -416,7 +418,7 @@ public class PlayerControllerHuman
if (canUseSelectCardsInput) {
if (delayedReveal != null) {
delayedReveal.reveal(this);
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
InputSelectEntitiesFromList<T> input = new InputSelectEntitiesFromList<T>(this, isOptional ? 0 : 1, 1, optionList);
input.setCancelAllowed(isOptional);
@@ -425,8 +427,10 @@ public class PlayerControllerHuman
return Iterables.getFirst(input.getSelected(), null);
}
final GameEntityView result = getGui().chooseSingleEntityForEffect(title, optionList, delayedReveal, isOptional, this);
endTempShowCards(); //assume tempShow called by GuiBase.getInterface().chooseSingleEntityForEffect
tempShow(optionList);
final GameEntityView result = getGui().chooseSingleEntityForEffect(title, GameEntityView.getEntityCollection(optionList), delayedReveal, isOptional);
endTempShowCards();
if (result instanceof CardView) {
return (T) game.getCard((CardView)result);
}
@@ -438,16 +442,16 @@ public class PlayerControllerHuman
@Override
public int chooseNumber(SpellAbility sa, String title, int min, int max) {
if (min >= max) {
return min;
}
if (min >= max) {
return min;
}
final Integer[] choices = new Integer[max + 1 - min];
for (int i = 0; i <= max - min; i++) {
choices[i] = Integer.valueOf(i + min);
}
return getGui().one(title, choices).intValue();
}
@Override
public int chooseNumber(SpellAbility sa, String title, List<Integer> choices, Player relatedPlayer) {
return getGui().one(title, choices).intValue();
@@ -563,19 +567,22 @@ public class PlayerControllerHuman
@Override
public void reveal(CardCollectionView cards, ZoneType zone, Player owner, String message) {
reveal(CardView.getCollection(cards), zone, PlayerView.get(owner), message);
}
@Override
public void reveal(Collection<CardView> cards, ZoneType zone, PlayerView owner, String message) {
if (StringUtils.isBlank(message)) {
message = "Looking at cards in {player's} " + zone.name().toLowerCase();
}
else {
} else {
message += "{player's} " + zone.name().toLowerCase();
}
String fm = MessageUtil.formatMessage(message, player, owner);
if (!cards.isEmpty()) {
tempShowCards(cards);
getGui().reveal(fm, CardView.getCollection(cards));
tempShowCards(game.getCardList(cards));
getGui().reveal(fm, cards);
endTempShowCards();
}
else {
} else {
getGui().message(MessageUtil.formatMessage("There are no cards in {player's} " +
zone.name().toLowerCase(), player, owner), fm);
}
@@ -993,17 +1000,18 @@ public class PlayerControllerHuman
return Iterables.getFirst(allTargets, null);
}
final Function<Pair<SpellAbilityStackInstance, GameObject>, String> fnToString = new Function<Pair<SpellAbilityStackInstance, GameObject>, String>() {
@Override
public String apply(Pair<SpellAbilityStackInstance, GameObject> targ) {
return targ.getRight().toString() + " - " + targ.getLeft().getStackDescription();
}
};
List<Pair<SpellAbilityStackInstance, GameObject>> chosen = getGui().getChoices(saSpellskite.getHostCard().getName(), 1, 1, allTargets, null, fnToString);
final List<Pair<SpellAbilityStackInstance, GameObject>> chosen = getGui().getChoices(saSpellskite.getHostCard().getName(), 1, 1, allTargets, null, new FnTargetToString());
return Iterables.getFirst(chosen, null);
}
private final static class FnTargetToString implements Function<Pair<SpellAbilityStackInstance, GameObject>, String>, Serializable {
private static final long serialVersionUID = -4779137632302777802L;
@Override public String apply(final Pair<SpellAbilityStackInstance, GameObject> targ) {
return targ.getRight().toString() + " - " + targ.getLeft().getStackDescription();
}
}
@Override
public void notifyOfValue(SpellAbility sa, GameObject realtedTarget, String value) {
String message = MessageUtil.formatNotificationMessage(sa, player, realtedTarget, value);
@@ -1352,8 +1360,8 @@ public class PlayerControllerHuman
return inputProxy.selectCard(cardView, otherCardViewsToSelect, triggerEvent);
}
public void selectAbility(final SpellAbility sa) {
inputProxy.selectAbility(sa);
public void selectAbility(final SpellAbilityView sa) {
inputProxy.selectAbility(getGame().getSpellAbility(sa));
}
public void alphaStrike() {