• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Snippet] Timer Stack

Level 7
Joined
Apr 30, 2011
Messages
359
a timer stack system, this system use only one timer, one trigger, and can support up to 8190 timer instances . . .
this system basically stack the timers, one that has the shortest remaining time will be runt first on that only one timer, then search another timer that has shortest expiration time again, this repeats until there's no more timer instances . . .

now it has a documentation . . .

Timer Stack:
JASS:
//===================================================================================
//      
//      Timer Stack System
//      -*- overcold_ice -*-
//      
//      [*] Requirements:
//          - JNGP
//          - latest version of JassHelper
//      
//          For your many long expiration time timers
//      This system merges all of its timer instances into a dynamic single timer
//      That timer will expire on all expiration time its timer instances have
//      
//      [*] Notes:
//          - Not really suitable for short-expiring timer instances
//          - Don't use a 0 expiration time timer instance, unless it have a destroy
//            method called inside it. Or it will crash the game due to infinite loop
//          - A timer instance will always loop if you don't destroy it
//      
//      [*] API:
//      
//      public module Implement
//          
//          static method tsCreate takes real time, code func returns integer
//              - create a new timer instance that will expire after 'real time'
//                seconds and run 'code func' function
//              - returns the timer instance's id
//          
//          method tsDestroy takes nothing returns nothing
//              - destroy a timer instance
//      
//      integer tsTimerId
//      integer tsTimerTypeId
//          - both of them are self-explanatory
//          - they will get updated when a new timer instance is created (will
//            return that new timer's id/typeid) and the timer expires (will
//            return running function's timer instance's id/typeid)
//          - you can re-assign those variables
//      
//      [*] Example/Add-on:
/*
library TimedFunc requires TimerStack
//  
//  API:
//      
//      set YourTFVariable = TF.start(time, function func)
//          run the function after 'time' delay
//      
//      call YourTFVariable.stop()
//          stop the running function
//  
//  Note:
//      - Use TF(tsTimerId).stop() to stop the function when it's called
//      - For additional information, read TimerStack's documentation
//  

    private struct TS extends array
        implement TimerStack_Implement  // it's a public module, so use that prefix
    endstruct
    
    struct TF extends array
        static method start takes real time, code func returns integer
            return TS.tsCreate(time, func)
        endmethod
        
        method stop takes nothing returns nothing
            call TS(this).tsDestroy()
        endmethod
    endstruct
endlibrary
*/
//===================================================================================
library TimerStack
    
    globals
        // Timer Section
        private integer       c   = 0           // timer instance count
        private integer array r                 // timer's recycle bin
        
        private integer array t                 // timer's type
        
        private integer array nt                // next timer
        private integer array pt                // previous timer
        private integer array lt                // last timer
        
        private conditionfunc array cft         // timer's action
        
        // Timer Type Section
        private integer       tc  = 0           // type count
        private integer array tr                // type's recycle bin
        
        private integer array tn                // next type
        private integer array tp                // previous type
        private integer       tl                // last type
        
        private real    array tt                // type's time
        private real    array trt               // type's remaining time
        private integer array ttc               // type's instance count
        
        // Trigger and Timer Section
        private trigger       trg = CreateTrigger() // the trigger
        private timer         tmr               // the timer
        private integer       cur               // current running timer type
        private real          let               // last elapsed time
        
        // Event Responses
        integer      tsTimerId     = 0          // get instance's timer id
        integer      tsTimerTypeId = 0          // get instance's timer type id
    endglobals
    
    //===============================================================================
    //  TIMER STACK TYPE
    //===============================================================================
    
    //  checks if there's another same type, if it's true, then use that type
    //  else, make a new one
    private function GetType takes real time returns integer
        local integer this = tl
        local boolean exit = false
        
        loop
            exitwhen tp [tn [this]] == 0 or exit
            
            if tt [this] == time then
                set exit = true
            else
                set this = tp [this]
            endif
        endloop
        
        if not exit then
            set this = tr [0]
            
            if this == 0 then
                set tc   = tc + 1
                set this = tc
            else
                set tr [0] = tr [this]
            endif
            
            if tl == 0 then
                set tp [this] = 0
            else
                set tp [this] = tl
            endif
            
            set tn [tp [this]] = this
            set tn     [this]  = 0
            set tl             = this
            set tr     [this]  = -1
        endif
        
        set tt  [this] = time
        set trt [this] = time
        set ttc [this] = ttc [this] + 1
        
        return this
    endfunction
    
    //  reduce the amount of an type in a type stack by 1, if it has no more
    //  instance remaining, then destroy the timer type
    private function LoseType takes integer this returns nothing
        if tr [this] == -1 then
            set ttc [this] = ttc [this] - 1
            
            if ttc [this] == 0 then
                if tn [this] == 0 then
                    if tp [this] == 0 then
                        set tl = 0
                    else
                        set tl = tp [this]
                        set tn  [tp [this]] = 0
                    endif
                else
                    set tn [tp [this]] = tn [this]
                    set tp [tn [this]] = tp [this]
                endif
                
                set tr [this] = tr [0]
                set tr [0]    = this
                
                set tt  [this] = 0
                set trt [this] = 0 
                set ttc [this] = 0
            endif
        endif
    endfunction
    
    //  refresh all timer types' remaining time
    private function RefreshType takes nothing returns nothing
        local integer this = tl
        local real    time = TimerGetElapsed(tmr) - let
        
        set let = TimerGetElapsed(tmr)
        
        loop
            exitwhen tp [tn [this]] == 0
            
            set trt [this] = trt [this] - time
            
            set this = tp [this]
        endloop
    endfunction
    
    //===============================================================================
    //  TIMER STACK
    //===============================================================================
    
    //  runs when the timer expires, calling each timer type instances' actions
    private function Expire takes nothing returns nothing
        local integer this = lt [cur]
        
        loop
            exitwhen pt [nt [this]] == 0
            
            set tsTimerId     = this
            set tsTimerTypeId = t [this]
            
            call TriggerClearConditions(trg)
            call TriggerAddCondition(trg, cft [this])
            call TriggerEvaluate(trg)
            
            set this = pt [this]
        endloop
        
        call RefreshType()
        set  trt [cur] = tt [cur]
        set  this = tl
        
        loop
            exitwhen tp [tn [this]] == 0
            
            if cur == 0 or trt [cur] > trt [this] then
                set cur = this
            endif
            
            set this = tp [this]
        endloop
        
        set  let = 0
        call DestroyTimer(tmr)
        set  tmr = CreateTimer()
        call TimerStart(tmr, trt [cur], false, function Expire)
    endfunction
    
    //  create a new timer instance, this timer instance will be automatically
    //  started
    private function Create takes real time, code func returns integer
        local integer this = r [0]
        local integer that
        
        if this == 0 then
            set c    = c + 1
            set this = c
        else
            set r [0] = r [this]
        endif
        
        call RefreshType()
        set  t   [this] = GetType(time)
        set  cft [this] = Condition(func)
        
        if lt [t [this]] == 0 then
            set nt [lt [t [this]]] = this
            
            set nt    [this]  = 0
            set pt    [this]  = 0
            set lt [t [this]] = this
        else
            set nt [lt [t [this]]] = this
            
            set nt    [this]  = 0
            set pt    [this]  = lt [t [this]]
            set lt [t [this]] = this
        endif
        
        set that = tl
        
        loop
            exitwhen tp [tn [that]] == 0
            
            if cur == 0 or trt [cur] > trt [that] then
                set cur = that
            endif
            
            set that = tp [that]
        endloop
        
        set  let = 0
        call DestroyTimer(tmr)
        set  tmr = CreateTimer()
        call TimerStart(tmr, trt [cur], false, function Expire)
        
        set  tsTimerId     = this
        set  tsTimerTypeId = t [this]
        
        return this
    endfunction
    
    //  destroy a timer instance
    private function Destroy takes integer that returns nothing
        local integer this = that
        
        call LoseType(t [this])
        
        if ttc [t [this]] == 0 and cur == t [this] then
            call RefreshType()
            set  this = tl
            set  cur  = 0
            
            loop
                exitwhen tp [tn [this]] == 0
                
                if cur == 0 or trt [cur] > trt [this] then
                    set cur = this
                endif
                
                set this = tp [this]
            endloop
            
            set  let = 0
            call DestroyTimer(tmr)
            set  tmr = CreateTimer()
            call TimerStart(tmr, trt [cur], false, function Expire)
        endif
        
        if nt [that] == 0 then
            if pt [that] == 0 then
                set lt [t [that]] = 0
            else
                set lt  [t [that]] = pt [that]
                set nt [pt [that]] = 0
            endif
        else
            set nt [pt [that]] = nt [that]
            set pt [nt [that]] = pt [that]
        endif
        
        set r   [that] = r [0]
        set r   [0]    = that
        
        set t   [that] = 0
        set cft [that] = null
    endfunction
    
    //===============================================================================
    //  A P I
    //===============================================================================
    public module Implement
        static method tsCreate takes real time, code func returns integer
            return Create(time, func)
        endmethod
        
        method tsDestroy takes nothing returns nothing
            call Destroy(this)
        endmethod
    endmodule
