Skip to content

fix(exit): flush 0x03 Redirect before WorldState cleanup races the socket#335

Merged
Caeldeth merged 1 commit into
mainfrom
fix/exit-redirect-flush
May 19, 2026
Merged

fix(exit): flush 0x03 Redirect before WorldState cleanup races the socket#335
Caeldeth merged 1 commit into
mainfrom
fix/exit-redirect-flush

Conversation

@Caeldeth

@Caeldeth Caeldeth commented May 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

On PacketHandler_0x0B (endSignal=0), the 0x03 Redirect packet was queued after Map.Remove(user) had already enqueued a 0x0E AoiDeparture, and with the default transmitDelay = 1200ms. Two compounding problems:

  1. FlushSendBuffer breaks out when it hits a delayed packet after buffering a no-delay one — so the 0x03 was always deferred to the next 50ms ProcessOutbound tick rather than going out in the immediate flush that Client.Redirect's Enqueue(flush: true) was trying to force.
  2. When the deferred 0x03 did get picked up, its 1200ms Task.Delay easily outlived the live-socket window. Eager-closing clients (or downstream socket teardown from the cleanup below this line) meant the Redirect never reached the wire.

The visible client symptom: a spurious "Connection Lost" popup on Exit Game because the client interpreted the silent socket close as an unexpected disconnect rather than a logout-and-redirect.

Fix

Two changes in one block (World.cs):

  • Reorder so SendRedirect runs first. Its flush: true Enqueue triggers an immediate FlushSendBuffer against an empty queue — the 0x03 is the only packet, so the "break on delayed packet after no-delay" rule doesn't fire.
  • Drop transmitDelay from 1200 to 50 so the inter-packet hold is short enough that the live-socket window comfortably covers it.
user.SendRedirect(this, Game.Login, user.Name, false, transmitDelay: 50);
user.UpdateLogoffTime();
user.Map.Remove(user);
// ... rest of cleanup

Diagnosis trail

Verified via client-side packet log (Chaos.Client notice-debug.log). Before fix:

outbound 0x0B [0]   ← client confirm
inbound  0x0E       ← AoiDeparture
World -> Disconnected   (~50-100ms after confirm)

No 0x03 ever arrives at the client. After this fix the 0x03 should appear ahead of the disconnect and the client's normal redirect flow takes over.

Test plan

  • Smoke test: log in to world, click Exit Game in client, verify clean return to login (no "Connection Lost")
  • Verify other Redirect paths still work (heartbeat-fail kicks, character-switch via Logoff, login→world transition)
  • No regression to the endSignal=1 (query) branch

🤖 Generated with Claude Code

…cket

PacketHandler_0x0B (endSignal=0) previously enqueued the Redirect with
the default 1200ms TransmitDelay *after* Map.Remove had already
enqueued a 0x0E AoiDeparture. Two problems compound:

1. FlushSendBuffer breaks when it hits a delayed packet after buffering
   a no-delay one — so the 0x03 was always deferred to the next 50ms
   ProcessOutbound tick rather than going out in the immediate flush
   that Client.Redirect's Enqueue(flush: true) tried to force.

2. When the deferred 0x03 *did* get picked up, its 1200ms Task.Delay
   easily outlived the live-socket window. Clients that close the
   connection eagerly after the confirm packet (or that the server
   itself tore down via the cleanup below this line) never saw the
   Redirect at all — the client interpreted the silent socket close as
   an unexpected disconnect and showed "Connection Lost".

Fix: send the Redirect first, with a short TransmitDelay (50ms) so the
immediate flush from Enqueue(flush: true) actually picks it up. The
rest of the cleanup (Map.Remove, AuthInfo state, Save, RemoveUser)
follows after the 0x03 is already in flight.

Verified diagnosis via client-side packet log: after the fix is
deployed, the client should see `inbound opcode=0x03` ahead of the
disconnect, follow the redirect normally, and land at the login screen
without a "Connection Lost" popup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented May 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 31.84%. Comparing base (d4e7e52) to head (2b24087).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #335      +/-   ##
==========================================
+ Coverage   31.79%   31.84%   +0.04%     
==========================================
  Files         339      339              
  Lines       26485    26485              
  Branches     3681     3681              
==========================================
+ Hits         8422     8433      +11     
+ Misses      17349    17337      -12     
- Partials      714      715       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Caeldeth Caeldeth merged commit 3c4c5b5 into main May 19, 2026
4 checks passed
@Caeldeth Caeldeth deleted the fix/exit-redirect-flush branch May 19, 2026 17:56
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.

2 participants