[vJASS] About TimerUtil's NewTimer()

Status
Not open for further replies.
Disclaimer: I'm terrible at reading code.

TimeUtils has NewTimer() and NewTimerEx(). So far, I've been using NewTimerEx() exclusively since I always have some sort of data attached to it, but I recently tried using NewTimer() for a delayed special effect destruction and it seems to work fine. Does that mean that NewTimer() allocates the timer its own instance? While all timer data from NewTimer() returns 0, the following seems to work without a hitch in single player:

JASS:
library DestroyEffectTimed requires TimerUtils

    globals
        private effect array Effect
    endglobals
   
    function EffectEnd takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer t_id = GetTimerData(t)
        call DestroyEffect(Effect[t_id])
        call ReleaseTimer(t)
        set t = null
    endfunction

    function DelayedDestroyEffect takes effect vfx, real timeout returns nothing
        local timer t = NewTimer()
        local integer t_id = GetTimerData(t)
        set Effect[t_id] = vfx
        call TimerStart(t, timeout, false, function EffectEnd)
        set t = null
    endfunction

endlibrary

Can anyone shed some light?
 
I believe it would attach just nothing to it, or maybe "0" explicitly; might depend on your used TimerUtils. Can you link?

Anyways, sure, it will work; kind of. You always will save effect to Effect[0] probably, so if function calls are not overlapping, then it will work. But once you overwrite it, while the timer is running, then it will probably break.

I'm curious if your intention was to go away from TimerUtils? - Because then, the data attaching can happen to a struct, but you still would require a timer to run. Else, I would just directly use the NewTimerEx().
 
Level 13
Joined
Nov 7, 2014
Messages
571
While all timer data from NewTimer() returns 0, the following seems to work without a hitch in single player:
It should "hitch" with this:
JASS:
call DelayedDestroyEffect(AddSpecialEffect("path/to/effect/A.mdx", 0.0, 0.0), <some-delay>)
call DelayedDestroyEffect(AddSpecialEffect("path/to/effect/B.mdx", 0.0, 0.0), <some-delay>)

// the first call of DelayedDestroyEffect sets Effect[0] = effect A // t_id == 0 becase a NewTimer's data is 0
// the second call of DelayedDestroyEffect sets Effect[0] = effect B

// after effect A's timer expires, it calls DestroyEffect(Effect[0]) (effect B)
// after effect B's timer expires, it calls DestroyEffect(Effect[0]) (effect B, which is already destroyed)
// as a result effect A should still be present
 
I believe it would attach just nothing to it, or maybe "0" explicitly; might depend on your used TimerUtils. Can you link?
Sure thing: Chromatic TimerUtils

Anyways, sure, it will work; kind of. You always will save effect to Effect[0] probably, so if function calls are not overlapping, then it will work. But once you overwrite it, while the timer is running, then it will probably break.
I think it's probably breaking without my realising it.

I'm curious if your intention was to go away from TimerUtils? - Because then, the data attaching can happen to a struct, but you still would require a timer to run. Else, I would just directly use the NewTimerEx().
No, actually. I quite like TimerUtils, I just find it bothersome to consistently have to come up with my own index all the time. I'm currently using an IdAllocator that Flux threw together to help me out, but I tend to avoid using it when I can because... I guess I just don't know if I using it correctly? Sort of a "Should I really be using this?" situation, I guess.
JASS:
library IdAlloc initializer OnInit
  
    globals
        private integer array recycler
    endglobals
  
    function IndexDeallocate takes integer id returns nothing
        set recycler[id] = recycler[0]
        set recycler[0] = id
    endfunction
  
    function IndexAllocate takes nothing returns integer
        local integer id = recycler[0]
        if (recycler[id] == 0) then
            set recycler[0] = id + 1
        else
            set recycler[0] = recycler[id]
        endif
        return id
    endfunction
  
    private function OnInit takes nothing returns nothing
        set recycler[0] = 1
    endfunction
  
endlibrary

It should "hitch" with this:
JASS:
call DelayedDestroyEffect(AddSpecialEffect("path/to/effect/A.mdx", 0.0, 0.0), <some-delay>)
call DelayedDestroyEffect(AddSpecialEffect("path/to/effect/B.mdx", 0.0, 0.0), <some-delay>)

// the first call of DelayedDestroyEffect sets Effect[0] = effect A // t_id == 0 becase a NewTimer's data is 0
// the second call of DelayedDestroyEffect sets Effect[0] = effect B

// after effect A's timer expires, it calls DestroyEffect(Effect[0]) (effect B)
// after effect B's timer expires, it calls DestroyEffect(Effect[0]) (effect B, which is already destroyed)
// as a result effect A should still be present
I just realised I was using DelayedDestroyEffect with special effects that don't loop, so it appears as though they are being destroyed, but aren't. Thanks for pointing out something that should have been quite obvious to me but I somehow misseed it, lol.

I guess the moral of the story is to stick to NewTimerEx(integer) ?
 
You can directly use struct's internal id allocation, here an example (read from bottom to top maybe):

JASS:
library DestroyEffectTimed requires TimerUtils

struct TimedEffect
    
// each instance has one effect member
    effect effect

// when our instance is destroyed we null agents and deallocate the instance index
    private method destroy takes nothing returns nothing
        call this.deallocate()  //recylce our instance index, so it may be allocated again later
        set this.effect = null
    endmethod

// our callback function from timer will destroy the effect and then our instance
    private static method destroyEffect takes nothing returns nothing
        local timer clock = GetExpiredTimer()
        local thistype this = GetTimerData(clock)
        call DestroyEffect(this.effect)
        call this.destroy()
        call ReleaseTimer(clock)
    endmethod

// allocate a new instance index (struct) and attach data to our new struct instance
    static method create takes effect e, real duration returns thistype
        local thistype this = allocate() // allocate a new unique index as our instance
        local timer clock = NewTimerEx(this)
        call TimerStart(clock, duration, false, function thistype.destroyEffect)
        set this.effect = e
        return this
    endmethod

endstruct
endlibrary

(didn't check if it compiless, but should roughly work probably)
 
Level 23
Joined
Feb 6, 2014
Messages
2,466
A way to make NewTimer() automatically attach an allocated index is make a few edit in your copy of TimerUtils. Replace SetTimerData(tT[tN], value) in NewTimerEx with an if-else block condition that if value == 0, then SetTimerData(tT[tN], tN) else SetTimerData(tT[tN], value) Won't work if you purposely called NewTimerEx(0) but it's a simple solution. Of course, it's also possible to make NewTimerEx(0) works while NewTimer() attachs an allocated index but it requires more editing.
 
Status
Not open for further replies.
Top