HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

StructTimers system

11-17-2008, 05:18 PM#1
ProFeT
Quote:
Originally Posted by cohadar
We already have too many timer systems man, make something less usual, an attachment system for example.

I'm not sure what you've mean is exactly this kind of system, but I take the risk to show you this one I've made some times ago.



It's purpose is to automatically handle timer/actions things, and let you place your actions inside the struct itself.
It is very versatile and can be used as well for periodic than one-shot actions.

I let you read a little more about it in the documentation below ;)

Note: obviously, timers systems are fully compatible with this one, just replace TimerStart/DestroyTimer with your own functions.

Collapse JASS:
//***************************************************************************************************
// StructTimers :
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Version     : 1.00
// Author(s)   : profet
//     
//
// DESCRIPTION:
//      This system enables the user to link a struct to a timer, it handles  timers'
//      expiration and automatically run the .End() method of the attached struct.
//
//
// HOW TO USE?
//      Each struct that uses a timer must extend  s_timer  interface, each struct must
//      include 3 methods according to following prototypes:
//      _________
//      .End() : Defines actions executed when the timer expires, also destroy the
//      ¯¯¯¯¯¯¯¯ structure's instance if it's return value is TRUE. 
//
//                  /!\ Take care to potential timer and structure leaks when
//                      returning FALSE if you don't destroy the struct by yourself.
//
//          method End takes nothing returns boolean
//              return true
//          endmethod
//
//      _______________
//      .onDestroy() : Frees the timer when the struct is destroyed.
//      ¯¯¯¯¯¯¯¯¯¯¯¯¯¯ If you are using other structs inside the struct you can
//                     destroy them here, but in most of cases you don't have to
//                     modify this method, rather put your expiration actions in
//                     .End() method.
//                     
//          method onDestroy takes nothing returns nothing
//              call StructTimers_ReleaseTimerStruct(.ti)
//          endmethod
//
//      ____________
//      .create() : Creates a struct instance, initialize the timer and custom
//      ¯¯¯¯¯¯¯¯¯¯¯ struct's variables.
//                  This method is with any doubt the less determinated of all,
//                  and the prototype below is only a frame on which you must base
//                  your own code.
//
//          static method create takes <arguments list> returns <struct.name>
//              local <struct.name> this = <struct.name>.allocate()
//              set this.ti = CreateTimer()
//              call StructTimers_StartTimedStruct( this.ti, <real:timeout>, <boolean:periodic>, this )
//              return this
//          endmethod
//
//
// EXAMPLE:
//      The following example shows how to create a timer that checks every 0.5s if
//      the unit has a specific buff, and kills it if not.
//
//      _____________________
//      Structure definition
//      ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          struct s_kill extends s_timer
//              private unit target
//              private integer buffid
//              private boolean explode
//              
//              method End takes nothing returns boolean
//                  if( GetUnitAbilityLevel(.target,.buffid)==0 )then
//                      call SetUnitExploded( .target, .explode )
//                      call KillUnit(.target)
//                      return true
//                  endif
//                  return false
//              endmethod
//              
//              method ChangePeriod takes real newdelay returns nothing
//                  call StructTimers_StartTimerStruct( this.ti, newdelay, true, this )
//              endmethod
//              
//              method Stop takes nothing returns nothing
//                  call .destroy()
//              endmethod
//              
//              method onDestroy takes nothing returns nothing
//                  call StructTimers_ReleaseTimerStruct(.ti)
//              endmethod
//              
//              static method create takes unit un, boolean explode, integer buffid, real delay returns s_kill
//                  local s_kill this = s_kill.allocate()
//                  //Replace following line if you're using a timers system
//                      set this.ti = CreateTimer()
//                  //end of replace
//                  set this.target = un
//                  set this.explode = explode
//                  set this.buffid = buffid
//                  call StructTimers_StartTimerStruct( this.ti, delay, true, this )
//                  return this
//              endmethod
//          endstruct
//
//      __________________________
//      Structure's usage example
//      ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          call s_kill.create( my_unit, false, 'BOac', 0.5 )
//
//
//
//***************************************************************************************************
library StructTimers
//===========================================================================
    globals
        private integer STRUCT_TIMERS_COUNT = 0
        private integer ASSOC_COUNT = 0
        private timer array ASSOC_TIMER
        private i_timer array ASSOC_VALUE
    endglobals
//===========================================================================
//INTERFACE DEFINITION:
//All structs using timers MUST extend the following interface:
    interface i_timer
        timer ti = null
        method End takes nothing returns boolean
    endinterface
    
//===========================================================================
//Searches for a timer in the associations-list, returns -1 if it isn't found.
    private function findTimer takes timer ti returns i_timer
    local integer i = 0
        loop
            exitwhen( i==ASSOC_COUNT )
            if( ASSOC_TIMER[i]==ti )then
                return i
            endif
            set i = i + 1
        endloop
        return -1
    endfunction

//Removes an association from the list.
    private function removeTimerAssoc takes timer ti, integer index returns nothing
        if( index<0 )then
            set index = findTimer(ti)
        endif
        if( index>-1 )then
            set ASSOC_COUNT = ASSOC_COUNT - 1
            set ASSOC_TIMER[index] = ASSOC_TIMER[ASSOC_COUNT]
            set ASSOC_VALUE[index] = ASSOC_VALUE[ASSOC_COUNT]
        endif
    endfunction

