I've got a bit of time up my sleeve so I thought I would post an (intermediate) guide to the way that I get data from the object manager, to give back to the community a little. The code in this guide is written in C# but it shouldn't be too hard to port to any other OOP language. Note that you should already know how to retrieve data out of the object manager. The thing I use to read memory is a library that I don't know the author of (If you are the author, tell me!) Also note that I say things such as 'all object have an XYZ value' - this is not true. For example, items and containers do not. But in this guide we're only going to be dealing with in-game objects.
If you take a sit back and think about it, what is the Object Manager (from now on referred to as OM) in Wow? In the broadest terms possible, it is an object that handles other objects. We know that once we have the base address of an WowObject (the object that the OM handles), we can get different information about it depending on the type of object it is.
So it makes sense then, to build one object to handle WowObjects, and then build the WowObjects themselves. Lets jot down some information here:
- Our OM needs a way to 'load' and find the base address of the in-game Object Manager.
- Our OM needs to iterate through the in-game Object Manager and get two peices of information about each object - the base address, and the type. Using this information, we can create a new "WowObject" and store it.
- Our OM needs to be able to store all the objects (arrays of WowObjects anyone?)
- Our WowObjects, given a base address and type, need to be able to get information about themselves and return it to whoever is asking, on the fly.
WowObjects
The first thing that I started out with was the WowObjects themselves. As you (should) know, the objects in the in-game Object Manager can be of seven different types:
Code:
1 -Items
2 - Contains
3 - NPC's
4 - Players
5 - GameObjects (Nodes etc)
6 - DynamicObjects (Spells and stuff)
7 - Corpses
In this guide, we're only going to deal with 3 through 7.
What you may not know, however is that each object has attributes that are common to 'all' objects. Note that XYZ isn't common to 'all', but in this guide we're only dealing with actual in-game objects (instead of items etc). Things that are common between all of these in-game objects are:
- A GUID (Globally Unique Id)
- A base address
- A type
- An X,Y,Z position
- A rotation value
- Descriptor fields
There are actually more but these are all we are interested in for now. So if we have things in common, wouldn't it make sense to make a base class that all the other objects inherit from? I give you (very creatively named): WowObject
Code:
class WowObject
{
protected const uint GuidOffset = 0x30,
NextObjectOffset = 0x3C,
TypeOffset = 0x14,
XPositionOffset = 0x7D0,
YPositionOffset = 0x7D4,
ZPositionOffset = 0x7D8,
RotationOffset = 0x7DC,
DescriptorFieldsOffset = 0x8;
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.ReadUInt32((IntPtr)(BaseAddress + DescriptorFieldsOffset)); }
}
public int Type
{
get { return ObjectManager.WowReader.ReadInt32((IntPtr)(BaseAddress + TypeOffset)); }
}
public virtual ulong Guid
{
get { return ObjectManager.WowReader.ReadULong((IntPtr)(BaseAddress + GuidOffset)); }
set { return; }
}
public virtual float XPosition
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(BaseAddress + XPositionOffset)); }
}
public virtual float YPosition
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(BaseAddress + YPositionOffset)); }
}
public virtual float ZPosition
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(BaseAddress + ZPositionOffset)); }
}
public float Rotation
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(BaseAddress + RotationOffset)); }
}
}
You'll notice that, providing only the base address to this class, we are able to get some interesting information. Now we can do some really nifty stuff! For example, say in another part of our project we created a WowObject with the name George. If we want to know George's X position, all we'd have to write (assuming that George already knows his base address) is this:
Code:
MessageBox.Show(George.XPosition.ToString());
George already knows how to get his XPosition, so he scrambles off and gets it and returns it to us whenever we need it!
Note that the in-game Object Manager does not actually hold any of these types of objects. It holds more specific objects. It can be thought about in an OOP way like this:
Code:
WowObject[] ObjectList = new WowObject[5];
ObjectList[0] = new NpcObject();
ObjectList[1] = new GameObject();
// and so on
Speaking of more specific objects, our WowObject class isn't going to be useful on its own, since no object in our OM's array is going to be a plain old WowObject! We need it to hold more specific objects. What is the best way to describe this in an OOP language? Subclasses that inherit. Therefore, I'll give you the rest of our WowObject (and subclasses) declarations:
Code:
class CreatureObject : WowObject
{
protected const uint LevelOffset = 0x35 * 4,
CurrentHealthOffset = 0x17 * 4,
MaxHealthOffset = 0x1F * 4,
CurrentManaOffset = 0x18 * 4,
MaxManaOffset = 0x20 * 4,
TargetGuidOffset = 0x12 * 4;
public CreatureObject(uint BaseAddress)
: base(BaseAddress)
{ }
public float Pitch
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(ObjectManager.WowReader.ReadUInt32((IntPtr)(baseAddress + 0x110)) + 0x20)); }
}
public ulong TargetGuid
{
get { return ObjectManager.WowReader.ReadULong((IntPtr)(DescriptorFields + TargetGuidOffset)); }
}
public int Level
{
get { return ObjectManager.WowReader.ReadInt32((IntPtr)(DescriptorFields + LevelOffset)); }
}
public int CurrentHealth
{
get { return ObjectManager.WowReader.ReadInt32((IntPtr)(DescriptorFields + CurrentHealthOffset)); }
}
public int MaxHealth
{
get { return ObjectManager.WowReader.ReadInt32((IntPtr)(DescriptorFields + MaxHealthOffset)); }
}
public int CurrentMana
{
get { return ObjectManager.WowReader.ReadInt32((IntPtr)(DescriptorFields + CurrentMana)); }
}
public int MaxMana
{
get { return ObjectManager.WowReader.ReadInt32((IntPtr)(DescriptorFields + MaxManaOffset)); }
}
public int HealthPercent
{
get
{
double percentage = CurrentHealth / MaxHealth;
percentage = percentage * 100;
return (int)Math.Round(percentage);
}
}
}
class NpcObject : CreatureObject
{
protected const uint SummonedByOffset = 0xE * 4,
AttackingGuidOffset = 0x0A38;
public NpcObject(uint BaseAddress)
: base(BaseAddress)
{ }
public string Name
{
get { return ObjectManager.WowReader.ReadString((IntPtr)(ObjectManager.WowReader.ReadUInt32((IntPtr)(ObjectManager.WowReader.ReadUInt32((IntPtr)(baseAddress + 0x9B0)) + 0x3C)))); }
}
public ulong AttackingGuid
{
get { return ObjectManager.WowReader.ReadULong((IntPtr)(BaseAddress + AttackingGuidOffset)); }
}
public ulong SummonedBy
{
get { return ObjectManager.WowReader.ReadULong((IntPtr)(DescriptorFields + SummonedByOffset)); }
}
public int GetAggroRadius(CreatureObject LocalPlayer)
{
// if they are the same level as us, the aggro radius is roughly 20 yards
int AggroRadius = 20;
// aggro radius varies with level difference at a rate of roughly 1 yard/level
if (LocalPlayer.Level > Level)
AggroRadius -= (int)BotControl.DifferenceBetween(LocalPlayer.Level, Level);
if (LocalPlayer.Level < Level)
AggroRadius += (int)BotControl.DifferenceBetween(LocalPlayer.Level, Level);
if (AggroRadius < 5)
AggroRadius = 5;
AggroRadius += 3; // give us a bit of leeway
return AggroRadius;
}
}
class PlayerObject : CreatureObject
{
protected const uint CurrentRageOffset = 0x19 * 4,
CurrentEnergyOffset = 0x1B * 4,
MaxEnergyOffset = 0x23 * 4;
public PlayerObject(uint BaseAddress)
: base(BaseAddress)
{ }
public int CurrentRage
{
get
{
int RageTemp = ObjectManager.WowReader.ReadInt32((IntPtr)(DescriptorFields + CurrentRageOffset));
return (int)(Math.Floor((double)(RageTemp / 10)));
}
}
public int MaxRage
{
get { return 100; }
}
public int CurrentEnergy
{
get { return ObjectManager.WowReader.ReadInt32((IntPtr)(DescriptorFields + CurrentEnergyOffset)); }
}
public int MaxEnergy
{
get { return ObjectManager.WowReader.ReadInt32((IntPtr)(DescriptorFields + MaxEnergyOffset)); }
}
}
class GameObject : WowObject
{
protected const uint gameObject_XPosition = 0x10 * 4,
gameObject_YPosition = 0x11 * 4,
gameObject_ZPosition = 0x12 * 4,
displayId = 0x8 * 4;
public GameObject(uint BaseAddress)
: base(BaseAddress)
{ }
public string Print()
{
return "Name: " + Name + " X: " + XPosition.ToString() + " Y: " + YPosition.ToString() + " Z: " + ZPosition.ToString();
}
public string Name
{
get
{
return ObjectManager.WowReader.ReadString((IntPtr)(ObjectManager.WowReader.ReadUInt32((IntPtr)(baseAddress + 0x1f4)) + 0x078));
}
}
public override float XPosition
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(DescriptorFields + gameObject_XPosition)); }
}
public override float YPosition
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(DescriptorFields + gameObject_YPosition)); }
}
public override float ZPosition
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(DescriptorFields + gameObject_ZPosition)); }
}
public int DisplayId
{
get { return ObjectManager.WowReader.ReadInt32((IntPtr)(DescriptorFields + displayId)); }
}
}
class DynamicObject : WowObject
{
protected const uint dynamicObject_XPosition = 0xB * 4,
dynamicObject_YPosition = 0xC * 4,
dynamicObject_ZPosition = 0xD * 4;
public DynamicObject(uint BaseAddress)
: base(BaseAddress)
{ }
public override float XPosition
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(DescriptorFields + dynamicObject_XPosition)); }
}
public override float YPosition
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(DescriptorFields + dynamicObject_YPosition)); }
}
public override float ZPosition
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(DescriptorFields + dynamicObject_ZPosition)); }
}
}
class CorpseObject : WowObject
{
protected const uint corpseObject_XPosition = 0xB * 4,
corpseObject_YPosition = 0xC * 4,
corpseObject_ZPosition = 0xD * 4;
public CorpseObject(uint BaseAddress)
: base(BaseAddress)
{ }
public override float XPosition
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(DescriptorFields + corpseObject_XPosition)); }
}
public override float YPosition
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(DescriptorFields + corpseObject_YPosition)); }
}
public override float ZPosition
{
get { return ObjectManager.WowReader.ReadFloat((IntPtr)(DescriptorFields + corpseObject_ZPosition)); }
}
}
Hopefully you can understand what is going on in the above code. Two things to note however. The first and simplest: in the GameObject, DynamicObject and CorpseObject classes, the X,Y,Z are retrieved in a different way. Therefore, we override the XYZ properties and retrieve them how they should be retrieved.
Secondly, if you were thinking about concept of object inheritance in realation to our OM for the first time, this would be an appropriate diagram to draw:
However, upon closer inspection of the descriptors, there are attributes that both NpcObject and PlayerObject both have, and they are obtained in exactly the same way (for example, health is obtained from UnitFields+HealthOffset). Why dont we make a base class for both of these two that incorporate this 'sameness'. Note: this class will never actually be used. An in game object can be a NpcObject, or a PlayerObject, which both inherit things from CreatureObject, however you can never be just a CreatureObject.
Here is our updated diagram:
Note that the code provided above already has these changes. Now if we have an NpcObject named John, we can write code like this:
Code:
MessageBox.Show("Johns health is: "+John.CurrentHealth.ToString());
And Johns health would automatically be read and returned to us.
Object Manager
Now that we have the objects that we will be using downpat, lets look again at what our OM needs to do:
- Our OM needs a way to 'load' and find the base address of the in-game Object Manager.
- Our OM needs to iterate through the in-game Object Manager and get two peices of information about each object - the base address, and the type. Using this information, we can create a new "WowObject" and store it.
- Our OM needs to be able to store all the objects (arrays of WowObjects anyone?)
Before we start doing some coding, we need to think about who will be using this class. Do we need more than one of these OM's? It depends. Are we going to be getting information from multiple copies of WoW? In most cases, we're only going to be worrying about one copy of WoW at a time. Instead of having to make new copies of our OM, lets make it static. Thefore, instead of using this code:
Code:
ObjectManager myObjectManager = new ObjectManager();
myObjectManager.DoSomething();
We're able to just do this:
Code:
ObjectManager.DoSomething();
If you look in the above code for the WowObjects, they are getting ObjectManager.WowReader to do something. What is WowReader? In this project, I decided the only memory reading I was going to need to do was to enumerate through the linked list of objects and pull data about them. So it seems reasonable then to include another object, a memory reading object inside of our Object Manager. I can't remember where I found this MemoryReader .dll, or whom the author is unfortunately.
Edit: Seem's I have been using alek900's (perhaps private) memory reading dll by accident. But you should be able to apply the concepts to any decent memory reading class.
So far, this is what our ObjectManager class looks like:
Code:
static class ObjectManager
{
public static Memory WowReader = new Memory();
}
Now, whenever we're doing any work with the in-game Object Manager, we need to find it's address. Granted, this isn't hard, but it is worth having a method for. Also, we need a place to store the base address of the in-game Object Manager, as well, and also have some meaningful constants that represent what addresses we're going to be working with.
Code:
static class ObjectManager
{
public static Memory WowReader = new Memory();
private const uint staticClientConnection = 0x011CB310, // client connection, same address every boot
objectManagerOffset = 0x28A4, // offset from the ClientConnection to the object manager
localGuidOffset = 0xC0, // offset from the object manager to the local guid
firstObjectOffset = 0xAC, // 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 private ulong localGuid; // the local guid.
public static void LoadAddresses()
{
WowReader.SetProcess("Wow", "read");
objectManagerBase = WowReader.ReadUInt32((IntPtr)(WowReader.ReadUInt32((IntPtr)(staticClientConnection)) + objectManagerOffset));
localGuid = WowReader.ReadUInt32((IntPtr)(objectManagerBase + localGuidOffset));
} }
I'll just quickly describe the method that we've added. It is the job of LoadAddresses to find out the base address of the object manager, and our local players guid. The first line lets our memory reading class know that we will be reading from WoW.exe.
But why did we get our local players Guid? Can't we simply access it through our WowObjects properties (SomeObject.Guid)? We most certainly could do that, and that would work to get the guid of any object. But how do we know which object is ourselves? Say we load a list of 10 objects into an imaginary array. The only details about these objects that we know are the base address and type.
Code:
WowObject[] ImaginaryArray = new WowObject[9];
ImaginaryArray.ImaginaryFill();
Now that we've put that information in there, we can get the guid of any object. But how do we know which object is ourselves, just by our guid? That is why we retrieve our players guid beforehand. Basically, by storing our local guid before hand, we have a way to single out the object that references our local player in-game.
No doubt that the object whose information we want to know most is the local player. Lets add in an easy way to access information about our local player. Just before LoadAddresses(), put this in:
Code:
public static PlayerObject LocalPlayer;
Soon, we will able to access information about the local player from any part of our code, eg: ObjectManager.LocalPlayer.CurrentHealth;
Now we need a way to store our objects. We're going to store all of our objects in different IDictionary's, one dictionary for each type of object we will be dealing with. We could have stored them all in one array of WowObjects, but then we would need to deal with complicated issues of downcasting.
But what is an IDictionary? An IDictionary is basically an easy to use array. When declaring the IDictionary, you give it two values: the key for accessing, and the value it will hold. The keys for an IDictionary need to be unique, so we're going to use our WowObjects Guid's, and the values are going to be the WowObjects themselves. Just under "public static PlayerObject LocalPlayer", add this:
Code:
public static IDictionary<ulong, PlayerObject> PlayerObjectList = new Dictionary<ulong, PlayerObject>();
public static IDictionary<ulong, NpcObject> NpcObjectList = new Dictionary<ulong, NpcObject>();
public static IDictionary<ulong, GameObject> GameObjectList = new Dictionary<ulong, GameObject>();
Of course, you can add an IDictionary for the missing objects yourself. Now pretend for a moment our IDictionary's were full of objects. We had the guid of a creature who was attacking us, and we wanted to find out its health. How would we do that? By the magic of IDictionarys!
Code:
ulong AttackingMonsterGuid = GetAttackingMonsterGuid();
int MonsterHealth = ObjectManager.NpcObjectList[AttackingMonsterGuid].CurrentHealth;
This is all fine and dandy, but we don't have any data to work with at the moment, all we have are empty IDictionarys. We need a way to populate our lists, and we should be able to do this whenever we want. Lets add a new method to our OM - PopulateLists
Code:
public static void PopulateLists()
{
}
To start off we want to clear out all of our current lists - if we don't, we're going to end up with multiple copies of each object.
Code:
PlayerObjectList.Clear();
NpcObjectList.Clear();
GameObjectList.Clear();
To iterate through the object list, we're going to need somewhere to store the data we need (base address and type) before we actually make a new object. Lets make a generic WowObject to handle this. We also know the base address of the first object in the in-game Object Manager, so thats where we will start reading from.
Code:
WowObject CurrentObject = new WowObject(WowReader.ReadUInt32((IntPtr)(objectManagerBase + firstObjectOffset)));
Now we're going to loop through the in-game Object Manager from first object to last, so lets open a while loop. It should look something like this: while the object we're reading from is a valid object, read the base address and the type of the object, then add a new object to the list that corrosponds to the obejects type, giving it a base address. Doesn't seem too hard. Thus I give you:
Code:
public static void PopulateLists()
{
PlayerObjectList.Clear();
NpcObjectList.Clear();
GameObjectList.Clear();
WowObject CurrentObject = new WowObject(WowReader.ReadUInt32((IntPtr)(objectManagerBase + firstObjectOffset)));
while (CurrentObject.BaseAddress != 0 && CurrentObject.BaseAddress % 2 == 0)
{
if (CurrentObject.Type == 3) // a npc
NpcObjectList.Add(CurrentObject.Guid, new NpcObject(CurrentObject.BaseAddress);
if (CurrentObject.Type == 4) // a player
PlayerObjectList.Add(CurrentObject.Guid, new PlayerObject(CurrentObject.BaseAddress);
if (CurrentObject.Type == 5) // a gameobject
GameObjectList.Add(CurrentObject.Guid, new GameObject(CurrentObject.BaseAddress);
CurrentObject.BaseAddress = WowReader.ReadUInt32((IntPtr)(CurrentObject.BaseAddress + nextObjectOffset));
}
}
What the last line does is it changes the base address of CurrentObject to the base address of the next object in the list (effectively setting CurrentObject to equal the next object).
A final thing...
The most commonly accessed object in our OM is going to be the local player (to see our current health, our position, etc). We can now access the local player from any of our code doing this (assuming you have called PopulateLists() beforehand:
Code:
int OurHealth = ObjectManager.PlayerObjectList[ObjectManager.localGuid].CurrentHealth;
However, lets add a a little extra goody to make our code easier to read, and easier to type. In our ObjectManager class, add this line up the top somewhere near the declarations:
Code:
public static PlayerObject LocalPlayer;
Change this line in PopulateLists():
Code:
if (CurrentObject.Type == 4) // a player
PlayerObjectList.Add(CurrentObject.Guid, new PlayerObject(CurrentObject.BaseAddress);
to this:
Code:
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];
}
And you should be good to go! Happy hacking (or memory reading, enumerating, being nerdy, etc.) :wave: