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

Does this cause a Leak?

Status
Not open for further replies.
Level 10
Joined
Mar 25, 2008
Messages
339
Does anything here leak?
I have probably one hundred similar triggers in my game.

Game - Display to (Player group((Owner of (Entering unit)))) the text: |c00cc0066Unknown V...
I'm concerned every time it does this its creating a new playergroup that never gets deleted.

And if it does is there any way to fix it easily? Because i'd have about 100 other ones to fix too, perhaps a copy paste way to instantly destroy the last created temporary group? shrug.

Also
Item - Remove (Item carried by (Entering unit) of type |c008801e2The Soulreaper|r)
Does "entering unit" or "triggering unit" leak? as im assuming its creating some sort of temporary variable each time it does this, i probably have multiple hundreds of those in the game.

Some people have reported instability in long term game sessions, I always assume it's just wc3's fault, but I do want to make my game more stable if possible.
 

Attachments

  • leak.png
    leak.png
    46.9 KB · Views: 23

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,455
Yes, Player group leaks. No, Entering unit and Triggering unit don't leak.

The main things that leak that you should worry about in GUI:
Points, Groups (Unit/Player), Special Effects

There really won't be any other leaks to worry about unless you use local variables in which case you may need to null them.

Edit:
Here's the function:
vJASS:
function GetForceOfPlayer takes player whichPlayer returns force
    local force f = CreateForce()
    call ForceAddPlayer(f, whichPlayer)
    return f
endfunction
So it creates a force (player group) and never destroys it, causing a leak.

Your only solution is to do this:
  • Set Variable Temp_PG = (Owner of (Entering unit))
  • Game - Display to Temp_PG the text: |c00cc0066Unknown V...
  • Custom script: call DestroyGroup (udg_Temp_PG)

That or just not worry about it. A few leaks here and there shouldn't really make a noticeable difference.
 
Last edited:
Level 10
Joined
Mar 25, 2008
Messages
339
Yes, Player group leaks. No, Entering unit and Triggering unit don't leak.

The main things that leak that you should worry about in GUI:
Points, Groups (Unit/Player), Special Effects

There really won't be any other leaks to worry about unless you use local variables in which case you may need to null them.

Edit:
Here's the function:
vJASS:
function GetForceOfPlayer takes player whichPlayer returns force
    local force f = CreateForce()
    call ForceAddPlayer(f, whichPlayer)
    return f
endfunction
So it creates a force (player group) and never destroys it, causing a leak.

Your only solution is to do this:
  • Set Variable Temp_PG = (Owner of (Entering unit))
  • Game - Display to Temp_PG the text: |c00cc0066Unknown V...
  • Custom script: call DestroyGroup (udg_Temp_PG)

That or just not worry about it. A few leaks here and there shouldn't really make a noticeable difference.
Would it be safe to use the same variable for dozens of triggers that might all be used within a second of eachother?

Issue is my map is a long-term game, people usually end up playing it for multiple hours, so i imagine instability is likely to happen after it has leaked over a thousand times.

I already went through and gave every point in my map it's own variable, and set them all to a specific point on map initialization to wipe out those leaks.
Lots of item drops in regions and monster respawns, I literally have like 100 defined point variables now.
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,455
Reusing a variable like Temp_PG is safe as long as you know what you're doing. You just need to be wary of Actions that can cause other triggers to run. Like how Creating a unit will cause the "A unit enters the map" Event to fire.

Here's an example of Temp_PG being misused:

Edit: Apparently this example is actually not true as Radicool mentioned but I'm positive this issue can occur. I think it depends on the Event/Action being used, there must be a timing thing to them. Some execute immediately, others are queued. So just be aware that this issue CAN happen, even if this example is an exception (I'm too lazy to show a proper example but I have dealt with this issue before and I know it can happen under the right circumstances).

Trigger A
  • Events:
  • Time - Elapsed game time is equal to 1.00 second
  • Actions:
  • Set Variable Temp_PG = (Some player)
  • Game - Display to Temp_PG the text: trigger a running
  • Unit - Create 1 Paladin for Player 1 at Center of playable map area
  • Custom script: call DestroyGroup (udg_Temp_PG)

