Spell TimerUtilsEx

Magthridon96

Member
Reaction score
2
This is a fixed version of Vexorian's TimerUtils.
It allows you to:
- call NewTimer inside module initializers (Useful for elapsed game time events and other things)
- get the timer data from ReleaseTimer (Doesn't break backwards compatibility)
- call NewTimerEx to preset the timer data

This system is 100% backwards compatible.
Just paste this code inside your old TimerUtils library and have fun with these new capabilities ;D

JASS:
library TimerUtilsEx requires optional Table
/*************************************************
*
*   TimerUtilsEx
*   v2.1.0.2
*   By Vexorian, Bribe & Magtheridon96
*
*   Original version by Vexorian.
*
*   Flavors:
*       Hashtable:
*           - RAM:              Minimal
*           - TimerData:        Slow
*
*       Array:
*           - RAM:              Maximal
*           - TimerData:        Fast
*
*   All the functions have O(1) complexity.
*   The Array version is the fastest, but the hashtable
*   version is the safest. The Array version is still
*   quite safe though, and I would recommend using it.
*   The system is much slower in debug mode.
*
*   Optional Requirement:
*       - Table by Bribe
*           - hiveworkshop.com/forums/showthread.php?t=188084
*
*   API:
*   ----
*       - function NewTimer takes nothing returns timer
*           - Returns a new timer from the stack.
*       - function NewTimerEx takes integer i returns timer
*           - Returns a new timer from the stack and attaches a value to it.
*       - function ReleaseTimer takes timer t returns integer
*           - Throws a timer back into the stack. Also returns timer data.
*       - function SetTimerData takes timer t, integer value returns nothing
*           - Attaches a value to a timer.
*       - function GetTimerData takes timer t returns integer
*           - Returns the attached value.
*
*************************************************/
    // Configuration
    globals
        // Use hashtable, or fast array?
        private constant boolean USE_HASH = false
        // Max Number of Timers Held in Stack
        private constant integer QUANTITY = 256
    endglobals
    
    globals
        private timer array tT
        private integer tN = 0
    endglobals
    
    private module Init
        private static method onInit takes nothing returns nothing
            static if not USE_HASH then
                local integer i = QUANTITY
                loop
                    set i = i - 1
                    set tT<i> = CreateTimer()
                    exitwhen i == 0
                endloop
                
                set tN = QUANTITY
            elseif LIBRARY_Table then
                set tb = Table.create()
            endif
        endmethod
    endmodule
    
    // JassHelper doesn&#039;t support static ifs for globals.
    private struct Data extends array
        static if not USE_HASH then
            static integer array data
        endif
        static if LIBRARY_Table then
            // You mad Vexorian?
            static Table tb = 0
        else
            static hashtable ht = InitHashtable()
        endif
        implement Init
    endstruct
    
    // Double free protection
    private function ValidTimer takes integer i returns boolean
        static if LIBRARY_Table then
            return Data.tb.boolean[-i]
        else
            return LoadBoolean(Data.ht, i, 1)
        endif
    endfunction
    
    private function Get takes integer id returns integer
        debug if not ValidTimer(id) then
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, &quot;[TimerUtils]Error: Tried to get data from invalid timer.&quot;)
        debug endif
        static if USE_HASH then
            static if LIBRARY_Table then
                return Data.tb[id]
            else
                return LoadInteger(Data.ht, id, 0)
            endif
        else
            return Data.data[id - 0x100000]
        endif
    endfunction
    
    private function Set takes integer id, integer data returns nothing
        debug if not ValidTimer(id) then
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, &quot;[TimerUtils]Error: Tried to attach data to invalid timer.&quot;)
        debug endif
        static if USE_HASH then
            static if LIBRARY_Table then
                set Data.tb[id] = data
            else
                call SaveInteger(Data.ht, id, 0, data)
            endif
        else
            set Data.data[id - 0x100000] = data
        endif
    endfunction
    
    function SetTimerData takes timer t, integer data returns nothing
        call Set(GetHandleId(t), data)
    endfunction
    
    function GetTimerData takes timer t returns integer
        return Get(GetHandleId(t))
    endfunction
    
    function NewTimerEx takes integer data returns timer
        local integer id
        if tN == 0 then
            static if USE_HASH then
                set tT[0] = CreateTimer()
            else
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, &quot;[TimerUtils]Error: No Timers In The Stack! You must increase &#039;QUANTITY&#039;&quot;)
                return null
            endif
        else
            set tN = tN - 1
        endif
        set id = GetHandleId(tT[tN])
        static if LIBRARY_Table then
            set Data.tb.boolean[-id] = true
        else
            call SaveBoolean(Data.ht, id, 1, true)
        endif
        call Set(id, data)
        return tT[tN]
    endfunction
    
    function NewTimer takes nothing returns timer
        return NewTimerEx(0)
    endfunction
    
    function ReleaseTimer takes timer t returns integer
        local integer id = GetHandleId(t)
        local integer data = 0
        
        // Pause the timer just in case.
        call PauseTimer(t)
        
        // Make sure the timer is valid.
        if ValidTimer(id) then
            // Get the timer&#039;s data.
            set data = Get(id)
            
            // Unmark handle id as a valid timer.
            static if LIBRARY_Table then
                call Data.tb.boolean.remove(-id)
            else
                call RemoveSavedBoolean(Data.ht, id, 1)
            endif
            
            //If it&#039;s not run in USE_HASH mode, this next block is useless.
            static if USE_HASH then
            
                //At least clear hash memory while it&#039;s in the recycle stack.
                static if LIBRARY_Table then
                    call Data.tb.remove(id)
                else
                    call RemoveSavedInteger(Data.ht, id, 0)
                endif
                
                // If the recycle limit is reached
                if tN == QUANTITY then
                    // then we destroy the timer.
                    call DestroyTimer(t)
                    return data
                endif
            endif
            
            //Recycle the timer.
            set tT[tN] = t
            set tN = tN + 1
            
        //Tried to pass a bad timer.
        debug else
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, &quot;[TimerUtils]Error: Tried to release non-active timer!&quot;)
        endif
        
        //Return Timer Data.
        return data
    endfunction

