18 KiB
18 KiB
Multiplayer
Primary atlas source:
Current grounded owners:
multiplayer_window_init_globalsmultiplayer_window_service_loopmultiplayer_register_session_event_callbacksmultiplayer_dispatch_requested_actionmultiplayer_preview_dataset_service_framemultiplayer_transport_service_framemultiplayer_transport_service_worker_oncemultiplayer_transport_service_route_callback_tablesmultiplayer_transport_service_status_and_live_routes
Current bounded state blocks:
- session globals at
0x006d40d0 - active session-event transport at
0x006cd970 - preview dataset at
0x006cd8d8 - Multiplayer window backing block at
0x006d1270 - selector-view store rooted at
[transport+0xab4]
What this note is for:
- Multiplayer window and preview-dataset ownership
- Session-event transport routing
- GameSpy-facing callback and live-route semantics
- Selector-view refresh, retry, and probe state
Highest-value open edge:
- The remaining multiplayer edge is narrower now:
the status-route callback vector is bounded through selector-text or averaged-sample publication,
control-id-list seeding, scalar-query forwarding, and the validated cookie or extended-payload
callbacks. The selector-view sidecar is tighter too:
[entry+0x80]now reads as the averaged millisecond probe sample and[entry+0x54]as the displayed version/build companion integer from the localX...X|%dmarker family. Separately, the route-callback side now has its own compact GameSpy-style server or route descriptor family with primary and optional secondary endpoint tuples plus keyed fields likehostname,gamever,numplayers, andnumservers, feeding the route-binding compatibility gate. The compact decode owners are tighter too:0x5907d0is now the allocate-and-append lane for self-consistent compact payloads, while0x590d00is the keyed upsert-by-primary-endpoint lane that reuses an existing descriptor when one already matches and then notifies the transport owner callback. The route-callback-table runtime above that decode path is tighter too:0x5905e0now constructs one transport-owned callback-table block,0x5906f0tears down the decoded schema dictionary rooted at[this+0x08],0x590540/0x5905a0acquire and release refcounted shared schema strings through the global pool, and0x5908c0now reads as the live receive/decode state machine serviced by0x591290in table states2/3. The higher transport bring-up split is tighter too:0x596090now clearly constructs[transport+0xba4]through0x5905e0with owner callback0x595a40, then seeds the local field-cache family[transport+0x1724]through0x5a08f0with helper0x595b60, and then constructs[transport+0x1164]through the same0x5905e0path but with owner callback0x595bc0. The live-route lifecycle above it is tighter too:0x590740now cleanly resets one table's live route plus decode-side runtime without destroying the outer object,0x5907a0is the broader destroy path that also releases the active descriptor collection,0x590ed0opens the route handle into[this+0x4a0], stages the initial outbound request, and seeds state3plus the receive buffer,0x5911e0is the state-2/3socket-service wrapper above0x5908c0,0x5912c0is the one-shot send-with-reopen-retry helper, and0x590ea0is the shared disconnect publication and reset tail. The recurring service helper0x591290is tighter too: it now first clears the staged intrusive descriptor list through0x590490before entering the state-driven seed-or-receive branch. The upstream openers are tighter now too:0x5962e0is the field-subscription route-table owner above[transport+0xba4], while0x596530is thegsi_am_ratingreopen path above[transport+0x18bc]. On that latter branch,0x590dc0is now bounded as the state-0raw-endpoint seed pass over the live route handle, repeatedly pulling endpoint tuples through0x58bc7erecord type0x1f3before stamping descriptor flag byte0x15with0x11. That makes the remaining flag-bit question much narrower too: current evidence now supports reading byte-0x15bit0x1as a primary-endpoint-seed or endpoint-only source marker. In thegsi_am_ratingdispatcher, clear-bit descriptors can take the richer direct transition lane, while set-bit descriptors are pushed through the queued enrichment path and later still suppress that direct transition even when the ready bit is present. The adjacent capacity-descriptor side is tighter too:0x595bc0no longer reads as a vague progress helper. Mode0is now clearly the live publish lane, and it lines up with the generic descriptor append-notify owner callback at0x590370: it sampleshostname,numwaiting,maxwaiting,numservers, andnumplayersfrom the current descriptor before publishing an opcode-2descriptor block, while still carrying three cached side scalars from the local capacity sidecar at[transport+0x1778]. That sidecar is tighter now too: current evidence says it behaves as one cached pointer into the transient work-record family at[transport+0x1780], because every meaningful branch in0x595bc0reads the same+0x0c/+0x10/+0x18metadata triplet and the replay modes later consume the pointer through0x5933a0. The negative result is stronger too: local text-side xrefs still show no direct store to[transport+0x1778], and a wider sweep also failed to show any obviouslea-based replay-band writer. So the sidecar writer remains upstream of this leaf capacity publisher. The payload split is tighter too:0x592ae0now grounds opcode2as a seven-dword descriptor payload with an owned string slot at+0x08, so live mode supplies a populated payload while modes3and5deliberately enqueue an all-zero payload and reuse only the sidecar metadata in the wrapper. Those two modes are tighter now too: they are not generic replay guesses, they are the live receive-state owner callbacks emitted by0x5911e0 -> 0x5908c0. So they are best read as delayed metadata replays over one cached work record, not over a standalone custom cache object. The producer side is tighter now too: callback-table attach, bound-route requests, selector-text route requests, and the type-9text fastpath all stage that same+0x0c/+0x10/+0x18triplet through0x5934e0, so the capacity replay sidecar is clearly reusing one broader transport work-record family. The generic owner-callback split above that family is tighter now too:0x590370is the shared append-notify lane in mode0, while0x590430is the distinct remove-notify-and-stage lane in mode2. So0x595bc0only owns the live append-style publish path plus delayed replay modes3/5, not the remove path. The neighboring transient work queue is tighter too:0x593330/0x593370/0x593380now bound[transport+0x1780]as a real construct/clear/destroy collection owner, while0x5933a0,0x5934e0, and0x593570now ground the remove, allocate, and completion side over that same family. The small sibling0x593400is tighter too: it is a pure uniqueness predicate over work-record field+0x0c. Its caller is tighter now too:0x58d720is an immediate-drain quiescence gate over one transport context id, using0x593400for the queued work family at[transport+0x1780]and0x592970for the active opcode-record collection at[transport+0x17fc]. The strongest current read is that0x5934c0seeds that shared drain context id first, then the transport copies it into queued work field+0x0cand active opcode-record field+0x14before the immediate-drain roots wait on one shared disappearance test rather than one vague “drain until something settles” loop. The currently grounded roots are0x58df20, the neighboring formatted selector-text publish path at0x58dfb0, and callback-table registration at0x58e200. The callback-table attach side now has a tighter active-opcode owner stack too:0x5927b0services and retires one active opcode record,0x592800performs the broader context-or-idle retirement sweep,0x5929a0removes active records by opcode type, and0x5929f0removes only opcode-3field-snapshot records keyed by the subscribed callback-pair payload. That also corrects0x595b80: its final cleanup is not a queued field-snapshot drain, but an active opcode-type-3purge beneath the field cache reset. The adjacent route-callback descriptor-table lifecycle is tighter too:0x590410now grounds[table+0x5bc]as a real staged intrusive descriptor-list head,0x590430is the generic remove-notify-and-stage path,0x590490releases that staged list, and0x5904d0releases the active descriptor collection before tearing the staged list down. That also tightens0x5962e0: its earlier “release active descriptors” step is now explicitly this same0x5904d0family, not a vague collection clear. The callback-table attach side now constrains the same work-record metadata family a little further too:0x593650deliberately duplicates one caller metadata dword into both work fields+0x0cand+0x18, while preserving the remaining caller callback function pointer in+0x10. The lower opcode wrappers are tighter now too: both0x592a40and0x592a70consume that staged triplet in the order( callback fn +0x10, callback companion +0x18, drain context id +0x0c ). So the replay-side triplet is clearly a broader transport callback-wrapper family, not one fixed route-only tuple. The type-9text fastpath confirms the same split from the other side too:0x593d00only emits the follow-on callback lane when work field+0x10is nonnull, and then forwards(+0x10, +0x18, +0x0c)into0x593170as callback function, callback companion, and trailing drain context. The nearby field-subscription side is tighter too:0x592b50now clearly uses[transport+0x1774]as a cached progress percentage under the[transport+0xba4]callback table, and0x5962e0seeds that percentage to1just before the first immediate mode-3snapshot. The nearby route-callback-table lifecycle is tighter now too:0x596090seeds[transport+0xba0]as the callback-plumbing enable latch, clears staged payload slot[transport+0xb50], and constructs the three owner branches rooted at[transport+0xba4],[transport+0x1164], and[transport+0x18bc]. The matching local cleanup is tighter too:0x5962c0is the explicit staged route-callback payload clear on[transport+0xb50], while0x595ce0now clearly resets only the capacity-descriptor route callback table at[transport+0x1164], not the field-subscription table at[transport+0xba4]. The only meaningful gap left on the capacity side is the still-unrecovered writer that stages[transport+0x1778]. The carried sidecar fields are no longer anonymous: current evidence now says they are just the same cached callback-wrapper triplet reused by other work-record families, namely drain context id+0x0c, callback function+0x10, and callback companion+0x18. The negative result is stronger now too: the neighboring replay-band fields[transport+0x176c],[transport+0x1770],[transport+0x1774],[transport+0x177c],[transport+0x1780], and[transport+0x1784]all have direct local lifecycle owners, but[transport+0x1778]still only appears as the single read in0x595bc0. So the remaining writer is upstream and indirect rather than one ordinary direct field store in the local text cluster. One adjacent staged-route callback is tighter now too:0x595860is the submit-result handler below0x5958e0, using the already-grounded third selector-generation counter at[transport+0xac0]plus the target at[transport+0xb48]to choose whether staged route-callback traffic can advance the route-mode state machine from mode2into3and later4. The counter beneath that decision is tighter too:0x594e30now reads as a selector-view entry counter for slot2flag bit0x20, optionally filtered by the current transport name buffer. The selector-view mutation side beneath that callback family is tighter too:0x594a30is now the direct keyed store remover,0x594fb0clears one selector-slot ownership pointer plus its slot-local flag dword and drops the whole entry when no slots remain,0x595010rekeys one selector-view entry under a new name while preserving the0x40..runtime band, and callback root0x59f9c0is now bounded as the lane that clears one named selector-view slot, publishes callback slot18, and can still re-enter the route-mode setter from the same slot-2state and generation gates. The next adjacent callback roots are tighter now too:0x5950a0clears one selector slot from every selector-view entry in the keyed store,0x59fab0is the rename or relabel sibling above0x595010,0x59faf0updates one selector slot's fixed sample-text buffer and refreshes the active selector object when present, and0x59fb60replaces one selector slot's name set, requests the default profile-key bundle for that slot, and publishes callback slot20. Slot16is tighter now too: current grounded caller0x59f440forwards the staged route-callback payload handle from[transport+0xb50]through0x592ea0just before route mode5. The last adjacent callback root in that block is tighter now too:0x59fbd0is the built-in per-slot profile-key query sibling. It resolves the caller selector name into one slot index, forwards the caller trio into0x596b90, and then publishes callback slot28; that lower helper indexes one slot-specific built-in string pair from[transport+0x189c]and[transport+0x18ac], reuses the generic per-key handler0x596970, and only republishes slot28when that lower query path succeeds. The compact-header side is tighter too:0x58fe20and0x58ff20now show that compact payloads always carry the primary IPv4 dword and that header bit0x10only gates whether the primary port word is inline or inherited from the owner default port. The variable tails are tighter too:0x58fe90now validates the0x40inline keyed-property vector against the owner schema, and0x58fe50validates the signed-0x80trailing string-pair tail before decode.0x58ff60then grounds bit0x02as the inline secondary IPv4 dword branch, bit0x20as the paired secondary-port word branch with owner-port fallback, bit0x08as one still-unresolved auxiliary inline dword stored at[descriptor+0x10], bit0x40as one inline keyed-property vector decoded through the property-store writers, and signed bit0x80as one trailing string-pair tail. The descriptor-state side is tighter too: queue insertion now cleanly splits pending tags0x4/0x8from serviced-ready bits0x1/0x2, and thegsi_am_ratinglane now separates direct primary-endpoint handling from queued-descriptor handling. Current evidence now also says byte[descriptor+0x14]is not queue-only:0x58ff60can also OR in bit0x1after the inline keyed-property vector and bit0x2after the signed string-pair tail. Byte[descriptor+0x15]bit0x1is source-side too, not queue-generated: it is explicitly seeded by the primary-endpoint refresh path and preserved by the compact descriptor decode path before the transport later uses it in thegsi_am_ratinglane both to choose direct-vs-queued handling and to suppress the direct0x595dc0transition even after ready bit0x2is present. The descriptor body is tighter too:[descriptor+0x20]is now the intrusive next-link used by the transport-owned primary-endpoint list, and[descriptor+0x1c]is now the special numeric scalar behind the currentqueryid/pingfallback pair. The compact-only auxiliary dword at[descriptor+0x10]is tighter in a different way: a local xref scan now shows it being preserved by later generic preservation helpers such asgeneric_record_0x1c_deep_copy_with_owned_string_at_0x080x591410and the callback-marshaling wrappers0x591480and0x591510, but still no dedicated semantic reader has been recovered. So the main remaining uncertainty is the exact higher-level meaning of header bit[descriptor+0x15] & 0x1, plus whether[descriptor+0x10]ever matters outside that preservation path. The route-mode setter is clearer too:0x595650now reads as a compact state machine over the transport-owned route tables, where mode0splits thegsi_am_ratingbranch into direct primary-endpoint handling versus queued insertion, mode1adds the ready-bit gate and the same queued fallback, mode2removes pending descriptors from the queued family, mode3forces mode2when the primary-endpoint table is empty, mode4updates the deferred route-status state around[this+0x1ed4]and[this+0x1ed8], and mode5copies the staged route companion dword at[this+0x490]into[this+0x54]while mirroring that value into the local field-cache family. The route-event dispatcher side is cleaner too: the mode-5tails in both callback families do not copy a descriptor-local field. They mirror the transport-staged companion dword at[this+0x490]into[this+0x54]and the local field-cache family instead. Thegsi_am_ratingmaintenance lane is tighter now too: it sorts the primary-endpoint descriptor table through0x590310in mode1with keygsi_am_rating, then selects the new head through0x590480rather than treating the table as pure insertion order.