Summary
Cancelling a gravship launch confirmation desyncs clients. Reproduced on vanilla Multiplayer + Odyssey with no third-party mods (async time + multifaction).
Reproduce
- Async time + multifaction game with multiple maps.
- Host presses the gravship launch button on the gravship map; the confirmation dialog appears on all peers.
- A client's camera is on a different map than the gravship map.
- Host cancels the launch.
- The client desyncs. On the client, only the gravship map stays paused while the other maps keep ticking; the host has all maps paused as expected.
Not 100% on its own, but reliable under a little load (e.g. several maps running at 3x). Observed messages: Map instances don't match and Wrong random state on map N.
Cause
PatchGravshipPreLaunchCancel.Postfix closes the session with:
GravshipTravelUtils.CloseSessionAt(Find.CurrentMap.Tile);
Cancelling is a synced command, so this runs on every peer — but Find.CurrentMap is the map each peer's camera happens to be on, i.e. local UI state, not synchronized simulation state. A peer whose camera is on another map calls CloseSessionAt with the wrong tile and never closes the GravshipTravelSession, so that map stays paused only for them. Since GravshipTravelSession.IsCurrentlyPausing(map) => map == Map gates whether the map ticks, the per-map tick counts diverge → desync.
Of the four CloseSessionAt call sites in GravshipTravelSessionPatches.cs, this is the only one using Find.CurrentMap; the others pass deterministic tiles (curTile / landingTile / takeoffTile).
Present on current master (4a3be27) and dev.
Evidence
Desync report captured with local_metadata.txt listing only Harmony + Prepatcher + DLCs (including Odyssey) + Multiplayer — no third-party mods — and Map instances don't match at the moment of cancel. Comparing the host/client saves at the desync, only the gravship map had diverged: that map alone was off by tens of ticks and thousands of rand iterations relative to the others, which were in sync. (Report/saves available on request.)
A PR fixing this follows.
Investigated and written with the help of Claude (Anthropic).
Summary
Cancelling a gravship launch confirmation desyncs clients. Reproduced on vanilla Multiplayer + Odyssey with no third-party mods (async time + multifaction).
Reproduce
Not 100% on its own, but reliable under a little load (e.g. several maps running at 3x). Observed messages:
Map instances don't matchandWrong random state on map N.Cause
PatchGravshipPreLaunchCancel.Postfixcloses the session with:Cancelling is a synced command, so this runs on every peer — but
Find.CurrentMapis the map each peer's camera happens to be on, i.e. local UI state, not synchronized simulation state. A peer whose camera is on another map callsCloseSessionAtwith the wrong tile and never closes theGravshipTravelSession, so that map stays paused only for them. SinceGravshipTravelSession.IsCurrentlyPausing(map) => map == Mapgates whether the map ticks, the per-map tick counts diverge → desync.Of the four
CloseSessionAtcall sites inGravshipTravelSessionPatches.cs, this is the only one usingFind.CurrentMap; the others pass deterministic tiles (curTile/landingTile/takeoffTile).Present on current
master(4a3be27) anddev.Evidence
Desync report captured with
local_metadata.txtlisting only Harmony + Prepatcher + DLCs (including Odyssey) + Multiplayer — no third-party mods — andMap instances don't matchat the moment of cancel. Comparing the host/client saves at the desync, only the gravship map had diverged: that map alone was off by tens of ticks and thousands of rand iterations relative to the others, which were in sync. (Report/saves available on request.)A PR fixing this follows.
Investigated and written with the help of Claude (Anthropic).