54 KiB
54 KiB
Multiplayer Session and Transport Flow
- Roots:
multiplayer_window_init_globalsat0x004efe80,multiplayer_window_service_loopat0x004f03f0, the Multiplayer.win session-event callback table built bymultiplayer_register_session_event_callbacksat0x0046a900, the active session-event transport object at0x006cd970, the Multiplayer preview dataset object at0x006cd8d8, and the lower pending-template and text-stream helpers around0x00597880..0x0059caf0. - Trigger/Cadence: shell-owned Multiplayer.win frame service plus event-driven session callbacks and repeated transport pump steps.
- Key Dispatchers: session-event wrappers for actions
1,2,4,7,8;multiplayer_register_session_event_callbacks;multiplayer_dispatch_requested_action;multiplayer_reset_preview_dataset_and_request_action;multiplayer_preview_dataset_service_frame;multiplayer_load_selected_map_preview_surface;multiplayer_flush_session_event_transport;multiplayer_transport_service_frame;multiplayer_transport_service_worker_once;multiplayer_transport_service_route_callback_tables;multiplayer_transport_service_status_and_live_routes;multiplayer_transport_text_stream_service_io;multiplayer_transport_dispatch_pending_template_node;multiplayer_transport_service_pending_template_dispatch_store. - State Anchors: live session globals at
0x006d40d0, active session-event transport object at0x006cd970, status latch at0x006cd974, session-event mode latch at0x006cd978, retry counter at0x006cd984, preview dataset object at0x006cd8d8, preview-valid flag at0x006ce9bc, staged preview strings at0x006ce670and[0x006cd8d8+0x8f48], Multiplayer.win backing block at0x006d1270, selector-view store root at[transport+0xab4], selector-view generation counters at[transport+0xab8]..[transport+0xac0], selector-view backing arrays around[transport+0xad0]..[transport+0xae4], selector-view entry probe-request id at[entry+0x50], live-state gate at[entry+0x58], third-slot flag word at[entry+0x64], probe-schedule tick at[entry+0x68], last-success tick at[entry+0x6c], pending-probe latch at[entry+0x74], success generation at[entry+0x78], consecutive-failure counter at[entry+0x7c], rolling average at[entry+0x80], recent sample window at[entry+0x84..], bounded sample-count at[entry+0x94], total-success count at[entry+0x98], pending-template list at[transport+0x550], dispatch store at[worker+0x54c], and text-stream buffers rooted under[worker+0x1c]. - Subsystem Handoffs: the Multiplayer.win initializer seeds the backing block at
0x006d1270, later reset paths construct the separate preview dataset at0x006cd8d8, and the shell-owned active-mode frame services that dataset every frame throughmultiplayer_preview_dataset_service_frame. That preview side publishes roster and status controls through the Multiplayer window control paths, loads.gmtpreview surfaces throughmultiplayer_load_selected_map_preview_surface, and is even reused by the map/save coordinator’s mode-11.gmtpath when the dataset already exists. The preview owner now has a tighter internal request split too. The fixed selector-descriptor list at[0x006cd8d8+0x8f28]is built throughmultiplayer_preview_dataset_append_named_callback_descriptor0x00469930, whose current grounded owner is the fixed registration blockmultiplayer_preview_dataset_register_fixed_named_callback_descriptors_1_to_100x00473a60; the variable-size named UI-request list at[0x006cd8d8+0x8f2c]is built throughmultiplayer_preview_dataset_append_named_ui_request_record0x00469a50; and the staged profile/path strings live at[0x006cd8d8+0x8e10]and[0x006cd8d8+0x8f48], with the broader action-2staging path now bounded bymultiplayer_preview_dataset_stage_profile_text_selected_path_and_sync_session_state0x0046a030. The companion action-3commit path is tighter too:multiplayer_preview_dataset_match_named_field_slot_copy_profile_text_and_probe_selection0x0046a110now sits directly above the lazily initialized0x80 * 0x11cfield-slot table at[0x006cd8d8+0x10], whose owner ismultiplayer_preview_dataset_ensure_field_slot_table_initialized0x0046a1a0. The shared submitter above those lists is now explicit too:multiplayer_preview_dataset_submit_transport_request0x00469d30accepts the caller’s request-id, selector, payload pointer and length, flag word, and optional auxiliary pointer, optionally allocates one sidecar object, and then either sends the request directly through the session-event transport or takes the slower packed branch through0x00553000/0x00552ff0into0x00521dc0. The constructor and teardown side are tighter now too.multiplayer_preview_dataset_construct_reset_globals_and_seed_callback_owners0x0046be80is the real reset owner above the action-2/3branches inmultiplayer_dispatch_requested_action: it re-enters the paired release bodymultiplayer_preview_dataset_release_owned_lists_transients_and_session_side_state0x0046bc40, clears the surrounding launcher globals, allocates the field-slot table and the keyed request/descriptor owners, seeds the render target and staging buffers, and then callsmultiplayer_preview_dataset_reset_global_callback_state_and_register_selector_handlers0x00473280plus the smaller fixed-name blockmultiplayer_preview_dataset_register_fixed_named_callback_descriptors_1_to_100x00473a60. That broader registration pass0x00473280now bounds the selector-handler family too: it zeroes the global scratch bands0x006ce808..0x006ce994and0x006ce290, seeds default callback root[0x006cd8d8+0x08]with0x0046f4a0, and populates the keyed selector table at[0x006cd8d8+0x04]throughmultiplayer_preview_dataset_register_selector_callback_if_absent0x00468b10for selectors1..0x83. The concrete callback bodies under that table are tighter now too. The small split is real rather than guessed:0x0046c390is the direct control-0x109publish leaf through0x00469d30;0x0046c3c0is the fixed-session-token plus launch-latch arm path; and the large sibling0x0046c420is the real apply-owner for one incoming session payload slab copied into0x006cec74before the shell-refresh follow-ons. A second callback cluster now owns live session-entry flags instead of just relaying transport payloads:0x0046c7c0rewrites the elapsed-pair dwords[entry+0x54/+0x58],0x0046c840sets bit0x08in[entry+0x5c],0x0046c870toggles bit0x01from payload byte[msg+0x08],0x0046cf10sets bit0x04, and0x0046cf70is the list-wide clear of that same bit-0x01lane across every live session entry. The broader callback0x0046c8c0sits above those single-field leaves and applies either one or many embedded session-field update records before republishing the list. The pending-state side is separated now too:0x0046cce0,0x0046cd10,0x0046ce90, and0x0046d230are the current small latch/state owners under0x006cd91c,0x006d1280,0x006d1284, and0x006ce9c8, while0x0046cd30and0x0046ce10are the paired current-session string/scalar submit-and-apply handlers over[entry+0x258c/+0x268c/+0x2690/+0x2694]. The same callback table also owns one small fixed transfer-progress family rooted at0x006ce2e8:0x0046cfe0allocates the first free0x5cslot and optionally formats one label string,0x0046d090appends progress payload into the matched slot while publishing one percent string through0x005387a0, and0x0046d1d0clears the finished slot and frees that optional label. On the release side,0x0046bc40now clearly owns the request-list, descriptor-list, semicolon-name, pooled-span, render-target, and auxiliary-owner teardown, whilemultiplayer_shutdown_preview_dataset_session_object_and_global_helper0x0046c230is the final wrapper that additionally drops the live session object at0x006d40d0and the shared helper at0x00d973b4. The small tuple staging below that family is bounded too:multiplayer_preview_dataset_touch_current_session_year_bucket_and_return_staged_tuple0x0046b6d0now owns the keyed session-bucket touch under[session+0x2580]for the staged tuple at[0x006cd8d8+0x9048], and the companionmultiplayer_preview_dataset_stage_optional_selected_token_from_source_ptr0x0046d610writes the optional derived token into[0x006cd8d8+0x8f38]. The next service layer under the same owner is tighter too:multiplayer_preview_dataset_prune_session_buckets_below_current_year_key0x0046b910now bounds the older keyed-bucket drain under[session+0x2580], whilemultiplayer_preview_dataset_service_current_session_buckets_and_publish_selector0x670x0046b9f0owns the current-bucket walk, the local counters[+0x987c/+0x9890], and the later selector-0x67publish branch back through0x00469d30. Its local batch-publish childmultiplayer_preview_dataset_publish_accumulated_selector0x71_record_batch0x00473bf0is now bounded too: it packages the fixed-width records at0x006cd990, prefixes(0, count), sends them as selector0x71, and then clears the live record count0x006ce9a0. The immediate local helpers under that same band are now explicit too:0x0046d260is the fixed-record append primitive that grows0x006cd990up to0x80entries, while0x0046d240is the paired release of the optional selector-0x71staging blob at0x006ce994. The first fixed named-descriptor block undermultiplayer_preview_dataset_register_fixed_named_callback_descriptors_1_to_100x00473a60is tighter in the same way now. Its concrete roots0x0046db10,0x0046db50,0x0046db90,0x0046dbd0,0x0046dd10,0x0046de40,0x0046de80,0x0046e030, and0x0046e250all return small static records rooted at0x006ce9dc..0x006cea3c; each record carries a leading tag byte plus the same derived world-year key from[0x006cec78+0x0d], while the heavier siblings add collection-backed totals from0x0062be10,0x006ceb9c,0x0062b26c,0x006cfca8, or0x006cfcbctogether with local overlay, geometry, or object-metric sample paths. The immediate helper strip beneath that same first named block is tighter now too.0x0046d980is the direct0x006ceb9cname-to-index lookup over[entry+0x08], while0x0046d9e0is the heavier companion that first resolves the current live session from0x006d40d0, matches that session-side string against the same0x006ceb9ctable, and returns the matching entry index or0xff. Above those,0x0046f8f0resolves and validates one0x0062be10entry, then keeps it only when its profile-index field[entry+0x3b]equals the live-session-backed0x006ceb9cindex from0x0046d9e0; and0x0046f870is the paired rounded-delta leaf that takes one float plus one collection byte index and writes the rounded positive/negative pair into metric ids0x0fand0x0dthrough0x0042a040. So this first descriptor band is no longer just a bag of static record roots: it also owns a real local helper family for0x006ceb9cname matching, live-session profile matching, and one narrow metric-pair apply path beneath the higher callback bodies. The next selector layer above that helper strip is tighter now too. Selector0x12is the small validate-or-error wrapper above selector0x13, and selector0x13body0x004706b0resolves the same live-session company match, attempts the placed-structure apply path through0x004197e0,0x004134d0,0x0040eba0,0x0052eb90, and0x0040ef10, and otherwise falls back to a hashed selector-0x6epublish over the first0x1cpayload bytes. The same pattern appears again one pair later: selector0x16is the thin validate-or-error wrapper above selector0x17, and selector0x17consumes a count plus0x33-stride adjunct record band, resolves one live train-side entry under0x006cfcbc, re-enters0x004a77b0,0x0052e720,0x0040eba0,0x0052eb90, and0x0040ef10, and again falls back to hashed selector0x6epublish when the live apply path does not land. The later status pair is bounded too: selector0x19is another thin wrapper, and selector0x1aeither derives a status code from0x0046ed30and current live-session name matching or treats the four-byte payload directly as that status code before publishing localized status text0x0b7f/0x0b80. One selector-pair above the metric leaf is now explicit too: the earlier front edge of the same callback table is tighter now too. Selector0x02compares staged profile text against the shell profile band at[0x006cec7c+0x11], can advance the requested-action fields0x006d1280/0x006d1284, can queue selector0x53, and on the success path syncs the larger shell profile block rooted at[0x006cec7c+0x44]. The next small strip is also grounded: selector0x0aclears[world+0x19], seeds[world+0x66ae], mirrors peer byte[peer+0x2690]into named-profile byte[entry+0x15c], refreshes0x006ce98c, and optionally republishes one local status path. Selector0x0bis then the small token-staging wrapper above selector0x0c, and selector0x0citself forwards one signed byte pair into0x00434680before adjusting dataset counter0x006cd8e8. The other small early leaves are now bounded too: selector0x0fpops one node from the current session queue at[session+0x64], publishes that node through0x00469d30, and releases it; selector0x10looks one payload key up in the session-side store[session+0x60]and forwards the result plus dataset string root[0x006cd8d8+0x8f48]into0x00521790. One selector-pair above the metric leaf is now explicit too: selector-0x15body0x00470950consumes the same compact(float delta, company-index byte)payload shape, resolves the matching live-session company entry through0x0046f8f0, submits selector0x6bthrough0x00469d30, and then immediately re-enters0x0046f870for the local apply. The neighboring name-match lane is now explicit too: selector-0x61body0x00472700scans0x0062be10for a company-name match against the caller string at[payload+0x08]and then either submits selector0x62with the original payload or falls back to the paired error-style0x21branch. The next registered band around selectors0x1c..0x5dis tighter now too. Selector-adjacent helpers0x00470ed0and0x00470fa0are the paired global preset passes beneath that strip: both walk the guarded named-profile table0x006ceb9c, add opposite signed integer presets into qword field[profile+0x154]through0x00476050, then walk0x0062be10and write opposite preset scalars into metric id0x0dthrough0x0042a040. Above them, selectors0x1d,0x1f,0x21,0x23,0x25,0x27,0x29,0x2b,0x2d,0x2f,0x31,0x33,0x35,0x37, and0x39are now explicit token-staging forwarders into selectors0x1e,0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38, and0x3a. The next live-train strip is also grounded: selectors0x3b,0x3d,0x3f,0x41, and0x43resolve live train ids from0x006cfcbc, stage the current token, and republish into selectors0x3c,0x3e,0x40,0x42, and0x44; selectors0x3c,0x3e, and0x40then dispatch directly into0x004abd70,0x004b2f00, and0x004b3000, while selector0x42is the heavier train-adjunct branch through0x004b2b70,0x004b3160,0x004b2c10,0x004a9460, and0x004ab980. The prompt or bitmap cluster is tighter too: selector0x48consumes one 12-byte record, either marks the current-session bit directly or opens localized prompt0x0b81through0x00469a50and callback0x004719c0, and then republishes selector0x49; selector0x49turns that 12-byte result into one keyed bitset object and republishes selector0x47; selector0x47consumes the resulting ten-slot masks and drops straight into0x004eb230plusshell_resolve_merger_vote_and_commit_outcome0x004ebd10. The same pattern repeats one size smaller for selectors0x4c,0x4d, and0x4b: selector0x4cconsumes one 10-byte record, either marks the current-session bit directly or opens localized prompt0x0b82through0x00469a50and callback0x00471d50, selector0x4dfolds that 10-byte result into the keyed bitset object, and selector0x4bturns the resulting masks into one ten-dword cache setter at0x0050c4e0rooted at0x006d1a08plus the paired outcome resolver0x0050c940. The direct setter strip after that is explicit too: selector0x4frepublishes selector0x6fwhen the live session object exists and dataset gate0x006cd91cis clear, selector0x50copies[dataset+0x9058]into[dataset+0x9054], selector0x51derives one small session-status code and either republishes selector0x52or shell control0x109, selectors0x55,0x56, and0x57directly store dataset field0x9860, a0x006ceb9cinline name, and guard field[entry+0x1e1], selector0x58flushes the deferred 16-slot named-profile clear queue[dataset+0x9864..+0x9873], selector0x59derives one roster or capacity status and republishes selector0x5a, selector0x5bis another token-staging forwarder into selector0x5c, selector0x5cgates a0x00493960dispatch plus optional local0x0046f870apply, and selector0x5dvalidates one payload string before republishing selector0x5e. The next registered band around selectors0x5e..0x7dis tighter too. Selector0x5eupdates the named-profile side table0x006ceb9c, mirrors the same string into the resolved live session object, and when the session-side guard is active hashes that string back into[session+0x48]and dataset field[0x006cd8d8+0x8f48]; selector0x5fthen stages the current year-derived token and republishes into selector0x60, whose body writes one guarded byte field into the same0x006ceb9centry family. The0x62..0x64strip forms the same kind of pair over0x0062be10: selector0x62copies one fixed0x32-byte band into the matched company entry, selector0x63rejects duplicate field-0x37values before forwarding, and selector0x64applies that same dword field directly into the matched entry or one live fallback owner. The receive-side correction is explicit now too: selector0x6bis the tiny local metric-apply wrapper0x00472db0 -> 0x0046f870, selector0x6cis the separate train-record wrapper0x00472dc0 -> 0x0046d780, selector0x6dformats localized status0x0f4einto the grounded world outcome-text buffer[world+0x4b47], and selector0x6ewalks the current keyed bucket under[session+0x2580]and marks the first matching companion record by payload hash. The later wrappers are cleaner too: selectors0x71,0x73,0x75,0x77,0x79,0x7b, and0x7dare all token-staging forwarders into selectors0x72,0x74,0x76,0x78,0x7a,0x7c, and0x7e. Beneath them, selector0x72is the heavier counted live-world apply path over0x0062b2fc,0x0062b26c, and0x0062bae0; selector0x74dispatches a resolved company-entry id into0x0062b26cunder the small latch0x006ce9a8; selectors0x76,0x7a, and0x7cresolve one company-style entry and then tail into narrower local handlers; and selector0x78is the broader projection-or-notify body over0x0044b160,0x00538e00, and the local-session refresh strip. The next adjacent owner0x0046e5c0is broader but still belongs to the same structural neighborhood: in mode1it serializes a dual-collection metric blob from0x0062be10and0x006ceb9c, writing the two row counts into local header bytes and then packing one0x24-stride band plus one0x2c-stride band behind a0x10-byte header; the opposite branch starts validating and applying those packed metrics back into live entries. So that first named block is no longer just a string-name registry; it already includes a real typed static-record family beneath the preview dataset, and it now sits directly beside one broader fixed-record callback-successor strip. Right after the selector-0x71batch publisher, the local0x005ce418fixed-record family becomes explicit:0x00473ce0constructs one0x187-byte record from two copied strings, shell-profile bytes[0x006cec78+0x0d/+0x0f/+0x11], owner field[this+0x04], and monotonic sequence dword[this+0x14]seeded from0x006cea48;0x00473dc0,0x00473e70,0x00473ee0, and0x00473f30are the max-sequence, min-sequence, previous-sequence, and next-sequence queries over the same live collection;0x00473e20is the boolean scan for any inactive record with a nonzero owner dword; and0x00473f80is the real allocator or constructor owner, trimming the collection down to0x19entries, allocating one live record through0x00518900, and then dispatching the heavier constructor. So the callback registry now leads directly into a concrete preview-side fixed-record collection, not into another anonymous transport helper band. The broader snapshot/apply owner still sits over the same multiplayer collection state. In parallel,multiplayer_register_session_event_callbacksallocates and registers the separate session-event transport object at0x006cd970. The shell-side bridge into that deeper transport cadence is now tighter:multiplayer_window_service_loopand neighboring reset or initializer branches callmultiplayer_flush_session_event_transport, which forces one status flush and then drops intomultiplayer_transport_flush_and_maybe_shutdown. That wrapper in turn runsmultiplayer_transport_service_frame, the recurring pump that services one worker step throughmultiplayer_transport_service_worker_once, sweeps the transport-owned callback tables and field caches throughmultiplayer_transport_service_route_callback_tables, services both the auxiliary status route and the current live route throughmultiplayer_transport_service_status_and_live_routes, and then descends one layer farther into the shared GameSpy route helpermultiplayer_gamespy_route_service_frameat0x58d040. The transport also owns a separate selector-view sidecar beneath that route cadence.multiplayer_transport_ensure_selector_view_storeallocates the keyed selector-view store at[transport+0xab4],multiplayer_transport_find_selector_view_entry_by_nameresolves entries from that store,multiplayer_transport_upsert_selector_name_entrymarks per-slot activity and flags inside each entry, andmultiplayer_transport_mark_selector_slot_views_dirtyplusmultiplayer_transport_reset_selector_view_entry_runtime_statemanage the dirty or refresh fields at+0xa0,+0xa4, and+0xa8. That selector-view maintenance path is now split more cleanly too. The recurring owner ismultiplayer_transport_service_selector_view_refresh_cycle, which first runs the fast deferred-probe lane:multiplayer_transport_collect_refreshable_selector_view_entrieswalks the store throughmultiplayer_transport_filter_insert_refreshable_selector_view_entry, which now shows that[entry+0x64]is not a generic flag bucket but the third selector-slot flag word, parallel to[entry+0x5c]and[entry+0x60]. In that collector, thegandamode-letter bits produced bymultiplayer_transport_parse_selector_mode_lettersbecome mask0x0cin the slot-local flag words, and any third-slot entry carrying those bits at[entry+0x64]is excluded from the refreshable set. Eligible entries then pass slot-aware retry timing on[entry+0x68],[entry+0x6c],[entry+0x78], and[entry+0x7c], after which the service loop schedules refresh probes throughmultiplayer_transport_schedule_selector_view_entry_refresh_probe. That fast lane is narrower now too: the entry-side match key[entry+0x50]is no longer just an opaque request field. The profile-key callback lanes feedmultiplayer_transport_parse_selector_view_probe_marker, which decodes one localX%sX|%dmarker into a probe request id plus displayed version/build integer, andmultiplayer_transport_arm_selector_view_probe_trackingstores those into[entry+0x50]and[entry+0x54]before arming the live probe gate at[entry+0x58]. The current-selector callback root at0x59f8b0is now bounded as well: it resolves and upserts the active selector name, optionally reuses a cachedusernamemarker to arm probe tracking immediately, then submits the same profile-key bundle with selector context and forwards that selector through callback slot17, with the status-route side able to force route-mode transitions2 -> 3 -> 4afterward. One layer lower,multiplayer_transport_handle_profile_key_query_resultat0x596970now bounds the per-key result path itself. It treatsusernameas the probe-marker field,b_flagsas the selector mode-letter field, and(END)as a real sentinel that publishes a zeroed slot-22payload instead of a marker pair. The same helper also hashes the selector-name, key-name, and resolved value text back into the caller table, so the profile-key bundle now looks like a real bounded handoff rather than an anonymous callback cloud. The deferred callback shimmultiplayer_transport_dispatch_selector_view_refresh_probe_resultthen walks the keyed store throughmultiplayer_transport_finish_selector_view_refresh_probe_if_matching, which only completes entries whose pending latch[entry+0x74]is still armed and whose parsed marker request id[entry+0x50]matches the finished request. A failed result-1clears the pending latch and increments the consecutive-failure counter at[entry+0x7c]. A nonfailure result clears the pending latch, increments the success generation at[entry+0x78]and total-success count[entry+0x98], clears[entry+0x7c], stamps the last-success tick at[entry+0x6c], appends the returned sample into the short rolling history at[entry+0x84..], grows the bounded sample-count[entry+0x94]up to four, computes the current average into[entry+0x80], and then publishes that averagedmssample throughmultiplayer_transport_enqueue_callback_slot24_record. So the publication boundary is explicit and the request-id ownership is explicit:[entry+0x80]now reads as the averaged millisecond probe sample and[entry+0x54]as the displayed version/build companion integer. The adjacent route-callback side is tighter too, but it is now kept separate: the staged route-callback path at0x5958e0and the later compatibility gate atmultiplayer_transport_route_binding_matches_route_callback_descriptor_tuple0x595d00operate on a compact GameSpy-style server or route descriptor family with a primary endpoint tuple at[descriptor+0x00]/[+0x04], an optional secondary endpoint tuple at[descriptor+0x08]/[+0x0c], string-key lookups such ashostnameandgamever, and numeric-key lookups such asnumplayers,numservers,numwaiting, andgsi_am_rating. The route-binding side uses that descriptor family's primary dword and host-order port plus the optional secondary tuple against route-binding offsets+0x54/+0x58and route word+0x30. Current evidence still does not prove that descriptor tuple is the same field family as the selector-view marker companion integer at[entry+0x54]. The higher compact decode owners are tighter now too:0x5907d0is the allocate-and-append lane for one self-consistent compact payload, while0x590d00is the keyed upsert-by-primary-endpoint lane that reuses an existing descriptor when possible and then notifies the owner callback. The route-callback-table runtime above that decode side is tighter now too:multiplayer_transport_route_callback_table_construct0x5905e0seeds one transport-owned table block,multiplayer_transport_route_callback_table_release_decoded_schema_dictionary0x5906f0tears down the decoded schema dictionary rooted at[this+0x08],multiplayer_route_callback_runtime_acquire_shared_string_copy0x590540andmultiplayer_route_callback_runtime_release_shared_string_copy0x5905a0now bound the shared string pool used by that decoded schema, and the higher bring-up owner0x596090now clearly splits between[transport+0xba4]with owner callback0x595a40, the local field-cache family[transport+0x1724]seeded through0x5a08f0/0x595b60, and[transport+0x1164]with owner callback0x595bc0, whilemultiplayer_transport_route_callback_table_service_receive_decode_state_machine0x5908c0is the current live receive/decode state machine serviced by0x591290in table states2/3. The callback-owner mode split above that runtime is now explicit too: append-notify0x590370publishes mode0, compact upsert0x590d00publishes mode1, remove-notify0x590430publishes mode2, and the live receive/decode path0x5908c0publishes modes6,5, and3. The route-handle lifecycle above that decode path is tighter now too:0x590740cleanly 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 live route handle into[this+0x4a0], stages the initial outbound request, and seeds state3plus the staged receive buffer;0x5911e0is the state-2/3socket-service wrapper that reads new bytes, grows the staged buffer, and re-enters0x5908c0;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 owners are tighter too:0x5962e0is now the field-subscription route-table opener 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 source-flag meaning narrower too: current evidence now supports reading byte-0x15bit0x1as a primary-endpoint-seed or endpoint-only marker. In thegsi_am_ratingdispatcher, clear-bit descriptors can take the richer direct transition lane, while set-bit descriptors are staged through the queued enrichment path and still suppress that direct transition even after the ready bit arrives. The adjacent capacity-descriptor side is tighter too:0x595bc0now clearly publishes a descriptor block from live descriptor propertieshostname,numwaiting,maxwaiting,numservers, andnumplayersplus three carried sidecar scalars. That sidecar at[transport+0x1778]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 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 local sweep also failed to show any obviouslea-based replay-band writer. The transient-request lifecycle tightens that further:0x593330/0x593370/0x593380/0x5934e0/0x5933a0now fully bound[transport+0x1780],0x1784, and0x1788without ever touching[transport+0x1778], and the neighboring active-opcode reset helper0x5929a0is likewise scoped only to[transport+0x17fc]. A broader constructor and teardown pass tightened that further too:0x596090,0x5961b0, and0x5962e0all touch the neighboring replay-band fields without ever seeding[transport+0x1778]. A full-binary literal-offset sweep tightens it further still: the only direct0x1778hit inRT3.exeis the read in0x595bc0. One nearby ambiguity is now closed too: the mode-5mirror path in0x595a40and0x595e10does not target[transport+0x1778]; it writes[transport+0x54]and mirrors the same staged route companion dword only into queue-side slot[transport+0x1724+0x24]through0x005a0940. So the sidecar writer remains upstream of this leaf publisher. Mode0is now also tied more cleanly to the generic descriptor append-notify lane at0x590370, while mode2stays outside this helper as the separate remove-notify-and-stage path at0x590430. The opcode-2payload boundary is tighter too:0x592ae0now grounds that payload as a seven-dword block 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 wrapper-side sidecar metadata. Those two modes are tighter now too: they are the live receive-state owner callbacks emitted by0x5911e0 -> 0x5908c0, not loose generic replay guesses. So those paths are better read as delayed metadata replays over one cached work record, not over a separate anonymous cache blob. The neighboring capacity-owner split is tighter now too:0x595bc0only stages descriptor records for modes0,3, and5; the upstream route-callback-table owner still delivers modes1,2, and6, but those are explicit no-ops in this capacity leaf. So the owner wiring itself is no longer the open edge; only the upstream sidecar producer remains unresolved. The neighboring work queue is tighter too:0x593330/0x593370/0x593380now bound[transport+0x1780]as the construct/clear/destroy owner family, while0x5933a0,0x5934e0, and0x593570ground the remove, allocate, and completion side over that same collection. The small sibling0x593400is tighter too: it is a pure work-record uniqueness predicate over 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 on a vague settle loop. The currently grounded roots are0x58df20, the neighboring formatted selector-text publish path at0x58dfb0, and callback-table registration at0x58e200. The active-opcode side is tighter too:0x5927b0now bounds the per-record service-and-retire path,0x592800the wider context-or-idle sweep,0x5929a0the remove-by-opcode-type sweep, and0x5929f0the narrower opcode-3field-snapshot removal keyed by the subscribed callback-pair payload. That also corrects0x595b80, whose final cleanup is an active field-snapshot purge rather than a queued-work drain. The adjacent route-callback descriptor-table lifecycle is tighter too:0x590410now grounds[table+0x5bc]as the staged intrusive descriptor-list head,0x590430is the generic remove-notify-and-stage lane,0x590490releases the staged list, and0x5904d0releases the active descriptor collection before tearing that staged list down. That also makes the earlier0x5962e0“release active descriptors” step explicit. The callback-table attach side now constrains the same work-record metadata family a little further too:0x593650deliberately duplicates its first caller metadata dword into both fields+0x0cand+0x10, while carrying the second caller metadata dword in+0x18. The lower opcode wrappers are tighter now too:0x592a40turned out to be the explicit opcode-1trigger wrapper whose constructor is a no-op and whose active-side service is0x5913c0, while0x592c40is the real0x08-byte explicit opcode-5binding leaf. The earlier opcode-4read was just the table-indexing mistake in0x5928a0: selector4lands on the row at0x5e2044, not the row at0x5e2034. The producer side is tighter too: bound-route requests, selector-text route requests, and the type-9text fastpath also stage that same triplet through0x5934e0, and the fastpath shim0x593d00now gives the cleanest proof of the callback split by only re-emitting the follow-on lane when+0x10is nonnull and then forwarding(+0x10, +0x18, +0x0c)into0x593170as callback function, callback companion, and trailing drain context. So the replay-side triplet is clearly a broader transport callback-wrapper family, not one fixed route-only tuple. The nearby field-subscription side is tighter too:0x592b50now clearly uses[transport+0x1774]as a cached progress percentage under[transport+0xba4], 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];0x596210is the recurring service sweep over those same three tables plus the local field cache and queued-descriptor family;0x596060is the explicitgsi_am_ratingreset; and0x596530is the reopen-from-stored-label sibling above that same am-rating table. 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 remaining gap on the capacity side is therefore narrower: the carried sidecar fields themselves now read more cleanly as the cached callback-wrapper triplet reused elsewhere (drain context id +0x0c,callback fn +0x10,callback companion +0x18), and the negative result is stronger too: nearby replay-band fields[transport+0x176c],[transport+0x1770],[transport+0x1774],[transport+0x177c],[transport+0x1780], and[transport+0x1784]all have direct local owners while[transport+0x1778]still appears only as the single read in0x595bc0; even the broader callback-owner lifecycle now skips it while seeding, servicing, resetting, reopening, or tearing down those neighboring tables and caches. The constructor now closes that local search further:0x58dc50bulk-zeroes the full transport body and still never writes a nonzero value into[transport+0x1778]before later explicit neighbor initialization. The callback-binding owner stack now tightens that boundary too:0x5934e0stages the shared work-record metadata triplet,0x593650binds it into the callback-table worker path, and0x593570later consumes and republishes it, while[transport+0x1778]still appears only as the borrowed sidecar read in0x595bc0. So this edge is now locally closed, and the remaining producer looks like an upstream callback or worker handoff rather than one missing ordinary field store in the local cluster. The adjacent staged-route callback side is tighter too:0x595860is now bounded as the submit-result handler beneath0x5958e0, and the old[transport+0xac0]ambiguity there is now gone. That branch is using the already-grounded third selector-generation counter at[0xac0]together with target[0xb48]to decide whether staged route-callback traffic can push the multiplayer route-mode ladder from2into3and later4. The selector-view counter beneath that gate is tighter now too:0x594e30counts slot-2entries whose flag dword carries bit0x20, optionally filtered by the current transport name buffer. The selector-view mutation family under that same lane 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 root0x59f9c0now reads as the sibling lane that clears one named selector-view slot, publishes callback slot18, and may still re-enter the route-mode setter from the same slot-2status and generation gates. The neighboring 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 now 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.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 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 now too: the shared queue helper at0x005a09a0stamps pending state0x4for the local field-cache family[transport+0x1724]and pending state0x8for thegsi_am_ratingqueued-descriptor family[transport+0x1e7c], while later service through0x005a0c80promotes those pending tags into ready bits0x1and0x2in descriptor byte[entry+0x14]. That makes the current transport-side tests cleaner:0x58d1c0is the field-cache ready gate,0x58d1d0is thegsi_am_ratingqueued-descriptor ready gate, and0x58d230is the remaining flag-byte split between direct primary-endpoint handling at[transport+0x18bc]and the queued path at[transport+0x1e7c]. That byte-0x14story is no longer queue-only either:0x58ff60can also OR in bit0x1after the inline keyed-property vector and bit0x2after the signed string-pair tail. The flag-byte split is no longer purely behavioral either: current evidence now says byte[descriptor+0x15]bit0x1is a source-side descriptor header bit, explicitly seeded during the primary-endpoint table refresh around0x590dc0and preserved by the compact descriptor decode path at0x58ff60, rather than a queue-generated runtime state. Thegsi_am_ratingdispatcher side is tighter too: that same bit no longer just looks like a direct-versus-queued routing split, because0x595e10also uses it to suppress the direct0x595dc0transition even after queued 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 headed at[table+0x5bc], 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 negative way too: local xref scans now only show it being preserved by later generic helpers likegeneric_record_0x1c_deep_copy_with_owned_string_at_0x080x591410and the adjacent callback-marshaling wrappers0x591480and0x591510, not read through any dedicated semantic accessor yet. The route-event dispatcher side is tighter too: the mode-5tails in both callback families do not copy a descriptor-local field but instead mirror the transport-staged companion dword at[this+0x490]into[this+0x54]and queue-side slot[this+0x1724+0x24]. Thegsi_am_ratingmaintenance lane is tighter now too: after pruning failed descriptors it sorts the surviving primary-endpoint table through0x590310in mode1with keygsi_am_rating, then selects the new head through0x590480before re-entering the route-transition path. The same service loop also owns a slower sidecar lane keyed off[entry+0xa4]:multiplayer_transport_select_stale_selector_view_progress_entrywalks the store throughmultiplayer_transport_pick_stale_selector_view_progress_entry, picks one stale entry whose progress latch[entry+0x9c]is clear and whose last progress tick[entry+0x70]is old enough, and then hands it tomultiplayer_transport_stage_selector_view_progress_snapshot. That helper now looks more bounded too: it rebuilds the coreX%sXmarker text from[entry+0x50]throughmultiplayer_transport_format_selector_view_probe_marker_core, formats onePNG %s %dline around that marker and the entry-local averaged millisecond sample at[entry+0x80], appends bounded selector-slotPNGfragments for live overlapping slots, marks progress-snapshot state in flight at[entry+0x9c], and stamps both[entry+0x70]and the transport-wide throttle tick[transport+0xaec]. So the selector-view sidecar no longer looks like one undifferentiated refresh bucket: it has a faster deferred-probe lane plus a slower progress-snapshot lane, both still under the shell-owned multiplayer transport cadence. The two descriptor lanes installed bymultiplayer_transport_attach_callback_table_descriptorare now tighter too. The first lane rooted at0x59f5c0can arm deferred-close state on the owner transport and then forward through callback slot23. The second lane is no longer just a loose selector-view bucket:multiplayer_transport_callback_dispatch_selector_name_payload_laneat0x59f650classifies selector payloads throughmultiplayer_transport_is_selector_control_line, routes@@@NFOcontrol lines intomultiplayer_transport_sync_selector_view_nfo_r_flag, and otherwise publishes either callback slot13or the split token-plus-tail callback slot14throughmultiplayer_transport_split_selector_payload_token_and_tail. That local@@@NFOhelper is now bounded more tightly too: it only accepts lines ending in the literalX\tail, searches for the field marker\$flags$\, and then sets or clears bit0x2in the third selector-slot flag word at[entry+0x64]depending on whether that field contains the letterrbefore the next backslash. The siblingmultiplayer_transport_callback_dispatch_current_selector_payload_laneat0x59f720first resolves the active selector through0x5951a0, then handles the current-selector variants of the same control vocabulary:@@@NFOcontinues into the same localr-flag sync helper,@@@GMLplus mode-3/4payloads feed the shared control-token helpermultiplayer_transport_handle_gml_or_png_selector_control, and the remaining non-control payloads publish callback slot9. That shared helper now narrows theGMLandPNGsplit materially: when the token isGMLand the tail isDisconnected, it requires the active selector-view entry to passmultiplayer_transport_selector_view_entry_has_gml_disconnect_gate, which currently means the entry participates in the third selector slot and has bit0x20set in[entry+0x64], before it forces one status-pump pass, emits callback slot16, and re-enters route mode5. Its siblingPNGbranch resolves a named selector-view entry from the tail text and, when that entry overlaps the active selector-slot ownership, refreshes selector-view runtime state through0x5948f0and republishes callback slot25. Alongside those dispatchers, one grounded branch at0x59f560still updates selector-view runtime state through0x5948f0and forwards the same selector-name pair through callback slot25, while0x59f850resets selector text state through0x5954b0, forwards through callback slot19, and when selector2is active in a nonterminal route mode re-entersmultiplayer_transport_set_route_modewith mode1. The low-level route helper still looks like a two-part cycle:multiplayer_gamespy_route_service_retry_and_keepalive_timershandles challenge or retry pressure and periodic outbound control traffic around themaster.gamespy.com,PING,natneg,localport,localip%d, andstatechangedstrings, whilemultiplayer_gamespy_route_drain_inbound_packetsdrains inbound datagrams and dispatches semicolon lines, backslash-delimited key bundles, and0xfe 0xfdGameSpy control packets. The transport-owned callback story is now narrower too. The shared route constructormultiplayer_gamespy_route_construct_and_seed_callback_vectorseeds[route+0x88]through[route+0x9c]from the caller-supplied transport callback table, records the owner transport at[route+0x104], and explicitly zeroes[route+0xa0],[route+0xa4], and[route+0xd4]before any later patch-up. For the transport-owned status route,multiplayer_transport_try_connect_status_routethen patches[route+0xa0]throughmultiplayer_gamespy_route_set_extended_payload_callbackto point atmultiplayer_transport_forward_validated_extended_route_payload0x00597330, which simply forwards the validated payload wrapper into the owner callback at[transport+0x17f4]with context[transport+0x17f8]. The grounded live-route connect path atmultiplayer_transport_try_connect_live_routedoes not currently perform any matching post-construction patch for[route+0xa0],[route+0xa4], or[route+0xd4], and the higher route-mode state machine now looks consistent with that:multiplayer_transport_set_route_modelatches the requested small mode at[this+0x18b8], then uses mode0for the direct-versus queuedgsi_am_ratingsplit, mode1for the ready-bit plus queued fallback, mode2for pending-descriptor cleanup, mode3for the empty-table fallback, mode4for deferred route-status recovery, and mode5for copying the staged route companion dword into[this+0x54]and queue-side slot[this+0x1724+0x24]. The current grounded mode transitions still switch by releasing route objects throughmultiplayer_gamespy_route_release_and_freeand rebuilding them throughmultiplayer_transport_try_connect_live_route, not by mutating callback slots in place. The parser behavior is now tighter as well: semicolon lines only dispatch when[route+0xd4]is non-null, and the subtype-6raw fallback only dispatches when[route+0xa4]is non-null. For the currently grounded transport-owned status and live routes, those two branches therefore remain optional and can cleanly no-op instead of implying a hidden mandatory callback path. Inside the packet parser, subtype4is no longer an unknown callback hop: after cookie validation it dispatches through[route+0x9c], which the transport-owned status and live routes seed tomultiplayer_transport_handle_validated_route_cookie_event0x005972c0. That helper either marks route progress and re-entersmultiplayer_transport_set_route_mode, or forwards the event id plus payload into the owner callback at[transport+0x17f0]with context[transport+0x17f8]. The surrounding status-route callback vector is tighter now too:0x005970e0publishes either the active selector text or the averaged probe sample at[entry+0x80]and otherwise falls back to owner callback[transport+0x17e0];0x00597180is a straight owner-forwarding lane through[transport+0x17e4];0x005971b0seeds the local status-control id list and can then notify owner callback[transport+0x17e8]; and0x00597270returns the third selector-slot generation counter[transport+0xac0]on its bounded local branch before falling back to owner callback[transport+0x17ec]. Subtype6still validates the same cookie, dedupes one 32-bit cookie or packet id, and then dispatches the trailing payload through the natneg-or-raw callback layer rooted at[route+0xa0]and[route+0xa4]. This separates the shell-frame preview refresh at0x006cd8d8from the actual transport cadence at0x006cd970, and also separates that transport cadence from the lower GameSpy route-service and packet-parser layer beneath it. - Evidence:
function-map.csv,pending-template-store-management.md,pending-template-store-functions.csv, plus objdump caller traces showingmultiplayer_window_service_loopreachingmultiplayer_flush_session_event_transportand the transport pump chainmultiplayer_flush_session_event_transport -> multiplayer_transport_flush_and_maybe_shutdown -> multiplayer_transport_service_frame -> multiplayer_transport_service_worker_once -> multiplayer_transport_drain_request_text_queue. - Open Questions: unresolved request-id semantics for
1,2,4, and7; whether any non-mode-path helper outside the currently grounded release-and-rebuild flow ever patches[route+0xa4]and[route+0xd4]for the transport-owned status/live routes, or whether those packet families are simply unused in this stack; and how far the Multiplayer preview-dataset machinery is reused outsideMultiplayer.winbeyond the currently grounded.gmtsave-mode hook.