- Joined
- Jul 20, 2018
- Messages
- 177
Hi everyone!
There is a small research on how timer API works in WarCraft 3. Tests were made on 1.26a and 1.32.10 versions.
I have attached an archive with:
There is a small research on how timer API works in WarCraft 3. Tests were made on 1.26a and 1.32.10 versions.
TimerGetTimeout
Whenever passed timer is started by TimerStart, this function returns the last passed timeout value. Example: timer is started by TimerStart with x seconds timeout. TimerGetTimeout returns x for this timer. After some time this timer is started again by TimerStart for y seconds. After that TimerGetTimeout returns y for it.TimerGetRemaining
Returns remaining time of passed timer while timer is running or paused by PauseTimer. But after non-periodic timer expires, this function returns remaining time that was at last pause of this timer. Example: non-periodic timer was last time paused when x seconds remains to expire. When this timer expired, TimerGetRemaining would return x.TimerGetElapsed
If passed timer is paused or has expired, this function returns (TimerGetTimeout - TimerGetRemaining). If passed timer was resumed by ResumeTimer, this function returns amount of time elapsed after last resuming.PauseTimer
Pauses passed timer. If passed timer is non-periodic, after this timer expires, TimerGetRemaining returns value that was at last pause. Does not have such effect on periodic timers.ResumeTimer
Has no effect if passed timer is running. If passed timer is paused or has expired, launches it for TimerGetRemaining, and after this time is elapsed, launches it again for TimerGetTimeout. After that passed timer is stopped even if it is periodic.DestroyTimer
Destroys passed timer. Destroying does not pause timer, so if call of its callback is scheduled, then callback is called with GetElapsedTimer being null. Thus, such situation can happen: timer is destroyed, all data in hashtables connected with this timer is cleared, but its callback is still called.How to overcome issues
TimerGetRemaining
Track whether timer is expired. When it is expired, return 0, otherwise value from TimerGetRemaining.TimerGetElapsed
UseTimerGetTimeout - TimerGetRemaining
, where TimerGetRemaining is a fixed version.PauseTimer
UseTimerStart(t, 0.0, false, null)
to pause non-periodic timers or use fixed version of TimerGetRemaining.ResumeTimer
Just write your own function for resuming.DesroyTimer
Call PauseTimer before destroying.I have attached an archive with:
- a test map where you can easily check statements above;
- message log files generated by test map on 1.26a and 1.32.10 versions;
- raw timer library on Lua where all issues are resolved.
JASS:
function DebugMsg takes string s returns nothing
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0., 0., 5., s)
endfunction
function R2SX takes real r returns string
return R2SW(r, 0, -1)
endfunction
JASS:
function GetT1Conf takes nothing returns nothing
call ClearTextMessages()
call DebugMsg("\nRemaining: " + R2SX(TimerGetRemaining(T1)))
call DebugMsg("Elapsed: " + R2SX(TimerGetElapsed(T1)))
call DebugMsg("Timeout: " + R2SX(TimerGetTimeout(T1)))
endfunction
function callback1 takes nothing returns nothing
set Counter1 = Counter1 + 1
call DebugMsg("\nRemaining in callback: " + R2SX(TimerGetRemaining(T1))) // outputs time remaining that was during last pause.
call DebugMsg("Elapsed in callback: " + R2SX(TimerGetElapsed(T1))) // outputs TimerGetTimeout() - TimerGetRemaining().
call DebugMsg("Timeout in callback: " + R2SX(TimerGetTimeout(T1))) // correct.
call DebugMsg(I2S(Counter1))
endfunction
function f5 takes nothing returns nothing
call TimerStart(T1, 1., Periodic, function callback1)
call DebugMsg("\nResumed")
call GetT1Conf()
endfunction
function f4 takes nothing returns nothing
call PauseTimer(T1)
call DebugMsg("\nPaused")
call GetT1Conf()
call TimerStart(T3, 1., false, function f5)
endfunction
function f3 takes nothing returns nothing
call TimerStart(T1, 8., Periodic, function callback1)
call TimerStart(T3, 2., false, function f4)
endfunction
function f2 takes nothing returns nothing
call TimerStart(T1, 1., Periodic, function callback1)
call DebugMsg("\nResumed")
call GetT1Conf()
call TimerStart(T3, 2., false, function f3)
endfunction
function f1 takes nothing returns nothing
call PauseTimer(T1)
call DebugMsg("\nPaused")
call GetT1Conf()
call TimerStart(T3, 1., false, function f2)
endfunction
function TimerTest_Actions takes nothing returns nothing
call TimerStart(T1, 8., Periodic, function callback1)
call TimerStart(T3, 1., false, function f1)
endfunction
function InitTrig_TimerTest takes nothing returns nothing
set gg_trg_TimerTest = CreateTrigger( )
call TriggerRegisterTimerEvent(gg_trg_TimerTest, 1., false)
call TriggerAddAction( gg_trg_TimerTest, function TimerTest_Actions )
endfunction
globals
constant timer T1 = CreateTimer()
constant timer T3 = CreateTimer()
integer Counter1 = 0
boolean Periodic = false // no bugs for 'true' value.
endglobals
JASS:
function callback3 takes nothing returns nothing
call ClearTextMessages()
call DebugMsg("\n\nRemaining: " + R2SX(TimerGetRemaining(T1)) + "\nElapsed: " + R2SX(TimerGetElapsed(T1)) + "\nTimeout: " + R2SX(TimerGetTimeout(T1)))
endfunction
function callback1 takes nothing returns nothing
set Counter1 = Counter1 + 1
call DebugMsg("\nRemaining in callback: " + R2SX(TimerGetRemaining(T1))) // outputs 0.
call DebugMsg("Elapsed in callback: " + R2SX(TimerGetElapsed(T1))) // outputs 8.
call DebugMsg("Timeout in callback: " + R2SX(TimerGetTimeout(T1))) // outputs 8.
call DebugMsg("Counter: " + I2S(Counter1))
call ResumeTimer(T1) // Runs timer for 0 and then immediately for timeout value, then again for 0 and so on.
// Call is 'zero' run is ignored since timer is running.
endfunction
function TimerTest_Actions takes nothing returns nothing
call TimerStart(T1, 8., false, function callback1)
call TimerStart(T3, 1. / 2., true, function callback3)
endfunction
function InitTrig_TimerTest takes nothing returns nothing
set gg_trg_TimerTest = CreateTrigger( )
call TriggerRegisterTimerEvent(gg_trg_TimerTest, 1., false)
call TriggerAddAction( gg_trg_TimerTest, function TimerTest_Actions )
endfunction
globals
constant timer T1 = CreateTimer()
constant timer T3 = CreateTimer()
integer Counter1 = 0
endglobals
JASS:
function callback1 takes nothing returns nothing
set Counter1 = Counter1 + 1
call DebugMsg("\nRemaining in callback: " + R2SX(TimerGetRemaining(T1))) // outputs time remaining that was during last pause.
call DebugMsg("Elapsed in callback: " + R2SX(TimerGetElapsed(T1))) // outputs TimerGetTimeout() - TimerGetRemaining().
call DebugMsg("Timeout in callback: " + R2SX(TimerGetTimeout(T1))) // outputs last given timeout.
call DebugMsg("Counter: " + I2S(Counter1))
endfunction
function callback3 takes nothing returns nothing
call ClearTextMessages()
set Counter3 = Counter3 + 1
call DebugMsg("\nRemaining: " + R2SX(TimerGetRemaining(T1)))
call DebugMsg("Elapsed: " + R2SX(TimerGetElapsed(T1))) // when T1 paused, outputs (TimerGetTimeout() - TimerGetRemaining()).
// When resumed, outputs amount of time after last resuming.
// The same behaviour for periodic timer.
call DebugMsg("Timeout: " + R2SX(TimerGetTimeout(T1))) // outputs last given timeout.
if Counter3 == 3 or Counter3 == 7 or Counter3 == 17 then
call PauseTimer(T1)
call DebugMsg("\nPaused")
elseif Counter3 == 5 or Counter3 == 15 or Counter3 == 19 then
call ResumeTimer(T1) // Launches T1 for TimerGetRemaining(), and after T1 expires, T1 will be started again for TimerGetTimeout().
call DebugMsg("\nResumed")
elseif Counter3 == 9 then
call TimerStart(T1, 1., false, function callback1)
call DebugMsg("\nStarted again")
elseif Counter3 == 32 then
call PauseTimer(T3)
endif
endfunction
function TimerTest_Actions takes nothing returns nothing
call TimerStart(T1, 8., false, function callback1)
call TimerStart(T3, 1. / 2., true, function callback3)
endfunction
function InitTrig_TimerTest takes nothing returns nothing
set gg_trg_TimerTest = CreateTrigger( )
call TriggerRegisterTimerEvent(gg_trg_TimerTest, 1., false)
call TriggerAddAction( gg_trg_TimerTest, function TimerTest_Actions )
endfunction
globals
constant timer T1 = CreateTimer()
constant timer T3 = CreateTimer()
integer Counter1 = 0
integer Counter3 = 0
endglobals
JASS:
function callback3 takes nothing returns nothing
call ClearTextMessages()
set Counter3 = Counter3 + 1
call DebugMsg("\nRemaining: " + R2SX(TimerGetRemaining(T1)))
call DebugMsg("Elapsed: " + R2SX(TimerGetElapsed(T1)))
call DebugMsg("Timeout: " + R2SX(TimerGetTimeout(T1)))
if DO then
if Counter3 == 20 then
call ResumeTimer(T1) // Launches T1 for TimerGetRemaining() (it's0 because T1 was paused in destination function),
// and after T1 expires, T1 will be started again for last given timeout.
call DebugMsg("\nResumed")
set DO = false
endif
endif
endfunction
function callback1 takes nothing returns nothing
set Counter1 = Counter1 + 1
call DebugMsg("\nRemaining in callback: " + R2SX(TimerGetRemaining(T1))) // everything fine here
call DebugMsg("Elapsed in callback: " + R2SX(TimerGetElapsed(T1)))
call DebugMsg("Timeout in callback: " + R2SX(TimerGetTimeout(T1)))
call DebugMsg("Counter: " + I2S(Counter1))
call PauseTimer(T1)
endfunction
function TimerTest_Actions takes nothing returns nothing
call TimerStart(T1, 8., true, function callback1)
call TimerStart(T3, 1. / 2., true, function callback3)
endfunction
function InitTrig_TimerTest takes nothing returns nothing
set gg_trg_TimerTest = CreateTrigger( )
call TriggerRegisterTimerEvent(gg_trg_TimerTest, 1., false)
call TriggerAddAction( gg_trg_TimerTest, function TimerTest_Actions )
endfunction
globals
constant timer T1 = CreateTimer()
constant timer T3 = CreateTimer()
boolean DO = true
integer Counter1 = 0
integer Counter3 = 0
endglobals
JASS:
function callback3 takes nothing returns nothing
call ClearTextMessages()
call DebugMsg("\n\nNonperiodic Timer\nRemaining: " + R2SX(TimerGetRemaining(T1)) + "\nElapsed: " + R2SX(TimerGetElapsed(T1)) + "\nTimeout: " + R2SX(TimerGetTimeout(T1)) + "\nData: " + I2S(R2I(TimerGetRemaining(T1) + 0.5)))
call DebugMsg("\nPeriodic Timer\nRemaining: " + R2SX(TimerGetRemaining(T2)) + "\nElapsed: " + R2SX(TimerGetElapsed(T2)) + "\nTimeout: " + R2SX(TimerGetTimeout(T2)) + "\nData: " + I2S(R2I(TimerGetRemaining(T2) + 0.5)))
endfunction
function callback2 takes nothing returns nothing
call DebugMsg("\nRemaining in callback2: " + R2SX(TimerGetRemaining(T2)))
call DebugMsg("Elapsed in callback2: " + R2SX(TimerGetElapsed(T2)))
call DebugMsg("Timeout in callback2: " + R2SX(TimerGetTimeout(T2)))
endfunction
function callback1 takes nothing returns nothing
call DebugMsg("\nRemaining in callback1: " + R2SX(TimerGetRemaining(T1))) // outputs saved value.
call DebugMsg("Elapsed in callback1: " + R2SX(TimerGetElapsed(T1))) // ouputs 8 - saved value.
call DebugMsg("Timeout in callback1: " + R2SX(TimerGetTimeout(T1))) // outputs 8.
call ResumeTimer(T1) // Runs timer for saved value, then for timeout value, then for saved and so on.
endfunction
function TimerExploit takes timer t, real period, integer data, boolean periodic, code callb returns nothing
call TimerStart(t, data, false, null)
call PauseTimer(t)
call TimerStart(t, period, periodic, callb)
endfunction
function Trig_TimerExploitTest_Actions takes nothing returns nothing
call TimerExploit(T1, 8., 5, false, function callback1)
call TimerExploit(T2, 8., 6, true, function callback2) // exploit does not work for periodic timers.
call TimerStart(T3, 1. / 2., true, function callback3)
endfunction
function InitTrig_TimerExploitTest takes nothing returns nothing
set gg_trg_TimerExploitTest = CreateTrigger( )
call TriggerRegisterTimerEvent(gg_trg_TimerExploitTest, 1., false)
call TriggerAddAction( gg_trg_TimerExploitTest, function Trig_TimerExploitTest_Actions )
endfunction
globals
constant timer T1 = CreateTimer()
constant timer T2 = CreateTimer()
constant timer T3 = CreateTimer()
endglobals
Attachments
Last edited: