Caching Client Values For Multiple Reads Within A Short Period menu

Shout-Out

User Tag List

Results 1 to 9 of 9
  1. #1
    EmilyStrange's Avatar Active Member
    Reputation
    34
    Join Date
    Jul 2009
    Posts
    125
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Caching Client Values For Multiple Reads Within A Short Period

    I created this code to be able to cache certain values within the game client with my out-of-process multi-boxing project. I found that my AI was doing an awful lot of the same reads to determine which of the characters were mounted, which ones were casting spells, all within a very narrow time period. Because of these many reads, the frame rate could get quite horrid at times, and the CPU usage of my multi-boxing application would spike, not very high, but high enough that I wanted to mitigate any impact it might have in the future.

    Not all values within the game client need to be cached, I just do the most frequently accessed ones such as spell cool downs, auras, spell casting, is mounted, is sitting, etc. These cached values are checked many times per update loop by the AI, usually within a very short period of time.

    Most people are not going to find a use for this code, but if you are having issues with your out-of-process bot doing too many reads at one time and slowing everything down, or if you want to multi-box more than five characters on a single workstation and remain out-of-process then this code can help with the amount of game client memory reads.

    Code:
    using System;
    
    namespace WarcraftVoodoo
    {
        /// <summary>
        /// This delegate writes a value immediately to the game client and updates the
        /// last time that the value was refreshed.
        /// </summary>
        /// <typeparam name="T">The defined type that the memory writer delegate takes.</typeparam>
        /// <param name="value">The value to write to the game client memory.</param>
        public delegate void CachedWriterCallback<T>(T value);
    
        /// <summary>
        /// This delegate either reads a value from the game client or the cache
        /// depending on whether the last read occured within a defined time period.
        /// </summary>
        /// <typeparam name="T">The defined type that the memory reader delegate returns.</typeparam>
        /// <returns>The value read by the reader function either from the game client memory or the cached version.</returns>
        public delegate T CachedReaderCallback<T>();
    
        /// <summary>
        /// This structure maintains a cached value for the game client.
        /// Its purpose is to reduce the amount of reading of the game client memory
        /// that needs to take place on a moment to moment basis, especially if the
        /// same memory location is checked multiple times within the AI framework.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public struct CachedValue<T>
        {
            // The default interval of time that must elapse before the cached value is refreshed.
            public static readonly TimeSpan DefaultUpdateInterval = TimeSpan.FromMilliseconds(50);
    
            /// <summary>
            /// The update interval of time that must elapse before the cached value is refreshed.
            /// </summary>
            public TimeSpan UpdateInterval;
            
            /// <summary>
            /// The delegate that handles reading of the client memory.
            /// </summary>
            public CachedReaderCallback<T> m_reader;
    
            /// <summary>
            /// The delegate that handles writing of the client memory.
            /// </summary>
            public CachedWriterCallback<T> m_writer;
            
            // The value that was last read from the game client and is currently being cached.
            private T m_value;
            
            /// The last time the cached value was refreshed
            private DateTime m_lastUpdate;
    
            /// <summary>
            /// Constructor that takes a reader delegate.
            /// </summary>
            /// <param name="reader">The delegate that performs reading of the value from the game client.</param>
            public CachedValue(CachedReaderCallback<T> reader)
                : this()
            {
                UpdateInterval = DefaultUpdateInterval;
                m_lastUpdate = DateTime.MinValue;
                m_reader = reader;
                m_writer = null;
            }
    
            /// <summary>
            /// Constructor that takes both a reader and a writer delegate.
            /// </summary>
            /// <param name="reader">The delegate that performs reading of the value from the game client.</param>
            /// <param name="writer">The delegate that performs writing of the value to the game client.</param>
            public CachedValue(CachedReaderCallback<T> reader, CachedWriterCallback<T> writer)
                : this()
            {
                UpdateInterval = DefaultUpdateInterval;
                m_lastUpdate = DateTime.MinValue;
                m_reader = reader;
                m_writer = writer;
            }
    
            /// <summary>
            /// Get/set the cached value in the game client.
            /// Set will immediately write the value to the game client. Get will read
            /// from the cached value if the amount of time elapsed since the last read
            /// is within the update interval.
            /// </summary>
            public T Value
            {
                set
                {
                    // If no writer delegate has been set, throw an error
                    // otherwise, invoke the writer delegate, update the locally cached
                    // value and update when the last time the cached value was refreshed
                    if (m_writer == null)
                    {
                        throw new InvalidOperationException("Delegate for writing operation has not been set.");
                    }
    
                    m_writer(value);
                    m_value = value;
                    m_lastUpdate = DateTime.Now;
                }
    
                get
                {
                    // if no reader delegate has been set, throw an error
                    // otherwise, check the last cache refresh time
                    // if the last cache refresh time is within the update interval
                    // return the cached value
                    // otherwise, invoke the reader delegate and update the last cached time.
                    if (m_reader == null)
                    {
                        throw new InvalidOperationException("Delegate for reading operation has not been set.");
                    }
    
                    if ((DateTime.Now - m_lastUpdate) < UpdateInterval)
                    {
                        return m_value;
                    }
    
                    m_value = m_reader();
                    m_lastUpdate = DateTime.Now;
    
                    return m_value;
                }
    
            }
    
        }
    
    }
    This is how I use the above code to read values from the game client:
    Code:
      // declare a member variable that needs to be cached
      private CachedValue<bool> m_isMounted;
    Code:
      // set up the reader delegate for the cached value
      m_isMounted = new CachedValue<bool>(() =>
      {
        if (ReadOffset<int>(UnitOffset.IsMounted1) <= 0)
        {
          return false;
        }
    
        if ((ReadOffset<int>(UnitOffset.IsMounted2) & 0x10000000) != 0)
        {
          return false;
        }
    
        return true;
      });
    Make use of the cached value in my code:
    Code:
      return m_isMounted.Value;
    Another example using a more complex type:
    Code:
                m_actionButtons = new CachedValue<ActionButton[]>(() =>
                {
                    return GetAllActionButtons();
                });
    where ActionButton is a structure that contains information about the buttons found on the action bar and the function GetAllActionButtons() returns an array of ActionButton structures.

    Caching Client Values For Multiple Reads Within A Short Period
  2. #2
    erix920's Avatar Private
    Reputation
    4
    Join Date
    Mar 2011
    Posts
    11
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm doing this "multi-box" with my system and wondering if this method would be used for each instance running or for all instances running.

    An example single instance: is that I have each instance of the bot with a separate thread that updates common items such as health, x y z r etc. When the thread completes the loop I just have it sleep for a period of time based on the CPUs load and operating frequency. However, this loop only runs when managing one instance at time so the loop is never running more than one copy.

    On the instance form however, I have to constantly read saved values from the hard file to update each instance.

    So with this system, can I cache values from each instance into one shared cache of values that all of my instances can read from instead of having to open read a hard file each time?

  3. #3
    EmilyStrange's Avatar Active Member
    Reputation
    34
    Join Date
    Jul 2009
    Posts
    125
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I am not sure what you mean by hard file but this caching system isn't really suitable for that purpose.

  4. #4
    erix920's Avatar Private
    Reputation
    4
    Join Date
    Mar 2011
    Posts
    11
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    A physical file on the drive. Instead of reading it each time to retrieve values, store it in memory to be retrieved later and much quicker.

  5. #5
    miceiken's Avatar Contributor Authenticator enabled
    Reputation
    209
    Join Date
    Dec 2007
    Posts
    401
    Thanks G/R
    7/9
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Then parse it into a list of objects or something? If the file doesn't change then it's not really a cache as a cache is 'temporary storage'.

  6. #6
    EmilyStrange's Avatar Active Member
    Reputation
    34
    Join Date
    Jul 2009
    Posts
    125
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    @eric920: I think that solution would be of only minor utility and then only if you were using a highly distributed system or attempting to make multiple disparate applications work together. If you have multiple separate applications that are your bots and you want to have them all communicate, there are better ways than having each bot write out its values to a file. Saving information in to a file is a quick and dirty solution but it is not particularly efficacious. If you have a single multi-boxing application then storing the values in main memory is a preferable solution. Again, this caching class is not directed towards that end and fulfills the role of not having to read from game client memory quite so often if you find your application constantly reading the same values repeatedly in a very short period of time.

  7. #7
    erix920's Avatar Private
    Reputation
    4
    Join Date
    Mar 2011
    Posts
    11
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    All the bots are running within one application. It seems that I wrote out the feature and then forgot about it and made it read from file

  8. #8
    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)
    Not questioning that this will increase performance if you're pulsing a lot, however I'm curious as to whether you have any information on the type of speedup that this caching will give you? (Profiling outputs etc) Just curious, not a big deal if you don't have anything.

  9. #9
    EmilyStrange's Avatar Active Member
    Reputation
    34
    Join Date
    Jul 2009
    Posts
    125
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Never attribute to human cunning what can easily be ascribed to human laziness

    Originally Posted by Cypher View Post
    Not questioning that this will increase performance if you're pulsing a lot, however I'm curious as to whether you have any information on the type of speedup that this caching will give you? (Profiling outputs etc) Just curious, not a big deal if you don't have anything.
    I don't have the profiling information anymore but for me the load when scanning memory across multiple clients was above 10% CPU, this technique reduces the load back to below 1% CPU. The main impact was of course reading another process's memory, not the evaluation of the values once obtained, even adding read and write delegate functions impacts the overall performance very little.

    My multi-boxing AI, even though it heartbeats at a lowly 5Hz, was hitting the game client for the same values several dozen times as it ran various situational evaluators. There are other caching mechanisms I could have used that would have been a better design, but this was a good fit solution for the architecture at the time.

    Also, laziness.

    If you are truly interested in the profiling data I can set up some profiles both with and without caching simply by removing the time check in the base delegate and post them.

Similar Threads

  1. [Bot] How to optimize the ESO client to get Low FPS , for multiple launch
    By cute_star in forum Elder Scrolls Online General
    Replies: 3
    Last Post: 06-05-2014, 11:57 AM
  2. WTT two 70's for one. READ!
    By Glikko92 in forum Members Only Accounts And CD Keys Buy Sell
    Replies: 3
    Last Post: 02-21-2008, 03:00 PM
  3. Question on finding addresses and values for editing.
    By Despite in forum World of Warcraft General
    Replies: 1
    Last Post: 09-26-2007, 06:21 PM
  4. Any new working values for cheat engine
    By ygf1975 in forum World of Warcraft General
    Replies: 3
    Last Post: 09-20-2006, 08:47 PM
All times are GMT -5. The time now is 06:06 PM. 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