Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions forge-gui/src/main/java/forge/gamemodes/match/GameLobby.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ public final boolean isMatchActive() {
return hostedMatch != null && hostedMatch.isMatchOver() == false;
}

public HostedMatch getHostedMatch() {
return hostedMatch;
}

public void setListener(final IUpdateable listener) {
this.listener = listener;
}
Expand Down
10 changes: 10 additions & 0 deletions forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ public class HostedMatch {

public HostedMatch() {}

/**
* Look up the IGuiGame for a given Player from the guis map.
* This is the authoritative source for the GUI assigned to each player,
* unlike PlayerControllerHuman.getGui() which may be overwritten.
*/
public IGuiGame getGuiForPlayer(final Player player) {
if (guis == null || player == null) { return null; }
return guis.get(player.getRegisteredPlayer());
}

public void setStartGameHook(Runnable hook) {
startGameHook = hook;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ public ClientGameLobby getLobby() {
public void send(final NetEvent event) {
if (event instanceof MessageEvent) {
final MessageEvent message = (MessageEvent) event;
if (server.handleCommand(message.getMessage())) {
return;
}
chatInterface.addMessage(new ChatMessage(message.getSource(), message.getMessage()));
server.broadcast(event);
}
Expand Down
14 changes: 14 additions & 0 deletions forge-gui/src/main/java/forge/gamemodes/net/ReplyPool.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,20 @@ public Object get(final int index) throws TimeoutException {
}
}

/**
* Cancel all pending replies by completing them with null.
* This is used when a player is converted to AI to unblock any waiting game threads.
*/
public void cancelAll() {
synchronized (pool) {
for (CompletableFuture future : pool.values()) {
// Complete with null to unblock waiting threads
future.set(null);
}
pool.clear();
}
}

private static final class CompletableFuture extends FutureTask<Object> {
public CompletableFuture() {
super(() -> null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ final class GameClientHandler extends GameProtocolHandler<IGuiGame> {
private Tracker tracker;
private Match match;
private Game game;
private GameView pendingGameView;

/**
* Creates a client-side game handler.
Expand Down Expand Up @@ -68,6 +69,14 @@ protected IGuiGame getToInvoke(final ChannelHandlerContext ctx) {
@Override
protected void beforeCall(final ProtocolMethod protocolMethod, final Object[] args) {
switch (protocolMethod) {
case setGameView:
// Capture the GameView synchronously on the IO thread.
// The actual gui.setGameView() runs on EDT (queued by channelRead),
// so gui.getGameView() may still be null when openView arrives next.
if (args.length > 0 && args[0] instanceof GameView) {
this.pendingGameView = (GameView) args[0];
}
break;
case openView:
gui.setNetGame();

Expand Down Expand Up @@ -145,6 +154,13 @@ private Match createMatch() {
final IGuiGame gui = client.getGui();
GameView gameView = gui.getGameView();

// gui.getGameView() may be null because setGameView was queued to EDT
// but hasn't executed yet. Fall back to the GameView captured synchronously
// in beforeCall when the setGameView event arrived.
if (gameView == null) {
gameView = this.pendingGameView;
}

final GameType gameType = getGameType();
final GameRules gameRules = createGameRules(gameType, gameView);
final List<RegisteredPlayer> registeredPlayers = createRegisteredPlayers(gameType);
Expand Down
Loading