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

[JASS] Fun... Visitors in the Lag department

Status
Not open for further replies.
Level 40
Joined
Dec 14, 2005
Messages
10,532
anyways, im leaking or something somewhere, as this gets obscenely laggy after awhile, and i was hoping that one of you guys could pick it up :D

anyways, basically, the problem has bbout a 99% chance of being in the WhirlfuryIniter function, as it didnt lag until i added that one in

the funcs look fine to me... but im probably missing something

EDIT: those funcs at the top are really constant, but i removed constant from the front of them cuz it was messing w/ the indenting :?

JASS:
function Whirlfury_Rawcode takes nothing returns integer 
    return 'A004'//rawcode of the Whirlfury spell
endfunction

function Whirlfury_HitsAllied takes nothing returns boolean
    return false//does not hit allies
endfunction

function Whirlfury_Damage takes real level returns real 
    return 0 + level * 50//the damage per second dealt
endfunction

function Whirlfury_AOE takes real level returns real 
    return 300 + level * 0//the AOE of whirlfury
endfunction

function Whirlfury_Anginc takes real level returns real 
    return 18 + level * 0//the angle ( rotation ) increases by 360 degrees per second ( 3.6 degree per 0.01 seconds )
endfunction

function Whirlfury_Duration takes real level returns real
    return 10 + level * 0//how long whirlfury lasts
endfunction

function Whirlfury_MaxTargets takes real level returns real
    return 2 + level * 1//how many units Whirlfury can pick up
endfunction

function WhirlfuryLoop2 takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit u = GetHandleUnit( t, "targ" )
    local real angle = GetHandleReal( t, "ang" )
    local real time = GetHandleReal( t, "time" ) + 0.01
    
    call SetUnitX( u, GetUnitX( u ) + 2 * Cos( angle * bj_DEGTORAD ) )
    call SetUnitY( u, GetUnitY( u ) + 2 * Sin( angle * bj_DEGTORAD ) )
    
    if time == GetHandleReal( t, "maxtime" ) then
        call PauseTimer( t )
        call FlushHandleLocals( t )
        call DestroyTimer( t )
        call PauseUnit( u, false )
    else
        call SetHandleReal( t, "time", time )
    endif
    
    set u = null
    set t = null    
endfunction

function WhirlfuryLoop takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit u = GetHandleUnit( t, "targ" )
    local unit u2 = GetHandleUnit( t, "cast" )
    local real angle = GetHandleReal( t, "ang" ) + GetHandleReal( t, "anginc" )
    local real initdist = GetHandleReal( t, "initdist" )
    local real time = GetHandleReal( t, "time" ) + 0.01
    local real maxtime = GetHandleReal( t, "maxtime" )
    
    call UnitDamageTarget( u2, u, GetHandleReal( t, "dmg" ), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null )
    
    if angle > 360 then
        set angle = angle - 360
    endif
    
    call SetUnitX( u, GetUnitX( u2 ) + initdist * Cos( angle * bj_DEGTORAD ) )
    call SetUnitY( u, GetUnitY( u2 ) + initdist * Sin( angle * bj_DEGTORAD ) )
    
    if time == maxtime then
        call PauseTimer( t )
        call FlushHandleLocals( t )
        call DestroyTimer( t )
        set t = CreateTimer()
        call SetHandleReal( t, "ang", Atan2( GetUnitY( u ) - GetUnitY( u2 ), GetUnitX( u ) - GetUnitX( u2 ) ) * bj_RADTODEG )
        call SetHandleHandle( t, "cast", u2 )
        call SetHandleHandle( t, "targ", u )
        call SetHandleReal( t, "maxtime", 2 )
        call SetHandleReal( t, "time", 0 )
        call TimerStart( t, 0.01, true, function WhirlfuryLoop2 )
    else
        call SetHandleReal( t, "time", time )
        call SetHandleReal( t, "ang", angle )
    endif
    
    set u = null
    set u2 = null
    set t = null    
endfunction

