• 🏆 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!
  • ✅ Time to vote for the top 3 models! The POLL for Hive's 6th HD Modeling Contest: Mechanical is now open! 📅 Poll close on July 16, 2024! 🔗 Cast your vote now!
  • ✅ The POLL for Hive's Texturing Contest #33 is OPEN! Vote for the TOP 3 SKINS! 🔗Click here to cast your vote!

Things That Leak

Level 20
Joined
Feb 23, 2014
Messages
1,264
Will this leak every 2 second? Or just keep one consistent group?
It won't leak - you're reusing a unit group that already exists and is assigned to a variable.

Leaks happen when you create a unit group that you have no reference to or do something to lose an existing reference (e.g. assigning a variable that held a unit group to another unit group, without storing it under another variable first). None of that happens here, so you're good.
 
Last edited:
View attachment 387961
Will this leak every 2 second? Or just keep one consistent group?

PS: This is just a trigger I made up. I'm not actually using it. I'm just wondering if a new group is created if I add a new unit to a cleared group.
No leaks, as there is no group being created. You're just referencing the existing UG_ActiveWave group.
 
Level 13
Joined
Jun 23, 2009
Messages
298
are there things that leak but cannot be destroyed? other than the ones mentioned in OP (all of which can be destroyed)
All leaks can be fixed by either destroying the leaking variable or nulling it except strings iirc. (correct me if I'm wrong, people out there!)
EDIT: According to various sources around the site units created always leak around 0.04 kb no matter how you handle them

Anyway, I need a refresher on this whole deal, I think I was told once that if you use unit variables to point at units with expiration timers you don't actually need to null the unit variable afterwards because when the unit expires it's basically null anyway, is that correct?

In other words does code like this:
JASS:
private function Flame takes nothing returns nothing
local unit dummy
local flame f = GetTimerData(GetExpiredTimer())
set dummy = CreateUnit(f.p, TWKDummy, f.x, f.y, bj_UNIT_FACING)
    call UnitAddAbility(dummy, DUMMY_SPELL)
    call UnitApplyTimedLife(dummy, 'BTLF', 1.00)
    call IssuePointOrder(dummy, "flamestrike", f.x, f.y)
    call ReleaseTimer(GetExpiredTimer())
    call f.destroy()
endfunction
Leak when it comes to the unit variable?

Bonus question: Timers are to be destroyed only when they're no longer needed, does that logic also apply to vJass structs? Like, if I pass the same struct through multiple functions and put them into local variables each time (like in this case local flame f, that was made together with the timer that's bringing it over) do I have to destroy those only in the last function they're used?
 
Last edited:
Level 9
Joined
Mar 26, 2017
Messages
376
Leaks that are concerned are mostly game engine objects and/or their attached handle.

The function above will cause a handle leak. Although the unit will be removed from memory after decay, the unit handle will remain. This is due to a bug in JASS where a local variable after function completion is not credited for its loss of reference to the referenced object. This is only achieved through explicitly setting to null. Else the unit handle will never be removed from memory by game engine cleanup process.

In Lua there is no such issue, and it is not needed to null the local.


Other question: It depends on what data is obtained by function GetTimerData.
The function GetExpiredTimer returns a reference to the Timer handle. If this handle will be stored in another variable indefinitely, it will leak. As long as least one variable references a particular handle, that handle will remain in memory.
 
Level 13
Joined
Jun 23, 2009
Messages
298
Thank you for the explanation, it's mostly clear to me now, aside from this:
Other question: It depends on what data is obtained by function GetTimerData.
GetTimerData is from TimerUtils 2.0, which is the last version Vexorian made iirc: function GetTimerData takes timer t returns integer
Based on this and the fact vJass (and by extension JassHelper) accepts structs as integers I would assume that I'm fine with not nulling/zeroing the structs at the end of every single function I use them in and that destroying them after having been used is enough, my structs however often contain handles so that's where I'm getting confused.
Granted, I could probably look at what the compiled code looks like and I'll have my answer but still... asking doesn't hurt anybody so, using the simplest snippet of code I can find atm:
vJASS:
scope SneakAttack initializer Init

globals
    private constant integer SPELL = 'Bek1'
    private constant integer BaseDamage = 75
    private constant integer IncDamage = 75
endglobals

private struct caster
    unit u

    static method create takes unit casting returns caster
        local caster Cas = caster.allocate()
    set Cas.u = casting
    return Cas
    endmethod

endstruct

private function Unpause takes nothing returns nothing
local caster u = GetTimerData(GetExpiredTimer())
call PauseUnit(u.u, false)
call ReleaseTimer(GetExpiredTimer())
call u.destroy()
endfunction

private function Main takes nothing returns boolean
local caster u
if GetSpellAbilityId() == SPELL then
    set u = caster.create(GetTriggerUnit())
    call PauseUnit(GetTriggerUnit(), true)
    call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl", GetTriggerUnit(), "origin"))
    call SetUnitPosition(GetTriggerUnit(), GetUnitX(GetSpellTargetUnit()), GetUnitY(GetSpellTargetUnit()))
    call SetUnitFacing(GetTriggerUnit(), bj_RADTODEG * Atan2(GetUnitY(GetSpellTargetUnit()) - GetUnitY(GetTriggerUnit()), GetUnitX(GetSpellTargetUnit()) - GetUnitX(GetTriggerUnit())))
    call SetUnitAnimation(GetTriggerUnit(), "attack")
    call UnitDamageTarget(GetTriggerUnit(), GetSpellTargetUnit(), BaseDamage + (IncDamage * GetUnitAbilityLevel(GetTriggerUnit(), SPELL)), true, false, ATTACK_TYPE_MELEE, DAMAGE_TYPE_ENHANCED, WEAPON_TYPE_METAL_LIGHT_SLICE)
    call FadingText(null, I2S(BaseDamage + (IncDamage * GetUnitAbilityLevel(GetTriggerUnit(), SPELL))) + "!", 255, 20, 50, GetUnitX(GetSpellTargetUnit()), GetUnitY(GetSpellTargetUnit()), 0.04, 2, 5, 10)
    if GetWidgetLife(GetSpellTargetUnit()) > 0.405 then
        call IssueTargetOrder(GetTriggerUnit(), "attack", GetSpellTargetUnit())
    endif
    call TimerStart(NewTimerEx(u), .9, false, function Unpause)
endif
return false
endfunction

private function Init takes nothing returns nothing
local trigger t = CreateTrigger()

    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function Main))

set t = null
endfunction

endscope
Is it fine like this? Can local caster u in function Main cause any sort of leaks?
 
Last edited:
@Michael Peppers calling destroy() on struct will internally make the struct instance (integer) be available again for new struct creations. It does this by calling this.deallocate(). Extra setting to 0 or something is not needed, and calling destroy() only once is enough.

If the struct has agent members, like units, they should be nulled together when an instance is destroyed. One usually defines own destroy() function, and puts the clean up logics there:
JASS:
method destroy takes nothing returns nothing
    call this.deallocate()          
    set this.target = null
endmethod

GetExpiredTimer() does not really leak, as the timer being recycled in used library. Same timer can/will be used again. But sure, one can say, in a scenario where a recyled timer won't be ever used again, then it results same as reference leak. But not too dangerous imo.

 
Level 13
Joined
Jun 23, 2009
Messages
298
Perfect, everything's clear now, thanks to you both :grin:

GetExpiredTimer() does not really leak, as the timer being recycled in used library. Same timer can/will be used again. But sure, one can say, in a scenario where a recyled timer won't be ever used again, then it results same as reference leak.
I assume that a single reference leak is small potatoes compared to the memory saved by TimerUtils through recycling timers over and over again instead of creating them each time.
 
Last edited:
Level 2
Joined
Aug 6, 2022
Messages
9
Hi all,
Coming back around to map editing, it's been a while so I'm afraid I might be rusty... Could anyone check this trigger and tell me if it's "clean" enough, or if there are some leaks I missed? Thanks
1659800277271.png
 
