How to: Use Timers in JASS

tooltiperror

Super Moderator
Reaction score
233
Timers
([ljass]type timer extends handle[/ljass])
INTRODUCTION
A timer is a 32-bit pointer. It is similar to a stopwatch, in JASS you can start a timer and execute a function you chose when the time has elapsed. You might think you can just use [ljass]TriggerSleepAction[/ljass] or [ljass]PolledWait[/ljass] rather than use a timer, but they are inaccurate on single player, and become even worse on Battle.net. Using timers can seem daunting for beginning JASSers even though they are quite simple. This tutorial will explain how to use JASS2 timers and then timer systems in increasing difficulty.

VANILLA TIMERS
The simplest timer example has three important parts to it.
  • The Declaration — The creation of the timer.
  • Life of a Timer — Entering the duration for the timer to run and starting it.
  • The Decline — Destroying the timer and nulling it.
Here's a commented snippet of code explaining the use of a timer.
JASS:
// Example #1
 function Handlerfunc takes nothing returns nothing
     call BJDebugMsg("It has been five seconds since foo_function was executed.")
 endfunction

 function foo_function takes nothing returns nothing
     local timer t=CreateTimer() // Create a new timer and set it to t.
     call TimerStart(t, 5.00, false, function Handlerfunc) // Start it, wait five seconds, then run the specified function.
     set t=null // It's a handle so it must be nulled. 
 endfunction

It's pretty self explanatory. A problem with timers is that you need to pause them before you destroy them. If you don't, you can get some strange bugs like timers being fired and recurring after you destroy them.
JASS:
// Example #2
 call PauseTimer(t)
 call DestroyTimer(t)

You might notice in the first example we pass the argument [ljass]false[/ljass] to the [LJASS]TimerStart[/LJASS] function. The name of that argument is [ljass]periodic[/ljass]. In our example, it is false, so the timer is not periodic. If we passed [ljass]true[/ljass] instead, the function would run every five seconds.

Another important point to make use of the event response [ljass]GetExpiredTimer[/ljass]. It returns the timer that expired to run the current function. Returns null if the function was not run by a timer (not sure why you would run into that circumstance anyways). Here's an example.
JASS:
// Example #3
 function Handlerfunc takes nothing returns nothing
     local timer t=GetExpiredTimer()
     call PauseTimer(t)
     call DestroyTimer(t)
     set t=null
 endfunction

 function foo_function takes nothing returns nothing
     local timer t=CreateTimer()
     call TimerStart(t, 5.00, false, function Handlerfunc)
     set t=null
 endfunction

