From 049f6cd8512c44999e823c94e6f019c6c0f128ae Mon Sep 17 00:00:00 2001 From: MarcelineVQ Date: Tue, 3 Mar 2026 15:13:14 -0800 Subject: [PATCH] Use name cache for combatlog player name, remove SignalEvent probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch from GetObjectPtr→GetUnitName (object manager, not populated at LoggingCombat time) to RetrieveNPCDataFromCache (name cache, available early). Remove the SignalEvent probe hook that was only for discovery. --- src/combatlog/combatlog.zig | 56 +++++++++++++++++-------------------- src/combatlog/offsets.zig | 14 +++++----- 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/src/combatlog/combatlog.zig b/src/combatlog/combatlog.zig index 1269720..4cdd316 100644 --- a/src/combatlog/combatlog.zig +++ b/src/combatlog/combatlog.zig @@ -95,35 +95,32 @@ fn getPlayerGUID() u64 { return (@as(u64, hi) << 32) | lo; } -/// Look up a unit name by GUID — two-step approach matching perfboost: -/// 1. GetObjectPtr (0x464870) — __stdcall(u64 guid) → object ptr in EAX -/// 2. CGUnit_C::GetUnitName (0x609210) — __thiscall(ECX=unit, stack: 0) → char* -fn getNameFromGUID(guid: u64) ?[*:0]const u8 { - if (guid == 0) return null; - - // Step 1: GUID → object pointer via __stdcall (GUID pushed on stack as 8 bytes) - const guid_lo: u32 = @truncate(guid); - const guid_hi: u32 = @truncate(guid >> 32); - const obj: u32 = asm volatile ( - \\ push %[hi] - \\ push %[lo] - \\ call *%[func] - : [ret] "={eax}" (-> u32), - : [lo] "r" (guid_lo), - [hi] "r" (guid_hi), - [func] "r" (@as(u32, o.FN_GET_OBJECT_PTR)), - : .{ .ecx = true, .edx = true, .memory = true, .cc = true }); - if (obj == 0) return null; - - // Step 2: object pointer → name (__thiscall: ECX=this, push flag=0) +/// Look up a player name from the name cache by GUID. +/// Calls RetrieveNPCDataFromCache — __thiscall(ECX=cache), 6 stack params, RET 0x18. +/// Available before the object manager is populated (unlike GetObjectPtr → GetUnitName). +fn getNameFromGUID(guid_lo: u32, guid_hi: u32) ?[*:0]const u8 { + if (guid_lo == 0 and guid_hi == 0) return null; + var name_buf: [2]u32 = .{ 0, 0 }; + const stack_args = [6]u32{ + guid_lo, + guid_hi, + @intFromPtr(&name_buf), + 0, 0, 0, + }; const result: u32 = asm volatile ( - \\ push $0 + \\ push 20(%[a]) + \\ push 16(%[a]) + \\ push 12(%[a]) + \\ push 8(%[a]) + \\ push 4(%[a]) + \\ push (%[a]) \\ call *%[func] : [ret] "={eax}" (-> u32), - : [_] "{ecx}" (obj), - [func] "r" (@as(u32, o.FN_GET_UNIT_NAME)), + : [_] "{ecx}" (@as(u32, o.NAME_CACHE_OBJ)), + [a] "r" (&stack_args), + [func] "r" (@as(u32, o.FN_NAME_CACHE_LOOKUP)), : .{ .ecx = true, .edx = true, .memory = true, .cc = true }); - return if (result != 0 and result >= 0x10000) @ptrFromInt(result) else null; + return if (result != 0) @ptrFromInt(result) else null; } // ============================================================================= @@ -141,12 +138,11 @@ fn maybeWriteSessionMarker() void { const player_guid = getPlayerGUID(); if (player_guid == 0) return; + const guid_lo: u32 = @truncate(player_guid); + const guid_hi: u32 = @truncate(player_guid >> 32); - const name = getNameFromGUID(player_guid) orelse { - // TODO: If player name is unavailable at this point (e.g. - // LoggingCombat enabled before login), could hook OnWorldUpdate for a - // per-frame retry until name is available. - con.print("[combatlog] session marker: player name not yet available\n"); + const name = getNameFromGUID(guid_lo, guid_hi) orelse { + con.print("[combatlog] session marker: player name not in cache yet\n"); return; }; diff --git a/src/combatlog/offsets.zig b/src/combatlog/offsets.zig index 3eaf32f..294389a 100644 --- a/src/combatlog/offsets.zig +++ b/src/combatlog/offsets.zig @@ -32,16 +32,16 @@ pub const FN_WRITE_FMT_LOG_MSG: usize = 0x0065ac20; pub const FN_ENABLE_CHAT_LOGGING: usize = 0x0049fe50; // ============================================================================= -// Player identity (shared with markers module) +// Player identity // ============================================================================= /// GetPlayerGUID — __fastcall(), no params, returns EAX(low):EDX(high). pub const FN_GET_PLAYER_GUID: usize = 0x00468550; -/// GetObjectPtr — __stdcall(u64 guid) → object ptr in EAX. -/// GUID is pushed on the stack as 8 bytes. (perfboost: 0x464870) -pub const FN_GET_OBJECT_PTR: usize = 0x00464870; +/// RetrieveNPCDataFromCache — __thiscall(ECX=cache_obj), 6 stack params, RET 0x18. +/// (guid_low, guid_high, name_buf_ptr, 0, 0, 0) → char* name or NULL in EAX. +/// Resolves player/NPC names from the name cache — available before the object manager. +pub const FN_NAME_CACHE_LOOKUP: usize = 0x0055f080; -/// CGUnit_C::GetUnitName — __thiscall(ECX=unit_ptr, stack: flag=0) → char* in EAX. -/// (perfboost: 0x609210) -pub const FN_GET_UNIT_NAME: usize = 0x00609210; +/// Name cache object — static instance passed as ECX (this) to RetrieveNPCDataFromCache. +pub const NAME_CACHE_OBJ: usize = 0x00c0e228;