The WorldObject lists inside elementclient are actually hashtables, which is a special array, where you can turn a key into an index via a hashing function.
This basically means that as long as you add key/value pairs with unique keys, lookups into the table wil be O(1). That just means that the time needed to find a specific element in the table is independent of the number of elements in the table. Very smart.
Looping through a hashtable to find an element with a known key is.. well, not smart.
Right... moving on..
To find an object with a known key inside the client, one would need the hashing function and the hash seed. Luckily, those are both provided in the code of the client.
mov esi, [esp+dwId]
mov eax, esi
xor edx, edx
div dword ptr [ecx+24h]
mov eax, [ecx+18h]
mov edx, [eax+edx*4]
The first thing we need to calculate the index of an object, is the hash seed for that particular list. Easy (pseudo-code)]seed = [[[[BaseAddress + 0x1C]+ 0x8] + ListOffset] + 0x24]
arrayStart = [[[[BaseAddress + 0x1C]+ 0x8] + ListOffset] + 0x18]
ListOffset is the offset for the specific list you wish to search in,
currently (Player = 0x20), (Mob = 0x24), (Item = 0x28).[/syntax]
Alright, now for the calculation:
index = arrayStart + (objectId % seed) * 4
The % (modulus) simply divides x by y and returns the remainder. The 'index' variable now contains a pointer to the list element that holds information on the wanted object. List elements look like this:
dw 0
dw *worldObject
dw objectID
objectBase = [index + 0x4]
objectId = [index + 0x8]
There you go, from objectId to objectBase without any looping.
Some code taken out of context to access the lists:
The code won't compile unless you write your own methods to read process memory. This is simple, and covered in a multitude of tutorials around the net, this forum included.
public enum WorldObjectType
{
Player = 0x20,
Monster = 0x24,
Item = 0x28,
}
public abstract class WorldObject
{
public uint Id { get; private set; }
protected uint BaseAddress { get; private set; }
protected IntPtr hProcess;
public WorldObject(IntPtr hProcess, uint id, uint baseAddress)
{
Id = id;
BaseAddress = baseAddress;
this.hProcess = hProcess;
}
public static WorldObjectType ResolveId(uint id)
{
WorldObjectType type;
if ((id & 0x80000000) == 0x80000000)
{
type = WorldObjectType.Monster;
}
else if ((id & 0x40000000) == 0x40000000)
{
type = WorldObjectType.Item;
}
else type = WorldObjectType.Player;
return type;
}
public static WorldObject GetObjectById(IntPtr hProcess, uint baseAddress, uint id)
{
WorldObjectType woType = ResolveId(id);
WorldObject output = null;
// standard stuff
// baseAddress is the so called real base, whatever you wanna call it, as of 15. nov its 0xA5B90C in PWI
uint pointer = Memory.GetUintAt(hProcess, baseAddress) + 0x1C;
// WorldObjects class, contains the 3 lists for world objects
pointer = Memory.GetUintAt(hProcess, pointer) + 0x8;
// WorldObjectType enum resolves to list offsets, smart huh? right... moving on.
pointer = Memory.GetUintAt(hProcess, pointer) + (uint)woType;
// hash seed pointer for the selected list
uint hash = Memory.GetUintAt(hProcess, pointer) + 0x24;
// hash seed
hash = Memory.GetUintAt(hProcess, hash);
// sequential list of pointers to listEntry objects
pointer = Memory.GetUintAt(hProcess, pointer) + 0x18;
// now, pointer contains the base of the array
pointer = Memory.GetUintAt(hProcess, pointer);
// hash function, this converts a given id to an index into the correct list.
uint index = pointer + (id % hash) * 4;
// base of this particular list entry
uint listEntry = Memory.GetUintAt(hProcess, index);
// pointer to the object desired
uint objectBase = Memory.GetUintAt(hProcess, listEntry + 0x4);
// the id of an object is stored directly inside the list entry struct
uint idImm = Memory.GetUintAt(hProcess, listEntry + 0x8);
// instantiate a WorldObject object, according to type
if (woType == WorldObjectType.Monster)
output = new Monster(hProcess, idImm, objectBase);
else if (woType == WorldObjectType.Player)
output = new Player(hProcess, idImm, objectBase);
else output = new Item(hProcess, idImm, objectBase);
return output;
}
}
public class Monster : WorldObject
{
private const uint HpOffset = 0x12C;
private const uint MaxHpOffset = 0x16C;
public uint Hp { get { return Memory.GetUintAt(hProcess, base.BaseAddress + HpOffset); } }
public uint MaxHp { get { return Memory.GetUintAt(hProcess, base.BaseAddress + MaxHpOffset); } }
public Monster(IntPtr hProcess, uint id, uint baseAddress)
: base(hProcess, id, baseAddress)
{
}
}
public class Player : WorldObject
{
private const uint HpOffset = 0x474;
private const uint MaxHpOffset = 0x4B4;
private const uint MpOffset = 0x478;
private const uint MaxMpOffset = 0x4B8;
public uint Hp { get { return Memory.GetUintAt(hProcess, base.BaseAddress + HpOffset); } }
public uint MaxHp { get { return Memory.GetUintAt(hProcess, base.BaseAddress + MaxHpOffset); } }
public uint Mp { get { return Memory.GetUintAt(hProcess, base.BaseAddress + MpOffset); } }
public uint MaxMp { get { return Memory.GetUintAt(hProcess, base.BaseAddress + MaxMpOffset); } }
public Player(IntPtr hProcess, uint id, uint baseAddress)
: base(hProcess, id, baseAddress)
{
}
}
public class Item : WorldObject
{
public Item(IntPtr hProcess, uint id, uint baseAddress)
: base(hProcess, id, baseAddress)
{
}
}
Usage:
WorldObject wo = WorldObject.GetObjectById(TargetId);
You need to cast to the correct WorldObject type to access the extended members like so]Monster m;
Player p;
Item i;
if (WorldObject.ResolveId(wo.Id)== WorldObjectType.Monster)
m = wo as Monster;
else if (WorldObject.ResolveId(wo.Id)== WorldObjectType.Player)
p = wo as Player;
else i = wo as Item;[/syntax]
or do a direct type check on the returned object] Monster m;
Player p;
Item i;
if (wo is Monster)
m = wo as Monster;
else if (wo is Player)
p = wo as Player;
else i = wo as Item;[/syntax]
The WorldObject class and it's subclasses are stubs i wrote in a hurry, go nuts with CE if you feel like expanding them. Do notice that the offsets for other player's info are the same as the local player
The code has been tested to work with player list and monster list, item list is untested, but there is no reason it should not work.
Do also notice that, fetching stuff from lists with this code is intensive, there are alot of calls to ReadProcessMemory. A smarter and quicker way would be to inject a function into elementclient.exe that writes the objectBase to a shared memory (gotten with VirtualAllocEx), and check there once the function returns.
with respect to author: zenvoid