tooltiperror
Super Moderator
- Reaction score
- 232
Timers
([ljass]type timer extends handle[/ljass])
([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.
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.
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.
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.
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.
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.
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.
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.
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.
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:
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.
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 039;A000039;
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:
~TIMERS ARE COOL~