endlibrary

[Example] Timed Function:
JASS:
library TimedFunc requires TimerStack
//  
//  API:
//      
//      set YourTFVariable = TF.start(time, function func)
//          run the function after 'time' delay
//      
//      call YourTFVariable.stop()
//          stop the running function
//  
//  Note:
//      - Use TF(tsTimerId).stop() to stop the function when it's called
//      - For additional information, read TimerStack's documentation
//  

    private struct TS extends array
        implement TimerStack_Implement  // it's a public module, so use that prefix
    endstruct
    
    struct TF extends array
        static method start takes real time, code func returns integer
            return TS.tsCreate(time, func)
        endmethod
        
        method stop takes nothing returns nothing
            call TS(this).tsDestroy()
        endmethod
    endstruct
endlibrary

Feel free to comment, your suggestions are gladly appreciated!
 
Last edited:
Give up and start over >.>. The timer system that I trashed at TH is much faster than this as that other one uses a binary heap.


You will never be able to do a decent 1 timer system using a linked list.


This should be insta gy'd. I already told you the last time you tried this >.>.


The best possible data structure to do this in is a binary heap, and even if you do that best possible data structure, a 1 timer system will always suck compared to a multi timer system.


I also see that you refresh remaining times, which is really, really, really bad >.>. Yea, def start over, or better yet, give up with the idea of a 1 timer system =).
 
This can be used for your own map, but it does not belong as a submitted resource because it is:

1) Much slower than TimerUtils.
2) Requires a lot of code, variables and fluff but gives fails to provide any benefit which was not already there.

I appreciate your enthusiasm to share your hard work with others, and to show off the possibility of doing things with only one timer, however it is not practical in real life. I'm sorry. I will schedule this to be graveyarded.
 
Top