Examples are examples, what if we want something more practical, like an MUI spell that kills the target after 5 seconds? The problem is that we don't want it to fail if someone pauses the game or lags. Perfectly possible with timers. The solution is to store the target in a [ljass]hashtable[/ljass], by the Handle ID of the timer. When we get the expired timer in the handler function, it will have the same Handle ID so we can get the same unit.
JASS:
// Example #4
//globals
//    hashtable udg_Hashtable=InitHashtable()
//endglobals
 constant function SomeSpell_ID takes nothing returns integer
     return 'A000'
 endfunction

 function SomeSpell_Conditions takes nothing returns nothing
     return GetSpellAbilityId() == SomeSpell_ID()
 endfunction

 function SomeSpell_Handlerfunc takes nothing returns nothing
     local timer t=GetExpiredTimer()
     call KillUnit(LoadUnitHandle(udg_Hashtable, GetHandleId(t), 0))
     call PauseTimer(t)
     call DestroyTimer(t)
     set t=null
 endfunction

 function SomeSpell_Actions takes nothing returns nothing
     local timer t=CreateTimer()
     call SaveUnitHandle(udg_Hashtable, GetHandleId(t), 0, GetSpellTargetUnit())
     call TimerStart(t, 5.00, false, function SomeSpell_Handlerfunc)
     set t=null
 endfunction

 function InitTrig_SomeSpell takes nothing returns nothing
     local trigger t=CreateTrigger()
     local integer index=0
     call TriggerAddAction(t, function SomeSpell_Actions)
     call TriggerAddCondition(t, Condition(function SomeSpell_Conditions))
     loop
         call TriggerRegisterPlayerUnitEvent(t, Player(index),  EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
         set index=index+1
         exitwhen index==bj_MAX_PLAYERS
     endloop
 endfunction

At this point, you should know enough of how to use timers in your spells, libraries, or whatever you need precise time instead. If you want to use vJASS and timers combined, keep reading.

TIMERUTILS (Note: The rest of this tutorial now uses vJASS)
Probably the most widely used timer system in WC3 modding, TimerUtils was written by Vexorian first using [ljass]gamecache[/ljass] and now utilizing [ljass]hashtable[/ljass]s. It replaces [ljass]CreateTimer[/ljass] with [ljass]NewTimer[/ljass] and [ljass]DestroyTimer[/ljass] with [ljass]ReleaseTimer[/ljass]. A simple example should suffice. This is the same as Example #1 but it uses TimerUtils functions.
JASS:
// Example #5
 function Handlerfunc takes nothing returns nothing
     call BJDebugMsg("It has been five seconds since foo_function was executed.")
     call ReleaseTimer(GetExpiredTimer()) // Release the timer to free up memory
 endfunction

 function foo_function takes nothing returns nothing
     local timer t=NewTimer() // Grabs a new timer from a stack.
     call TimerStart(t, 5.00, false, function Handlerfunc)
     set t=null // It's a handle so it must be nulled. 
 endfunction

TimerUtils uses a different system than normal timers for increased efficiency. However, the best part of TimerUtils is two more functions it provides, [ljass]SetTimerData[/ljass] and [ljass]GetTimerData[/ljass]. Here's yet another timer example with those functions.
JASS:
// Example #6
 function Handlerfunc takes nothing returns nothing
     local integer value=GetTimerData(GetExpiredTimer())
     call BJDebugMsg(I2S(value)) // Displays "10"
     call ReleaseTimer(GetExpiredTimer())
 endfunction

 function foo_function takes nothing returns nothing
     local timer t=NewTimer()
     call SetTimerData(t, 10)
     call TimerStart(t, 5.00, false, function Handlerfunc)
     set t=null
 endfunction

You can also use structs, however. You see, structs are really just integers. So you can pass any struct to [ljass]SetTimerData[/ljass], like this.
JASS:
// Example #7
 struct Data
     unit unit_pointer
 endstruct

 function Handlerfunc takes nothing returns nothing
     local timer t=GetExpiredTimer()
     local Data data=GetTimerData(t)
     // Data data = the same data as in the previous function ...
     call DestroyTimer(t)
     set t=null
 endfunction

 function foo_function takes nothing returns nothing
     local timer t=CreateTimer()
     local Data data=Data.create()
     set data.unit_pointer=CreateUnit(...)
     call SetTimerData(t, data)
     set t=null
 endfunction

You can now store all your spell information in the struct and use TimerUtils, so making timed effects is easy.

Key Timers 2
A bit after the time Vexorian wrote TimerUtils, Jesus4Lyf entered the timer industry with his own attempt at a timer system. His used some complicated method to handle timers all differently. The result was Key Timers. Cohadar hounded him some more and he came up with an even more efficient system, Key Timers 2. Abbreviated as KT2 or KT, it handled low and high period timers differently. At its time it may have been better than TimerUtils, but now it's only useful for people with weird interface fetishes and for compatibility (read: new content should use TimerUtils) so this is only one example for those of you who want to use the interface.
JASS:
// Example #8
 struct Data
     integer tick
 endstruct

 function Handlerfunc takes nothing returns nothing
     local Data data=KT_GetData()
     set data.tick=data.tick+1
     call BJDebugMsg(I2S(data.tick))
     if (data.tick==5) then
         return true // return true = stop periodic
     endif
     return false // return false = keep executing
 endfunction

 function foo_function takes nothing returns nothing
     call KT_Add(function Handlerfunc, Data.create(), 5.00) // attach a new data to the timer, and run Handlerfunc every five seconds.  Notice you don't need to create a timer
 endfunction

Timer32
Some time after modules came out, Jesus4Lyf thought it'd be cool to make an object oriented timer system or something, I don't really know why, but he felt like making some cool interface I guess. He pretty much succeeded and made the super kinky Timer32 (T32). It's the most efficient timer system to date and very useful for writing your code inside of a struct. The idea is that if you have a method called periodic in your struct and you call [ljass].startPeriodic[/ljass] it will be run every 0.03125 seconds. However, the period can be changed so you use the public variable [ljass]T32_PERIOD[/ljass] instead. You also get [LJASS]T32_FPS[/ljass] (1/T32_PERIOD) to use to calculate when a whole second has gone by. It's easier to understand with an example.
JASS:
// Example #9
 struct Data
     integer tick=0

     private method periodic takes nothing returns nothing
         if (this.tick*T32_PERIOD==1) then
             call BJDebugMsg("It has been a whole second")
             call this.stopPeriodic() // stop the periodic 
         endif
         set this.tick=this.tick+1
     endmethod

     private static method onInit takes nothing returns nothing
         local thistype this=thistype.create()
         call this.startPeriodic() // start calling .periodic on "this" every T32_PERIOD
     endmethod

     implement T32x // <-- That sets up T32 for the struct.
 endstruct


Conclusion
At this point you should know how to use timers, with either vanilla JASS or also with vJASS. Here are some additional links for you to read from:

Rising_Dusk's Timer Tutorial — Click Me!
Viikuna's Timer Tutorial — Click Me!
Weep's GTS — Click Me!

~TIMERS ARE COOL~
 
Key Timers 2
A bit after the time Vexorian wrote TimerUtils, Jesus4Lyf entered the timer industry with his own attempt at a timer system. His used some complicated method to handle timers all differently. The result was Key Timers. Cohadar hounded him some more and he came up with an even more efficient system, Key Timers 2. Abbreviated as KT2 or KT, it handled low and high period timers differently. At its time it may have been better than TimerUtils, but now it's only useful for people with weird interface fetishes and for compatibility (read: new content should use TimerUtils) so this is only one example for those of you who want to use the interface.
Love it. :p (Did you know I think KT2 is still actually the fastest timer system for most spells? Problem is, who cares! XD)

>He pretty much succeeded and made the super kinky Timer32 (T32). It's the most efficient timer system to date and very useful for writing your code inside of a struct
Fix'd. It's still the case, right? I haven't missed anything..? :)
T32 basically slaughters everything in efficiency.

>I don't really know why
That's why.

Edit:
>came up with an even more efficient system
Lol, actually, here's some trivia. That's untrue. KT1 is faster than KT2, it just has an even worse interface! KT1 basically is T32, T32 is KT1 with a much better interface. Cohadar's tips which led to KT2 were interface oriented, and gave no efficiency improvements. Except in the niche case for which KT2 is still the fastest, maybe.

But yeah, you basically use T32 and TU, if you're looking at efficiency. :)
 
I was hoping for a response like that :D

I'll throw that in about T32 being efficient and all that.

Hopefully you can end the scourge of lack of approving of like anything for months cursed upon this forum.
 
What about TimerQueue? : ). It's another viable option if you have static code and a constant time or a set of constant times. It's also a normal practice in regular coding (many people use timer queues rather than regular timers in order to decrease overhead).

