From f986fdcddde71a99d467ac28cda6a41abac92fde Mon Sep 17 00:00:00 2001 From: Docker Build Date: Sat, 9 May 2026 16:52:37 -0400 Subject: [PATCH] mod-paragon: let class 12 actually USE class-restricted glyphs / items 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//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 --- .../sql/db-world/updates/2026_05_10_04.sql | 30 +++++++++++++++++++ src/server/game/Spells/Spell.cpp | 12 ++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 modules/mod-paragon/data/sql/db-world/updates/2026_05_10_04.sql diff --git a/modules/mod-paragon/data/sql/db-world/updates/2026_05_10_04.sql b/modules/mod-paragon/data/sql/db-world/updates/2026_05_10_04.sql new file mode 100644 index 0000000..06abc7c --- /dev/null +++ b/modules/mod-paragon/data/sql/db-world/updates/2026_05_10_04.sql @@ -0,0 +1,30 @@ +-- mod-paragon: extend ItemTemplate::AllowableClass to include class 12 +-- (Paragon, bit 1<<11 = 2048) for every class-restricted item. +-- +-- Server-side, Player::CanUseItem (PlayerStorage.cpp) already short- +-- circuits the AllowableClass check for class 12. That's enough for any +-- code path the server controls (vendor list filter, AH "usable" filter, +-- CanRollForItemInLFG, CanBuyItem). It is NOT enough on the 3.3.5 client: +-- the WoW.exe binary independently pre-checks AllowableClass against the +-- player's class on right-click of a bag item and refuses *locally* with +-- the red "You can't use that item." text in UIErrorsFrame, never sending +-- CMSG_USE_ITEM at all. Server logs stay silent; only client knows it +-- refused. +-- +-- Fix: OR class 12's bit into AllowableClass on every class-restricted +-- row so the client engine's pre-check passes for Paragon. Other +-- classes' bits are unchanged, so e.g. a warrior-only item is still +-- warrior-only for everyone except Paragon. Items with AllowableClass +-- == -1 ("all classes") or 0 ("no restriction recorded") already pass +-- the client engine's check and are not touched. +-- +-- After applying this migration the *client* still caches item info in +-- Cache//itemcache.wdb. Players who already inspected the item +-- before the change must delete that file (or the whole Cache folder) +-- and reconnect to repopulate it from the worldserver, otherwise the +-- stale cached AllowableClass keeps the engine pre-check failing. + +UPDATE `item_template` + SET `AllowableClass` = `AllowableClass` | 2048 + WHERE `AllowableClass` > 0 + AND (`AllowableClass` & 2048) = 0; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 2084e1d..75b2198 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -7296,8 +7296,16 @@ SpellCastResult Spell::CheckItems(uint32* param1, uint32* param2) { // Xinef: this is not true in my opinion, in eg bladestorm will not be canceled after disarm //if (!HasTriggeredCastFlag(TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT)) - if (m_caster->IsPlayer() && !m_caster->ToPlayer()->HasItemFitToSpellRequirements(m_spellInfo)) - return SPELL_FAILED_EQUIPPED_ITEM_CLASS; + if (m_caster->IsPlayer()) + { + // Cast-from-glyph: many glyph on-use spells set EquippedItemClass to ITEM_CLASS_GLYPH. + // HasItemFitToSpellRequirements only implements weapon/armor, so it would always fail here + // even though the glyph item in the bag is the valid spell source. + bool const castFromGlyphScroll = m_CastItem && m_CastItem->GetTemplate() && + m_CastItem->GetTemplate()->Class == ITEM_CLASS_GLYPH; + if (!castFromGlyphScroll && !m_caster->ToPlayer()->HasItemFitToSpellRequirements(m_spellInfo)) + return SPELL_FAILED_EQUIPPED_ITEM_CLASS; + } } // do not take reagents for these item casts