From b47d528bf06320bd403973479aac03701513406c Mon Sep 17 00:00:00 2001 From: "Jamin W. Collins" Date: Thu, 15 Feb 2018 17:07:14 -0700 Subject: [PATCH] ensure that remote client has trackers defined Signed-off-by: Jamin W. Collins --- .../forge/net/client/GameClientHandler.java | 182 +++++++++++++++++- 1 file changed, 176 insertions(+), 6 deletions(-) diff --git a/forge-gui/src/main/java/forge/net/client/GameClientHandler.java b/forge-gui/src/main/java/forge/net/client/GameClientHandler.java index d8125bee65a..f15a4576bc5 100644 --- a/forge-gui/src/main/java/forge/net/client/GameClientHandler.java +++ b/forge-gui/src/main/java/forge/net/client/GameClientHandler.java @@ -1,5 +1,14 @@ package forge.net.client; +import com.google.common.collect.Lists; +import forge.LobbyPlayer; +import forge.game.*; +import forge.game.player.RegisteredPlayer; +import forge.interfaces.ILobbyListener; +import forge.match.LobbySlot; +import forge.player.LobbyPlayerHuman; +import forge.trackable.TrackableObject; +import forge.trackable.Tracker; import io.netty.channel.ChannelHandlerContext; import forge.game.player.PlayerView; import forge.interfaces.IGuiGame; @@ -12,9 +21,12 @@ import forge.net.event.LoginEvent; import forge.properties.ForgePreferences.FPref; import forge.trackable.TrackableCollection; +import java.util.*; + final class GameClientHandler extends GameProtocolHandler { private final FGameClient client; private final IGuiGame gui; + private Tracker tracker; /** * Creates a client-side game handler. @@ -23,6 +35,7 @@ final class GameClientHandler extends GameProtocolHandler { super(true); this.client = client; this.gui = client.getGui(); + this.tracker = null; } @Override @@ -44,12 +57,169 @@ final class GameClientHandler extends GameProtocolHandler { @Override protected void beforeCall(final ProtocolMethod protocolMethod, final Object[] args) { switch (protocolMethod) { - case openView: - final TrackableCollection myPlayers = (TrackableCollection) args[0]; - client.setGameControllers(myPlayers); - break; - default: - break; + case openView: + if (this.tracker == null) { + int maxAttempts = 5; + for (int numAttempts = 0; numAttempts < maxAttempts; numAttempts++) { + try { + + this.tracker = createTracker(); + + for (PlayerView myPlayer : (TrackableCollection) args[0]) { + if (myPlayer.getTracker() == null) { + myPlayer.setTracker(this.tracker); + } + } + + final TrackableCollection myPlayers = (TrackableCollection) args[0]; + client.setGameControllers(myPlayers); + + } catch (Exception e) { + System.err.println("Failed: attempt number: " + numAttempts + " - " + e.toString()); + try { + Thread.sleep(100); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + } + } + } + break; + default: + break; + } + if (!(this.tracker == null)) { + updateTrackers(args); + } + } + + /** + * This method attempts to recreate a GameRules object from existing state. + * + * @return GameRules + */ + private GameRules createGameRules(GameType gameType, GameView gameView) { + // FIXME: how do we know the rules are the same on each side??? + GameRules gameRules = new GameRules(gameType); + gameRules.setGamesPerMatch(gameView.getNumGamesInMatch()); + gameRules.setPoisonCountersToLose(gameView.getPoisonCountersToLose()); + + return gameRules; + } + + /** + * This method creates the necessary objects and state to retrieve a Tracker object. + * + * Near as I can tell, that means that we need to create a Match. + * + * Creating a Match requires that we have: + * * GameRules + * * RegisteredPlayers + * * Title + * + * @return Tracker + */ + private Tracker createTracker() { + + // retrieve what we can from the existing (but incomplete) state + final IGuiGame gui = client.getGui(); + GameView gameView = gui.getGameView(); + + final GameType gameType = gameView.getGameType(); + final GameRules gameRules = createGameRules(gameType, gameView); + final List registeredPlayers = createRegisteredPlayers(gameType); + + // pull the title from the existing (incomplete) GameView + final String title = gameView.getTitle(); + + // create a valid match object and game + Match match = new Match(gameRules, registeredPlayers, title); + Game game = match.createGame(); + + // replace the existing incomplete GameView with the newly created one + gui.setGameView(null); + gui.setGameView(game.getView()); + + return gui.getGameView().getTracker(); + } + + /** + * This method retrieves existing information about the players to + * build a list of RegisteredPlayers. + * + * + * @param gameType + * @return List + */ + private List createRegisteredPlayers(GameType gameType) { + // get all lobby players + List lobbyListeners = client.getLobbyListeners(); + ILobbyListener lobbyListener = lobbyListeners.get(0); + ClientGameLobby myLobby = lobbyListener.getLobby(); + + List players = Lists.newArrayList(); + int playerCount = myLobby.getNumberOfSlots(); + for (int i = 0; i < playerCount; i++) { + LobbySlot playerSlot = myLobby.getSlot(i); + RegisteredPlayer player = RegisteredPlayer.forVariants( + playerCount, + Collections.singleton(gameType), + playerSlot.getDeck(), + null, + false, + null, + null + ); + LobbyPlayer lobbyPlayer = new LobbyPlayerHuman( + playerSlot.getName(), + playerSlot.getAvatarIndex() + ); + player.setPlayer(lobbyPlayer); + player.setTeamNumber(playerSlot.getTeam()); + players.add(player); + } + + final List sortedPlayers = Lists.newArrayList(players); + Collections.sort(sortedPlayers, new Comparator() { + @Override + public final int compare(final RegisteredPlayer p1, final RegisteredPlayer p2) { + final int v1 = p1.getPlayer() instanceof LobbyPlayerHuman ? 0 : 1; + final int v2 = p2.getPlayer() instanceof LobbyPlayerHuman ? 0 : 1; + return Integer.compare(v1, v2); + } + }); + + return sortedPlayers; + } + + /** + * This method is used to recursively update the tracker + * references on all objects and their props. + * + * @param objs + */ + private void updateTrackers(final Object[] objs) { + for (Object obj: objs) { + if (obj instanceof TrackableObject) { + TrackableObject trackableObject = ((TrackableObject) obj); + if (trackableObject.getTracker() == null) { + trackableObject.setTracker(this.tracker); + // walk the props + EnumMap props = (EnumMap) trackableObject.getProps(); + if (!(props == null)) { + for (Object propObj : props.values()) { + updateTrackers(new Object[]{propObj}); + } + } + } + } else if (obj instanceof TrackableCollection) { + TrackableCollection collection = ((TrackableCollection) obj); + Iterator itrCollection = collection.iterator(); + while (itrCollection.hasNext()) { + Object objCollection = itrCollection.next(); + updateTrackers(new Object[]{objCollection}); + } + } } }