fix packedguid parsing, add corpse info to spell start and spell go, refactor

This commit is contained in:
avitasia
2026-02-20 16:06:36 -08:00
parent 91e1892146
commit a54af0b960
15 changed files with 9356 additions and 9260 deletions
+3 -1
View File
@@ -136,7 +136,8 @@ Parameters:
5. int castFlags
6. int castTime - cast time in milliseconds
7. int duration - channel duration in milliseconds (only set for channeling spells, 0 otherwise. See GetSpellDuration if you want it for other spell types)
8. int spellType - 0 = Normal, 1 = Channeling, 2 = Autorepeating
8. int spellType - 0 = Normal, 1 = Channeling, 2 = Autorepeating
9. string corpseOwnerGuid - guid of the player who owns the corpse target, nil if the spell has no corpse target
### SPELL_GO_SELF and SPELL_GO_OTHER
Fire when a spell go packet is received. "Self" fires when the active player is the caster, "Other" fires for any other caster. Triggered by the server to indicate a spell completed casting.
@@ -151,6 +152,7 @@ Parameters:
5. int castFlags
6. int numTargetsHit
7. int numTargetsMissed
8. string corpseOwnerGuid - guid of the player who owns the corpse target, nil if the spell has no corpse target
#### CastFlags (bitmask)
```
+9197 -9194
View File
File diff suppressed because it is too large Load Diff
+5 -10
View File
@@ -6,6 +6,7 @@
#include "offsets.hpp"
#include "logging.hpp"
#include "game.hpp"
#include "helper.hpp"
namespace Nampower {
uint32_t gAuraExpirationTime[MAX_AURA_SLOTS] = {};
@@ -71,12 +72,7 @@ namespace Nampower {
}
static char format[] = "%s%d%d%d%d%d%d";
char *guidStr = new char[21]; // 2 for 0x prefix, 18 for the number, and 1 for '\0'
if (isSelf) {
std::snprintf(guidStr, 21, "0x%016llX", static_cast<unsigned long long>(playerGuid));
} else {
std::snprintf(guidStr, 21, "0x%016llX", static_cast<unsigned long long>(unitGuid));
}
char *guidStr = ConvertGuidToString(isSelf ? playerGuid : unitGuid);
// Trigger the event with spellId as parameter
((int (__cdecl *)(int, char *, char *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)) Offsets::SignalEventParam)(
@@ -90,7 +86,7 @@ namespace Nampower {
slot,
state);
delete[] guidStr;
FreeGuidString(guidStr);
}
void CGUnit_C_OnAuraRemovedHook(hadesmem::PatchDetourBase *detour, uintptr_t *unit, void *dummy_edx, uint32_t slot,
@@ -172,14 +168,13 @@ namespace Nampower {
// Trigger UNIT_DIED event
static char format[] = "%s";
char *guidStr = new char[21]; // 2 for 0x prefix, 18 for the number, and 1 for '\0'
std::snprintf(guidStr, 21, "0x%016llX", static_cast<unsigned long long>(guid));
char *guidStr = ConvertGuidToString(guid);
((int (__cdecl *)(int, char *, char *)) Offsets::SignalEventParam)(
game::UNIT_DIED,
format,
guidStr);
delete[] guidStr;
FreeGuidString(guidStr);
}
}
+2 -2
View File
@@ -45,8 +45,8 @@ namespace Nampower {
totalAbsorb,
totalResist);
delete[] attackerGuidStr;
delete[] targetGuidStr;
FreeGuidString(attackerGuidStr);
FreeGuidString(targetGuidStr);
}
void AttackRoundInfo_ReadPacketHook(hadesmem::PatchDetourBase *detour, void *thisptr, void *dummy_edx,
-12
View File
@@ -119,18 +119,6 @@ namespace Nampower {
}
void CDataStore::GetPackedGuid(uint64_t &val) {
unsigned int bytes = m_read + sizeof(val);
if (bytes > m_size) {
m_read = m_size + 1;
return;
}
if ((m_read < m_base) || (bytes > m_alloc + m_base)) {
if (!AssertFetchRead(m_read, sizeof(val))) {
m_read = m_alloc + 1;
return;
}
}
uint64_t guid = 0;
uint8_t mask; // Read the bitmap
Get(mask);
+13
View File
@@ -277,4 +277,17 @@ namespace game {
return unitFields->mountDisplayId;
}
uint64_t GetCorpseOwner(uintptr_t *corpse) {
if (!corpse) {
return 0;
}
auto *corpseFields = *reinterpret_cast<CorpseFields **>(corpse + 68);
if (!corpseFields) {
return 0;
}
return corpseFields->owner;
}
}
+37
View File
@@ -1789,6 +1789,41 @@ namespace game {
UNIT_FLAG_UNK_29 = 0x20000000, // used in Feing Death spell
};
constexpr uint32_t OBJECT_END = 6; // ObjectFields: guid(2) + type + entry + scaleX + padding
enum ECorpseFields {
CORPSE_FIELD_OWNER = OBJECT_END + 0x00,
CORPSE_FIELD_FACING = OBJECT_END + 0x02,
CORPSE_FIELD_POS_X = OBJECT_END + 0x03,
CORPSE_FIELD_POS_Y = OBJECT_END + 0x04,
CORPSE_FIELD_POS_Z = OBJECT_END + 0x05,
CORPSE_FIELD_DISPLAY_ID = OBJECT_END + 0x06,
CORPSE_FIELD_ITEM = OBJECT_END + 0x07, // 19
CORPSE_FIELD_BYTES_1 = OBJECT_END + 0x1A,
CORPSE_FIELD_BYTES_2 = OBJECT_END + 0x1B,
CORPSE_FIELD_GUILD = OBJECT_END + 0x1C,
CORPSE_FIELD_FLAGS = OBJECT_END + 0x1D,
CORPSE_FIELD_DYNAMIC_FLAGS = OBJECT_END + 0x1E,
CORPSE_FIELD_PAD = OBJECT_END + 0x1F,
CORPSE_END = OBJECT_END + 0x20,
};
typedef struct CorpseFields {
uint64_t owner; // Size:2
float facing; // Size:1
float posX; // Size:1
float posY; // Size:1
float posZ; // Size:1
uint32_t displayId; // Size:1
uint32_t items[19]; // Size:19
uint32_t bytes1; // Size:1
uint32_t bytes2; // Size:1
uint32_t guild; // Size:1
uint32_t flags; // Size:1
uint32_t dynamicFlags; // Size:1
uint32_t pad; // Size:1
} CorpseFields;
typedef struct UnitFields {
uint64_t charm; // Size:2
uint64_t summon; // Size:2
@@ -1910,4 +1945,6 @@ namespace game {
bool UnitIsDebuffCapped(uintptr_t *unit);
uint32_t UnitGetMountDisplayId(uintptr_t *unit);
uint64_t GetCorpseOwner(uintptr_t *corpse);
}
+15
View File
@@ -286,13 +286,28 @@ namespace Nampower {
return startTime != 0 && duration > 1.5;
}
const char kNilGuidStr[] = "0x0000000000000000";
void FreeGuidString(char *str) {
if (str != kNilGuidStr) {
delete[] str;
}
}
char *ConvertGuidToString(uint64_t guid) {
if (!guid) {
return const_cast<char *>(kNilGuidStr);
}
char *guidStr = new char[21]; // 2 for 0x prefix, 18 for the number, and 1 for '\0'
std::snprintf(guidStr, 21, "0x%016llX", static_cast<unsigned long long>(guid));
return guidStr;
}
void PushGuidString(uintptr_t *luaState, uint64_t guid) {
if (!guid) {
lua_pushstring(luaState, const_cast<char *>(kNilGuidStr));
return;
}
char guidStr[21] = {0};
std::snprintf(guidStr, sizeof(guidStr), "0x%016llX", static_cast<unsigned long long>(guid));
lua_pushstring(luaState, guidStr);
+2
View File
@@ -70,7 +70,9 @@ namespace Nampower {
bool IsSpellOnCooldown(uint32_t spellId);
extern const char kNilGuidStr[];
char *ConvertGuidToString(uint64_t guid);
void FreeGuidString(char *str);
void PushGuidString(uintptr_t *luaState, uint64_t guid);
bool IsReadableMemory(const void *ptr, size_t size);
bool SafeReadMemory(const void *address, void *out, size_t size);
+2 -2
View File
@@ -25,8 +25,8 @@ namespace Nampower {
constexpr uint32_t DISENCHANT_QUALITY_PURPLE = 0x04; // Epic
constexpr uint32_t MAJOR_VERSION = 2;
constexpr uint32_t MINOR_VERSION = 39;
constexpr uint32_t PATCH_VERSION = 1;
constexpr uint32_t MINOR_VERSION = 40;
constexpr uint32_t PATCH_VERSION = 0;
constexpr int32_t LUA_REGISTRYINDEX = -10000;
constexpr int32_t LUA_GLOBALSINDEX = -10001;
+1 -3
View File
@@ -960,9 +960,7 @@ namespace Nampower {
uint32_t guidHigh = frameObj[0x13b];
uint64_t guid = (static_cast<uint64_t>(guidHigh) << 32) | guidLow;
char guidStr[21] = {0};
std::snprintf(guidStr, sizeof(guidStr), "0x%016llX", static_cast<unsigned long long>(guid));
lua_pushstring(luaState, guidStr);
PushGuidString(luaState, guid);
} else {
// GetName via vtable[1] (offset 0x4)
using GetNameT = char *(__thiscall *)(uintptr_t *thisPtr);
+2 -3
View File
@@ -328,8 +328,7 @@ namespace Nampower {
void
TriggerSpellCastEvent(bool result, uint32_t spellId, CastType castType, std::uint64_t guid, uint32_t itemId) {
static char format[] = "%d%d%d%s%d";
char *guidStr = new char[21]; // 2 for 0x prefix, 18 for the number, and 1 for '\0'
std::snprintf(guidStr, 21, "0x%016llX", static_cast<unsigned long long>(guid));
char *guidStr = ConvertGuidToString(guid);
((int (__cdecl *)(int, char *, uint32_t, uint32_t, uint32_t, char *, uint32_t)) Offsets::SignalEventParam)(
game::SPELL_CAST_EVENT, // SPELL_CAST_EVENT event we are adding
@@ -340,7 +339,7 @@ namespace Nampower {
guidStr,
itemId);
delete[] guidStr;
FreeGuidString(guidStr);
}
void clearCastingSpell() {
+2 -2
View File
@@ -29,7 +29,7 @@ namespace Nampower {
targetGuidStr,
duration);
delete[] targetGuidStr;
FreeGuidString(targetGuidStr);
}
void TriggerSpellChannelUpdateEvent(uint32_t spellId, uint32_t channelRemainingTime) {
@@ -49,7 +49,7 @@ namespace Nampower {
targetGuidStr,
channelRemainingTime);
delete[] targetGuidStr;
FreeGuidString(targetGuidStr);
}
int SpellChannelStartHandlerHook(hadesmem::PatchDetourBase *detour, uint32_t *opCode, CDataStore *packet) {
+72 -28
View File
@@ -84,7 +84,7 @@ namespace Nampower {
casterGuidStr,
spellId);
delete[] casterGuidStr;
FreeGuidString(casterGuidStr);
}
@@ -144,8 +144,8 @@ namespace Nampower {
periodic);
}
delete[] targetGuidStr;
delete[] casterGuidStr;
FreeGuidString(targetGuidStr);
FreeGuidString(casterGuidStr);
}
@@ -205,8 +205,8 @@ namespace Nampower {
periodic);
}
delete[] targetGuidStr;
delete[] casterGuidStr;
FreeGuidString(targetGuidStr);
FreeGuidString(casterGuidStr);
}
void Spell_C_SpellFailedHook(hadesmem::PatchDetourBase *detour, uint32_t spellId,
@@ -442,7 +442,7 @@ namespace Nampower {
casterGuidStr,
delayMs);
delete[] casterGuidStr;
FreeGuidString(casterGuidStr);
}
void Spell_C_CooldownEventTriggeredHook(hadesmem::PatchDetourBase *detour,
@@ -686,7 +686,7 @@ namespace Nampower {
auraCapStatus);
if (createdGuidStr) {
delete [] targetGuidStr;
FreeGuidString(targetGuidStr);
}
}
@@ -769,19 +769,21 @@ namespace Nampower {
}
}
delete[] casterGuidStr;
FreeGuidString(casterGuidStr);
}
void TriggerSpellStartEvent(uint32_t itemId,
uint32_t spellId,
uint64_t casterGuid,
uint64_t targetGuid,
uint64_t corpseOwnerGuid,
uint16_t castFlags,
uint32_t castTime) {
static char format[] = "%d%d%s%s%d%d%d%d";
static char format[] = "%d%d%s%s%d%d%d%d%s";
char *casterGuidStr = ConvertGuidToString(casterGuid);
char *targetGuidStr = ConvertGuidToString(targetGuid);
char *corpseOwnerGuidStr = corpseOwnerGuid ? ConvertGuidToString(corpseOwnerGuid) : nullptr;
uint32_t duration = 0;
SpellType spellType = SPELL_TYPE_NORMAL;
@@ -812,7 +814,8 @@ namespace Nampower {
uint32_t castFlagsParam,
uint32_t castTimeParam,
uint32_t durationParam,
uint32_t spellTypeParam)) Offsets::SignalEventParam)(
uint32_t spellTypeParam,
char *corpseTargetGuidParam)) Offsets::SignalEventParam)(
event,
format,
itemId,
@@ -822,24 +825,28 @@ namespace Nampower {
castFlags,
castTime,
duration,
spellType);
spellType,
corpseOwnerGuidStr);
delete[] casterGuidStr;
delete[] targetGuidStr;
FreeGuidString(casterGuidStr);
FreeGuidString(targetGuidStr);
FreeGuidString(corpseOwnerGuidStr);
}
void TriggerSpellGoEvent(uint32_t itemId,
uint32_t spellId,
const uint64_t *casterGuid,
uint64_t targetGuid,
uint64_t corpseOwnerGuid,
uint16_t castFlags,
uint8_t numTargetsHit,
uint8_t numTargetsMissed) {
static char format[] = "%d%d%s%s%d%d%d";
static char format[] = "%d%d%s%s%d%d%d%s";
uint64_t casterGuidValue = casterGuid ? *casterGuid : 0;
char *casterGuidStr = ConvertGuidToString(casterGuidValue);
char *targetGuidStr = ConvertGuidToString(targetGuid);
char *corpseOwnerGuidStr = corpseOwnerGuid ? ConvertGuidToString(corpseOwnerGuid) : nullptr;
auto event = game::SPELL_GO_OTHER;
if (casterGuidValue == game::ClntObjMgrGetActivePlayerGuid()) {
@@ -854,7 +861,8 @@ namespace Nampower {
char *targetGuidParam,
uint32_t castFlagsParam,
uint32_t numTargetsHitParam,
uint32_t numTargetsMissedParam)) Offsets::SignalEventParam)(
uint32_t numTargetsMissedParam,
char *corpseTargetGuidParam)) Offsets::SignalEventParam)(
event,
format,
itemId,
@@ -863,10 +871,12 @@ namespace Nampower {
targetGuidStr,
castFlags,
numTargetsHit,
numTargetsMissed);
numTargetsMissed,
corpseOwnerGuidStr);
delete[] casterGuidStr;
delete[] targetGuidStr;
FreeGuidString(casterGuidStr);
FreeGuidString(targetGuidStr);
FreeGuidString(corpseOwnerGuidStr);
}
void TriggerSpellMissEvent(uint64_t casterGuid,
@@ -896,8 +906,8 @@ namespace Nampower {
spellId,
missInfo);
delete[] casterGuidStr;
delete[] targetGuidStr;
FreeGuidString(casterGuidStr);
FreeGuidString(targetGuidStr);
}
void SpellGoHook(hadesmem::PatchDetourBase *detour, uint64_t *itemGUID, uint64_t *casterGUID,
@@ -940,8 +950,22 @@ namespace Nampower {
packet->Get(spellCastTargetMask);
uint64_t unitTargetGUID = 0;
if (spellCastTargetMask & (game::TARGET_FLAG_UNIT | game::TARGET_FLAG_UNK2))
packet->GetPackedGuid(unitTargetGUID);
uint64_t corpseTargetGUID = 0;
if (spellCastTargetMask & (game::TARGET_FLAG_UNIT | game::TARGET_FLAG_PVP_CORPSE | game::TARGET_FLAG_OBJECT | game::TARGET_FLAG_CORPSE | game::TARGET_FLAG_UNK2)) {
if (spellCastTargetMask & game::TARGET_FLAG_UNIT) {
packet->GetPackedGuid(unitTargetGUID);
} else if (spellCastTargetMask & (game::TARGET_FLAG_OBJECT | game::TARGET_FLAG_OBJECT_UNK)) {
uint64_t goTargetGUID = 0;
packet->GetPackedGuid(goTargetGUID);
} else if (spellCastTargetMask & (game::TARGET_FLAG_CORPSE | game::TARGET_FLAG_PVP_CORPSE)) {
uint64_t corpseGUID = 0;
packet->GetPackedGuid(corpseGUID);
auto *corpse = game::ClntObjMgrObjectPtr(game::TYPEMASK_CORPSE, corpseGUID);
if (corpse) {
corpseTargetGUID = game::GetCorpseOwner(corpse);
}
}
}
uint32_t itemId = 0;
if (itemGUID && *itemGUID != 0 && *itemGUID != casterGuidValue) {
@@ -953,7 +977,7 @@ namespace Nampower {
}
if (gUserSettings.enableSpellGoEvents) {
TriggerSpellGoEvent(itemId, spellId, casterGUID, unitTargetGUID, castFlags,
TriggerSpellGoEvent(itemId, spellId, casterGUID, unitTargetGUID, corpseTargetGUID, castFlags,
numTargetsHit, numTargetsMissed);
}
@@ -1033,8 +1057,28 @@ namespace Nampower {
packet->Get(spellCastTargetMask);
uint64_t unitTargetGUID = 0;
if (spellCastTargetMask & (game::TARGET_FLAG_UNIT | game::TARGET_FLAG_UNK2))
packet->GetPackedGuid(unitTargetGUID);
uint64_t goTargetGUID = 0;
uint64_t corpseTargetGUID = 0;
uint64_t itemTargetGUID = 0;
if (spellCastTargetMask & (game::TARGET_FLAG_UNIT | game::TARGET_FLAG_PVP_CORPSE | game::TARGET_FLAG_OBJECT | game::TARGET_FLAG_CORPSE | game::TARGET_FLAG_UNK2)) {
if (spellCastTargetMask & game::TARGET_FLAG_UNIT) {
packet->GetPackedGuid(unitTargetGUID);
} else if (spellCastTargetMask & (game::TARGET_FLAG_OBJECT | game::TARGET_FLAG_OBJECT_UNK)) {
packet->GetPackedGuid(goTargetGUID);
} else if (spellCastTargetMask & (game::TARGET_FLAG_CORPSE | game::TARGET_FLAG_PVP_CORPSE)) {
uint64_t corpseGUID = 0;
packet->GetPackedGuid(corpseGUID);
auto *corpse = game::ClntObjMgrObjectPtr(game::TYPEMASK_CORPSE, corpseGUID);
if (corpse) {
corpseTargetGUID = game::GetCorpseOwner(corpse);
}
}
}
if (spellCastTargetMask & (game::TARGET_FLAG_ITEM | game::TARGET_FLAG_TRADE_ITEM)) {
packet->GetPackedGuid(itemTargetGUID);
}
uint32_t itemId = 0;
if (itemGuid != 0 && itemGuid != casterGuid) {
@@ -1046,7 +1090,7 @@ namespace Nampower {
}
if (gUserSettings.enableSpellStartEvents) {
TriggerSpellStartEvent(itemId, spellId, casterGuid, unitTargetGUID, castFlags, castTime);
TriggerSpellStartEvent(itemId, spellId, casterGuid, unitTargetGUID, corpseTargetGUID, castFlags, castTime);
}
if (casterGuid == game::ClntObjMgrGetActivePlayerGuid()) {
@@ -1199,8 +1243,8 @@ namespace Nampower {
spellSchool,
effectsAuraTypeStream.str().c_str());
delete[] targetGuidStr;
delete[] casterGuidStr;
FreeGuidString(targetGuidStr);
FreeGuidString(casterGuidStr);
}
int PeriodicAuraLogHandlerHook(hadesmem::PatchDetourBase *detour, uint32_t unk, uint32_t opCode, uint32_t unk2,
+3 -3
View File
@@ -127,7 +127,7 @@ namespace Nampower {
if (!filtered) {
char *guidStr = ConvertGuidToString(names.guid);
SignalEventParam(eventCode, format, guidStr);
delete[] guidStr;
FreeGuidString(guidStr);
}
}
@@ -199,7 +199,7 @@ namespace Nampower {
names.pet ? 1 : 0,
names.partyIndex,
names.raidIndex);
delete[] guidStr;
FreeGuidString(guidStr);
}
void SendUnitSignalHook(hadesmem::PatchDetourBase *detour, uint64_t *guid, uint32_t eventCode) {
@@ -266,7 +266,7 @@ namespace Nampower {
names.pet ? 1 : 0,
names.partyIndex,
names.raidIndex);
delete[] guidStr;
FreeGuidString(guidStr);
}
}
}