System Timer Tools

Nestharus

o-o
Reaction score
84
Code

Library Size: 648 Lines

The fastest timer system there is.

The speed blows away standard timers and all other timer systems, including T32x.

CTM specializes in all types of repeating timers from small timeout timers to timeouts up to 81.90. If it repeats, it can and should run on CTM, even if there is no data attachment to worry about.

The CTM (Constant Timer Method) Module contains 4 parts.
JASS:

struct MyTimerMethod extends array
    readonly integer value

    //this is your constant method
    implement CTM
        //declare locals here
        local string s="My Value: "
    implement CTMExpire
        //expiration code
        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,s+I2S(value))
    implement CTMNull
        //null locals here
        set s=null //pointless, but shows how to use null block
    implement CTMEnd
    //end constant method

    private static method onInit takes nothing returns nothing
        set create(2).value=1
    endmethod
endstruct


Aside from the module, Timer Tools also contains a variety of functions for your use.

Creating a constant repeating timer, which is common in most maps, is easy.
[ljass]call Timer[TIMEOUT].list.register(Condition(function MyFunction))[/ljass]

The above line returns the registered function, meaning it can be unregistered at a later time (for example, clean up on game end). This also means that you can use this function directly for things like projectile systems or whatever else you need.

TimerLists, which are retrieved from Timer[TIMEOUT].list, are created and destroyed, meaning that they should not be stored. A smart TimerList is created when it is first accessed via .list and is destroyed when it no longer has any boolean expressions registered to it. If the TimerList is an auto merging TimerList, it is never destroyed and can be stored safely. A constant merging TimerList occurs when the timeout is small enough that all timers on it will always merge regardless of when they are started. For example, a timeout of .05 and a CONSTANT_MERGE of .1 would cause that timeout to always merge, meaning that Timer[.05].list would be a constant merge list. Whether a list is a constant merge list or not is dependent upon more than just the CONSTANT_MERGE.

JASS:

set merge = Ln(RELATIVE_MERGE/timeout)/230.258509*timeout
if (merge < CONSTANT_MERGE) then
    set merge = CONSTANT_MERGE)
endif
set isConstantMerge = CONSTANT_MERGE < (timeout-merge)/2


Any timeout that falls into the above scenario will be a constant merging list. Constant merging lists still have their timers paused, they are just never deallocated, meaning that the lists can be stored and accessed quickly. Whenever you are working with small constant timeouts, be sure to store the list so that you can quickly retrieve it and add to it.

[ljass]function TimerMethodAddInstance takes TimerList list, integer timerMethod returns integer[/ljass]

Adding instances requires the list. For constant merging timers, the list can still be quickly retrieved without having to be stored.

JASS:

local integer list = first[this]      //first timeout node
if (0 == list) then
    return ...
endif
if (notConstantMerge[this]) then
    return ...
endif
return list


When working with the functions rather than the modules, be warned that accessing the first node within an expiring method using the function will read from a hashtable. To prevent this, store the firsts yourself into your own arrays.

JASS:

*       function GetTimerFirst takes nothing returns integer
*           -   Can only be called right after adding a new instance to a timer method
*           -   Useful for storing first into an array
*       function GetTimerFutureFirst takes nothing returns integer
*           -   Can only be called right after adding a new instance to a timer method
*           -   Useful for storing future first into an array so that the first
*           -   can be updated after the timer expires.
*       function GetTimerParent takes nothing returns integer
*           -   Retrieves parent of newly added instance
*           -   Can only be used directly after adding instance


Example
JASS:

set first[GetTimerParent()] = GetTimerFirst()
set futureFirst[GetTimerParent()] = GetTimerFutureFirst()


Be sure to update the actual first to the future first at the end of your loop in your expiration method.

Please note
[ljass]function GetExpired takes nothing returns integer[/ljass]

This will retrieve the parent -> GetTimerParent() within an expiring method, meaning that you will be able to retrieve the first node.

Except from CTM Module
JASS:

local thistype this = first[GetExpired()] //first is a custom array
loop
    exitwhen 0 == this
    set this = GetTimerNext(this)
endloop
set first[GetExpired()] = futureFirst[GetExpired()] //futureFirst is a custom array


When dealing with constant timeouts, use the CTC module.

When dealing with constant merging timeouts, use the CTCC module or TimerMethodAddInstanceC/TimerMethodRemoveInstanceC/GetTimerFirstC

Be sure to enable debug mode and SUGGEST_MODULE_CHANGES while in the testing phases of your map to pick up errors and possible efficiency gains.

That is about all you need to know to use Timer Tools effectively.

Happy mapping ;D
 

Attachments

  • Timer Tools.w3m
    34.9 KB · Views: 445

Hatebreeder

So many apples
Reaction score
381
this isn't exactly a small, compact system, so it's pretty hard to believe that it's faster than other systems which are smaller.
 

Nestharus

o-o
Reaction score
84
If you don't believe me, try running the map to see for yourself.

There is also this map, which has CTM, CLTQ, standard timers 1, and standard timers 2 in it
http://www.wc3c.net/attachment.php?attachmentid=50825&d=1330329851

Open that map in WE and then compare. My suggestion is to change RELATIVE_MERGE or w/e to .1 and CONSTANT_MERGE to .000010000. This will put CTM in about the worst possible scenario, allowing you to test for a case that is so bad that it wouldn't happen in a normal wc3 map. It gives standard timers the greatest possible advantage, and it still owns them >: D. Also, if standard timers have any data attachment in them, even the TimerUtils red sort, they will fall way behind CTM.


Just about all of the code in there is for creation and destruction.

This is the actual expiration code.
JASS:

        static method CTMe takes nothing returns boolean
            local thistype this = CTMq[exp]
            loop
                exitwhen 0 == this
                set this = mln[this]
            endloop
            set CTMq[exp] = CTMqt[exp]
            return false
        endmethod


And here is the code that runs all expiration code for an expiring timer
JASS:

    private function EXP takes nothing returns nothing
        local integer i = R2I(TimerGetTimeout(GetExpiredTimer())*100)
        local integer n = lf<i>
        set lf<i> = ln[n]
        
            set nr[n] = true
            set exp = n
            call TriggerEvaluate(nt[n])
            set exp = 0
            set nr[n] = false
</i></i>


So EXP runs for all timers of the same timeout in an instance and then that loop runs for each group of expiring timers that have the same method ;D.


As you see, the expiration code is extremely light and compact. The EXP function also has 4 loops, but they almost always terminate immediately, so it's pretty much just [ljass]exitwhen 0 == i[/ljass] x4. There is also an if statement -> [ljass]if (0 == nc[n]) then[/ljass]


Now, I'm not going to bother comparing this to KT2 or KT because I already know this one is faster... KT2 is especially slow and loses to standard timers.
 

NoobImbaPro

You can change this now in User CP.
Reaction score
60
So with this, I can manipulate hp/mp regeneration in very short timeouts without hesitation, right?
 

