System TimerCompressor Library

Nestharus

o-o
Reaction score
84
Nope. I already told you the best method. If you have any sort of search or iteration over any time other than the one current expiring, then something is wrong and you won't be able to outclass regular wc3 timers. Let timers handle their own stuff. Like I said, the problem is many timers expiring at once, not many timers running >.>.
 

GFreak45

I didnt slap you, i high 5'd your face.
Reaction score
130
it is only the one currently expiring, the only itteration is when creating a new timer

this is what i was meaning, i wrote this up in my free time at work, so dont expect it to be pristine or complete:

JASS:
library TimerSync/* v1.00
  */ uses /*
        */optional Timer32/*

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*                                                                         *
*    API:                                                                 *
*                                                                         *
*    function NewTimer takes real timeout, code c, returns integer        *
*        creates a new timer and returns the integer id of the timer.     *
*        (these can overlap without creating problems)                    *
*                                                                         *
*    function MinimizeTimerSync takes real sizedesired returns nothing    *
*        deallocates the instances required to reduce the current list    *
*        to the desired size (does overwrite timers when not in debug     *
*        mode but wont when in debug mode and will give an error msg)     *
*                                                                         *
*                                                                         *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

    globals/*

===========================================================================
============================== USER SETTINGS ==============================
===========================================================================
*/
        private constant real INIT_SIZE = 40.0
      //this is the initial size of the list, should be a multiple of
      //FIRST_DIMENSION_GAP

        private constant real FIRST_DIMENSION_GAP = 4.0
      //this is the gap between timers in organization in the first
      //dimension, this should be a multiple of SECOND_DIMENSION_GAP

        private constant real SECOND_DIMENSION_GAP = 0.5
      //this is the gap between timers organization in the second
      //dimension, it is best if this small, but it MUST be smaller
      //than or equal to FIRST_DIMENSION_GAP, should be a multiple of
      //COMBINE_GAP

        private cosntant real COMBINE_GAP = 0.1
      //this is how close the timers must be to each other in order to
      //combine them, it is best if this is small, but it MUST be smaller
      //than or equal to SECOND_DIMENSION_GAP

        private constant boolean USE_T32 = true
      //this should only be false if you DO have Timer32 and DO NOT want
      //to use the same interval but it does not matter at all if you are
      //not using Timer32

        private real INTERVAL = 0.025
      /*this is the interval that it updates the time on the timers left,
        this is only used if Timer32 is not available or USE_T32 is false

===========================================================================
============================ END USER SETTINGS ============================
===========================================================================

      */private real SIZE = INIT_SIZE
    
    endglobals

    //! textmacro VARS takes D
        static real time$D$
        static thistype headLoc$D$
        thistype next$D$
        thistype previous$D$
    //! endtextmacro

    private struct TS extends array
        boolean timerFire
        debug boolean timerEncapsuled
        trigger trig
        //! runtextmacro VARS("FD")
        //! runtextmacro VARS("SD")
        //! runtextmacro VARS("CD")
        //yup im that lazy <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite7" alt=":p" title="Stick Out Tongue    :p" loading="lazy" data-shortname=":p" />

        static integer array reycleList
        static integer lastRecycled = 0
        static integer listSize = 0

        method deallocateCD takes nothing returns nothing
            set this.nextCD.previousCD = this.previousCD
            set this.previousCD.nextCD = this.nextCD
            call DestroyTrigger(this.trig)
            set this.timerFire = false
            set .recycleList[.lastRecycled] = this
            set .recycleList[this] = 0
            set .lastRecycled = this
        endmethod

        method deallocateSD takes nothing returns nothing
            set this.nextSD.previousSD = this.previousSD
            set this.previousSD.nextSD = this.nextSD
            call DestroyTrigger(this.trig)
            set this.timerFire = false
            set .recycleList[.lastRecycled] = this
            set .recycleList[this] = 0
            set .lastRecycled = this
        endmethod

        method deallocateFD takes nothing returns nothing
            set this.nextFD.previousFD = this.previousFD
            set this.previousFD.nextFD = this.nextFD
            call DestroyTrigger(this.trig)
            set this.timerFire = false
            debug set this.timerEncapsuled = false
            set .recycleList[.lastRecycled] = this
            set .recycleList[this] = 0
            set .lastRecycled = this
        endmethod

        private static method allocate takes nothing returns nothing
            local integer this = .recycleList[0]
            if this == 0 then
                debug if .listSize &gt; 8190 then
                debug     call BJDebugMsg(&quot;TimerSync ERROR: you have exceded the maximum timer sync length of &quot;+I2S(R2I(8190 * INTERVAL + 0.5))+&quot; seconds, use a generic timer.&quot;)
                debug     return 0
                debug endif
                set .listSize = .listSize + 1
                return .listSize
            endif
            set .recycleList[0] = .recycleList[this]
            set .recycleList[this] = 0
            return this
        endmethod

        private static method extend takes nothing returns nothing
            local thistype this = thistype.allocate()
            local thistype thisSD = this
            local thistype thisCD = this
            local integer a = R2I(FIRST_DIMENSION_GAP/SECOND_DIMENSION_GAP + 0.5) - 1
            local integer b
            if this != 0 then
                set this.nextFD = .headLocFD
                set this.previousFD = .headLocFD.previousFD
                set .headLocFD.previousFD = this
                set this.previousFD.nextFD = this
                set this.nextSD = this
                set this.previousSD = this
                set this.nextCD = this
                set this.previousCD = this
                set this.timerFire = false
                set this.trig = CreateTrigger()
                loop
                    exitwhen a == 0
                    if thisSD != 0 then
                        set thisSD.nextSD = this
                        set thisSD.previousSD = this.previousSD
                        set thisSD.nextSD.previousSD = thisSD
                        set thisSD.previousSD.nextSD = thisSD
                        set b = R2I(SECOND_DIMENSION_GAP + 0.5) - 1
                        set thisCD = thisSD
                        loop
                            exitwhen b == 0
                            if thisCD != 0 then
                                set thisCD.nextCD = thisSD
                                set thisCD.previousCD = this.previousCD
                                set thisCD.nextCD.previousCD = thisCD
                                set thisCD.previousSD.nextCD = thisCD
                                set thisCD.timerFire = false
                                set thisCD.trig = CreateTrigger()
                                set thisCD = thistype.allocate
                            endif
                            set b = b - 1
                        endloop
                        set thisSD = thistype.allocate()
                    endif
                    set a = a - 1
                endloop
                set SIZE + FIRST_DIMENSION_GAP
            endif
        end method

        static if LIBRARY_T32 and USE_T32 then
            private method periodic takes nothing returns nothing
        else
            private static method periodic takes nothing returns nothing
        endif
            set .timeFD = .timeFD + INTERVAL
            set .timeSD = .timeSD + INTERVAL
            set .headLocCD = .headLocCD.next
            if .timeSD &gt;= SECOND_DIMENSION_GAP then
                set .timeSD = 0
                set .headLocSD = .headLocSD.nextSD
                set .headLocCD = .headLocSD
            endif
            if .timeFD &gt;= FIRST_DIMENSION_GAP then
                set .timeFD = 0
                if .headLocFD.next == 0 then
                    set .headLocFD = thistype(0).nextFD
                else
                    set .headLocFD = .headLocFD.nextFD
                endif
                set .headLocSD = .headLocFD
                set .headLocCD = .headLocFD
            endif
            if .headLocCB.timerFire then
                call TriggerEvaluate(.headLocCB.trig)
                call TriggerClearConditions(.headLocCB.trig)
            endif
        endmethod

        static if USE_T32 then
            implement optional T32x
        else

        private static method onInit takes nothing returns nothing
            local timer t = CreateTimer()
            local integer i = R2I((INIT_SIZE/FIRST_DIMENSION_GAP) + 0.5)
            if LIBRARY_T32 and USE_T32 then
                set INTERVAL = T32_PERIOD
                call DestroyTimer(t)
            else
                call TimerStart(t, INTERVAL, true, function thistype.periodic)
            endif
            loop
                exitwhen i = 0
                call thistype(0).extend()
                set i = i - 1
            endloop
            set t = null
        endmethod

    endstruct

    function NewTimer takes real timeout, code c returns integer
        local TS this = TS.headLocFD
        local real time = timeout
        debug if timeout &lt;= 8190 * INTERVAL then
            if SIZE &lt; timeout then
                set this = this.previousFD
                set time = time - I2R(R2I(FIRST_DIMENSION_GAP*(I2R(R2I(time/FIRST_DIMENSION_GAP)))))
                loop
                    exitwhen SIZE &gt;= timeout
                    call TS.extend()
                    set this = this.nextFD
                endloop
            endif
            loop
                exitwhen time - FIRST_DIMENSION_GAP &lt; 0.0
                set time = time - FIRST_DIMENSION_GAP
                set this = this.nextFD
            endloop
            loop
                exitwhen time - SECOND_DIMENSION_GAP &lt; 0.0
                set time = time - SECOND_DIMENSION_GAP
                set this = this.nextSD
            endloop
            loop
                exitwhen time - COMBINE_GAP &lt; 0.0
                set time = time - COMBINE_GAP
                set this = this.nextCD
            endloop
            set this.timerFire = true
            debug set this.nextFD.previousFD.timerEncapsuled = true
            return this
        debug else
        debug     call BJDebugMsg(&quot;TimerSync ERROR: you have exceded the maximum timer sync length of &quot;+I2S(R2I(8190 * INTERVAL + 0.5))+&quot; seconds, use a generic timer.&quot;)
        debug endif
        debug return 0
    endfunction

    function MinimizeTimerSync takes real sizedesired returns nothing
        local TS this = TS.headLocFD.previous
        debug local real size = SIZE - sizedesired
        debug local real time = 0.0
        loop
            exitwhen SIZE - FIRST_DIMENSION_GAP &lt;= sizedesired
            debug exitwhen this.timerEncapsuled
            if this.nextCD == this.nextSD.previousSD then
                if this.nextSD == this.nextFD.previousFD then
                    set this = this.previousFD
                    call TS(this.nextFD).deallocateFD()
                    set SIZE = SIZE - FIRST_DIMENSION_GAP
                    debug set time = time + FIRST_DIMENSION_GAP
                else
                    set this = this.nextSD
                    call TS(this.previousSD).deallocateSD()
                endif
            else
                set this = this.nextCD
                call TS(this.previousCD).deallocateCD()
            endif
        endloop
        debug if time + FIRST_DIMENSION_GAP &gt;= size
        debug     call BJDebugMsg(&quot;TimerSync Message: Successfully reduced the size of the Timer Sync list by &quot;+R2I(time)+&quot; seconds.&quot;)
        debug else
        debug     call BJDebugMsg(&quot;TimerSync ERROR: You attempted to deallocate &quot;+I2S(this)+&quot; and it currently contains a timer.  You can not do this in debug mode.&quot;)
        debug endif
    endfunction
