Database

Database

Firelands uses MySQL 8.0 / MariaDB with three logical databases. Persistence adapters in src/infrastructure/persistence/ implement domain repository ports as MySql* classes.

Databases

DatabasePurpose
firelands_authAccounts (SRP-6a), realm list, sessions, schema migrations
firelands_charactersCharacters, spells, cooldowns, mail, GM tickets
firelands_worldStatic data: spawns, gossip, quests, playercreateinfo

Local Development

docker-compose up -d db
  • Image: mysql:8.0
  • Port: 3306
  • Root: root/root
  • User: firelands/firelands
  • Bundled schema loads from sql/bundled/ on first container start

SQL Files

sql/
├── init/           # Base schema (auth_schema, characters_schema, world_schema)
├── migrations/     # Incremental changes (numbered prefixes, ~26 files)
└── bundled/        # Merged schema for Docker and fresh installs

Bundled Schema

  • firelands_auth.sql
  • firelands_characters.sql
  • firelands_world.sql
  • zz_seed_schema_migrations.sql — seeds migration tracking for Docker

Merging Migrations

python3 tools/merge_migrations.py
# or
cmake --build build --target merge-migrations

Migrations at Runtime

DatabaseMigrator runs on auth and world startup in this order:

  1. sql/bundled/*.sql (skips zz_*.sql prefix files during normal apply)
  2. sql/init/*.sql
  3. sql/migrations/*.sql (lexicographic order)

Applied files are tracked in firelands_auth.schema_migrations. Each statement is split and executed safely.

Auth Database (firelands_auth)

account

Stores user accounts. Uses SRP-6a — password never stored.

ColumnTypeDescription
idINT PKAuto-increment account ID
usernameVARCHAR(32)Unique login name
saltBINARY(32)SRP salt
verifierBINARY(32)SRP verifier
emailVARCHAR(255)Account email
joindateTIMESTAMPCreation date
last_ipVARCHAR(15)Last login IP
expansionTINYINT3 = Cataclysm
lockedTINYINT1 = banned / login lock
access_levelTINYINTGM level 0–3

Adapter: MySqlAccountRepository. Console .account / .ban commands modify this table.

realmlist

ColumnDescription
id, nameRealm ID and display name
address, portWorld server endpoint
icon, timezoneClient realm list metadata
allowedSecurityLevelMinimum access to join
populationPopulation float

account_session

Active session keys (session_key BINARY(40)) for logged-in accounts.

account_data

Cached client UI data (macros, keybinds) — type 0–8, serialized BLOB.

schema_migrations

Tracks applied migration filenames.

Characters Database (firelands_characters)

characters

Main player row.

ColumnDescription
guidCharacter global unique ID
accountOwner account ID
nameCharacter name (max 12)
race, class, genderCreation choices
skin, face, hairStyle, …Appearance
level, xp, moneyProgression
mapId, zoneId, x, y, z, orientationPosition
live_health, live_power1Runtime vitals (nullable)
equipmentCacheJSON equipment snapshot
tutorial0-7Tutorial flags

Flows through CharacterService + MySqlCharacterRepository.

character_spell

Extra spells (e.g. from .learn): (guid, spell) composite PK.

character_spell_cooldown

Persisted spell and category cooldowns for .cd and relog.

gm_ticket

ColumnDescription
idTicket ID
account_id, character_guidOwner
statusOpen / assigned / answered / closed
message, gm_responsePlayer text and staff reply
assigned_account_idClaiming GM
map_id, pos_*Snapshot at creation

See GM Tickets.

mail / mail_items

In-game mail with optional item attachments (e.g. .additem overflow).

Instance tables

instance, instance_reset, character_instance, group_instance, account_instance_times, item_refund_instance — raid/instance persistence (schema present; gameplay partial).

World Database (firelands_world)

TablePurpose
playercreateinfoStarter position per race/class
playercreateinfo_spell / playercreateinfo_skillStarter spells and skills (masks + level filter at login). See Player Create Info.
creature_templateNPC templates (gossip_menu_id, stats, flags)
creatureCreature spawn rows
gossip_menu, gossip_menu_option, gossip_menu_option_actionNPC gossip menus
npc_textDialog copy for SMSG_NPC_TEXT_UPDATE
quest_template, creature_queststarterQuest gossip lines (class/race masks)
phase_areaArea → player phase IDs
phase_x_phase_groupPhaseGroup → member phase IDs
conditions (type 26)Quest/aura gates for phase_area rows

Character DB quest tables (character_queststatus, character_queststatus_rewarded) back phase condition checks. See Phase System.

World seed data can be regenerated with import scripts:

python3 tools/sql/import_ref_playercreateinfo.py   # → migrations 42/43
python3 tools/sql/generate_playercreateinfo_dbc_spells.py # → migration 62
python3 tools/sql/import_ref_gossip.py      # → migration 35
python3 tools/sql/import_ref_npc_text.py    # → migration 34
python3 tools/sql/import_ref_quest_gossip.py # → migration 38
python3 tools/sql/import_ref_phase_data.py       # → migration 55
python3 tools/sql/import_ref_phase_conditions.py # → migration 57

Repository Mapping

Domain PortInfrastructure AdapterDatabase
IAccountRepositoryMySqlAccountRepositoryauth
IRealmRepositoryMySqlRealmRepositoryauth
ICharacterRepositoryMySqlCharacterRepositorycharacters
IGmTicketRepositoryMySqlGmTicketRepositorycharacters
IPlayerCreateInfoRepositoryMySqlPlayerCreateInfoRepositoryworld
IGossipRepositoryMySqlGossipRepositoryworld
INpcTextRepositoryMySqlNpcTextRepositoryworld
IQuestGossipRepositoryMySqlQuestGossipRepositoryworld
ICreatureSpawnRepositoryMySqlCreatureSpawnRepositoryworld
IPhaseAreaCatalogRepositoryMySqlPhaseAreaCatalogRepositoryworld
IPhaseConditionRepositoryMySqlPhaseConditionRepositoryworld
IPhaseGroupCatalogRepositoryMySqlPhaseGroupCatalogRepositoryworld
IPlayerQuestProgressRepositoryMySqlPlayerQuestProgressRepositorycharacters

Migration best practices

  • Use IF NOT EXISTS, ADD COLUMN IF NOT EXISTS
  • Never DROP TABLE in migrations
  • Prefix files for ordering: 001_, 002_, …
  • After changes: cmake --build build --target merge-migrations

Querying locally

mysql -u firelands -p firelands_auth
docker exec -it firelands-db-1 mysql -u firelands -p