• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Snippet] TimedHandles

Use this to destroy a handle after X amount of seconds.

TimerUtils is optional.

JASS:
library TimedHandles uses optional TimerUtils
/**************************************************************
*
*   v1.0.5 by TriggerHappy
*   ----------------------
*
*   Use this to destroy a handle after X amount seconds.
*
*   It's useful for things like effects where you may
*   want it to be temporary, but not have to worry
*   about the cleaning memory leak. By default it supports
*   effects, lightning, weathereffect, items, ubersplats, and units.
*
*   If you want to add your own handle types copy a textmacro line
*   at the bottom and add whichever handle you want along with it's destructor.
*
*   Example: //! runtextmacro TIMEDHANDLES("handle", "DestroyHandle")
*
*   Installation
    ----------------------
*       1. Copy this script and over to your map inside a blank trigger.
*       2. If you want more efficiency copy TimerUtils over as well.
*
*   API
*   ----------------------
*       call DestroyEffectTimed(AddSpecialEffect("effect.mdx", 0, 0), 5)
*       call DestroyLightningTimed(AddLightning("CLPB", true, 0, 0, 100, 100), 5)
*
*   Credits to Vexorian for TimerUtils and his help on the script.
*
**************************************************************/

    globals
        // If you don't want a timer to be ran each instance
        // set this to true.
        private constant boolean SINGLE_TIMER = true
        // If you chose a single timer then this will be the speed
        // at which the timer will update
        private constant real    UPDATE_PERIOD = 0.05
    endglobals

    // here you may add or remove handle types
    //! runtextmacro TIMEDHANDLES("effect", "DestroyEffect")
    //! runtextmacro TIMEDHANDLES("lightning", "DestroyLightning")
    //! runtextmacro TIMEDHANDLES("weathereffect", "RemoveWeatherEffect")
    //! runtextmacro TIMEDHANDLES("item", "RemoveItem")
    //! runtextmacro TIMEDHANDLES("unit", "RemoveUnit")
    //! runtextmacro TIMEDHANDLES("ubersplat", "DestroyUbersplat")
    
    // Do not edit below this line
    
    //! textmacro TIMEDHANDLES takes HANDLE,DESTROY
        
        struct $HANDLE$Timed
        
            $HANDLE$ $HANDLE$_var
            static integer index = -1
            static thistype array instance
            static real REAL=UPDATE_PERIOD
            
            static if SINGLE_TIMER then
                static timer timer = CreateTimer()
                real duration
                real elapsed = 0
            else static if not LIBRARY.TimerUtils then
                static hashtable table = InitHashtable()
            endif
            
            method destroy takes nothing returns nothing
                call $DESTROY$(this.$HANDLE$_var)
                set this.$HANDLE$_var = null
                
                static if SINGLE_TIMER then
                    set this.elapsed = 0
                endif
                
                call this.deallocate()
            endmethod
            
            private static method remove takes nothing returns nothing
                static if SINGLE_TIMER then
                    local integer i = 0
                    local thistype this
                    loop
                        exitwhen i > thistype.index
                        set this = instance[i]
                        set this.elapsed = this.elapsed + UPDATE_PERIOD
                        if (this.elapsed >= this.duration) then
                            set instance[i] = instance[index]
                            set i = i - 1
                            set index = index - 1
                            call this.destroy()
                            if (index == -1) then
                                call PauseTimer(thistype.timer)
                            endif
                        endif
                        set i = i + 1
                    endloop
                else
                    local timer t = GetExpiredTimer()
                    static if LIBRARY.TimerUtils then
                        local $HANDLE$Timed this = GetTimerData(t)
                        call ReleaseTimer(t)
                        call this.destroy()
                    else
                        local $HANDLE$Timed this = LoadInteger(table, 0, GetHandleId(t))
                        call DestroyTimer(t)
                        set t = null
                        call this.destroy()
                    endif
                endif
            endmethod
            
            static method create takes $HANDLE$ h, real timeout returns $HANDLE$Timed
                local $HANDLE$Timed this = $HANDLE$Timed.allocate()
                
                static if SINGLE_TIMER then
                    set index = index + 1
                    set instance[index] = this
                    if (index == 0) then
                        call TimerStart(thistype.timer, UPDATE_PERIOD, true, function thistype.remove)
                    endif
                    set this.duration = timeout
                else
                    static if LIBRARY.TimerUtils then
                        call TimerStart(NewTimerEx(this), timeout, false, function $HANDLE$timed.remove)
                    else
                        local timer t = CreateTimer()
                        call SaveInteger(thistype.table, 0, GetHandleId(t), this)
                        call TimerStart(t, timeout, false, function $HANDLE$Timed.remove)
                        set t = null
                    endif
                endif  
                
                set this.$HANDLE$_var = h
                
                return this
            endmethod
            
        endstruct
        
        function $DESTROY$Timed takes $HANDLE$ h, real duration returns $HANDLE$Timed
            return $HANDLE$Timed.create(h, duration)
        endfunction

    //! endtextmacro
    
endlibrary

JASS:
    call DestroyEffectTimed(AddSpecialEffect("effect.mdx", 0, 0), 5)
    call DestroyLightningTimed(AddLightning("CLPB", true, 0, 0, 100, 100), 5)
    call RemoveUnitTimed(CreateUnit(Player(0), 'hfoo', 0, 0 ,0), 5)
    call RemoveItemTimed(CreateItem('ratf', 0, 0), 60)
 
Last edited:
Level 16
Joined
Oct 12, 2008
Messages
1,570
Just recycle, with a timer array, instead of local timers. If the timer = null, start it and set with that index also the var. release when destroyed and null the timer, then you can use the timer & the var again,,
 
