• 🏆 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!

If I were to post this Timer system...

Status
Not open for further replies.
If I were to post this Timer system, ... well, let me rephrase that:
What do you think of this:

JASS:
library TimerLoop
    
    globals
        private integer c = 0
        private integer array r
    endglobals
    
    private function A takes nothing returns integer
        local integer i = r[0]
        if i == 0 then
            set c = c + 1
            return c
        endif
        set r[0] = r[i]
        return i
    endfunction
    
    private function D takes integer i returns nothing
        set r[i] = r[0]
        set r[0] = i
    endfunction
    
    module TL
        static integer am = 0
        static integer array nx
        static integer array pv
        static timer tt = CreateTimer()
        static method e takes nothing returns nothing
            local thistype this = nx[0]
    endmodule
    
    module TLExpire
            loop
                exitwhen this == 0
    endmodule
    
    module TLNull
                set this = nx[this]
            endloop
    endmodule
    
    module TLEnd
        endmethod
        private static method create takes nothing returns thistype
            local thistype i = A()
            set nx[i] = 0
            set pv[i] = pv[0]
            set nx[pv[0]] = i
            set pv[0] = i
            set am = am + 1
            if am == 1 then
                call TimerStart(tt, INTERVAL, true, function thistype.e)
            endif
            return i
        endmethod
        private method destroy takes nothing returns nothing
            call D(this)
            set nx[pv[this]] = nx[this]
            set pv[nx[this]] = pv[this]
            set am = am - 1
            if am == 0 then
                call PauseTimer(tt)
            endif
        endmethod
    endmodule

endlibrary

It's a timer system that makes your lives way easier.

A spell that needs tons of extra code for allocation/deallocation, linked list adding and removing, and timer managing would shrink drastically.

It works just like CTL, but you have to declare a constant real INTERVAL to define the timer's interval.

This Timer system very easy to use. If you know how to use CTL, you'd know how to use it.

Example:

JASS:
struct DoT extends array
    private static real INTERVAL = 0.5

    private static unit array caster
    private static unit array target
    private static real array damage
    
    static method getDamage takes integer level returns real
        return 30. + level * 50
    endmethod

    implement TL
        // locals here
        // init code here
    implement TLExpire
        call UnitDamageTarget(caster[this], target[this], damage[this], false, false, null, null, null)
    implement TLNull
        // null locals here
        // end code here
    implement TLEnd

    static method run takes nothing returns nothing
        local thistype this = create()
        set caster[this] = GetTriggerUnit()
        set target[this] = GetSpellTargetUnit()
        set damage[this] = getDamage(GetUnitAbilityLevel(caster[this], 'A000'))
    endmethod

    private static method onInit takes nothing returns nothing
        call RegisterSpellEffectEvent('A000', function thistype.run)
    endmethod
endstruct

I really need feedback because I'm not sure if I should post it or not.
 
The system would never know when it expires, it's for periodic timers only :p
To run a code after a certain amount of time, you might as well write your own because it would be MUCH shorter :p

module abuse = sphagetti code.

It looks pretty clear to me :eek:

edit

Here's another pro for this system:
The map code will decrease by 8 lines per struct using this.

In my opinion, this:

JASS:
struct DoT extends array
    private static real INTERVAL = 0.5

    private static unit array caster
    private static unit array target
    private static real array damage
    
    static method getDamage takes integer level returns real
        return 30. + level * 50
    endmethod

    implement TL

        // locals here
        // init code here

    implement TLExpire

        call UnitDamageTarget(caster[this], target[this], damage[this], false, false, null, null, null)

    implement TLNull

        // null locals here
        // end code here

    implement TLEnd

    static method run takes nothing returns nothing
        local thistype this = create()
        set caster[this] = GetTriggerUnit()
        set target[this] = GetSpellTargetUnit()
        set damage[this] = getDamage(GetUnitAbilityLevel(caster[this], 'A000'))
    endmethod

    private static method onInit takes nothing returns nothing
        call RegisterSpellEffectEvent('A000', function thistype.run)
    endmethod
endstruct

is much more readable than this:

JASS:
struct DoT extends array
    private static integer ic = 0
    private static thistype array rn
    
    private static thistype array next
    private static thistype array prev
    
    private static integer amount = 0

    private static unit array caster
    private static unit array target
    private static real array damage
    
    private static timer timer = CreateTimer()
    
    static method getDamage takes integer level returns real
        return 30. + level * 50
    endmethod

    method destroy takes nothing returns nothing
        set rn[this] = rn[0]
        set rn[0] = this
        set next[prev[this]] = next[this]
        set prev[next[this]] = prev[this]
        set amount = amount - 1
        if amount == 0 then
            call PauseTimer(timer)
        endif
    endmethod
    
    private static method periodic takes nothing returns nothing
        local thistype this = next[0]
        loop
            exitwhen this == 0
            call UnitDamageTarget(caster[this], target[this], damage[this], false, false, null, null, null)
            set this = next[this]
        endloop
    endmethod
    
    static method run takes nothing returns nothing
        local thistype this = rn[0]
        if this == 0 then
            set ic = ic + 1
            set this = ic
        else
            set rn[0] = rn[this]
        endif
        set next[this] = 0
        set prev[this] = prev[0]
        set next[prev[0]] = this
        set prev[0] = this
        set amount = amount + 1
        if amount == 1 then
            call TimerStart(timer, 0.5, true, function thistype.periodic)
        endif
        set caster[this] = GetTriggerUnit()
        set target[this] = GetSpellTargetUnit()
        set damage[this] = getDamage(GetUnitAbilityLevel(caster[this], 'A000'))
    endmethod

    private static method onInit takes nothing returns nothing
        call RegisterSpellEffectEvent('A000', function thistype.run)
    endmethod
endstruct

edit

coz there's so many timer systems already

And a lot of them are bad :/ (except for T32, CTL, TimerTools and TimerUtils)
TimerUtils needs an update though. Even though Vexorian updated it months ago, he still didn't make ReleaseTimer(timer) return the timer data.
And he added a Trigger evaluation to NewTimer() instead of using a simple module initializer -.-
 
Last edited:
Level 6
Joined
Feb 10, 2008
Messages
300
Notice that the less readable code is probably faster due to fewer function calls.

Why would I use this?
If it were for speed, doing your own stack without a struct is faster.
If it were for readability, I'd use T32 or TT.

Dunno if this actually works, though.
JASS:
globals
    unit array caster
    unit array target
    real array damage
    integer array index

    integer count = 0
    timer   timer = CreateTimer()
endglobals

function tick takes nothing returns nothing
    local integer i = count 
    loop
        exitwhen i==0
        call UnitDamageTarget(caster[i], target[i], damage[i], ...)
        set i = i - 1
    endloop
endfunction

function AddDamageOverTime takes unit c, unit t, real d returns integer
    set count = count + 1
    set caster[count] = c
    set target[count] = t
    set damage[count] = d
    set index[count] = count

    if (count == 1) then
        call TimerStart(..)
    endif
    
    return count
endfunction

function RemoveDamageOverTime takes integer i returns nothing
    set caster[i] = caster[count]
    set target[i] = target[count]
    set damage[i] = damage[count]
    set index[i] = index[count]

    set count = count - 1
    if (count == 0) then
        call PauseTimer(..)
    endif
endfunction

Bottom line is; Since you can't compete in speed, you'll have to provide a more convenient option.
Module spamming is in my opinion, not very convenient.

What Cohadar did in his TT library is probably the most convenient way.
T32 is close, but requires a struct + import.
 
Last edited:
Well, most of the "unreadable" part comes from "silly" optimization, struct extends array and custom allocator.
I mean your comparaison is not fair.

Well, I use custom allocaters all the time :eek:
I guess it's fair for me personally.

But hey, everyone should be using custom allocaters :p

The code generated by JassHelper is really ugly. You get trigger arrays, Trigger evaluations, useless checks, and a bunch of useless globals spamming the global scope ;/

edit

What else do you suggest for the API? (Instead of modules)

The only pro for this is less map code at one point (All structs share the same allocator/deallocator) and less typing + shorter code.

edit

By the way, that stack structure looks pretty good (It would be great for spells with very little data for each instance, because if you were to have a lot of data members, the deallocation would be very slow (relatively))

edit

Notice that the less readable code is probably faster due to fewer function calls.

Speed is not really what I'm going for here.

T32 is close, but requires a struct + import.

The struct is taken for granted.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Well, I use custom allocaters all the time :eek:
I guess it's fair for me personally.

Code abstraction is fine, especially for repetitive things like struct allocators, plus your custom allocator doesn't provide debug stuff.
Personnaly the debug allocators helped me many times.
And seriously vJass default allocators are good enough in real cases.

But hey, everyone should be using custom allocaters :p

Hell, no.