Level 2
Joined
Aug 6, 2022
Messages
9
Your last "RemoveLocation(udg_Location(CasterMI))" should be before the "Unit Group - pick every unit" scope
True, I don't use that variable after I begin this loop. Does it make a difference in terms of leak, whether I remove it before or after the loop?
 
Level 41
Joined
Feb 27, 2007
Messages
5,184
There's no reason to save the dummies in an array, wait, and then remove them all directly. Instead of all that just give each dummy a 1.0 second expiration timer when they're created. Also note that a properly configured dummy unit (speed 0, movement None, 0.00 cast backswing, using a model without cast animations (or no model at all)) can cast instantaneously in any direction without turning, which means that you can do something like this where only one dummy is needed and it successfully casts invisibility on all affected units by itself.
 
Level 2
Joined
Aug 6, 2022
Messages
9
There's no reason to save the dummies in an array, wait, and then remove them all directly. Instead of all that just give each dummy a 1.0 second expiration timer when they're created. Also note that a properly configured dummy unit (speed 0, movement None, 0.00 cast backswing, using a model without cast animations (or no model at all)) can cast instantaneously in any direction without turning, which means that you can do something like this where only one dummy is needed and it successfully casts invisibility on all affected units by itself.
Thanks, I forgot about expiration timers; they remove units without leaks? If so, that would indeed be simpler.
As for the number of dummy units, I understand your point. Since I've done it with multiple units, I'll keep it this way for now, but I'll try it your way for the next one.
Thanks all for your help!
 
Level 6
Joined
Jul 22, 2015
Messages
74
  • unitgroup.gif
    Unit Group - Pick all units of type Footman and do...
  • cam.gif
    Camera - Pan Camera as needed...
i still didn't understand of this....
so if i using this trigger it will leak right?

  • HeroSpawn
    • Events
      • Unit - A unit Dies
    • Conditions
      • ((Triggering unit) is A Hero) Equal to True
      • (Owner of (Triggering unit)) Not equal to Player 23 (Emerald)
      • (Owner of (Triggering unit)) Not equal to Player 24 (Peanut)
    • Actions
      • Wait 5.00 seconds
      • Selection - Clear selection for (Owner of (Triggering unit))
      • Set PM_Loop = (Custom value of (Triggering unit))
      • Set Point[PM_Loop] = (Center of Region[CheckPoint[PM_Loop]])
      • Hero - Instantly revive (Triggering unit) at Point[PM_Loop], Show revival graphics
      • Unit - Set mana of (Triggering unit) to 100.00%
      • Selection - Select (Triggering unit) for (Owner of (Triggering unit))
      • Camera - Pan camera for (Owner of (Triggering unit)) to Point[PM_Loop] over 0.00 seconds
      • Custom script: call RemoveLocation(udg_Point[udg_PM_Loop])
 
i still didn't understand of this....
so if i using this trigger it will leak right?

  • HeroSpawn
    • Events
      • Unit - A unit Dies
    • Conditions
      • ((Triggering unit) is A Hero) Equal to True
      • (Owner of (Triggering unit)) Not equal to Player 23 (Emerald)
      • (Owner of (Triggering unit)) Not equal to Player 24 (Peanut)
    • Actions
      • Wait 5.00 seconds
      • Selection - Clear selection for (Owner of (Triggering unit))
      • Set PM_Loop = (Custom value of (Triggering unit))
      • Set Point[PM_Loop] = (Center of Region[CheckPoint[PM_Loop]])
      • Hero - Instantly revive (Triggering unit) at Point[PM_Loop], Show revival graphics
      • Unit - Set mana of (Triggering unit) to 100.00%
      • Selection - Select (Triggering unit) for (Owner of (Triggering unit))
      • Camera - Pan camera for (Owner of (Triggering unit)) to Point[PM_Loop] over 0.00 seconds
      • Custom script: call RemoveLocation(udg_Point[udg_PM_Loop])
No, that won't leak because you aren't using "Pan Camera as needed" and are just using the normal "Pan Camera" function.

However, if you are using a WarCraft 3 version >= 1.29, the problem with that function is resolved.
 
Level 4
Joined
Nov 1, 2015
Messages
11
1675689208743.png

