Results 1 to 1 of 1
  1. #1
    Dwar
    Dwar is offline
    Veteran Dwar's Avatar
    Join Date
    2010 Mar
    Posts
    2,222
    Thanks Thanks Given 
    211
    Thanks Thanks Received 
    2,230
    Thanked in
    292 Posts
    Rep Power
    10

    Gameobject lists without looping

    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
    Please, post your questions on forum, not by PM or mail

    I spend my time, so please pay a little bit of your time to keep world in equilibrium

  2. The Following User Says Thank You to Dwar For This Useful Post:


Similar Threads

  1. [Guide] Dragon Nest Enhancement Crest Lists
    By Grooguz in forum Dragon Nest Guides, Tutorials
    Replies: 0
    Last Post: 2011-10-12, 06:45 AM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •