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

[Trigger] Help understanding leaks

Status
Not open for further replies.
Level 2
Joined
Dec 10, 2017
Messages
9
Hi guys,

I've lurked here since I have been interested in world editor and since I have been playing WC3. I decided to make a campaign, based loosely on a non-descript DnD campaign.

I'm having a lot of fun throwing stuff together, as I'm pretty new to most parts of the world editor. I've created something that I'm proud of, but I'll find that after some time in game, I'll freeze up or crash. I believe this is due to memory leak.

I'm at a loss on what to do next, because I don't know how a leak really occurs, or how to fix it. I tried reviewing the 'Leaks' thread, but it's greek to me... over half the time I add one of those custom scripts it breaks the trigger or the game. :confused:

My question is, can somebody play through what small amount of content I've got, possibly look at my triggers and let me know where I'm going wrong? A specific example using one of my triggers and a way to fix it would be greatly appreciated.

I've disabled a starting cinematic so the campaign is less tedious to access, enable it if you wish to see it.


It's a big goal of mine that I complete this map, but the leaking will not allow me to do that! Thank you so much in advance.

I apologize if what I've created is a mess. Please contact me if you have any questions!
 

Attachments

  • zgame.1.05pt.w3x
    212.1 KB · Views: 38
Leaks are most dangerous, if the code producing them runs frequently.
Like on spellcast/enter rect/damage/attacked/dies if your map has many units dying/ every x seconds.
So your Take EXP trigger, which fires on every unit taking damage, you fixed the location leak but missed the group, the first group enumeration creates a group which you should destroy by setting the wantDestroyGroup Flag. You only want to destroy temp groups.

  • Take EXP
    • Events
    • Conditions
    • Actions
      • Set XP_DAMAGEDTARGETPOINT = (Position of (Triggering unit))
      • Set XP_DAMAGES = ((Integer((Damage taken))) / 10)
      • -------- Auto destroy the next group after it done it work --------
      • Custom script: set bj_wantDestroyGroup = true
      • Unit Group - Pick every unit in (Units within 1400.00 of XP_DAMAGEDTARGETPOINT matching ((((Matching unit) belongs to an ally of (Owner of (Damage source))) Equal to True) and ((((Matching unit) is A Hero) Equal to True) and ((Hero Level of (Matching unit)) Less than 10)))) and do (Actions)
        • Loop - Actions
          • Unit Group - Add (Picked unit) to HERO_PICKED
      • Set XP_DAMAGES = (XP_DAMAGES / (Integer((Real((Number of units in HERO_PICKED))))))
      • Unit Group - Pick every unit in HERO_PICKED and do (Actions)
        • Loop - Actions
          • Hero - Add XP_DAMAGES experience to (Picked unit), Show level-up graphics
      • Unit Group - Remove all units from HERO_PICKED
      • Custom script: call RemoveLocation(udg_XP_DAMAGEDTARGETPOINT)
Both below are creating a location which you should destroy after using it, except you want to reuse the location (having the same x/y).
Position of x
center of x
Some stuff to Fix, not all:
The Trigger Attacked is heavy 10 times a second you check every unit on the map
a lower frequenzy or the event enters map + add preplaced to the xp group would be better.​
Take EXP Heroes can't have Unit Levels, use Hero Level.
Take EXP leaks 1 Group Object every time a unit is damaged.
add before the first group enumeration Custom script: set bj_wantDestroyGroup = true
start enemy leaks 2 groups too, but it will fire only once.
holywater USE1 leaks a location Position of holywater.
holywater USEFRIENDLY leaks a loc, you should destroy it in holywater REMOVEBLIGHT FRIENDLY before the first wait.
reoll odds leaks 2 locations when a zombie dies.
split leaks 1 location
BarbarianPassiveSTATSACTIVE that event adding is somehow wrong and can produce some dangerous behaviour.
zombie respawnINIT is currently bullshit you should overwork it, luckly it is not enabled.
randomcrates, overwork it, leaks 80 locations.
crate LOOT: leaks upto 40 further locations. Custom script: call RemoveLocation(udg_CrateTempLoc)
bulwark LOOT: leaks locations Custom script: call RemoveLocation(udg_lootdrop1)
lakecave TEXT 1: (Triggering unit) Equal (Triggering unit) <- you can remove such conditions which are always true, but you surely wanted something different here.

randomcrates could look somehow like this:
  • randomcrates
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Center of Map is reused 40 times and destoryed only once --------
      • Set Loc2 = (Center of (Playable map area))
      • For each (Integer A) from 1 to 40, do (Actions)
        • Loop - Actions
          • Set Loc = (Random point in (Playable map area))
          • Unit - Create 1 Wooden Crates for Neutral Passive at Loc facing Loc2
          • Custom script: call RemoveLocation(udg_Loc)
      • Custom script: call RemoveLocation(udg_Loc2)
 
Level 20
Joined
Feb 23, 2014
Messages
1,264
Since Tasyen has already taken a look at your map, I will try to explain what leaks are.

1. HOW THINGS WORK - THE TALE OF JOHN, THE WARCRAFT GAME.

Let's start off by taking a very simple action as an example - one that I'm sure you're pretty familar with:

  • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Region A)) facing Default building facing degrees
I'm going to explain to you what happens when this action is run. It might not be accurate from a technical point of view, but that doesn't really matter for the purpose of this guide.

Try to imagine the game being a single person. Let's call that person John.

Now... While Jon is very powerful and can do a lot of things, it completely lacks the mind of his own. Because of that you need to give him orders if you want him to do something. Triggers are your way of communicating with John and giving him commands.

So, when you give John the command like the one above, what he will do is:
  1. First he will go and find Region A.
  2. Then he will figure out where is the center of that region.
  3. Next he will put a flag in the ground in that point.
  4. Finally he will create 1 Footman in the position of that flag for Player 1 (Red) and make it face Default building degrees.
Now, don't really worry about how points 1) and 4) work, this is not important for the purpose of this explanation. What matters are points 2 and 3 - or more specifically, the flag (i.e. the point).

The "flag" is basically a set of coordinates (X,Y) for a point - in this case the center of region A. As a piece of data, these coordinates are stored in your PC's memory thus taking up some of the memory space. While a single point uses a very, very tiny amount of memory space, you need to know that unless you tell the game to remove it from memory, it will just sit there for as long as you play.

Obviously, if your map had only this one action and only this one point stored in the memory, you wouldn't notice it at all since there would be way more than enough free memory space left for the game to run smoothly.

But since you're making a campaign, I imagine that there's way more things stored in your memory and while on their own they are pretty insignificant, in time they might pile on and take up so much memory that it will cause performance issues.

Now, points aren't the only things that can get stored in the memory - there are others like for instance special effects or unit groups. What you will notice in tutorials is that these data types are often referred to as handles. I will talk about them later. For now, let us focus on variables.

2. VARIABLES - WHY ARE THEY IMPORTANT?

Let's take the action in the example:

  • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Region A)) facing Default building facing degrees
I've already explained what happens when you run this action once, but what if you run it multiple times? Like, twice for instance.

If John (i.e. the game) was an actual person, he would realize that he has already performed that action and he would use the same flag (i.e. point equal to the center of Region A) as before. Or at the very least, he would go to Region A, find its center and realize that there's already a flag (i.e. a point) there, so he doesn't need to create a new one.

Yeah, about that... Remember how I told you that John doesn't have a mind of his own? He really doesn't. The reality is that if you told John to perform that action again, he would completely ignore the flag that's already there and perform steps 1-4 just like he did before. The end result would be that you would have 2 flags in the exact same point.

Now, if you run this action every second, it's easy to calculate that after 1 minute John would have placed 60 flags in the exact same spot (i.e. create 60 points). And of course, each one of those flags/points would take up memory space. It's not hard to see how this might add up, isn't it?

In fact, periodic triggers (i.e. ones that use "Every X seconds" event or the ones that are run over and over again otherwise) are the easiest way to clog your memory with a ton of data. The reason for this is that the game isn't smart enough to figure out that it can just reuse previously stored data (such as points), so each time a trigger of this type is run it creates new data that uses up the memory space.

This is where variables come in.

Let's go back to the example, i.e. creating a unit at a point multiple times.

We've established that John will mark the center of Region A with a new flag (i.e. create a new point) each time this action is run, because he won't reckognize the flags/points that he had placed before. You're probably thinking: "this is stupid, there should be a way to make John remember that flag/point". There is. It's called a variable.

Try to think of variables as giving things a name and ordering John to remember them. So, for instance - if you do something like this:

  • Set Point1 = (Center of (Region A)) //Point1 is a Point Variable
What will happen is that John will go and find a center of Region A and slam a flag in that point (i.e. steps 1-3 from the first example), but this time he will remember that flag as "Point1". You can then do multiple things with that point (i.e. center of Region A), without worrying about clogging up your memory as only this one point will be stored for all actions that use it.

Please note that this will only help your memory if you set this point once - you don't need to set it again before each action that uses it. In fact, you shouldn't do that as variables are only a way to reference things and if you assign a variable to another object, the previous one will not cease to exist - you will just lose a way to reference it again.

To make it simple, variables are like names with one exception - each thing has to have a unique name. You can't have two things that are assigned to the same variable at once (unless you use arrays, which is a topic for another time). So, in other words - let's say you tell John to find a center of Region A, slam a flag in there and then call that flag "Point1". You do whatever it is you want to do with that flag/point and then you order John call some other flag/point "Point1" - for example, something like this:

  • Set Point1 = (Center of (Region A))
  • Unit - Create 1 Footman for Player 1 (Red) at Point1 facing Default building facing degrees
  • Set Point1 = (Center of (Region B))
What will happen is that John will find the center of Region B, put a flag there and call it "Point1", just like you wanted. Meanwhile, that center of Region A flag that was called "Point1" just a moment ago will lose its name, but it will still continue to exist and take up memory space. So, as you see, the only thing that changes is that you John won't remember that point anymore and thus you won't be able to tell him to do anything more with it.

In other words, you will create a piece of data that takes up memory, serves no purpose and cannot be referenced (i.e. John doesn't remember it).

This is a leak.

3. WHAT LEAKS ARE AND HOW TO REMOVE THEM?

There are 2 basic cryteria that characterize leaks:
1. It takes up memory space.
2. Serves no purpose and/or can't be referenced.


Let's look at them:

1. Every handle that you create in trigger editor takes up memory space. What is a handle? I won't go into details, but what you need to know is that pretty much everything that is not a number (integer, real), text (string, custom code) or boolean (true/false) is most likely a handle and thus it might cause leaks.

The best way to reckognize things that leak is to scroll through the actions in the trigger editor - if there is an action to remove/destroy something (for example a unit, destructible, weather effect, special effect, etc.), then you can suspect that it's a handle. On top of that, there are handles that do not have proper removal functions in the trigger editor - most notable include points, unit groups and terrain deformations).

Also, for Points - while we've already established that "Center of Region" function creates a point, it needs to be said that pretty much any other function which results you can imagine as a point most likely also creates a Point handle, for example - all sorts of "Position of X", "Random Point in Region", "Target Point of Order/Ability", etc.

2. Serves no purpose and/or can't be referenced. What this means is that basically leaks are things that take up memory that you no longer use. You can not use something either out of your own choice (i.e. you don't need it for anything) or through the simple fact that you cannot reference it again, because it isn't assigned to a variable (i.e. John/the game cannot remember it, so you can't tell it to use it).

---

In simple words, you might just say that if you generate a handle in triggers and:
  1. It is not tied to a variable, it's a leak.
  2. It has a variable assigned to it, but is no longer used, it's a leak.
  3. The only case when a handle isn't a leak is when it is tied to a variable and it is still being used, either at the moment or in the future.
---

So, let's tackle a couple simple examples:

  • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Region A)) facing Default building facing degrees
a) I've said before, "Center of Region" creates a Point - Points are handles, so they can leak and thus the first condition of our leak check is true.
b) Does this serve a purpose? Outside of this one time when you create a unit in that point, it doesn't really serve any purpose, because you're not doing anything else with it. On top of that, <Center of Region A> is not tied to a variable and thus even if you wanted to reuse it, you cannot reference it. That said, the second condition of our leak check is true.

Result: This action leaks 1 point/location.

TRIGGER 1
  • Set Point1 = (Center of (Region A))
TRIGGER 2
  • Events
    • Time - Every 1.00 seconds of game time
  • Conditions
  • Actions
    • Unit - Create 1 Footman for Player 1 (Red) at Point1 facing Default building facing degrees
a) Once again, "Center of Region" in TRIGGER 1 creates a Point, points are handles. Check.
b) Can this handle be referenced? Yes, it's assigned to a variable in TRIGGER 1.
c) Does this handle serve any purpose? Yes, it's used every second to create a unit.

Result: As long as TRIGGER 2 is active (or might be activated), this set of triggers doesn't leak, because despite the fact that TRIGGER 1 created a handle, that handle can be referenced and serves a purpose.

  • Actions
    • Unit Group - Pick every unit in (Units within 512.00 of (Position of (Triggering unit))) and do (Actions)
      • Loop - Actions
        • Unit - Kill (Picked unit)
a) Handles:
- First of all, "Pick every unit in <unit group>" action does create a unit group handle. How can I tell? The simplest way is to think whether you could create a unit group variable like this (i.e. Units within 512.00 of (Position of (Triggering unit)))). If the answer is "yes" then that's exactly what the game does with this action.
- Secondly "Position of (Triggering Unit)" does create a Point handle.
b) Both handles are not tied to a variable and thus cannot be used again.

Result: Since we have 2 handles that are not referenced or used again, this trigger leaks a Point and Unit Group.

---

So, finally... We get to the point where we remove the leaks.

First of all, you might use Destroy/Remove actions for handles that have it (like destructibles, units, special effects, etc.), when they are no longer useful.

Just make sure to assign them to a variable if there's a chance that the refence will be lost (for example, if you create two special effects in succession and want to remove them after X seconds, the "last created special effect" reference will only work on the second one, while the first will need to be tied to a variable and removed that way).

As for the ones that do not have a remove/destroy action, you will need to use a custom code. Two of the most common ones are:

call DestroyGroup(udg_variable)
call RemoveLocation(udg_variable)


How these work is:
  1. Set a variable(-s) to the handle(-s) you're creating first.
  2. Perform all actions with the handles by using the created variables.
  3. Once handles are no longer used, select Custom Script action and copy the code above.
  4. Replace the "variable" part with the name of the variable assigned to a handle you want to remove.
It should look like this:

  • Actions
    • Set TempPoint = (Center of (Playable map area))
    • Unit - Create 1 Footman for Player 1 (Red) at TempPoint facing Default building facing degrees
    • Custom script: call RemoveLocation(udg_TempPoint)
Keep in mind that removing/destroying a handle does not remove/destroy a variable itself, so once you are done with destroying the handle, you can totally reuse the variable for something else. So, for instance for quick one time triggers like the one above you might want to have a "TempVariable" and use it in each one instead of creating a new variable for each trigger.

Another thing to keep in mind is that if you destroy a handle that is used later on, the trigger will stop working. This is why you should only remove handles that you are no longer using.

Also, if you remove certain objects like units or quests, they will stop being visible instantly (so you will not see a quest in the quest log or a unit will not finish it's death/decay animation), while others will be visible (mostly special effects).

And finally, when using the custom code, if you fail to save your map because of errors, check if you have properly typed in the name of the variable. If the variable name is incorrect, the code is then referring to an inexistant object, which causes the editor to fail in writing map script and thus not save the map.

---

So, to summarize.

Leaks are indeed dangerous, but the reality is that if you're making a campaign, you don't really have to bother and remove every single one of them. In truth, even if your triggers generate a couple hundred or thousand leaks, the game should work perfectly fine.

That said, if you're having problems I'd advise you to take a look at triggers that are either running on a periodic timer or events that fire very often/multiple times. These triggers are the ones that might generate a tremendous amount of leaks and thus cause your game to lag.
 
Last edited:
Level 2
Joined
Dec 10, 2017
Messages
9
Tasyen, MasterBlaster, thank you guys so much. I really appreciate the time you both have put into your responses, and I'm going to internalize all the advice.

I'm at work now, so I'll get to leak-proofing when I get home, but just looking at this post gives me hope that my game won't crash after 5 minutes in.

Seriously guys, I couldn't have made it through this mess without your responses! I'll post an update when I've got this thing polished off!

+1 to you both

Trousas
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
There is also a leak caused by a game engine bug. Local variables declared using the "local" keyword are not subject to destruction mechanics on function return and hence do not decrement handle reference counters. This counter leaks means the handle index cannot ever be recycled, an activity that usually happens between frames.

This is especially problematic for GUI users as it effects several GUI actions and functions.
 
Level 2
Joined
Dec 10, 2017
Messages
9
Hmm. I was under the impression that GUI users were forced to use global variables. I haven't declared any local ones for that reason.

I need to learn JASS... any tips/tutorials for a noob?

Thanks again for all the tips guys. My map doesn't currently crash after 5 minutes!! It's still a mess as far as naming/organization goes so I won't post an update yet but things are definitely looking better.
 

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
Hmm. I was under the impression that GUI users were forced to use global variables. I haven't declared any local ones for that reason.
It's true that you cannot declare local variables by yourself in GUI (unless you use custom script which is basically JASS).
However all GUI actions were coded by Blizzard as JASS functions. These JASS functions use locals and some of them are badly implemented and do not null the locals. As a result using these GUI actions will leak without the GUI-triggerer even knowing.

I need to learn JASS... any tips/tutorials for a noob?
Just look at the JASS tutorials. If you sort them by views, you will find some of the basic tutorials:
JASS/AI Scripts Tutorials
 
Status
Not open for further replies.
Top