Conditions can leak right? Here are the examples of the ones I use alot:
The 1st condition makes sense to leak since it spawns a new unassigned group.
But what about the 2nd condition? I searched around but don't see any info about this "Living units" condition.
As for 3rd condition am not sure if that one would leak if i keep setting different units to Temp_UnitType
 
View attachment 424171
Conditions can leak right? The 1st condition makes sense to leak since it spawns a new unassigned group, but what about the 2nd condition? I searched around but don't see any info about this "Living units" condition.
Short answer: Yes, they can leak, but "living units count" does not.

Long answer (Requires some amount of jass knowledge):

Let's find out.
1. create the "minimum" trigger with the thing we want to check:
  • Untitled Trigger 001
    • Events
    • Conditions
      • (Number of living Footman units owned by Player 1 (Red)) Equal to 0
    • Actions
2. Edit -> Convert to custom text and look for the function called
JASS:
function Trig_Untitled_Trigger_001_Conditions takes nothing returns boolean
    if ( not ( CountLivingPlayerUnitsOfTypeId('hfoo', Player(0)) == 0 ) ) then //<-- This seems to contain the words we are looking for (Living units). 
        return false
    endif
    return true
endfunction

function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
    set gg_trg_Untitled_Trigger_001 = CreateTrigger(  )
    call TriggerAddCondition( gg_trg_Untitled_Trigger_001, Condition( function Trig_Untitled_Trigger_001_Conditions ) )
    call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions )
endfunction
3. Open blizzard.j (found in My documents/JassHelper/) with a decent text editor and search for <thing> (CountLivingPlayerUnitsOfTypeId). (Note: unsure if any GUI-action is defined in Common.j)
4. See if it leaks by creating something that it doesn't clean up.
JASS:
function CountLivingPlayerUnitsOfTypeId takes integer unitId, player whichPlayer returns integer
    local group g
    local integer matchedCount
    set g = CreateGroup()  //<--- Creates a group
    set bj_livingPlayerUnitsTypeId = unitId
    call GroupEnumUnitsOfPlayer(g, whichPlayer, filterLivingPlayerUnitsOfTypeId)
    set matchedCount = CountUnitsInGroup(g)
    call DestroyGroup(g)  //<--- cleans it up
    return matchedCount
endfunction
5. In this case, it creates a group and cleans it up. Neat!
 
So the leak is avoided because the actual function itself had the command to clean it up, pretty useful thanks.
Yes, correct.
Apparently those functions exists. I have the feeling like most GUI-actions that interacts with groups and points leaked, so I'm surprised that this didn't to be honest.
 
Yep, only risk is the handle ID reference leak, which is something almost impossible to avoid in GUI anyway.
I considered mentioning it, but I thought I had enough details and complexity in my answer already, but good point.
I.E. In order to be "completely leak free" the jass that the GUI-condition is calling need to set group g null before returning the answer.
It's fairly minor compared to many other things, but in a 0.01 second timer, it could get bad after a while.
 
I considered mentioning it, but I thought I had enough details and complexity in my answer already, but good point.
I.E. In order to be "completely leak free" the jass that the GUI-condition is calling need to set group g null before returning the answer.
It's fairly minor compared to many other things, but in a 0.01 second timer, it could get bad after a while.
Switching to Lua would fix all such problems without having to modify the blizzard.j file.
 
Level 17
Joined
Jun 2, 2009
Messages
1,193
Hello. I have to ask something about leaks. This trigger runs every seconds. Should i destroy DestekGrubu and PG_Income every seconds? I am totally confused at this point. Because these groups not for temporary actions. Every seconds i am giving extra gold to players within groups.

  • Her saniye gold
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • Player Group - Pick every player in (All players) and do (Player - Add 1 to (Picked player) Current gold)
      • Player Group - Pick every player in DestekGrubu and do (Player - Add 1 to (Picked player) Current gold)
      • Player Group - Pick every player in PG_Income and do (Player - Add 2 to (Picked player) Current gold)
 
Level 4
Joined
Nov 1, 2015
Messages
11
Hello. I have to ask something about leaks. This trigger runs every seconds. Should i destroy DestekGrubu and PG_Income every seconds? I am totally confused at this point. Because these groups not for temporary actions. Every seconds i am giving extra gold to players within groups.
1676916425050.png

As an example, the first one should leak because it's a "temporary" group that was made in this trigger, but the second one should not because it's an already existing group in our variables, though somebody can correct me if am wrong. I've also heard that All Players does not leak, so everything in your trigger should be fine.
 
Hello. I have to ask something about leaks. This trigger runs every seconds. Should i destroy DestekGrubu and PG_Income every seconds? I am totally confused at this point. Because these groups not for temporary actions. Every seconds i am giving extra gold to players within groups.

  • Her saniye gold
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • Player Group - Pick every player in (All players) and do (Player - Add 1 to (Picked player) Current gold)
      • Player Group - Pick every player in DestekGrubu and do (Player - Add 1 to (Picked player) Current gold)
      • Player Group - Pick every player in PG_Income and do (Player - Add 2 to (Picked player) Current gold)
None of these leak.
 
Level 41
Joined
Feb 27, 2007
Messages
5,184
Should i destroy DestekGrubu and PG_Income every seconds? I am totally confused at this point. Because these groups not for temporary actions.
The only differences between something being a "leak" and something being "permanent data" is whether or not you re-use it (you are) and whether or not the reference (sometimes called a pointer) to that object is 'lost' (these are not lost). Neither is true here, so neither of those player groups should be considered "leaks"; they are permanent data for you.
 
Level 3
Joined
Nov 26, 2009
Messages
35
Should i clear variables(caster, casterPoint, dummy?) in this function?
JASS:
function a takes unit caster, location casterPoint, real distance, unit dummy returns  nothing
   
endfunction
 
Should i clear variables(caster, casterPoint, dummy?) in this function?
JASS:
function a takes unit caster, location casterPoint, real distance, unit dummy returns  nothing
  
endfunction
You do not need to null any of those variables. Parameters don't cause handle reference leaks like local variables do.

As Wrda said, you do need to call RemoveLocation, provided it's not a persistent location that is needed for something like a projectile system or Spell System.
 
Level 7
Joined
Sep 10, 2022
Messages
96
  • Custom script: set bj_wantDestroyGroup = true
  • Unit Group - Pick every unit in (Playable Map Area) and do (Unit - Hide (Picked unit))
Do* I need to write "set bj_wantDestroyGroup = true" every time before every new "Unit Group - Pick every unit in (Playable Map Area) and do (Unit - Hide (Picked unit))" ?
 
Last edited:
  • Custom script: set bj_wantDestroyGroup = true
  • Unit Group - Pick every unit in (Playable Map Area) and do (Unit - Hide (Picked unit))
Does I need to write "set bj_wantDestroyGroup = true" every time before every new "Unit Group - Pick every unit in (Playable Map Area) and do (Unit - Hide (Picked unit))" ?
Yes, before each one. Except in Lua when using Lua-infused GUI.
 
Level 41
Joined
Feb 27, 2007
Messages
5,184
Frankly, Herly, there's no reason for anyone to use any of those functions which is why it's not part of the list.
  • Entire Map is pointless because no units can go into the bounds that it includes that Playable Map Area does not include. There's a reason this isn't just a global rect variable and instead returns a new rect object each time it's called.
  • Using regions for enter/leave events is reasonable but you should just define them in the region editor rather than making them dynamically.
  • 99% of the time you want to do something in an area (search, move objects, etc.) you want to do it in a circle rather than a rectangle, so GroupEnum... functions are the better choice. That covers basically every other use case for creating a dynamic region.
There is this native to properly destroy rects: native RemoveRect takes rect whichRect returns nothing.
 
Level 24
Joined
Jun 26, 2020
Messages
1,861
Frankly, Herly, there's no reason for anyone to use any of those functions which is why it's not part of the list.
  • Entire Map is pointless because no units can go into the bounds that it includes that Playable Map Area does not include. There's a reason this isn't just a global rect variable and instead returns a new rect object each time it's called.
  • Using regions for enter/leave events is reasonable but you should just define them in the region editor rather than making them dynamically.
  • 99% of the time you want to do something in an area (search, move objects, etc.) you want to do it in a circle rather than a rectangle, so GroupEnum... functions are the better choice. That covers basically every other use case for creating a dynamic region.