endlibrary


so it only checks the currently expiring one on the periodic, and changes the head's location on the list, but when you create a new one, it has to itterate through it to see where to place it on the list, but i have minimize the itteration to ~1/40th of what it would be usually
 

Nestharus

o-o
Reaction score
84
If the timeouts are the same, it is always added to the last node.


If the timeouts are different, you are going to run into serious problems on expiration (serious problems).
 

GFreak45

I didnt slap you, i high 5'd your face.
Reaction score
130
How do you get that? It adds all new timers within a certain period of time to a single trigger... the interval is adjustable to be more or less accurate

The max difference in expire time is the COMBINE_GAP... and timers can be added at any interval based on the total time using the NewTimer func, I plan on adding anotherlist to manage the code for each timer and make it more functional/less rigid
 

GFreak45

I didnt slap you, i high 5'd your face.
Reaction score
130
EDIT:
if you read this post before i updated it, my bad my JH was off but i forgot
i re-updated it now, compiles, will re-update again when its working better, has a few kinks to work out

unless this is a trully ineficient way of doing it...
tell me now :/
 

Nestharus

o-o
Reaction score
84
Ok... so yours is just approaching what mine currently is, lol...


Things to be improved:

Your combination algorithm is bad. Yours is just a constant (COMBINE_GAP). Mine is an equation and handles larger timeouts much better.
You will end up needing to go out to modules to handle lists of the same method (which is what mine already does).

