Skip to content

Serialize commander bracket to fix network client game-start crash#11164

Open
MostCromulent wants to merge 2 commits into
Card-Forge:masterfrom
MostCromulent:fix-network-commander-bracket-crash
Open

Serialize commander bracket to fix network client game-start crash#11164
MostCromulent wants to merge 2 commits into
Card-Forge:masterfrom
MostCromulent:fix-network-commander-bracket-crash

Conversation

@MostCromulent

Copy link
Copy Markdown
Contributor

Closes #11135.

Summary

Fixes a crash that stranded non-host players on the lobby screen when a network Commander game started.

Bug report

The host started the game, but the client stayed in the lobby while the match ran on the server (card sounds audible, no board). The client log showed a NullPointerException during openView:

NullPointerException: Cannot invoke "forge.game.Match.getPlayers()"
    because the return value of "forge.game.GameView.getMatch()" is null
  at forge.game.GameView.getDeck(GameView.java:256)
  at forge.screens.match.views.VField.getCommanderBracketTooltipLine(VField.java:457)
  at forge.screens.match.views.VField.getPlayerDetailsHtml(VField.java:423)
  at forge.screens.match.views.VField.updateDetails(VField.java:418)
  at forge.screens.match.views.VField.<init>(VField.java:162)
  at forge.screens.match.CMatchUI.initMatch(CMatchUI.java:388)
  at forge.screens.match.CMatchUI.openView(CMatchUI.java:1225)

Root cause

The commander-bracket tooltip added in #10803 calls GameView.getDeck(player), which dereferences GameView's transient, server-only match field — null on every network client by design (registered decks live only on the host). The tooltip NPEs as the client builds its VFields, aborting match-screen setup. It only triggers when the client's DECKGEN_MAXIMUM_COMMANDER_BRACKET is 1–4; the default short-circuits before the deck is touched.

Fix

The host computes each player's bracket at game start and publishes it as a serialized PlayerView property, which the tooltip reads instead of the deck. VField no longer calls getDeck, so the crash is gone and the bracket displays on clients in parity with the host.

Why this is architecturally consistent

  • Mirrors how GameView already serializes its other match-derived data (title, games-played, match-over) as TrackableProperty values, instead of reaching through the transient match field.
  • Only the resulting integer crosses the wire — no decklist — so no hidden information is exposed. Computed host-side, where CommanderBracketCalculator (a forge-gui class forge-game can't depend on) lives.

🤖 Generated with Claude Code

The commander-bracket tooltip called GameView.getDeck(), which
dereferences the server-only transient match field and NPEs on network
clients — crashing during openView and freezing the client in the lobby.
Compute the bracket host-side and serialize it as a PlayerView property
instead of reaching through the deck, so clients get the value directly.
Addresses <!-- -->Card-Forge#11135.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tool4ever

Copy link
Copy Markdown
Contributor

looks like the same crash happens if remote client calls viewDeckList() from Game Menu

can't we better access GameLobby since both sides should already be able to reach decks this way?

Hanmac
Hanmac previously approved these changes Jul 6, 2026
Clients already receive every player's deck in the lobby broadcast, so
resolve decks from ClientGameLobby instead of the server-only match
(getMatch() is transient and null after deserialization). This fixes both
the commander bracket tooltip and the Game Menu deck list, which shared
the same getDeck NPE on clients.

The tooltip stayed hidden for a second reason: it is gated on the
maximum-bracket preference, but that control lives only in the host's
lobby start panel. A client never sets it, so its local copy stays at the
default of 5 (off - the gate wants 1-4) and never passes. The host's value
now travels in the lobby state, so clients gate the tooltip on the host's
setting and both sides display identically.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Development

Successfully merging this pull request may close these issues.

Multiplayer game doesnt start due to nullpointer

3 participants