endlibrary

library TimerUtils requires TimerUtilsEx
endlibrary</i>


Here's a strictly Jass version that runs on hashtables (no array version for simplicity... and the lack of static ifs in Jass)

JASS:
//*************************************************
//*
//*   TimerUtilsEx (Jass Version)
//*   v2.1.0.2
//*   By Vexorian, Bribe &amp; Magtheridon96
//*
//*   Original version by Vexorian.
//*   All the functions have O(1) complexity.
//*
//*   Requires:
//*       - TimerUtils_hash (Hashtable)
//*       - TimerUtils_int (Integer)
//*       - TimerUtils_timer (Timer Array)
//*
//*   API:
//*       - function NewTimer takes nothing returns timer
//*           - Returns a new timer from the stack.
//*       - function NewTimerEx takes integer i returns timer
//*           - Returns a new timer from the stack and attaches data to it.
//*       - function ReleaseTimer takes timer t returns integer
//*           - Throws a timer back into the stack. Also returns the timer data.
//*       - function SetTimerData takes timer t, integer value returns nothing
//*           - Attaches a value to a timer.
//*       - function GetTimerData takes timer t returns integer
//*           - Returns the attached value.
//*
//*************************************************

    function SetTimerData takes timer t, integer value returns nothing
        call SaveInteger(udg_TimerUtils_hash,GetHandleId(t),0,value)
    endfunction
    
    function GetTimerData takes timer t returns integer
        return LoadInteger(udg_TimerUtils_hash,GetHandleId(t),0)
    endfunction
    
    function NewTimerEx takes integer i returns timer
        if 0==udg_TimerUtils_int then
            return CreateTimer()
        endif
        set udg_TimerUtils_int = udg_TimerUtils_int - 1
        call SaveInteger(udg_TimerUtils_hash, GetHandleId(udg_TimerUtils_timer[udg_TimerUtils_int]), 0, i)
        return udg_TimerUtils_timer[udg_TimerUtils_int]
    endfunction
    
    function NewTimer takes nothing returns timer
        return NewTimerEx(0)
    endfunction
    
    function ReleaseTimer takes timer t returns integer
        call PauseTimer(t)
        set udg_TimerUtils_timer[udg_TimerUtils_int] = t
        set udg_TimerUtils_int = udg_TimerUtils_int + 1
        return GetTimerData(t)
    endfunction
    
    function InitTrig_TimerUtils takes nothing returns nothing
        set udg_TimerUtils_hash = InitHashtable()
    endfunction



Feel free to comment.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Why the SetTimerData and GetTimerData are wrappers for the private Set and Get functions?
 

Bribe

vJass errors are legion
Reaction score
67
Cause NewTimer and ReleaseTimer use them, but they already have a handle ID. It just just for an optimization to avoid calling the GetHandleId more times than needed.
 

Magthridon96

Member
Reaction score
2
Vexorian is actually working on an update to TimerUtils and he's fixed one of two bugs at this point already, and surprisingly even added NewTimerEx..

It's about time..
If he does that, I'll trash this and the one I posted on the Hive.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Bribe:
1. Cause NewTimer and ReleaseTimer use them, but they already have a handle ID. It just just for an optimization to avoid calling the GetHandleId more times than needed.

1. You do know that GetTimerData is called on every timer expiration (usually 32 times a second) and it using a wrapper is surly not an "optimisation". GetTimerData should really just inline to an array lookup + GetHandleId + subtraction not to a DoNothing + GetHandleId + array lookup + subtraction. The bottleneck is not NewTimer nor ReleaseTimer it's GetTimerData.

