Network (DirectPlay 3)#
Purpose#
Multiplayer transport and session management built on DirectPlay 3
(DPLAYX.DLL). Handles service-provider selection, connection init, hosting and
joining sessions, player creation, and a custom application-message stream
(reliable + unreliable) layered over IDirectPlay3::Send / Receive.
Key entry points#
| Symbol | Address | Role |
|---|---|---|
lotr2_net_start_session |
0x4d243e | Session entry; sets active flag, runs join handshake wait |
lotr2_net_setup_session |
0x4d266d | InitializeConnection, Open (host/join), CreatePlayer |
lotr2_net_create_dplay3 |
0x4d2d1f | DirectPlayCreate + QueryInterface IID_IDirectPlay3 |
lotr2_net_shutdown |
0x4d23d0 | Close + Release the IDirectPlay3 object |
lotr2_net_leave_session |
0x4d25c6 | DestroyPlayer + shutdown |
lotr2_net_set_player_data |
0x4d260d | SetPlayerData (vtable +0x7c) |
lotr2_net_poll_messages |
0x4d36f0 | Receive loop; routes system vs app messages |
lotr2_net_dp_send |
0x4d39d5 | IDirectPlay3::Send wrapper |
lotr2_net_send_message |
0x448641 | Pack frame, checksum, reliable triple-send |
lotr2_net_dispatch_app_message |
0x448362 | Validate + dedupe + dispatch app message |
lotr2_net_handle_system_message |
0x4480ef | DPSYS_* handler (sets host on DPSYS_HOST) |
lotr2_net_checksum |
0x448c6c | 8-bit additive payload checksum (skips 4-byte header) |
lotr2_net_buffer_checksum |
0x448cbd | Full-buffer 8-bit additive sum from offset 0 |
lotr2_net_strided_checksum |
0x448d0e | Strided row/column checksum for sync tables |
lotr2_net_partial_checksum |
0x448d8e | Checksum of buffer slice [skip .. size); used by sync refresh |
lotr2_net_payload_write / lotr2_net_payload_read |
0x448dee / 0x448e2a | Payload cursors (lotr2_net_payload_write_ptr / _read_ptr) |
lotr2_net_on_join_handshake |
0x44a03b | Completes join; sets lotr2_net_handshake_done |
lotr2_net_reset_send_state |
0x448832 | Reset send slots / counters pre-play |
lotr2_net_clear_send_slot_table |
0x448b3f | Zero 6×10 send-slot table |
lotr2_net_clear_recv_dedupe_table |
0x448b9a | Zero 7×0x28 receive dedupe table |
lotr2_net_clear_msg_history_table |
0x448bfa | Zero per-player message history |
lotr2_net_refresh_player_sync_checksums |
0x448e65 | Recompute per-player sync checksum bytes pre-send |
lotr2_net_host_check_sync_ready |
0x44927a | Host sync gate; may send opcode 0x0f |
lotr2_net_compare_player_sync_slot |
0x4493e9 | Compare sync row across connected slots |
lotr2_net_apply_host_sync_masks |
0x449553 | Apply host sync bitfield; client sends opcode 0x12 |
lotr2_net_host_resolve_sync_conflicts |
0x44973f | Host compares checksum rows; sends opcode 0x10 |
lotr2_net_host_replay_sync_deltas |
0x449ab4 | Host replays non-zero sync delta table; sends opcode 0x10 |
lotr2_net_sync_clear_battle_tiles |
0x449c22 | Clears battle tile occupancy before sync (opcode 0x12) |
lotr2_net_sync_apply_state_slice |
0x449c3d | Apply state slice during sync; sends opcodes 0x50/0x14/0x13 |
lotr2_net_sync_restore_state_slice |
0x449d7c | Restore state slice during sync; sends opcode 0x0e |
lotr2_net_reset_send_state_ok |
0x4d2653 | Wrapper: lotr2_net_reset_send_state() then return 1 |
lotr2_net_sync_local_faction_id |
0x43b22b | Assign local faction id at session start |
| Service-provider dialog | 0x4d2a1e / 0x4d2ac0 | DirectPlayEnumerate picker |
| Host/Join + browse dialogs | 0x4d2a48 / 0x4d2a99 / 0x4d2a6f / 0x4d3120 | Session setup UI + EnumSessions |
lotr2_net_dlg_sp_list_hwnd |
0x502a78 | SP picker listbox handle |
lotr2_net_dlg_sp_sel_index |
0x502a84 | Selected SP list index |
lotr2_net_dlg_session_name_hwnd |
0x502a70 | Host session-name edit control |
lotr2_net_dlg_browse_list_hwnd |
0x502a7c | Session browser listbox handle |
DirectPlayCreate / DirectPlayEnumerate |
0x4db1e4 / 0x4db1de | DPLAYX.DLL ordinal-1 / ordinal-2 import stubs |
App-message handlers (pass 50 complete)#
Pack/unpack functions are indexed by opcode via PTR_LAB_004f06b8 (pack) and
PTR_LAB_004f0820 (unpack). All 90 opcode slots now have semantic
lotr2_net_pack_* / lotr2_net_unpack_* names (pass 49 + pass 50). Deferred
turn actions go through lotr2_net_deferred_action_enqueue (was opcode 0x53)
and lotr2_net_deferred_queue_drain.
| Opcode | Pack | Unpack |
|---|---|---|
| 0x01 | lotr2_net_pack_local_id |
lotr2_net_unpack_join_handshake_request |
| 0x02 | lotr2_net_pack_join_data |
lotr2_net_on_join_handshake |
| 0x03 | lotr2_net_pack_player_roster_sync |
lotr2_net_unpack_player_roster_sync |
| 0x0a | lotr2_net_pack_heartbeat_toggle |
lotr2_net_unpack_heartbeat_toggle |
| 0x04 | lotr2_net_pack_begin_campaign |
lotr2_net_unpack_begin_campaign |
| 0x0b | lotr2_net_pack_sync_ui_viewport |
lotr2_net_unpack_sync_ui_viewport |
| 0x1f | lotr2_net_pack_save_game |
lotr2_net_unpack_save_game |
| 0x23 | lotr2_net_pack_upkeep_increase |
lotr2_net_unpack_upkeep_decrease |
| 0x36 | lotr2_net_pack_move_army_to_cell |
lotr2_net_unpack_move_army_to_cell |
| 0x56 | lotr2_net_pack_write_save_slot |
lotr2_net_unpack_load_save_slot |
| 0x57 | lotr2_net_pack_menu_selection_inc |
lotr2_net_unpack_menu_selection_dec |
Full opcode catalog: ../../protocols/dplay-app-messages.md.
Apply renames via ../../tools/ghidra/net_renames_pass50.py.
Key globals#
| Symbol | Address | Role |
|---|---|---|
lotr2_net_dplay3 |
0x4ff400 | IDirectPlay3* object |
lotr2_net_local_player_id |
0x4ff408 | Local player DPID |
lotr2_net_iid_dplay3 |
0x4eb0d8 | IID_IDirectPlay3 GUID blob |
lotr2_net_player_data |
0x4ff404 | Player-data buffer (SetPlayerData) |
lotr2_net_session_active |
0x55a370 | Session-running gate (read by 100+ game sites) |
lotr2_net_handshake_done |
0x55a854 | Join handshake-complete flag |
lotr2_net_is_host |
0x5636e0 | Host vs client role |
lotr2_net_session_preselected |
0x4ff410 | EnumSessions preselected-entry flag |
lotr2_net_send_slot |
0x52a578 | Rolling send-slot index (wraps 1..0x27) |
lotr2_net_recv_frame |
0x52d490 | Receive frame buffer (header + payload) |
lotr2_net_recv_length |
0x52d491 | Received payload length byte |
lotr2_net_recv_checksum |
0x52d492 | Received checksum byte |
lotr2_net_recv_seq_flags |
0x52d493 | Received sequence / resend flags |
lotr2_net_payload_write_ptr |
0x52d488 | Payload write cursor |
lotr2_net_payload_read_ptr |
0x52d460 | Payload read cursor |
lotr2_net_send_slots |
0x52d5a0 | Send slot array base (stride 0x104) |
Dependencies#
DPLAYX.DLL—DirectPlayCreate(ord 1),DirectPlayEnumerate(ord 2),IDirectPlay3COM interface.USER32.DLL—DialogBoxParamAsession/SP dialogs,SendMessageAlist boxes.KERNEL32.DLL—GetTickCount/Sleepfor send pacing and handshake timeout.ui/MODULE.md— main window handlelotr2_ui_hwnd_main.
Data files#
- None on disk. Session name default string
"Lords2 Session1", player short name"Nobleman".
Cross-references#
- Protocol:
../../protocols/dplay.md - System messages:
../../protocols/dplay-system-messages.md - App opcodes:
../../protocols/dplay-app-messages.md/../../protocols/_generated/net_opcode_table.md - Struct:
../../structs/lotr2_net_msg.h/../../structs/lotr2_net_msg.md../../structs/lotr2_net_msg_join.h/../../structs/lotr2_net_msg_roster.h
Open questions#
- Authoritative opcode table dump needs a binary that passes anchor validation
against this Ghidra export; generated docs currently mark unknown entries as
TBD. - Which service-provider GUID(s) are used (modem / serial / IPX / TCP).
- Orphan handler bodies at
0x44ef1f–0x44f598have no xrefs; defer until jump-table dump at0x004f0820is validated (see symbol-map pass 28). - Opcodes 0x08 and 0x09 have no distinct handler pair in the
0x449elayout; table slots may alias other handlers (see pass-20 notes in symbol-map).