Trigger B
  • Events:
  • Unit - A unit enters playable map area
  • Actions:
  • Set Variable Temp_PG = (Some player)
  • Game - Display to Temp_PG the text: trigger b running
  • Custom script: call DestroyGroup (udg_Temp_PG)

The problem here is that Trigger A causes Trigger B to fire before it's finished running all of it's actions. So when Trigger A "Creates the Paladin" it causes the "A unit enters map" Event of Trigger B to run, which then causes Trigger B's Actions to run. You may think that both of these triggers would run separately but in this case the Actions of Trigger B are actually inserted into Trigger A immediately after the Paladin creation Action.

The result of this would be a trigger that looks like this:
  • Events:
  • Time - Elapsed game time is equal to 1.00 second
  • Actions:
  • Set Variable Temp_PG = (Some player)
  • Game - Display to Temp_PG the text: trigger a running
  • Unit - Create 1 Paladin for Player 1 at Center of playable map area
  • Set Variable Temp_PG = (Some player)
  • Game - Display to Temp_PG the text: trigger b running
  • Custom script: call DestroyGroup (udg_Temp_PG)
  • Custom script: call DestroyGroup (udg_Temp_PG)
Two player groups are being created but only the last one is being destroyed, leaking the first.

So be on the lookout for cases where one trigger's action(s) can cause another trigger to run MID execution of the first trigger. If these two (or more) triggers share the same variables then their information could get overwritten leading to not only leaks but often times bugs/issues.

The solution here would be to use unique variables so they don't conflict with one another. I like to use unique variables for certain Events that I know are easily triggered (A unit enters map, dies, acquires/loses an item, etc) and use the "temp" variables in cases that I know for a fact are safe.
 
Last edited:
Level 16
Joined
Mar 27, 2011
Messages
1,349
...

Here's an example of Temp_PG being misused:

Trigger A
  • Events:
  • Time - Elapsed game time is equal to 1.00 second
  • Actions:
  • Set Variable Temp_PG = (Some player)
  • Game - Display to Temp_PG the text: trigger a running
  • Unit - Create 1 Paladin for Player 1 at Center of playable map area
  • Custom script: call DestroyGroup (udg_Temp_PG)

Trigger B
  • Events:
  • Unit - A unit enters playable map area
  • Actions:
  • Set Variable Temp_PG = (Some player)
  • Game - Display to Temp_PG the text: trigger b running
  • Custom script: call DestroyGroup (udg_Temp_PG)

The problem here is that Trigger A causes Trigger B to fire before it's finished running all of it's actions. So when Trigger A "Creates the Paladin" it causes the "A unit enters map" Event of Trigger B to run, which then causes Trigger B's Actions to run. You may think that both of these triggers would run separately but in this case the Actions of Trigger B are actually inserted into Trigger A immediately after the Paladin creation Action.

The result of this would be a trigger that looks like this:
  • Events:
  • Time - Elapsed game time is equal to 1.00 second
  • Actions:
  • Set Variable Temp_PG = (Some player)
  • Game - Display to Temp_PG the text: trigger a running
  • Unit - Create 1 Paladin for Player 1 at Center of playable map area
  • Set Variable Temp_PG = (Some player)
  • Game - Display to Temp_PG the text: trigger b running
  • Custom script: call DestroyGroup (udg_Temp_PG)
  • Custom script: call DestroyGroup (udg_Temp_PG)
Two player groups are being created but only the last one is being destroyed, leaking the first.

...

Yes using Temp variables in multiple triggers is fine if used properly however I don't think Uncle is correct in saying "A Unit Enters Playable Map Area" will intercept a running trigger that spawns a unit. Here is a test I made:

  • Spawn Trigger
    • Events
      • Time - Elapsed game time is 3.00 seconds
    • Conditions
    • Actions
      • Game - Display to (All players) for 30.00 seconds the text: Starting Spawn Trig...
      • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Game - Display to (All players) for 30.00 seconds the text: Finishing Spawn Tri...
  • Enter Trigger
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
    • Actions
      • Game - Display to (All players) for 30.00 seconds the text: Starting Enter Area...
      • Game - Display to (All players) for 30.00 seconds the text: Finishing Enter Are...

See attached screenshot for the result. We can clearly see that the Spawn trigger finishes it's execution before the Enter triggers fires. Are you able to edit your post as to prevent any mis-information? Sorry if I've gotten anything wrong, happy to discuss.

