From 7462f424e02064680533803622740fa65fa5f43e Mon Sep 17 00:00:00 2001 From: MarcelineVQ Date: Tue, 10 Mar 2026 13:27:43 -0700 Subject: [PATCH] Add dpslog research notes for packet handlers and event registration --- src/dpslog/RESEARCH.md | 599 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 599 insertions(+) create mode 100644 src/dpslog/RESEARCH.md diff --git a/src/dpslog/RESEARCH.md b/src/dpslog/RESEARCH.md new file mode 100644 index 0000000..f4c2c1f --- /dev/null +++ b/src/dpslog/RESEARCH.md @@ -0,0 +1,599 @@ +# DPS Log Module Research + +## Goal +Provide structured Lua objects for combat log events so addons can read parsed +fields directly instead of re-parsing localized combat log strings. Modeled +after TBC's `COMBAT_LOG_EVENT_UNFILTERED`. + +## The Problem +Vanilla 1.12.1 fires `CHAT_MSG_COMBAT_*` events with localized text in `arg1`. +Addons like DPSMate parse these with 5-15 `strfind` calls per event across +2177 lines of locale-specific pattern matching. In 40-man raids this causes +measurable frame rate impact. + +--- + +## Two Pipelines + +### Pipeline 1: Chat-type combat messages (the Lua events addons see) + +``` +Server (SMSG_MESSAGECHAT) + -> ProcessIncomingChatPacket (0x0049d560) + -> AddChatMessageToQueue (0x0049cae0) [enqueue with type + text + GUIDs] + -> queue at PTR_00b4fdc0 + -> ProcessChatText (0x0049b560) [dequeue, fire Lua events] + -> CHAT_MSG_COMBAT_SELF_HITS, CHAT_MSG_SPELL_SELF_DAMAGE, etc. +``` + +- `AddChatMessageToQueue`: `__fastcall(ECX=msgType, EDX=textPtr, stack: guid_lo, guid_hi, ...)` + ~13 params, allocates 0xC8-byte queue entry, stores type + text + GUIDs +- `ProcessChatText` (0x0049b560): dequeues messages, fires Lua events +- These are the **formatted localized strings** DPSMate has to parse + +### Pipeline 2: Spell/combat log display (structured data) + +``` +Server (SMSG_SPELLLOGEXECUTE / SMSG_ATTACKERSTATEUPDATE / etc.) + -> ProcessUnitUpdateWithTimingValidation (0x00618c30) + -> ProcessUnitActionWithSpellCastingAndValidation (0x0061a820) + -> PacketHandler_Wrapper_Generic2 (0x00602d00) + -> ProcessComplexSpellCast (0x00629b60) + -> ProcessSpellDamageWithLocalization (0x00629d30) + -> ProcessEnchantmentApplication + -> DisplaySpellLogMessage + -> etc. + -> LogCombatMessage (0x006268f0) + -> FireLuaEvent(0x21e, formatted_text) +``` + +- `ProcessComplexSpellCast` (0x629b60): `__fastcall(ECX=spellData)`, dispatches + by spell flags to sub-handlers. Calls `ValidateSpellCastAndGetObjects` first + to resolve source/target GUIDs and spell info. +- `ProcessSpellDamageWithLocalization` (0x629d30): `__fastcall(ECX=spellEffectData, + EDX=sourceGuid, stack: targetGuid, combatFlags, damageAmount, absorptionAmount, + resistAmount)` -- has ALL the structured data before string formatting. +- `LogCombatMessage` (0x6268f0): `__fastcall(ECX=msgTypeIndex, EDX=unused, + stack: extraText)`. Reads format string from table at `[ESI*4 + 0x862920]`, + copies from g_LuaEventData (0x835154), fires `FireLuaEvent(0x21e, ...)`. + +### Format String Table at 0x862920 + +Index -> string (used by LogCombatMessage to select combat result type): + +``` + [0] INTERRUPT + [1] DAMAGE_CRIT + [2] DAMAGE + [4] BLOCK + [5] ABSORB + [6] RESIST + [7] EVADE + [8] DODGE + [9] PARRY +[10] IMMUNE +[11] DEFLECT +[12] ENCHANTMENT_REMOVED +[31] SPELL_PARRIED +[32] SPELL_BLOCKED +[33] SPELL_EVADED +[34] SPELL_IMMUNE +[35] SPELL_DEFLECTED +[36] SPELL_REFLECTED +[37] SPELL_MISSED +[38] SPELL_ACTIVE +[39] FACTION +``` + +--- + +## Key Addresses + +| Address | Function | Calling Convention | Notes | +|---------|----------|--------------------|-------| +| 0x0049cae0 | AddChatMessageToQueue | __fastcall(ECX,EDX + 11 stack) | Enqueue combat msg | +| 0x0049b560 | ProcessChatText | __fastcall | Dequeue + fire Lua events | +| 0x0049d560 | ProcessIncomingChatPacket | ?? | SMSG_MESSAGECHAT handler | +| 0x00629b60 | ProcessComplexSpellCast | __fastcall(ECX=data) | Main spell dispatch | +| 0x00629d30 | ProcessSpellDamageWithLocalization | __fastcall(ECX,EDX + 5 stack) | Structured damage data | +| 0x00628100 | ExecuteSpellWithLocalizedText | ?? | Called for periodic | +| 0x0062aac0 | ProcessEnvironmentalDamage | ?? | Fall/fire/lava/drown | +| 0x0062cd80 | ProcessSpellCastEffect | ?? | Spell cast results | +| 0x006268f0 | LogCombatMessage | __fastcall(ECX=index, stack: text) | Format + fire 0x21e | +| 0x00703f50 | FireLuaEvent | __cdecl(eventId, eventData) | Generic event fire | +| 0x0051ab30 | InitializeGameEventTable | ?? | Registers CHAT_MSG_* names | +| 0x00862920 | (data) | -- | Combat result format string table | +| 0x00835154 | g_LuaEventData | -- | Global combat msg text buffer | +| 0x00b4fdc0 | PTR_00b4fdc0 | -- | Chat message queue head | + +--- + +## Event System + +- `FireLuaEvent` (0x703f50): `__cdecl(eventId, eventDataFmtStr, ...)` + Looks up event handler list at `PTR_00ceef68 + eventId * 0x10`. + Pushes event name as Lua global, iterates callback linked list, calls each. +- Event 0x21e (542): used by `LogCombatMessage` -- appears to be a general + combat log display event. +- `CHAT_MSG_COMBAT_SELF_HITS` etc. are registered in `InitializeGameEventTable` + with their own event IDs. The mapping from queue message type to event ID + happens in `ProcessChatText`. + +--- + +## Hook Strategy Options + +### Option A: Hook AddChatMessageToQueue (0x0049cae0) +- **Pro**: Single funnel point for ALL chat-type combat messages +- **Pro**: Has message type ID + formatted text + GUIDs +- **Con**: Text is already formatted -- we'd need to either parse it ourselves + (defeats the purpose) or capture structured data from upstream before it reaches here +- **Con**: __fastcall with ~13 params, complex signature + +### Option B: Hook upstream structured functions +- Hook `ProcessSpellDamageWithLocalization` (0x629d30) and siblings +- **Pro**: Has raw structured data (source, target, spell, damage, absorb, resist, flags) +- **Con**: Multiple functions to hook (damage, periodic, environmental, cast effects) +- **Con**: Need to correlate with the eventual CHAT_MSG event type + +### Option C: Hook ProcessChatText (0x0049b560) +- **Pro**: Where events actually fire to Lua -- can inject additional data here +- **Pro**: Has the message type -> event name mapping +- **Con**: By this point the text is formatted, structured data is lost + +### Option D: Hybrid -- capture structured data upstream, attach at dispatch +- Hook the structured functions to capture data into a ring buffer +- Hook ProcessChatText to attach the captured data as extra Lua globals + when firing the event +- **Pro**: Best of both worlds -- structured data + correct event correlation +- **Con**: Most complex, need to match upstream captures to downstream events + +### Option E: Fire a new custom event alongside existing ones +- Hook at the structured data level, fire our own `COMBAT_LOG_EVENT_UNFILTERED` + event with structured args (like TBC) in addition to the normal events +- **Pro**: Cleanest API, addons opt in, existing behavior unchanged +- **Con**: Need to hook multiple upstream functions, need our own event registration + +--- + +## Nampower Reference (reference/nampower/) + +Nampower already implements partial structured combat events. Key patterns: + +### Custom Event Registration via Unused Slots + +The event table has unused slots. Nampower repurposes them by overwriting +the event name string pointer: + +``` +Event 369 (0x171) -> "SPELL_QUEUE_EVENT" at 0xBE175C +Event 540 (0x21C) -> "SPELL_CAST_EVENT" at 0xBE1A08 +Event 549 (0x225) -> "SPELL_DAMAGE_EVENT_SELF" at 0xBE1A2C +Event 550 (0x226) -> "SPELL_DAMAGE_EVENT_OTHER" at 0xBE1A30 +``` + +To register events beyond the default max (549), hook `FrameScript_CreateEvents` +(0x703D90) and increase `maxEventId`. Nampower bumps 549 -> 551. + +### Packet Handler Hooks (from nampower offsets.hpp) + +| Address | Handler | Packet | Conv | +|---------|---------|--------|------| +| 0x626DD0 | PeriodicAuraLogHandler | SMSG_PERIODICAURALOG | FastCall(unk,opCode,unk2,CDataStore*) | +| 0x5E85E0 | SpellNonMeleeDmgLogHandler | SMSG_SPELLNONMELEEDAMAGELOG | FastCall(unk,opCode,unk2,CDataStore*) | +| 0x6E7640 | SpellStartHandler | SMSG_SPELL_START (0x131) | FastCall(unk,opCode,unk2,CDataStore*) | +| 0x6E7330 | CastResultHandler | SMSG_CAST_RESULT | PacketHandler(opCode*,CDataStore*) | +| 0x6E8D80 | SpellFailedHandler | SMSG_SPELL_FAILURE | PacketHandler(opCode*,CDataStore*) | +| 0x6E7550 | SpellChannelStartHandler | SMSG_CHANNEL_START | PacketHandler(opCode*,CDataStore*) | +| 0x6E75F0 | SpellChannelUpdateHandler | SMSG_CHANNEL_UPDATE | PacketHandler(opCode*,CDataStore*) | +| 0x6E9460 | SpellCooldownHandler | SMSG_SPELL_COOLDOWN | PacketHandler(opCode*,CDataStore*) | + +### SMSG_SPELLNONMELEEDAMAGELOG Packet Format + +``` +targetGuid : PackedGuid +casterGuid : PackedGuid +spellId : u32 +damage : u32 +school : u8 +absorb : u32 +resist : i32 +periodicLog : u8 +unused : u8 +blocked : u32 +hitInfo : u32 (flags: 0x1=crit, 0x4000=crushing, etc.) +extendData : u8 +``` + +### SMSG_PERIODICAURALOG Packet Format + +``` +targetGuid : PackedGuid +casterGuid : PackedGuid +spellId : u32 +count : u32 +auraType : u32 + case 3/89 (PERIODIC_DAMAGE/PERIODIC_DAMAGE_PERCENT): + amount : u32 + spellSchool : u32 + absorb : u32 + resist : i32 + case 8/20 (PERIODIC_HEAL/OBS_MOD_HEALTH): + amount : u32 + case 21/24 (OBS_MOD_MANA/PERIODIC_ENERGIZE): + powerType : u32 + amount : u32 + case 64 (PERIODIC_MANA_LEECH): + powerType : u32 + amount : u32 + multiplier : u32 +``` + +### Nampower TriggerSpellDamageEvent Format + +Fires via `SignalEventParam(eventId, fmt, args...)`: +``` +format: "%s%s%d%d%s%d%d%s" +args: targetGuidStr, casterGuidStr, spellId, amount, + "absorb,blocked,resist", hitInfo, spellSchool, + "effect0,effect1,effect2,auraType" +``` + +### SignalEvent Signatures + +- `SignalEvent` (0x703E50): `__fastcall(eventId)` -- no params, just fires event +- `SignalEventParam` (0x703F50): `__cdecl(eventId, fmtStr, ...)` -- variadic, + pushes formatted string args as Lua event args (arg1, arg2, ...) + +--- + +## Packets NOT Covered by Nampower (DPSMate needs these too) + +Based on DPSMate's 54 registered events, we still need handlers for: + +### Melee Combat +- SMSG_ATTACKERSTATEUPDATE -- melee hits/crits/misses/dodges/parries/etc. + Handler likely in the 0x62xxxx range. Contains source/target GUIDs, + damage, school, hitInfo flags, blocked/absorbed/resisted amounts. + +### Healing +- SMSG_SPELLHEALLOG -- direct heals + Contains casterGuid, targetGuid, spellId, healAmount, critFlag + +### Deaths +- SMSG_DESTRUCTOBJ or similar -- unit deaths + +### Buffs/Debuffs (aura application/removal) +- Aura gain/loss events -- DPSMate tracks buff uptime, dispels + +### Environmental Damage +- SMSG_ENVIRONMENTALDAMAGELOG -- fall, lava, drown, fire + Already partially handled via ProcessEnvironmentalDamage (0x62aac0) + +--- + +## DPSMate Coverage Matrix + +DPSMate registers 54 events across these categories. Our goal: provide structured +data for each so addons skip the 2177-line locale-specific string parser. + +### Category 1: Spell Damage (direct) — DONE ✓ + +**Packet**: SMSG_SPELLNONMELEEDAMAGELOG (handler 0x5E85E0) +**Our event**: `COMBAT_LOG_SPELL_DMG` (slot 551) +**Fields**: targetGUID, casterGUID, spellId, damage, school, absorb, resist, blocked, hitInfo + +Covers these DPSMate events: +- [x] CHAT_MSG_SPELL_SELF_DAMAGE (spell hits/crits/misses/parries/dodges/resists/absorbs) +- [x] CHAT_MSG_SPELL_FRIENDLYPLAYER_DAMAGE +- [x] CHAT_MSG_SPELL_PARTY_DAMAGE +- [x] CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE +- [x] CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE +- [x] CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE +- [x] CHAT_MSG_SPELL_CREATURE_VS_CREATURE_DAMAGE +- [x] CHAT_MSG_SPELL_PET_DAMAGE +- [x] CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF (damage shields are spell damage) +- [x] CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS + +### Category 2: Periodic Damage/Heal Ticks — DONE ✓ + +**Packet**: SMSG_PERIODICAURALOG (handler 0x626DD0) +**Our event**: `COMBAT_LOG_PERIODIC` (slot 552) +**Fields**: targetGUID, casterGUID, spellId, amount, school, absorb, resist, auraType, powerType + +auraType distinguishes: 3=PERIODIC_DAMAGE, 89=PERIODIC_DAMAGE_PERCENT, +8=PERIODIC_HEAL, 20=OBS_MOD_HEALTH, 21=OBS_MOD_MANA, 24=PERIODIC_ENERGIZE, +64=PERIODIC_MANA_LEECH + +Covers these DPSMate events: +- [x] CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE +- [x] CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE +- [x] CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE +- [x] CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE +- [x] CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE +- [x] CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS (periodic heal component) +- [x] CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS (periodic heal component) +- [x] CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS (periodic heal component) +- [x] CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS (periodic heal component) + +### Category 3: Melee Damage — DONE ✓ + +**Packet**: SMSG_ATTACKERSTATEUPDATE (opcode 0x14A) +**Needed event**: `COMBAT_LOG_MELEE` (allocate slot 554) +**Fields needed**: targetGUID, casterGUID, damage, school, hitInfo, blocked, absorbed, resisted, victimState + +**Handler found**: Opcode 0x14A dispatches through a large switch at **0x6255B0** +(registered in `UpdateBindLocation`, shared with opcodes 0x143-0x149). +Jump table: byte lookup at 0x625B0C, jump at 0x625AEC. Case 5 -> **0x62578D**. + +The actual **packet parser** is at **0x625C60** -- `__thiscall(ECX=struct_out, stack=CDataStore*)`. +After parsing, calls `ClntObjMgrObjectPtr` then **0x625E20** (post-parse handler). + +**Verified packet wire format** (from Ghidra disassembly of 0x625C60): +``` +hitInfo : u32 +attackerGuid : PackedGuid +targetGuid : PackedGuid +totalDamage : u32 +subDamageCount : u8 + per subDamage (5 fields each): + school : u32 + damageFP : f32 + damage : u32 + absorb : u32 + resist : u32 +victimState : u32 (0=hit,1=dodge,2=parry,3=interrupt,4=block,5=evade,6=immune,7=deflect) +unknown1 : u32 +unknown2 : u32 +spellId : u32 (0 for melee) +if hitInfo & 0x1: + blocked : u32 + ... extended block data +``` + +**HitInfo flags** (from mangos): + 0x00001=NORMALSWING (block data present), 0x00002=OFFHAND, 0x00004=MISS, + 0x00008=FULL_ABSORB, 0x00010=FULL_RESIST, 0x00020=CRIT, 0x00200=CRUSHING, + 0x00400=GLANCING + +**CDataStore read functions** (verified byte sizes from disassembly): +- 0x418EB0 (ReadPointerFromBuffer): reads 4 bytes (u32) +- 0x418E30 (ReadPointerFromStream): reads 4 bytes (u32) +- 0x418CB0 (ReadByteFromBuffer): reads 1 byte (u8) +- 0x419130 (ReadPointerFromData): reads 4 bytes (f32) +- 0x642ED0 (GetPackedGuid): reads packed GUID + +**Hooking strategy -- IMPORTANT**: Do NOT hook the packet handler (0x6255B0) or +parser (0x625C60) directly. Packet handler hooks are sensitive to Warden detection. +Prefer hooking slightly further downstream -- e.g., the post-parse handler at +**0x625E20** which receives the already-parsed struct and resolved unit pointer. +Research needed: verify 0x625E20 signature, what data is accessible, whether it's +safe as a hook target. + +Covers these DPSMate events (the largest group -- 16 events): +- [ ] CHAT_MSG_COMBAT_SELF_HITS +- [ ] CHAT_MSG_COMBAT_SELF_MISSES +- [ ] CHAT_MSG_COMBAT_PARTY_HITS +- [ ] CHAT_MSG_COMBAT_PARTY_MISSES +- [ ] CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS +- [ ] CHAT_MSG_COMBAT_FRIENDLYPLAYER_MISSES +- [ ] CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS +- [ ] CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES +- [ ] CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS +- [ ] CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES +- [ ] CHAT_MSG_COMBAT_CREATURE_VS_PARTY_HITS +- [ ] CHAT_MSG_COMBAT_CREATURE_VS_PARTY_MISSES +- [ ] CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_HITS +- [ ] CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_MISSES +- [ ] CHAT_MSG_COMBAT_PET_HITS +- [ ] CHAT_MSG_COMBAT_PET_MISSES + +**SMSG_ATTACKERSTATEUPDATE packet format** (from mangos/wowdev): +``` +hitInfo : u32 (flags: 0x1=normal, 0x2=offhand, 0x4=miss, etc.) +attackerGuid : PackedGuid +targetGuid : PackedGuid +totalDamage : u32 +subDamageCount : u8 + per subDamage: + school : u32 + damageFP : f32 + damage : u32 + absorb : u32 + resist : u32 +victimState : u32 (0=hit, 1=dodge, 2=parry, 3=interrupt, 4=block, 5=evade, 6=immune, 7=deflect) +unknown1 : u32 +spellId : u32 (0 for melee) +blocked : u32 (if hitInfo & BLOCK) +``` + +Tracing approaches: +- Search for opcode 0x14A in packet dispatch table +- Trace from `ProcessUnitUpdateWithTimingValidation` (0x618c30) +- Search for callers of functions that reference melee hit strings + +### Category 4: Direct Heals — TODO + +**Packet**: SMSG_SPELLHEALLOG +**Handler**: Trace from `ProcessHealingSpell` (0x622420) callers +**Our event**: `COMBAT_LOG_HEAL` (slot 553 — already registered, no hook yet) +**Fields needed**: targetGUID, casterGUID, spellId, healAmount, isCrit + +Covers these DPSMate events: +- [ ] CHAT_MSG_SPELL_SELF_BUFF (heal component: "Your X heals Y for Z") +- [ ] CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF (heal component: "X's Y heals Z for W") +- [ ] CHAT_MSG_SPELL_FRIENDLYPLAYER_BUFF (heal component) +- [ ] CHAT_MSG_SPELL_PARTY_BUFF (heal component) + +### Category 5: Environmental Damage — TODO (low priority) + +**Handler**: `ProcessEnvironmentalDamage` (0x62aac0) +**Fields needed**: targetGUID, damageType (fall/fire/lava/drown/slime), amount + +DPSMate currently infers these from CHAT_MSG_COMBAT_SELF_HITS / FRIENDLYPLAYER_HITS +by matching strings like "fall and lose", "swimming in lava", "suffer X points of +fire damage", "drowning and lose". Our melee/spell hooks won't cover these since +they come from a separate opcode (SMSG_ENVIRONMENTALDAMAGELOG). + +### Category 6: Aura Gain/Loss — TODO (low priority) + +No single packet handler — these are derived from SMSG_AURA_UPDATE and various +spell effect handlers. DPSMate uses: +- [ ] CHAT_MSG_SPELL_AURA_GONE_SELF +- [ ] CHAT_MSG_SPELL_AURA_GONE_OTHER +- [ ] CHAT_MSG_SPELL_AURA_GONE_PARTY +- [ ] CHAT_MSG_SPELL_BREAK_AURA (dispel confirmation) +- [ ] PLAYER_AURAS_CHANGED + +These track: buff uptime, debuff uptime, proc tracking, shield absorb tracking, +CC tracking, HoT application/removal. Complex — may be better to let addons +continue using the existing text events for these until we have more bandwidth. + +### Category 7: Deaths — TODO (low priority) + +- [ ] CHAT_MSG_COMBAT_FRIENDLY_DEATH ("X dies.", "X is slain by Y") +- [ ] CHAT_MSG_COMBAT_HOSTILE_DEATH ("X dies.") + +DPSMate uses these to call `DB:UnregisterDeath(source)`. Could fire from +SMSG_DESTROYOBJECT or unit flag changes, but low impact — death events are +infrequent and the string parsing is trivial. + +### Category 8: Interrupts — Embedded in spell damage events + +DPSMate parses interrupts from SPELL_SELF_DAMAGE and SPELL_CREATURE_VS_SELF_DAMAGE +("You interrupt X's Y"). These are already covered by our `COMBAT_LOG_SPELL_DMG` +event since interrupts flow through the same packet handlers. The addon can detect +interrupts by checking hitInfo flags. + +### Category 9: Power Gains — Partially covered + +DPSMate parses "You gain X Mana/Rage/Energy from Y" from SPELL_SELF_BUFF and +SPELL_PERIODIC_SELF_BUFFS. Our PERIODIC hook already covers periodic energize +(auraType 21/24). Direct power gains from spell casts (e.g., Life Tap, mana gems) +would need a separate hook or could be left to text events. + +--- + +## Implementation Status + +### Done +- [x] Custom event registration (slots 551-554, FrameScript_CreateEvents hook, maxId 556) +- [x] CDataStore read helpers (cdsGet, cdsGetPackedGuid, save/restore m_read) +- [x] GUID-to-string conversion +- [x] SignalEventParam typed wrappers +- [x] SpellNonMeleeDmgLogHandler hook (0x5E85E0) → COMBAT_LOG_SPELL_DMG +- [x] PeriodicAuraLogHandler hook (0x626DD0) → COMBAT_LOG_PERIODIC +- [x] SpellHealLogHandler hook (0x5E89C0) → COMBAT_LOG_HEAL (opcode 0x150) +- [x] MeleeDispatcher hook (0x6255B0) → COMBAT_LOG_MELEE (opcode 0x14A filter) +- [x] Event string pointer base verified: 0xBE1198 (derived from nampower 549→0xBE1A2C) + +### Melee Hook Research Findings +- 0x625E20 (originally targeted as "post-parse handler") is just a flag-setter — too simple +- Dispatcher 0x6255B0 handles opcodes 0x143-0x14A via switch/jump table (byte lookup at 0x625B0C, targets at 0x625AEC) +- Opcode 0x14A maps to case 5 → 0x62578D, which zeros struct, calls parser 0x625C60, then resolves unit via 0x468460 +- Same FastCall(unk, opCode, unk2, CDataStore*) convention as other packet handlers +- Decision: hook the shared dispatcher with opcode filter (minimal overhead, proven pattern) +- Handler registered from `UpdateBindLocation` (0x625520) via `setClientIndexedPointers` (0x5AB650) + +### Heal Handler Discovery +- `setClientIndexedPointers(opcode, handler, 0)` is the packet handler registration function at 0x5AB650 +- Registration pattern: `PUSH 0x0; MOV EDX,handler; MOV ECX,opcode; CALL 0x5AB650` +- Opcode 0x150 (SMSG_SPELLHEALLOG) → handler 0x5E89C0 (registered in applySpellModifiers at 0x5E3010) +- Same prologue/convention as SpellNonMeleeDmgLogHandler +- Packet format verified from disassembly: targetGuid(PackedGuid), casterGuid(PackedGuid), spellId(u32), healAmount(u32), isCrit(u8) + +### Next Steps (priority order) +1. [ ] Environmental damage hook via ProcessEnvironmentalDamage (0x62aac0) +2. [ ] Addon-side example code showing how to register for our events +3. [ ] Nampower conflict detection via GetModuleHandleA("nampower.dll") (low priority) + +### CDataStore Structure + +``` ++0x00: vtable ptr ++0x04: m_buffer (u8 pointer) ++0x08: m_base ++0x0C: m_alloc ++0x10: m_size ++0x14: m_read (current read position) +``` + +### Event Slot Layout + +``` +Nampower: 549 → SPELL_DAMAGE_EVENT_SELF (0xBE1A2C) + 550 → SPELL_DAMAGE_EVENT_OTHER (0xBE1A30) +Ours: 551 → COMBAT_LOG_SPELL_DMG (0xBE1A34) + 552 → COMBAT_LOG_PERIODIC (0xBE1A38) + 553 → COMBAT_LOG_HEAL (0xBE1A3C) + 554 → (reserved for COMBAT_LOG_MELEE) +FrameScript_CreateEvents maxEventId bumped to 555. +``` + +--- + +## Additional Function Map (from latest Ghidra analysis) + +### Combat Message Formatting Chain + +``` +ProcessSpellDamageWithLocalization (0x629d30) + -> GetCombatSchoolHitMessage (0x62a080) -- hit message key + -> GetCombatMissMessage (0x62a0d0) -- miss message key + -> LogCombatMessage (0x6268f0) -- fires event 0x21e +``` + +### Healing Functions + +| Address | Function | Called By | +|---------|----------|-----------| +| 0x622420 | ProcessHealingSpell | UpdateSpellEffects, HandleCombatAction, ProcessSpellDamage, etc. | +| 0x62adc0 | ProcessHealingSpellEffect | ProcessSpellEffect (0x62ace0) | +| 0x62c940 | GetHealingMessageConfigString | | +| 0x62c9a0 | GetCriticalHealingMessageConfigString | | +| 0x627440 | GetPeriodicHealMessageKey | | + +### Spell Effect Processing + +| Address | Function | Notes | +|---------|----------|-------| +| 0x62ace0 | ProcessSpellEffect | Calls ProcessHealingSpellEffect | +| 0x629d30 | ProcessSpellDamageWithLocalization | __fastcall(spellData, sourceGuid, targetGuid, flags, dmg, absorb, resist) | +| 0x628100 | ExecuteSpellWithLocalizedText | Called for periodic effects | +| 0x62aac0 | ProcessEnvironmentalDamage | Fall/fire/lava/drown | +| 0x62cd80 | ProcessSpellCastEffect | | +| 0x626a10 | DisplaySpellInterruptMessage | | +| 0x62a710 | DisplayAdvancedSpellLogMessage | | + +### Combat Message System Init + +| Address | Function | +|---------|----------| +| 0x626d00 | InitializeCombatMessageSystem | +| 0x626810 | GetSpellTextByIndex | +| 0x626850 | DisplayColoredMessage | +| 0x6264b0 | GetSpellNameById | +| 0x6264e0 | GetObjectName | +| 0x626630 | ValidateSpellCastAndGetObjects | + +### Melee Combat + +- `ProcessPlayerAutoAttack` (0x5ecb70) -- called from SetPlayerTarget, UpdatePlayerState +- `processAutoAttack` (0x6e6900) +- Melee hits are part of SMSG_ATTACKERSTATEUPDATE (opcode 0x14A) -- handler not yet identified +- The packet comes through the general update pipeline: + `ProcessComplexUnitUpdateWithCameraSync` (0x619320) -> + `ProcessUnitUpdateWithTimingValidation` (0x618c30) -> + `PacketHandler_Wrapper_Generic2` (0x602d00) + +### Key Nampower-Confirmed Address Cross-References + +| Our Name | Nampower Name | Address | +|----------|--------------|---------| +| FireLuaEvent | SignalEventParam | 0x703F50 | +| SignalEvent (no params) | SignalEvent | 0x703E50 | +| GetObjectByGUID | GetObjectPtr | 0x464870 | +| ClntObjMgrGetActivePlayer | GetActivePlayer | 0x468550 | +| FrameScript_CreateEvents | FrameScript_CreateEvents | 0x703D90 | +| FrameScript_RegisterFunction | FrameScript_RegisterFunction | 0x704120 | +| GetSpellNameById | (SpellDb at 0xC0D780) | 0x6264b0 |