Quantcast
Using a C_Timer.After and how to add a time delay - WoWInterface
Thread Tools Display Modes
03-26-20, 10:05 PM   #1
Walkerbo
A Wyrmkin Dreamwalker
 
Walkerbo's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2010
Posts: 51
Using a C_Timer.After and how to add a time delay

Hi all

I am the author of a small addon Aardvark, I am self taught with no background in coding at all.

I have a couple of issues that I have not been able to solve that I would like some guidance for.

Q1. I have a sell function that scans my bags and auto sells grey items and items that a player adds to their sell list when they visit a vendor, (line 1910).

When the vendor opens the sell function runs however it does not always sell all the grey and listed items, I get the "that object is busy" message and I have to spam the sell button to clean out all of the remaining items.

I have tried adding a C_Timer.After in the sell function wrapped around the actual sell chunk, (line 1942) using:

Code:
C_Timer.After(1, function(self)
    -- code chunk
end)
however I then get the following error,

Code:
2x Aardvark\Aardvark-AR 9.0.8.3.lua:1951: attempt to perform arithmetic on local 'itemValue' (a nil value)
[string "@Aardvark\Aardvark-AR 9.0.8.3.lua"]:1951: in function <Aardvark\Aardvark.lua:1910>
[string "@Aardvark\Aardvark-AR 9.0.8.3.lua"]:2469: in function <Aardvark\Aardvark.lua:2456>
I have not been able to kill this error no matter where I wrap a chunk within the sell function.

How do I add a timer delay in the sell function to ensure all the items sell properly?
Is there a better way to ensure all the items sell other than using a timer?


Q2. I have used a timer in my add item by itemID function, (line 5987), without issue.

I have tried to use a time less than 1 sec but it does not seem to reduce the time below 1 sec.

Is there a way of reducing the timer to less than 1 sec or is there a alternative way of adding a delay that will allow a sub 1 sec timer?


Looking forward to some help and understanding of what I am doing wrong.

Last edited by Walkerbo : 03-26-20 at 10:28 PM.
  Reply With Quote
03-26-20, 10:20 PM   #2
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,260
The function called by C_Timer.After can't have parameter so you can't pass self to it. You would have to do it by another mechanism.

If it's not that then the code you posted does not include the part causing the error ie. the parts where local itemValue is initialised and subsequently used at line 1951.
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.
  Reply With Quote
03-26-20, 10:38 PM   #3
Walkerbo
A Wyrmkin Dreamwalker
 
Walkerbo's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2010
Posts: 51
Hi Fizzlemizz

Thank for your help, I had linked the code instead of the paste, I have now added the correct link to my paste

I am not sure of what you mean in regards to passing self as I have got it to work in the add item by itemID function, (line 5985), with the exact same syntax.

What do you mean as I thought that self used the following code as the parameter?
  Reply With Quote
03-26-20, 11:14 PM   #4
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,260
You do the check
Code:
if (checkItemOnGlobalProtectList(currentItemID) or checkItemOnCharacterProtectList(currentItemID)) and
                            itemValue ~= 0
If itemValue is nil then it is not equal zero. It appears GetItemInfo(currentItemID) is returning a nil for itemValue

Code:
if not itemValue or (checkItemOnGlobalProtectList(currentItemID) or checkItemOnCharacterProtectList(currentItemID)) and
                            itemValue ~= 0
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.

Last edited by Fizzlemizz : 03-27-20 at 12:27 AM.
  Reply With Quote
03-26-20, 11:17 PM   #5
Kanegasi
A Firelord
 
Kanegasi's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2007
Posts: 496
Fizzlemizz means that C_Timer only accepts a function object with no arguments. You're not using self anyways, so just function() will do.

As for your error, what is the item causing the error? Are you sure itemValue is not nil? Some items have no value and there's no guarantee it's zero.

