Paragon: ship server-side DBC overlay as SQL so fresh installs can roll class 12

Stock Docker installs fill data/dbc/ from the vanilla 3.3.5a extract
in `ac-wotlk-client-data`, which has no class 12 in ChrClasses.dbc and
no class-12 bit on SkillRaceClassInfo.dbc. CharacterHandler.cpp's
sChrClassesStore.LookupEntry(12) returns null and the create fails
with CHAR_CREATE_FAILED ("Class (12) not found in DBC ...") before the
contributor ever sees the panel. Fixing it required hand-copying the
patched DBCs onto the named volume — undocumented, fragile, and not
portable to native installs.

DBCStores.cpp::LoadDBC merges every <table>_dbc world-DB row on top of
the on-disk DBC store (storage.LoadFromDB after storage.Load). We use
that merge layer to ship Paragon's class-12 deltas as SQL:

- chrclasses_dbc: 1 row defining class 12 (Paragon, power=Mana,
  family=Warrior, expansion=2). Resolves CHAR_CREATE_FAILED.
- skillraceclassinfo_dbc: 235 rows REPLACEing stock entries with the
  patched ClassMask (class-12 bit OR'd in) so baseline skills (defense,
  weapon skills, etc.) are available to Paragon characters.

The new `modules/mod-paragon/data/sql/db-world/updates/2026_05_09_00.sql`
is applied automatically by AC's DBUpdater on every fresh `ac-db-import`
run (Docker) or first worldserver boot (native). End-to-end verified
locally: truncate -> docker compose up ac-db-import -> rows reappear
with hash 33B1A05 recorded in updates table.

The migration is auto-generated by
fractured-tooling/from-workspace-root/_gen_paragon_dbc_overlay_sql.py
(outside this repo per the repo-tidy policy). Re-run it whenever the
DBC bake changes.