//Create an association.
    private function setTimerAssoc takes timer ti, i_timer value returns boolean
    local integer index = findTimer(ti)
        if( index==-1 and value>0 )then
            set ASSOC_TIMER[ASSOC_COUNT] = ti
            set ASSOC_VALUE[ASSOC_COUNT] = value
            set ASSOC_COUNT = ASSOC_COUNT + 1
            return true
        endif
        return false
    endfunction

//Finds and returns an association, returns 0 if no association.
    private function getTimerAssoc takes timer ti returns i_timer
    local integer index = findTimer(ti)
        if( index>-1 )then
            return ASSOC_VALUE[index]
        endif
        return 0
    endfunction

//===========================================================================
//This function is executed when a struct-timer expires. Associated .end() method
//is called, the struct is destructed if the method returns TRUE.
    private function timerExpiration takes nothing returns nothing      
    local i_timer timerstruct = getTimerAssoc( GetExpiredTimer() )
        if( timerstruct.End() )then
            call timerstruct.destroy()
        endif
    endfunction

//===========================================================================
//Similar to the usual TimerStart function, except the fact it takes a 'structid'
//argument, and does not need the <code:func> parameter.
    public function StartTimerStruct takes timer ti, real timeout, boolean periodic, integer structid returns boolean
        if( structid>0 and ti!=null and timeout>0. )then
            //Replace following line if you're using a timers system
                call TimerStart( ti, timeout, periodic, function timerExpiration )
            //end of replace
            if( setTimerAssoc( ti, structid ) )then
                set STRUCT_TIMERS_COUNT = STRUCT_TIMERS_COUNT + 1
            endif
            return true
        endif
        return false
    endfunction
    
//Removes linked association (to keep the assoc-list as small as possible).
    public function ReleaseTimerStruct takes timer ti returns nothing
        if( ti!=null )then
            //Replace following lines if you're using a timers system
                call PauseTimer(ti)
                call DestroyTimer(ti)
            //end of replace
            call removeTimerAssoc(ti,-1)
            set STRUCT_TIMERS_COUNT = STRUCT_TIMERS_COUNT - 1
        endif
    endfunction

endlibrary
Attached Images
File type: jpgStructTimersA.jpg (183.7 KB)
Attached Files
File type: w3xStructTimers_v1.00.w3x (27.0 KB)
11-17-2008, 06:35 PM#2
Anitarf
I'm rather sure that cohadar was being sarcastic there, since attachment systems pretty much match timer systems in numbers.
11-17-2008, 07:01 PM#3
ProFeT
I was not sure..

Anyway, i'm just trying to share what I did and i hope someone could find it usefull ;)
11-17-2008, 07:16 PM#4
chobibo
I just want to ask if the search function "findTimer" searches linearly?
11-17-2008, 07:30 PM#5
ProFeT
Yes, but since removeTimerAssoc doesn't leave free indexes, i think it doesn't really matter with the number of timers involved in a war3's map. Does it ?
11-17-2008, 10:05 PM#6
Anitarf
Depends on the map.
11-19-2008, 03:21 AM#7
ProFeT
I'm not so used to maths than you are, so could you explain what is the better search method I should use, and in wich specific cases use it ?
11-19-2008, 06:51 AM#8
Captain Griffen
O(n) searches are for all bar the smallest 'n's shit in almost all cases.
11-19-2008, 01:17 PM#9
Anitarf
Either use gamecache, or extended arrays like TimerUtils does.

But your system is fairly limiting in that structs must extend a specific interface, often you may want to attach structs to timers that must extend a different interface.
11-19-2008, 03:30 PM#10
xombie
You could try using linked lists... I believe that would be the quickest way.
11-19-2008, 11:11 PM#11
Ammorth
Quote:
Originally Posted by xombie
You could try using linked lists... I believe that would be the quickest way.

Linked lists will not speed up linear searches.

Worst Case Scenarios:
Linked ListsArrays
AccessO(n)O(1)
Remove ItemO(1)*O(n)
Add ItemO(1)*O(n)
Linear SearchingO(n)O(n)
*this does not include the access time.

Linked lists are only good if you are constantly adding/removing items from places within the list (not the end only).

Linear searches are horribly horrendous.
11-20-2008, 04:48 AM#12
chobibo
that depends on how you use it, in jass there is really no linked list data structure, you just emulate it using arrays, so known index/address access can still be considered as O(1), but still if it's random accessing then you're right, you'll have to linearly search lol.

Either use hashing (timerutils) or gamecahe(table).
11-20-2008, 05:24 AM#13
Ammorth
Linked lists can be emulated with locations or, better yet, structs.

Collapse JASS:
struct ListElement
   ListElement next
   dataType data
endstruct

The best approach would be the binary search, but it would require the list to be sorted to work, which then adds to the add/remove times. It really comes down to what you need it for.
11-20-2008, 09:22 AM#14
ProFeT
I'll think to make a version using a different search method soon or later (maybe based on TimerUtils).

This system is not designed especially for speed (but it could be very quick if I decide to base it on TU) or special cases that require the struct to be extended from an interface, but mainly for it's easy use.
But I agree the fastest a system is, better it is ;)

Quote:
Originally Posted by Anitarf
But your system is fairly limiting in that structs must extend a specific interface, often you may want to attach structs to timers that must extend a different interface.
That's true, but I think it isn't most of cases you encounter while developping a map.
I've done a lot of different systems and it has proved some kind of versatibility.