Concerning C_Timer delay, the lowest value is determined by your fps. It can only delay something as quick as a single frame. If you are consistently running at 60fps, the lowest value is 1/60 or 0.016. If your computer is average or below and it gets, say, 10fps, the lowest value is 1/10 or 0.1. The reason behind this is that Lua only runs frame-by-frame. Once the next frame is processed, the low value delay has already passed.
  Reply With Quote
03-27-20, 07:02 AM   #6
Walkerbo
A Wyrmkin Dreamwalker
 
Walkerbo's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2010
Posts: 51
Hi Fizzlemizz and Kanegasi

Thanks for your help, I am going to try a few different fixes using your advice.

I will add a reply to this thread when I get it working or when I need more help.

Cheers for your replies.
  Reply With Quote
04-02-20, 09:13 PM   #7
Walkerbo
A Wyrmkin Dreamwalker
 
Walkerbo's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2010
Posts: 51
Hi all

I still have not been able to crack this nut, no matter where I put the timer the sell does not slow.

I have also tried moving the timer into a separate function that I call in the sell with no success.

Is there any way to implement a delay between each loop in the sell function?
  Reply With Quote
04-02-20, 11:54 PM   #8
Vrul
An Onyxian Warder
 
Vrul's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2007
Posts: 355
You have a scope problem. The values of the variables inside the function you pass to C_Timer.After are still being modified by the main for-loop. You need to make local copies of each one used so that they cannot be modified. Basically you need to take a snapshot of their values at that time.

Some notes:
1. You seem to be leaking globals like crazy, but maybe they're declared at the top of the file.

2. I don't understand your use of the twelveCounter variable. It seems to be to limit the number of items/stacks you try and sell at once but you check it inside the function and not outside before creating the function. That seems very counter productive to me.

3. You are assuming you are selling items when that may not be the case. To keep track of each individual sell like you are trying you will almost need to make a list of items you can sell, try to sell them, and then see which items actually sold.

Try replacing your avkSell function with this (dry coded):
Code:
local avkSell
do
    local profitCounter = 0 -- Cash counting variable
    local sellList = { }    -- Array with entries: { bag, slot, itemLink, itemValue, itemCount }

    local function avkSellList()
        for index = #sellList, 1, -1 do -- Process backwards so we can remove items from list safely
            local bag, slot, itemLink, itemValue, itemCount = unpack(sellList[index])
            local itemID = GetContainerItemID(bag, slot)
            if itemID then -- Item still needs to be sold
                UseContainerItem(bag, slot)
            else -- Item has been sold
                tremove(sellList, index)
                local value = itemValue * itemCount
                if AVKGlobalSettings.Sell.sdsm then -- If messages are on
                    print((" - Sold: %s %s for %s"):format(itemCount, itemLink, GetCoinTextureString(value)))
                end
                profitCounter = profitCounter + value -- Add sale value to profit counter
            end
        end
        if #sellList == 0 then -- All items sold
            if profitCounter ~= 0 then -- If items were sold print out total -- this message cannot be disabled
                print(("%s%sTotal Profit :-|r %s"):format(ColourList.textBluePrefixSuffix, ColourList.textGreen, GetCoinTextureString(profitCounter)))
            elseif AVKGlobalSettings.Sell.sntsm then -- If no items sell print out grind message
                print(("%s%sNothing to sell. Better grind some more."):format(ColourList.textBluePrefixSuffix, ColourList.textGreen))
            end
        else -- Try to sell remaining items in 1-second
            C_Timer.After(1, avkSellList)
        end
    end

    function avkSell()
        local vendorGUID = UnitGUID('target')
        if not vendorGUID then -- Catches engineer repair anvils -- debug
            return
        end
        local _, _, _, _, _, npcID, _ = strsplit('-', vendorGUID)
        local vendorID = tonumber(npcID)
        if checkVendorBlacklist(vendorID) then -- Check if vendor ID is on blacklist
            print(ColourList.textBluePrefixSuffix .. ColourList.textGreen .. UnitName('target') .. ' refuses to buy your items.')
        else
            wipe(sellList)
            profitCounter = 0
            local numListed = 0
            for bag = 0, 4 do -- Check each bag
                for slot = 0, GetContainerNumSlots(bag) do -- Check each bag slot
                    local itemID = GetContainerItemID(bag, slot)
                    if itemID then -- If current bag slot has an item then get the item info
                        local _, itemLink, itemQuality, _, _, _, _, _, _, _, itemValue = GetItemInfo(itemID)
                        if
                            (checkItemOnGlobalProtectList(itemID) or checkItemOnCharacterProtectList(itemID)) and
                                itemValue > 0
                         then
                            if AVKGlobalSettings.Sell.sdsm and AVKGlobalSettings.Protect.pdpm then
                                print(' - Protected: ' .. itemLink .. ' not sold.')
                            end
                        elseif
                            checkItemOnGlobalSellList(itemID) or checkItemOnCharacterSellList(itemID) or
                                (AVKGlobalSettings.Sell.seasg and itemQuality == 0)
                         then
                            local _, itemCount = GetContainerItemInfo(bag, slot)
                            numListed = numListed + 1
                            sellList[numListed] = { bag, slot, itemLink, itemValue, itemCount }
                            if numListed == 12 and AVKGlobalSettings.Sell.stil then -- If twelve item limit
                                break
                            end
                        end
                    end
                end
            end
            avkSellList()
        end
    end