Just a thought ;P. You could just go over the concept of a timer queue rather than the system I wrote =), but timer queues are things that are actually used in the real world ;D.
 
at your example 7:

JASS:
call TimerSetData(t, data)

// >>

call SetTimerData(t, data)
 
>TimerSetData
fixed

>apple juice
orange juice costs 3
 
Yo dude, this is a good tutorial! Newcomers would really benefit from this, since it clearly explains what a timer is and also the usage of timers and timer attachments.

Here are some of my suggestions and clarifications:

FIGURE 1
VANILLA TIMERS
The simplest timer example has three important parts to it.

* The Declaration — The creation of the timer.
* Life of a Timer — Entering the duration for the timer to run and starting it.
* The Decline — Destroying the timer and nulling it.

Timer Phasing
  • Declaration : request warcraft for a timer object.
  • Countdown phase : start the timer and set it's countdown duration; specify if repeating or not, specify the code it will execute after it's countdown duration.
  • Execution phase: execute the specified code.
  • Destruction phase: request warcraft to remove the timer.
  • [1 Optional]De-referencing phase: set all locally defined pointer variable to null to avoid handle reference leaks.
Note: setting the timer variable to null is not part of the timer's phases, it should be discussed on a subject about the locally defined variable bug. But it's inclusion would be good too so the [1] list element was added