There is this native to properly destroy rects: native RemoveRect takes rect whichRect returns nothing.
Yeah I know about that native.
I was talking for that 1% of the cases when someone want to use a rectangle to check.
 
Level 9
Joined
Jul 5, 2014
Messages
324
I'm a bit nervous about unchecked leaks since I have a few dodgy triggers. Here's one potentially vomit inducing that's needed but probably could have done better.

  • Exploding Guys
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Unit Group - Pick every unit in (Units of type Animated Bandit) and do (Unit - Make (Picked unit) Explode on death)
      • Unit Group - Pick every unit in (Units of type Animated Brigand) and do (Unit - Make (Picked unit) Explode on death)
      • Unit Group - Pick every unit in (Units of type Animated Rogue) and do (Unit - Make (Picked unit) Explode on death)
      • Unit Group - Pick every unit in (Units of type Animated Spear Thrower) and do (Unit - Make (Picked unit) Explode on death)
      • Unit Group - Pick every unit in (Units of type Animated Fighter) and do (Unit - Make (Picked unit) Explode on death)
      • Unit Group - Pick every unit in (Units of type Animated Villain) and do (Unit - Make (Picked unit) Explode on death)
      • Unit Group - Pick every unit in (Units of type Animated Footman) and do (Unit - Make (Picked unit) Explode on death)
      • Unit Group - Pick every unit in (Units of type Animated Knight) and do (Unit - Make (Picked unit) Explode on death)
      • Unit Group - Pick every unit in (Units of type Animated Militia) and do (Unit - Make (Picked unit) Explode on death)
      • Unit Group - Pick every unit in (Units of type Animated Rifleman) and do (Unit - Make (Picked unit) Explode on death)
      • Unit Group - Pick every unit in (Units of type Animated Chaplain) and do (Unit - Make (Picked unit) Explode on death)
I have a number of "within range" triggers, I'm not sure how much these are leaking.

  • Guard Change 2
    • Events
      • Unit - A unit comes within 400.00 of Blacksmith 0243 <gen>
    • Conditions
      • (Owner of (Triggering unit)) Equal to Player 2 (Blue)
    • Actions
      • Trigger - Turn off (This trigger)
      • Unit - Order Enforcer 0404 <gen> to Move To (Center of BanditGuard1 <gen>)
      • Unit - Order Enforcer 0400 <gen> to Move To (Center of BanditGuard2 <gen>)
I also have a triggered spell that uses a visual version of thunderclap (no debuff/damage) to give a rippling air effect which I've read is prone to cause terrain deformation leak. Any way to minimize the leaks these causing?
 
Level 41
Joined
Feb 27, 2007
Messages
5,184
Anywhere a (unit) group exists temporarily and will not be reused again, you must destroy the group so it doesn’t become a memory leak. Each of those lines creates a group (“units of type __”) and then iterates over the units in that group.

You can save the group directly into a variable and destroy each of them, or you can benefit from the shortcut the developers put in place to automatically destroy groups after they’re used. Both are equivalent as long as you use them properly:
  • Set GroupVar = (Units of type Footman)
  • Unit Group - Pick every unit in GroupVar and do (Actions)
    • Loop - Actions
      • Unit - Cause (Picked Unit) to explode on death
  • Custom script: call DestroyGroup(udg_GroupVar) //the specifics are shown at the beginning of this thread/tutorial
  • //…
  • //or this:
  • Custom script: set bj_wantDestroyGroup = true //this only works for the ONE next created group, after which this variable automatically returns to the value of false
  • Unit Group - Pick every unit I (Units of type Footman) and do (Actions)
    • Loop - Actions
      • Unit - Cause (Picked Unit) to explode on death
As a side note: there is no reason to use single-line if/then/else and single-line group/force pick actions. They are frustrating to edit, can’t be copied from/to, and are a pain to convert to regular loops if you learn you need more than one action per unit. They look a bit smaller but are functionally the same as the full loops.

