HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Potential Timer Attaching Method

07-07-2008, 12:19 PM#1
Captain Griffen
Okay, I've been fiddling around with timers a lot the last day or so, and come up with what appears to be a safe, reliable and lightning fast method of attaching a struct to a timer.

Basically, it involves adding a slight amount to the timeout. Obviously, this requires that you know the timeout at both ends to exploit it, but that's normally the case I find (if not always). Due to my method of type casting between int and real, the amount of increase is minimised while maxisiming safety.

Attaching values between 0 and 9000 results in no higher than 0.095% increase in timeout according to my tests (see below). Attaching non-structs is not advisable. Timeout wise, it bugged out when I took it to 10 million, but one million seems to be fine (and if you need more than 11 days timeout to attach something to...well, I dunno).

Pros:
- Very fast (should get inlined by JASSHelper).
- Easy-ish to use.

Cons:
- Slight inaccuracies in timing, which increases as the value attached increases.
- Requires that you know the timeout value at both ends.


Collapse JASS:
library CGTimerLib

    function i2r takes integer i returns real
        return i
        return 0.
    endfunction

    function r2i takes real r returns integer
        return r
        return 0
    endfunction

    function StartTimerStruct takes timer t, integer itime, integer value, boolean repeat, code func returns nothing
        call TimerStart(t, i2r(itime+value), repeat, func)
    endfunction
    
    function GetTimerStruct takes timer t, integer itime returns integer
        return r2i(TimerGetTimeout(t))-itime
    endfunction

endlibrary

Example usage:

Collapse JASS:
library Example initializer Init
    
    globals
        private real timeout = 5.
        private integer itimeout
    endglobals

    private function Example takes nothing returns nothing
        call BJDebugMsg("Random int was: " + I2S(GetTimerStruct(GetExpiredTimer(), itimeout)))
    endfunction
    
    private function Init takes nothing returns nothing
        set itimeout = r2i(timeout)
        call StartTimerStruct(CreateTimer(), itimeout, GetRandomInt(0, 8191), true, function Example)
    endfunction
    
endlibrary

The precalculating of r2i(timeout) speeds it up, but is also there because Blizzard fails at coding, so I had to avoid a random bug where JASS forgets how to do maths around some two type casts or something.

If you want to take a look at my testing code, feel free (NB: I know this is ugly, but it does the job of testing it):

Test Code

Collapse JASS:
globals
    real maxabove = 0.
    real minbelow = 0.
    real sum = 0.
    integer count = 0
endglobals

library Print initializer Init
    private function Print takes nothing returns nothing
        call BJDebugMsg("MAX: " + R2S(maxabove*100) + "%")
        call BJDebugMsg("MIN: " + R2S(minbelow*100) + "%")
        call BJDebugMsg("MEAN: " + R2S(sum*100/count) + "%")
    endfunction
    private function Init takes nothing returns nothing
        call TimerStart(CreateTimer(), 0.01, false, function Print)
    endfunction
endlibrary

//! textmacro Test takes TIME, VALUE

library A$VALUE$ initializer Init requires CGTimerLib

    globals
        private integer ITIMEOUT = 0
    endglobals

    private function testimon takes nothing returns nothing
        if $VALUE$ != GetTimerStruct(GetExpiredTimer(), ITIMEOUT) then
            call BJDebugMsg("ERROR: " + "$VALUE$" + "___" + I2S(GetTimerStruct(GetExpiredTimer(), ITIMEOUT)))
        endif
    endfunction

    private function Init takes nothing returns nothing
        local timer t = CreateTimer()
        set ITIMEOUT = r2i($TIME$)
        call StartTimerStruct(t, ITIMEOUT, $VALUE$, true, function testimon)
        if TimerGetRemaining(t) / $TIME$ - 1. > maxabove then
            set maxabove = TimerGetRemaining(t) / $TIME$ - 1.
        endif
        if TimerGetRemaining(t) / $TIME$ - 1. < minbelow then
            set minbelow = TimerGetRemaining(t) / $TIME$ - 1.
        endif
        if TimerGetRemaining(t) / $TIME$ - 1. > 0.005 then
            call BJDebugMsg(R2S(TimerGetRemaining(t)/$TIME$) + ",  " + "$VALUE$")
        endif
        if $VALUE$ != r2i(TimerGetTimeout(t))-ITIMEOUT then
            call BJDebugMsg("ERROR: " + "$VALUE$" + "___" + I2S(GetTimerStruct(t, ITIMEOUT)))
        endif
        set sum = sum + TimerGetRemaining(t) / $TIME$ - 1.
        set count = count + 1
    endfunction

endlibrary

//! endtextmacro

