-
Added some error checks
The previous version went into an infinite loop a couple times so I added a couple checks to prevent that.
First check is a counter that assumes at any time there will not be more than 2000 objects.
Second check is to see if the next address is 0, if so break.
Code:
int count = 0;
Int64 address = Bot.Mem64.Read<Int64>( CurMgr + 0x120 );
while ( address != (CurMgr + 0x120))
{
count++;
if ( count > 2000 )
{
Bot.TimeLogDebug( "Object Manager Loop Error" );
break;
}
Int64 objectBase = address - 0x18;
WowObjStruct header = Bot.Mem64.Read<WowObjStruct>( objectBase );
ObjectData data = new ObjectData { BaseAddress = objectBase, Header = header };
switch ( header.WowType )
{
case ( Int32 ) eWowType.ITEM:
NumItems++;
break;
case ( Int32 ) eWowType.AREATRIGGER:
NumAreaTrigger++;
break;
case ( Int32 ) eWowType.CONTAINER:
NumContaniners++;
break;
case ( Int32 ) eWowType.CORPSE:
NumCorpse++;
break;
case ( Int32 ) eWowType.DYNAMICOBJECT:
NumDynamic++;
break;
case ( Int32 ) eWowType.GAMEOBJECT:
NumGameObjects++;
break;
case ( Int32 ) eWowType.PLAYER:
NumPlayers++;
break;
case ( Int32 ) eWowType.SCENEOBJECT:
NumScene++;
break;
case ( Int32 ) eWowType.UNIT:
NumUnits++;
break;
case ( Int32 ) eWowType.ACTIVEPLAYER:
NumActivePlayers++;
break;
case ( Int32 ) eWowType.AzeriteItem:
NumAzeriteItem++;
break;
case ( Int32 ) eWowType.AzeriteEmpoweredItem:
NumbAzeriteEmpoweredItem++;
break;
default:
NumOther++;
break;
}
currentScan.TryGetValue( data.Header.WowGuid, out ObjectData test );
if ( test == null )
{
currentScan.Add( data.Header.WowGuid, data );
}
else
{
}
address = header.Next;
if ( address == 0 )
{
Bot.TimeLogDebug( "Object Manager Next was Zero" );
break;
}
}
return currentScan;
}
-
Originally Posted by
65774332
i dont understand how to use it. it's my code
Gamer_NameCacheBase = 0x2441C20
Gamer_NameCacheName = 0x31;
Gmaer_NameCacheGuid = 0x20;
ptr := readint64(GamehProcess, module + Gamer_NameCacheBase);
for i := 0 to 100000 do
begin
curguid:= readGuid(GamehProcess, ptr);
if (curguid = guid) then
begin
str := ReadUtf8Text(GamehProcess, ptr + Gamer_NameCacheName);
if (str <> '') then
begin
result := str;
Break;
end;
end;
ptr := readint(GamehProcess, ptr);
end;
Where you read curguid, I think you need to add the guid offset (Gamer_NameCacheGuid) to ptr. Also, where you read the next object pointer at the end of the loop, you're using readint. Instead I think you should be using readint64 (unless they are equivalent in whatever implementation you're using).
-
Member
Originally Posted by
ndrax
Where you read curguid, I think you need to add the guid offset (Gamer_NameCacheGuid) to ptr. Also, where you read the next object pointer at the end of the loop, you're using readint. Instead I think you should be using readint64 (unless they are equivalent in whatever implementation you're using).
my mistake, I want you to see more clearly, so I deleted part of it.and readint64 is equivalent in readint
-
Member
Originally Posted by
ChrisIsMe
Unit Descriptors haven't been a real thing, most stuff is just placed into the object itself now... I haven't had a chance to really get a good look at much during the week, but I'll take a look this weekend to see if anything beyond looping the OM has changed.
8.2.5.31961 Object Manager
wow: 31961
Mgr: 0x288BF60
I'm having troubles with ObjectFirst and ObjectNext.
Maybe u has updates on this?
-
Active Member
Originally Posted by
counted
The previous version went into an infinite loop a couple times so I added a couple checks to prevent that.
First check is a counter that assumes at any time there will not be more than 2000 objects.
Second check is to see if the next address is 0, if so break.[
had the same problem, all old checks didn't work (ptr == 0 etc),
so i'm using the CurMgr counter at +0x10 for now
Originally Posted by
84771768
I'm having troubles with ObjectFirst and ObjectNext.
Maybe u has updates on this?
Code:
[StructLayout(LayoutKind.Explicit)]
public struct CurObj {
[FieldOffset(0x10)] public byte Type;
[FieldOffset(0x18)] public long Next;
[FieldOffset(0x40)] public WowGuid Guid;
}
var CurMgr = mem.ReadLong(base + 0x288bf60);
var ptr = mem.ReadLong(CurMgr + 0x120); // address in to first object
var cnt1 = mem.ReadInt(CurMgr + 0x10); // max count
var cnt2 = 0;
var obj = new CurObj();
while (true) {
obj = mem.ReadData(ptr - 0x18); // read from start (base address) of object
// --> do your stuff here, like parsing your obj or make a temporary database
cnt2++; if (cnt2 >= cnt1) break;
ptr = obj.Next; // address in to next object
}
i also have the problem of resolving player names, can some one hint me on this.
my current not working code:
Code:
var ptr = mem.ReadLong(base + 0x2441c20);
while (true) {
if (mem.ReadUInt64(ptr + 0x20) == player.Guid.Uint64) {
return utfbytes2string(mem.ReadBytes(ptr + 0x31, 0x30));
}
ptr = mem.ReadLong(ptr);
}
Last edited by evil2; 09-30-2019 at 07:53 AM.
-
Member
Originally Posted by
evil2
had the same problem, all old checks didn't work (ptr == 0 etc),
so i'm using the CurMgr counter at +0x10 for now
Code:
[StructLayout(LayoutKind.Explicit)]
public struct CurObj {
[FieldOffset(0x10)] public byte Type;
[FieldOffset(0x18)] public long Next;
[FieldOffset(0x40)] public WowGuid Guid;
}
var CurMgr = mem.ReadLong(base + 0x288bf60);
var ptr = mem.ReadLong(CurMgr + 0x120); // address in to first object
var cnt1 = mem.ReadInt(CurMgr + 0x10); // max count
var cnt2 = 0;
var obj = new CurObj();
while (true) {
obj = mem.ReadData(ptr - 0x18); // read from start (base address) of object
// --> do your stuff here, like parsing your obj or make a temporary database
cnt2++; if (cnt2 >= cnt1) break;
ptr = obj.Next; // address in to next object
}
i also have the problem of resolving player names, can some one hint me on this.
my current not working code:
Code:
var ptr = mem.ReadLong(base + 0x2441c20);
while (true) {
if (mem.ReadUInt64(ptr + 0x20) == player.Guid.Uint64) {
return utfbytes2string(mem.ReadBytes(ptr + 0x31, 0x30));
}
ptr = mem.ReadLong(ptr);
}
I think I have already done it, thank you very much.
In addition, I would like to know about the method of the item name.
This problem has been bothering me.
-
Player Name Cache
I do NOT have this figured out yet but here is what I have:
The new structure appears to be an array structure like the 0x8 curMgr list
if you read the memory location of PlayerNameCache you get this
Code:
//0 | 7FF61A56FCE0
//8 | 100 I think this is the number of array elements in the 0x10 list
//10 | 19133DC5460
//18 | 9D
//20 | 3F800000
//28 | 0
//30 | 1
//38 | 100 i think this is the number of array elements in the 0x40 list
//40 | 19133DB6D30
//48 | 99
//50 | 3F800000
//58 | FFFFFFFFFFFFFFFF
//60 | FFFFFFFF
//68 | 0
//70 | FFFFFFFFFFFFFFFF
//78 | FFFFFFFF
//80 | 2000000000
//88 | 0
//90 | 7FF61A56FE58
//98 | 400
So i think there are two arrays the 0x10 and 0x40 arrays.
if you read the 0x10 pointer you will see a bunch of addresses to cache entries i think.
Some array entreis are 0.
So my current code is
Code:
[StructLayout( LayoutKind.Sequential )]
public class PlayerCacheEntry
{
public Int64 next; //0x00 0x08 THIS IS NOT NEXT IN THE NEW STRUCTURE, I stated with this because it was my old CacheEntry
public Int64 unknown1; //0x08 0x10
public Int64 unknown2; //0x10 0x18
public Int64 unknown3; //0x18 0x20
public Int128 guid; //0x20 0x24 0x28 0x2c 0x30
public Byte pad;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x30)]
public Byte[] name; // 0x31
}
public static String GetName( Int128 playerGuid )
{
if( playerGuid == Game.ActivePlayerGuid())
{
return Game.ActivePlayerName();
}
Int32 count = Bot.Mem64.Read<Int32>( Bot.Mem64.Rebase( Offsets.pPlayerNameCache ) + 0x8 );
Int64 startAddress = Bot.Mem64.Read<Int64>(Bot.Mem64.Rebase(Offsets.pPlayerNameCache) + 0x10);
for ( Int32 x = 0; x < count; x++ )
{
Int64 address = Bot.Mem64.Read<Int64>( startAddress + 0x8 * x);
if ( address == 0 )
{
continue;
}
PlayerCacheEntry data = Bot.Mem64.Read<PlayerCacheEntry>(address - 0x18);
if ( data.next == 0) // using this to debug the old next field, it is NOT next in the new structure
{
bool crap = true;
}
if ( data.guid == playerGuid )
{
String name = Bot.Mem64.UTF8Decoder(data.name);
return name;
}
}
return "Error";
}
Note: you need to subtract 0x18 from the array address to get to the base of the PlayerCacheEntry.
I have not been able to find a subroutine that I can reverse to figure this out, i figured that out by poking around the memory map.
This gives me several of the in game player names but there are many that are not found.
Also there are a lot of PlayerCahceEntries for guids that are not in the object manager.
Still working on this one
you can use this code to dump all of the names and as you will see there are a crap load, but not all of them
Code:
public static String DumpNames( Int128 playerGuid )
{
Int32 count = Bot.Mem64.Read<Int32>( Bot.Mem64.Rebase( Offsets.pPlayerNameCache ) + 0x8 );
Int64 startAddress = Bot.Mem64.Read<Int64>(Bot.Mem64.Rebase(Offsets.pPlayerNameCache) + 0x10);
Bot.TimeLogBoth( count.ToString() );
for ( Int32 x = 0; x < count; x++ )
{
Int64 address = Bot.Mem64.Read<Int64>( startAddress + 0x8 * x);
if ( address == 0 )
{
continue;
}
PlayerCacheEntry data = Bot.Mem64.Read<PlayerCacheEntry>(address - 0x18);
String name = Bot.Mem64.UTF8Decoder(data.name);
Bot.TimeLogBoth(x + " | " + name );
}
return "Error";
}
Last edited by counted; 09-30-2019 at 09:28 PM.
-
Post Thanks / Like - 1 Thanks
evil2 (1 members gave Thanks to counted for this useful post)
-
I think i just thought of what the problem is, I was comparing the complete guid and which includes realm and server id info. I probably need to just compare short guid (creation bits), I am guessing that the playername cache does not update as players change servers ( going in and out of instances, BG, phasing ....)
I am traveling for business atm, will test when i get home.
-
PlayerNameCache Solved
I think, lol
Code:
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public class PlayerCacheEntry
{
public Int64 next;
public Int128 guid;
public Byte pad;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x30)]
public Byte[] name;
}
public static String GetName( Int128 playerGuid )
{
if ( playerGuid == Game.ActivePlayerGuid() )
{
return Game.ActivePlayerName();
}
// number of entries in the array
Int32 count = Bot.Mem64.Read<Int32>( Bot.Mem64.Rebase( Offsets.pPlayerNameCache ) + 0x8 );
// base address of the array
Int64 baseAddress = Bot.Mem64.Read<Int64>(Bot.Mem64.Rebase(Offsets.pPlayerNameCache) + 0x10);
// first entry
Int64 currentAddress = Bot.Mem64.Read<Int64>( baseAddress );
// need to traverse through all of the entry chains in the 0x10 array
// array entries can be empty currentAddress == 0
// array entries can point to one cache entry, if data.next == 0
// array entries can point to a chain of entries while ( data.next != 0 )
for ( Int32 x = 0; x < count; )
{
if ( currentAddress == 0 )
{
// current entry is empty, go to the next one
x++;
currentAddress = Bot.Mem64.Read<Int64>( baseAddress + 0x8 * x);
continue;
}
PlayerCacheEntry data = Bot.Mem64.Read<PlayerCacheEntry>(currentAddress);
if ( data.guid == playerGuid )
{
// found the guid, woot
String name = Bot.Mem64.UTF8Decoder(data.name);
// not sure this is required, put it here incase there is an old entry that has not been flushed
// will keep looking if name is ""
if ( name != "" )
{
return name;
}
}
if( data.next == 0)
{
// end of the current chain, go to next array entry
x++;
currentAddress = Bot.Mem64.Read<Int64>( baseAddress + 0x8 * x );
}
else
{
// go to next cache entry in this chain, do not increment x
currentAddress = data.next;
}
}
return "Unknown";
}
-
Post Thanks / Like - 6 Thanks
-
Member
Originally Posted by
counted
I think, lol
Code:
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public class PlayerCacheEntry
{
public Int64 next;
public Int128 guid;
public Byte pad;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x30)]
public Byte[] name;
}
public static String GetName( Int128 playerGuid )
{
if ( playerGuid == Game.ActivePlayerGuid() )
{
return Game.ActivePlayerName();
}
// number of entries in the array
Int32 count = Bot.Mem64.Read<Int32>( Bot.Mem64.Rebase( Offsets.pPlayerNameCache ) + 0x8 );
// base address of the array
Int64 baseAddress = Bot.Mem64.Read<Int64>(Bot.Mem64.Rebase(Offsets.pPlayerNameCache) + 0x10);
// first entry
Int64 currentAddress = Bot.Mem64.Read<Int64>( baseAddress );
// need to traverse through all of the entry chains in the 0x10 array
// array entries can be empty currentAddress == 0
// array entries can point to one cache entry, if data.next == 0
// array entries can point to a chain of entries while ( data.next != 0 )
for ( Int32 x = 0; x < count; )
{
if ( currentAddress == 0 )
{
// current entry is empty, go to the next one
x++;
currentAddress = Bot.Mem64.Read<Int64>( baseAddress + 0x8 * x);
continue;
}
PlayerCacheEntry data = Bot.Mem64.Read<PlayerCacheEntry>(currentAddress);
if ( data.guid == playerGuid )
{
// found the guid, woot
String name = Bot.Mem64.UTF8Decoder(data.name);
// not sure this is required, put it here incase there is an old entry that has not been flushed
// will keep looking if name is ""
if ( name != "" )
{
return name;
}
}
if( data.next == 0)
{
// end of the current chain, go to next array entry
x++;
currentAddress = Bot.Mem64.Read<Int64>( baseAddress + 0x8 * x );
}
else
{
// go to next cache entry in this chain, do not increment x
currentAddress = data.next;
}
}
return "Unknown";
}
Thanks worked for me! PlayerNameCacheOff 0x2586BE0
-
Member
Originally Posted by
counted
I think i have the changes to the object manager figured out.
I started with binary 31921 sub_F2F550 which is the ObjectUsageCallback and reversed it.
Here is what I got
Code:
public static void ObjectUsageCallback()
{
//31921 sub_F2F550
Int32 numVisible = 0;
for ( Int64 i = Mem64.Read<Int64>( CurMgr + 0x120 ); i != ( CurMgr + 0x120 ); ++numVisible )
i = Mem64.Read<Int64>( i );
Int64 pArrayBase = Mem64.Read<Int64>( CurMgr + 0x8 );
Int64 objectArraySize = Mem64.Read<Int64>( CurMgr );
Int64 pArrayLast = pArrayBase + objectArraySize;
Int64 pObjectHeader = Mem64.Read<Int64>( pArrayBase );
Int32 numActive = 0;
Int32 numItems = 0;
Int32 numGameObjects = 0;
Int32 numUnits = 0;
Boolean done = false;
Int32 arrayindex = 0;
Int64 pArrayNext = 0;
while ( !done )
{
Int32 chain = 0;
do
{
++chain;
++numActive;
Int64 pObject = Mem64.Read<Int64>(pObjectHeader + 0x18);
Byte wowObjectType = Mem64.Read<Byte> ( pObject + 0x10 );
UInt32 wowObjectTypeFlags = Mem64.Read<UInt32>(Mem64.Rebase(pWowTypeFlags) + wowObjectType * 0x4);
if ( ( ( Int32 ) wowObjectTypeFlags & 6 ) > 0 )
{
++numItems;
}
else if ( ( ( ( Int32 ) wowObjectTypeFlags >> 5 ) & 1 ) > 0 )
{
++numUnits;
}
else if ( ( ( Int32 ) wowObjectTypeFlags & 0x100 ) > 0 )
{
++numGameObjects;
}
pObjectHeader = Mem64.Read<Int64>( pObjectHeader );
}
while ( pObjectHeader > 0 );
TimeLogBoth( "Chain " + arrayindex + " | " + chain );
arrayindex++;
pArrayNext = pArrayBase + 0x8 + arrayindex;
if ( pArrayNext >= pArrayLast )
{
break;
}
while ( true )
{
pObjectHeader = Mem64.Read<Int64>( pArrayNext );
if ( pObjectHeader > 0 )
{
break;
}
TimeLogBoth( "Chain " + arrayindex + " | 0" );
arrayindex++;
pArrayNext = pArrayBase + 0x8 * arrayindex;
if ( pArrayNext >= pArrayLast )
{
done = true;
break;
}
}
}
Int32 numWaitingToBeFreed = Mem64.Read<Int32>( CurMgr + 0x30 );
}
This shows that the curMgr + 0x120 contains all of the visible objects.
The curMgr + 0x8 appears to be an array of 80 object chains. Some chains are empty some have one object some have a couple objects.
when i ran the code it does NOT have all of he objects in the visible 0x120 object list.
So I focused on the 0x120 object list
I looked at 31921 sub_F30010 ClntObjMgrEnumVisibleObjects and the call back that gets passed to if from Script_ClosestUnitPosition which is sub_15DD560
ClntObjMgrEnumVisibleObjects sub_F30010 subtracts 0x18 from the 0x120 address and inside sub_15DD560 it adds 0x10 back to that address and indexs the WowObjectFlags array so this gives us the info we need.
so basically the 0x120 address link list is pointing to the Next Object Address of the Object and we need to subtract 0x18 from it to the get object base. From that Base the type is 0x10 and the guild is 0x40. It also looks like 0x20 is the previous object. The struct is shown below.
Code:
//31921 sub_F30010 ClntObjMgrEnumVisibleObjects
//31921 Script_ClosestUnitPosition call back for ClntObjMgrEnumVisibleObjects sub_15DD560
NumPlayers = 0;
NumGameObjects = 0;
NumContaniners = 0;
NumItems = 0;
NumUnits = 0;
NumCorpse = 0;
NumDynamic = 0;
NumAreaTrigger = 0;
NumScene = 0;
NumOther = 0;
NumAzeriteItem = 0;
NumbAzeriteEmpoweredItem = 0;
NumActivePlayers = 0;
Dictionary<Int128, ObjectData> currentScan = new Dictionary<Int128, ObjectData>();
if ( CurMgr == 0 )
{
return currentScan;
}
for ( Int64 i = Mem64.Read<Int64>( CurMgr + 0x120 ); i != ( CurMgr + 0x120 ); )
{
Int64 address = i - 0x18;
WowObjStruct header = Mem64.Read<WowObjStruct>( address );
ObjectData data = new ObjectData { BaseAddress = address, Header = header };
switch ( header.WowType )
{
case ( Int32 ) eWowType.ITEM:
NumItems++;
break;
case ( Int32 ) eWowType.AREATRIGGER:
NumAreaTrigger++;
break;
case ( Int32 ) eWowType.CONTAINER:
NumContaniners++;
break;
case ( Int32 ) eWowType.CORPSE:
NumCorpse++;
break;
case ( Int32 ) eWowType.DYNAMICOBJECT:
NumDynamic++;
break;
case ( Int32 ) eWowType.GAMEOBJECT:
NumGameObjects++;
break;
case ( Int32 ) eWowType.PLAYER:
NumPlayers++;
break;
case ( Int32 ) eWowType.SCENEOBJECT:
NumScene++;
break;
case ( Int32 ) eWowType.UNIT:
NumUnits++;
break;
case ( Int32 ) eWowType.ACTIVEPLAYER:
NumActivePlayers++;
break;
case ( Int32 ) eWowType.AzeriteItem:
NumAzeriteItem++;
break;
case ( Int32 ) eWowType.AzeriteEmpoweredItem:
NumbAzeriteEmpoweredItem++;
break;
default:
NumOther++;
break;
}
currentScan.TryGetValue( data.Header.WowGuid, out ObjectData test );
if ( test == null )
{
currentScan.Add( data.Header.WowGuid, data );
}
else
{
//duplicate guid ??
}
i = Mem64.Read<Int64>( i );
}
return currentScan;
}
Last thing i did was set a break point on the object manager for object type 7 ( Active Player) and searched the object for my guid to get the guid offset 0x40;
Code:
//31921
[StructLayout( LayoutKind.Explicit )]
public struct WowObjStruct
{
[FieldOffset(0)]
public Int64 vtable;
[FieldOffset(0X10)]
public Byte WowType;
[FieldOffset(0X18)]
public Int64 Next;
[FieldOffset(0X20)]
public Int64 Previous;
[FieldOffset(0X40)]
public Int128 WowGuid;
}
Mem64.Rebase What is the meaning of?
-
It is a function that adds the binary base address to the pointer offset. This is needed because a ASLR which randomizes the binary base address each time it is loaded.
Most of the pointers that are listed in binary update threads are really pointer offsets from the base of the binary.
-
Member
Originally Posted by
counted
It is a function that adds the binary base address to the pointer offset. This is needed because a ASLR which randomizes the binary base address each time it is loaded.
Most of the pointers that are listed in binary update threads are really pointer offsets from the base of the binary.
Thank you, I have learned a lot and made some progress, I've got what I want
-
Member
Originally Posted by
counted
It is a function that adds the binary base address to the pointer offset. This is needed because a ASLR which randomizes the binary base address each time it is loaded.
Most of the pointers that are listed in binary update threads are really pointer offsets from the base of the binary.
Can the 0x8 entry method traverse to object's Position? Why did I not get the correct Myself Player posX value after looping 5000 times
-
Originally Posted by
Lvv
Can the 0x8 entry method traverse to object's Position? Why did I not get the correct Myself Player posX value after looping 5000 times
Yes and No. The traversing the 0x8 object manager list will give you the base address and guid for every object in the game. You need to find your player object [ hint your guid is in the CurMgr struct ] and then read the correct offset from the object base to get the xyz. If you read some of the posts at the beginning of this thread several people list useful offset from object base.