TheLegend

New Member
Reaction score
10
dude, this is just a proof how awesome jass can be when manipulated by people who know what they do, congrats on the fast system
 

NoobImbaPro

You can change this now in User CP.
Reaction score
60
I benchmarked my own system with this timer and T32 with the same timeouts, when t32 had 0 fps , yours had 5 and when t32 had 10 fps your's had 29. Trully the fastest timer system.
 

Nestharus

o-o
Reaction score
84
Improved merging algorithm, meaning faster creation/destruction for auto merging and more accurate first ticks.


The benchmark went from 54 fps to 63 fps.


Also now does timer recycling instead of mass create/destroy.
 

Nestharus

o-o
Reaction score
84
Improved merging algorithm further by handling two types of TimerLists, constant merging and smart merging. This ensures that small timeouts will have very fast creation and destruction. This gets rid of the need for specialized libraries like T32x as the speed gain for proper creation and destruction will be negligible.

I removed CLTQ because it was kinda crappy... it was around the same speed as normal timers... I coded a specialized thing that was similar to TimerUtils but much faster, but the problem with preloading timers is that more timers = less performance for all timers... so I scrapped the idea.

The rankings were
CLTQ = Standard
TimerUtils (far, far behind...)

In my specialized thing
Tt 1 Shot Timers
Standard (far behind)
TimerUtils (far, far behind...)

The 1 shot timers I made could handle around 4500 timers with .02 timeouts at 64 fps.TimerUtils ofc froze up at around 2200. Standard froze up at around 2800 or so.


What does this tell you? Using standard timers is smart and better than using TimerUtils >.>, lolz.
 

Nestharus

o-o
Reaction score
84
JASS:

library Tt /* v2.1.6.1
*************************************************************************************
*
*   Timer tools timers use a special merging algorithm. When two timers merge, they
*   both expire on the same timer. The first tick accuracy is based on the size of the timer&#039;s timeout. 
*   The larger the timeout, the more inaccurate the first tick can be. The tick only becomes inaccurate
*   if it merges with another timer.
*
*   Max Timeout for CTM: 81.90
*   Min Timeout for CTM: .01
*
*   Specializes in repeating timers.
*
************************************************************************************
*
*    SETTINGS
*/
    /*
    *   Checks if any CTM modules appear to be constant
    */
    static if DEBUG_MODE then
        private struct DEBI extends array
            static constant boolean SUGGEST_MODULE_CHANGES = true
        endstruct
    endif
globals
    /*************************************************************************************
    *
    *                    RELATIVE_MERGE
    *
    *    Effects the accuracy of first tick. The smaller the merge value, the less chance two
    *    timers have of sharing the same first tick, which leads to worse performance.
    *    However, a larger merge value decreases the accuracy of the first tick.
    *
    *    Formula: Ln(RELATIVE_MERGE/timeout)/230.258509*3600*timeout
    *        Ln(64000/3600)/230.258509*3600=44.995589035797596625536391805959 seconds max off
    *
    *************************************************************************************/
    private constant real RELATIVE_MERGE =                 64000

    /*************************************************************************************
    *
    *                    CONSTANT_MERGE
    *
    *    Effects the accuracy of the first tick. This is a constant merge. If this value +.01 is
    *    greater than the value calculated from RELATIVE_MERGE, the timer auto merges.
    *
    *    Constant merge must be greater than 0.
    *
    *************************************************************************************/
    private constant real CONSTANT_MERGE =                  .1