[EXAMPLE CODE HERE]

Timer Phasing but with Attachment
  • Declaration : request warcraft for a timer object.
  • Attachment : Attach a struct instance to the timer.
  • Countdown : start the timer and set it's countdown duration; specify if repeating or not, specify the code it will execute after it's countdown duration.
  • Execution : execute the specified code.
  • Destruction phase: request warcraft to remove the timer.
  • [1 Optional]De-referencing : set all locally defined pointer variable to null to avoid handle reference leaks.

[TimerUtils Sample Here]
[KeyTimers2 Sample Here]
[Timer32 Sample Here]
[TimerQueue Sample Here] <-- If you would want to add it?


[Info for TimerUtils]
[Info for KT2]
[Info for T32]
[Info for TimerQueue]

FIGURE 2
TimerUtils uses a different system than normal timers for increased efficiency. However, the best part of TimerUtils is two more functions it provides, SetTimerData and GetTimerData. Here's yet another timer example with those functions.

TimerUtils uses a stack data structure implementation as storage for warcraft's native timers, having a better resource management as it's main feature, not increased processing efficiency.

This was done primarily because setting local timer pointers to null sometimes causes jass's internal handle pointer stack to get corrupted (double free?). Dunno if that still applies now since the engine's been patched.
JASS:
function TimerSample takes nothing returns nothing
    local timer t=CreateTimer()
    set t=null //&lt;------------ causes pointer corruption?
endfunction


Secondly, allocation, deallocation and monitoring (all of which are done internally by the engine) of multiple handles consume a good amount of processing power, which decreases the game's frame rate. (which is inversely proportional to a player's rage meter)

Lastly, it implements a three (orange, red and blue; red: IIRC, 'twas grim's or dusk's suggestion to use pre-loaded timers created at initialization; orange:Used to use gamecache but is now replaced with wc3's hashtable) hash function implementation, which should be used to store integers. A long time ago, there was no jasshelper and we (I) used this for parallel arrays (probably a proto-struct data structure?) and most jassers used kattana's handle vars, which utilized gamecache as a hashtable, but the thing about it is it takes strings as keys, so it needs more function calls to store the handle Id's of handle types. And when jasshelper was available, it was self-evident that its use would be for structs. 'Tis accessible through the SetTimerData and GetTimerData function.

Oh, when the system was still part of CSSafety, it didn't include a double free protection.

FIGURE 3
Timer Dialogues could be included too, since its related to the timer.

That's all! LOL :thup:​
 
I don't think the 'phases' need to be tweaked, after all, it's just a three point comment for people to remember.

I think the way it's organized with Info=>Example, Info2=>Example2 is better, so people don't have to look from place to place. It's more like a book.

I'm pretty sure nulling doesn't do anything funky anymore.

Yeah, I know how a stack works :p (If I ever finish my data structure tutorial ... )

And timer Dialogues are pretty much out of the subject even though I guess they sort of relate.

Thanks for all the suggestions.
 
I still think you should talk about timer queues as that is quite a viable solution for some problems ; |.

A lot of spell makers are now moving to using TimerUtils, TimerQueue, and T32 for their timer solutions depending on what they need.

Again, don't care about mentioning the system, just the idea behind it and possibly how to do it =).

Code:
Timer 1-
    15
    14
    13
    12
    11
    10
    09
    08
    07
    06
    05 Timer 2- (15-5 = 10)
    04 14
    03 13 Timer 3 (15-(3+10) = 2)
    02 12 14
    01 11 13
    00 10 12
       09 11
       08 10
       07 09
       06 08
       05 07
       04 06
       03 05
       02 04
       01 03
       00 02
 
Name a point in which T32 will be too inefficient.

I doubt if I did a test with T32 and a Timer Queue, one would be able to spot a difference between them.
 
A Timer Queue is for longer time periods. If you did the same thing T32 does for a period of like 10 seconds, it'd run every 10 seconds all instances. What if you add an instance 5 seconds after the first?

