diff --git a/Source/Client/Patches/GravshipTravelSessionPatches.cs b/Source/Client/Patches/GravshipTravelSessionPatches.cs index 4939f821c..5543893c1 100644 --- a/Source/Client/Patches/GravshipTravelSessionPatches.cs +++ b/Source/Client/Patches/GravshipTravelSessionPatches.cs @@ -38,7 +38,13 @@ static void Postfix(Dialog_MessageBox __instance) if (Multiplayer.Client == null) return; if (!Multiplayer.ExecutingCmds) return; - GravshipTravelUtils.CloseSessionAt(Find.CurrentMap.Tile); + // Cancelling is a synced command, so this runs on every peer. Using Find.CurrentMap here is + // non-deterministic: it's the map each peer's camera happens to be on, not synchronized state. + // A peer whose camera is on another map would call CloseSessionAt with the wrong tile and never + // close the gravship session, leaving that map paused only for them -> the per-map tick counts + // diverge and the game desyncs. There is exactly one open GravshipTravelSession at prelaunch time, + // so close it by looking it up rather than by the local camera. + GravshipTravelUtils.CloseAllSessions(); GravshipTravelUtils.CloseGravshipPrelaunchDialog(); } } diff --git a/Source/Client/Persistent/GravshipTravelSession.cs b/Source/Client/Persistent/GravshipTravelSession.cs index 92461b7b8..8e8de83e4 100644 --- a/Source/Client/Persistent/GravshipTravelSession.cs +++ b/Source/Client/Persistent/GravshipTravelSession.cs @@ -57,6 +57,22 @@ public static void CloseSessionAt(PlanetTile tile) } } + // Closes every open GravshipTravelSession, independent of the local camera (Find.CurrentMap). + // Used by the prelaunch-cancel path, which runs as a synced command on all peers: closing by the + // local camera's tile is non-deterministic and leaves the session (and thus the map's pause) alive + // on peers looking at another map. There is only ever one such session while a launch is pending. + public static void CloseAllSessions() + { + foreach (var sessionManager in Multiplayer.game.mapComps.Select(mp => mp.sessionManager)) + { + foreach (var session in sessionManager.AllSessions.OfType().ToList()) + { + session.Map.MpComp()?.sessionManager?.RemoveSession(session); + session.StopPausing(); + } + } + } + public static bool TryGetSessionAt(PlanetTile takeoffTile, out GravshipTravelSession session) { session = null;