6.4 KiB
Shared server framework ("Magi") — configuration and database access
The server EXEs share one in-house framework. Its classes are named Magi*,
and the network sessions are MG*Session. This document defines the parts
every binary shares:
- server identity;
- startup modes;
- configuration files;
- the path to the database.
Analyse each new binary against this document, and record in its own
evidence.md where it differs, rather than re-deriving the framework.
Status: first pass. Primary evidence: MessageServer_Release_x64.exe
(decomp/MessageServer_Release_x64/). Cross-binary checks: strings and RTTI
only, unless a row says otherwise. Field offsets are from MessageServer. Other
binaries share the same code, so they are expected to match, but this hasn't
been verified. Every claim is static; nothing here was observed at runtime.
Notation: O = observed in decompiled code, S = observed in
strings/RTTI only, I = inference, ? = unknown. MS:0x… means a
function in decomp/MessageServer_Release_x64/full/functions/0x….c.
1. Framework presence across binaries (S)
| Marker | Present in |
|---|---|
RTTI MagiServerOption, MagiCustomOption, MagiServerFile, MagiServerManager |
all 14 server EXEs. The two SMMonitoring EXEs only have MagiServerManager. |
Option-loader keys (write_thread_count, each_option), the -AGNET MODE- banner [sic], and the DBC_CHAT/DBC_EMERGENCY names |
all 14 server EXEs; none in SMMonitoring |
Servers2DbcProxy RTTI (client of the DBC servers) |
Auction, Dungeon, GameGateway, Game, GM, Guild, Manager, Message, PVP |
ADO / _Connection strings (direct SQL access) |
DBCServer and DBCServer_LOG (4 hits each); 1–2 hits in Auction, Dungeon, Game, Guild, Manager and PVP (? — may be incidental) |
Inference: only the DBC servers talk to SQL Server. Every other server reaches the database through ProudNet RMI to a DBC server (§5). This needs confirming by decompiling the ADO usage in the six non-DBC servers that have hits.
2. Server-kind enum (O, MS:0x140002930)
The one-byte server type is stored in the option object (+0x1e8) and in
peer/session records. Names below are the wide strings the binary prints.
| Value | Name | Value | Name |
|---|---|---|---|
| 1 | Lobby (= GameServer EXE, D: ServerFile) |
0x0c | DBC_GAME |
| 2 | Dungeon | 0x0d | Agent |
| 3 | PVP | 0x11 | DBC_LOG |
| 4 | WAR | 0x12 | DBC_CHAT |
| 5 | Guild | 0x13 | DBC_EMERGENCY |
| 6 | Message | 0x15 | Agentmanager |
| 7 | GM | 0x16 | MiniGame |
| 8 | Gate | 0x17 | api peer (no display name; MS:0x14005dc20) |
| 9 | Manager | 0x0e–0x10, 0x14 | ? |
| 0x0a | Auction | ||
| 0x0b | Match | 0x18 (24) | PatchServer (D: ServerFile only; not shipped) |
The same numbers are stored in RustyHearts_Auth.ServerOption.Type, and
ServerFile maps each one to an EXE (D; see CONFIG.md §5).
Separately, servers also have a numeric code (the instance ID). It appears
in XML as code="61" and in file names such as MSG_61.xml. In the shipped
deployment config every kind has one code: DBC_GAME=1, DBC_LOG=2, DBC_CHAT=3,
DBC_EMERGENCY=4, Msg=61, GM=71, Gate=81, Manager=91, Auction=101, Match=111,
Agent=800, AgentManager=900, Dungeon=2001, PvP=3003, Guild=5001, Lobby=21001
(S, binaries/Option/*.xml).
3. Startup modes (O, MS:0x14005c7b0)
MagiServerOption::Init(argv1) records the PID and the start time
(+0x1ec, +0x1f0), then branches on the first character of the argument:
argv[1] |
Mode | What is loaded |
|---|---|---|
| does not start with a digit | standalone | argv[1] is a path to a per-server XML file, which MS:0x14005dc20 parses (§4.1) |
| starts with a digit | agent mode (prints -AGNET MODE-) |
atoi(argv1) is this server's code. MS:0x14005ceb0 reads option\total_option.xml (§4.3). The full server list then arrives from the Agent over RMI: the MGSAgentSession vftable slot 7 is MS:0x140065ab0. The entry whose code equals our code configures this server (MS:0x14005c9f0); every other entry is added as a peer (MS:0x14005ff50). |
This matches the Agent side: the Agent launches a server with the numeric
server code as its argument (decomp/Agent_Release_x64/evidence.md finding 3).
ServerInfo record pushed by the Agent (O, copy constructor MS:0x140065900; offsets in bytes):
| Off | Type | Field (I) |
|---|---|---|
| +0x00 | int | world |
| +0x04 | int | code |
| +0x08 | int | ? |
| +0x0c | u8 | server kind (§2) |
| +0x10 | int | private_port |
| +0x14 | int | public_port |
| +0x18 | int | public_udpport |
| +0x1c | int | udp_count |
| +0x20 | u8 | ? |
| +0x28 | std::wstring |
private_ip |
| +0x50 | std::wstring |
public_ip |
| +0x78 | std::wstring |
name |
4. Configuration files
Moved to CONFIG.md. It covers:
- the per-server XML schema with field offsets;
total_option.xmlandcustomoption.xml;- the database-backed server topology (
RustyHearts_Auth.ServerOption/ServerFile).
The MagiServerOption field layout matches between MessageServer and
DBCServer (O).
5. Database access path
Moved to DATABASE.md. It covers the RMI → command queue →
DBReq* → stored procedure chain, the four connection types, the
RMI → procedure map and the schema of the five databases.
Summary:
- Clients and connections: client servers keep 4
MGDBCMsgSessionpools (GAME/LOG/CHAT/EMERGENCY), sized bydb_cache session_count. They sendSERVERS2DBCRMIs (262, IDs 0x2ee1–0x2fe6); this table is identical in MessageServer and DBCServer, O. DBCServer runs stored procedures over ADO and answers withDBC2SERVERS(76 RMIs, 0x2af9–0x2b46).
6. Other shared protocols seen in MessageServer (O)
| Protocol | IDs | Direction |
|---|---|---|
SERVERS2MSG (stub) / MSG2SERVERS (proxy) |
0x3a99–0x3aa0 | servers ↔ MessageServer (decomp/MessageServer_Release_x64/evidence.md) |
SERVER2AGENT |
12 proxy RMIs | see the TSV |
AGENT2SERVER (stub) |
? | Agent pushes ServerInfo (§3) |
7. Open questions (priority order)
- AgentManager: confirm that it reads
ServerOption/ServerFileand pushes the topology (CONFIG.md§5). - DBCServer_LOG: the LOG RMIs and RustyHearts_Log (
DATABASE.md§5). - Diff the
SERVERS2DBCtable in the remaining client binaries. - Semantics of
send,auto,lock,each_option,guildlobby,channel, and of theapipeer. - Server kinds 0x0e–0x10 and 0x14.