Add dpslog research notes for packet handlers and event registration

This commit is contained in:
MarcelineVQ
2026-03-10 13:27:43 -07:00
parent 82cf5475b5
commit 7462f424e0
+599
View File
@@ -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 |