- default-launcher.json files: only Wow-patched.exe from release.
- config-store: strip deprecated patch-Z.MPQ from merged files; rewrite
launcher.json on load if user still had that entry.
- Docs/scripts examples updated; version 1.0.4.
Co-authored-by: Cursor <cursoragent@cursor.com>
AppImage mounts read-only at /tmp/.mount_*; writing launcher.json beside
execPath failed. Use app.getPath('userData') for linux/darwin when packaged.
Bump version to 1.0.3.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Trim input; fail fast if tag contains whitespace (common mistake: pasting
release title instead of git tag).
- Multiline GITHUB_OUTPUT for tag value safety.
- README checklist + input description clarify tag vs title.
Co-authored-by: Cursor <cursoragent@cursor.com>
- PanelLearnSpellChain: record every non-chain passive as panel_spell_child;
only revoke non-passive (Blood Presence, Death Coil, Death Grip, etc.).
- RevokeUnwantedCascadeSpellsForPlayer: skip passive rewards on login sweep.
- RevokeBlockedSpellsForPlayer: migrate legacy passive revoke rows to
children; walk (parent, revoked) pairs from DB.
- PruneSkillLineCascadeChildrenFromDb: only strip actives wrongly stored as
children; never strip passives.
- SpellInfoCorrections: set SPELL_ATTR0_PASSIVE on Forceful Deflection (49410)
and Runic Focus (61455) so IsPassive() matches spellbook behavior.
- PanelUnlearnTalentPurchase: mirror resetTalents (_removeTalentAurasAndSpells,
_removeTalent, SendTalentsInfoData) so Beast Mastery loss triggers pet reset.
- OnPlayerLogin: run legacy passive attach before scoped cascade sweep.
- Add .paragon recalibrate GM command (RBAC modify): full panel reset + AE/TE
reconciliation for selected player or self.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Merge baked base_url/owner/repo/release_tag at load time (no inject script,
no fractured-release-channel.json, no CI env for pack).
- Fix mergeConfig deep-merge for gitea, patch_manifest, launcher_updates_from_github.
- Remove inject-release-channel.js and fractured-release-channel.json.
Co-authored-by: Cursor <cursoragent@cursor.com>
Release tags can point at commits older than launcher lib additions; building
only from the tag omitted gitea-release.js etc. Fetch default branch and
checkout tools/fractured-launcher-electron from it before npm ci/pack.
Co-authored-by: Cursor <cursoragent@cursor.com>
These modules were required by main.js / auto-update.js / github.js but never
committed, so packaged builds lacked them and crashed at startup.
Co-authored-by: Cursor <cursoragent@cursor.com>
Two related additions to mod-paragon:
* HandleCommit gains a third payload section, " u:<id>,...", carrying
spell IDs the player wants to refund + unlearn in the same commit
that learns / talents through. The protocol stays backward-compat
(older clients omit the section). PanelUnlearnSpellPurchase mirrors
the per-spell branch of HandleParagonResetAbilities: tracked passive
children are removed first, then the chain head, then panel_spells /
panel_spell_children / panel_spell_revoked rows for that purchase
are dropped, then LookupSpellAECost(head) is refunded into the
cache. Unlearns are applied before learns inside the commit so the
refund covers the same-commit spends. Allow-list for the silence
window now includes chain ranks + panel_spell_children for the
intentional unlearns so "You have unlearned X" toasts stay visible
for the targeted spell while cascade dependents stay silenced.
* ReconcileEssenceForPlayer reads panel_spells + panel_talents and
sets the cache to ComputeStartingAE/TE(level) - sum-of-spends.
Self-heals drift in either direction: clamps the cache down when
the player has more essence than their level + spends allow
(cheese clamp), and tops up when they have less (admin-tweak /
crash recovery). Wired into OnPlayerLogin (after LoadCurrencyFromDb,
before PushCurrency so the first balance the client sees is the
reconciled one) and OnPlayerLevelChanged (replaces the old
GrantLevelUpEssence delta -- Reconcile sets the absolute correct
balance from level + spend, so it subsumes the per-level grant and
the cheese clamp in one call). Costs come from the same
paragon_spell_ae_cost / config keys HandleCommit uses so the math
stays in lockstep across any future cost rebalance.
Both features ship in patch-enUS-6.MPQ v0.9.16: right-click a learned
spell row to queue an unlearn (header shows +N AE refund preview) and
hit Learn All to apply. The icon picker also got two fixes -- the
leading INV_Misc_QuestionMark is no longer duplicated, and the
selection ring is now a tooltip-border Frame anchored to the cell
bounds (the prior UI-ActionButton-Border texture rendered nearly
invisible at non-native sizes).
Co-authored-by: Cursor <cursoragent@cursor.com>
- inject-release-channel.js merges GITEA_* (or fractured-release-channel.json) into
default-launcher.json before electron-builder.
- CI passes existing GITEA_BASE_URL/OWNER/REPO secrets into the Windows pack job.
- npm run pack:win/publish:win run the injector; workflows use npm run pack:win.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Gate electron-updater GitHub provider on launcher_updates_from_github (default false)
so GITHUB_TOKEN no longer targets the source repo without latest.yml.
- Improve GitHub releases 404 hint when assets are on Gitea.
- Document in README and default-launcher.json.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Use --publish never in pack/CI so tagged builds do not require GH_TOKEN.
- Set build.publish to null and align publish:win with local-only packaging.
- Add Gitea release sync workflow and upload script; fetch script from default
branch so reruns work for tags that predate the script.
Co-authored-by: Cursor <cursoragent@cursor.com>
Server side of the v0.7.10 Builds drop. Squashes a few footguns from
the original Builds catalog and adds a one-click "save what I have
right now" path the Overview pane can hook directly into.
- HandleBuildSaveCurrent: new C BUILD SAVE_CURRENT verb. Inserts a
fresh build row, snapshots the live panel into its recipe, sets it
active. No AE/TE motion, no relearning -- just a named slot for
whatever the player already has.
- Reset abilities / Reset talents now SetActiveBuildId(0) and re-push
the catalog. Without this, the next swap silently overwrote the
active build's saved recipe with the (now empty/partial) post-reset
state -- effectively erasing the build.
- Delete of the *active* build is now a hard reset (HandleParagonResetAll):
unlearn everything the panel bought, refund all AE/TE. Deleting a
non-active slot still just removes the saved recipe row + parked pet.
- Load of the currently-active build is now a "revert to last snapshot"
instead of a no-op refresh: keeps the saved recipe authoritative,
parks the pet, resets, re-applies. Useful for discarding pending
edits.
- After a successful Learn All while a build is active: archive the
build's previous share_code + recipe into
character_paragon_build_share_archive* (so codes already posted to
Discord keep importing the frozen loadout), snapshot the new panel
into the live build, assign a fresh share_code, push catalog.
- HandleBuildImport now falls back to the archive tables when a code
isn't in the live catalog -- old shared codes resurrect the recipe
they pointed at when they were retired.
- Imports never copy pet_number (the parked pet belongs to the source
player); if the imported recipe contains Tame Beast we hint that the
importer needs to tame their own pet.
- BuildPanelOwnedSpellsAllowlist now walks SPELL_EFFECT_LEARN_SPELL
effects on talent rank spells (Mangle, Feral Charge, Mutilate, ...)
so the login cascade sweep stops revoking talent-granted active
abilities.
Schema: new mod-paragon migration 2026_05_10_05.sql adds
character_paragon_build_share_archive (+ _spells / _talents).
Co-authored-by: Cursor <cursoragent@cursor.com>
- Replace the "favorite" toggle with import-by-share-code: every build
gets a 6-char realm-unique alphanumeric code on creation; pasting one
into the BuildsPane share box copies the recipe (name + icon + spells
+ talents) into the importer's catalog as a new build, with a fresh
share code so the imported copy can be re-shared independently.
- Add C BUILD UNLOAD verb so the client can clear a stale active-build
pointer without forcing a swap. Wired to a new "Unload (clear active)"
right-click context menu entry on the active build.
- Per-build tooltip now shows "Remaining if loaded: X AE / Y TE",
computed server-side as total_earned - recipe_cost. Negative renders
red so the player sees insufficient-currency cases before clicking
Load. Suppressed for the active build (HandleBuildLoad short-circuits
on target == active so the line would be misleading).
- Schema migration 2026_05_10_04.sql: drop is_favorite from
character_paragon_builds and add share_code CHAR(6) UNIQUE NULL with
lazy backfill on every PushBuildCatalog (so pre-migration rows pick
up codes the first time the player opens the panel).
Co-authored-by: Cursor <cursoragent@cursor.com>
Server-side Character Advancement now stores named, icon-tagged build
recipes (panel-purchased spells + per-spec talent ranks) and atomically
swaps between them by snapshotting the active build, refunding AE/TE
through HandleParagonReset{Talents,Abilities}, and re-spending on the
target recipe. Hunter pets attached to a build are parked to
PET_SAVE_NOT_IN_SLOT (mirroring HandleStableSwapPet) so name, talents,
and exp survive swaps; non-hunter pets (warlock demon, DK ghoul, mage
water elemental) are NOT parked because the engine resummons them from
a fresh template each cast.
New PARAA verbs: Q BUILDS / C BUILD NEW / C BUILD EDIT / C BUILD
DELETE / C BUILD FAVORITE / C BUILD LOAD. The catalog is pushed on
login and after every mutation as a single addon message.
Schema (mod-paragon migration 2026_05_10_03.sql):
- character_paragon_builds (build_id PK, guid, name, icon, is_favorite,
pet_number, created_at)
- character_paragon_build_spells (build_id, spell_id)
- character_paragon_build_talents (build_id, spec, talent_id, rank)
- character_paragon_active_build (guid PK, build_id)
The talent recipe table is spec-keyed so a build remembers tank/dps
dual-spec layouts independently. Swaps are blocked while in combat.
Co-authored-by: Cursor <cursoragent@cursor.com>
Broaden OnPlayerIsClass for CLASS_CONTEXT_ABILITY, pet/charm/equip contexts; add PARAA C RESET PET TALENTS handler. Update CLIENT-PATCHES.md for patch-enUS-5/6 and PARAA.
Co-authored-by: Cursor <cursoragent@cursor.com>
- SpellInfoCorrections: zero Reagent/ReagentCount on spells with non-zero
SpellFamilyName so class abilities no longer require shards, candles,
etc., while profession crafts (SpellFamilyName 0) keep mats. Matches
the client Spell.dbc bake in patch-enUS-4.MPQ.
- Paragon_SC: OnPlayerIsClass returns true for CLASS_CONTEXT_EQUIP_RELIC
for paladin/druid/shaman/warlock/dk so Paragon can equip all relic types
in the ranged slot.
- CLIENT-PATCHES: document Spell.dbc reagent pass, rune script order, and
stock ammo slot behavior in patch-enUS-5.
Co-authored-by: Cursor <cursoragent@cursor.com>
Stock 3.3.5 hardcodes per-class stat -> AP/SP formulas in
Player::UpdateAttackPowerAndDamage and Unit::SpellBase{Damage,Healing}BonusDone,
so class 12 fell into the default branches and ended up with 0 AP and 0 SP
regardless of STR / AGI / INT / SPI. The character sheet, combat log, and
ability damage all reflected this, and Mental Quickness-style AP->SP plumbing
silently no-oped on Paragon characters.
Add Paragon-specific branches in core (no PlayerScript hooks - those caused
SIGSEGVs when the new mid-list enum entry shifted later hook ordinals and
broke vtable dispatch):
- StatSystem.cpp: melee and ranged AP = level*2 + STR + AGI - 20, mirroring
the formula the UI patch already advertises in tooltips.
- Unit.cpp: intrinsic SP = level*2 + INT + SPI - 20 (clamped >=0),
added symmetrically to SpellBaseDamageBonusDone and
SpellBaseHealingBonusDone so the single advertised Spell Power value the
character sheet renders matches what spells actually use in combat.
Drop the now-unused UnitDefines.h include in Paragon_SC.cpp - it was only
needed by the AP PlayerScript hook that was rolled back in favor of the
core change.
Co-authored-by: Cursor <cursoragent@cursor.com>
RevokeUnwantedCascadeSpellsForPlayer and RevokeBlockedSpellsForPlayer
built their allowlist only from character_paragon_panel_spells and
panel_spell_children. Many Character Advancement "abilities" (e.g.
Scourge Strike) are panel talents stored in character_paragon_panel_talents,
so learning Death Coil afterward activated DK skill lines and the sweep
removed those spells as false orphans.
Add BuildPanelOwnedSpellsAllowlist to union spell chains, talent rank spell
IDs up to the purchased rank, and passive children. Also keep the prior
fixes: clear stale panel_spell_revoked rows on purchase and skip+delete
revoke entries that now match the allowlist on login.
Co-authored-by: Cursor <cursoragent@cursor.com>
Two paths still rejected glyph use on Paragon characters even after the
earlier AllowableClass server bypass:
1. Spell::CheckItems (server) treated cast-from-glyph as a normal
"equipped item required" cast and called HasItemFitToSpellRequirements,
which only handles weapon/armor and falls through default for
ITEM_CLASS_GLYPH -> SPELL_FAILED_EQUIPPED_ITEM_CLASS. Skip that check
when the cast item itself is the glyph.
2. The 3.3.5 client engine pre-checks ItemTemplate.AllowableClass against
the player's class locally and refuses the right-click before sending
CMSG_USE_ITEM, regardless of what the server would do. Bake the
Paragon class bit (1<<11 = 2048) into AllowableClass for every
class-restricted item via a mod-paragon SQL migration so the engine's
pre-check passes for class 12.
Cache caveat: clients that previously inspected an affected item have
the old AllowableClass cached in Cache/<locale>/itemcache.wdb; deleting
the Cache folder forces a re-query. The server also caches item_template
in memory at boot, so this migration only takes effect for clients after
a worldserver restart (or .reload item_template) once the SQL has been
applied -- DBUpdater handles the SQL automatically on the next start.
Co-authored-by: Cursor <cursoragent@cursor.com>
Paragon is a classless concept layered on top of WotLK class data: stock items and class glyphs gate equip / vendor visibility / loot rolls / AH 'usable' filter via ItemTemplate.AllowableClass, which never has the class-12 bit (0x800). Bypassing the gate at the five enforcement sites lets Paragon equip any class-restricted item -- including class glyphs, since EffectApplyGlyph itself has no class check beyond the item gate. Race / level / proficiency / skill / required-spell checks still apply, so Paragon can't skip baseline progression.
Co-authored-by: Cursor <cursoragent@cursor.com>
Paragon OnPlayerHasActivePowerType only reported POWER_RAGE when
Paragon.MultiResource.HasActivePowers was true. Core melee rage uses
Unit::DealDamage -> HasActivePowerType(POWER_RAGE) before RewardRage;
missing module config (common on fresh clones / Docker without merged
mod_paragon.conf) fell through to GetOption(..., false) and white swings
never generated rage. Match mod_paragon.conf.dist and default the C++
fallback to true so Paragon behaves correctly out of the box. Set
Paragon.MultiResource.HasActivePowers = 0 only for intentional test builds.
Co-authored-by: Cursor <cursoragent@cursor.com>
Extend spell_paragon_arcane_torrent to EnergizeBySpell POWER_RAGE 150 (15
displayed; rage uses the same 10x internal scaling as runic power, see the
`-20` rage decay step in Player::Regenerate). Paragon's combined Arcane
Torrent now refunds mana, rage, energy, and runic power -- whichever pool
the character is using at the moment. ModifyPower no-ops on pools with
MaxPower == 0, so it's safe even before the Paragon picks up rage abilities.
Co-authored-by: Cursor <cursoragent@cursor.com>
Building on the previous fix that hid the rogue and DK Arcane Torrent variants
for Paragon Blood Elves: instead of just dropping the duplicates, turn the
remaining mana variant (28730) into a single combined racial that refunds
whichever resource pool the character is using at the moment.
Add SpellScript spell_paragon_arcane_torrent in modules/mod-paragon/src/
Paragon_SC.cpp. Hooks AfterCast on 28730: when the caster is class 12 the
script EnergizeBySpell's 15 energy and 150 internal runic power (= 15 displayed,
matching stock 25046 / 50613 amounts) on top of the spell's stock mana effect.
ModifyPower no-ops on pools the player has no max for, so it is safe even
before the Paragon picks up energy- or RP-using abilities. Non-Paragon Blood
Elves are untouched and keep learning their stock racial.
Update migration 2026_05_10_03.sql to also register the script binding via
spell_script_names (28730 -> 'spell_paragon_arcane_torrent'). Idempotent
DELETE + INSERT.
Co-authored-by: Cursor <cursoragent@cursor.com>
Blood Elf racial skill line 756 grants three different Arcane Torrent spell
IDs (28730 mana, 25046 rogue energy, 50613 DK runic power). The blanket
SkillLineAbility overlay in 2026_05_10_02 OR'd class 12 into all three, so
Paragon Blood Elves auto-learned every variant and the spellbook listed three
identical "Arcane Torrent" entries.
Add db-world migration 2026_05_10_03.sql to clear the class-12 bit on the rogue
and DK rows only (SkillLineAbility IDs 13338 and 17510), leaving 28730 as the
sole Paragon-visible racial cast. OnPlayerLogin removes 25046/50613 if still
present so existing characters self-heal without a manual unlearn.
The fractured-tooling DBC overlay generator is updated in the same workspace
to skip those two rows when regenerating SkillLineAbility SQL.
Co-authored-by: Cursor <cursoragent@cursor.com>
Player::LearnTalent enforces the column-arrow prereq (talentInfo->DependsOn)
even when called with command=true, so Character Advancement's commit path
was silently dropping any talent whose Talent.dbc row points to an unrelated
sibling -- e.g. Deep Wounds (depends on Improved Heroic Strike), Bloody
Vengeance (depends on Dark Conviction), Expose Weakness (depends on Lethal
Shots). Players spent points in the panel, hit Learn All, and the talent
silently never reached addTalent / OnPlayerLearnTalents -- the snapshot came
back without it and the client repainted the points as "unspent."
The Character Advancement panel gates progression via AE/TE essence cost,
not via the spec-tree column arrows, so the DependsOn rule doesn't apply to
class 12. Skip it for Paragon, mirroring the existing class-mask bypass a
few lines above.
Co-authored-by: Cursor <cursoragent@cursor.com>
Companion to 2026_05_09_00.sql (DBC overlay for chrclasses + srci) and
2026_05_10_01.sql (proficiency skill rows in playercreateinfo_skills).
Those two grant the SKILL (Maces, Shield, Cloth, ...) to Paragon at
character creation; this one opens the SkillLineAbility rows that
CASCADE skill -> passive spell, so when a fresh Paragon is created
AC's `Player::LearnDefaultSkill` actually grants the proficiency
passives:
Block (107), Parry (3127), Dual Wield (674), Defense, weapon Shoot,
racial Mace/Sword Specialization, ...
Without this overlay, a class-12 Paragon spawns with the right skill
rows but a near-empty spellbook past the racials and class defaults
that come from playercreateinfo_action.
How it works
------------
AC's DBCStores.cpp::LoadDBC loads each store from the on-disk .dbc
file first, then merges <table>_dbc world-DB rows on top. Our patched
client SkillLineAbility.dbc (in patch-enUS-4.MPQ) OR's the class-12
bit (0x800) into ClassMask on 3,314 rows -- the same rows the server
needs for the cascade to fire on Paragon. Stock Docker installs use
the upstream `ac-wotlk-client-data` image which fills data/dbc/ from
a vanilla 3.3.5a extract, so without this SQL overlay the server
runs against an unmodified SkillLineAbility.dbc and the cascade
never fires.
Generation
----------
Auto-generated end-to-end by
`fractured-tooling/from-workspace-root/_gen_paragon_dbc_overlay_sql.py`,
extended in this commit to handle SkillLineAbility.dbc (14-int
WotLK layout, 56 bytes per record). The script diffs patched vs
stock by ID, keeps only rows whose stock ClassMask did NOT include
the class-12 bit but whose patched ClassMask does, and emits the
3,314 REPLACE INTO rows. Re-running with the same inputs is byte-
stable.
Verified locally
----------------
- Migration applies twice in a row at exactly 3,314 SQL overlay rows
(idempotent: DELETE WHERE ID IN (...) before INSERT).
- ac-worldserver restart logs:
>> Loaded 10219 SkillLineAbility MultiMap Data
-- the same total as stock (10,219 rows), confirming our overlay
REPLACES existing rows by ID rather than appending duplicates.
- Spot-checked spell IDs: 107 (Block, ClassMask 2115 = warrior +
paladin + dk + Paragon), 3127 (Parry, 2063), 674 (Dual Wield,
2157), 75 (Auto Shoot, 2052) all carry the 0x800 bit.
Existing characters
-------------------
The cascade fires inside Player::Create and Player::LearnDefaultSkill
at character spawn, so existing class-12 characters created before
this migration keep their broken state. Delete and re-roll, or hand-
grant the missing spells via .learn for individual existing chars.
CLIENT-PATCHES.md updated to add the third symptom ("proficiency
skills exist but passive spells don't auto-learn") and document
this migration as the fourth piece of the class-12 bootstrap.
Co-authored-by: Cursor <cursoragent@cursor.com>
Companion to 2026_05_10_00.sql. The spawn-data migration teaches the
worldserver where Paragon characters spawn and what per-level base
stats they have; this one teaches it which weapon/armor skill lines
to grant at first character login.
Without these rows a fresh Paragon character lands in their newbie
zone with no weapon or armor proficiencies (auto-attack greys out
on anything beyond a fist) -- the universal classMask=0 rows in
playercreateinfo_skills only cover Defense, Unarmed, Cloth,
languages, Mounts, and Companion Pets.
Adds 20 rows in playercreateinfo_skills with classMask=2048 (class
12 only) for every weapon and armor proficiency:
- Weapons: Swords, Axes, Bows, Guns, Maces, 2H Swords, Dual Wield,
Staves, 2H Maces, 2H Axes, Daggers, Thrown, Crossbows,
Wands, Polearms, Fist Weapons.
- Armor: Plate Mail, Mail, Leather, Shield. (Cloth already
granted via the classMask=0 universal row.)
Idempotent: DELETE WHERE classMask=2048 then INSERT, so it replays
cleanly on a partially-seeded DB (e.g. one where a contributor hand-
patched these rows before the migration landed).
Verified locally: applies cleanly twice in a row, worldserver restart
now logs `>> Loaded 1391 Player Create Skills` (was 1371 pre-Paragon
= +20 class-12 rows) and a freshly-rolled Draenei Paragon spawns with
the full weapon/armor kit.
CLIENT-PATCHES.md troubleshooting block updated to call out the
"Paragon spawns naked / can't equip anything" failure mode and list
all three migrations in the current rebuild recipe.
Co-authored-by: Cursor <cursoragent@cursor.com>
Companion to 2026_05_09_00.sql (DBC overlay). The DBC overlay teaches
the world server that class 12 (Paragon) exists; this migration
teaches it WHERE class-12 characters spawn, what action bar they boot
with, and what per-level base stats Player::InitStatsForLevel uses.
Without these rows, contributors hit:
- Player::Create -> "invalid race/class pair (R/12) - refusing"
and the client shows "Error creating character".
- WorldServer load -> "class-12 Level-L does not have stats data!"
integrity warnings.
Tables touched (idempotent: DELETE WHERE class=12 then INSERT):
- playercreateinfo : 10 rows, every DK-eligible race spawning
in their racial newbie zone (Northshire,
Valley of Trials, Ammen Vale, ...).
NOT Acherus -- Paragon is from-level-1.
- playercreateinfo_action : 46 rows, default action bar layout
per race (attack 6603, eat 78, racial,
etc.).
- player_class_stats : 80 rows, per-level base HP/Mana/STR/AGI/
STA/INT/SPI. Curve mirrors Warrior to
level 60, Paladin-style HP inflation
past 60 to keep Paragon competitive
in Wrath content.
Tables intentionally untouched: playercreateinfo_item is empty for
class 12 (Paragon ships no per-class starting items, only racial
kit), and the mask-based playercreateinfo_skills/_cast_spell/
_spell_custom rows already cover class 12 via their classMask=0
"all classes" entries.
Verified locally: applies cleanly twice in a row (idempotent),
worldserver restart now logs `>> Loaded 72 Player Create Definitions`
(was 62 pre-Paragon = +10 races for class 12) and creates a Draenei
Paragon without rejection.
CLIENT-PATCHES.md troubleshooting block updated to merge the two
"Character Creation Failed" modes (DBC overlay missing + spawn data
missing) into a single fix recipe. Existing contributors with a
pre-built dbimport image need
`docker compose build ac-db-import ac-worldserver` before this
migration is visible to DBUpdater; fresh clones get it on first
`docker compose up`.
Co-authored-by: Cursor <cursoragent@cursor.com>
The previous seed pinned auth/realmlist to production values
(`hsrwow.net` + RealmServerPort 47497), which silently bricked every
fresh local install: after auth login the realm hand-off pointed
clients at our public host, where their local credentials don't
exist, and they were dropped within a frame.
Seed now matches stock AzerothCore for solo dev:
- realmlist.address = 127.0.0.1 (was hsrwow.net)
- RealmServerPort = 3724 (was 47497)
Production owners apply both overrides post-dbimport via a one-shot
SQL UPDATE + an authserver.conf edit. Documented end-to-end in
contrib/fractured-dev-extras/BUILD-NATIVE.md (new "Production
deployment overrides" section) and the disconnect-after-login
symptom is called out in CLIENT-PATCHES.md.
Co-authored-by: Cursor <cursoragent@cursor.com>
Use utf8mb4_unicode_ci in base SQL (MariaDB lacks utf8mb4_0900_ai_ci).
Add Updates.ExceptionShutdownDelay to dbimport.conf.dist to match
DBUpdater expectations.
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>
Explains why Character Creation Failed occurs when the client has
patch-enUS-4 but Docker/native data/dbc is still vanilla: ChrClasses
row 12 only exists in the patched DBC set. Adds Docker volume copy
steps, native install path, and log verification.
Co-authored-by: Cursor <cursoragent@cursor.com>
Track vps-clone-without-docker.sh and vps-sparse-checkout-no-docker.sh
so a fresh GitHub clone can run the helper end-to-end. Mark both
executable in git for ./ usage after clone.
Documents the four binary artifacts that pair with this server
(patch-enUS-4/5/6.MPQ + patched Wow.exe), where they live (the
Releases page; not in the tree per the repo-tidy policy), and how a
contributor installs them on top of a clean 3.3.5a client. Cross-
referenced from the contrib/fractured-dev-extras README.
Co-authored-by: Cursor <cursoragent@cursor.com>
mod-paragon Paragon_Essence.cpp:
- Broaden SkillLinesLinkedToSpell: collect every SkillLineAbility row for
an anchor spell regardless of AcquireMethod, so anchor spells whose
primary SLA uses AcquireMethod 0 (e.g. Blood Strike) correctly identify
their skill lines and let the dependent classifier do its job.
- IsSpellSkillLineCascadeDependent / RevokeUnwantedCascadeSpellsForPlayer
use the broadened helper. HandleCommit calls the post-purchase sweep
immediately so the spellbook never carries lingering cascade dependents
(Blood Presence / Forceful Deflection / Death Coil / Death Grip).
- New character_paragon_panel_spell_revoked table tracks which active
dependents we've revoked per (guid, parent) so OnPlayerLogin can
re-revoke them after AC's _LoadSkills -> learnSkillRewardedSpells
silently re-grants them.
- OnPlayerLogin opens the client SILENCE window via SendSilenceOpenForCommit
with an empty allow list and intentionally omits the matching
SendSilenceClose: the chat frame buffers CHAT_MSG_SYSTEM during the
loading screen and only flushes after PLAYER_ENTERING_WORLD, so a paired
CLOSE would shut the filter before the buffered "you have unlearned X"
toasts hit it. The addon's 8s fail-open closes the window after the flush.
- New `.paragon hat` chat command for diagnosing Honor Among Thieves
triggers (talent rank, learned spell, applied aura, proc table entry).
mod-paragon Paragon_SC.cpp:
- OnPlayerUpdate pushes server-authoritative combo points to the client
via PARAA "R CP <n>" whenever the count changes. The client-side
ComboFrame Paragon simulator listens for this and updates the target
frame, fixing HAT-generated CP not displaying (HAT's trigger casts
with a null target, which the combat-log inference path can't see).
- OnPlayerUpdate also pushes "R RUNES <cd0..cd5>" (ms remaining per
rune slot) on rune mask changes, so the client RuneFrame simulator
stays in lock-step with Spell::TakeRunePower instead of drifting
through combat-log latency.
mod-paragon SQL:
- New updates/2026_05_09_00.sql migration creates
character_paragon_panel_spell_revoked for AC's auto-DBUpdater so a
fresh checkout can stand up an existing characters DB without
manual intervention. Matching CREATE TABLE IF NOT EXISTS in
base/character_paragon_panel_learned.sql for fresh installs.
mod-paragon conf:
- New Paragon.Diag.PanelLearn flag traces every PanelLearnSpellChain
commit (chain ids, before/after spell-map sizes, side-spell
classification) for diagnosing "spell reappears on relog" bugs.
Co-authored-by: Cursor <cursoragent@cursor.com>