//! runtextmacro Test("0.1", "5")
//! runtextmacro Test("0.1", "8190")
//! runtextmacro Test("0.001", "8191")
//! runtextmacro Test("0.01", "1337")
//! runtextmacro Test("200", "543")
//! runtextmacro Test("155", "18")
//! runtextmacro Test("122", "32")
//! runtextmacro Test("0.21", "24")
//! runtextmacro Test("0.15", "0")
//! runtextmacro Test("15", "1")
//! runtextmacro Test("1.5", "2")
//! runtextmacro Test("0.6", "274")
//! runtextmacro Test("28", "375")
//! runtextmacro Test("64", "65")
//! runtextmacro Test("282", "654")
//! runtextmacro Test("650", "4")
//! runtextmacro Test("1000000", "48")
//! runtextmacro Test("1001000", "9000")
//! runtextmacro Test("1002000", "8900")
//! runtextmacro Test("432", "8000")
//! runtextmacro Test("42", "8193")



Anyway, feel free to try and break it (while staying within the struct range). And StartTimerStruct needs a new name, but I don't know what.
07-07-2008, 01:21 PM#2
MindWorX
AWESOME!!!!11!!!
07-07-2008, 01:29 PM#3
cohadar
I am always amazed at the possibilities of human imagination.
07-07-2008, 02:44 PM#4
DiscipleOfLife
Expand JASS:

This is almost a year old idea. First public appearance can be found here as far as I know. HINDY used the same idea in some version of SEE too.
That typecast thing you do with this is actually new though.
07-07-2008, 02:55 PM#5
Captain Griffen
Quote:
Originally Posted by DiscipleOfLife
This is almost a year old idea. First public appearance can be found here as far as I know. HINDY used the same idea in some version of SEE too.
That typecast thing you do with this is actually new though.

Older than a year old. I had the idea way back but never actually released or used it due to it not being safe enough outside specific usage. Typecasting allows for the best possible values for a very small cost.
07-07-2008, 04:14 PM#6
Vexorian
Quote:
Originally Posted by DiscipleOfLife
Expand JASS:

This is almost a year old idea. First public appearance can be found here as far as I know. HINDY used the same idea in some version of SEE too.
That typecast thing you do with this is actually new though.
Man, griffen invented that one before that site copied it . Now Griffen is 'improving' it. Please see the subtle differences in the code....
07-07-2008, 04:22 PM#7
Captain Griffen
Vex, that code uses a very different method to TimerAttach. When I was investigating timers way back when I thought up the TimerAttach method (start, pause, start; inspired from the pause trick on units), I experimented with the editing values method, but I don't think I ever went public with it (since it didn't work well enough to be reliable without far too much requirement on sensible use). So they didn't copy it, probably just came up with it independantly (after me, but actually used it, but I'm fairly sure people had thought of it before me as well).

TimerAttach doesn't break, unless you go to like 10 digits on the attached integer, and is perfectly accurate, but only works with non-periodics. The method used in TimerStartWithUserData is either unreliable or adjusts the timer by a significant amount. StartTimerStruct (needs a new name that isn't a blatant lie) improves significantly upon both methods.
07-07-2008, 05:32 PM#8
cohadar
Well this is all pointless in the long run since attaching to timers is pointless per se.
07-07-2008, 05:42 PM#9
grim001
As much as people keep saying that, I always manage to find a few applications where attaching to timers is the preferred method.
07-07-2008, 06:16 PM#10
cohadar
Quote:
Originally Posted by grim001
As much as people keep saying that, I always manage to find a few applications where attaching to timers is the preferred method.

Please do enlighten us.
07-07-2008, 08:23 PM#11
Toadcop
Quote:
Please do enlighten us.
alot of events with different timeouts and callback functions ? or low period precise timeouts.
now we know 100500 ways to attach something to timers awesome...
// in fact it doesnt matter but gj.
07-07-2008, 10:26 PM#12
cohadar
ABCT + TT = no attaching.
(no code inside libraries does not count, only what user sees.)
07-07-2008, 10:27 PM#13
Captain Griffen
Quote:
Originally Posted by cohadar
ABCT + TT = no attaching.
(no code inside libraries does not count, only what user sees.)

That makes no sense. Even if it is in a library, it's still attaching.
07-08-2008, 12:49 AM#14
cohadar
Quote:
Originally Posted by Captain Griffen
That makes no sense. Even if it is in a library, it's still attaching.

That is the same reasoning Assembler programmers used when they were introduced with C and we know how that ended up...
07-08-2008, 08:00 AM#15
Captain Griffen
Quote:
Originally Posted by cohadar
That is the same reasoning Assembler programmers used when they were introduced with C and we know how that ended up...

Except we're still coding both sides of it.