[3.4.2.50664] Cooldowns menu

User Tag List

Results 1 to 9 of 9
  1. #1
    wardrive's Avatar Active Member
    Reputation
    20
    Join Date
    Jul 2023
    Posts
    43
    Thanks G/R
    23/8
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    [3.4.2.50664] Cooldowns

    Is there a logical structure to the cooldowns table? I've been looking at it for a while and it isn't making a ton of sense to me. The pointers shift every time a spell is cast. I'm completely external.

    What I've gathered so far:

    Cooldowns
    spell_cooldown_offset = 0x2E5A050
    address = self.base + spell_cooldown_offset

    first_address = address + 0x8


    cooldown_obj.spellid = 0x10
    cooldown_obj.itemid = 0x14
    cooldown_obj.startTime = 0x1C
    cooldown_obj.duration = 0x20
    cooldown_obj.cd = ((cooldown_obj.startTime + cooldown_obj.duration) / 1000) - gettime

    I can't seem to find a reliable way to parse the table, for tracking spells that are currently on cooldown. Anyone have any insight into this?

    The posts I've seen seem to accept a spellid as input. I'm trying to iterate the linked list and return a list of spellid's for spells currently on cooldown.

    [3.4.2.50664] Cooldowns
  2. #2
    Razzue's Avatar Contributor Avid Ailurophile

    CoreCoins Purchaser Authenticator enabled
    Reputation
    379
    Join Date
    Jun 2017
    Posts
    588
    Thanks G/R
    186/268
    Trade Feedback
    2 (100%)
    Mentioned
    14 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by wardrive View Post
    ~~
    I'm sure there's better ways to go about this. I plan on revisiting this for retail within a week or two but:

    Code:
    private static bool GetCooldowns(out List<CooldownData> cooldowns){
        cooldowns = new List<CooldownData>();
        try
        {
            var pointer = Memory.Read<IntPtr>((Client.BaseAddress + Offsets.Cooldowns.Base) + 0x10);
            if((pointer.ToInt64() & 1)== 1)
                pointer = Memory.Read<IntPtr>((Client.BaseAddress + Offsets.Cooldowns.Base) + 0x28);
            else
            {
                do
                {
                    var cooldown = Memory.Read<CooldownData>(pointer);
                    if (cooldown.SpellID == 0 && cooldown.ItemID == 0)
                        pointer = IntPtr.Zero;
                    else
                    {
                        pointer = cooldown.NextCooldown;
                        cooldowns.Add(cooldown);
                    }
                } while (IntPtr.Zero != pointer);
            }
            return cooldowns.Count > 0;
        }
        catch (Exception e) { Console.WriteLine(e); }
        return cooldowns.Count > 0;
    }
    Code:
    [StructLayout(LayoutKind.Explicit)]public struct Cooldown
    {
        [FieldOffset(0x0)]
        public IntPtr LastCooldown;
    
    
        [FieldOffset(0x8)]
        public IntPtr NextCooldown;
    
    
        [FieldOffset(0x10)]
        public int SpellID;
    
    
        [FieldOffset(0x14)]
        public int ItemID;
    
    
        [FieldOffset(0x18)]
        public uint TickTime;
    
    
        [FieldOffset(0x1C)]
        public uint RecoveryStart;
    
    
        [FieldOffset(0x20)]
        public uint RecoveryTime;
    
    
        [FieldOffset(0x24)]
        public uint CategoryID;
    
    
        [FieldOffset(0x28)]
        public uint CategoryStart;
    
    
        [FieldOffset(0x2C)]
        public uint CategoryTime;
    
    
        [FieldOffset(0x38)]
        public uint GlobalID;
    
    
        [FieldOffset(0x3C)]
        public uint GlobalTime;
    
    
        [FieldOffset(0x40)]
        public uint GlobalStart;
    }
    Seems to have worked alright for classic so far
    "May all your bacon burn"

  3. Thanks wardrive, imnothonorbuddy (2 members gave Thanks to Razzue for this useful post)
  4. #3
    wardrive's Avatar Active Member
    Reputation
    20
    Join Date
    Jul 2023
    Posts
    43
    Thanks G/R
    23/8
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    @Razzue
    I appreciate it man, thanks! I managed to work together this method earlier, but I'll try implementing yours. The method I wrote feels terribly inefficient:

    Code:
            def update_cooldowns(self, object_manager):
                global active_player, gettime, debug_output
                
                try:
                    address = self.base + spell_cooldown_offset
                    first_address = address + 0x8
                    pointer = self.process.read_longlong(first_address)
                    cooldown_obj = object_manager.CooldownObject()
                    cooldown_obj.pointer = pointer
                    #cooldown_obj.nextcooldown = self.process.read_longlong(cooldown_obj.pointer + 0x0)
                    cooldown_obj.spellid = self.process.read_int(cooldown_obj.pointer + 0x10)
                    cooldown_obj.itemid = self.process.read_int(cooldown_obj.pointer + 0x14)
                    cooldown_obj.startTime = self.process.read_int(cooldown_obj.pointer + 0x1C)
                    cooldown_obj.duration = self.process.read_int(cooldown_obj.pointer + 0x20) #0x2c 
                    cooldown_obj.gcd = self.process.read_int(cooldown_obj.pointer + 0x3C)
                    cooldown_obj.cd = ((cooldown_obj.startTime + cooldown_obj.duration + cooldown_obj.gcd)/1000) - gettime
                    debug_output = (f"{cooldown_obj.pointer:X}, {cooldown_obj.spellid}, {cooldown_obj.cd:.2f} | {len(active_player.cooldown_objs)}")
    
                    if cooldown_obj.cd > 0:
                        if cooldown_obj.spellid not in active_player.cooldowns:
                            active_player.cooldowns.append(cooldown_obj.spellid)
                            active_player.cooldown_objs.append(cooldown_obj)    
                    
                    for c in active_player.cooldown_objs:
                        c.cd = ((c.startTime + c.duration + cooldown_obj.gcd) / 1000) - gettime
                        if c.cd <= 0:
                            active_player.cooldown_objs.remove(c)
                            active_player.cooldowns.remove(c.spellid)                
                    
                        
                except Exception as e:
                    logger_function(e)
                 
            
            def get_cooldowns(self, object_manager):
                global active_player
                self.update_cooldowns(object_manager)
                return active_player.cooldowns
    It's on a threaded loop in my bot (python), and appears to be working to build an array of spellID's in realtime. It just feels terribly inefficient, and is only reading the 0x8 pointer, so if the bot is started AFTER triggering a cooldown, it's possible to miss it until the pointers rotate again.

    Also, in my code, spells for items weren't working because it looks like the offset for duration is different. (tested with shadowmeld and hearthstone). I appreciate you sharing.
    Last edited by wardrive; 08-23-2023 at 03:21 AM.

  5. #4
    Razzue's Avatar Contributor Avid Ailurophile

    CoreCoins Purchaser Authenticator enabled
    Reputation
    379
    Join Date
    Jun 2017
    Posts
    588
    Thanks G/R
    186/268
    Trade Feedback
    2 (100%)
    Mentioned
    14 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by wardrive View Post
    ~~
    It may seem inefficient, but you want to read an array of the structs instead of a cooldown class/object and updating individual fields.
    1 * Length memory reads vs 7 * Length reads

    Wow does much the same internally:
    Code:
    while ( (CooldownData & 1) == 0 && CooldownData )
      {
        if ( *(_DWORD *)(CooldownData + 0x10) == SpellId && *(_DWORD *)(CooldownData + 0x14) == ItemId )
        {
          Recovery = *(_DWORD *)(CooldownData + 0x20);
          if ( (Recovery || *(_BYTE *)(CooldownData + 0x30)) && !v30 )
          {
            v39 = *(_BYTE *)(CooldownData + 0x30);
            RecoveryStart = v37 + 1;
            if ( !v39 )
              RecoveryStart = *(_DWORD *)(CooldownData + 0x1C);
            RecoveryEnd = RecoveryStart + Recovery;
            if ( RecoveryStart + Recovery - v33 >= 0 )
            {
              if ( v11 )
              {
                if ( v39 )
                  Recovery = 1;
                *v11 = Recovery;
              }
              if ( a6 )
              {
                v42 = v37;
                if ( !*(_BYTE *)(CooldownData + 48) )
                  v42 = *(_DWORD *)(CooldownData + 28);
                *a6 = v42;
              }
              if ( a7 && *a7 )
                *a7 = *(_BYTE *)(CooldownData + 48) == 0;
              if ( a5 )
                *a5 = *(_DWORD *)(CooldownData + 24);
              v33 = RecoveryEnd;
            }
          }
        }
    patterns for binary (this is on retail btw, but should be same/similar on classic)
    4C 89 4C 24 ? 44 89 44 24 ? 89 54 24 ? 48 89 4C 24 ? 55 56 57 41 54 41 55
    If pattern doesn't work:
    - String search "GetSpellCooldown"
    - Xref string pointer and jump to .data ref
    - Open sub BELOW where the string is now (sub will be Lua_GetSpellCooldown)
    - Open sub that passes 3 value pointers, then open the base sub in that one
    Now we "should" be at some of the inner workings of how the script operates
    Last edited by Razzue; 08-23-2023 at 03:22 AM.
    "May all your bacon burn"

  6. Thanks wardrive (1 members gave Thanks to Razzue for this useful post)
  7. #5
    Razzue's Avatar Contributor Avid Ailurophile

    CoreCoins Purchaser Authenticator enabled
    Reputation
    379
    Join Date
    Jun 2017
    Posts
    588
    Thanks G/R
    186/268
    Trade Feedback
    2 (100%)
    Mentioned
    14 Post(s)
    Tagged
    0 Thread(s)
    Then for actual cooldown checks i was doing something like :
    Code:
    internal static long ItemCooldown(object item, bool ignoreGCD = false){
        long length = 0;
    
    
        try
        {
            if (!GetCooldown(out var cooldowns)) return length;
            var str = item switch
            {
                int id => C_Item.Name(id), 
                string name => name, 
                _ => string.Empty
            };
    
    
            foreach (var cd in cooldowns)
            {
                if (ignoreGCD && cd.GlobalID != 0) continue;
                if (C_Item.Name(cd.ItemID) != str) continue;
    
    
                var time =
                    cd.RecoveryTime != 0 ? cd.RecoveryStart + cd.RecoveryTime :
                    cd.CategoryTime != 0 ? cd.CategoryStart + cd.CategoryTime :
                    !ignoreGCD ? cd.GlobalTime != 0 ? cd.GlobalStart + cd.GlobalTime : 0 : 0;
    
    
                if (time == 0) return time;
    
    
                Imports.QueryPerformanceFrequency(out var frequency);
                Imports.QueryPerformanceCounter(out var perfCount);
                var stamp = time - ((perfCount * 1000) / frequency);
                if (stamp <= 0) continue;
                length = stamp;
                break;
            }
        }
        catch (Exception e) { }
        return length;
    }
    "May all your bacon burn"

  8. Thanks wardrive (1 members gave Thanks to Razzue for this useful post)
  9. #6
    wardrive's Avatar Active Member
    Reputation
    20
    Join Date
    Jul 2023
    Posts
    43
    Thanks G/R
    23/8
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    As always, quality feedback sir. Much appreciated! This was most helpful.

  10. #7
    LiquiDD's Avatar Member
    Reputation
    1
    Join Date
    Nov 2014
    Posts
    3
    Thanks G/R
    2/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    thanks for sharing Razzue!
    can some1 help me how i can get gcd please
    in wow addons i made it like this but i cant find spell with id 61304 in memorys cooldowns, in localplayer structure i found only info about cast(and it so slowly updating?)

    Code:
    local function GetGCD()
      local start, duration, enabled, modRate = GetSpellCooldown(61304)
      if ( start > 0 and duration > 0) then
        local cdLeft = start + duration - GetTime()
        --print(start .. " " .. duration .. " " .. cdLeft)    
        return cdLeft;
      end
      return 0;
    end;
    my problem is GlobalStart(at +0x40) == 133, not 1500 like from addon, i checked some spells(wotlk paladin) most of them got gcd 133 which is not real gcd,

    SpellID(21084) ItemID(0) TickTime(1.00) RecoveryStart(6735166) RecoveryTime(0)
    CategoryID(0) CategoryStart(6735166) CategoryTime(0) GlobalID(0) GlobalTime(6735166) GlobalStart(133) CurTime(6740344)
    Last edited by LiquiDD; 08-24-2023 at 06:56 AM.

  11. #8
    Lumi666's Avatar Member
    Reputation
    1
    Join Date
    May 2012
    Posts
    19
    Thanks G/R
    3/0
    Trade Feedback
    3 (100%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Razzue View Post
    I'm sure there's better ways to go about this. I plan on revisiting this for retail within a week or two but:

    Code:
    private static bool GetCooldowns(out List<CooldownData> cooldowns){
        cooldowns = new List<CooldownData>();
        try
        {
            var pointer = Memory.Read<IntPtr>((Client.BaseAddress + Offsets.Cooldowns.Base) + 0x10);
            if((pointer.ToInt64() & 1)== 1)
                pointer = Memory.Read<IntPtr>((Client.BaseAddress + Offsets.Cooldowns.Base) + 0x28);
            else
            {
                do
                {
                    var cooldown = Memory.Read<CooldownData>(pointer);
                    if (cooldown.SpellID == 0 && cooldown.ItemID == 0)
                        pointer = IntPtr.Zero;
                    else
                    {
                        pointer = cooldown.NextCooldown;
                        cooldowns.Add(cooldown);
                    }
                } while (IntPtr.Zero != pointer);
            }
            return cooldowns.Count > 0;
        }
        catch (Exception e) { Console.WriteLine(e); }
        return cooldowns.Count > 0;
    }
    Code:
    [StructLayout(LayoutKind.Explicit)]public struct Cooldown
    {
        [FieldOffset(0x0)]
        public IntPtr LastCooldown;
    
    
        [FieldOffset(0x8)]
        public IntPtr NextCooldown;
    
    
        [FieldOffset(0x10)]
        public int SpellID;
    
    
        [FieldOffset(0x14)]
        public int ItemID;
    
    
        [FieldOffset(0x18)]
        public uint TickTime;
    
    
        [FieldOffset(0x1C)]
        public uint RecoveryStart;
    
    
        [FieldOffset(0x20)]
        public uint RecoveryTime;
    
    
        [FieldOffset(0x24)]
        public uint CategoryID;
    
    
        [FieldOffset(0x28)]
        public uint CategoryStart;
    
    
        [FieldOffset(0x2C)]
        public uint CategoryTime;
    
    
        [FieldOffset(0x38)]
        public uint GlobalID;
    
    
        [FieldOffset(0x3C)]
        public uint GlobalTime;
    
    
        [FieldOffset(0x40)]
        public uint GlobalStart;
    }
    Seems to have worked alright for classic so far
    Any idea how to make this work for all spell Cooldown on retail ?
    For example with mage "Frost Nova" SpellID 122, the CooldownData disapear from the list even if still on CD.

    For some spell the real cooldowns seems to be at (Client.BaseAddress + Offsets.Cooldowns.Base) + 0x40 (instead of + 0x10) but this list doesn't use the spellID.

  12. #9
    imnothonorbuddy's Avatar Member
    Reputation
    2
    Join Date
    Oct 2023
    Posts
    10
    Thanks G/R
    2/1
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Razzue View Post
    //4C 89 4C 24 ? 44 89 44 24 ? 89 54 24 ? 48 89 4C 24 ? 55 56 57 41 54 41 55
    What parameters does this function accept? The fact is that I found this function using your pattern (unfortunately, I could not find it using the string you gave, because in my case such a string is just a string, without references to it)
    and when I try to disassemble it, I get an internal error in IDA, so I can’t find out how many and what parameters the function takes, or what type of value it returns. I tried to use it with the spell_guid parameter, but the function returned 0, I've been doing this for 2 days now, but due to IDA errors I can't make any progress, and I also can't get information about cooldown using normal iteration, because it seems to me , the structures are disfigured, I also couldn’t get information about the cooldown duration from there.

    im working with wow version 9.2.7 45745

    EDITED:
    I recreated some function that iterating trough cooldowns, (i recreated only iterating ) and i can get info about spell, but i have only id, time when i casted it, and thats all(of useful info)
    here is not info about spell recovery time btw, idk what to do
    Attached Thumbnails Attached Thumbnails [3.4.2.50664] Cooldowns-screenshot-1-png  
    Last edited by imnothonorbuddy; 10-13-2023 at 05:35 PM. Reason: found some info

Similar Threads

  1. No cooldown hack
    By typ in forum World of Warcraft General
    Replies: 10
    Last Post: 02-12-2007, 05:50 AM
  2. No Hearthstone Cooldown
    By palmeter in forum World of Warcraft Exploits
    Replies: 25
    Last Post: 01-06-2007, 11:27 AM
  3. No cooldown time on Items!
    By myojinyahiko in forum World of Warcraft Exploits
    Replies: 18
    Last Post: 12-27-2006, 12:11 AM
  4. BG Exploits - WTF Bye Cooldowns!
    By treyska in forum World of Warcraft Exploits
    Replies: 34
    Last Post: 12-25-2006, 04:57 AM
  5. Bandage Exploit -NO Cooldown-
    By Matt in forum World of Warcraft Exploits
    Replies: 3
    Last Post: 03-21-2006, 04:14 PM
All times are GMT -5. The time now is 03:34 PM. Powered by vBulletin® Version 4.2.3
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2024 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search