Skip to content

Isolate RNG in the gravship launch ritual's reachability check - #968

Open
cmlee119 wants to merge 1 commit into
rwmt:masterfrom
cmlee119:fix/gravship-launch-role-check-rand-desync
Open

Isolate RNG in the gravship launch ritual's reachability check#968
cmlee119 wants to merge 1 commit into
rwmt:masterfrom
cmlee119:fix/gravship-launch-role-check-rand-desync

Conversation

@cmlee119

Copy link
Copy Markdown

Fixes #967.

Problem

Building the gravship launch confirmation dialog decides which pawns can board, in Dialog_BeginRitual's constructor:

Dialog_BeginRitual..ctor -> CreateRitualRoleAssignments
  -> RitualRoleAssignments.PawnNotAssignableReason
  -> RitualBehaviorWorker_GravshipLaunch.PawnCanFillRole
  -> CanReachGravship -> GravshipUtility.TryFindSpotOnGravship
  -> Region.RandomCell -> Rand.Range        // consumes RNG

Building the dialog is UI work opened only by the issuing peer (the currentExecutingCmdIssuedBySelf gate in CancelDialogBeginRitual), so this RNG runs on just one peer and advances the shared stream there only. Spamming the launch button repeats it and the divergence accumulates into a desync (Random state from commands doesn't match). Measured: with nothing ticking, the gravship map alone was off by +11,617 rand iterations, all on the peer that pressed launch.

Fix

Wrap RitualBehaviorWorker_GravshipLaunch.CanReachGravship in Rand.PushState / PopState, matching the existing SeedPreceptComp_UnwillingToDo_Chance isolation in the same file for RNG that "can be called in interface". CanReachGravship returns a bool that does not depend on the RNG (it only searches for any allowed cell), and the real boarding calls TryFindSpotOnGravship outside this check, so isolating here leaves both the check result and determinism intact.

Testing

  • Reproduced on vanilla Multiplayer + Odyssey (async time + multifaction, no third-party mods): spamming the launch button desyncs a client with Random state from commands doesn't match.
  • With this fix, ~100 rapid clicks produce no desync.
  • Source/Client builds clean (Release).

Investigated and written with the help of Claude (Anthropic).

Building the gravship launch confirmation dialog decides which pawns can
board in Dialog_BeginRitual's constructor: CreateRitualRoleAssignments ->
RitualRoleAssignments.PawnNotAssignableReason ->
RitualBehaviorWorker_GravshipLaunch.PawnCanFillRole -> CanReachGravship ->
GravshipUtility.TryFindSpotOnGravship -> Region.RandomCell, which consumes
RNG. Building the dialog is UI work opened only by the issuing peer (the
currentExecutingCmdIssuedBySelf gate in CancelDialogBeginRitual), so that
RNG runs on just one peer and advances the shared stream on that peer only.
Spamming the launch button repeats it and the divergence accumulates into a
desync ("Random state from commands doesn't match").

Wrap CanReachGravship in Rand.PushState/PopState, matching the existing
SeedPreceptComp_UnwillingToDo_Chance isolation for RNG that "can be called in
interface". CanReachGravship returns a bool that does not depend on the RNG
(it only searches for any allowed cell), and the real boarding calls
TryFindSpotOnGravship outside this check, so isolating here leaves both the
check result and determinism intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@notfood notfood added fix Fixes for a bug or desync. 1.6 Fixes or bugs relating to 1.6 (Not Odyssey). odyssey Fix or bugs relating to Odyssey (Not 1.6) labels Jul 27, 2026
@notfood notfood moved this to In review in 1.6 and Odyssey Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1.6 Fixes or bugs relating to 1.6 (Not Odyssey). fix Fixes for a bug or desync. odyssey Fix or bugs relating to Odyssey (Not 1.6)

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

Desync when spamming the gravship launch button (RNG in CanReachGravship not isolated)

2 participants