The code generated by JassHelper is really ugly. You get trigger arrays, Trigger evaluations, useless checks, and a bunch of useless globals spamming the global scope ;/

Trigger evaluations are done only if you code badly.
The other things don't affect the speed of the code, while i'm agree that it increases the map script size.
I agree that how vJass generates jass code should be improved, but it's not up to the user ...

If you are such a speedfreak, go for jass, forget vJass or any other jass preprocessor.

Else, i agree with Tukki.
 
If you are such a speedfreak, go for jass, forget vJass or any other jass preprocessor.

Nah, these kinds of workarounds and limitations are things we should get used to.
They're very abundant when it comes to High level programming languages like Java or C++

Hell, no.

Pff, Why not? :/

-----

I'm still not convinced that this is a bad resource.
Still, it doesn't matter, because if you guys don't like it, I'm not going to put it up.
The entire point of it was to shorten code length and speed up spell Development.

I could shorten it to 2 modules, but then you wouldn't be able to declare locals (I hope you'd be fine with that.)
 
Level 6
Joined
Feb 10, 2008
Messages
300
You could make that structure become faster on deallocation by trading some speed in the tick function.
JASS:
    function tick ..
        local integer i = count
        local integer j
        loop
            exitwhen i == 0
            set j = index[i]
            call UnitDamageTarget(caster[j], target[j], damage[j], ..)
    endfunction

    function RemoveDamage takes integer i ..
        set index[i] = count
        set count = count - 1
    endfunction

Although I'm not sure if it'd beneficial in the end.

If you're using structs, why not make something similar to T32 if you want development speed?
It's pretty user friendly, and it's very noticeable if you do something you aren't supposed to.
I.e calling .startPeriodic() twice, calling .startPeriodic() from the periodic method etc..
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
And a lot of them are bad :/ (except for T32, CTL, TimerTools and TimerUtils)
and most of them needs to be implemented, except TU...it would be better if not
coz it saves a little time on where to put the implementation, even though
modules are better...

The system would never know when it expires, it's for periodic timers only :p
To run a code after a certain amount of time, you might as well write your own because it would be MUCH shorter :p
it's really ovious that it expires when real reaches "0" :)...TimedLoop is an example
of .destroy() automatically, but it doesnt recycle...
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
It would be ok if it didn't break on destroy/create ^)^.


Try destroying a timer in the loop and then creating a new timer and see what happens with like 10 timers running. It'll end up breaking the pointer to next and end the loop prematurely.

edit
and I know that T32 and CTL have that same problem ; p, but I'm going to fix it in CTL... eventually : D.
 
Last edited:
Level 18
Joined
Jan 21, 2006
Messages
2,552
Quote:
Originally Posted by Magtheridon96 View Post
Well, I use custom allocaters all the time :eek:
I guess it's fair for me personally.
Code abstraction is fine, especially for repetitive things like struct allocators, plus your custom allocator doesn't provide debug stuff.
Personnaly the debug allocators helped me many times.
And seriously vJass default allocators are good enough in real cases.

Quote:
But hey, everyone should be using custom allocaters :p
Hell, no.

Quote:
The code generated by JassHelper is really ugly. You get trigger arrays, Trigger evaluations, useless checks, and a bunch of useless globals spamming the global scope ;/
Trigger evaluations are done only if you code badly.
The other things don't affect the speed of the code, while i'm agree that it increases the map script size.
I agree that how vJass generates jass code should be improved, but it's not up to the user ...

If you are such a speedfreak, go for jass, forget vJass or any other jass preprocessor.

Else, i agree with Tukki.

I completely agree with Troll Brain on that one. Thumbs up.

JASS:
        static method e takes nothing returns nothing
            local thistype this = nx[0]
    endmodule
    
    module TLExpire
            loop
                exitwhen this == 0
    endmodule
    
    module TLNull
                set this = nx[this]
            endloop
    endmodule
    
    module TLEnd
        endmethod
        private static method create takes nothing returns thistype

This doesn't really make much sense, at all. The implementation of module TLNull and module TLExpire is ridiculous.

The actual implementation of a stack and a timer that traverses the stack on each interval would be of equivalent speed and takes maybe 5 seconds to do, sparing the readability of your code. There's no way I could use this and expect my development time to decrease, because the implementation required by this system is completely unconventional.

If I wanted efficiency, I would do it myself. If efficiency wasn't an issue, then there are dozens of options that don't require strange syntax manipulations that result in compilable code.
 
Status
Not open for further replies.
Top