server-re/decomp/DBCServer_Release_x64/porting.md
2026-10-15 09:08:48 -04:00

5.6 KiB
Raw Permalink Blame History

DBCServer_Release_x64.exe — porting spec (Win32 → Linux)

This was kept from the removed Linux reconstruction src/dbc_server/ (removed 2026-10-15). It records the facts about the original binary that a re-implementation needs, and the nearest Linux equivalent for each Win32 call.

Sources: evidence.md, docs/DATABASE.md, docs/CONFIG.md. Static analysis only.

Structure to reproduce

peer ──SERVERS2DBC RMI {rmi_id, …}──▶ stub handler            (e.g. 0x140050d20: ++per-RMI counter)
                                       │ push {size, cmd 1001–1317, args}
                                       ▼
                     write queue [idx < 1000]                (0x140144730; else "Invalid Queue Index")
                                       │ critical section + semaphore, 1 worker per queue
                                       ▼
                     worker switch (cmd - 1001)              (0x1400a8190, jump table 0x1400a9dd4)
                                       ▼
                     CDBLobbyWriteQuery::DBReq*              (e.g. 0x1400acbb0) — ADODB.Command "dbo.up_xxx"
                                       ▼
                     reply via DBC2SERVERS proxy (76 RMIs, 0x2af9–0x2b46)
  • Data tables:
    • The RMI → command → procedure mapping is in rmi_to_proc.tsv, and the command dispatch in cmd_dispatch.tsv.
    • Procedure parameters (names, SQL types, OUTPUT flags) are in decomp/schema/<db>/routines.tsv.
    • decomp/scripts/gen_dbc_tables.py joins them into procs_used.tsv.
  • Queue array: a 0x1f48-byte block holding 1000 pointers. How queue slots are created has not been decoded.
  • Connections (0x1400a7aa0):
    • Type 1 WEB = db_web, 2 AUTH = db_account, 3 GAME = db_game, 4 GAME LOG = db_game_log.
    • A timeout of 0 becomes 500.
    • A string longer than 255 characters gives Error … String Max.!.
    • AUTH uses the WEB timeout (+0x134); this is an original bug.
  • Startup (0x140042cf0): opens AUTH and GAME connections, then up_read_world_server reads the world list.
  • Connect failure (0x1400a80c0): DB Connect failed\n Connection String = %s is appended to Error_ADO DB Connect.Log. The original writes the password verbatim.
  • Procedure error: ADO_ON_ERROR : [ %s ] Proc [ %s ] with source file and line (catch funclets, dbreq_methods.tsv).
  • Single instance: mutex Global\RustyHearts_DBC.
  • Startup modes: a numeric argv[1] selects agent mode (total_option.xml <db><game>); otherwise argv[1] is the path of the per-server XML (docs/CONFIG.md).

Win32 → Linux substitutions

Original (address) Win32 / library Nearest Linux equivalent Notes
Single instance CreateMutexW("Global\\RustyHearts_DBC") flock(LOCK_EX|LOCK_NB) on $TMPDIR/RustyHearts_DBC.lock Lock file instead of a kernel object
DB access (0x1400a7aa0, 0x1400a80c0, DBReq*) ADO _Connection / ADODB.Command over SQLOLEDB, CoInitialize, _com_issue_errorex ODBC: unixODBC + Microsoft ODBC Driver 18, or FreeTDS libtdsodbc Call as {? = call dbo.proc(?,…)} with positional parameters (ADO uses named ones); GUIDs in SQL Server byte order. ODBC 18 needs TrustServerCertificate=yes, because SQLOLEDB never validated TLS. FreeTDS needs Port= (not host,port) and TDS_Version=7.4
ADO connection string Provider=SQLOLEDB;Data Source;Initial Catalog;User ID;Password Driver={…};Server;Database;UID;PWD Keys map 1:1. Treating the XML timeout as milliseconds (rounded to seconds) is an inference
Transport ProudNet CNetServer over Winsock2/MSWSOCK/IOCP BSD sockets / epoll ProudNet internal net version is 196978; 1.8 is 197144, so the protocols are incompatible. Interop would need a reimplementation of the 2013 protocol (third-party/README.md)
Write queue (0x140144730) CRITICAL_SECTION + semaphore (ReleaseSemaphore) std::mutex + std::condition_variable —
Threads _beginthreadex std::thread —
XML (MagiServerOption, MagiCustomOption) TinyXML 2.5.x pugixml with parse_fragment (the files have several top-level elements) —
Console / stop wprintf, console menu stdout, SIGINT/SIGTERM; ignore SIGPIPE —
timeGetTime WINMM std::chrono::steady_clock —
Crash reporting BugTrap, MiniDumpWriteDump core dumps —

Prior validation of the DB mapping (2026-10-14)

Run with the removed port against copies of the shipped .mdf files, on SQL Server 2022 through FreeTDS 1.5.19:

  • up_read_world_server returned 1 row.
  • REQ_READ_ALL_GUILD(10101) returned the guild row.
  • REQ_UPDATE_CHARACTER_HEARTS(guid, n) updated CharacterTable.Hearts (then reverted).
  • REQ_INVEN_SLOT failed with "Could not find stored procedure", confirming that procedure is missing from the shipped schema.

This validates the mapping and the parameter types against the schema. It says nothing about the original binary's runtime behaviour.

Unknown / not recovered

  • RMI argument marshalling (ProudNet) and the per-DBReq post-processing of recordsets (e.g. FIRST_CHARACTER in 0x1400acbb0).
  • 52 RMIs without a procedure mapping, mostly log, logout and auth.
  • Some mappings may be wrong: those found two call levels deep, e.g. REQ_CHARACTER_READ_ALL_DATA → up_read_Inquiry_character.
  • Which connection each queue/DBReq uses: inferred from the database that holds the procedure.
  • Queue-slot creation; agent-mode address/port (pushed by the Agent over RMI); the guildlobby, channel and Option elements.