Would it be safe to use the same variable for dozens of triggers that might all be used within a second of eachother?

Issue is my map is a long-term game, people usually end up playing it for multiple hours, so i imagine instability is likely to happen after it has leaked over a thousand times.

I already went through and gave every point in my map it's own variable, and set them all to a specific point on map initialization to wipe out those leaks.
Lots of item drops in regions and monster respawns, I literally have like 100 defined point variables now.
I'll use the word "Temp Variable" to mean a variable which is used in multiple different unrelated triggers.

You're welcome to use the same temp variable in as many triggers as you like and they can all be running within seconds of eachother no problem.

The general rule I follow is never add any kind of wait or amount of time between setting and using/removing a Temp variable. The moment you pause a trigger, something else can begin to run. That something else might overwrite your important variables.

Here is an example of bad Temp variable usage:

  • Heal Unit
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
    • Actions
      • Set VariableSet TempUnit = (Triggering unit)
      • Set VariableSet TempPoint = (Position of TempUnit)
      • Wait 5.00 seconds
      • Unit - Set life of TempUnit to 100.00%
      • Special Effect - Create a special effect at TempPoint using Abilities\Spells\Human\Resurrect\ResurrectTarget.mdl
      • Special Effect - Destroy (Last created special effect)
      • Custom script: call Remove Location (udg_TempPoint)
Here we have a simple heal spell. A unit casts the ability and 5 seconds later, they are healed. Let's pretend the unit has to "charge" up or something. This will work fine in isolation, but the trouble is.....what happens if another ability or trigger (which uses TempUnit or TempPoint) runs while you're "charging up" (waiting 5 seconds)?

The other ability will set it's variables to do it's thing then 5 seconds later in our Heal spell, the TempUnit will refer to someone else. It no longer references the original unit whom casted the spell. So now a different unit will get healed. Depending on what and how many triggers happen to be using the same variable improperly, this could cause numerous different problems or strange outcomes.

So what is the solution?

If you ever need to use a wait, or have something remembered for later, make sure you use unique variables that can't be hijacked or used by another system, spell, or whatever.

  • Heal Unit
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
    • Actions
      • Set VariableSet HealUnit_Unit = (Triggering unit)
      • Set VariableSet HealUnit_Point = (Position of TempUnit)
      • Wait 5.00 seconds
      • Unit - Set life of HealUnit_Unit to 100.00%
      • Special Effect - Create a special effect at HealUnit_Point using Abilities\Spells\Human\Resurrect\ResurrectTarget.mdl
      • Special Effect - Destroy (Last created special effect)
      • Custom script: call Remove Location (udg_HealUnit_Point)
I use temp variables all the time. Saves me making hundreds of variables in my map. You just gotta use them properly :)

A side note, when making spells and stuff with waits, you often use concepts like MPI (Multi-Player Instancability) or MUI (Multi Unit Intancability) which allow the same trigger to run multiple times, even at the same time without overwriting eachother. This is needed if you have multiple players or units with the same heal ability. It might be easier to hold this discussion off for another day and let you focus on fixing your leaks :) There are tutorials on the Hive about MPI and MUI if you're interested.
 

Attachments

  • Trigger Execution Order.png
    Trigger Execution Order.png
    2.5 MB · Views: 17
Last edited:

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,455
Hmm... It definitely does this with other Actions/Events though, I swear I've tested it multiple times to confirm it (plus Pyrogasm and I had discussed it a few times in the past). Normally I use Kill Unit / A unit dies as my example for this issue so perhaps it all depends on the Events/Actions being used. I'll add a note to my post regarding this but the concept still holds true even if the example is an exception.

Also, just to clarify, Triggering unit is the one exception to the Wait rule as it's treated like a local variable.

I wish this game was more consistent xD
 
Last edited:
Level 16
Joined
Mar 27, 2011
Messages
1,349
Hmm... It definitely does this with other Actions/Events though, I swear I've tested it multiple times to confirm it (plus Pyrogasm and I had discussed it a few times before in the past). Normally I use Kill Unit / A unit dies as my example for this issue so perhaps it all depends on the Events/Actions being used.

Also, just to clarify, Triggering unit is the one exception to the Wait rule as it's treated like a local variable.

I wish this game was more consistent xD

