[WoW][3.2.0] Better Object Managment menu

User Tag List

Page 1 of 3 123 LastLast
Results 1 to 15 of 44
  1. #1
    Apoc's Avatar Angry Penguin
    Reputation
    1388
    Join Date
    Jan 2008
    Posts
    2,750
    Thanks G/R
    0/13
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    [WoW][3.2.0] Better Object Managment

    First things first.

    1. Yes, this is injection.
    2. No, it isn't currently detected.
    3. No, I don't plan to give some sort of way to do it out of process.
    4. No, I won't hold your hand, or answer stupid questions.
    5. Yes, it's C#. Get over it.


    Now then, we've got the stupid questions dealt with. We can move on to the good stuff.

    Alot of people seem to think that enumerating the object list is the best approach to keeping track of current objects in WoW. While this is a great way to do it, you need to keep track of quite a few things.

    1. g_clientConnection
    2. s_curMgr
    3. nextObj
    4. curObj
    5. localGuid
    6. etc


    I prefer to keep track of only 3 things.
    1. EnumVisibleObjects
    2. ClntObjMgrGetObjectPtr (GetObjectByGuid)
    3. ClntObjMgrGetActivePlayer


    A whole 3 functions is all I ever need to update in order to get my object manager working for a new patch. Since PatchDiff2 can handle the work of finding these functions for me, it makes life that much easier between patches.

    Now for the rundown of the functions... shall we?

    EnumVisibleObjects

    Signature:
    Code:
    signed int __cdecl EnumVisibleObjects(int (__cdecl *pCallback)(WGUID, DWORD), eWOWOBJECTTYPE filter)
    C# Signature:
    Code:
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            private delegate int EnumVisibleObjectsDelegate(IntPtr callback, int filter);
    Use: This function allows you to enumerate over WoW's list of objects, and also base that enumeration around a filter. (Object type) Using the callback passed in, it will allow you to retrieve the GUID, and type of each object. (Note: Inheritance does work here! Object = 0, so passing 0 will retrieve every object possible.)

    Params: A callback to be used to let WoW know when to stop, or continue to call your callback with objects.
    A filter of WoWObjectType. Basically; to narrow down the 'search' to specific types of objects.

    EnumVisibleObjectsCallback

    Signature:
    Code:
    int __cdecl EnumVisibleObjectsCallback(WGUID guid, eWOWOBJECTTYPE filter)
    C# Signature:
    Code:
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            private delegate int EnumObjectsCallback(ulong guid, int filter);
    Use: Acts as the callback from WoW. This function will be called for each object during the enumeration. (Think EnumWindows API) A GUID will be passed back that you can then use in the ClntObjMgrGetObjectPtr function to get the actual pointer.
    Params: A GUID to use, and a filter for the object type. Nothing really special here.
    Notes: Returning 0 will stop the enumeration. Returning 1 will continue to the next object. If no objects are left to enumerate, the function will no longer be called.

    ClntObjMgrGetObjectPtr

    Signature:
    Code:
    WoWObject *__cdecl ClntObjMgrObjectPtr(WGUID guid, eWOWOBJECTTYPE filter)
    C# Signature:
    Code:
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            private delegate IntPtr ClntObjMgrGetObjectPtrDelegate(ulong guid, int filter);
    Use: The typical GetObjectByGuid function. Returns a pointer to an object based on GUID. NULL if no object exists.
    Params: The GUID of the object, and a filter, if any.
    Notes: This function works from the Object descriptor GUID. (OBJECT_FIELD_GUID) Not the typical obj+0x30 GUID. (They can be, and usually are different.)

    ClntObjMgrGetActivePlayer

    Signature:
    Code:
    WGUID __cdecl ClntObjMgrGetActivePlayer()
    C# Signature:
    Code:
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            private delegate ulong ClntObjMgrGetActivePlayer();
    Use: Retrieves the GUID of the active player (ourselves) which can then be passed to the ClntObjMgrGetObjectPtr function.
    Params: None.


    Putting it all together...

    We need a small building block to work from. The basic WoWObject class, and it's descendants.

    Code:
        public class WoWObject
        {
            protected IntPtr ObjPtr;
    
            internal WoWObject(IntPtr objPtr)
            {
                ObjPtr = objPtr;
            }
    
            public ulong Guid { get { return 0; /*Add your code here to grab the OBJECT_FIELD_GUID descriptor*/ } }
    
            public bool IsValid { get { return ObjPtr != IntPtr.Zero; } }
    
            internal void UpdateObjectPointer(IntPtr ptr)
            {
                ObjPtr = ptr;
            }
    
            public static implicit operator IntPtr(WoWObject obj)
            {
                // Allows us to implicitly cast a WoWObject to an IntPtr (objBase)
                return obj.ObjPtr;
            }
        }
    (I'll leave creating the descendant classes to you. WoWUnit, WoWItem, etc)

    Firstly; it is ALWAYS better to manage a list of objects in your project, rather than rely on WoW's. We will use WoW's to grab the objects only. All the other management of the objects, will be done internally in our project.

    Since the best way to keep a correlation of non-sequential things with a key/value is obviously a Dictionary, we'll use that. (It uses a HashTable-esque implementation internally, so it's very fast to use.) This part is very simple. Assuming you already have your WoWObject class (and descendants), we can dive right in.

    Code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.InteropServices;
    
    namespace ObjMgrDemo
    {
        public class ObjectManager
        {
            private static readonly EnumObjectsCallback Callback = EnumObjectsCallbackHandler;
    
            private static readonly IntPtr CallbackPtr;
            private static readonly ClntObjMgrGetObjectPtrDelegate ClntObjMgrGetObjectPtrWoW;
            private static readonly EnumVisibleObjectsDelegate EnumObjs;
            private static readonly ClntObjMgrGetActivePlayerDelegate GetActivePlayerDelegate;
            private static readonly Dictionary<ulong, WoWObject> RealObjects = new Dictionary<ulong, WoWObject>();
            private static ulong _frameCounter = 1;
    
            static ObjectManager()
            {
                Me = new WoWActivePlayer(IntPtr.Zero);
                CallbackPtr = Marshal.GetFunctionPointerForDelegate(Callback);
                GetActivePlayerDelegate =
                    Utilities.RegisterDelegate<ClntObjMgrGetActivePlayerDelegate>((uint) 0x00476A90);
                EnumObjs = Utilities.RegisterDelegate<EnumVisibleObjectsDelegate>((uint) 0x004780A0);
                ClntObjMgrGetObjectPtrWoW =
                    Utilities.RegisterDelegate<ClntObjMgrGetObjectPtrDelegate>((uint) 0x00478320);
            }
    
            internal static WoWActivePlayer Me { get; private set; }
            public static List<WoWObject> ObjectList { get { return RealObjects.Values.Where(o => o.IsValid).ToList(); } }
    
            public static void Pulse()
            {
    
                _frameCounter++;
            }
    
            private static int EnumObjectsCallbackHandler(ulong guid, int filter)
            {
    
                return 1;
            }
    
            #region Nested type: ClntObjMgrGetActivePlayerDelegate
    
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            private delegate ulong ClntObjMgrGetActivePlayerDelegate();
    
            #endregion
    
            #region Nested type: ClntObjMgrGetObjectPtrDelegate
    
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            private delegate IntPtr ClntObjMgrGetObjectPtrDelegate(ulong guid, int filter);
    
            #endregion
    
            #region Nested type: EnumObjectsCallback
    
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            private delegate int EnumObjectsCallback(ulong guid, int filter);
    
            #endregion
    
            #region Nested type: EnumVisibleObjectsDelegate
    
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            private delegate int EnumVisibleObjectsDelegate(IntPtr callback, int filter);
    
            #endregion
        }
    }
    As you can see, I filled in a good bit of the required stuff. This is mostly boilerplate code. I'm keeping a static reference to 'Me', as we should never 'not be there'. We'll simply update a single instance of that object. The frame counter will be explained later, as we'll need it for some optimizations. You'll also notice how I have the RealObjects dictionary, and then the public ObjectList... List. This is so we're not handing off a Dictionary of values to other areas. Instead, we simply return only valid objects, so you can easily enumerate over it. (It makes it feel more natural, I swear!)

    Pulse should be called on every frame (Yes, it's perfectly safe, and viable to do this, WoW does it many times over.) We'll be using the FrameCounter to do some 'trickery' to keep resources to a minimum.

    Now, we just need to wrap the typical GetObjectByGuid function. Note though, we'll actually be wrapping TWO functions. Our internal one, and then WoW's. I'll just hand the source over here, so it's easier to understand.

    Code:
            /// <summary>
            /// Uses our current internal list of objects to retrieve a WoWObject. This is much faster
            /// if the object is known to be in the current object list. USE THIS FUNCTION!
            /// </summary>
            /// <param name="guid"></param>
            /// <returns></returns>
            internal static WoWObject GetObjectByGuid(ulong guid)
            {
                WoWObject ret;
                RealObjects.TryGetValue(guid, out ret);
    
                return ret ?? new WoWObject(IntPtr.Zero);
            }
    
            /// <summary>
            /// Calls WoW's internal ClntObjPtr (or whatever it's called) to retrieve an object pointer
            /// based on the specified GUID.
            /// </summary>
            /// <param name="guid"></param>
            /// <returns></returns>
            internal static IntPtr InternalGetObjectByGuid(ulong guid)
            {
                return ClntObjMgrGetObjectPtrWoW(guid, -1);
            }
    Notice how our 'public' (which isn't actually public) GetObjectByGuid function only checks the dictionary. If no valid GUIDs are present, it returns an INVALID WoWObject. This is very important!

    Also note, we're passing -1 to WoW's ClntObjMgrGetObjectPtr function. This just tells WoW we want to search every type of object. (Feel free to overload this)

    Now we have the basics for getting an object via GUID, we can get into more complicated things. Firstly, we need a way to invalidate the current object list, without 'nulling' it out. (Think what happens when you keep a reference to an object, and all the sudden, it no longer exists, but the reference says otherwise.)

    Code:
            public static void Pulse()
            {
                // Reset all the pointers.
                // This is faster than instantiating the class hundreds of times...
                foreach (WoWObject o in RealObjects.Values)
                {
                    o.UpdateObjectPointer(IntPtr.Zero);
                }
    
                _frameCounter++;
            }
    Pretty simple. Since our IsValid check in WoWObject checks that ObjPtr != IntPtr.Zero, this 'invalidates' every object.

    Next, we'll tell WoW to start enumerating. This part is fairly simple, so stay with me.

    Code:
            public static void Pulse()
            {
                // Reset all the pointers.
                // This is faster than instantiating the class hundreds of times...
                foreach (WoWObject o in RealObjects.Values)
                {
                    o.UpdateObjectPointer(IntPtr.Zero);
                }
    
                // Enum all object types.
                EnumObjs(CallbackPtr, 0);
    
                _frameCounter++;
            }
    Our EnumObjectsCallbackHandler will now be called constantly until the entire object list is iterated. Pretty simple, yet very effective.

    We'll get back to the callback handler in a moment, we still need to do some quick optimizations. kynox and I had a conversation a while back about how to best handle removing invalid entries, and keeping track of those invalid entries. (As you can tell, our IsValid property handles most of it.) Now, we just need to physically remove those entries. Since we don't want to do it each frame, as that's quite intensive, we do it every 10th frame. (Still 3-6 times per second, which is plenty.)

    Code:
            public static void Pulse()
            {
                // Reset all the pointers.
                // This is faster than instantiating the class hundreds of times...
                foreach (WoWObject o in RealObjects.Values)
                {
                    o.UpdateObjectPointer(IntPtr.Zero);
                }
    
                // Enum all object types.
                EnumObjs(CallbackPtr, 0);
    
                // Every 10th frame, lets clear out the invalid entries in the object list
                // to conserve resources
                if (_frameCounter++ % 10 == 0)
                {
                    RemoveInvalidEntries();
                }
            }
    
            /// <summary>
            /// Clears out any invalid objects from our list.
            /// </summary>
            private static void RemoveInvalidEntries()
            {
                // Yes, this is pretty damned simple. Just clear out any invalid
                // objects. (IsValid is just ObjPtr != IntPtr.Zero)
                IEnumerable<KeyValuePair<ulong, WoWObject>> r = from o in RealObjects
                                                                where !o.Value.IsValid
                                                                select o;
    
                foreach (var pair in r)
                {
                    RealObjects.Remove(pair.Key);
                }
            }
    It's not the prettiest solution, but it works great for me.

    Now, we've got the list basically managing valid/invalid, and all that stuff. Now we can do the fun things.

    Code:
            private static int EnumObjectsCallbackHandler(ulong guid, int filter)
            {
                // Note: Filter is actually a WoWObjectType enum value.
                // If we decide to call EVO with a filter set; we will only receive objects of that type!
    
                // Get the objects pointer, and move forward!
                IntPtr objPtr = InternalGetObjectByGuid(guid);
    
                if (!RealObjects.ContainsKey(guid))
                {
                    var tmp = new WoWObject(objPtr);
    
                    switch (tmp.Type)
                    {
                        case WoWObjectType.Item:
                            tmp = new WoWItem(objPtr);
                            break;
                        case WoWObjectType.Container:
                            tmp = new WoWContainer(objPtr);
                            break;
                        case WoWObjectType.Unit:
                            tmp = new WoWUnit(objPtr);
                            break;
                        case WoWObjectType.Player:
                            // Skip ourselves
                            //if (tmp.Guid == Me.Guid)
                            //    return 1;
                            tmp = new WoWPlayer(objPtr);
                            break;
                        case WoWObjectType.GameObject:
                            tmp = new WoWGameObject(objPtr);
                            break;
                        case WoWObjectType.Corpse:
                            tmp = new WoWCorpse(objPtr);
                            break;
                        default:
                            // Maybe in the future we'll support the rest of the types
                            // for now however, we'll just skip over them completely.
                            return 1;
                    }
    
                    RealObjects.Add(guid, tmp);
                }
                else if (RealObjects[guid] != objPtr)
                {
                    RealObjects[guid].UpdateObjectPointer(objPtr);
                }
                return 1;
            }
    That's pretty much it. The RealObjects dictionary is filled with the type-specific objects. Which can then be used however you wish.

    So with this, call Pulse each frame -> Do whatever you want with your current list of objects.

    Enjoy folks.

    Credits:

    kynox - Being awesome, and helping debug some stupid shit.
    jjaa - Being awesome, and doing the updates for me. (I'm lazy, shoot me!)
    Cypher - Jew.
    Somebody else I forgot - Tough shit.
    2dgreengiant - cos hes fking hot

    Disclaimer:

    This is written entirely by me, with some help from the above credited people. This is NOT to be used in commercial products for any reason whatsoever.

    JuJu is herein, BANNED from any of said code. Period.
    Jay (AkA KRYPTON) is also BANNED from any of said code. Period. (You little twat)

    All others; please ask before ripping this code. All I ask for is a simple 'thanks' somewhere.
    Last edited by KuRIoS; 08-22-2012 at 08:45 AM.

    [WoW][3.2.0] Better Object Managment
  2. #2
    2dgreengiant's Avatar ★ Elder ★


    Reputation
    1192
    Join Date
    Feb 2007
    Posts
    7,129
    Thanks G/R
    1/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    thansk fgt.

    this all my code btw as i am pro at c#
    If you need me you have my skype, if you don't have my skype then you don't need me.

  3. #3
    Kryso's Avatar Active Member
    Reputation
    40
    Join Date
    Jul 2009
    Posts
    97
    Thanks G/R
    0/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This look pretty nice. I see you are working with clr inside process; are you disabling warden or hiding somehow everything? Loading clr makes kinda mess. Or warden is just that blind?

  4. #4
    DragonWaxter's Avatar Active Member
    Reputation
    51
    Join Date
    Aug 2006
    Posts
    348
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I was going to ask the same thing but its really intersting how you proced with your steps.
    I am going to use this for sure.
    WZ7K4HW9XZW9

  5. #5
    Apoc's Avatar Angry Penguin
    Reputation
    1388
    Join Date
    Jan 2008
    Posts
    2,750
    Thanks G/R
    0/13
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm not hiding anything.

    Blizzard isn't stupid enough to detect the CLR as a 'hack'. Many legit applications load the CLR into other processes. This is no different.

  6. #6
    Robske's Avatar Contributor
    Reputation
    305
    Join Date
    May 2007
    Posts
    1,062
    Thanks G/R
    3/4
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Very interesting read, thanks for sharing

    Originally Posted by Apoc View Post
    JuJu is herein, BANNED from any of said code. Period.
    Jay (AkA KRYPTON) is also BANNED from any of said code. Period. (You little twat)
    What about that moneybags guy?
    "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

  7. #7
    Cypher's Avatar Kynox's Sister's Pimp
    Reputation
    1358
    Join Date
    Apr 2006
    Posts
    5,368
    Thanks G/R
    0/6
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    EnumVisibleObjects is for terrorists.

  8. #8
    vulcanaoc's Avatar Member
    Reputation
    31
    Join Date
    Jul 2008
    Posts
    125
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Robske View Post
    Very interesting read, thanks for sharing



    What about that moneybags guy?
    HEY, woah, whoa, woaaaahhh.. I'm out of process. lawl.

  9. #9
    adaephon's Avatar Active Member
    Reputation
    76
    Join Date
    May 2009
    Posts
    167
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    By using
    if (_frameCounter++ % 10 == 0) { ... }
    _frameCounter++;

    aren't you counting each 'Pulse' as two frames?

  10. #10
    Apoc's Avatar Angry Penguin
    Reputation
    1388
    Join Date
    Jan 2008
    Posts
    2,750
    Thanks G/R
    0/13
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by adaephon View Post
    By using
    if (_frameCounter++ % 10 == 0) { ... }
    _frameCounter++;

    aren't you counting each 'Pulse' as two frames?
    Good catch. Was kinda pulling bits and pieces out of our own implementation. Didn't realize I left that in there.

  11. #11
    adaephon's Avatar Active Member
    Reputation
    76
    Join Date
    May 2009
    Posts
    167
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Figured it was just that, as I'm sure your actual Pulse code does a little more than just what you have here

  12. #12
    Apoc's Avatar Angry Penguin
    Reputation
    1388
    Join Date
    Jan 2008
    Posts
    2,750
    Thanks G/R
    0/13
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Not really.

    Code:
            /// <summary>
            /// Called each frame. This 'pulses' our object manager.
            /// </summary>
            internal static void Pulse()
            {
                // Reset all the pointers.
                // This is faster than instantiating the class hundreds of times...
                // Make sure this is called BEFORE RemoveInvalidEntries!!!
                ClearPtrs();
    
                // Yep, that's it. Populate the current object list; and/or update pointers.
                // Note: Using 0 as the filter, as we want everything!
                // Can use more specific filters if needed.
                EnumObjs(CallbackPtr, 0);
    
                // Every 10th frame, lets clear out the invalid entries in the object list
                // to conserve resources
                if (_frameCounter++ % 10 == 0)
                {
                    RemoveInvalidEntries();
                }
    
                // Make sure 'Me' is correct.
                Me.UpdatePointer(InternalGetObjectByGuid(GetActivePlayer()));
            }

  13. #13
    LegacyAX's Avatar Active Member
    Reputation
    21
    Join Date
    Apr 2009
    Posts
    193
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I suggest not taking wow so seriously mate, it must be a strain on your personal life. That is if there is one.

    @apoc , clean code. +Rep for posting.

  14. #14
    coffeemug's Avatar Member
    Reputation
    1
    Join Date
    Jun 2009
    Posts
    4
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    thanks for sharing, will try this when I have the time

  15. #15
    barthen's Avatar Contributor Authenticator enabled
    Reputation
    94
    Join Date
    Apr 2007
    Posts
    112
    Thanks G/R
    4/4
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    thanks for sharing, nice read. If you keep sharing your stuff like that we'll be able to copypasta your private bot ;-P

Page 1 of 3 123 LastLast

Similar Threads

  1. [Source] WPF Wow Object manager
    By !@^^@! in forum WoW Memory Editing
    Replies: 11
    Last Post: 01-26-2010, 04:13 PM
  2. Object Manager
    By Shamun in forum WoW Memory Editing
    Replies: 11
    Last Post: 11-28-2008, 02:06 PM
  3. WoW Object Manager ?
    By discorly in forum WoW ME Questions and Requests
    Replies: 4
    Last Post: 07-28-2007, 06:34 PM
All times are GMT -5. The time now is 08:27 AM. Powered by vBulletin® Version 4.2.3
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Google Authenticator verification provided by Two-Factor Authentication (Free) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search