e649402163
Server-side cross-class wildcard pass for several talents that were
previously locked to a single SpellFamilyName, plus a server+client
Warrior stance bypass and a Paragon-aware Mirror Image rebuild that
mimics the owner's spellbook instead of stock Frostbolt/Fire Blast.
Talent expansions (Paragon owners only; stock classes unchanged):
- Cold Snap (11958): resets cooldown of any Frost-school spell.
- Nature's Swiftness (17116, 16188) + Predator's Swiftness (69369):
instant-cast on any Nature-school spell.
- Vampiric Embrace (15286): leech-heals from any single-target
Shadow-school spell.
- Fingers of Frost (44543/44545) + Frostbite (11071/12496/12497):
proc from any Frost-school chill effect (DK Howling Blast / Icy
Touch / Chains of Ice, Hunter Frost Trap, Shaman Frost Shock,
cross-class chill auras via SPELL_AURA_MOD_DECREASE_SPEED).
- Maelstrom Weapon (53817): now also affects Mage Fireball (133),
Frostbolt (116), and Arcane Blast (30451) at every rank, both
for cast-time/cost spellmod and for stack consumption.
Warrior stance bypass:
- SpellInfo::CheckShapeshift returns SPELL_CAST_OK whenever a
Paragon caster hits any Stances!=0 spell (no SpellFamilyName
gate). Stock classes still see the regular form rules.
- Client side: patch-enUS-4.MPQ now zeroes Stances on every
SPELLFAMILY_WARRIOR Spell.dbc row (105 spells) so the engine's
pre-cast "Must be in Battle/Defensive/Berserker Stance" check
no longer eats CMSG_CAST_SPELL packets for Paragons. Server
bypass enforces the actual decision; stock Warriors still
error mid-cast if they actually click while out of stance.
- patch-enUS-5.MPQ Lua tooltip post-processor recolors and
appends "(Paragon: bypassed)" to "Requires *Stance*" lines on
Warrior abilities, plus Paragon notes on Maelstrom Weapon and
Mirror Image tooltips. Action-bar UseAction wrapper routes
stance-gated Warrior spell clicks through CastSpellByName so
the stance-zero DBC + server bypass actually run.
Mirror Image:
- npc_pet_mage_mirror_image rebuilds its spell list from the
Paragon owner's spellbook on InitializeAI AND JustEngagedWith
(the second pass + events.Reset clears any stale events the
CasterAI base scheduler may have queued from stock 59637 /
59638 entries before the rebuild ran).
- Curated filter keeps single-target damaging spells (instant,
cast-time, or channeled) with a base cooldown <=10s, with the
"damaging" definition expanded to include
SPELL_EFFECT_TRIGGER_MISSILE and
SPELL_AURA_PERIODIC_TRIGGER_SPELL so Arcane Missiles
qualifies. Rejects passives, AoE, melee/ranged weapon strikes,
item/reagent/stance/equip-gated, and lower spell ranks.
- UpdateAI picks a random spell from the curated list per cast
and reschedules the next pick by the actually-cast spell's
cast/channel duration + 750ms breather, so a 5s Arcane
Missiles channel waits its full duration before re-rolling
rather than visually looping across four images.
Helpers:
- Unit::IsParagonWildcardCaller / Unit::ParagonFamilyMatches
used by Spell.cpp, SpellInfo.cpp, Player.cpp,
SpellAuraEffects.cpp, and the spell scripts.
- SpellInfo::CheckShapeshift signature gains an optional caster
pointer; all call sites updated.
SQL migrations under modules/mod-paragon/data/sql/db-world/updates/:
- 2026_05_11_01.sql Vampiric Embrace spell_proc relax + script
gate (CheckProc enforces stock for non-Paragon).
- 2026_05_11_02.sql Maelstrom Weapon spell_proc relax (initial,
superseded by _04 below for stack-consumption fix).
- 2026_05_11_03.sql Fingers of Frost / Frostbite spell_proc relax
and spell_script_names binding.
- 2026_05_11_04.sql Maelstrom Weapon spell_proc fixup: restore
SpellPhaseMask=1 (CAST) and AttributesMask=8
(REQ_SPELLMOD); previous _02 set 8/0 which
silently dropped every proc event.
Diagnostics from this debugging session demoted from LOG_INFO to
LOG_DEBUG (silent at default info level) so production logs stay
quiet but the probes remain available for reproducing future
regressions: pet_mage.cpp MirrorImage probe/kept/rebuild/init/
engage/cast lines and SpellInfo.cpp CheckShapeshift bypass line.
CLIENT-PATCHES.md updated to document the new Warrior stance DBC
patcher (_patch_spell_dbc_stances.py), the spell-tooltip post-
processor and stance UseAction wrapper in patch-enUS-5.MPQ, and
the Mirror Image / Maelstrom Weapon Paragon notes.
Co-authored-by: Cursor <cursoragent@cursor.com>
270 lines
17 KiB
Markdown
270 lines
17 KiB
Markdown
# Fractured Client Patches
|
||
|
||
Binary client artifacts that pair with this server. They live on the
|
||
[Releases page](https://github.com/Dawnforger/Fractured/releases),
|
||
**not** in the tree, so the repo stays lean and binaries can be
|
||
re-downloaded without bloating `git clone`.
|
||
|
||
This file is the table of contents and install guide.
|
||
|
||
**Launcher (Windows):** The maintained client launcher lives in
|
||
[`tools/fractured-launcher-electron/`](../../tools/fractured-launcher-electron/)
|
||
(see its README for build and config). **Public downloads** for the launcher
|
||
and mirrored patch assets are pushed to
|
||
[Fractured-Distro releases](https://github.com/Dawnforger/Fractured-Distro/releases)
|
||
when a release is published here (workflow **Sync release to Fractured-Distro**).
|
||
|
||
---
|
||
|
||
## What ships in a release
|
||
|
||
| Artifact | Size | Purpose |
|
||
|---|---|---|
|
||
| `patch-enUS-4.MPQ` | ~5 MB | DBC + GlueXML bake. Adds `CLASS_PARAGON` (id 12), the character-create slot, glue strings, game-table DBCs, and a patched `Spell.dbc`: **(1)** `RuneCostID` zeroed on every rune-cost spell so non–Death Knight clients still send DK casts (rune costs are shown via `RuneFrame.lua`); **(2)** `Reagent[]` / `ReagentCount[]` zeroed on every spell whose `SpellFamilyName` is non-zero (all class abilities), while profession crafts (`SpellFamilyName == 0`) keep their materials. Both edits mirror server load-time corrections so client preflight and server validation stay aligned. Required for character creation as Paragon to even show up. |
|
||
| `patch-enUS-5.MPQ` | ~64 KB | FrameXML overrides. Replaces stock `PlayerFrame.lua` / `RuneFrame.lua` / `ComboFrame.lua` / `UnitFrame.lua` / `SpellBookFrame.lua` + `SpellBookFrame.xml` with Paragon-aware versions: rune simulator, combo-point simulator, server-authoritative resource sync over the `PARAA` addon channel, action-button usability + click guards, an expanded spellbook (higher `MAX_SPELLS`, 24 skill-line tabs instead of stock 8) so all-class spells render, Paragon stat tooltips on the character sheet (including filtering duplicate “attack power from strength” lines so the paper doll matches server AP), a tooltip post-processor that appends ", Paragon" to the "Classes:" line on class-restricted gear / glyphs (the server bypasses `AllowableClass` for class 12, but the engine paints the line red and omits Paragon — the Lua hook recolors it green and adds the name so the player can tell it's wearable), a **spell-tooltip post-processor** that (1) recolors and appends "(Paragon: bypassed)" to "Requires *Stance*" lines on Warrior abilities (server-side `SpellInfo::CheckShapeshift` skips stance enforcement for Paragons on `SPELLFAMILY_WARRIOR` spells, but the client still renders the requirement from the stock `Stances` DBC field which we deliberately leave unzeroed so stock Warriors keep enforcement), (2) appends a Paragon line to **Maelstrom Weapon** (53817) noting that Fireball / Frostbolt / Arcane Blast also benefit, and (3) appends a Paragon line to **Mirror Image** (55342) noting that the images cast random damage spells with a cast time from the caster's spellbook instead of Frostbolt — all three patches gate on `UnitClass("player") == "PARAGON"` so stock-class tooltips are byte-identical to vanilla, and **PetFrame** re-anchored so the **pet unit frame sits below the rune row** for Paragon (stock layout had runes overlapping the pet portrait). A **Warrior stance click bypass** wraps `UseAction` so that for Paragon characters, clicking an action slot bound to a stance-gated Warrior ability (Whirlwind, Charge, Pummel, Shield Slam, Hamstring, Overpower, Shield Bash, Shield Block, Disarm, Revenge, Spell Reflection, Recklessness, Bladestorm, Shockwave, Concussion Blow, Last Stand, Sweeping Strikes, Mocking Blow, Heroic Fury, Slam, Devastate, Intercept) routes through `CastSpellByName(name)` instead of the engine's stance-gated `UseAction` path; the engine's stance pre-check inside `UseAction` would otherwise drop the cast packet client-side and our server-side `CheckShapeshift` bypass would never get to run. Stock classes never enter the bypass branch. The paper-doll **ammo slot** follows stock visibility rules (shown for hunters / ranged weapons; hidden when `UnitHasRelicSlot` applies). |
|
||
| `patch-enUS-6.MPQ` | ~134 KB | The `ParagonAdvancement` addon. Replaces the talent pane (`N` key) for Paragon characters with the Character Advancement panel: per-class spell tabs, talent grid, Overview/Search tabs, AE/TE currency, commit / reset / preview, login-time toast suppression, a **PETS** tab with live hunter pet talent trees (preview learn, no TE/AE cost), a dedicated **Reset Pet Talents** control (server `PARAA` `C RESET PET TALENTS` — instant, no gold, no confirmation; requires matching worldserver), bottom-row **Reset all Abilities / Reset Build / Reset all Talents** disabled while on the PETS tab so those paths cannot dismiss the pet or unlearn Tame Beast, and a **Builds** page (full-pane overlay opened from the bottom-row Builds button) for saving named, icon-tagged loadouts: New Build (+) icon picker reuses `MACRO_ICON_FILENAMES`, right-click for edit/delete, shift-left-click to favorite (favorites bubble to the top), left-click pops a Load Build confirm. Build swaps reset + refund AE/TE, re-spend on the saved recipe, and **park hunter pets** to `PET_SAVE_NOT_IN_SLOT` so their name/talents/exp are preserved across swaps. |
|
||
| `Wow.exe` | ~7.5 MB | 3.3.5a (build 12340) client byte-patched to skip the MPQ signature check so custom `patch-enUS-N.MPQ` files load. Diff against stock is a few bytes; everything else is unchanged. |
|
||
|
||
Server and client work as a pair: the addon talks to `mod-paragon` on the
|
||
worldserver via `WHISPER` addon-channel messages with the `PARAA` prefix
|
||
(currency push, spell/talent snapshot, commit, combo points, rune
|
||
cooldowns, learn-toast silence window, **`C RESET PET TALENTS`**
|
||
for hunter pet talent resets from the Character Advancement PETS tab,
|
||
and the **build catalog** verbs `Q BUILDS` / `C BUILD NEW` / `C BUILD
|
||
EDIT` / `C BUILD DELETE` / `C BUILD FAVORITE` / `C BUILD LOAD` for the
|
||
saved-loadout system on the Builds page). Build swaps require the
|
||
matching worldserver image because the swap path is server-driven
|
||
(snapshot → reset → re-spend → pet park/unpark). Mismatched versions
|
||
usually manifest as the panel rendering blank or AE/TE reading 0/0.
|
||
|
||
---
|
||
|
||
## Install
|
||
|
||
You need a 3.3.5a (build 12340) WoW client. ChromieCraft's free 3.3.5a
|
||
download is the canonical clean source if you don't already have one.
|
||
|
||
1. Download every artifact from the latest release matching this server
|
||
build:
|
||
`https://github.com/Dawnforger/Fractured/releases/latest`
|
||
2. Replace `Wow.exe` at the root of your client install with the patched
|
||
one from the release. Keep a backup of the original if you want.
|
||
3. Drop `patch-enUS-4.MPQ`, `patch-enUS-5.MPQ`, `patch-enUS-6.MPQ` into
|
||
`Data/enUS/`. (For other locales, rename the suffix accordingly —
|
||
contents are locale-independent, only the filename ordering matters
|
||
to the loader.)
|
||
4. Edit `Data/enUS/realmlist.wtf` to point at your server, e.g.
|
||
`set realmlist your.host.tld`.
|
||
5. Launch `Wow.exe`. The login screen should reach the realm list. Make
|
||
a Paragon character (the new class entry on the create screen) and
|
||
press `N` in-world to open the Character Advancement panel.
|
||
|
||
If the panel opens empty / AE+TE read 0/0 / `N` opens the stock talent
|
||
pane: your client is loading an older `patch-enUS-6.MPQ`, or the
|
||
worldserver image is older than commit `4d2a80d` (the
|
||
`character_paragon_panel_spell_revoked` migration). Pull both ends to
|
||
the same release tag and rebuild the worldserver image.
|
||
|
||
If the **client** shows the Paragon class on the create screen but the
|
||
server replies **Character Creation Failed** (sometimes shown as
|
||
"Error creating character") when you pick it -- **or** the character
|
||
is created but spawns with no weapon / armor proficiencies (auto-attack
|
||
greys out, can't equip anything beyond a fist), or with the proficiency
|
||
**skills** but no **passive spells** like Block, Parry, Dual Wield --
|
||
the worldserver is missing one of four pieces of class-12 data. All
|
||
ship as SQL migrations under
|
||
`modules/mod-paragon/data/sql/db-world/updates/` and are auto-applied
|
||
by AzerothCore's DBUpdater on every `ac-db-import` run, but the SQL
|
||
files are baked into the dbimport Docker image at build time -- so a
|
||
stale image won't pick up new migrations. Fix:
|
||
|
||
```bash
|
||
git pull origin main
|
||
docker compose build ac-db-import ac-worldserver
|
||
docker compose up -d ac-db-import
|
||
docker compose restart ac-worldserver
|
||
```
|
||
|
||
Existing class-12 characters created before these migrations will
|
||
keep their broken state -- the cascade only fires inside
|
||
`Player::Create` and `Player::LearnDefaultSkill` at character spawn.
|
||
Delete the old Paragon and re-roll after the rebuild.
|
||
|
||
The four migrations:
|
||
|
||
- `2026_05_09_00.sql` -- DBC overlay rows for `chrclasses_dbc` and
|
||
`skillraceclassinfo_dbc`. Without this the server can't even
|
||
resolve class 12 in `sChrClassesStore`. See **Server-side Paragon
|
||
DBC overlay** below.
|
||
- `2026_05_10_00.sql` -- `playercreateinfo`, `playercreateinfo_action`,
|
||
and `player_class_stats` rows for class 12. Without this
|
||
`Player::Create` rejects every (race, class=12) pair as an
|
||
"invalid race/class pair" and the worldserver prints
|
||
`class-N Level-L does not have stats data!` integrity warnings on
|
||
load.
|
||
- `2026_05_10_01.sql` -- 20 `playercreateinfo_skills` rows
|
||
(`classMask = 2048` = class 12) granting every weapon /
|
||
armor proficiency at level 1. Without this a Paragon spawns with
|
||
only the universal `classMask = 0` skills (Defense, Unarmed,
|
||
Cloth, languages, Mounts) -- no Swords, no Mail, no Shield, etc.
|
||
- `2026_05_10_02.sql` -- 3,314 `skilllineability_dbc` rows opening
|
||
the class-12 bit on every SkillLineAbility row our patched
|
||
`SkillLineAbility.dbc` modified. AC reads these rows in
|
||
`Player::LearnDefaultSkill` to drive the `skill -> passive spell`
|
||
cascade. Without it the proficiency *skills* from `_01.sql` exist
|
||
but the *passive spells* (Block, Parry, Dual Wield, Defense,
|
||
weapon Shoot, racial Mace/Sword Specialization, etc.) never auto-
|
||
learn, so the spellbook past the racials looks empty.
|
||
|
||
After the rebuild + restart, `ac-worldserver` should log
|
||
`>> Loaded 72 Player Create Definitions` (was 62 pre-Paragon),
|
||
`>> Loaded 1391 Player Create Skills` (was 1371),
|
||
`>> Loaded 10219 SkillLineAbility MultiMap Data` (unchanged total --
|
||
the SQL overlay replaces existing rows by ID, doesn't add new ones),
|
||
and character creation succeeds for any DK-eligible race with a full
|
||
weapon / armor kit and the matching passive spells.
|
||
|
||
If the client **logs in** successfully but **disconnects immediately**
|
||
when entering the realm: the auth server is handing your client the
|
||
wrong world-server address. On a fresh local install the seed defaults
|
||
to `127.0.0.1` (commit landing this paragraph). If your DB was
|
||
imported from an older Fractured checkout, the seed may still point at
|
||
`hsrwow.net`, which sends the client to our production world server
|
||
instead of yours. Fix:
|
||
|
||
```bash
|
||
# Docker:
|
||
docker exec ac-database mysql -uroot -ppassword \
|
||
-e "UPDATE acore_auth.realmlist SET address='127.0.0.1' WHERE id=1;"
|
||
docker compose restart ac-authserver
|
||
```
|
||
|
||
Substitute your public hostname/IP for `127.0.0.1` if remote players
|
||
will be connecting. See `BUILD-NATIVE.md` -> *Production deployment
|
||
overrides* for the full list of values to set on a production box.
|
||
|
||
---
|
||
|
||
## Server-side Paragon DBC overlay (automatic)
|
||
|
||
The Fractured **client** learns about Paragon from `patch-enUS-4.MPQ`
|
||
(DBC + GlueXML). The **worldserver** never reads your MPQs — it reads
|
||
plain `.dbc` files under its `DataDir` (`.../data/dbc/` by default).
|
||
|
||
Stock Docker installs populate `data/dbc/` from a vanilla 3.3.5a
|
||
extract (`ac-client-data-init` in `docker-compose.yml`). That tree has
|
||
no `ChrClasses` row for id **12** and no class-12 bit on
|
||
`SkillRaceClassInfo` rows, which would normally trigger:
|
||
|
||
`Class (12) not found in DBC while creating new char ... wrong DBC files or cheater?`
|
||
|
||
…and reject the create with `CHAR_CREATE_FAILED`.
|
||
|
||
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:
|
||
|
||
- `chrclasses_dbc` — 1 row defining class 12 ("Paragon", power=Mana,
|
||
family=Warrior, expansion=2).
|
||
- `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.
|
||
|
||
`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`.
|
||
|
||
### Regenerating the migration
|
||
|
||
The SQL is auto-generated from the patched DBCs that already live
|
||
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
|
||
```
|
||
|
||
If the regenerated SQL has new content, commit it as a **new** dated
|
||
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.
|
||
|
||
### Manual DBC overlay (rare, fallback)
|
||
|
||
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
|
||
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
|
||
```
|
||
|
||
**Native:** copy into `<CMAKE_INSTALL_PREFIX>/data/dbc/` and restart.
|
||
|
||
This is **not required** for normal operation — the SQL migration
|
||
covers everything `mod-paragon` needs at runtime. Use the manual
|
||
overlay only when you're consciously bypassing the SQL merge layer.
|
||
|
||
---
|
||
|
||
## Building the patches yourself
|
||
|
||
The MPQs are reproducible from the dev tree (not in this repo —
|
||
`Classless Dev/Paragon Patch UI/` and
|
||
`Classless Dev/Paragon Advancement/` on the maintainer's workstation)
|
||
via two PowerShell scripts in `tools/`:
|
||
|
||
```
|
||
tools\build_paragon_ui_patch.ps1 -Deploy # -> patch-enUS-5.MPQ
|
||
tools\build_paragon_advancement_patch.ps1 -Deploy # -> patch-enUS-6.MPQ
|
||
```
|
||
|
||
`patch-enUS-4.MPQ` is the DBC + GlueXML bake; the bake scripts live with
|
||
the rest of the dev tooling and are not part of this repo by design
|
||
(see the repo-tidy policy in `README.txt` next to this file). Typical
|
||
order on a maintainer machine:
|
||
|
||
1. `fractured-tooling/from-workspace-root/_patch_spell_dbc_runes.py` — stage `Spell.dbc` with `RuneCostID` cleared.
|
||
2. `fractured-tooling/from-workspace-root/_patch_spell_dbc_reagents.py` — same staged `Spell.dbc`, clear class-spell reagents for client preflight.
|
||
3. `fractured-tooling/from-workspace-root/_patch_spell_dbc_stances.py` — same staged `Spell.dbc`, zero the `Stances` field on every `SpellFamilyName == 4` (Warrior) row so the client engine's "Must be in Battle/Defensive/Berserker Stance" pre-cast check stops eating `CMSG_CAST_SPELL` packets for Paragon casters who never picked the stance form. The server's `SpellInfo::CheckShapeshift` Paragon bypass takes over from there. Stock Warriors still see the server-side stance error mid-cast if they actually click while out of stance.
|
||
4. `fractured-tooling/from-workspace-root/_make_paragon_dbc_patch.py` — rebuild `ChrClasses` / `CharBaseInfo` / game tables, then pack `patch-enUS-4.MPQ`.
|
||
|
||
The patched `Wow.exe` is a one-time hex-edit of the stock 3.3.5a
|
||
client. The diff is publicly documented in the WoW emulation community
|
||
under names like "MPQ signature patch" / "no-CD-signature patch".
|
||
|
||
---
|
||
|
||
## Versioning
|
||
|
||
Releases are tagged to match the state of `main` they were built from.
|
||
The release notes call out which server commit shipped alongside each
|
||
artifact set, so a contributor running `git checkout <tag>` on this
|
||
repo can pull the matching client bundle and have a guaranteed-aligned
|
||
end-to-end build.
|