Group Leaking

Status
Not open for further replies.
Level 7
Joined
Apr 18, 2010
Messages
102
Hi.. I made a simple dash ability that creates after images when the caster is moving.
however there seems to be a leak that I cannot solve...

JASS:
scope Dashv2
    globals
        private real FPS = 1. / 32.
        private string ANIMATION = "SLEEP"
        private real RANGE = 450
        private real CASTTIME = 0.0
        private real DURATION = 0.25
        private string DASHSFX = "war3mapImported\\nitu.mdx"
        private integer SPELLID = 'Adsh'
    endglobals
  
    private struct tempDat
        unit caster
        player owner
        real angle
        real casttime
        real duration
        real speed
        group fadegroup
        integer stage
      
        static integer dindex = -1
        static timer period = CreateTimer()
        static thistype array data
      
        method GetUnitModel takes unit u returns string
            local integer ut = GetUnitTypeId(u)
            local string s
          
            if ut == 'Hemy' then
                set s =  "war3mapImported\\Emiya.mdl"
            endif
          
            return s
        endmethod
      
        method destroy takes nothing returns nothing
            local unit u
            loop
                set u = FirstOfGroup(this.fadegroup)
                exitwhen u == null
                call DummyAddRecycleTimer(u, 6)
                call DestroySFX(u)
                call GroupRemoveUnit(this.fadegroup, u)
            endloop
          
            set u = null  
            set this.caster = null
            set this.owner = null
            call DestroyGroup(this.fadegroup)
            set this.fadegroup = null
          
            if dindex == -1 then
                call PauseTimer(period)
            endif
            call this.deallocate()
        endmethod
      
        static method periodic takes nothing returns nothing
            local tempDat this
            local integer i = 0
            local real x1
            local real y1
            local real x2
            local real y2
            local unit u
          
            loop
                exitwhen i>dindex
                set this = data[i]
              
                if this.stage == 1 then
                    if GetWidgetLife(this.caster) >= 0.405 then
                        if this.casttime > 0 then
                            set this.casttime = this.casttime - FPS
                        else
                            set this.stage = 2
                            set x1 = GetUnitX(this.caster)
                            set y1 = GetUnitY(this.caster)
                            set u = GetRecycledDummy(x1,y1,0, Rad2Deg (this.angle) ) //uses DummyRecycler by flux
                            call DestroyEffect( AddSpecialEffectTarget( DASHSFX ,  u, "origin") )
                            call DummyAddRecycleTimer(u, 0.7)
                        endif
                    else
                        set this.stage = 0
                    endif
              
                else
                    if this.stage == 2 then
                        if this.duration>0 then
                            set this.duration = this.duration - FPS
                            set x1 = GetUnitX(this.caster)
                            set y1 = GetUnitY(this.caster)
                            set x2 = x1 + this.speed*Cos(this.angle)
                            set y2 = y1 + this.speed*Sin(this.angle)
                          
                          
                            if IsTerrainWalkable(x2, y2) == true then
                                call SetUnitX(this.caster, x2)
                                call SetUnitY(this.caster, y2)
                            endif
                          
                            set u = FirstOfGroup(this.fadegroup)
                            call GroupRemoveUnit(this.fadegroup, u)
                            call SetUnitVertexColor(u, 100, 100, 100, 50)
                            call SetUnitX(u, x1)
                            call SetUnitY(u, y2)
                            call FadeSFXTimed( u, 0.3)
                            call DummyAddRecycleTimer(u, 0.3 + 6.0)
                        else
                            set this.stage = 0
                        endif
                    endif
                endif  
              
                if this.stage == 0 then
                    call PauseUnit(this.caster, false)
                    call SetUnitAnimation(this.caster, "stand")
                    call KB_DoneCasting(this.caster)
                    set data[i] = data[dindex]
                    set i = i - 1
                    set dindex = dindex - 1
                    call this.destroy()
                endif
              
            //increase i
            set i = i + 1
            set u = null
            endloop
        endmethod
      
        static method onCast takes nothing returns nothing
            local tempDat this
            local integer looper
            local unit u
            local string sfx
            local integer id
            local real x
            local real y
            local real z
          
            set this = thistype.allocate()
          
            set this.caster = GetTriggerUnit()
            set this.speed = RANGE / (DURATION/FPS)
            set this.owner = GetOwningPlayer(this.caster)
            set this.angle = moveangle[GetConvertedPlayerId(this.owner)]
            set this.stage = 1
            set this.casttime = CASTTIME
            set this.duration = DURATION
            set this.fadegroup = CreateGroup()
            call PauseUnit(this.caster, true)
            call IssueImmediateOrder(this.caster, "stop")
            call SetUnitAnimation(this.caster, ANIMATION)
            call KB_Casting(this.caster)
      
            set x = GetUnitX(this.caster)
            set y = GetUnitY(this.caster)
            set z = GetUnitFlyHeight(this.caster)
            set sfx = GetUnitModel(this.caster)
      
            set looper = R2I( this.duration / FPS )
            loop
                exitwhen looper<1
                set u = GetRecycledDummy(x, y, z, Rad2Deg(this.angle) )
                call CreateSFX( u,  sfx, "")
                set id = GetUnitUserData(u)
                call BlzPlaySpecialEffect( SFXhead[id], ANIM_TYPE_SLEEP)
                call GroupAddUnit( this.fadegroup, u)
                call SetUnitVertexColor(u, 0, 0, 0, 0)
                set looper = looper - 1
            endloop
            set u = null
            set sfx = ""
          
        set dindex = dindex + 1
        set data[dindex] = this
        if dindex == 0 then
            call TimerStart(period, FPS, true, function thistype.periodic)
        endif
        endmethod
      
        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('Adsh', function thistype.onCast)
        endmethod
      
    endstruct
endscope



upload_2019-10-26_9-44-49.png




upload_2019-10-26_9-45-2.png



If i comment out this part of code

  • set u = FirstOfGroup(this.fadegroup)
no fade sfx will appear but then there will be no leaks.
 
Level 13
Joined
May 10, 2009
Messages
868
This looks weird to me
JASS:
    set data[i] = data[dindex]
    set i = i - 1
    set dindex = dindex - 1
    call this.destroy()
Have you tried doing this instead?
JASS:
    call this.destroy()
    set data[i] = data[dindex]
    set i = i - 1
    set dindex = dindex - 1
 
Level 41
Joined
Feb 27, 2007
Messages
5,229
Error here, I assume this should be x2 and y2:
call SetUnitX(u, x1)
call SetUnitY(u, y2)

It's hard to guess what the problem is because you're using a variety of functions not defined in the script so we don't know what they do or that they work properly:
CreateSFX()
DestroySFX()
FadeSFXTimed()
GetRecycledDummy()
DummyAddRecycleTimer()


What are you using to display that printout of leaks? Why are you allocating all the dummies at once into a group instead of just creating them at each step of the dash? This would remove the need for the group entirely. I presumed we weren't supposed to overwrite the default .destroy method and instead should use an .onDestroy method to do whatever is necessary. Perhaps that is your problem.
 
Level 7
Joined
Apr 18, 2010
Messages
102
- sorry, hmm allow me to explain

the reason i create the dummies at the start of the spell is so that their dash animation will start at the same time as the caster's (so while he is dashing, it would appear that a trail of images appear behind him)

the x1. y1 is intentional.


CreateSFX(), DestroySFX(), FadeSFXTimed() functions are here:

JASS:
library SFXsystem
    /* FUNCTION LIST
 
    -CreateSFX(unit u, string head, string tail)
        -creates 2 special effects and attaches them to unit u
     
    -DestroySFX(unit u)
        -destroys 2 special effects attached to unit u
     
    -FadeSFXTimed(unit u, real t)
        -destroys special effects attached to unit u after a delay t
    -uses Bribe's Unit Indexing
     
    */
    globals
        group SFXgroup = CreateGroup()
        private real FPS = 1./32.
        private timer period = CreateTimer()
        effect array SFXhead
        effect array SFXtail
        private real array SFXtimer
        private integer SFXindex = 0
        private integer id
    endglobals
 
    function CreateSFX takes unit u, string a, string b returns nothing
        set id = GetUnitUserData(u)
        if a != "" then
            set SFXhead[id] = AddSpecialEffectTarget(a, u, "origin")
        endif
        if b != "" then
            set SFXtail[id] = AddSpecialEffectTarget(b, u, "origin")
        endif
    endfunction
    function DestroySFX takes unit u returns nothing
        set id = GetUnitUserData(u)
        call DestroyEffect(SFXhead[id])
        set SFXhead[id] = null
        call DestroyEffect(SFXtail[id])
        set SFXtail[id] = null
    endfunction
     
 
    private function TimedSFX takes nothing returns nothing
        local unit u = GetEnumUnit()
        set id = GetUnitUserData(u)
        if SFXtimer[id] >= 0.0 then
            set SFXtimer[id] = SFXtimer[id] - FPS
        else
            call SetUnitVertexColor(u, 0, 0 , 0 , 0)
            call DestroyEffect(SFXhead[id])
            set SFXhead[id] = null
            call DestroyEffect(SFXtail[id])
            set SFXtail[id] = null
            call GroupRemoveUnit( SFXgroup, u)
            set SFXindex = SFXindex - 1
            if SFXindex == 0 then
                call PauseTimer(period)
            endif
         
        set u = null
        endif
    endfunction
 
    private function sfxperiodic takes nothing returns nothing
        call ForGroup( SFXgroup, function TimedSFX)
    endfunction
 
 
    function FadeSFXTimed takes unit u, real t returns nothing
        set id = GetUnitUserData(u)
        set SFXtimer[id] = t
        set SFXindex = SFXindex + 1
            if SFXindex==1 then
                call TimerStart(period, FPS, true, function sfxperiodic)
            endif
        call GroupAddUnit(SFXgroup, u)
    endfunction
 
endlibrary


- the DummyAddRecycleTimer() and GetRecycleDummy() are from Flux's dummy recycler system.

Dummy Recycler v1.25


- the checking of leaks is from here:
Memory Leaks Checker v1.6
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,241
Your map is using a unit indexer that uses unit user data (GUI custom value) right? The SFX functions require this.

Can you test it is actually leaking and not just hitting some sort of steady state?

For example cast it 100 times (use a trigger to do this if necessary). If the reported leaks are high then you have a problem. If they are low then it might be false positive reports.
 
Level 9
Joined
Sep 20, 2015
Messages
385
I am not good with code so i suggest another solution but it require some model editing skills. Basically you ceate a model with the hero in that dashing position, you make it with only the birth animation and after some times it fade and disappear. Then you can use Destroy Effect to create it

JASS:
 call DestroyEffect( AddSpecialEffect( "TheHeroModel.mdx", X of unit, Y of unit))

This way you don't have to worry about leaks, i think
 

Uncle

Warcraft Moderator
Level 68
Joined
Aug 10, 2018
Messages
7,166
You don't need to edit the model or use dummies.

With Special Effects you can achieve a trail effect fairly easily using these:
vJASS:
BlzSetSpecialEffectTime(whichEffect, time)
BlzSetSpecialEffectYaw(whichEffect, yaw)
BlzSpecialEffectAddSubAnimation(whichEffect, whichSubAnim)
BlzPlaySpecialEffect(whichEffect, whichAnim)
BlzSetSpecialEffectAlpha(whichEffect, alpha)
Use Time to make the special effects match the hero's current animation frame. Or you could use Alpha to hide the special effects until you want to display them. Use Yaw for the direction of the special effects. Use PlaySpecialEffect and AddSubAnimation to play the dash animation. Then move the Special Effects out of bounds and remove them once they're finished.
 
Status
Not open for further replies.
Top