Anywhere a point exists that will not be saved and reused, you must properly remove the point after use so it does not become a memory leak. You can only do this with RemoveLocation, as there is no backdoor shortcut like groups.
  • Set TempPoint = (Center of BanditGuard1 <gen>)
  • Unit - Order Enforcer 0404 <gen> to Move To TempPoint
  • Custom script: call RemoveLocation(udg_TempPoint) //also explained in first post of thread/tutorial
There’s nothing else leaking in either of those two triggers. No issues with those events or anything; it’s entirely about the objects you create and making sure they don’t keep existing after you no longer need them.
I also have a triggered spell that uses a visual version of thunderclap (no debuff/damage) to give a rippling air effect which I've read is prone to cause terrain deformation leak.
What you have said is not quite right; there is no terrain deformation leak. The terrain deformation created by Thunderclap only shows for players who have vision of the cast point, and will not show in Fog of War. This normally isn’t an issue, but the terrain deformation is actually a temporary alteration of the terrain height and would return different values for the height there depending on if the player could see the deformation or not. If you then query the terrain height of a location within the deformation the variable that holds the computed terrain height will have different values for different players!

The variable existing in that superposition isn’t inherently bad, but trying to do something with the variable will then usually produce two different game states for the different groups of players. (Player A thinks some unit’s fly height should be set to 100 when everyone else agrees it should be set to 200). When that occurs the two groups of players will immediately desync from each other and the game will effectively lose some of its players.

None of this matters for single-player-only campaigns. I’m pretty sure that’s what you’re working on so you’re fine to keep using Thunderclap (and also would have been in the other thread now that I think about it… oops), but wanted to give the full picture about why deformations are to be avoided.
 
Level 9
Joined
Jul 5, 2014
Messages
324
The exploding units are all over until a certain point. Should I only destroy the unit group once there's no more of them? I'd guess if I do it immediately, those units would stop exploding from that point. Also, would it be a good idea to cram the whole bunch of different unit types into a single variable as "explodingUnits" and handle them as a single group?

And, based on your suggestion, regions I manually added to the map (not ones created through trigger) should be placed into variables and have them removed once they executed their associated task?

Also, is it a standard solution to have a single variable that I always use for one-time points? So whenever I have one of the hundred different "position of unit" or such, I just use the Temp_Point variable for them over and over?

What you have said is not quite right; there is no terrain deformation leak. The terrain deformation created by Thunderclap only shows for players who have vision of the cast point, and will not show in Fog of War. This normally isn’t an issue, but the terrain deformation is actually a temporary alteration of the terrain height and would return different values for the height there depending on if the player could see the deformation or not. If you then query the terrain height of a location within the deformation the variable that holds the computed terrain height will have different values for different players!
Okay, I've got confused by the initial post. There are a number of things that being said by players then refuted by others, so it's getting a little chaotic what's what.
 
Level 41
Joined
Feb 27, 2007
Messages
5,184
Okay, I've got confused by the initial post. There are a number of things that being said by players then refuted by others, so it's getting a little chaotic what's what.
What’s in the post is the handy reference. You do not need to read all 27 pages here. The thread linked to at the top is a detailed discussion of what a leak is and why you care about catching them. It is worth your time to read that one because something has not yet clicked for you.
I'd guess if I do it immediately, those units would stop exploding from that point.
Marking units to explode on death was the action you needed to do to units in the group. Once you have done that the group has no purpose for you because you will not loop over the group again. It doesn’t need to persist for those units to actually explode; the flag you have set in the action is what determines that. You would destroy the groups immediately after use, as I showed in my example. The group existing by itself doesn’t do or mean anything; it’s just a container for units that you then have to use intentionally to do something; they are not passive.