So if you piece this one together with mine (mine's only missing the 1 trigger per module of a given timeout in the core), you'll have the perfect timer system ; ).


Also, your adding needs to use a table, not a loop.
 

GFreak45

I didnt slap you, i high 5'd your face.
Reaction score
130
never used a table before how would that look? and i was planning on making this a lot more complex
if i can add another list of the conditions and include trigger sleeps i could get it to be combining them into one trigger, in a much larger time, and still be exact.

I was thinking about adding backtracking if the timeout > half the current SIZE

ALSO:
something i didnt like:

JASS:
static if LIBRARY_T32 then
    implement T32x
endif

gives a syntax error if T32 is not available... any way around this? Textmacro perhaps?

Later tonight ill probably make a new thread and close this one, and center it around what i have now, since it goes about the problem an entirely different way

EDIT:
Thanks for actually going through it Nest
 

GFreak45

I didnt slap you, i high 5'd your face.
Reaction score
130
oh never knew that... ill adjust that
never had to have an optional implementation in anything before

EDIT:
Apparently it didnt update the code last night when i hit save on the edit post... ill re-update when i get home, this one is the one that does not compile
 

Nestharus

o-o
Reaction score
84
Get rid of everything to do with T32 and the interval. You can't do anything with updating the remaining time and you can only run code when a timer actually expires and the only loops you can have in there are loops for calling methods (the methods that are supposed to run on the timer in a given trigger condition).
 

