• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Lua] [Deprecated] TimerUtils

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
TimerUtils is far more flexible in Lua, and allows for some powerful new features to make things easier to program.

Lua:
do
   -- Lua TimerUtils 2.1.0.0
  
   local data = {}
   local recycle = {}
  
   --NewTimer functionality includes optional parameters to pass data to timer and to provide a timeout/repeat boolean.
   --If timeout is provided, it assumes that "dat" is a function.
   --One can also swap those parameters and assign the timeout first, the function second.
   --If the user doesn't declare a "repeats" boolean, then the timer will automatically release itself at the end.
   function NewTimer(dat, timeout, repeats)
      local t = recycle[#recycle] or CreateTimer()
      recycle[#recycle] = nil
      if timeout then
         if type(dat) == "number" then  --user wishes to swap timeout with the function
            dat, timeout = timeout, dat --swap the variables accordingly
         end
         if type(dat) == "function" or type(repeats) == "function" then --a function is needed.
            if repeats then
               if type(dat) == "boolean" then --user wants to use the TimerStart syntax, swap the boolean with dat
                  dat, repeats = repeats, dat
               end
               TimerStart(t, timeout, repeats, dat)
            else
               TimerStart(t, timeout, false,
                  function()
                     recycle[#recycle + 1] = t
                     dat()
                  end
               )
            end
         --else
            --print("TimerUtils error: forgot to pass a function to NewTimer, despite a duration being specified")
         end
      else
         data[t] = dat
      end
      return t
   end

   --Release functionality doesn't even need for you to pass the expired timer.
   --as an arg. It also returns the user data passed.
   function ReleaseTimer(whichTimer)
      whichTimer = whichTimer or GetExpiredTimer()
      if not whichTimer then
         --print("TimerUtils error: ReleaseTimer either needs a valid parameter or must be called immediately after expiring")
         return 0
      end
      for i = 1, #recycle do
         if recycle[i] == whichTimer then
            --print("TimerUtils error: Attempt to release timer more than once")
            return 0
         end
      end
      PauseTimer(whichTimer)
      local dat = data[whichTimer]
      data[whichTimer] = nil
      recycle[#recycle + 1] = whichTimer
      return dat
   end
  
   --GetTimerData functionality doesn't even require an argument.
   function GetTimerData(whichTimer)
      return data[whichTimer or GetExpiredTimer()]
   end

   function SetTimerData(whichTimer, dat)
      data[whichTimer] = dat
   end

end

Example usage:

Lua:
TimerStart(NewTimer(5), 0.00, false, function()
    print(ReleaseTimer()) --prints 5
end)

--The above is a much shorter variant of the below script (which also works just fine):

local t = NewTimer()
SetTimerData(t, 7)
TimerStart(t, 0.00, false, function()
    local t = GetExpiredTimer()
    print(GetTimerData(t)) --prints 7
    ReleaseTimer(t)
end)

--Of course, this being Lua, the data doesn't even have to be an integer:

TimerStart(NewTimer("Yo bro"), 0.00, false, function()
    print(ReleaseTimer()) --prints Yo bro
end)

--And now, with Lua TimerUtils 2.0, you have the option of an event shorter script (for non-repeating timers)
--where you don't even manually release the timer at the end.
--The Release-Timer process is handled completely behind-the-scenes:
NewTimer(function()
   print("doing some stuff after 5 1/2 seconds")
end, 5.50)

--And, finally, with 2.1, you can even swap the parameters of NewTimer:
NewTimer(3.25, function()
   print("doing some stuff after 3 1/4 seconds")
end)
 
Last edited:
Level 7
Joined
Jun 5, 2018
Messages
180
Awesome library in lua. It would be great if you could explain how to use it.
Just put your lua code block behind my code that uses it?
Or need a "requires yourlib" like what is done in vJass?
Or should I use "doFile" to use your code?

I think a lua proper application guide (LPAG) is urgent to present.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I just use Lua like I used JASS - paste it into a custom script trigger.

Others prefer to use external compilers or whatever. I know nothing about that.

I'm actually waiting to see what the modding scene looks like post-reforged before I make any further investments into the modding community.

My wife had to convince me not to cancel my preorder out of disgust for the way Blizzard treated their player and casters. I considered deleting all my submissions to The Hive as well.

I liked seeing Blizzard employees staging sit-outs from work. It shows the human side of the company- the compassion. And I know the arm of Blizzqrd putting together Reforged consists of regular people who have souls.

So I'll keep my preorder live and will just have to wait and see what the future holds.
 
Level 20
Joined
Jul 10, 2009
Messages
477
Hey Bribe,

just out of curiosity, what is the advantage of your system in contrast to just having one function to call another function with a delay?
Anonymous functions in Lua make this quite easy:

Lua:
--The function to call something else with a delay:
function CallDelayed(delayInSeconds, callbackFunc)
    local timer = CreateTimer() ---@type timer
    TimerStart(timer, delayInSeconds, false,
        function()
            DestroyTimer(timer)
            callbackFunc()
        end)
end
Then you can just do this:
Lua:
CallDelayed(3., function() print("Hello World") end)
which is a one-liner and really easy to understand.
Functions without parameters don't even require anonymous function encapsulation:
Lua:
function f()
    print("Hello World")
end
CallDelayed(3., f)

I see that recycling timers is not possible this way, but apart from that, it looks much cleaner to me.
 

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
I agree that there is no need to manually store the data when lua can already do that automatically.
I don't see what the benefit with named would be. If the function needs some data, it should be given by the parameters, which can easily be given by wrapping the function call in an anonymous function.
Eikonium's code also handles destroying the timer automatically.

One use case I can see is to make it easier to translate JASS resources to lua.
 
Level 20
Joined
Jul 10, 2009
Messages
477
Hey @Jampion , thanks for crawling through all these submissions, much appreciated!

Just fyi, there is another Lua code submission for delayed function calls on hive: DelayedAction, which I noticed after posting my snippet here.
In that submission, @Insanity_AI uploaded his version of CallDelayed, based on this thread's TimerUtils. The advantage of his version was that it also supported the CallDelayed(delay, function [, functionArgs]) syntax, allowing you to write CallDelayed(3., print, "Hello World") instead of CallDelayed(3. function() print("Hello World") end). Ofc, this was merely a convenience feature at the cost of some small performance overhead.

In the same thread, @AGD then had the idea of additionally using only one single timer for all executions of CallDelayed, saving performance on multiple rapid executions (by preventing multiple timers running simoulatenously).
I worked with him on performance optimizing and bug-cleaning his version.
The (current) result of our effort is the code in the very bottom post of DelayedAction. It has no dependencies to this thread's TimerUtils, has better performance upon multiple rapid executions and allows CallDelayed(delay, function [, functionArgs]) syntax.

As of now, I prefer that version over my snippet from this thread. :)

Maybe it would be beneficial to upload it as a separate submission, but I wasn't sure, if I would have breached against anyone's deserved credits upon doing so :D
 
I recommend updating it to the following code because there are fewer if/thens and the timers get recycled
Lua:
do
    local data = {}
    local recycle = {}
    function SetTimerData(whichTimer, dat)
        data[whichTimer] = dat
    end
 
    --GetData functionality doesn't even require an argument.
    function GetTimerData(whichTimer)
        return data[whichTimer or GetExpiredTimer()]
    end
 
    --NewTimer functionality includes optional parameter to pass data to timer.
    function NewTimer(dat)
        local t = recycle[#recycle] or CreateTimer()
        recycle[#recycle] = nil
        data[t] = dat
        return t
    end
 
    --Release functionality doesn't even need for you to pass the expired timer.
    --as an arg. It also returns the user data passed.
    function ReleaseTimer(whichTimer)
        whichTimer = whichTimer or GetExpiredTimer()
        local dat = data[whichTimer]
        data[whichTimer] = nil
        PauseTimer(whichTimer)
        table.insert(recycle,whichTimer)
        return dat
    end
end
 
Level 20
Joined
Jul 10, 2009
Messages
477
Again @Bribe, why don't we just use this simple snippet instead of your library?

Lua:
--The function to call something else with a delay:
function CallDelayed(delayInSeconds, callbackFunc)
    local timer = CreateTimer()
    TimerStart(timer, delayInSeconds, false,
        function()
            DestroyTimer(timer)
            callbackFunc()
        end)
end

You have successfully ignored the three posts talking about it, so I feel like I need to bring this up again...
 
Level 20
Joined
Jul 10, 2009
Messages
477
@SmitingDevil Well, you could use recursion:
Lua:
function exampleFunc()
    print("Hello")
    CallDelayed(5., exampleFunc)
end
exampleFunc() --prints "Hello" on screen every 5 secs
But you are right that the snippet is not initially designed for periodic execution.


Opting it for periodic execution would probably look like this (not tested):
Lua:
function CallPeriodically(intervalInSeconds, callbackFunc)
    local timer = CreateTimer()
    TimerStart(timer, intervalInSeconds, true, function() callbackFunc(timer) end)
    return timer
end
So you can store the returned timer t somewhere and stop execution with DestroyTimer(t) at any time.
The timer is also passed to the callbackFunc, which allows you to destroy it even inside callbackFunc under any chosen condition.


But well, timer stuff is not too hard in Lua anyway. You could completely ignore my snippet and still produce easy timed callbacks without any library:
Lua:
---Moves the specified unit upwards by the specified distance 32 times a second, until the specified break-condition applies.
---@param unitToMove unit
---@param dist number
---@param breakCondition fun():boolean
function MoveUnitUpwardsUntil(unitToMove, dist, breakCondition)
    local timer = CreateTimer()
    local func = function()
        SetUnitY(unitToMove, GetUnitY(unitToMove) + dist)
        if breakCondition() then
            DestroyTimer(timer)
        end
    end
    TimerStart(timer, 1/32, true, func)
end
Lua is so well prepared for this stuff that using Bribe's library just to remember some values feels... overly complicated.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I took the feedback into account as realistically as possible, as you can see in the latest update. My understanding of Lua was quite a bit weaker back then.

If you want a wrapper function for CallDelayed, with parameters in that order, it is fine by me to add it in. It’d just be NewTimer with swapped parameters.

However, Timer Utils was never meant to solve the problem of quickly-executing Timers. That’s where Jesus4Lyf’s Timer32 module came in handy.

If you think this resource should include something like that, I’d disagree for the same reason Nestharus disagreed with that in the vJass era: they should exist as two separate resources.

I think you should release your tiny-timer resource as a standalone thing that aims to fill the requirement that Timer32 did.

As AGD mentioned, the legacy API should remain to make peoples’ time converting their vJass resources a bit less shitty.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
@Eikonium @AGD I have updated this resource yet again, and yet again without breaking backwards-compatibility or adding to the _G table. NewTimer() can now take the duration as the first argument and the function as the second argument (or vice-versa as I had it yesterday).

If someone wants a traditional, quickly repeating timer, they can use Timer32, which I just imported to Lua:

 
Level 20
Joined
Jul 10, 2009
Messages
477
If you want a wrapper function for CallDelayed, with parameters in that order, it is fine by me to add it in. It’d just be NewTimer with swapped parameters.
It's not just NewTimer with swapped parameters. NewTimer leaks a timer, if you don't use ReleaseTimer afterwards. CallDelayed does not.

But that wasn't even my point. My point is, why should we use your library, when CallDelayed handles it cleaner and shorter?
What is the benefit of using your library instead of my snippet?
That's a serious question, I'm waiting for you to come up with actual advantages.
Jampion came up with potential usefulness for JASS users switching to Lua.
Is that all?

However, Timer Utils was never meant to solve the problem of quickly-executing Timers. That’s where Jesus4Lyf’s Timer32 module came in handy.
All example code you provided in the resource is just doing one thing: Calling a function after a delay and remembering parameters in the meantime. Which is the same as what my snippet does.
So if your resource wasn't meant to do exactly that, what then was it meant for?

Also, why do you keep referring to outdated JASS resources? We are talking about Lua and should work up to its standards.

If you think this resource should include something like that, I’d disagree for the same reason Nestharus disagreed with that in the vJass era: they should exist as two separate resources.
Well, then give me a reason for why we need your resource.

Don't get me wrong, I don't want to offend you. I appreciate your code contribution, just want a clear statement. Your library surely has good use cases and I want to encourage you to be clear on what these are.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
It's not just NewTimer with swapped parameters. NewTimer leaks a timer, if you don't use ReleaseTimer afterwards. CallDelayed does not.

If someone uses NewTimer(1.00, function() print("done after 1 second") end), this will automatically recycle the timer.

But that wasn't even my point. My point is, why should we use your library, when CallDelayed handles it cleaner and shorter?
What is the benefit of using your library instead of my snippet?
That's a serious question, I'm waiting for you to come up with actual advantages.
Jampion came up with potential usefulness for JASS users switching to Lua.
Is that all?

My goal with any resource, historically and presently, has been to make something more accessible to people. Some areas of focus for me are providing a clean UI, enabling GUI syntax or writing cross-compatibility scripts.

If I can keep some of the syntax the same for people converting their vJass resources to Lua ones, then great!

All example code you provided in the resource is just doing one thing: Calling a function after a delay and remembering parameters in the meantime. Which is the same as what my snippet does.
So if your resource wasn't meant to do exactly that, what then was it meant for?

That was the original purpose of this resource, and it very well could have been scrapped a couple of years ago in the state that it was in.

Also, why do you keep referring to outdated JASS resources? We are talking about Lua and should work up to its standards.

The vast majority of people here are using GUI. A much, much smaller subset of people are using vJass. An even smaller percentage are using Lua. Comparing to resources that people actually know and may have had some familiarity with is beneficial.

Well, then give me a reason for why we need your resource.

No one needs any snippet. They can code everything by hand. They can choose a snippet if it prevents them from having to re-write the same code for different resources.

Don't get me wrong, I don't want to offend you. I appreciate your code contribution, just want a clear statement. Your library surely has good use cases and I want to encourage you to be clear on what these are.

I'll just add to this, that I envision this resource to really help people who don't have a full understanding of Lua to take advantage of its constructs. For people who have ANY understanding of Lua, deep or shallow, but who have a background in vJass, TimerUtils API is already in their heads and they don't have to remember new function names.
 
Level 20
Joined
Jul 10, 2009
Messages
477
No one needs any snippet. They can code everything by hand. They can choose a snippet if it prevents them from having to re-write the same code for different resources.
Bribe, seriously. I asked for the benefit of using your resource over mine. You answered with why people should use resources in general. Are you kidding me or do you really don't understand the question?
Sorry for being rude here, but I don't want to discuss this based on straw man arguments.

However, Timer Utils was never meant to solve the problem of quickly-executing Timers.
So if your resource wasn't meant to do exactly that, what then was it meant for?
That was the original purpose of this resource.
So what now? You originally say that this resource was never meant for quick timer execution. I said your example code looks like it was meant for that - and you agree?

My goal with any resource, historically and presently, has been to make something more accessible to people. [...]
You keep telling me random stuff about your motivation instead of providing the clear point I've asked for.
I give up now.

If I can keep some of the syntax the same for people converting their vJass resources to Lua ones, then great!
This point is the only one you made that partially anwers my question.
Look, we had a very lengthy discussion now that basically led to nothing. If JASS-compatibility is your sole reason for using your resource over mine, why don't you say that earlier and save us some time?

I'll just add to this, that I envision this resource to really help people who don't have a full understanding of Lua to take advantage of its constructs. For people who have ANY understanding of Lua, deep or shallow, but who have a background in vJass, TimerUtils API is already in their heads and they don't have to remember new function names.
Err, how should a resource simply copied from JASS help people to take advantage of Lua constructs?
You just teach them to do the same as they've been used to, even if it doesn't even make sense in Lua.
Look, if people want to stick to their JASS habits, they will most likely just stick to JASS.
The few people interested in learning Lua probably want to benefit from its advantages, so we should teach them the Lua way of doing it.
The ReleaseTimer-function searches a specific timer in a list of timers by looping through it:
Lua:
for i = 1, #recycle do
         if recycle[i] == whichTimer then
[...]
This would be considered bad style in Lua, because you are searching a list with potentially thousands of elements with every call of ReleaseTimer, while you could simply have saved any timer's list position in a separate table like this: timerPositions[whichTimer] = index.
This is called double representation, because you use two tables mirroring each other, i.e. you save both table1[index] = timer and table2[timer] = index for quick reference. It is possible to do that in Lua, because the language allows keys of any type.

Lua simply doesn't have the limitations that made TimerUtils useful in JASS. Callbacks are super simple in Lua anyway.

If you prefer to keep your resource for the specific use case of JASS -> Lua conversions, then that's of course fine. But maybe be clear about that fact in your resource description.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I’m not going to go down a long list again and yet again respond to every point. I’ve already done that now more times than it was worth. Yet you still won’t lay off attacking this resource as if you have some kind of personal vendetta against it. I’ve seen resources get approved here with far less description or justification for their existence, yet here we are nit picking about this or that.

You are such a discouraging person in a community that should be based around helping each other, not bringing each other down. Who even are you? I don’t recognize your name (despite you being here as long as I have) nor why you seem to have it out for me.


Just wanted to strike that out to make it clear that I was in the wrong at the time of writing this. I was emotional due to events at work and incorrectly wanted to pass myself off as a victim of an attack that didn't exist.
 
Last edited:
Level 20
Joined
Jul 10, 2009
Messages
477
You are such a discouraging person in a community that should be based around helping each other, not bringing each other down. Who even are you? I don’t recognize your name.
Wow...
I didn't intend to bring this on a personal level, but you just did.
I don't have anything against you and I haven't attacked you personally, but you maybe realize that you just did exactly that.
Who am I even? Good point, man.
Look, I wrote in one of my posts that I don't want to offend you and appreciate your contribution. That's still true.

However, I've attempted across a whole bunch of posts now to get one simple answer from you: what are this resource's advantages in contrast to the snippet I've posted?
You have either ignored these attempts or you have answered something completely unrelated.
Yes, that led to my posts becoming gradually less nice, but how do you think should I react, when you refuse to give any answer related to the question?
Funny that you say I have a personal vendetta against your resource, while I just tried to get the same answer the whole time. I didn't even critisize it apart from saying that my snippet is doing the same thing cleaner and shorter.
Instead of defending your resource on a rhetorical level, why didn't you just say that the technical advantage of your script is JASS-similarity for Lua-conversions - and the conversion would have been over?

Again and to be clear, I do not think that this resource is bad at all.
I just wanted you to be clear about its use cases.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
This can be graveyarded in favor of [Lua] - Timed Call and Echo. This can still exist in the graveyard for reference, if someone is having a hard time switching from vJass to Lua.

A standalone Timer Recycling snippet is probably worth its own resource, rather than being bundled into a timer system that doesn't cover every single use case for one's timer needs.
 
Top