function WhirlfuryInitLoop takes unit u, unit u2, integer i, real duration returns nothing
    local timer t = CreateTimer()
    call SetHandleReal( t, "dmg", Whirlfury_Damage(i)/100 )
    call SetHandleReal( t, "anginc", Whirlfury_Anginc(i) )
    call SetHandleReal( t, "ang", Atan2( GetUnitY( u2 ) - GetUnitY( u ), GetUnitX( u2 ) - GetUnitX( u ) ) * bj_RADTODEG )
    call SetHandleHandle( t, "cast", u2 )
    call SetHandleHandle( t, "targ", u )
    call SetHandleReal( t, "initdist", SquareRoot( ( GetUnitX( u2 ) - GetUnitX( u ) ) * ( GetUnitX( u2 ) - GetUnitX( u ) ) + ( GetUnitY( u2 ) - GetUnitY( u ) ) * ( GetUnitY( u2 ) - GetUnitY( u ) ) ) )
    call SetHandleReal( t, "maxtime", duration )
    call SetHandleReal( t, "time", 0 )
    call PauseUnit( u, true )
    call TimerStart( t, 0.01, true, function WhirlfuryLoop )
    set t = null
endfunction

function WhirlfuryIniter takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit u = GetHandleUnit( t, "u" )
    local integer i = GetUnitAbilityLevel( u, Whirlfury_Rawcode() )
    local group g = CreateGroup()
    local group safety = GetHandleGroup( t, "safe" )
    local group safety2 = CreateGroup()
    local unit u2
    local unit u3
    local integer maxTargs = GetHandleInt( t, "maxTargs" )
    local real Time = GetHandleReal( t, "timeelapsed" ) + .5
    local boolean b = false
    call GroupEnumUnitsInRange( g, GetUnitX( u ), GetUnitY( u ), Whirlfury_AOE(i), null )
    loop
        set u2 = FirstOfGroup( g )
        exitwhen u2 == null
        call GroupAddGroup( safety, safety2 )
        loop
            set u3 = FirstOfGroup( safety2 )
            exitwhen u3 == null
            if u2 == u3 then
                set b = true
            endif
            call GroupRemoveUnit( safety2, u3 )
        endloop
        if maxTargs != Whirlfury_MaxTargets(i) and ( IsUnitEnemy( u2, GetOwningPlayer( u ) ) or Whirlfury_HitsAllied() ) and not b then
            call WhirlfuryInitLoop( u2, u, i, Whirlfury_Duration(i) - Time )
            call GroupAddUnit( safety, u )
            set maxTargs = maxTargs + 1
        endif
        set b = false
        call GroupRemoveUnit( g, u2 )
    endloop
    if maxTargs == Whirlfury_MaxTargets(i) or Time == Whirlfury_Duration(i) then
        call PauseTimer( t )
        call FlushHandleLocals( t )
        call DestroyTimer( t )
        call DestroyGroup( safety )
    else
        call SetHandleReal( t, "maxTargs", maxTargs )
        call SetHandleReal( t, "timeelapsed", Time )
        call SetHandleHandle( t, "safe", safety )
    endif
    call DestroyGroup( safety2 )
    call DestroyGroup( g )
    set g = null
    set u = null
    set safety = null
    set safety2 = null
    set t = null
endfunction

function Whirlfury takes nothing returns nothing
    local timer t = CreateTimer()
    local unit u = GetTriggerUnit()
    call SetHandleHandle( t, "safe", CreateGroup() )
    call SetHandleHandle( t, "u", u )
    call SetHandleReal( t, "timeelapsed", 0 )
    call SetHandleInt( t, "maxTargs", 0 )
    call TimerStart( t, .5, true, function WhirlfuryIniter )
    set u = null
    set t = null
endfunction

function WhirlfuryConds takes nothing returns boolean
    return GetSpellAbilityId() == Whirlfury_Rawcode() 
endfunction

function InitTrig_Whirlfury takes nothing returns nothing
    set gg_trg_Whirlfury = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Whirlfury, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Whirlfury, Condition( function WhirlfuryConds ) ) 
    call TriggerAddAction( gg_trg_Whirlfury, function Whirlfury )
endfunction
 
Level 6
Joined
Feb 18, 2005
Messages
263
within the function WhirfuryIniter ou have 3 groups defined as locals, but only clear off 2 off them (at least at the end of the function)

try counting the locals of each type (that needs beeing cleared) and then cound the number of vars per type that you clear of again... maybe there are even more leaks, but thats the one i found right now (i didn't go through your whole code - just flow over it once.)
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
Me not deleting the group "safety" is not a leak. The reason i dont clean it up at the end of the function is that its reused every function call ( stored in the handle vars inbetween ), and cleaned up finally here. It is used to detect units already picked up.:wink:

JASS:
 if maxTargs == Whirlfury_MaxTargets(i) or Time == Whirlfury_Duration(i) then
call PauseTimer( t )
call FlushHandleLocals( t )
call DestroyTimer( t )
call DestroyGroup( safety )
else
call SetHandleReal( t, "maxTargs", maxTargs )
call SetHandleReal( t, "timeelapsed", Time )
call SetHandleHandle( t, "safe", safety )
endif
 
Level 3
Joined
Aug 17, 2006
Messages
38
I don't see a leak in there, but there could still be one. Perhaps just using the grouping functions in that loop is slow, and creates lag on its own. You could try using a temporary array and see if that's any faster. Or maybe exiting the loop earlier and then clear the group, instead of going through the rest of it.

Do you know if you need to call GroupClear before you destroy the group? I'd assume destroying it would clear it, but you never know. It doesn't look like your loops will end with units in the group anyway, but it'd be a simply thing to test.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
hmm.. well, tested again and... it still lags.

got some help on wc3jass, and learned a new func (IsUnitInGroup) i never knew about ( for some reason... )

here is the updated code

JASS:
constant function Whirlfury_Rawcode takes nothing returns integer
    return 'A004'//rawcode of the Whirlfury spell
endfunction

constant function Whirlfury_HitsAllied takes nothing returns boolean
    return false//does not hit allies
endfunction

constant function Whirlfury_Damage takes real level returns real
    return 0 + level * 50//the damage per second dealt
endfunction

constant function Whirlfury_AOE takes real level returns real
    return 300 + level * 0//the AOE of whirlfury
endfunction

constant function Whirlfury_Anginc takes real level returns real
    return 18 + level * 0//the angle ( rotation ) increases by 360 degrees per second ( 3.6 degree per 0.01 seconds )
endfunction

constant function Whirlfury_Duration takes real level returns real
    return 10 + level * 0//how long whirlfury lasts
endfunction

constant function Whirlfury_MaxTargets takes real level returns real
    return 2 + level * 1//how many units Whirlfury can pick up
endfunction

function WhirlfuryLoop2 takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit u = GetHandleUnit( t, "targ" )
    local real angle = GetHandleReal( t, "ang" )
    local real time = GetHandleReal( t, "time" ) + 0.01

    call SetUnitX( u, GetUnitX( u ) + 2 * Cos( angle * bj_DEGTORAD ) )
    call SetUnitY( u, GetUnitY( u ) + 2 * Sin( angle * bj_DEGTORAD ) )

    if time == GetHandleReal( t, "maxtime" ) then
        call PauseTimer( t )
        call FlushHandleLocals( t )
        call DestroyTimer( t )
        call PauseUnit( u, false )
    else
        call SetHandleReal( t, "time", time )
    endif

    set u = null
    set t = null
endfunction

function WhirlfuryLoop takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit u = GetHandleUnit( t, "targ" )
    local unit u2 = GetHandleUnit( t, "cast" )
    local real angle = GetHandleReal( t, "ang" ) + GetHandleReal( t, "anginc" )
    local real initdist = GetHandleReal( t, "initdist" )
    local real time = GetHandleReal( t, "time" ) + 0.01
    local real maxtime = GetHandleReal( t, "maxtime" )

    call UnitDamageTarget( u2, u, GetHandleReal( t, "dmg" ), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null )

    if angle > 360 then
        set angle = angle - 360
    endif

    call SetUnitX( u, GetUnitX( u2 ) + initdist * Cos( angle * bj_DEGTORAD ) )
    call SetUnitY( u, GetUnitY( u2 ) + initdist * Sin( angle * bj_DEGTORAD ) )

    if time >= maxtime then
        call PauseTimer( t )
        call FlushHandleLocals( t )
        call DestroyTimer( t )
        set t = CreateTimer()
        call SetHandleReal( t, "ang", Atan2( GetUnitY( u ) - GetUnitY( u2 ), GetUnitX( u ) - GetUnitX( u2 ) ) * bj_RADTODEG )
        call SetHandleHandle( t, "cast", u2 )
        call SetHandleHandle( t, "targ", u )
        call SetHandleReal( t, "maxtime", 2 )
        call SetHandleReal( t, "time", 0 )
        call TimerStart( t, 0.01, true, function WhirlfuryLoop2 )
    else
        call SetHandleReal( t, "time", time )
        call SetHandleReal( t, "ang", angle )
    endif

    set u = null
    set u2 = null
    set t = null
endfunction

function WhirlfuryInitLoop takes unit u, unit u2, integer i, real duration returns nothing
    local timer t = CreateTimer()
    call SetHandleReal( t, "dmg", Whirlfury_Damage(i)/100 )
    call SetHandleReal( t, "anginc", Whirlfury_Anginc(i) )
    call SetHandleReal( t, "ang", Atan2( GetUnitY( u2 ) - GetUnitY( u ), GetUnitX( u2 ) - GetUnitX( u ) ) * bj_RADTODEG )
    call SetHandleHandle( t, "cast", u2 )
    call SetHandleHandle( t, "targ", u )
    call SetHandleReal( t, "initdist", SquareRoot( ( GetUnitX( u2 ) - GetUnitX( u ) ) * ( GetUnitX( u2 ) - GetUnitX( u ) ) + ( GetUnitY( u2 ) - GetUnitY( u ) ) * ( GetUnitY( u2 ) - GetUnitY( u ) ) ) )
    call SetHandleReal( t, "maxtime", duration )
    call SetHandleReal( t, "time", 0 )
    call PauseUnit( u, true )
    call TimerStart( t, 0.01, true, function WhirlfuryLoop )
    set t = null
endfunction

function WhirlfuryIniter takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit u = GetHandleUnit( t, "u" )
    local integer i = GetUnitAbilityLevel( u, Whirlfury_Rawcode() )
    local group g = CreateGroup()
    local group safety = GetHandleGroup( t, "safe" )
    local unit u2
    local unit u3
    local integer maxTargs = GetHandleInt( t, "maxTargs" )
    local real Time = GetHandleReal( t, "timeelapsed" ) + .5
    call GroupEnumUnitsInRange( g, GetUnitX( u ), GetUnitY( u ), Whirlfury_AOE(i), null )
    loop
        set u2 = FirstOfGroup( g )
        exitwhen u2 == null
        if maxTargs != Whirlfury_MaxTargets(i) and ( IsUnitEnemy( u2, GetOwningPlayer( u ) ) or Whirlfury_HitsAllied() ) and not IsUnitInGroup(u2,safety) then
            call WhirlfuryInitLoop( u2, u, i, Whirlfury_Duration(i) - Time )
            call GroupAddUnit( safety, u )
            set maxTargs = maxTargs + 1
        endif
        call GroupRemoveUnit( g, u2 )
    endloop
    if maxTargs == Whirlfury_MaxTargets(i) or Time >= Whirlfury_Duration(i) then
        call PauseTimer( t )
        call FlushHandleLocals( t )
        call DestroyTimer( t )
        call DestroyGroup( safety )
    else
        call SetHandleReal( t, "maxTargs", maxTargs )
        call SetHandleReal( t, "timeelapsed", Time )
        call SetHandleHandle( t, "safe", safety )
    endif
    call DestroyGroup( g )
    set g = null
    set u = null
    set safety = null
    set t = null
endfunction

function Whirlfury takes nothing returns nothing
    local timer t = CreateTimer()
    local unit u = GetTriggerUnit()
    call SetHandleHandle( t, "safe", CreateGroup() )
    call SetHandleHandle( t, "u", u )
    call SetHandleReal( t, "timeelapsed", 0 )
    call SetHandleInt( t, "maxTargs", 0 )
    call TimerStart( t, .5, true, function WhirlfuryIniter )
    set u = null
    set t = null
endfunction

function WhirlfuryConds takes nothing returns boolean
    return GetSpellAbilityId() == Whirlfury_Rawcode()
endfunction

function InitTrig_Whirlfury takes nothing returns nothing
    set gg_trg_Whirlfury = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Whirlfury, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Whirlfury, Condition( function WhirlfuryConds ) )
    call TriggerAddAction( gg_trg_Whirlfury, function Whirlfury )
endfunction
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
Hmm seems the problem "FlushHandleLocals( t )"!
From what I can see every variable is flushed except you use that function to flush the chache.

The leaking might be caused by that function not flushing the chache properly.

Try replacing all the handle functions with a global veriable (removing multincastability) and see if it then leaks. If it still leaks then its some problem with you forgeting to flush a variable.

Also try testing the chache after after you use "FlushHandleLocals( t )" for values to check that it really does what it is meant to do and flush the chache.

Maybe the "real" and "integer" variables are leaking (I dought though since no one has reported they leak ever) so try setting them to 0 after the function is over.

If your testing the spell in SP maybe the handle chache your using is horriably leaked from previous spell tests and so try deleting the game chache file under your profile in the wc3 folder and retesting.

Also please state how you detected the leak since the game lagging (common method) is inaccurate since background programs can cause lag and also too many function calls per sec can cause game lag.
The memory check method is also inaccurate since wc3 naturly increases memory over time due to replays and such.

Also check that all timers in all function in the map are destroyed since the leak could be caused by a timer every so often not being destroyed (not in your actual spell but in other functions).

Also are you creating many units and stuff since it could just be that the "leak" is the delay taken to find a variable but there are so many non leaked variables that it semms as if some have leaked yet none have.

If none of theses help it will then probably be a blizzard programming bug and is unfixable.
In therory Your script is ok buti do not know off hand whats causing the leak the only way to find out is threw trial and error.

I hope this post helps you fix your spell.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
i found out its actually NOT a leak, its that

JASS:
and not IsUnitInGroup(u2,safety)

isnt firing, and its returning false even if they are in the group.

I detected this by checking the targets so far ( with a BJDebugMsg ), and found that a single unit was still increasing the targets every .5s.
 
Level 5
Joined
May 22, 2006
Messages
150
Maybe you should replace as much of this cache stuff with function parameters.
Makes the whole thing easier to look over.
... And as you wrote yourself: These cache functions are slow.

This manner:
JASS:
function WhirlfuryLoop2 takes unit u,real angle,real time returns nothing
local timer t = GetExpiredTimer() 
set time = time + 0.01 

[...]
endfunction

Of course, this means to replace the timer controlled function calls with a less simple construction...
Or to use something like "TriggerSleepAction(0.001)" (if 0.26 is not the minimal value).
 
Level 5
Joined
May 9, 2006
Messages
162
You could use pools from CSCache, they are limited but much faster than gc.
In 0.01 cycle gc becomes obsolete and very slow so using gc is not a good solution.
 
Level 5
Joined
May 9, 2006
Messages
162
Yes, you're right, but
a) This is no matter for linked lists
b) Linked lists are no matter for you
You didn't understand me. I suugest you use locations instead of GC in a cycle, if you want to increase the efficency. Do you not understand?
 
Status
Not open for further replies.
Top