CLIENT-PATCHES.md is rewritten so contributors no longer need the
manual DBC sync section as their primary install path. Manual overlay
is preserved as a labelled fallback for tools that read data/dbc/
directly.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Docker Build
2026-05-09 12:19:59 -04:00
parent 20a24b7935
commit fae3ff5028
2 changed files with 332 additions and 50 deletions
+62 -50
View File
@@ -53,16 +53,17 @@ worldserver image is older than commit `4d2a80d` (the
the same release tag and rebuild the worldserver image. the same release tag and rebuild the worldserver image.
If the **client** shows the Paragon class on the create screen but the If the **client** shows the Paragon class on the create screen but the
server replies **Character Creation Failed** when you pick it: the server replies **Character Creation Failed** when you pick it on a
worldserver's on-disk DBC set still does not define class **12** **very old** server checkout (predating commit landing
(`ChrClasses.dbc`). SQL migrations alone cannot fix that when `modules/mod-paragon/data/sql/db-world/updates/2026_05_09_00.sql`):
`chrclasses_dbc` in MySQL is empty (normal for stock AC — AzerothCore pull `main` and run `docker compose up -d ac-db-import`. Recent
loads `.dbc` files from `data/dbc/` and only *merges* optional DB rows Fractured server builds ship the Paragon DBC overlay as a SQL
on top). See **Worldserver DBC sync** below. migration (see **Server-side Paragon DBC overlay** below); fresh
checkouts do **not** need the patched DBCs copied into `data/dbc/`.
--- ---
## Worldserver DBC sync (required for Paragon character create) ## Server-side Paragon DBC overlay (automatic)
The Fractured **client** learns about Paragon from `patch-enUS-4.MPQ` The Fractured **client** learns about Paragon from `patch-enUS-4.MPQ`
(DBC + GlueXML). The **worldserver** never reads your MPQs — it reads (DBC + GlueXML). The **worldserver** never reads your MPQs — it reads
@@ -70,46 +71,68 @@ plain `.dbc` files under its `DataDir` (`.../data/dbc/` by default).
Stock Docker installs populate `data/dbc/` from a vanilla 3.3.5a Stock Docker installs populate `data/dbc/` from a vanilla 3.3.5a
extract (`ac-client-data-init` in `docker-compose.yml`). That tree has extract (`ac-client-data-init` in `docker-compose.yml`). That tree has
no `ChrClasses` row for id **12**, so `CharacterHandler` rejects the no `ChrClasses` row for id **12** and no class-12 bit on
create with `CHAR_CREATE_FAILED` and logs: `SkillRaceClassInfo` rows, which would normally trigger:
`Class (12) not found in DBC while creating new char ... wrong DBC files or cheater?` `Class (12) not found in DBC while creating new char ... wrong DBC files or cheater?`
**Fix:** merge every `.dbc` that ships inside `patch-enUS-4.MPQ` under …and reject the create with `CHAR_CREATE_FAILED`.
`DBFilesClient/` (or the archive's `dbc/` root — same layout depending
on tool) into the server's `data/dbc/` directory, then restart
**only** `ac-worldserver`. Copy the **whole** set from the MPQ, not
just `ChrClasses.dbc`, so dependent stores (`CharBaseInfo`,
`SkillRaceClassInfo`, `TalentTab`, `PowerDisplay`, etc.) stay consistent
with the client patch.
### Extract from the MPQ (any OS) To remove that gap, the repo ships
`modules/mod-paragon/data/sql/db-world/updates/2026_05_09_00.sql`,
which `INSERT`s the Paragon class-12 deltas into:
Use Ladik's MPQ Editor, `mpqcli`, or any StormLib tool. You want every - `chrclasses_dbc` — 1 row defining class 12 ("Paragon", power=Mana,
`*.dbc` that Fractured added or changed in that patch, staged into a family=Warrior, expansion=2).
host folder (example: `./paragon-dbc-extract/`). - `skillraceclassinfo_dbc` — 235 rows replacing stock entries with the
patched ClassMask (class-12 bit OR'd in) so every baseline skill is
available to Paragon characters.
### Docker: write into the named volume `AzerothCore`'s DBC loader (`DBCStores.cpp::LoadDBC` -> `LoadFromDB`)
merges these rows on top of whatever `data/dbc/` contains at every
worldserver boot. The DBUpdater in `ac-db-import` (Docker) or the
worldserver itself (native) applies the migration automatically — so
the **only** steps a fresh contributor needs are `git clone` and
`docker compose up -d`.
Compose uses `${DOCKER_VOL_DATA:-ac-client-data}` (see ### Regenerating the migration
`docker-compose.yml`). Discover the real volume name:
```bash The SQL is auto-generated from the patched DBCs that already live
docker volume ls | grep -i client-data inside `patch-enUS-4.MPQ`. The bake script lives outside this repo
(per the repo-tidy policy) at:
`fractured-tooling/from-workspace-root/_gen_paragon_dbc_overlay_sql.py`
Re-run it whenever you change the Paragon DBC bake — for example,
adding a new race to the Paragon class mask. It diffs the patched
DBCs against a stock 3.3.5a DBC extract and emits a fresh
`2026_05_09_00.sql` (or successor migration with a new timestamp if
deltas change). Workflow:
```powershell
# Extract the patched DBCs once:
.\tools\mpq\mpqcli.exe extract `
"ChromieCraft_3.3.5a\Data\enUS\patch-enUS-4.MPQ" `
-o "$env:TEMP\paragon-dbc-extract"
# Regenerate the SQL migration:
python fractured-tooling\from-workspace-root\_gen_paragon_dbc_overlay_sql.py
``` ```
Copy extracted `.dbc` files into the volume's `dbc/` subdirectory. If the regenerated SQL has new content, commit it as a **new** dated
Example (Linux / macOS — adjust volume name and host path): migration filename (e.g. `2026_06_01_00.sql`) — never edit a file that
has already been applied to live databases, AC's DBUpdater will detect
the hash change and re-run the SQL, which can be fine but is best
reserved for emergencies.
```bash ### Manual DBC overlay (rare, fallback)
docker run --rm \
-v ac-client-data:/data \
-v "$PWD/paragon-dbc-extract:/patch:ro" \
alpine sh -c 'cp -f /patch/*.dbc /data/dbc/'
docker compose restart ac-worldserver
```
Windows PowerShell (same idea; escape backticks if you wrap lines): If you ever need the patched DBCs *on disk* — e.g. for a tool that
reads `data/dbc/` directly outside the worldserver, or to verify a
client-vs-server DBC mismatch — extract `patch-enUS-4.MPQ` and copy
its `DBFilesClient/*.dbc` into `data/dbc/`:
**Docker:**
```powershell ```powershell
docker run --rm ` docker run --rm `
@@ -119,22 +142,11 @@ docker run --rm `
docker compose restart ac-worldserver docker compose restart ac-worldserver
``` ```
If your compose **project** name prefixes the volume (e.g. **Native:** copy into `<CMAKE_INSTALL_PREFIX>/data/dbc/` and restart.
`fractured_ac-client-data`), use that full name from `docker volume ls`.
### Native install (no Docker) This is **not required** for normal operation — the SQL migration
covers everything `mod-paragon` needs at runtime. Use the manual
Copy the same extracted `.dbc` files into: overlay only when you're consciously bypassing the SQL merge layer.
`<CMAKE_INSTALL_PREFIX>/data/dbc/`
then restart `worldserver`.
### Verify
After restart, search startup logs for errors mentioning `ChrClasses` or
`dbc`. On failure you would still see the `Class (12) not found` line at
create time — if that disappears, the DBC merge worked.
--- ---
@@ -0,0 +1,270 @@
-- mod-paragon: server-side DBC overlay for class 12 (Paragon).
-- Auto-generated by fractured-tooling/from-workspace-root/
-- _gen_paragon_dbc_overlay_sql.py
--
-- AzerothCore's DBCStores.cpp::LoadDBC merges every <table>_dbc
-- world-DB row on top of the on-disk DBC store at startup
-- (storage.LoadFromDB). We use that to ship Paragon's class-12
-- DBC deltas in SQL form so a stock data/dbc/ tree (e.g. the
-- vanilla `ac-wotlk-client-data` Docker image) still resolves
-- class 12 in sChrClassesStore and class-12 entries in
-- sSkillRaceClassInfoStore.
--
-- Without this migration, fresh installs hit:
-- CHAR_CREATE_FAILED -- "Class (12) not found in DBC ..."
-- the moment a contributor tries to roll a Paragon character.
--
-- This file is regenerated end-to-end from patch-enUS-4.MPQ;
-- do not hand-edit. Update the patched DBC source and rerun
-- the bake script.
-- chrclasses_dbc: classes added or modified by patch-enUS-4.MPQ.
-- AzerothCore merges this on top of the on-disk ChrClasses.dbc
-- so a stock data/dbc tree still gets class 12 at runtime.
DELETE FROM `chrclasses_dbc` WHERE `ID` IN (12);
INSERT INTO `chrclasses_dbc` (`ID`,`Field01`,`DisplayPower`,`PetNameToken`,`Name_Lang_enUS`,`Name_Lang_Mask`,`Name_Female_Lang_Mask`,`Name_Male_Lang_Mask`,`Filename`,`SpellClassSet`,`Flags`,`CinematicSequenceID`,`Required_Expansion`) VALUES
(12, 0, 0, 0, 'Paragon', 0, 0, 0, 'PARAGON', 4, 50, 0, 2);
-- skillraceclassinfo_dbc: rows where patch-enUS-4 OR'd the
-- class-12 bit (0x800) into ClassMask, opening every
-- baseline skill to Paragon. Replaces the stock row by ID so
-- AzerothCore picks the patched mask on the SQL merge pass.
DELETE FROM `skillraceclassinfo_dbc` WHERE `ID` IN (
57,301,107,82,75,140,328,638,872,880,881,885,886,910,117,335,628,629,630,912,126,127,133,134,635,31,39,135,325,636,637,643,644,888,889,914,125,626,884,898,901,58,60,916,59,40,41,68,48,49,44,45,42,43,50,51,131,132,883,913,105,71,70,69,925,54,25,138,139,91,882,85,84,93,88,865,87,441,94,443,92,481,89,442,123,124,624,625,702,908,6,922,33,243,899,241,122,621,622,701,907,970,129,323,631,632,633,634,641,642,142,143,639,640,28,63,282,29,284,65,97,244,940,72,128,878,879,137,144,136,915,55,79,81,76,149,112,111,106,66,26,83,74,73,108,109,110,113,38,35,36,37,61,62,64,24,34,21,906,46,47,52,53,281,104,102,101,27,95,98,96,30,145,146,147,148,151,155,158,159,271,175,178,183,186,270,189,191,193,198,200,265,266,203,204,205,268,269,246,272,330,381,403,445,446,461,501,463,464,521,522,541,544,581,601,741,742,781,841,861,862,866,867,877,934,892,896,897,951,895,900,936,938,939,947
);
INSERT INTO `skillraceclassinfo_dbc` (`ID`,`SkillID`,`RaceMask`,`ClassMask`,`Flags`,`MinLevel`,`SkillTierID`,`SkillCostIndex`) VALUES
(57,6,-1,2176,1040,0,0,0),
(301,8,-1,2176,1040,0,0,0),
(107,26,-1,2049,1040,0,0,0),
(82,38,-1,2056,1040,0,0,0),
(75,39,-1,2056,1040,0,0,0),
(140,43,1115,2049,128,0,0,0),
(328,43,3071,2052,128,0,0,0),
(638,43,164,2049,128,0,0,0),
(872,43,32767,2056,128,0,0,0),
(880,43,1024,2052,128,0,0,0),
(881,43,32767,2432,128,0,0,0),
(885,43,1029,2050,128,0,0,0),
(886,43,512,2050,128,0,0,0),
(910,43,262143,2080,128,0,0,0),
(117,44,166,2052,128,0,0,0),
(335,44,2147483647,2122,128,0,0,0),
(628,44,1544,2052,128,0,0,0),
(629,44,167,2049,128,0,0,0),
(630,44,1112,2049,128,0,0,0),
(912,44,262143,2080,128,0,0,0),
(126,45,650,2052,128,0,0,0),
(127,45,32767,2061,128,0,0,0),
(133,46,36,2052,128,0,0,0),
(134,46,32767,2057,128,0,0,0),
(635,46,1674,2052,128,0,0,0),
(31,50,-1,2052,1040,0,0,0),
(39,51,-1,2052,1040,0,0,0),
(135,54,2147483647,2128,128,0,0,0),
(325,54,-1,2056,128,0,0,0),
(636,54,1133,2049,128,0,0,0),
(637,54,658,2049,128,0,0,0),
(643,54,8,3072,128,0,0,0),
(644,54,32,3072,128,0,0,0),
(888,54,261631,2050,128,0,0,0),
(889,54,512,2050,128,0,0,0),
(914,54,262143,2080,128,0,0,0),
(125,55,262143,2052,128,0,0,0),
(626,55,163839,2049,128,0,0,0),
(884,55,512,2050,128,0,0,0),
(898,55,262143,2080,128,0,0,0),
(901,55,261631,2050,128,0,0,0),
(58,56,-1,2064,1040,0,0,0),
(60,78,-1,2064,1040,0,0,0),
(916,95,524287,2080,640,0,0,0),
(59,96,2047,3072,1168,0,0,0),
(40,98,1101,3583,128,0,0,0),
(41,98,674,3551,160,0,21,0),
(68,101,4,3583,1170,0,0,0),
(48,109,690,3583,128,0,0,0),
(49,109,1101,3551,160,0,21,0),
(44,111,4,3583,128,0,0,0),
(45,111,2043,3551,160,0,21,0),
(42,113,8,3583,128,0,0,0),
(43,113,2039,3551,160,0,21,0),
(50,115,32,3583,128,0,0,0),
(51,115,2015,3551,160,0,21,0),
(131,118,32767,2056,146,1,0,0),
(132,118,32767,2053,146,20,0,0),
(883,118,32767,2112,402,0,0,0),
(913,118,262143,2080,146,0,0,0),
(105,120,2047,2304,1170,0,0,0),
(71,124,32,3583,1170,0,0,0),
(70,125,2,3583,146,0,0,0),
(69,126,8,3583,1170,0,0,0),
(925,129,-1,2080,128,0,63,0),
(54,130,2047,2176,1168,4,0,0),
(25,134,-1,3072,1040,10,0,0),
(138,136,32767,3536,128,0,0,0),
(139,136,32767,2053,128,0,0,0),
(91,137,1535,3551,160,0,21,0),
(882,137,512,3583,128,0,0,0),
(85,138,2047,3583,128,0,0,0),
(84,139,2047,3583,160,0,21,0),
(93,140,2047,3583,128,0,0,0),
(88,141,2047,3583,160,0,21,0),
(865,142,2047,3583,0,0,0,0),
(87,148,1,3551,1170,0,181,0),
(441,148,222,3583,1170,0,182,0),
(94,149,2,3551,1170,0,181,0),
(443,149,509,3583,1170,0,182,0),
(92,150,8,3551,1170,0,181,0),
(481,150,215,3583,1170,0,182,0),
(89,152,4,3551,1170,0,181,0),
(442,152,219,3583,1170,0,182,0),
(123,160,262143,2050,128,0,0,0),
(124,160,-1,3072,128,0,0,0),
(624,160,32,2049,128,0,0,0),
(625,160,262111,2049,128,0,0,0),
(702,160,-1,2112,128,0,0,0),
(908,160,262143,2080,128,0,0,0),
(6,162,2147483647,3551,128,0,0,0),
(922,162,262143,2080,128,0,0,0),
(33,163,-1,2052,1040,0,0,0),
(243,164,2047,3583,160,0,41,0),
(899,165,2047,3583,160,0,41,0),
(241,171,2047,3583,160,0,41,0),
(122,172,163839,2050,128,0,0,0),
(621,172,6,2049,128,0,0,0),
(622,172,1529,2049,128,0,0,0),
(701,172,163839,2112,128,0,0,0),
(907,172,524287,2080,128,0,0,0),
(970,172,163839,2052,128,0,0,0),
(129,173,32767,2312,128,0,0,0),
(323,173,32767,2256,128,0,0,0),
(631,173,520,2052,128,0,0,0),
(632,173,1190,2052,128,0,0,0),
(633,173,216,2049,128,0,0,0),
(634,173,1063,2049,128,0,0,0),
(641,173,32,3072,128,0,0,0),
(642,173,8,3072,128,0,0,0),
(142,176,-1,2056,128,0,0,0),
(143,176,-1,2052,128,0,0,0),
(639,176,128,2049,128,0,0,0),
(640,176,262015,2049,128,0,0,0),
(28,182,2047,3583,160,0,2,0),
(63,184,-1,2050,1040,0,0,0),
(282,185,2047,3583,128,0,61,0),
(29,186,2047,3583,160,0,2,0),
(284,197,2047,3583,160,0,62,0),
(65,198,2047,2050,1168,0,0,0),
(97,199,2047,2112,1168,0,0,0),
(244,202,2047,3583,160,0,41,0),
(940,205,524287,2176,2048,0,0,0),
(72,220,16,3583,1170,0,0,0),
(128,226,32767,2057,128,0,0,0),
(878,226,1024,2052,128,0,0,0),
(879,226,31743,2052,128,0,0,0),
(137,227,2047,3077,128,0,0,0),
(144,228,-1,2448,128,0,0,0),
(136,229,32767,3079,128,20,0,0),
(915,229,262143,2080,128,0,0,0),
(55,237,-1,2176,1040,0,0,0),
(79,238,2047,2056,1168,4,0,0),
(81,239,2047,2056,1168,0,0,0),
(76,241,2047,2056,128,40,0,0),
(149,242,2047,2056,1168,16,0,0),
(112,243,2047,2049,1170,0,0,0),
(111,244,2047,2049,1168,0,0,0),
(106,245,2047,2049,1168,0,0,0),
(66,246,2047,2050,1168,0,0,0),
(26,247,2047,3072,1168,20,0,0),
(83,252,2047,2057,128,0,0,0),
(74,253,-1,2056,1040,0,0,0),
(73,254,2047,2056,1168,10,0,0),
(108,255,2047,2049,1168,0,0,0),
(109,256,-1,2049,1040,0,0,0),
(110,257,-1,2049,1040,0,0,0),
(113,258,2047,2049,1168,10,0,0),
(38,260,2047,2052,128,0,0,0),
(35,262,2047,2052,128,0,0,0),
(36,263,2047,2052,128,0,0,0),
(37,264,2047,2052,128,0,0,0),
(61,267,-1,2050,1040,0,0,0),
(62,268,2047,2050,1170,0,0,0),
(64,269,2047,2050,1168,0,0,0),
(24,272,2047,3072,1168,10,0,0),
(34,273,2047,2052,128,0,0,0),
(21,293,2047,2051,128,40,0,0),
(906,293,262143,2080,128,0,0,0),
(46,313,64,3583,128,0,0,0),
(47,313,1983,3551,160,0,21,0),
(52,315,128,3583,128,0,0,0),
(53,315,1919,3551,160,0,21,0),
(281,333,2047,3583,160,0,62,0),
(104,353,2047,2304,1170,0,0,0),
(102,354,-1,2304,1040,0,0,0),
(101,355,-1,2304,1040,0,0,0),
(27,356,2047,3583,128,0,23,0),
(95,373,-1,2112,1040,0,0,0),
(98,374,262143,2112,1040,0,0,0),
(96,375,262143,2112,1040,0,0,0),
(30,393,2047,3583,160,0,161,0),
(145,413,2047,2116,128,40,0,0),
(146,413,2047,2083,128,0,0,0),
(147,414,2047,3183,128,0,0,0),
(148,415,2047,3583,128,0,0,0),
(151,416,2047,2049,192,0,0,0),
(155,416,2047,2050,192,0,0,1),
(158,416,2047,3136,192,0,0,1),
(159,416,2047,2060,192,0,0,1),
(271,416,2047,2448,192,0,0,2),
(175,418,2047,2049,384,0,0,0),
(178,418,2047,2050,384,0,0,0),
(183,418,2047,3332,384,0,0,1),
(186,418,2047,2192,384,0,0,1),
(270,418,2047,2120,384,0,0,1),
(189,419,2047,2060,640,0,0,2),
(191,419,2047,3072,640,0,0,1),
(193,419,2047,2192,640,0,0,0),
(198,419,2047,2050,640,0,0,1),
(200,419,2047,2049,640,0,0,2),
(265,419,2047,2304,640,0,0,0),
(266,419,2047,2112,640,0,0,1),
(203,420,2047,2061,1152,0,0,2),
(204,420,2047,3074,1152,0,0,1),
(205,420,2047,2320,1152,0,0,0),
(268,420,2047,2176,1152,0,0,0),
(269,420,2047,2112,1152,0,0,1),
(246,433,2047,2115,128,0,0,0),
(272,453,2047,2051,128,0,0,0),
(330,473,4095,3149,130,0,0,0),
(381,493,8,3583,164,0,0,0),
(403,515,2047,3551,128,0,0,0),
(445,533,128,3583,1170,0,181,0),
(446,533,95,3551,1170,0,182,0),
(461,553,64,3551,1170,0,181,0),
(501,553,4,3583,1170,0,182,0),
(463,554,16,3551,1170,0,181,0),
(464,554,207,3583,1170,0,182,0),
(521,573,-1,3072,1040,0,0,0),
(522,574,-1,3072,1040,0,0,0),
(541,593,-1,2304,1040,0,0,0),
(544,594,-1,2050,1040,0,0,0),
(581,613,-1,2064,1040,0,0,0),
(601,633,-1,2056,128,0,0,0),
(741,673,16,3583,128,0,0,0),
(742,673,2031,3551,160,0,21,0),
(781,713,255,3583,1170,0,181,0),
(841,733,128,3583,1170,0,0,0),
(861,753,64,3583,1170,0,0,0),
(862,754,1,3583,1170,0,0,0),
(866,755,2047,3583,160,0,41,0),
(867,756,512,3583,146,0,0,0),
(877,760,1024,3583,146,0,0,0),
(934,762,524287,2080,144,0,223,0),
(892,769,32767,3583,1040,0,0,0),
(896,770,-1,2080,1040,0,0,0),
(897,771,262143,2080,1040,0,0,0),
(951,771,2097151,3583,0,0,0,0),
(895,772,-1,2080,1040,0,0,0),
(900,773,262143,3583,160,0,41,0),
(936,776,262143,2080,128,0,0,0),
(938,777,524287,3583,2,0,0,0),
(939,778,524287,3583,2,0,0,0),
(947,778,2097151,3583,0,0,0,0);