Good point on the Triggering Unit. I was nearly going to mention it, but my explanation was long enough as is, haha.

Do you mind sharing some test cases with attached map to help prove the trigger execution order issue in question? If I have anything wrong, it could be causing issues in my map! Hehe. I've just posted my simple map. You could try reproducing the same problem on your client?
 

Attachments

  • Trigger Execution Test.w3m
    16.4 KB · Views: 10

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,455
I tested your map and it works the way you suggested. But I also tested this:
  • Spawn Trigger
    • Events
      • Time - Elapsed game time is 0.10 seconds
    • Conditions
    • Actions
      • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Unit - Kill (Last created unit)
      • Game - Display to (All players) for 30.00 seconds the text: Finishing Spawn Tri...
  • Death Trigger
    • Events
      • Unit - A unit Dies
    • Conditions
    • Actions
      • Game - Display to (All players) for 30.00 seconds the text: Finishing Death Tri...
This combination of Event/Action results in the issue I was talking about. So yeah, I guess it all depends on the type of Event/Action.
 

Attachments

  • ex.png
    ex.png
    639.5 KB · Views: 15
Level 16
Joined
Mar 27, 2011
Messages
1,349
I tested your map and it works the way you suggested. But I also tested this:
  • Spawn Trigger
    • Events
      • Time - Elapsed game time is 0.10 seconds
    • Conditions
    • Actions
      • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Unit - Kill (Last created unit)
      • Game - Display to (All players) for 30.00 seconds the text: Finishing Spawn Tri...
  • Death Trigger
    • Events
      • Unit - A unit Dies
    • Conditions
    • Actions
      • Game - Display to (All players) for 30.00 seconds the text: Finishing Death Tri...
This combination of Event/Action results in the issue I was talking about. So yeah, I guess it all depends on the type of Event/Action.
Very interesting....yeah, I wish things were more consistent....

I did another test with the below triggers to observe their execution. The results and test map are attached. It seems they wait for each other.

  • Death Trigger
    • Events
      • Unit - A unit Dies
    • Conditions
    • Actions
      • Game - Display to (All players) for 4.00 seconds the text: Death Started
      • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Game - Display to (All players) for 4.00 seconds the text: Death Finished
  • Register Trigger
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
    • Actions
      • Game - Display to (All players) for 4.00 seconds the text: Register Started
      • Game - Display to (All players) for 4.00 seconds the text: Register Finished
  • Testing Kill Unit
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • Unit Group - Pick every unit in (Units in (Playable map area)) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Unit-type of (Picked unit)) Equal to Footman
            • Then - Actions
              • Unit - Kill (Picked unit)
            • Else - Actions
I wonder what the difference between our two approaches is. Maybe by provoking the event (in your case, killing a unit) from within your trigger, the consequential actions are "inserted" so to speak. In my cases, the provoking event have been by other external causes (pressing the ESC key, or maybe a unit dying naturally to another unit).
 

Attachments

  • Trigger Execution Order Tes t2.png
    Trigger Execution Order Tes t2.png
    2.6 MB · Views: 12
  • Trigger Execution Test 2.w3m
    17.4 KB · Views: 13
Level 16
Joined
Mar 27, 2011
Messages
1,349
Excuse the double post. I tested my hypothesis but it seems incorrect. I provoked a unit selection via a trigger, but it did not immediately stop the trigger to begin executing another "Player selects a unit" event trigger. Results attached as usual.

  • Select Trigger
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • Game - Display to (All players) for 4.00 seconds the text: Selection Started
      • Unit Group - Pick every unit in (Units in (Playable map area)) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Unit-type of (Picked unit)) Equal to Footman
            • Then - Actions
              • Selection - Select (Picked unit) for Player 1 (Red)
            • Else - Actions
      • Game - Display to (All players) for 4.00 seconds the text: Selection Finished
  • Detect Trigger
    • Events
      • Player - Player 1 (Red) Selects a unit
    • Conditions
    • Actions
      • Game - Display to (All players) for 4.00 seconds the text: Detect Started
      • Game - Display to (All players) for 4.00 seconds the text: Detect Finished

So I have no idea why there is an inconsistency. Maybe Kill Unit is special somehow.....Confusing...
 

