I havn't seen this anywhere on this forum, which is kinda odd, since it
should have been obvious to any programmer what is happening inside
the client.
At the end of the post i'll provide some C# code to access the 3 lists.
WorldObjects is my name for the well known player/npc/item lists.
First a little education.
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 (well, d'uh).
The first thing we need to calculate the index of an object, is the hash
seed for that particular list. Easy (pseudo-code):
ListOffset is the offset for the specific list you wish to search in,
currently (Player = 0x20), (Mob = 0x24), (Item = 0x28).
Alright, now for the calculation:
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:
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.
Usage:
You need to cast to the correct WorldObject type to access the
extended members like so:
or do a direct type check on the returned object:
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 :D
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. I might post my inject function later if
anyone feels a need for it.
Feel free to alter, use and abuse the code in any project you want to.
Feel free to ignore this entire post.
Cheers.
should have been obvious to any programmer what is happening inside
the client.
At the end of the post i'll provide some C# code to access the 3 lists.
WorldObjects is my name for the well known player/npc/item lists.
First a little education.
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 (well, d'uh).
Code:
mov esi, [esp+dwId] mov eax, esi xor edx, edx div dword ptr [ecx+24h] mov eax, [ecx+18h] mov edx, [eax+edx*4]
seed for that particular list. Easy (pseudo-code):
Code:
seed = [[[[BaseAddress + 0x1C]+ 0x8] + ListOffset] + 0x24] arrayStart = [[[[BaseAddress + 0x1C]+ 0x8] + ListOffset] + 0x18]
currently (Player = 0x20), (Mob = 0x24), (Item = 0x28).
Alright, now for the calculation:
Code:
index = arrayStart + (objectId % seed) * 4
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 |
Code:
objectBase = [index + 0x4] objectId = [index + 0x8]
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.
Code:
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)
{
}
}
Code:
WorldObject wo = WorldObject.GetObjectById(TargetId);
extended members like so:
Code:
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;
Code:
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;
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 :D
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. I might post my inject function later if
anyone feels a need for it.
Feel free to alter, use and abuse the code in any project you want to.
Feel free to ignore this entire post.
Cheers.