Level 11
Joined
Apr 29, 2007
Messages
826
lol, looks similier to a library I made for my map. Actually does the same, mine only works different.

JASS:
library_once TimedHandles

globals

    private timer Timer  = CreateTimer()
    private integer Runs = 0
    private integer array Struct
    
endglobals

    //! textmacro TIMEDHANDLES takes HANDLE, DESTROY
    struct $HANDLE$Timed
        $HANDLE$ var
        real Interval=0.
        real IntervalMax
        static method create takes $HANDLE$ var, real Interval returns $HANDLE$Timed
            local thistype this=thistype.allocate()
            set .var=var
            set .IntervalMax=Interval
            if Runs==0 then
                call TimerStart(Timer,0.035,true,function thistype.remove)
            endif
            set Struct[Runs]=this
            set Runs=Runs+1
            return this
        endmethod
        static method remove takes nothing returns nothing
            local thistype this
            local integer Loop=0
            loop
                exitwhen Loop>=Runs
                set this=Struct[Loop]
                set .Interval=.Interval+0.035
                if .Interval>=.IntervalMax then
                    call $DESTROY$(.var)
                    set Runs=Runs-1
                    set Struct[Loop]=Struct[Runs]
                    call .destroy()
                endif
                set Loop=Loop+1
            endloop
            if Runs==0 then
                call PauseTimer(Timer)
            endif
        endmethod
    endstruct
    function $DESTROY$Timed takes $HANDLE$ var, real Interval returns nothing
        call $HANDLE$Timed.create(var,Interval)
    endfunction
    //! endtextmacro
    
    //! runtextmacro TIMEDHANDLES( "unit", "RemoveUnit" )
    //! runtextmacro TIMEDHANDLES( "effect", "DestroyEffect" )
    //! runtextmacro TIMEDHANDLES( "item", "RemoveItem" )
    //! runtextmacro TIMEDHANDLES( "destructable", "RemoveDestructable" )
    
endlibrary

Dunno really which one here is more efficient, but I guess this one since it uses TimerUtils and not a bunch of arrays. Also your code is smaller than mine.
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
yeah I'm also sure that yours is faster, but mine doesn't require another library :p.
I hate to add a bunch of other libraries just to make a single system work. That's why I write my systems so they work on their own. But that's only my opinion. :)

It's only one library, and it's a library *everyone* using vjass will use in his map anyway. I mean, who is going to use a library such as TimedHandles and is NOT going to use Table or TimerUtils?
 
Level 3
Joined
Dec 15, 2007
Messages
44
i dont understand any of this, i'm trying to make a timer for something like " rounds " in a game. could this be something i'm looking for?
 
Nope. All you need is a simple timer...

JASS:
globals
    //if you don't have NewGen, this is a variable defined in the trigger 
    //editor of type "integer" named "Round". You should aslo leave
    //out the keywords "globals" and "endglobals" if you're not using
    //vJass
    integer udg_Round = 0
endglobals
function NextRound takes nothing returns nothing
    set udg_Round = udg_Round + 1
    //do actions based on what round it is
endfunction
function InitTrig_Rounds takes nothing returns nothing
    call TimerStart(CreateTimer(), 60., true)
    //Change 60. to the number of seconds between each round
endfunction
 
Level 3
Joined
Dec 15, 2007
Messages
44
this looks like jass and i'm really bad with that, how would i go about using it? just paste this into a jass section or something? sorry for being difficult
 
Anachron said:
You have a simple member, it's not even a global. Of course if you compile structs it's all just globals but that's not the point.

How is it not the point? Struct members literally are just global variables of course it matters.

Anachron said:
The problem is the pointer still linking to an handle.
I'm not quite sure if it leaks, but it's not safe after all.

Try to run a script thousands of times without nulling a global handle and watch the memory rise.
 
The leak is too small to matter, it's just something that I've done as a good programming practice - to keep references as minimal as possible.

It's not grounds for approval/rejection.

Maker tested it over thousands of iterations and the memory didn't rise.

However I'll test it myself in a little to be sure.

EDIT: Confirmed it doesn't leak, ran the test for over 20 minutes and the memory basically stayed the same.

JASS:
function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
    local timer t = NewTimer()
    call ReleaseTimer(t)
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
    call TimerStart(NewTimer(), 0.01, true, function Trig_Untitled_Trigger_001_Actions)
endfunction
 
Last edited:
Level 22
Joined
Sep 24, 2005
Messages
4,821
So, does that mean local references pointing to existing objects get cleared correctly? What the hell...
 
So, does that mean local references pointing to existing objects get cleared correctly? What the hell...

I don't think so.

Keep in mind that the objects are just reference counted, for all we know, Blizzard might not track where the references come from. As such, we can imagine that agents have a separate reference count whenever they are referred to. Declaring a local to point to an object will increment that reference count, and nulling the local pointing to that object (or making it point something else) will decrement the reference count. I'm guessing, based on the results (I ran a similar test and got the same results), that the reference count is just a 32-bit integer associated with the agent. So the more references that are added won't actually take up more memory--they'll just change the value of the reference count integer.

... but that is just theoretical. It makes sense in my mind though. So if you know you're never going to destroy the agent, then you don't need to null the local.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Hmm right, so you could do the same stuff with other agent types ?
http://www.hiveworkshop.com/forums/triggers-scripts-269/fun-stuff-images-210359/
Don't take me wrong, i'm not saying that would be useful, just trying to know.

At least it makes sense for me now why people said you don't need to null agents, even if i still highly doubt you don't need it, for let's say timer, just as an agent example.
I have a doubt for item/unit/pool as an handle example, but for the others it makes sense.
 
Top