That is why you use a timer queue, so that you can run them like they would as regular timers =).
 
If T32 is too inaccurate at a longer period, use TimerUtils. I don't see the problem.
 
I'm saying if you had a period of 10 seconds, you wouldn't use T32.

And TimerUtils isn't near as good as a timer queue when dealing with constant periods and you know it >: P.
 
This is great but it should be mentioned in the title that this is for Advanced Users, or, at the very least, those with JASS knowledge.

Change the title to something along the lines of "Timers in JASS", because this tutorial will be featured on the WET website alongside beginner tutorials.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    A probate is usually done with a will, yes? If so I am sorry for your loss
    +1
  • The Helper The Helper:
    Yeah Tom, me too sorry for your loss buddy my mom told me she finds out her olds friend died from Google searching them. She had not talked to one of her old friends in a year and found out she died from Google. Also another one in the same session. RIP all of them my sincere condolences Tom
    +1
  • Varine Varine:
    We have some elderly guests that regularly come hang out at the bar at the end of the night, and every once in a while we don't see someone for a few weeks and then someone shows up with their obituary.
  • Varine Varine:
    We usually let them do their memorials there in the morning if they want to and I'll make them some snacks and drinks. There was one guy named Tom that came in like every night and would sit by himself and get a bunch of soup and a glass of wine. idk why but he LOVED our fucking soup, like he would order a fucking quart of it at a time and would always get so sad when we stop doing it for the summer.
    +1
  • Varine Varine:
    But he also loved our calamari, which is another thing I hate but it sells super well so I can't change it. There was one day he came in and was asking me how to make it, because he tried to at home once in the off season when we stop running it and he really wanted it lol
  • Varine Varine:
    I think he's one of the only people I've made recipes for for free because he really wanted a broccoli cheddar, and it was like dude I don't have a recipe, it's just whatever I have, but here, this is how you do it
  • Varine Varine:
    I don't think he ever figured out how to do the calamari in a pan though, like idk how to do that either. He was afraid of the at home deep fryers though and it's like yeah, that's fair, I am too
  • Varine Varine:
    He was just such a sweet old man, we had two servers pregnant and they held a baby shower together, he was soooooo fucking excited to get to see a baby. Unfortunately he died a month or so before they were born
  • The Helper The Helper:
    So I decided to Google some people that I had not seen or heard from in a while and sure enough one of my old best friends, we had a falling out years ago but whatever, find out he died of Pancreatic Cancer in January. I have also lost a few of my closer acquaintances from growing up the last year. Getting old - people die - I kinda thought it was going to be this way a few years ago....
    +2
  • The Helper The Helper:
    Forum running super slow again
  • Ghan Ghan:
    Not really clear from the stats as to what is causing the slowness.
  • Ghan Ghan:
    We get a lot of guest traffic so it may just be the load is getting too high and not from any particular source.
  • Ghan Ghan:
    Looks like the server is maxed out on CPU.
  • Ghan Ghan:
    Oh it looks like a lot of the traffic is Silkroad Forums. That domain isn't protected by Cloudflare.
  • Ghan Ghan:
    But the old Silkroad site is still on its own server. I just had a test site set up on this server for it.
  • Ghan Ghan:
    I just disabled that test site. Let's see if that helps the load.
  • Ghan Ghan:
    Looks much better already.
  • The Helper The Helper:
    I had actually forgot about the Silkroad site. I had asked
  • The Helper The Helper:
    SD Ryoko about it and he said the couple of people left on there really like it, that was a few years ago, maybe I should check back
  • jonas jonas:
    I guess when you're getting old, and the last day of soup season draws near, you start wondering
  • jonas jonas:
    will I make it to the start of the next season? or was this the last time I'll ever have my favorite dish?
  • The Helper The Helper:
    I am doing my first Vibe Coding project. In installed the environment and tools according to instructions but it is all chat doing this for me at my direction. It is fun really and holy shit I might finish in 2 hours what it would have taken a day to in my Access and this would be an electron app complete new
  • Ghan Ghan:
    Good stuff.
  • Ghan Ghan:
    Just make sure it is secure. :)
  • The Helper The Helper:
    It will only be on internal network

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials
      Top