• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[Snippet] SimpleTimers

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,550
RecycleTimers

Preface:

After much discussion in another submission thread of mine ( http://www.hiveworkshop.com/forums/submissions-414/snippet-temporaryheroattribute-vjass-216972/ ), I've decided that a very simple, efficient timer recycler is worth posting.

My argument was that using TimerUtils is an overly-targeted system which was designed to be way too configurable and fill way too many uses, when in reality it's just a bloated system. I created an example of a spell using this simple timer recycler ( http://www.hiveworkshop.com/forums/2161637-post62.html ), and with the help of another hive user, realized that my system was not useful because calling jass functions is simply too slow a process to make it worth while, and that at this point simply creating and destroying timers is a better choice.

I admitted that was probably true and moved on, but after a short delay I decided to test this myself ( http://www.hiveworkshop.com/forums/2161951-post64.html ), and found that recycling timers really is worthwhile (At least twice as fast, basically).

So here you are. It's only 20 lines. Any idiot with basic programming knowledge could write their own version that does the same thing and more, but this should be extremely useful for newer vJass/JASS users. Especially those making spells.

Pre-requisites:

The script requires no other libraries to function, but as it is written in vJass, it DOES require JassHelper (Though I recommend just compiling with JNGP).

So what's the difference?

This is probably what you're wondering now. If you're using TimerUtils you're going to wonder what the difference is.

The answer is that this does the "recycle" part of TimerUtils, and nothing else. It won't handle hashtables for you or preload timers. But you can write your own script to do that, and you can handle hashtables yourself! 9 times out of 10, more efficiently than TU does, too.

API:


public static method release takes timer time returns nothing

RecycleTimers.release(<timer>)

This method tells the system to stop using it and store it in a list. The list has no limit to it's size; it's your job to be smart.


public static method get takes nothing returns timer

RecycleTimers.get()

This method gets a timer. If there are none available in the list, it creates a new one. That simple.


The script:


JASS:
library RecycleTimers
    struct SimpleTimers extends array
        private static timer array availableTimers
        private static integer maxAvailable=-1
        
        public static method release takes timer time returns nothing
            call PauseTimer(time)
            set maxAvailable=maxAvailable+1
            set availableTimers[maxAvailable]=time
        endmethod
        
        public static method get takes nothing returns timer
            if maxAvailable==-1 then
                return CreateTimer()
            endif
            set maxAvailable=maxAvailable-1
            return availableTimers[maxAvailable+1]
        endmethod
    endstruct
endlibrary


Example Usage:


JASS:
//uses Knockback3D and RecycleTimers

scope thunderClap initializer i
    globals
        private constant integer THUNDERCLAP_CODE='AHtc'
        private constant real POWER=35.
        private constant real RADIUS=300.
        private constant real TRAJECTORY=bj_PI/16.
        private constant real DELAY=1.
        private group grp=CreateGroup()
        private hashtable ht=InitHashtable()
    endglobals
    
    private function a takes nothing returns nothing
        local timer time=GetExpiredTimer()
        local integer id=GetHandleId(time)
        local real direc
        local unit u=LoadUnitHandle(ht,id,0)
        local real x=GetUnitX(u)
        local real y=GetUnitY(u)
        local unit FoG
        call GroupEnumUnitsInRange(grp,GetUnitX(u),GetUnitY(u),RADIUS,null)
        loop
            set FoG=FirstOfGroup(grp)
            exitwhen FoG==null
            if FoG!=u then
                set direc=Atan2(GetUnitY(FoG)-y,GetUnitX(FoG)-x)
                call Knockback3D_add(FoG,POWER,direc,TRAJECTORY)
            endif
            call GroupRemoveUnit(grp,FoG)
        endloop
        call FlushChildHashtable(ht,id)
        call RecycleTimers.release(time)
        set u=null
        set time=null
    endfunction
    
    private function c takes nothing returns boolean
        local timer time
        if GetSpellAbilityId()==THUNDERCLAP_CODE then
            set time=RecycleTimers.get()
            call SaveUnitHandle(ht,GetHandleId(time),0,GetTriggerUnit())
            call TimerStart(time,DELAY,false,function a)
        endif
        return false
    endfunction
    
    private function i takes nothing returns nothing
        local trigger t=CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,Condition(function c))
        set t=null
    endfunction
endscope


Change Log:

2012.06.05 #2 - Updated the script to "extends array" to make the struct syntax worth while. Also changed the name to RecycleTimers.
2012.06.05 - Initial submission to Hive: Jass Resources: Submissions

Special Thanks:

  • -Kobas- for creating a map submission template, which turned out useful for system submissions as well.
  • Vexorian for developing JassHelper. Without vJass, I almost certainly would not still be scripting for wc3.
  • The various developers of JNGP including PitzerMike and MindworX. Integrating JassHelper and TESH is a godsend.

Q/A:

But all the systems use TU already, and tons of spells too! Are they supposed to update to use yours? No. I don't expect this snippet to be used by public resources, but by users. If a public resource does use this, then you have to make a choice of which libraries to use. Using this in parallel with TU is twice is as bad as using one or the other.

My map uses TU but I want to take advantage of the speed benefit of this! What can I do? Start a new map or keep using TU. It's most likely not worth your time to change spells/systems to use this.

But this is gonna use a bunch of handle id's! Why can't I just destroy my timers? :< If you're worried about your timers using too many handles, you're probably using way too many timers, seeing as how the handle limit is over 8,000, and timers shouldn't even reach 1/100 of that. Regardless, this timer system doesn't give a shit about the source of the timer. You can recycle ones you CreateTimer()'d yourself, and destroy timers you RecycleTimers.get() (got).

Pastebin Mirror (Newest First):


http://pastebin.com/AvuEJT3T
 
Last edited:
You should make that struct extend an array because you're not using allocate or deallocate.
(Meaning, just add extends array to the end of the struct declaration line.
It'll shorten up the code quite a lot. (Relatively speaking that is.)

Personally, I like the names "TimerBin" and "TimerRecycler" better.
SimpleTimers is relatively too verbose because the "Simple" adjective has no place in there D:

Timer would've been fine, but unfortunately, Nestharus' TimerTools uses that for a struct already.
 
Even better:

JASS:
    function ActuallyDestroyTimer takes timer t returns nothing
        call PauseTimer(t)
        call DestroyTimer(t)
    endfunction

Recycling is not worth keeping a larger-than-needed handle stack. Subtraction by 0x100000 is good when you can know the limits of your own script, and I use it in WarChasers II (unreleased version that is).

Also, TimerUtils script is not very large when compiled. Magtheridon96's TimerUtilsEx beats this one pretty heavily as well. I see no reason to include "generic timer recycling system #2304983504".

http://www.hiveworkshop.com/forums/graveyard-418/system-timerutilsex-204500/
http://www.hiveworkshop.com/forums/...eeds-one-more-timer-system-timerstack-210407/
http://www.hiveworkshop.com/forums/graveyard-418/safetyqueue-175735/
http://www.thehelper.net/threads/stack-safety-pack.118325/
http://www.wc3c.net/showthread.php?t=101322
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,550
You should make that struct extend an array because you're not using allocate or deallocate.
(Meaning, just add extends array to the end of the struct declaration line.
It'll shorten up the code quite a lot. (Relatively speaking that is.)

Thanks, I forgot about that. I've updated the script to include that.

Normally I would have just not used a struct and instead done RecycleTimers_get(), but I know you guys like your dot syntax :3

Personally, I like the names "TimerBin" and "TimerRecycler" better.
SimpleTimers is relatively too verbose because the "Simple" adjective has no place in there D:

You're right. It's a simple script, but there's nothing simple about using it. I've changed the name.

Even better:

JASS:
    function ActuallyDestroyTimer takes timer t returns nothing
        call PauseTimer(t)
        call DestroyTimer(t)
    endfunction

Recycling is not worth keeping a larger-than-needed handle stack. Subtraction by 0x100000 is good when you can know the limits of your own script, and I use it in WarChasers II (unreleased version that is).

You can destroy all the timers you want, it won't bork the system. Regardless, I proved this morning with fps tests that recycling them is better than destroying them. If your timer stack is getting bigger 100, you have much bigger problems than just the system -.-


...this is pretty frustrating because at this point in reading your post I started to feel like you didn't read my post at all. Please check the preface and the script, not just the title.

since this is similar to TimerUtils, but very simple, why dont you just put a hashtable in it
so that the user dont need to make a HT...

Because when you need a timer you don't always need a hashtable. Besides, creating a hashtable takes literally 1 line of code. I'm sure you can manage ;)

---

Edit: Please graveyard this thread. This post ( http://www.hiveworkshop.com/forums/2162157-post65.html ) shows that CreateTimer() and DestroyTimer() is faster than any method of recycling.
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
Edit: Please graveyard this thread. This post ( http://www.hiveworkshop.com/forums/2162157-post65.html ) shows that CreateTimer() and DestroyTimer() is faster than any method of recycling.

The majority of the pro vjass community has known that recycling was much slower than creating/destroying timers. I also proved this when I was working on a module for the fastest 1 shot timers ever and the recycling code was slower than creating/destroying timers, lolz. The only way I got it to be faster was by using a circular array, which meant that there was a constant number of timers up at a time :\. Many timers working together also slows down timers by a drastic amount (shown by both Vexorian and myself). I don't remember the specific threshold, but it was like 500 or 1200 :\. It's at that point that all timers just slow way the heck down. Vexorian postulated that it might be an optimization feature.

The recycling method for the circular array I did was just index = index + 1. The traditional recycling algorithm is just much slower than CreateTimer/DestroyTimer :\.

If you want to win in memory, the best way is to just create/destroy timers. If you want to win in speed and memory, the best way is to merge timers and assume that they are repeating. Timer Tools does the latter at the moment, but I have been made aware of a couple of bugs in it that are going to be extremely difficult for me to squash should I choose to fix it up >.<.

I know that you are against overbearing large scale systems. I am as well ;p. I see one and I just automatically don't really want to use it. I've been moving away from them myself over time, the largest example of this move being save/load with snippets. If you want to help out with timer systems, take the algorithms in Timer Tools and fix them up =).
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Circular with a variable incrementation ?!
You mean with a circular linked list and .next maybe ?
Or you have some if/then somewhere, or weird : a real event variable event maybe to reset it when it's needed.
Or something else ?

I'm just curious.
 
Top