Edit:

A jass version using arrays not hashtable:
JASS:
    function SetTimerData takes timer t, integer value returns nothing
        set udg_TimerUtils_data[GetHandleId(t) - udg_TimerUtils_TMI] = value
    endfunction
    
    function GetTimerData takes timer t returns integer
        return udg_TimerUtils_data[GetHadleId(t) - udg_TimerUtils_TMI]
    endfunction
    
    function NewTimerEx takes integer i returns timer
        if 0 == udg_TimerUtils_int then
            call BJDebugMsg(&quot;|cffFF0000error TimerUtils: function NewTimer: out of timers, preload some more? =)|r&quot;)
            return CreateTimer()
        endif
        set udg_TimerUtils_int = udg_TimerUtils_int - 1
        set udg_TimerUtils_data[GetHandleId(udg_TimerUtils_timer[udg_TimerUtils_int]) - udg_TimerUtils_TMI] = i
        return udg_TimerUtils_timer[udg_TimerUtils_int]
    endfunction
    
    function NewTimer takes nothing returns timer
        return NewTimerEx(0)
    endfunction

    function ReleaseTimer takes timer t returns integer
        call PauseTimer(t)
        set udg_TimerUtils_timer[udg_TimerUtils_int] = t
        set udg_TimerUtils_int = udg_TimerUtils_int + 1
        return udg_TimerUtils_data[GetHadleId(t) - udg_TimerUtils_TMI]
    endfunction

    function InitTrig_TimerUtils takes nothing returns nothing
        local integer i = -1
        local constant integer TIMERS_TO_PRELOAD = 256

        set udg_TimerUtils_timer[0] = CreateTimer()
        //            TIMER_MIN_INDEX
        set udg_TimerUtils_TMI = GetHandleId(udg_TimerUtils_timer[0])
        set i = 1
        loop
            exitwhen i &gt;= TIMERS_TO_PRELOAD
            set udg_TimerUtils_timer<i> = CreateTimer()
            set i = i + 1
        endloop

    endfunction

</i>
 

Bribe

vJass errors are legion
Reaction score
67
The speed is exactly the same, you confound me with your lack of knowledge but your desire to sound like you know what you're talking about anyway. Sorry to be harsh but you should not be so blindly confident that you "have the magic answer" so much of the time. You do this a lot, please be more careful to not dig too deeply before you know the facts.

Get/SetTimerData are the exact same speed as in Vexorian TimerUtils because of function inlining.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Bribe:

1. The speed is exactly the same, you confound me with your lack of knowledge but your desire to sound like you know what you're talking about anyway. Sorry to be harsh but you should not be so blindly confident that you "have the magic answer" so much of the time. You do this a lot, please be more careful to not dig too deeply before you know the facts.

2. Get/SetTimerData are the exact same speed as in Vexorian TimerUtils because of function inlining.

1. You could've just written that they inilne... and what's with the "blindly confident that you "have the magic answer" so much of the time.", "confound me with your lack of knowledge"?
2. Tested and the Get/SetTimerData both compile/inline to the array lookup + H2I + subtraction =), no "wrappers".
 

Bribe

vJass errors are legion
Reaction score
67
There are several examples of you being presumptuous but I don't want to get too much off-topic. It's a fundamental concept to grasp by not assuming that you are always right.

The last time you did that you tried to make me look foolish in this post of yours, and that did not make me feel good because of your reprehensible attitude you had when you wrote it: http://www.thehelper.net/forums/showthread.php/168558-MissileRecycler?p=1381729#post1381729
 

luorax

Invasion in Duskwood
Reaction score
67
Nah, why should this be graveyarded? Clear, efficient and performs well. I'll stick to this.
 

tooltiperror

Super Moderator
Reaction score
231
Seeing as script rewriting is pretty much evil, I'll be graveyarded this, seeing as Vexorian himself is rewriting this.
 

Bribe

vJass errors are legion
Reaction score
67
Currently there is still a bug in Vexorian's TimerUtils and this is at the very least 100% functional in the mean while, never mind the fact that it's not about to usher in a revolution of people switching over.
 

Magthridon96

Member
Reaction score
2
Nah, why should this be graveyarded? Clear, efficient and performs well. I'll stick to this

That's true, so let's wait until Vexorian makes a final update on his TU.
Originally, it's HIS resource :/
 

tooltiperror

Super Moderator
Reaction score
231
Seeing as Vexorian is back (at least for now), go badger him about that. The only time resources may be updated by the unoriginal author such as this is when the author is not around.
 

tooltiperror

Super Moderator
Reaction score
231
Enough of this nonsense. If you two would truly like to argue about this, take it to Private Messaging, and continue respectfully.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top