GFreak45

I didnt slap you, i high 5'd your face.
Reaction score
130
that confused me... the code is run off a trigger eval not a timer expiration
or are you saying to run it off a method that calls the boolexprs somehow? because iv been asking around to find out if that is possible...
i can remove T32, i just figured i would use it as an option, and it does update the remaining time already through the use of a dynamic head... with 0 as a static head
 

Nestharus

o-o
Reaction score
84
I didn't read through the code that much, I just saw some alarming things. When you have an interval for updating the time and what not, that tells me that you have some sort of remaining time variable, which you shouldn't have. If you have that, something is wrong ; P.


You shouldn't have any variables to do with the timer's time, 0 : P.


Also, the timers should always be repeating = ).
 

GFreak45

I didnt slap you, i high 5'd your face.
Reaction score
130
i have one as extra protection for the head rotation, it isnt needed but isnt intensive either, its not a variable for each and none of them actually store their current time
the only loop is to go ahead of the current head and place a timer, and its only used on creation of a new timer or allocation of extra time
and it doesnt record the actual time of the timer it does this every interval:
[ljass]set .headLocCD = .headLocCD.next[/ljass]
then it checks that instance for a true/false variable for an expiring timer and fires the trigger if it has one

see how i did that?

PS. what did you mean by timers should always be repeating? that any new timers should be repeating by default?
 

Nestharus

o-o
Reaction score
84
This is what your timer algorithm should be

JASS:

//on timer expire
call TriggerEvaluate(trig[hash(GetHandleId)])


And within the trigger condition (in a module)
JASS:

local thistype this = mtd[first[expired]]
loop
    exitwhen head[this]
    //run timer code
    set this = next[this]
endloop
set first[expired] = next[first[expired]]




add/remove/pause/change time could be a little tricky ; P.
 

GFreak45

I didnt slap you, i high 5'd your face.
Reaction score
130
but what i am asking is how do you run the code without firing another trigger, iv been asking around and searching online but i cant find one...
or does it not matter? I wanted to keep the trig eval count as low as possible
 

Nestharus

o-o
Reaction score
84
but what i am asking is how do you run the code without firing another trigger, iv been asking around and searching online but i cant find one...
or does it not matter? I wanted to keep the trig eval count as low as possible

Only way is to do something like ForForce or w/e with a single player in there. You might as well just use the trigger : P.
 

GFreak45

I didnt slap you, i high 5'd your face.
Reaction score
130
cant you make a force with only player 15 or something in it? and is trigger eval worth avoiding or should i just not care about it?
i thought that was the whole reason for SpellEffectEvent and PlayerUnitEvent etc
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top