end
I removed the stuff at the end of the function as it was mostly repair stuff that was only messages and variable resets and not actually repairing.
  Reply With Quote
04-03-20, 09:36 PM   #9
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 428
You should really just use the event BAG_UPDATE_DELAYED, which fires after you've sated the amount of requests you can send to the server to sell items, then continue trying to sell the rest of the items when that event fires. Keep repeating that process until you've sold all the items in the list. Using a timer is always going to be unreliable because player or server latency can affect the outcome when it's asynchronous.
__________________
  Reply With Quote
04-05-20, 08:26 PM   #10
Walkerbo
A Wyrmkin Dreamwalker
 
Walkerbo's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2010
Posts: 51
Hi Vrul & MunkDev

Thanks for your help.

I decided to go with Vrul's solution; with the event the item totals are still counted even when the item did not actually sell, so the list method worked out better for me.

I do have a question about Vrul's solution;

I can see the do but how does it work outside of a function?

Also the bleeding local's is due to the max limit of 200 local's used, so I am in the process of moving variables into tables to resolve this issue.
  Reply With Quote
04-06-20, 04:32 PM   #11
Vrul
An Onyxian Warder
 
Vrul's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2007
Posts: 355
Originally Posted by Walkerbo View Post
I can see the do but how does it work outside of a function?
Think of your file as one large function. The do...end block creates a new scope. That allows everything in that scope to see everything before it and globals but nothing outside of it can see or modify it's locals.

In this case the variable profitCounter, the table sellList, and the function avkSellList cannot be seen by the rest of your code and also cannot be modified if you happen to accidentally reuse one of those variable names.

Originally Posted by Walkerbo View Post
Also the bleeding local's is due to the max limit of 200 local's used, so I am in the process of moving variables into tables to resolve this issue.
If you have too many locals then you are either doing something wrong or need to split your addon into multiple files.
  Reply With Quote
04-07-20, 08:33 PM   #12
Walkerbo
A Wyrmkin Dreamwalker
 
Walkerbo's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2010
Posts: 51
Hi Vrul
Thanks for that explanation, it does raise a question regarding the do,

Is this a method that should be utilised for every addon file or just in limited circumstances?

Also in the case of splitting an addon into more than one file I have tried to work this out but have yet been successful, (though I am sure I will be able to work this out eventually), why do some addon use one localisation file, whilst others have a file for each localisation? is this the best method or is it just a personal choice?

I think that using a separate file for all variables may be useful when tweaking numbers and print strings but I too wonder if this is a good idea or not.

Once again thanks for your help.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Using a C_Timer.After and how to add a time delay

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off