The type is at [ObjBase + 0x14]. You're reading part of the GUID at offset 0x10.
Hello everyone,
I have been following some tutorials and now I'm trying to work with an ObjectManager to get some more understanding of the whole process.
After some reading I'm quite understanding the architecture of the WoWObjects and with some help of sample code I made a little program which should return (my) player object.
There is going something wrong (I think with the player base), because the WowObject that I'm creating has some invalid values. For example, the WoWobject carries a (int) Type to see which type of object it is. It has to be something like 1,2,3,4 but it returns something like 899283829. There COULD be an offset wrong, but I went through the dump and couldn't find my mistake. Hopefully you guys can see my problem so I can (finally) move on.
ObjectManager class:
Code:static class ObjectManager { public static BlackMagic WowReader = new BlackMagic(); private const uint staticClientConnection = 0x009BE678, // client connection, same address every boot objectManagerOffset = 0x463C, // offset from the ClientConnection to the object manager localGuidOffset = 0xC8, // offset from the object manager to the local guid firstObjectOffset = 0xC0, // offset from the object manager to the first object nextObjectOffset = 0x3C; // offset from one object to the next static private uint objectManagerBase; // the address off the object manager static public ulong localGuid; // the local guid. public static PlayerObject LocalPlayer; public static IDictionary<ulong, PlayerObject> PlayerObjectList = new Dictionary<ulong, PlayerObject>(); public static void LoadAddresses() { WowReader.OpenProcessAndThread(SProcess.GetProcessFromProcessName("Wow")); objectManagerBase = WowReader.ReadUInt(WowReader.ReadUInt(staticClientConnection + (uint)WowReader.MainModule.BaseAddress) + objectManagerOffset); localGuid = (uint)WowReader.ReadInt64((uint)(objectManagerBase + localGuidOffset)); } public static void PopulateLists() { PlayerObjectList.Clear(); WowObject CurrentObject = new WowObject(WowReader.ReadUInt((objectManagerBase + firstObjectOffset))); while (CurrentObject.BaseAddress != 0 && CurrentObject.BaseAddress % 2 == 0) { if (CurrentObject.Type == 4) { // a player PlayerObjectList.Add(CurrentObject.Guid, new PlayerObject(CurrentObject.BaseAddress)); if (CurrentObject.Guid == localGuid) // it is the local player LocalPlayer = PlayerObjectList[localGuid]; } CurrentObject.BaseAddress = WowReader.ReadUInt((CurrentObject.BaseAddress + nextObjectOffset)); } } }
WoWObjects
There could be an offset wrong, but I am not sure.. I hope someone is able to assist me with this problem. And I do have one question; how are you able to debug with Memory editing? How do you know what you're doing wrong, like having a wrong offset? You're not getting an exception, just wrong data.Code:class WowObject { protected const uint GuidOffset = 0x0, NextObjectOffset = 0x3C, TypeOffset = 0x10, XPositionOffset = 0x790, YPositionOffset = 0x794, ZPositionOffset = 0x798, RotationOffset = XPositionOffset + 0x10, DescriptorFieldsOffset = 0xc; protected uint baseAddress; public WowObject(uint baseAddress) { this.baseAddress = baseAddress; } public uint BaseAddress { get { return baseAddress; } set { baseAddress = value; } } public uint DescriptorFields { get { return ObjectManager.WowReader.ReadUInt((BaseAddress + DescriptorFieldsOffset)); } } public int Type { get { return ObjectManager.WowReader.ReadInt((BaseAddress + TypeOffset)); } } public virtual UInt64 Guid { get { return ObjectManager.WowReader.ReadUInt64((BaseAddress + GuidOffset)); } set { return; } } public virtual float XPosition { get { return ObjectManager.WowReader.ReadFloat((BaseAddress + XPositionOffset)); } } public virtual float YPosition { get { return ObjectManager.WowReader.ReadFloat((BaseAddress + YPositionOffset)); } } public virtual float ZPosition { get { return ObjectManager.WowReader.ReadFloat((BaseAddress + ZPositionOffset)); } } public float Rotation { get { return ObjectManager.WowReader.ReadFloat((BaseAddress + RotationOffset)); } } } class CreatureObject : WowObject { protected const uint LevelOffset = 0x20 + 0xA0 * 4, CurrentHealthOffset = 0x20 + 0x48 * 4, MaxHealthOffset = 0x20 + 0x60 * 4, CurrentManaOffset = 0x20 + 0x1AC * 4, MaxManaOffset = 0x20 * 4, TargetGuidOffset = 0x20 + 0x30 * 4; public CreatureObject(uint BaseAddress) : base(BaseAddress) { } public UInt64 TargetGuid { get { return ObjectManager.WowReader.ReadUInt64((DescriptorFields + TargetGuidOffset)); } } public int Level { get { return ObjectManager.WowReader.ReadInt((DescriptorFields + LevelOffset)); } } public int CurrentHealth { get { return ObjectManager.WowReader.ReadInt((DescriptorFields + CurrentHealthOffset)); } } public int MaxHealth { get { return ObjectManager.WowReader.ReadInt((DescriptorFields + MaxHealthOffset)); } } public int CurrentMana { get { return ObjectManager.WowReader.ReadInt((DescriptorFields + CurrentManaOffset)); } } public int MaxMana { get { return ObjectManager.WowReader.ReadInt((DescriptorFields + MaxManaOffset)); } } public int HealthPercent { get { double percentage = CurrentHealth / MaxHealth; percentage = percentage * 100; return (int)Math.Round(percentage); } } }
Thanks alot,
Require
Trade Feedbacks
The type is at [ObjBase + 0x14]. You're reading part of the GUID at offset 0x10.
Weird, saw 0x10 few times in the dumpthanks that was my Type problem hehe
---------- Post added at 11:53 PM ---------- Previous post was at 11:29 PM ----------
How am I able to see if the localGuid I'm retrieving is correct? Because I'm getting 2 times a playerobject with the same Guid. But I do not get my localGuid :/
Trade Feedbacks
You are reading the GUID at the wrong place
the GUID is located at [[objectBase + descriptorOffset] + GUIDOffset], not at [objectBase + GUIDOffset]
Last edited by xalcon; 01-06-2012 at 09:24 PM.
Trade Feedbacks
"Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live." - Martin Golding
"I cried a little earlier when I had to poop" - Sku
@xalcon: Hmm that didn't help. I am getting localGuid = 1 back.. plus again it adds 2 objects with same Guid and never comes inside the localPlayer if statement.
@Robske + xalcon: Could there be something else wrong? because I seem to have the offsets right and even with the feedback of xalcon I'm still experiencing this problem.
private const uint staticClientConnection = 0x009BE678, // client connection, same address every boot
This one is the objectManagerPointer offset right? or am I using the wrong one here, because this was the only one I wasn't certain about to be honest.
Thanks for your help guys
Trade Feedbacks
try this:
GuidOffset = 0x30; // not sure if 100% correct may have changed.
get { return ObjectManager.WowReader.ReadUInt64((BaseAddress + GuidOffset)); }
or read from from descriptors:
GuidOffset = 0x0;
et { return ObjectManager.WowReader.ReadUInt64((DescriptorFields() + GuidOffset));
just as robske wrote. your guid is stored in two different places. that can be a bit confusing if you are looking at different sources and you donīt know about it.
if something is in the descriptors, always try to read from there, because every read from somewhere other is one offset more to maintain.
Last edited by boredevil; 01-07-2012 at 12:04 PM.
Trade Feedbacks
ooh...kay... good to know :< My code is very similar to require's, but I don't use a Dictionary to store my references. Might be better ofc, but I was lazy on my first implementation :P
and well... after I've found the bug, I had to use my Stress reduction kit... such a simple problem
@require: there is some 'u' missing in your code... and in addition (and this is the main problem) you are casting an uint64 to an uint32 which causes your problem... so please STOP using unnecessary casts! the ReadXXXX-Methods already return the desired data type in the most cases!
localGuid = (uint)WowReader.ReadInt64((uint)(objectManagerBase + localGuidOffset));
||
\/
localGuid = WowReader.ReadUInt64((uint)(objectManagerBase + localGuidOffset));
Last edited by xalcon; 01-07-2012 at 12:55 PM.
Thanks alot guys I really appreciate your help.. I am at my girlfriends place now so I will give it a try tomorrow. Xalcon I indeed read somewhere that I should use an Uint, you are 100% correct on that. Thanks again for helping me out in such detail, both of you. Is it indeed true that the staticClient equals the object manager offset in the dump? Because I was rather confused about that.
This didn't help either. I am still having the problem that my code finds 2 times the same object(same GUID) so it crashes. And it NEVER finds my localPlayer for some reason.
Edit: Hmm something clearly goes wrong. The two objects it tries to add everytime is actually my playerobject. But in the property: MaxMana it shows my MaxHealth lol, so that offset isn't right. Because in my MaxHealth property it gives me a value that I can't recall to any value ingame:S So with those 2 objects which I know is my playerObject I was able to get the guid and it was far different from what I try to calculate with localGuid. Still don't know where it's going wrong though![]()
Last edited by Require; 01-08-2012 at 01:23 PM.
Trade Feedbacks
Well, I've fired up my testproject and copy'n'pasted your code. I can reproduce your problem and it seems it is as I said in my first post. When I change your WoWObject.GUID-getter to code to the following:
everything is working as intended o.O" Either blizzard has changed something or the code is still wrong. Anyway... use this GUID getter code or try to find the GUID with only one memory read instead of two. Do it as you like.Code:public virtual UInt64 Guid { get { return ObjectManager.WowReader.ReadUInt64(ObjectManager.WowReader.ReadUInt(BaseAddress + DescriptorFieldsOffset) + GuidOffset ); } set { return; } }
Last edited by xalcon; 01-08-2012 at 05:38 PM.
Trade Feedbacks
jep.
Here is a full explanation about the WoW GUID: (Maybe a bit outdated, but the way how the GUID works should be well explained)
Source: API Meta-Types - World of Warcraft Programming: A Guide and Reference for Creating WoW AddonsAll entities in World of Warcraft are identified by a unique 64-bit number; generally presented as a string containing a hexadecimal representation of the number (e.g. "0xF530007EAC083004"). (Note that Lua in WoW does not support 64-bit integers, so this value cannot be converted with tonumber.)
The type of unit represented by a GUID can be determined by using bit.band() to mask the first three digits with 0x00F: - 0x000 - A player - 0x003 - An NPC - 0x004 - A player's pet (i.e. hunter/warlock pets and similar; non-combat pets count as NPCs) - 0x005 - A vehicle
Further content of the GUID varies by unit type:
- Players - The remaining thirteen digits are unique to a player character at least within that character's battlegroup (that is, they remain unique and constant even in cross-server battlegrounds). This number is also semi-permanent -- it persists from character creation until deletion, renaming, or server transfer.
- NPCs - Remaining digits can be broken down as follows:
- Digits 4-6 - Unused.
- Digits 7-10 - NPC creature ID: identifies the specific named NPC (e.g. Hogger, Loque'nahak) or type of NPC (e.g. Sunfury Nethermancer, Shattertusk Mammoth). Converting to decimal results in the ID found on database sites such as wowhead.com; can also be used with [[docs/widgets/PlayerModel/SetCreature|PlayerModel:SetCreature]] to view the NPC's model.
- Digits 11-16 - Spawn counter: identifies the individual NPC (i.e. differentiates between the Gamon you recently killed and the Gamon that respawned a few minutes later, or between one Ymirheim Defender and another).
- Pets - Hunter pets immediately after taming retain the GUID they had as a wild creature; after resummoning or logout/login, their GUID changes to the pet format. Remaining digits can be broken down as follows:
- Digits 4-10 - A constant value unique to the individual pet: like a player's unique ID it is constant across multiple sessions.
- Digits 11-16 - Spawn counter: changes when the pet is dismissed and re-summoned.
- Vehicles - Same format and content as NPCs.
For example, the GUID 0xF530007EAC083004 can be deconstructed as follows:
- digits 1-3 are "F53"; bit.band(0xF53, 0x00F) == 0x003, so this is an NPC
- digits 7-10 are "7EAC"; 0x7EAC == 32428, which we can look up to find the NPC is a Underbelly Rat.
- digits 11-16 have no intrinsic meaning, but distinguish this Underbelly Rat from all others spawned since the last server reset.
Last edited by xalcon; 01-08-2012 at 06:52 PM.
Thanks Xalcon, I have been able to create a basic UI showing basic stuff about the player and because of reading a lot I was able to do this. Of course you helped me a lot by fixing that localGuid and I would like to thank you for that. There is just one thing I don't understand. I wanted to get the playersname in my WoWObject : PlayerObject, but in the tutorials I saw it gets the playersName from the WoWBaseaddress(process) why isn't this from the descriptors? and am I able to get the playersName from the descriptors? that would be much better regarding I am getting all my information from the descriptors.
Trade Feedbacks
The username of a player is not inside the object description, because of its dynamic length. (Well, atleast I think so)
To get the Name of the playerobject, you need to do some calculations and lookups. All playernames are stored in a temporary linked list. You will need a mask which you can apply on the players guid and then read the name out of the list... well, after you've done some magic :3
This is my PlayerName getter function. If you want to know how it works, try to reverse the Lua function "UnitName()" using IDA. To read the Name for a gameobject or a unit (monster, npc), the code differs.Code:get { string cname = ""; int mask = Sirenia.Memory.ReadInt((uint)Offsets.Name.NameStorePtr + (uint)Sirenia.Memory.MainModule.BaseAddress + (uint)Offsets.Name.NameMaskOffset); if (mask == -1) { cname = "UNKNOWN"; } else { UInt64 guid = this.GUID; int nameStoreBasePtr = Sirenia.Memory.ReadInt((uint)Offsets.Name.NameBaseOffset + (uint)Offsets.Name.NameStorePtr + (uint)Sirenia.Memory.MainModule.BaseAddress); int maskedGUID = mask & (int)guid; maskedGUID += maskedGUID * 2; int ptr = Sirenia.Memory.ReadInt((uint)(nameStoreBasePtr + (maskedGUID * 4) + 4) + 4); while (Sirenia.Memory.ReadUInt((uint)ptr) != (uint)guid) { int maskedGUID2 = (int)this.GUID & mask; maskedGUID2 += maskedGUID2 * 2; int ptr2 = Sirenia.Memory.ReadInt((uint)nameStoreBasePtr + ((uint)maskedGUID2 * 4)); ptr2 += ptr; ptr = Sirenia.Memory.ReadInt((uint)ptr2 + 4); } cname = Sirenia.Memory.ReadASCIIString((uint)ptr + (uint)Offsets.Name.NameStringOffset, 64); } return cname; }
Bookmarks