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.
  • Varine Varine:
    I ordered like five blocks for 15 dollars. They're just little aluminum blocks with holes drilled into them
  • Varine Varine:
    They are pretty much disposable. I have shitty nozzles though, and I don't think these were designed for how hot I've run them
  • Varine Varine:
    I tried to extract it but the thing is pretty stuck. Idk what else I can use this for
  • Varine Varine:
    I'll throw it into my scrap stuff box, I'm sure can be used for something
  • Varine Varine:
    I have spare parts for like, everything BUT that block lol. Oh well, I'll print this shit next week I guess. Hopefully it fits
  • Varine Varine:
    I see that, despite your insistence to the contrary, we are becoming a recipe website
  • Varine Varine:
    Which is unique I guess.
  • The Helper The Helper:
    Actually I was just playing with having some kind of mention of the food forum and recipes on the main page to test and see if it would engage some of those people to post something. It is just weird to get so much traffic and no engagement
  • The Helper The Helper:
    So what it really is me trying to implement some kind of better site navigation not change the whole theme of the site
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air

      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