endglobals
/*
************************************************************************************
*
*    Functions
*
*       function GetExpired takes nothing returns integer
*           -   Gets the expiring timer. This is not the expiring timer method instance! This is read
*           -   inside of GetTimerFirstInstance, which is why timer instances can only be retrieved inside of
*           -   an expiring timer method.
*           -
*           -   Boolean expressions on the same timer will expire for that same timer. Use timer methods instead.
*
*       function TimerMethodAddInstance takes TimerList list, integer timerMethod returns integer
*           -   Adds a new instance to a timer list. These must be looped through inside of the boolean expression method.
*           -   Returns a timer instance.
*       function TimerMethodRemoveInstance takes integer timerInstance returns nothing
*           -   Removes an instance from a timer method. Timer method is destroyed after it is done expiring when no instances
*           -   are left.
*       function CreateTimerMethod takes boolexpr method returns integer
*           -   Creates a new timer method from a boolean expression. This boolean expression is run whenever the timer
*           -   expires.
*
*       function TimerGetElapsedEx takes integer timerInstance returns real
*       function TimerGetRemainingEx takes integer timerInstance returns real
*       function TimerGetTimeoutEx takes integer timerInstance returns real
*
*       function GetTimerFirstInstance takes integer timerMethod returns integer
*           -   Gets first timer instance in a timer method. Can only be called within an expiring timer method.
*       function GetTimerNext takes integer timerInstance returns integer
*           -   Gets next timer instance given a timer instance.
*           -   Sentinel is 0.
*
*       function GetTimerFirst takes nothing returns integer
*           -   Can only be called right after adding a new instance to a timer method
*           -   Useful for storing first into an array
*       function GetTimerFutureFirst takes nothing returns integer
*           -   Can only be called right after adding a new instance to a timer method
*           -   Useful for storing future first into an array so that the first
*           -   can be updated after the timer expires.
*       function GetTimerParent takes nothing returns integer
*           -   Retrieves parent of newly added instance
*           -   Can only be used directly after adding instance
*
*           -   set first[GetTimerParent()] = GetTimerFirst()
*           -   set futureFirst[GetTimerParent()] = GetTimerFutureFirst()
*
*       function TimerMethodAddInstanceC takes TimerList list, integer timerMethod returns integer
*           -   Adds a new instance to a timer method on a constant merging timer list.
*       function TimerMethodRemoveInstanceC takes integer timerInstance returns nothing
*           -   Removes an instance from a timer method on a constant merging timer list.
*
*       function GetTimerFirstC takes integer timerMethod returns integer
*           -   Retrieves the first node given a timer method on a constant merging timer list. Can be called at any point.
*
*               set this = GetTimerFirstC(myTimerMethod)
*               loop
*                   exitwhen 0 == this
*                   set this = GetTimerNext(this)
*               endloop
*
************************************************************************************
*
*   struct TimerList extends array
*
*       method register takes boolexpr b returns integer
*           -   Registers a boolean expression to timer
*       method unregister takes integer i returns nothing
*           -   Unregisters boolean expression from timer
*           -   Timer is automatically destroyed if no boolean expressions are left
*
************************************************************************************
*
*   struct Timer extends array
*       static method operator [] takes real timeout returns thistype
*           -   Converts a timeout to a timer group
*       method operator list takes nothing returns TimerList
*           -   Creates a new timer in the timer group if there are no viable currently
*           -   existing timers. Will return an existing timer if that timer either expires soon enough
*           -   or has enough time left to be close enough to the timeout.
*
************************************************************************************
*
*   Modules
*
*       module CTM (optional)
*           locals
*       module CTMExpire (not optional)
*           expiration code
*       module CTMNull (optional)
*           null locals
*       module CTMEnd (not optional)
*           static method create takes real timeout returns thistype
*           method destroy takes nothing returns nothing
*           method operator elapsed takes nothing returns real
*           method operator remaining takes nothing returns real
*           method operator timeout takes nothing returns real
*
*               Method: Constant
*               Timeout: Not Constant
*               Timeout List: Not Constant
*
*       module CTT (optional)
*           locals
*       module CTTExpire (not optional)
*           expiration code
*       module CTTNull (optional)
*           null locals
*       module CTTEnd (not optional)
*           static method create takes nothing returns thistype
*           method destroy takes nothing returns nothing
*           method operator elapsed takes nothing returns real
*           method operator remaining takes nothing returns real
*           method operator timeout takes nothing returns real
*
*               Requires: private static constant real TIMEOUT
*
*               Method: Constant
*               Timeout: Constant
*               Timeout List: Not Constant
*
*       module CTTC (optional)
*           locals
*       module CTTCExpire (not optional)
*           expiration code
*       module CTTCNull (optional)
*           null locals
*       module CTTCEnd (not optional)
*           static method create takes nothing returns thistype
*           method destroy takes nothing returns nothing
*           method operator elapsed takes nothing returns real
*           method operator remaining takes nothing returns real
*           method operator timeout takes nothing returns real
*
*               Requires: private static constant real TIMEOUT that will be constant merge
*
*               Method: Constant
*               Timeout: Constant
*               Timeout List: Constant
*
************************************************************************************/
    globals
        //list
        private integer array lf            //timeout first node
        private integer array ln            //node next
        private integer array lp            //node previous
        private integer array lr            //node recycler
        private integer lc = 0              //node instance count
        private timer array tm              //node timer
        
        //nodes
        private trigger array nt            //node trigger
        private integer array nc            //node instance count
        private integer array nh            //node head (timeout*100)
        private boolean array aa            //node set to add after
        private boolean array nr            //node running
        
        //trigger condition instancing
        private integer ntcc = 0            //instance count
        private integer array ntcr          //recycler
        private triggercondition array ntc  //condition
        
        private boolean array nprm          //not permanent
        
        //trigger condition add after
        private integer array paf           //first
        private integer array pan           //next
        private integer array pap           //prev
        private boolexpr array pab          //set to add after
        
        //trigger condition destroy after
        private integer array ds            //to destroy
        private integer dsc = 0             //destroy count
        
        //expiring timer
        private integer exp = 0
        
        //module
            //timer method instancing, stores boolean expressions
            //timer method can&#039;t be destroyed
            private integer mtfr = 0        //instance count
            private boolexpr array mb       //boolexpr
            
            private integer mnf = 0         //module node first return on add
            private integer mnft = 0        //module node future first return on add
            private integer mnp = 0         //module node parent
            //first[mnp] = mnf
            //futureFirst[mnp] = mnft
        
            //list
            private hashtable mlt = InitHashtable()         //stores firsts and counts
                                                            //instance count:               + +
                                                            //method first:                 - -
                                                            //method future first:          - +
                                                            //registered method instance:   + -
                                                            
            //actual expiring nodes
            private integer array mln                       //expiration next
            private integer array mlp                       //expiration previous
            
            //instantly updated list
            private integer array mlnd                      //expiration next true
            private integer array mlpd                      //expiration prev true
            
            //add instances after expiration
            private integer array mlnp                  //previous
            private integer array mlpp                  //next
            private boolean array mlap                  //is set to add after
            private integer array mlfp                  //first to add after
            
            //recycler
            private integer array mlr                   //module node recycler
            private integer mlc = 0                     //module node instance count
            
            //nodes
            private integer array mnh                   //module node head (head is timeout node)
            private integer array mno                   //module node origin (origin is timer method instance, boolexpr)
            
            //destroy after
            private integer array mds                   //node to destroy
            private integer mdsc = 0                    //node to destroy count
            private boolean array mdb                   //is node marked to destroy
            
            private integer array mpf                   //module perm first
            private integer array mpff                  //module perm future first
            private integer array mptc                  //module perm tc
            private boolean array mp                    //module perm
    endglobals
    
    //hiveworkshop.com/forums/jass-functions-413/snippet-natural-logarithm-108059/
    //credits to BlinkBoy
    private function Ln takes real a returns real
        local real s=0
        loop
        exitwhen a&lt;2.71828
        set a=a/2.71828
        set s=s+1
        endloop
        return s+(a-1)*(1+8/(1+a)+1/a)/6
    endfunction
    
    static if DEBUG_MODE then
        private struct DEB extends array
            static boolean array al                     //is timeout node allocated
            static boolean array alm                    //is module node allocated
            static boolean en = true                    //is system enabled
            static timer array tm2                      //timer to verify paused/started timers don&#039;t freak out
        endstruct
        
        /*
        *   Disable System
        */
        private function DIS takes nothing returns nothing
            set DEB.en = false
            
            //pause all running timers
            loop
                exitwhen 0 == lc
                call PauseTimer(tm[lc])
                debug call PauseTimer(DEB.tm2[lc])
                set lc = lc - 1
            endloop
        endfunction
        
        private function CMQ takes real a returns boolean
            local real m = Ln(RELATIVE_MERGE/a)/230.258509*a
            if (m &lt; CONSTANT_MERGE) then
                set m = CONSTANT_MERGE
            endif
            return CONSTANT_MERGE &gt;= (a-m)/2
        endfunction
    endif
    
    /*
    *   Allocate Timeout Node
    */
    private function AL takes nothing returns integer
        //standard allocation
        local integer i = lr[0]
        
        if (0 == i) then
            set lc = lc + 1
            
            debug if (8191 &lt; lc) then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;INSTANE OVERLOAD&quot;)
                debug call DIS()
            debug endif
            
            debug set DEB.al[lc] = true
            
            set tm[lc] = CreateTimer()
            set nt[lc] = CreateTrigger()
            
            return lc
        endif
        
        set lr[0] = lr<i>
        
        debug set DEB.al<i> = true
        
        return i
    endfunction
    
    function GetExpired takes nothing returns integer
        return exp
    endfunction
    
    /*
    *   Delete Module Node
    */
    private function DELM takes integer i returns nothing
        debug if (DEB.en) then
            debug if (DEB.alm<i>) then
                set mlr<i> = mlr[0]
                set mlr[0] = i
                debug set DEB.alm<i> = false
            debug else
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;DOUBLE FREE: &quot;+I2S(i))
                debug call DIS()
            debug endif
        debug endif
    endfunction
    //! textmacro TIMER_TOOLS_ADD_NODE takes D
        debug if (DEB.en) then
            debug if (DEB.alm<i>) then
                set mln$D$<i> = 0
                if (0 == mlp$D$[t]) then
                    set mlp$D$<i> = t
                    set mln$D$[t] = i
                else
                    set mlp$D$<i> = mlp$D$[t]
                    set mln$D$[mlp$D$<i>] = i
                endif
                set mlp$D$[t] = i
            debug else
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;ATTEMPTED TO ADD NULL NODE: &quot;+I2S(i)+&quot; on &quot;+I2S(mnh<i>))
                debug call DIS()
            debug endif
        debug endif
    //! endtextmacro
    
    /*
    *   Add Module Node
    */
    private function ADDM takes integer i, integer t returns nothing
        //! runtextmacro TIMER_TOOLS_ADD_NODE(&quot;&quot;)
    endfunction
    /*
    *   Add Module Node To Instant Update True List
    */
    private function ADDMD takes integer i, integer t returns nothing
        //! runtextmacro TIMER_TOOLS_ADD_NODE(&quot;d&quot;)
    endfunction
    /*
    *   Add Module Node To Add After
    */
    private function ADDMP  takes integer i, integer t returns nothing
        //! runtextmacro TIMER_TOOLS_ADD_NODE(&quot;p&quot;)
    endfunction
    //! textmacro TIMER_TOOLS_REMOVE_NODE takes D
        debug if (DEB.en) then
            debug if (DEB.alm<i>) then
                if (0 != f) then
                    if (0 == mln$D$<i>) then
                        set mlp$D$[f] = mlp$D$<i>
                    else
                        set mlp$D$[mln$D$<i>] = mlp$D$<i>
                    endif
                    
                    set mln$D$[mlp$D$<i>] = mln$D$<i>
                    
                    if (f == mln$D$[f]) then
                        set mln$D$[f] = 0
                        set mlp$D$[f] = 0
                    else
                        set mln$D$[mlp$D$[f]] = 0
                    endif
                endif
            debug else
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;ATTEMPTED TO REMOVE NULL NODE: &quot;+I2S(i)+&quot; on &quot;+I2S(mnh<i>))
                debug call DIS()
            debug endif
        debug endif
    //! endtextmacro
    /*
    *   Remove Module Node
    */
    private function REMM takes integer i, integer f returns nothing
        //! runtextmacro TIMER_TOOLS_REMOVE_NODE(&quot;&quot;)
    endfunction
    /*
    *   Remove Module Node From Instant Update True List
    */
    private function REMMD takes integer i, integer f returns nothing
        //! runtextmacro TIMER_TOOLS_REMOVE_NODE(&quot;d&quot;)
    endfunction
    /*
    *   Remove Module Node From Add After List
    */
    private function REMMP takes integer i, integer f returns nothing
        //! runtextmacro TIMER_TOOLS_REMOVE_NODE(&quot;p&quot;)
    endfunction
    
    /*
    *   Add Module Node To Destroy After Stack
    */
    private function DAF takes integer i returns nothing
        debug if (DEB.en) then
            debug if (not mdb<i>) then
                set mdb<i> = true
                set mds[mdsc] = i
                set mdsc = mdsc + 1
            debug else
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;DOUBLE FREE ATTEMPT: &quot;+I2S(i))
                debug call DIS()
            debug endif
        debug endif
    endfunction
    
    /*
    *   On Expiration
    */
    private function EXP takes nothing returns nothing
        //retrieve timeout instance
        local integer e = R2I(TimerGetTimeout(GetExpiredTimer())*100+.5)
        local integer i = e
        
        //retrieve first node on timeout
        local integer n = lf<i>
        local integer t
        local integer f
        
        //set first = first.next
        set lf<i> = ln[n]
        
        debug if (DEB.en) then
            debug if (not DEB.al[n]) then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG NODE: &quot;+I2S(n))
                debug call DIS()
                debug return
            debug endif
            debug if (0 != TimerGetRemaining(DEB.tm2[n])) then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG TIMEOUT NODE: &quot;+I2S(n)+&quot; &quot;+R2S(TimerGetRemaining(DEB.tm2[n])))
                debug call DIS()
                debug return
            debug endif
            
            set nr[n] = true                    //node running = true
            set exp = n                         //expiring = node
            call TriggerEvaluate(nt[n])         //evaluate all registered methods
            set exp = 0                         //expiring = 0
            set nr[n] = false                   //node running = false
            
            //add all to add after to node
            set i = paf[n]
            set paf[n] = 0
            loop
                exitwhen 0 == i
                set ntc<i> = TriggerAddCondition(nt[n], pab<i>)
                set pab<i> = null
                set ntcr<i> = ntcr[0]
                set ntcr[0] = i
                set i = pan<i>
            endloop
            
            //add all to add after to modules
            set i = mlfp[n]
            set mlfp[n] = 0
            loop
                exitwhen 0 == i
                
                if (mp<i>) then
                    set t = mpf[mno<i>]
                else
                    set t = LoadInteger(mlt, -mno<i>, -mnh<i>)            //mlf
                endif
                
                call ADDM(i, t)
                
                set i = mlnp<i>
            endloop
            
            //destroy all to destroy after from node
            set i = dsc
            loop
                exitwhen 0 == i
                set i = i - 1
                call TriggerRemoveCondition(nt[n], ntc[ds<i>])
                set ntc[ds<i>] = null
            endloop
            set dsc = 0
            
            //destroy all to destroy after from modules
            set t = mdsc
            loop
                exitwhen 0 == t
                set t = t - 1
                set i = mds[t]
                set mdb<i> = false
                
                if (mp<i>) then
                    set f = mpf[mno<i>]
                    
                    if (f == i) then
                        set f = mpff[mno<i>]
                        if (f == i) then
                            set f = 0
                        endif
                        set mpf[mno<i>] = f
                    endif
                else
                    set f = LoadInteger(mlt, -mno<i>, -mnh<i>)        //mlf
                    
                    if (f == i) then
                        set f = LoadInteger(mlt, -mno<i>, mnh<i>)       //mlf2
                        if (f == i) then
                            set f = 0
                        endif
                        call SaveInteger(mlt, -mno<i>, -mnh<i>, f)        //mlf
                    endif
                endif
                
                call REMM(i, f)
                call DELM(i)
                
                set mlap<i> = false
            endloop
            set mdsc = 0
            
            //if no methods are left on the node, destroy the node
            if (0 == nc[n]) then
                //handle timer
                call PauseTimer(tm[n])
                debug call PauseTimer(DEB.tm2[n])
                
                if (nprm[e]) then
                    debug call DestroyTimer(DEB.tm2[n])
                    debug set DEB.tm2[n] = null
                    
                    //remove from timeout list
                    if (lf[nh[n]] == n) then
                        set lf[nh[n]] = ln[n]
                    endif
                    
                    set ln[lp[n]] = ln[n]
                    set lp[ln[n]] = lp[n]
                    
                    if (ln[n] == n) then
                        set lf[nh[n]] = 0
                    endif
                    
                    //recycler
                    set lr[n] = lr[0]
                    set lr[0] = n
                endif
                debug set DEB.al[n] = false
            endif
        debug endif
    endfunction
    
    struct TimerList extends array
        method register takes boolexpr b returns integer
            local integer h = nh[this]
            
            //allocate new trigger condition
            local integer i = ntcr[0]
            if (0 == i) then
                set i = ntcc + 1
                set ntcc = i
            else
                set ntcr[0] = ntcr<i>
            endif
            
            if (0 == nc[this] and not nprm[h]) then
                call TimerStart(tm[this], h/100., true, function EXP)
                debug call TimerStart(DEB.tm2[this], h/100., true, null)
                set ntc<i> = TriggerAddCondition(nt[this], b)
                debug set DEB.al[this] = true
            //if to add after (to add after is marked when TimerList is retrieved)
            //only store TimerList for perm
            elseif ((not nprm[h] and nr[this]) or (nprm[h] and aa[this])) then
                //add it to add after list
                if (0 == paf[this]) then
                    set paf[this] = i
                    set pan<i> = 0
                    set pap<i> = i
                else
                    set pan<i> = 0
                    set pap<i> = pap[paf[this]]
                    set pan[pap<i>] = i
                    set pap[paf[this]] = i
                endif
                set pab<i> = b
            else
                //register condition
                set ntc<i> = TriggerAddCondition(nt[this], b)
            endif
            
            //increase node count
            set nc[this] = nc[this] + 1
            
            return i
        endmethod
        method unregister takes integer i returns nothing
            local integer h = nh[this]
            
            //if the node has not been set to be added after
            if (null == pab<i>) then
                //if node running, add to destroy after stack
                if (nr[this]) then
                    set ds[dsc] = i
                    set dsc = dsc + 1
                else
                    //remove instantly
                    call TriggerRemoveCondition(nt[this], ntc<i>)
                    set ntc<i> = null
                endif
            else
                //remove from to add after list
                if (paf[this] == i) then
                    set paf[this] = pan<i>
                endif
                
                if (0 == pan<i>) then
                    set pap[paf[this]] = pap<i>
                else
                    set pap[pan<i>] = pap<i>
                endif
                
                if (0 == pap<i>) then
                    set pan[paf[this]] = pan<i>
                else
                    set pan[pap<i>] = pan<i>
                endif
                
                //mark as not to add after
                set pab<i> = null
                
                //recycler
                set ntcr<i> = ntcr[0]
                set ntcr[0] = i
            endif
            
            //if the node isn&#039;t running, do instant update
            set nc[this] = nc[this] - 1
            
            //destroy node
            if (not nr[this] and 0 == nc[this]) then
                //handle timer
                call PauseTimer(tm[this])
                
                if (nprm[h]) then
                    debug call PauseTimer(DEB.tm2[this])
                    debug call DestroyTimer(DEB.tm2[this])
                    debug set DEB.tm2[this] = null
                    
                    //remove node from timeout list
                    if (lf[nh[this]] == this) then
                        set lf[nh[this]] = ln[this]
                    endif
                    
                    set ln[lp[this]] = ln[this]
                    set lp[ln[this]] = lp[this]
                    
                    if (ln[this] == this) then
                        set lf[nh[this]] = 0
                    endif
                    
                    //recycler
                    set lr[this] = lr[0]
                    set lr[0] = this
                    debug set DEB.al[this] = false
                endif
            endif
        endmethod
    endstruct
    struct Timer extends array
        static method operator [] takes real timeout returns thistype
            local integer i = R2I(timeout*100)
            
            if (0 == i) then
                return 1
            elseif (8191 &lt; i) then
                return 8191
            endif
            
            return i
        endmethod
        
        method operator list takes nothing returns TimerList
            local integer t = lf[this]      //first timeout node
            local integer l
            local real tl
            local real a                    //timeout
            local integer n
            
            //if no first timeout node
            if (0 == t) then
                set a = this/100.
                
                //allocate
                set t = AL()
                
                debug set DEB.tm2[this] = CreateTimer()
                
                //initialize list
                set lf[this] = t
                set lp[t] = t
                set ln[t] = t
                
                if (not nprm[this]) then
                    set tl = Ln(RELATIVE_MERGE/a)/230.258509*a
                    if (tl &lt; CONSTANT_MERGE) then
                        set tl = CONSTANT_MERGE
                    endif
                    set nprm[this] = CONSTANT_MERGE &lt; (a-tl)/2
                endif
                
                if (nprm[this]) then
                    debug if (DEB.en) then
                        //start timer
                        call TimerStart(tm[t], a, true, function EXP)
                        debug call TimerStart(DEB.tm2[t], a, true, null)
                    debug endif
                endif
                
                //head is always timeout for a timeout node
                set nh[t] = this
                
                //not adding after (added now)
                set aa[t] = false
                
                return t
            endif
            
            if (nprm[this]) then
                set a = this/100.
                
                //calculate relative timeout limit
                set tl = Ln(RELATIVE_MERGE/a)/230.258509*a
                
                //if the relative limit is less than the constant limit
                //make it the constant limit
                if (tl &lt; CONSTANT_MERGE) then
                    set tl = CONSTANT_MERGE
                endif
                
                set l = lp[t]
                if (TimerGetElapsed(tm[t]) &lt; tl) then
                    //merge first node before
                    set aa[t] = false
                    return t
                elseif (TimerGetRemaining(tm[l]) &lt; tl) then
                    //merge last node after
                    set aa[l] = true
                    return l
                elseif (TimerGetElapsed(tm[l]) &lt; tl) then
                    //merge last node before
                    set aa[l] = false
                    return l
                endif
            
                //create new node
                set n = AL()
                debug if (DEB.en) then
                    call TimerStart(tm[n], a, true, function EXP)
                    debug set DEB.tm2[n] = CreateTimer()
                    debug call TimerStart(DEB.tm2[n], a, true, null)
                debug endif
                
                //add to list
                set ln[l] = n
                set lp[t] = n
                set ln[n] = t
                set lp[n] = l
                
                set nh[n] = this
                
                //add immediately
                set aa[n] = false
                
                return n
            endif
            
            return t
        endmethod
    endstruct
    
    /*
    *   Allocate module node
    */
    private function ALM takes nothing returns integer
        local integer i = mlr[0]
        
        debug if (DEB.alm<i>) then
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;DOUBLE ALLOCATE: &quot;+I2S(i))
            debug call DIS()
            debug return 0
        debug endif
        
        if (0 == i) then
            set mlc = mlc + 1
            debug if (8191 &lt; mlc) then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;INSTANCE OVERLOAD&quot;)
                debug call DIS()
            debug endif
            debug set DEB.alm[mlc] = true
            return mlc
        endif
        
        set mlr[0] = mlr<i>
        set mp<i> = false
        debug set DEB.alm<i> = true
        
        return i
    endfunction
    
    private function RegisterMethod takes integer this, integer t, integer i, boolean after returns nothing
        //this: timeout head
        //t: node timeout head
        //i: module node
        
        //always add to true list for an instant update
        call ADDMD(i, t)
        
        //if not adding after, can add to actual list as well
        if (not after) then
            call ADDM(i, t)
        else
            //add to add after list
            set t = mlfp[this]
            if (0 == t) then
                set mlfp[this] = i
                set mlnp<i> = 0
                set mlpp<i> = 0
            else
                call ADDMP(i, t)
            endif
        endif
        
        //only called when a timer method is initialized (no first)
    endfunction
    
    function TimerMethodAddInstance takes TimerList this, integer o returns integer
        local integer t = LoadInteger(mlt, -o, this)        //mlf2
        local integer n
        
        //return head (timeout)
        set mnp = this
        
        //if no first, initialize
        if (0 == t) then
            set t = ALM()
            call SaveInteger(mlt, -o, -this, t)       //mlf, first
            call SaveInteger(mlt, -o, this, t)      //mlf2, future first
            call SaveInteger(mlt, o, -this, this.register(mb[o]))        //mnt, condition
            
            //initialize lists
            set mlp[t] = 0
            set mln[t] = 0
            
            set mlpd[t] = 0
            set mlnd[t] = 0
            
            //head, origin (o = timer method)
            set mnh[t] = this
            set mno[t] = o
            
            //first, future first
            set mnf = t
            set mnft = t
            
            return t
        endif
        
        //first and future first will always be t as n is always added to t
        set mnf = t
        set mnft = t
        
        set n = ALM()
        
        //head is timeout, o is method
        set mnh[n] = this
        set mno[n] = o
        
        //add n to t
        call RegisterMethod(this, t, n, aa[this])
        
        return n
    endfunction
    function TimerMethodRemoveInstance takes integer i returns nothing
        local TimerList this = mnh<i>                           //timeout
        local integer o = mno<i>                                //method
        local integer f = LoadInteger(mlt, -o, this)            //mlf2
        
        debug if (not DEB.alm<i>) then
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;Attempted To Deallocate Null: &quot;+I2S(i))
            debug call DIS()
            debug return
        debug endif
        
        //update future*** first
        if (f == i) then
            set f = mlnd<i>
            if (0 == f) then
                call this.unregister(LoadInteger(mlt, o, -this))
            endif
            call SaveInteger(mlt, -o, this, f)      //mlf2
        endif
        set mnft = f
        
        //remove from true list
        call REMMD(i, f)
        
        //if set to add after, remove from add after list, first does not change
        if (mlap<i>) then
            set mnf = LoadInteger(mlt, -o, -this)     //mlf, first
            
            //remove from to add after list
            call REMMP(i, mlfp[this])
            
            //set to add after to false
            set mlap<i> = false
            
            //delete
            call DELM(i)
        else
            //if node is running, mark to destroy after
            if (nr[this]) then
                call DAF(i)
                //first doesn&#039;t change
                set mnf = LoadInteger(mlt, -o, -this)     //mlf
            else
                //first can** change
                set f = LoadInteger(mlt, -o, -this)       //mlf
                
                //if deallocated node is first, set first to next
                if (f == i) then
                    set f = mln<i>
                endif
                
                //remove from list
                call REMM(i, f)
                
                //delete
                call DELM(i)
                
                //update first
                call SaveInteger(mlt, -o, -this, f)       //mlf
                
                //return possibly new first
                set mnf = f
            endif
        endif
    endfunction
    function TimerMethodAddInstanceC takes TimerList this, integer o returns integer
        local integer t = mpff[o]
        local integer n
        
        debug if (nprm[nh[this]]) then
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;ATTEMPTED TO USE CONSTANT MERGE FOR NON CONSTANT MERGE TIMEOUT&quot;)
            debug call DIS()
        debug endif
        
        //if no first, initialize
        if (0 == t) then
            set t = ALM()
            set mp[t] = true
            
            set mpf[o] = t
            set mpff[o] = t
            set mptc[o] = this.register(mb[o])
            
            //initialize lists
            set mlp[t] = 0
            set mln[t] = 0
            
            set mlpd[t] = 0
            set mlnd[t] = 0
            
            //head, origin (o = timer method)
            set mnh[t] = this
            set mno[t] = o
            
            return t
        endif
        
        set n = ALM()
        set mp[n] = true
        
        //head is timeout, o is method
        set mnh[n] = this
        set mno[n] = o
        
        //add n to t
        call RegisterMethod(this, t, n, aa[this])
        
        return n
    endfunction
    function TimerMethodRemoveInstanceC takes integer i returns nothing
        local TimerList this = mnh<i>                           //timeout
        local integer o = mno<i>                                //method
        local integer f = mpff[o]
        
        debug if (nprm[nh[this]]) then
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;ATTEMPTED TO USE CONSTANT MERGE FOR NON CONSTANT MERGE TIMEOUT&quot;)
            debug call DIS()
        debug endif
        
        debug if (not DEB.alm<i>) then
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;Attempted To Deallocate Null: &quot;+I2S(i))
            debug call DIS()
            debug return
        debug endif
        
        //update future*** first
        if (f == i) then
            set f = mlnd<i>
            if (0 == f) then
                call this.unregister(mptc[o])
            endif
            set mpff[o] = f
        endif
        
        //remove from true list
        call REMMD(i, f)
        
        //if set to add after, remove from add after list, first does not change
        if (mlap<i>) then
            //remove from to add after list
            call REMMP(i, mlfp[this])
            
            //set to add after to false
            set mlap<i> = false
            
            //delete
            call DELM(i)
        else
            //if node is running, mark to destroy after
            if (nr[this]) then
                call DAF(i)
            else
                //first can** change
                set f = mpf[o]
                
                //if deallocated node is first, set first to next
                if (f == i) then
                    set f = mln<i>
                endif
                
                //remove from list
                call REMM(i, f)
                
                //delete
                call DELM(i)
                
                //update first
                set mpf[o] = f
            endif
        endif
    endfunction
    
    function CreateTimerMethod takes boolexpr b returns integer
        //allocate new timer method (simple)
        set mtfr = mtfr + 1
        set mb[mtfr] = b
        return mtfr
    endfunction
    function TimerGetElapsedEx takes integer i returns real
        return TimerGetElapsed(tm[mnh<i>])
    endfunction
    function TimerGetRemainingEx takes integer i returns real
        return TimerGetRemaining(tm[mnh<i>])
    endfunction
    function TimerGetTimeoutEx takes integer i returns real
        return TimerGetTimeout(tm[mnh<i>])
    endfunction
    function GetTimerFirstInstance takes integer timerMethod returns integer
        return LoadInteger(mlt, -timerMethod, -exp)       //mlf
    endfunction
    function GetTimerNext takes integer timerInstance returns integer
        return mln[timerInstance]
    endfunction
    function GetTimerFirst takes nothing returns integer
        return mnf
    endfunction
    function GetTimerFutureFirst takes nothing returns integer
        return mnft
    endfunction
    function GetTimerParent takes nothing returns integer
        return mnp
    endfunction
    function GetTimerFirstC takes integer o returns integer
        return mpf[o]
    endfunction
    module CTM
        debug private static integer created = 0
        debug private static boolean array timerH
        debug private static integer timerCount = 0
        static integer CTMf             //timer method
        static integer array CTMq       //current first
        static integer array CTMqt      //future first
        
        static method create takes real t returns thistype
            local integer i
            local integer n
            
            debug if (DEB.en) then
                //get new instance
                set i = TimerMethodAddInstance(Timer[t].list, CTMf)
                
                debug if (DEBI.SUGGEST_MODULE_CHANGES) then
                    debug set created = created + 1
                    debug if (not timerH[Timer[t]]) then
                        debug set timerH[Timer[t]] = true
                        debug set timerCount = timerCount + 1
                    debug endif
                    debug if (created &gt; 30 and timerCount == 1) then
                        debug if (CMQ(t)) then
                            debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;IT APPEARS THAT THIS MODULE IS A CONSTANT MERGE TIMEOUT, TRY USING CTTC INSTEAD OF CTM: &quot;+R2S(t))
                        debug else
                            debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;IT APPEARS THAT THIS MODULE IS A CONSTANT TIMEOUT, TRY USING CTT INSTEAD OF CTM: &quot;+R2S(t))
                        debug endif
                        debug call DIS()
                    debug endif
                debug endif
                
                //update firsts
                set CTMq[mnp] = mnf
                set CTMqt[mnp] = mnft
                
                debug if (mnf != LoadInteger(mlt, -CTMf, -mnh<i>)) then       //mlf
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;CREATE FIRST DESYNC: &quot;+I2S(mnf)+&quot;!=&quot;+I2S(LoadInteger(mlt, -CTMf, -mnh<i>)))  //mlf
                    debug call DIS()
                debug endif
                debug if (mnft != LoadInteger(mlt, -CTMf, mnh<i>)) then         //mlf2
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;CREATE FIRST DESYNC TEMP: &quot;+I2S(mnft)+&quot;!=&quot;+I2S(LoadInteger(mlt, -CTMf, mnh<i>)))       //mlf2
                    debug call DIS()
                debug endif
                
                return i
            debug endif
            debug return 0
        endmethod
        method destroy takes nothing returns nothing
            local integer n
            
            debug if (DEB.en) then
                //destroy
                call TimerMethodRemoveInstance(this)
                
                //update firsts
                set CTMq[mnh[this]] = mnf
                set CTMqt[mnh[this]] = mnft
                
                debug if (mnf != LoadInteger(mlt, -CTMf, -mnh[this])) then        //mlf
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;DESTROY FIRST DESYNC: &quot;+I2S(mnf)+&quot;!=&quot;+I2S(LoadInteger(mlt, -CTMf, -mnh[this])))        //mlf
                    debug call DIS()
                debug endif
                debug if (mnft != LoadInteger(mlt, -CTMf, mnh[this])) then      //mlf2
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;DESTROY FIRST DESYNC TEMP: &quot;+I2S(mnft)+&quot;!=&quot;+I2S(LoadInteger(mlt, -CTMf, mnh[this])))       //mlf2
                    debug call DIS()
                debug endif
            debug endif
        endmethod
        method operator elapsed takes nothing returns real
            return TimerGetElapsedEx(this)
        endmethod
        method operator remaining takes nothing returns real
            return TimerGetRemainingEx(this)
        endmethod
        method operator timeout takes nothing returns real
            return TimerGetTimeoutEx(this)
        endmethod
        static method CTMe takes nothing returns boolean
            //get first
            local thistype this = CTMq[exp]
            debug local boolean array hit
    endmodule
    module CTMExpire
            implement CTM
            debug if (not DEB.en) then
                debug return false
            debug endif
            debug if (CTMq[exp] != LoadInteger(mlt, -CTMf, -exp)) then        //mlf
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG FIRST DESYNC: &quot;+I2S(CTMq[exp])+&quot;!=&quot;+I2S(LoadInteger(mlt, -CTMf, -exp))+&quot; from &quot;+I2S(exp))        //mlf
                debug call DIS()
                debug return false
            debug endif
            debug if (CTMqt[exp] != LoadInteger(mlt, -CTMf, exp)) then      //mlf2
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG FIRST DESYNC TEMP: &quot;+I2S(CTMqt[exp])+&quot;!=&quot;+I2S(LoadInteger(mlt, -CTMf, exp))+&quot; from &quot;+I2S(exp))     //mlf2
                debug call DIS()
                debug return false
            debug endif
            debug if (0 == this) then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;NULL TIMER EXPIRED ON &quot;+I2S(exp))
                debug call DIS()
            debug endif
            //loop through all instances
            loop
                exitwhen 0 == this
                debug if (hit[this]) then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG: HIT &quot;+I2S(this)+&quot; MORE THAN ONCE FROM &quot;+I2S(exp))
                    debug call DIS()
                    debug return false
                debug endif
                debug set hit[this] = true
    endmodule
    module CTMNull
                debug if (mnh[this] != exp) then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG: &quot;+I2S(this)+&quot; &quot;+I2S(mnh[this])+&quot;!=&quot;+I2S(exp)+&quot; from &quot;+I2S(exp))
                    debug call DIS()
                    debug return false
                debug endif
                debug if (not DEB.alm[this]) then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG NOT ALLOCATED: &quot;+I2S(this)+&quot; from &quot;+I2S(exp))
                    debug call DIS()
                    debug return false
                debug endif
                set this = mln[this]
            endloop
            //update first to future first
            set CTMq[exp] = CTMqt[exp]
    endmodule
    module CTMEnd
        implement CTMNull
            return false
        endmethod
        private static method onInit takes nothing returns nothing
            //initialize timer method
            set CTMf = CreateTimerMethod(Condition(function thistype.CTMe))
        endmethod
    endmodule
    
    module CTT
        static Timer CTMt               //timout
        static integer CTMf             //timer method
        static integer array CTMq       //current first
        static integer array CTMqt      //future first
        
        static method create takes nothing returns thistype
            local integer i
            local integer n
            
            debug if (DEB.en) then
                //get new instance
                set i = TimerMethodAddInstance(CTMt.list, CTMf)
                
                //update firsts
                set CTMq[mnp] = mnf
                set CTMqt[mnp] = mnft
                
                debug if (mnf != LoadInteger(mlt, -CTMf, -mnh<i>)) then       //mlf
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;CREATE FIRST DESYNC: &quot;+I2S(mnf)+&quot;!=&quot;+I2S(LoadInteger(mlt, -CTMf, -mnh<i>)))  //mlf
                    debug call DIS()
                debug endif
                debug if (mnft != LoadInteger(mlt, -CTMf, mnh<i>)) then         //mlf2
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;CREATE FIRST DESYNC TEMP: &quot;+I2S(mnft)+&quot;!=&quot;+I2S(LoadInteger(mlt, -CTMf, mnh<i>)))       //mlf2
                    debug call DIS()
                debug endif
                
                return i
            debug endif
            debug return 0
        endmethod
        method destroy takes nothing returns nothing
            local integer n
            
            debug if (DEB.en) then
                //destroy
                call TimerMethodRemoveInstance(this)
                
                //update firsts
                set CTMq[mnh[this]] = mnf
                set CTMqt[mnh[this]] = mnft
                
                debug if (mnf != LoadInteger(mlt, -CTMf, -mnh[this])) then        //mlf
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;DESTROY FIRST DESYNC: &quot;+I2S(mnf)+&quot;!=&quot;+I2S(LoadInteger(mlt, -CTMf, -mnh[this])))        //mlf
                    debug call DIS()
                debug endif
                debug if (mnft != LoadInteger(mlt, -CTMf, mnh[this])) then      //mlf2
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;DESTROY FIRST DESYNC TEMP: &quot;+I2S(mnft)+&quot;!=&quot;+I2S(LoadInteger(mlt, -CTMf, mnh[this])))       //mlf2
                    debug call DIS()
                debug endif
            debug endif
        endmethod
        method operator elapsed takes nothing returns real
            return TimerGetElapsedEx(this)
        endmethod
        method operator remaining takes nothing returns real
            return TimerGetRemainingEx(this)
        endmethod
        method operator timeout takes nothing returns real
            return TIMEOUT
        endmethod
        static method CTMe takes nothing returns boolean
            //get first
            local thistype this = CTMq[exp]
            debug local boolean array hit
    endmodule
    module CTTExpire
            implement CTT
            debug if (not DEB.en) then
                debug return false
            debug endif
            debug if (CTMq[exp] != LoadInteger(mlt, -CTMf, -exp)) then        //mlf
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG FIRST DESYNC: &quot;+I2S(CTMq[exp])+&quot;!=&quot;+I2S(LoadInteger(mlt, -CTMf, -exp))+&quot; from &quot;+I2S(exp))        //mlf
                debug call DIS()
                debug return false
            debug endif
            debug if (CTMqt[exp] != LoadInteger(mlt, -CTMf, exp)) then      //mlf2
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG FIRST DESYNC TEMP: &quot;+I2S(CTMqt[exp])+&quot;!=&quot;+I2S(LoadInteger(mlt, -CTMf, exp))+&quot; from &quot;+I2S(exp))     //mlf2
                debug call DIS()
                debug return false
            debug endif
            debug if (0 == this) then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;NULL TIMER EXPIRED ON &quot;+I2S(exp))
                debug call DIS()
            debug endif
            //loop through all instances
            loop
                exitwhen 0 == this
                debug if (hit[this]) then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG: HIT &quot;+I2S(this)+&quot; MORE THAN ONCE FROM &quot;+I2S(exp))
                    debug call DIS()
                    debug return false
                debug endif
                debug set hit[this] = true
    endmodule
    module CTTNull
                debug if (mnh[this] != exp) then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG: &quot;+I2S(this)+&quot; &quot;+I2S(mnh[this])+&quot;!=&quot;+I2S(exp)+&quot; from &quot;+I2S(exp))
                    debug call DIS()
                    debug return false
                debug endif
                debug if (not DEB.alm[this]) then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG NOT ALLOCATED: &quot;+I2S(this)+&quot; from &quot;+I2S(exp))
                    debug call DIS()
                    debug return false
                debug endif
                set this = mln[this]
            endloop
            //update first to future first
            set CTMq[exp] = CTMqt[exp]
    endmodule
    module CTTEnd
        implement CTTNull
            return false
        endmethod
        private static method onInit takes nothing returns nothing
            //initialize timer method
            set CTMf = CreateTimerMethod(Condition(function thistype.CTMe))
            set CTMt = Timer[TIMEOUT]
            
            debug if (CMQ(TIMEOUT)) then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;THIS MODULE SHOULD BE CTTC AS TIMEOUT QUALIFIES FOR CONSTANT MERGE: &quot;+R2S(TIMEOUT))
                debug call DIS()
            debug endif
        endmethod
    endmodule
    module CTTC
        static TimerList CTMt           //timout list
        static integer CTMf             //timer method
        
        static method create takes nothing returns thistype
            local integer i
            local integer n
            
            debug if (DEB.en) then
                //get new instance
                set i = TimerMethodAddInstanceC(CTMt, CTMf)
                
                return i
            debug endif
            debug return 0
        endmethod
        method destroy takes nothing returns nothing
            local integer n
            
            debug if (DEB.en) then
                //destroy
                call TimerMethodRemoveInstanceC(this)
            debug endif
        endmethod
        method operator elapsed takes nothing returns real
            return TimerGetElapsedEx(this)
        endmethod
        method operator remaining takes nothing returns real
            return TimerGetRemainingEx(this)
        endmethod
        method operator timeout takes nothing returns real
            return TIMEOUT
        endmethod
        static method CTMe takes nothing returns boolean
            //get first
            local thistype this = mpf[CTMf]
            debug local boolean array hit
    endmodule
    module CTTCExpire
            implement CTTC
            debug if (not DEB.en) then
                debug return false
            debug endif
            debug if (0 == this) then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;NULL TIMER EXPIRED ON &quot;+I2S(exp))
                debug call DIS()
            debug endif
            //loop through all instances
            loop
                exitwhen 0 == this
                debug if (hit[this]) then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG: HIT &quot;+I2S(this)+&quot; MORE THAN ONCE FROM &quot;+I2S(exp))
                    debug call DIS()
                    debug return false
                debug endif
                debug set hit[this] = true
    endmodule
    module CTTCNull
                debug if (mnh[this] != exp) then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG: &quot;+I2S(this)+&quot; &quot;+I2S(mnh[this])+&quot;!=&quot;+I2S(exp)+&quot; from &quot;+I2S(exp))
                    debug call DIS()
                    debug return false
                debug endif
                debug if (not DEB.alm[this]) then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;BUG NOT ALLOCATED: &quot;+I2S(this)+&quot; from &quot;+I2S(exp))
                    debug call DIS()
                    debug return false
                debug endif
                set this = mln[this]
            endloop
    endmodule
    module CTTCEnd
        implement CTTCNull
            return false
        endmethod
        private static method onInit takes nothing returns nothing
            //initialize timer method
            set CTMf = CreateTimerMethod(Condition(function thistype.CTMe))
            set CTMt = Timer[TIMEOUT].list
            debug if (nprm[Timer[TIMEOUT]]) then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,&quot;ATTEMPTED TO USE CONSTANT MERGE MODULE FOR NON CONSTANT MERGE TIMEOUT: &quot;+R2S(TIMEOUT))
                debug call DIS()
            debug endif
        endmethod
    endmodule
endlibrary
</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top