Skip to content

fix(gateway): keep players connected when a move's session retires before activation - #340

Merged
oglassdev merged 5 commits into
mainfrom
fix/move-into-retiring-session
Oct 13, 2026
Merged

oglassdev merged 5 commits into
mainfrom
fix/move-into-retiring-session

Conversation

@oglassdev

@oglassdev oglassdev commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #292. When a player was moved into a session at the moment it retired through scope.finish(), control failed the activation with stale activation and the gateway disconnected the player. Players now keep their connection.

What the player sees

When the destination session retires Result
Before the gateway starts configuration The move fails as a move, and the player stays in play on the source session. Their source claim is never fenced.
During or after the configuration handshake (the issue's reproduction) The player keeps their connection and is routed again as a login.
On a first login, while claiming or before activation The login is claimed again, under a new operation ID.

The issue asked for the player to stay on their source session. That is not reachable in the second case: by the time activation runs, the move has already released the source claim and the client has left play state for configuration. Returning them to the source would need the JVM to take a player back after configuration, or activation before the source is fenced. This PR does not attempt either.

How

  • Control reports a retired destination as its own outcome, DESTINATION_RETIRED, when claiming and when activating. A claim is lost to retirement when it never admitted its player, control did not cancel it while its session still ran, and its session is retired now. That is read from the session when asked, so it does not depend on the order a JVM report's deliveries and sessions are applied in. The only new state is Claim.canceled, set where control itself begins a withdrawal.
  • A stale generation, a claim control canceled, and a claim a newer claim has superseded stay fatal.
  • On the wire, ActivateResult gains retired and ClaimRefusal gains DESTINATION_RETIRED, so the gateway does not match on error strings.
  • The gateway re-checks a move before starting configuration. If the move vanishes afterwards because its destination retired, it records the move as failed, withdraws the retired claim, and claims a destination again as a login on the same connection. It reroutes at most once per arrival; a second retirement or a failed reroute disconnects with "Server temporarily unavailable". Activation and the reroute share one wait of at most 45 seconds.

Worth a reviewer's attention

  • A rerouted player gets login semantics: PlayerLogin and route hooks run again under a new membership, and no PlayerDisconnect fires for the old one.
  • A connected player whose move failed for another reason, and whose destination has also retired, is rerouted rather than disconnected, as long as no newer claim owns them.
  • A retired roster destination reroutes each member individually.
  • The client sits in configuration without keepalives while the withdrawal and the new claim run, as existing cutover waits do.
  • examples/arena still resets an emptied arena instead of finishing it. Reverting that is a behaviour change to the example, left for later.

Tests

  • One JVM report that closes a delivery and ends its session together leaves the move retired.
  • Members of a roster whose session retires are told so, not that their move was abandoned.
  • A login whose delivery closes with its session while control prepares it is retired, and the gateway claims again.
  • A claim canceled while its session ran stays stale when the session retires later; a retired claim stops being reported once a newer claim owns the player.
  • Through the gateway's serve loop with a client and a fake JVM: a move whose destination retires during configuration keeps the player connected on a new login.

Checks

cargo test -p chunk-control (91 passed), cargo test -p chunk-proxy (84 passed), cargo test -p chunk-environment --lib (193 passed, 2 ignored, run before the last two commits), cargo clippy --all-targets --all-features -D warnings and cargo fmt --check on the touched crates, buf lint and buf format.

🤖 Generated with Claude Code

@oglassdev
oglassdev force-pushed the fix/move-into-retiring-session branch from 4823828 to 2fe795a Compare October 12, 2026 23:54
…fore activation

Control refuses to claim a reservation whose session retired, so a move still preparing fails as a move and its player stays active on the source. Activation of a claim whose session retired first is reported as a distinct outcome instead of a stale activation; the gateway then withdraws the claim, records the move as failed and routes the player as a login on the same connection, since the move already fenced its source. A stale generation or a claim withdrawn while its session runs is still rejected.

Fixes #292
…m or configuration

Record on a claim that its session retired, so activation reports retirement only for claims it ended and not for cancelled, abandoned or superseded ones. A login whose session retires while claiming is refused with a new claim refusal and claimed again under a new operation ID. A move whose claim ends before configuration is abandoned, and one whose destination retires during configuration is routed again as a login.
…one report, in a roster, mid-claim and mid-configuration
@oglassdev
oglassdev force-pushed the fix/move-into-retiring-session branch from 2fe795a to ea452f0 Compare October 13, 2026 00:16
@oglassdev
oglassdev merged commit 7a1f97a into main Oct 13, 2026
13 checks passed
@oglassdev
oglassdev deleted the fix/move-into-retiring-session branch October 13, 2026 00:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gateway: a move into a retiring session disconnects the player with "stale activation"

1 participant