-
Member
Originally Posted by
scizzydo
The structure it points to is a chained hash. 0x0 is the capacity, 0x8 is the data and 0x10 is the number of elements. The data is the chained hash node, with 0x0 being the pointer to the next node, and 0x8 being the data. The node data is a pair<guid, entitybuilder<object*>*> (I called it EntityBuilder looking at the RTTI where it's used). The entity builder has the GUID at 0x10. Then the object pointer at 0x20. From the object, the type is at 0x8
The data seems to be judged by comparing two GUIDs. I can only find one GUID in entity builder 0x10. How can I query the other GUID for comparison?
-
Member
Originally Posted by
84771768
This looks very powerful, but I still don’t know how to obtain the data in ObjectManagerStruct, ObjectManagerEntry, and ObjectManagerEntryData. Can you provide a way to obtain it?

Search and compare with already known things - like guid, entity id, etc.
If you're looking for current offsets -
The structure it points to is a chained hash. 0x0 is the capacity, 0x8 is the data and 0x10 is the number of elements. The data is the chained hash node, with 0x0 being the pointer to the next node, and 0x8 being the data. The node data is a pair<guid, entitybuilder<object*>*> (I called it EntityBuilder looking at the RTTI where it's used). The entity builder has the GUID at 0x10. Then the object pointer at 0x20. From the object, the type is at 0x8
'The structure' is ObjectManagerStruct. 'The data' is ObjectManagerEntry. 'The node data' is ObjectManagerEntryData.
-
Active Member
edit: solved
Code:
unit aura table = 0x588
unit aura count = aura table + 8
unit name ptr = 0x108
unit name off = 0x168
object name ptr = 0xD8
object name off = 0xE0
Last edited by evil2; 10-29-2024 at 07:29 AM.
-
Member
Originally Posted by
evil2
thanks all for the great stuff, i got nearly everything running again... only missing:
- unit auras (buffs)
- unit name (not players)
did they changed the simple objbase->ptr->off to something like a chained hash also?
unit name is ( UnitAddress + 0x108 ) + 0x168
I have been able to get other data, but I still can't get the GUID data of ObjectManagerEntry. Can you share the GUID offset?
-
Active Member
Originally Posted by
84771768
The data seems to be judged by comparing two GUIDs. I can only find one GUID in entity builder 0x10. How can I query the other GUID for comparison?

by "other" GUID do you mean a GUID you are looking for or the GUID in the data entry?
it's a "chained hash table". wow GUID numbers are used as keys in this table.
you can go thru all table entries until you find your-key (guid) = data-key (guid)
or you can calculated (see page 1) the table entry index from your key (guid) and go directly to the data.
pseudo code for going thru all entries: (also see earlier posts from users scizzydo, thateuler, provirus)
Code:
adr = ReadLong (table_pointer) // objectmanager table
for (int x=0; x < table_max) { // table max size
if (adr == 0) { // unused entry? skip
adr = ReadLong (table_pointer) + 0x8 * ++x) // next in the table
continue
}
entry = ReadStruct <entry> (adr) // read entry [ next, key (guid), pointer to data ]
data = ReadStruct <data> (entry.pointer) // read data [ key (guid), value (pointer to object) ]
if (entry.key != data.key) ... // something went wrong, should never happen
else if (data.key == your-key) ... // found, do your stuff, WOW game object is at data.pointer
adr = entry.next // pointer to next (0 = no more)
count++; if (count > table_size) break // all used entries done => finish
}
struct entry {
0x00 long next
0x08 WowGuid key
0x18 long pointer (to data)
struct data {
0x10 WowGuid key
0x20 long pointer (to game object)
Last edited by evil2; 10-29-2024 at 01:33 AM.
-
Established Member
Originally Posted by
84771768
unit name is ( UnitAddress + 0x108 ) + 0x168
I have been able to get other data, but I still can't get the GUID data of ObjectManagerEntry. Can you share the GUID offset?
Well... I laid the data and offsets in the 4th post here: https://www.ownedcore.com/forums/wor...ml#post4506973 (57171 Retail 11.0.5 objectmgr changes)
GUID is at 0x10 of the Entity node, which is the second component of the data pair. Out of the data pair though, the key is also the GUID (read my description of the structures)
-
Active Member
Originally Posted by
thateuler
I'm trying to reverse that gmvision function to understand how object visibility is tracked. In 57171 there's a function call at 0x142966FB4 that determines is the object is visible or not. it returns -1 if not visible.
Code:
isvisible = ObjectInfo(something, 0x13);
value = num_activevisibleobjs + 1;
if ( isvisible == -1 )
value = num_activevisibleobjs;
v24 = mg4_entry;
num_activevisibleobjs = value;
Its the first parameter of ObjectInfo that I'm struggling to understand. (I just guessed at the name). The assembly is
Code:
.text:0000000142966FA1 8B 00 mov eax, [rax]
.text:0000000142966FA3 BA 13 00 00 00 mov edx, 13h
.text:0000000142966FA8 48 8D 0C 40 lea rcx, [rax+rax*2]
.text:0000000142966FAC 48 C1 E1 06 shl rcx, 6
.text:0000000142966FB0 48 03 4D 58 add rcx, [rbp+58h]
.text:0000000142966FB4 E8 C7 A4 94 FD call ObjectInfo
My guess is that there's some data structure traversal going on. It looks like rcx is a pointer to a struct because inside ObjectInfo it appears to be accessing fields. it gets something from 0x2c, then something else from 0x8
Any insight is appreciated.
the ObjectInfo function you mentioned does a binary search on a sorted array (ptr @ something+ 0x
, the second parameter is the value to search for which I like to call object category. It then returns the index of that category if found in that array. something+0x2C is the binary search upper bound. It seems game does this to determine if the entity has the given category. i.e. 19 is the category for visible objects, 7 for game objects etc.
Code:
//offset at 0x4724708
[StructLayout(LayoutKind.Explicit)]
internal struct CVisibleObjects
{
[FieldOffset(8)]
public IntPtr IndexesArray;
[FieldOffset(0x10)]
public long LastIndex;
[FieldOffset(0x20)]
public IntPtr EntitiesArray;
[FieldOffset(0x28)]
public long EntitiesCount;
[FieldOffset(0x58)]
public IntPtr CategoriesArray;
[FieldOffset(0x60)]
public long CategoriesCount;
}
Last edited by InnerSilence; 10-29-2024 at 02:40 AM.
-
Post Thanks / Like - 1 Thanks
provirus (1 members gave Thanks to InnerSilence for this useful post)
-
Member
Originally Posted by
evil2
by "other" GUID do you mean a GUID you are looking for or the GUID in the data entry?
it's a "chained hash table". wow GUID numbers are used as keys in this table.
you can go thru all table entries until you find your-key (guid) = data-key (guid)
or you can calculated (see page 1) the table entry index from your key (guid) and go directly to the data.
pseudo code for going thru all entries: (also see earlier posts from users scizzydo, thateuler, provirus)
Code:
adr = ReadLong (table_pointer) // objectmanager table
for (int x=0; x < table_max) { // table max size
if (adr == 0) { // unused entry? skip
adr = ReadLong (table_pointer) + 0x8 * ++x) // next in the table
continue
}
entry = ReadStruct <entry> (adr) // read entry [ next, key (guid), pointer to data ]
data = ReadStruct <data> (entry.pointer) // read data [ key (guid), value (pointer to object) ]
if (entry.key != data.key) ... // something went wrong, should never happen
else if (data.key == your-key) ... // found, do your stuff, WOW game object is at data.pointer
adr = entry.next // pointer to next (0 = no more)
count++; if (count > table_size) break // all used entries done => finish
}
struct entry {
0x00 long next
0x08 WowGuid key
0x18 long pointer (to data)
struct data {
0x10 WowGuid key
0x20 long pointer (to game object)
Thank you very much, I have succeeded.
But there is a problem. After I discard the items in the backpack, the items will still exist in the traversal table for a while. Is this normal?
-
Member
Originally Posted by
InnerSilence
the ObjectInfo function you mentioned does a binary search on a sorted array (ptr @ something+ 0x

, the second parameter is the value to search for which I like to call object category. It then returns the index of that category if found in that array. something+0x2C is the binary search upper bound. It seems game does this to determine if the entity has the given category. i.e. 19 is the category for visible objects, 7 for game objects etc.
Code:
//offset at 0x4724708
[StructLayout(LayoutKind.Explicit)]
internal struct CVisibleObjects
{
[FieldOffset(8)]
public IntPtr IndexesArray;
[FieldOffset(0x10)]
public long LastIndex;
[FieldOffset(0x20)]
public IntPtr EntitiesArray;
[FieldOffset(0x28)]
public long EntitiesCount;
[FieldOffset(0x58)]
public IntPtr CategoriesArray;
[FieldOffset(0x60)]
public long CategoriesCount;
}
Could you please explain how to get the address of `CVisibleObjects` struct for object (game obj, npc or player)?
-
Active Member
Originally Posted by
provirus
Could you please explain how to get the address of `CVisibleObjects` struct for object (game obj, npc or player)?
Code:
[StructLayout(LayoutKind.Explicit)]
internal struct CVisibleObjects
{
[FieldOffset(8)]
public IntPtr IndexesArray;
[FieldOffset(0x10)]
public long LastIndex;
[FieldOffset(0x20)]
public IntPtr EntitiesArray;
[FieldOffset(0x28)]
public long ObjectsCount;
[FieldOffset(0x58)]
public IntPtr CategoriesArray;
[FieldOffset(0x60)]
public long CategoriesCount;
}
[StructLayout(LayoutKind.Explicit)]
internal unsafe struct CEntityBuilder
{
[FieldOffset(0x0)]
public uint CategoryIndex;
[FieldOffset(0x08)]
public long EntityIndex;
[FieldOffset(0x10)]
public CGUID GUID;
[FieldOffset(0x20)]
public IntPtr WowObjectPtr;
}
[StructLayout(LayoutKind.Explicit)]
internal struct CEntityCategoryInfo
{
[FieldOffset(0x0)]
public IntPtr WowObjectOffset;
[FieldOffset(0x8)]
public IntPtr CategoriesSortedArray;
[FieldOffset(0x10)]
public IntPtr Unk_10;
[FieldOffset(0x18)]
public IntPtr Unk_18;
[FieldOffset(0x28)]
public uint Unk_28;
[FieldOffset(0x2C)]
public ushort SearchMax;
[FieldOffset(0xB8)]
public IntPtr Unk_B8;
}
int GetObjectCategoryIndex(ushort searchMax, byte[] categories, int category)
{
if (searchMax == 0)
return -1;
int searchMin = 0;
switch (searchMax)
{
case 0x100:
case 0x80:
case 0x40:
case 0x20:
case 0x10:
case 0x8:
case 0x4:
case 0x2:
searchMin = SearchArray(categories, searchMax, searchMin, category);
break;
default:
int offset = searchMax >> 1;
while (offset > 0x80)
{
if (categories[searchMin + offset] <= category)
searchMin += offset;
offset >>= 1;
}
if (searchMax > 0x10)
searchMin = SearchArray(categories, 0x80, searchMin, category);
else
searchMin = SearchArray(categories, searchMax, searchMin, category);
break;
}
int v7 = categories[searchMin];
return (v7 == category) ? searchMin : -1;
}
int SearchArray(byte[] bytes, ushort searchMax, int searchMin, int category)
{
int offset = searchMax >> 1;
while (offset != 0)
{
if (bytes[searchMin + offset] <= category)
searchMin += offset;
offset >>= 1;
}
return searchMin;
}
So what the game does is something like this:
However it got the entitybuilder object (i.e. from the methods posted in this topic), it reads the EntityIndex from it, then after doing bitwise AND with 0x3FFFFFFF, goes to the IndexesArray in the CVisibleObjects struct above and reads the entity index which then will be used to get the entitybuilder ptr from the EntitiesArray. Then game validates that the pointer found is the same as the original entitybuilder by comparing EntityIndexs of both. if they match then it reads CategoryIndex from the entitybuilder object to read the CEntityCategoryInfo from the CategoriesArray in the CVisibleObjects above. For visible objects game calls the GetObjectCategoryIndex I posted above with category=0 (first and second parameters are from the CEntityCategoryInfo). Only if index of this category 0 is not -1 then it is considered a visible game object and game reads the offset of the wowObjPtr from it to get the object from the entityBuilder object. Pls note that it is true that for objects that we care for WowObjectPtr is at offset 0x20 of the entityBuilder object. But game does not assume that and reads this offset from the CEntityCategoryInfo.WowObjectOffset. You can use the pattern E8 ? ? ? ? 48 85 C0 74 ? 49 8B D6 48 8B C8 41 FF D7 in the current build to get the offset to the method in the ClntObjMgrEnumVisibleObjectsPtr function that gets the EntityIndex as a parameter and returns the ptr to the visible object
-
Post Thanks / Like - 3 Thanks
-
Active Member
Originally Posted by
szKaXo
I also use this offset to determine, but sometimes even after looted, it still shows as lootable.
You can try this code, I am currently using it without any problems
Code:
*(int *)(obj + 0x7C) ==4
It sometimes doesn't refresh values immediately, it may be waiting for a server signal.
Last edited by gdfsxwy; 10-31-2024 at 09:42 PM.
-
Member
Originally Posted by
gdfsxwy
You can try this code, I am currently using it without any problems
Code:
*(int *)(obj + 0x7C) ==4
It sometimes doesn't refresh values immediately, it may be waiting for a server signal.
Yes, I also use the same code as you, but this situation rarely occurs. It still happens occasionally, and after it appears, it takes about 30 seconds to disappear
When I found such a object, when I tried to read its name, it displayed "unknown target", which should be an invisible object
Last edited by szKaXo; 10-31-2024 at 10:10 PM.
-
Active Member
Originally Posted by
szKaXo
Yes, I also use the same code as you, but this situation rarely occurs. It still happens occasionally, and after it appears, it takes about 30 seconds to disappear
When I found such a object, when I tried to read its name, it displayed "unknown target", which should be an invisible object
This is the function for determining Active objects. The value of parameter 2 (1
in sub_1402B8440 specifies the type of Active objects, and there are many similar functions elsewhere. I am not interested in collecting items and have not conducted further research. I hope it will be useful to you. The same applies to other visible target judgments.
Code:
//11.0.5.57292
bool __fastcall sub_1402B9710(_QWORD *EntityIndex)
{
_QWORD *v1; // r8
__int64 v3; // r10
unsigned __int64 v4; // rax
unsigned int **v5; // rdx
__int64 v6; // rax
unsigned int *v7; // rax
v1 = (_QWORD *)sub_142912DF0();
v3 = v1[4];
v4 = *EntityIndex & 0x3FFFFFFFi64;
v5 = (unsigned int **)(v3 + 8i64 * v1[5]);
if ( v4 < v1[2] )
{
v6 = *(unsigned int *)(v1[1] + 4 * v4);
if ( (_DWORD)v6 != 0x3FFFFFFF && *EntityIndex == *(_QWORD *)(*(_QWORD *)(v3 + 8 * v6) + 8i64) )
v5 = (unsigned int **)(v3 + 8 * v6);
}
if ( v5 == (unsigned int **)(v3 + 8i64 * v1[5]) )
v7 = 0i64;
else
v7 = *v5;
return (unsigned int)sub_1402B8440(v1[11] + 192i64 * *v7, 18) != -1;// Active objects = 18, GameObjs = 7 , visible = 19
}
I have tested and proven it to be effective.
Last edited by gdfsxwy; 11-01-2024 at 06:37 AM.
-
Member
Hi GetTransportGUID used to be vmt index 29 before the recent changes, does anyone know where this function is now?
Edit: Found it at 0x1425B3F40 (retail 57388 )
Last edited by goblin2kx; 11-02-2024 at 09:55 PM.