Attachments

  • Trigger Execution Test 3.w3m
    17.7 KB · Views: 12
  • Trigger Execution Order Test 3.jpg
    Trigger Execution Order Test 3.jpg
    244.2 KB · Views: 8
Level 10
Joined
Mar 25, 2008
Messages
339
Yes, Player group leaks. No, Entering unit and Triggering unit don't leak.

The main things that leak that you should worry about in GUI:
Points, Groups (Unit/Player), Special Effects

There really won't be any other leaks to worry about unless you use local variables in which case you may need to null them.

Edit:
Here's the function:
vJASS:
function GetForceOfPlayer takes player whichPlayer returns force
    local force f = CreateForce()
    call ForceAddPlayer(f, whichPlayer)
    return f
endfunction
So it creates a force (player group) and never destroys it, causing a leak.

Your only solution is to do this:
  • Set Variable Temp_PG = (Owner of (Entering unit))
  • Game - Display to Temp_PG the text: |c00cc0066Unknown V...
  • Custom script: call DestroyGroup (udg_Temp_PG)

That or just not worry about it. A few leaks here and there shouldn't really make a noticeable difference.
Do you have to call to destroy the group in the variable each time or will it just overwrite?
 
Level 19
Joined
Feb 27, 2019
Messages
563
My understanding is this and may be flawed so I am also posting this to get some feedback.

The local force f has to be removed after it is used to remove the leak, but it cant be nulled within the function its created because it would render it unusable. I believe its something like that and this is a side effect of using gui. So yes, you must destroy the force. Overwriting it will make it impossible to reference later. A unit on the other hand can be referenced easily, any leaks will be removed when the unit is removed from the game and overwriting a unit variable will not make you lose the ability to reference the unit. Imagine if a unit was permanent, invisible, hidden, invulnerable, non-intractable, has locust and is not set to a variable how would you remove it from the game? Thats a bit of how a force that is created and not set to a varaible and removed is.

Also am I correct in saying only local unit variable have to be nulled or... SHOULD be nulled because they will remain in memory until the unit is removed?

I feel like there may be some disconnect in my text so the main part is: Yes, you must destroy the force after you use it. Overwriting it will not remove it and make it unable to be referenced and therefor create a leak.

The way to destroy a player group is call DestroyForce()
call DestroyGroup() is used for unit groups
 
Last edited:

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,455
When dealing with locals and Jass (the default language) you need to null anything that ISN'T an integer, real, string, or boolean. Otherwise, the local will leak.

And yes, you need to destroy the group each time.

The pattern is always Set/Destroy. So you always need to Destroy an existing Point/Group/Special Effect before Setting another one. That being said, sometimes you may want these things to be permanent, in which case you should assign them a unique variable or just not worry about it. Your only concern is getting rid of trash, as in things you no longer need.

Here's an example of losing reference to something and creating a memory leak:
  • Special Effect - Create a special effect
  • Special Effect - Create a special effect
  • Special Effect - Destroy (Last Created Special Effect)
The first special effect become inaccessible the moment we create the second one. This is because (Last Created Special Effect), our only way to reference a newly created Special Effect in GUI, is a global variable and can only be set to one thing at a time. The second special effect causes (Last Created Special Effect) to stop tracking the first special effect and begin tracking the second one.

This is why the pattern I described above is important, you need to manage these things properly or else you'll lose track of them. And once they're lost there's no going back, it's not like there's an Event Response for the (Second To Last Created Special Effect).
 
Last edited:
Level 10
Joined
Mar 25, 2008
Messages
339
if call destroyforce is for player groups, and call destroygroup is for unit groups, what should I use to empty out a variable that holds a single unit, a unit variable, those leak too right? if i just keep overwriting it tons of times.

And is there any way to just clear the games temporary variable every time it does "Owner of triggering player"? or Player group triggering player
I literally have individual triggers that use that 100 times, replacing all of those with a new variable will take forever.

I was trying to find any pages that would tell me a list of "call destroy***" because i could also use one that destroys a point variable, that and unit variable.
 
Level 19
Joined
Feb 27, 2019
Messages
563
Reads these first, then return with any questions if youre still wondering about something.

Then there is Chapter 7, this thread overall is GODLIKE btw.
but just chapter 7 may not be as much of a guide as just a list of ways to remove stuff.
 
Last edited:
Status
Not open for further replies.
Top