fix(core): harden session, settings, and realtime lifecycle races - #95
fix(core): harden session, settings, and realtime lifecycle races#95Kiro-XVI wants to merge 2 commits into
Conversation
Stop Follow Character fighting itself, clear DM/key state across account changes, marshal confirms and incoming calls onto Framework, and fix realtime Stop/Start plus settings privacy retry storms. Co-authored-by: Cursor <cursoragent@cursor.com>
Ask/Proceed were null-refing Plugin.Framework outside the game, which broke ModerationNoticeTests on CI. Co-authored-by: Cursor <cursoragent@cursor.com>
| { | ||
| try | ||
| { | ||
| toWait.Wait(StopJoinTimeout); |
There was a problem hiding this comment.
Blocking: AethernetSession raises Changed inside RunOnFrameworkThread and CallHub.Reconcile subscribes to it, so router.Stop() runs on the Framework thread. Sign-in, sign-out, account switch and 401 now stall the game up to 2s here (CallHub.SetEnabled reaches it from the settings toggle too). The generation guard already covers the race the join was meant to close, so drop the Wait or move Stop off the main thread.
| catch (Exception exception) | ||
| { | ||
| AepLog.Warning($"Chat privacy load failed: {exception.Message}"); | ||
| chatPrivacyLoaded = true; |
There was a problem hiding this comment.
Blocking: latching on failure kills the refetch storm but makes the failure permanent for the session. The page then renders the shareReadReceipts/sharePresence defaults (both true) as authoritative, and Push sends both booleans, so flipping one silently overwrites the other server-side. Latch only when me is not null, and give the failure path a retry cooldown instead.
| catch (Exception exception) | ||
| { | ||
| AepLog.Warning($"Tag privacy load failed: {exception.Message}"); | ||
| loaded = true; |
There was a problem hiding this comment.
Blocking: same shape as PrivacyPage. After a failed load the page shows default policies as real values, and PushTags sends tagPolicy and requireTagApproval together, so touching one overwrites the other with a default. Same fix: cooldown rather than a permanent latch.
| return false; | ||
| } | ||
|
|
||
| if (localSlot < 0) |
There was a problem hiding this comment.
Non-blocking: CallHub.HandleRoster still sets state = CallState.Active when this returns false, so a roster that omits the local slot leaves the call Active with no audio session and no elapsed timer, and nothing retries until the next roster frame.
| { | ||
| session.SignIn(auth.Token, auth.User); | ||
| signedIn?.Invoke(); | ||
| _ = Plugin.Framework.RunOnFrameworkThread(() => signedIn?.Invoke()); |
There was a problem hiding this comment.
Nit: Reset() now runs before signedIn fires, so handlers observe the cleared flow state instead of the post-verify state. Also Plugin.Framework is unguarded here while ConfirmService gained a null guard in ab98f8f; worth keeping the two consistent.
| @@ -67,6 +67,5 @@ public void Run( | |||
| public void Dispose() | |||
| { | |||
| cancellation.Cancel(); | |||
There was a problem hiding this comment.
Nit: dropping cancellation.Dispose() is the right call since in-flight tasks still hold the token, but it reads as an accidental leak. A one-line why comment would stop someone re-adding it.
What
Fixes several session, settings, and telephony lifecycle bugs on master: Follow Character toggle fighting itself, decrypted DM plaintext surviving sign-out, KeyVault not refreshing on account switch, Confirm/incoming-call UI mutated off the Framework thread, RealtimeConnection Stop/Start races, privacy settings refetch storms, RegionSync no-op after SignIn, and related settings/clock/call edge cases.
Why
These bugs are present on upstream master and cause broken account follow, leftover chat plaintext after sign-out, dropped/corrupt confirm dialogs, overlapping websocket loops on account switch, and settings pages that hammer MeAsync every frame after a failed load.
Closes #
How to test
TimeText.Checklist
dotnet build -c ReleasepassesWindows/Components/widgets, nowhatcommentsMade with Cursor