HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

[Feedback] Advanced Timers system

07-28-2007, 05:52 PM#1
ProFeT
Hi everybody,
I finished a system that alows us to use timers and associated data, without any use of gamecache.

I just wanted some feedback, improvement ideas and even spelling corrections =p...

And is is faster than gamecache ?


Collapse JASS:
//***************************************************************************************************
//* Timer interface :
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* This system links a timer to a struct, and automatically run the .end() method when
//* the timer expires.
//*
//* Each struct that uses a timer must extend the s_timer interface.
//* These structs must include 3 specific methods:
//*    - end method       : defines actions executed when the timer ends
//*    - onDestroy method : releases the timer when the struct is destroyed.
//*    - create method    : init the struct variables, and set up a timer if needed.
//*
//*   Example :
//*   ¯¯¯¯¯¯¯¯¯
//*     struct my_struct extends s_timer
//*       //my variables
//*
//*       method end takes nothing returns nothing
//*         //my ending actions : remove a buff, kill a unit, etc...
//*         //you may have to call the .destroy() method, if the struct won't be used anymore and if you don't call it elsewhere.
//*       endmethod
//*
//*       method onDestroy takes nothing returns nothing
//*         //my onDestroy actions (if you include structs in this struct by example)
//*         if( .ti!=null )then
//*           call ReleaseTimer(.ti)
//*         endif
//*       endmethod
//*
//*       static method create takes <type> arg1, <type> arg2, boolean timed returns my_struct
//*         local my_struct s = my_struct.allocate()
//*         if( timed )then
//*           set s.ti = NewTimer()
//*           call SetTimerValue( s.ti, s )
//*         endif
//*         //init. of my variables
//*         return s
//*       endmethod
//*
//*     endstruct
//* 
//* 
//* You may need to use only 2 functions, that are SetTimerValue and LaunchTimer, other functions
//* are internal to the system.
//*
//***************************************************************************************************
scope timer
  globals
    private timer array T
    private timer array U
    private integer Tn = 0
    private integer Un = 0
    private s_timer array Uval
  endglobals

    interface s_timer
      timer ti = null
      method end takes nothing returns nothing
    endinterface


    private function FindListedTimer takes timer ti returns integer
    local integer i = 1                                         //search the timer in the list
      loop                                                      //returns 0 if timer is not
        exitwhen(U[i]==null)                                    //found.
        if( U[i]==ti )then
          return i
        endif
        set i=i+1
      endloop
      return 0
    endfunction

    private function FlushActiveTimer takes timer ti returns nothing
    local integer i = FindListedTimer(ti)
      if( i!=0 )then                                             //if the time is found,
        if( i!=Un )then                                          //and if it isn't at the end of list
          set U[i] = U[Un]                                       //then change the index of the last
          set Uval[i] = Uval[Un]                                 //timer of the list, to the deleted
        endif                                                    //one.
        set U[Un] = null                                         //Delete the last stored timer
        set Uval[Un] = 0                                         //and clear it's value.
        set Un = Un-1
      endif
   endfunction

    private function EndTimer takes nothing returns nothing      //Run when a timer expires
    local integer i = FindListedTimer( GetExpiredTimer() )
      if( i!=0 )then                                             //if the timer is found in the list,
        call Uval[i].end()                                       //then call it's associated
      endif                                                      //.end() method.
    endfunction

  //**

  function SetTimerValue takes timer ti, s_timer value returns nothing
  local integer i = FindListedTimer(ti)
    if( i==0 )then                                             //if the time is not found,
      set Un = Un+1                                            //increase list's items count
      set i = Un
    endif
    set U[i] = ti                                              //store the timer in the list
    set Uval[i] = value                                        //and it's associated struct
  endfunction

  function LaunchTimer takes timer ti, real dur returns nothing
    call PauseTimer(ti)                                        //Pause the timer to prevent bugs
    call TimerStart( ti, dur, false, function EndTimer)        //start the timer
  endfunction




//***************************************************************************************************
//* Timers recycling functions :
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* Based on CSSafety system (by Vexorian), improved by myself.
//* 
//* This system recycles timers :
//*
//* When you need a timer just use the NewTimer() function, it will look in the stack for a free
//* timer and return one if it is available, else it will create and return a new one.
//*
//* When you don't need your timer anymore, instead of use the DestroyTimer function, call the
//* ReleaseTimer function, that will pause and store the timer in the stack, to be used again
//* when needed.
//* Then you'll no longer have to care about setting timers to null nor about timer related issues
//* with the handle index stack.
//* 
//***************************************************************************************************


  function ReleaseTimer takes timer ti returns nothing
    if( ti!=null )then
      call PauseTimer(ti)
      call FlushActiveTimer(ti)
      if (Tn==8191) then
        debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
        //stack is full, the map already has much more troubles than the chance of bug
        call DestroyTimer(ti)
        set ti = null
      else
        set T[Tn] = ti
        set Tn = Tn+1
      endif
    endif
  endfunction

  function NewTimer takes nothing returns timer
    if( Tn==0 )then
      return CreateTimer()
    endif
    set Tn=Tn-1
    return T[Tn]
  endfunction

  function NewTimerI takes nothing returns integer
    if( Tn==0 )then
      return h2I(CreateTimer())
    endif
    set Tn=Tn-1
    return h2I(T[Tn])
  endfunction

endscope




EDIT : some usage examples

Simple timer usage :
Collapse JASS:
function my_end_func takes nothing returns nothing
   call ReleaseTimer( GetExpiredTimer()  )
endfunction

function AA takes nothing returns nothing
local timer ti = NewTimer()
   call StartTimer( ti, is_periodic, 5.00, func my_end_func )
endfunction


or with an associated struct, using the fist example above :
Collapse JASS:
 function BB takes <type> arg1, <type> arg2 returns nothing
   local my_struct S = my_struct.create( arg1, arg2, true )
   call LaunchTimer( S.ti, 5.00 )
 endfunction
07-28-2007, 07:50 PM#2
blu_da_noob
Hmm. It probably won't be too bad (as long as you're not abusing timers all over the place). I prefer something like Vex's timer substitute setup (heap for log n operations and operates off a single timer). You can find an example of his in his Hydra spell if I recall correctly.