1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Issues with timer functions

Discussion in 'The Lab' started by Prometheus3375, Oct 19, 2018.

  1. Prometheus3375

    Prometheus3375

    Joined:
    Jul 20, 2018
    Messages:
    91
    Resources:
    0
    Resources:
    0
    Hi everyone.

    I do not how too much time to describe everything step by step, that's why only consequences from my tests are written below. Almost all these statement can be proved by running tests that I mention here.
    Tests were made on 1.26a version.


    > TimerGetTimeout()
    Whenever passed timer is started by TimerStart(), this function returns the last passed timeout value. Example: timer was started by TimerStart() with x seconds timeout. This function will return x for this timer. After some time it was started again by TimerStart() for y seconds. After that this function will return y for it.

    > TimerGetRemaining()
    Returns remaining time of 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 expires, TimerGetRemaining() will return x.

    > PauseTimer()
    Pauses passed timer. If passed timer is not periodic, after this timer expires, TimerGetRemaining() function will return 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 timer expires, launches it again for TimerGetTimeout(). After that passed timer is stopped even if it is periodic.

    > TimerGetElapsed()
    If passes 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.

    > DestroyTimer()
    Destroys passed timer. It's better to pause timer before destroying since it can still call its destination function.
    This is the only statement without proof, but still is worth to mention. In my map sometimes was occurring the next situation: timer is destroyed and all references in hash-table are cleared, but for some reason destination function is called with no references (since they were cleared).



    I will appreciate if someone will test it in the latest patch of WarCraft 3 and report any issues revealed to Blizzard.
    When I have learnt about timer exploit (the bug with PauseTimer() that I described above), I realized that it can ruin my map.
    For now I suggest to use
    TimerStart(t, 0., false, null)
    to pause non-periodic timers and do not use ResumeTimer() at all.



    Code (vJASS):
    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
     
    Code (vJASS):
    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
     
    Code (vJASS):
    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
     
    Code (vJASS):
    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
     
    Code (vJASS):
    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
     
    Code (vJASS):
    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
     
     
    Last edited: Oct 22, 2018
  2. Clanzion

    Clanzion

    Joined:
    Jul 4, 2016
    Messages:
    391
    Resources:
    0
    Resources:
    0
    The destroyed time function statement is true and has been known for a while.