Just to be clear: the only units that will explode on death are those present in the map at the time these picks run. If you want units spawned later to expose lose you’ll have to add the explode on death flag some other way once the unit begins to exist.
Also, would it be a good idea to cram the whole bunch of different unit types into a single variable as "explodingUnits" and handle them as a single group?
The difference would not be noticeable in any way. If you were picking all these units constantly throughout the map it’s possibly more efficient in some meaningful way, but not for you. In a similar vein Units of Type has a permanent reference leak in it (not a memory leak, read the structured tutorial) which is why it’s blacklisted… but using it a few times in each map for things like this will never matter.
And, based on your suggestion, regions I manually added to the map (not ones created through trigger) should be placed into variables and have them removed once they executed their associated task?
In a literal sense yes those regions are the same as a leak if they are never used again. Trigger-created regions are absolutely something you should clean up, but regular regions are fine to just leave. There are a finite amount of them you made that never increases over the game. The point of learning to catch your own leaks is to prevent endless or uncapped generation of useless objects because they literally occupy memory space for no purpose. The existence of 100 regions won’t change anything.

That being said, you never, ever want to destroy a region variable that has been set to “Playable Map Area.” Internally that’s a global region variable generated in the map script for east reference in all your triggers. Destroying it will cause it to return null anywhere else something refers to it.
Also, is it a standard solution to have a single variable that I always use for one-time points? So whenever I have one of the hundred different "position of unit" or such, I just use the Temp_Point variable for them over and over?
Yes because triggers run sequentially, though instantaneously, and can interrupt each other only when their event fires and you should have a pretty good idea of when they might go off. This means you can be sure when a variable will be changed ‘prematurely’ and account for that:

  • Trigger A runs when a unit is issued a point order, and creates a special effect on its target point using TempPoint.
  • Trigger B sets TempPoint then causes a unit to cast Silence in an area, then destroys the TempPoint.
  • Bince B causes A to run, then the value of TempPoint will be overwritten by A interrupting B, and the original TempPoint is never destroyed. Its reference is lost and it exists forever.
 
Level 9
Joined
Jul 5, 2014
Messages
324
I'm reading the detailed version right now, some things are clearing up. I'll definitely need an overhaul on my triggers, I scarcely used custom script back then and even then I wasn't quite clear on it. I knew about leaks but not about how many things can be a factor.
In a similar vein Units of Type has a permanent reference leak in it (not a memory leak, read the structured tutorial) which is why it’s blacklisted… but using it a few times in each map for things like this will never matter.
So, you're saying I shouldn't put all of them into a single variable? Also how much of that is too much? I have 11 type of units picked for explosion and overall, "type of unit" was a pretty comfy until now. According to the detailed tutorial, there's a JASS option to remove reference leaks although the blacklisted ones are exceptions? Also, even the detailed version don't seem to specify terrain deformation leaks as multi or singleplayer, unless multi is default map being discussed.

For clarification: pick every unit to explode only occurs when I actually explode them? Until they're just running around on the map, they're not part of that group and they have to be destroyed exploded unit by unit? (Sorry if I appear dummy but this is not my native language and I tried to avoid misunderstanding. I'd rather give the impression of being slow than being uncertain.)

That being said, you never, ever want to destroy a region variable that has been set to “Playable Map Area.” Internally that’s a global region variable generated in the map script for east reference in all your triggers. Destroying it will cause it to return null anywhere else something refers to it.
This is only "playable map area" right? Not "center of playable map" or such?

  • Trigger A runs when a unit is issued a point order, and creates a special effect on its target point using TempPoint.
  • Trigger B sets TempPoint then causes a unit to cast Silence in an area, then destroys the TempPoint.
  • Bince B causes A to run, then the value of TempPoint will be overwritten by A interrupting B, and the original TempPoint is never destroyed. Its reference is lost and it exists forever.
So, I gotta make sure I won't leave undestroyed TempPoints behind before making a new one. I'm guessing having TempPoint1, TempPoints2 and such is a way to deal with these if I manipulate multiple single-used points. And I'd guess these TempPoints shouldn't be rendered to spells that get used by the player all the time because their reference can be overwritten by a sudden trigger.

Also: are leaks tied to a single map or the whole campaign? If Rexxar's main map, Durotar leaks and he takes a trip to the Darkspear Tribe which is a sub-map, will those come along? Or it will stick to Durotar?
 
Top