For the complete documentation index, see llms.txt. This page is also available as Markdown.

Overview

DataServer persists character, item, guild, and event data to SQL Server

DataServer is the only component that talks to SQL Server. Every GameServer routes persistence (character load/save, inventory, warehouse, guild, chaos box, rankings, cash shop) through it. DataServer also owns the Write-Ahead Log that records critical save operations before the SQL transaction runs so interrupted saves can be detected and replayed on the next startup.

Role

  • Loads characters and inventories on login, saves them on logout and periodic autosave

  • Handles warehouse, chaos box, personal shop, and cash shop persistence

  • Stores guild, alliance, Castle Siege, and Gens state

  • Writes a Write-Ahead Log entry before character, warehouse, and chaos box saves; entries are replayed on startup when the database's LastSaveId shows the save was not committed

  • Rejects GameServer connections whose IP is not whitelisted

  • Filters character names against a bad-word list (guild names only get the baseline space-and-quote check, not the list)

File Location

DataServer/
├── DataServer.exe
├── DataServer.ini
├── AllowableIpList.txt
├── BadSyntax.txt
├── WAL/                        (created on first run)
│   └── WAL_YYYYMMDD.bin
└── METRICS/                    (created on first run)
    └── DSMetrics_YYYYMMDD.csv

All configuration lives in the DataServer root. There is no Data/ subfolder.

Configuration Files

File
Purpose

Database, port, logging, license

GameServer IP whitelist

Forbidden substrings in character names (creation, availability check, rename)

Startup Order

1

License activation

Reads LicenseKey from DataServer.ini and validates it with the license service. Failure aborts startup before any network or database work.

2

Primary database connection

Connects using DataServerODBC, DataServerUSER, DataServerPASS. Required; failure logs Could not connect to database and prevents the TCP port from opening.

3

Connection pool

Opens 4 additional SQL connections for parallel query processing. All pool connections use the same credentials as the primary. Any pool connection failure logs Could not connect pool connection N and aborts the open; DataServer never half-opens the port.

4

TCP listen

Opens DataServerPort for GameServer connections. If the listen call fails, every SQL connection is closed and the server idles with no port open.

5

Whitelist and filter load

Reads AllowableIpList.txt then BadSyntax.txt from the DataServer root. The whitelist is consulted inside the accept-condition callback, so the check runs for every incoming connection without any allocated socket state.

6

Guild manager and timers

Starts the 1-second timer (status bar, metrics display, license heartbeat) and initializes the in-memory guild manager.

7

WAL init and replay

Creates the WAL folder if missing, opens today's WAL_YYYYMMDD.bin, and scans it for entries with status pending. For each pending entry, DataServer queries the target row's LastSaveId in SQL; if the DB column is still behind the WAL entry id, the save is replayed and marked complete. Already-applied entries are skipped. Incomplete saves are counted in a red log line.

8

Metrics

Creates the METRICS folder if missing, opens today's DSMetrics_YYYYMMDD.csv, and starts a background thread at THREAD_PRIORITY_BELOW_NORMAL that writes a row every 10 seconds.

Dependencies

  • SQL Server with the MuOnline database restored from the shipped backup

  • ODBC DSN (name configured in DataServerODBC) registered on the DataServer host

  • GameServer IPs listed in AllowableIpList.txt

  • Writable working directory for WAL/ and METRICS/ subfolders

Write-Ahead Log

Before executing a character, warehouse, or chaos box save, DataServer appends a WAL entry with status pending to WAL/WAL_YYYYMMDD.bin and flushes it to disk. After the SQL transaction commits, the entry is updated in place to status complete. A crash between the two writes leaves the entry pending.

On startup, DataServer walks the WAL file and for each pending entry queries the target row's LastSaveId column in SQL:

  • If LastSaveId is greater than or equal to the WAL entry id, the save already committed and the entry is skipped

  • Otherwise the save is replayed against the database, LastSaveId is written with the WAL entry id, and the entry is marked complete

Details:

  • One WAL file per calendar day; the file rolls automatically when the day of month changes

  • Each entry is a fixed-size header followed by the raw save payload; do not edit by hand

  • Entry types covered: character save, warehouse save, chaos box save. Inventory-only saves are not written separately because they are part of the character save

  • The log is compacted in place after many completes, keeping only still-pending entries; a .bak copy of the pre-compact file is left next to it

  • WAL files accumulate across days; archive or delete old ones as disk allows

  • A non-zero pending count on startup is a signal to investigate the previous shutdown

Metrics

Every 10 seconds the metrics thread appends a row to METRICS/DSMetrics_YYYYMMDD.csv covering query count and timings, queue adds, overflows, queue peak depth, save counts, and derived per-second rates. The CSV header is written automatically when a new daily file is created. The DataServer window title and status bar show the live queries-per-second, queue peak, and overflow count.

Use metrics to verify the pool is keeping up. A sustained queue peak near the limit or any overflows mean either SQL Server is too slow or the pool size is undersized for the player load.

Common Issues

  • "Could not connect to database" - verify the ODBC DSN name, credentials, and that SQL Server is reachable

  • "Could not connect pool connection N" - the DSN works for 1 connection but rejects more; check SQL Server max connections and the login's allowed sessions

  • "[WAL] Found N incomplete saves from previous session" - last shutdown was not clean. Review the log for the affected characters; the data on disk is whatever was committed before the crash

  • GameServer connects then drops - its IP is not in AllowableIpList.txt

  • Character name creation fails for valid-looking names - a BadSyntax.txt entry matches as a substring

Last updated