• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[General] [Force leak] why these code seems simple but makes Force leak?

Level 3
Joined
Jan 7, 2025
Messages
18
I hope this message finds you guys well. I am learning and creating my Warcraft 3 map, which frequently crashes both at the game startup and around the 10-minute mark during gameplay. To address this, I have used a leak detection tool (Memory Leaks Checker v1.6) and systematically checked my triggers. After learning about unit group leaks and point leaks, I encountered a difficult-to-understand issue:

Before the game begins, I use triggers to ally players and save their alliances, so that I can apply technology bonuses to the players all at once later. However, after investigation, the leak detection tool informed me that this code causes a Force leak of 20 times per second. This appears to be a one-time code, and I am unsure how to resolve the issue. The code is as follows:

I have performed two checks on it:

  1. I compared the leak information when it is activated and deactivated. When it is activated, the Force leak significantly increases.
  2. I tried adding a [call BJDebugMsg("game the team triggered!")] function to confirm whether it is being repeatedly executed, and also attempted to disable the original trigger and activate a new trigger that uses this part of the code (to avoid it being called elsewhere). The result was that it did not repeatedly execute and was not called by any other triggers (at least in the two tests I conducted).
Could someone kindly advise me on what steps I can take to resolve this issue?

  • game the team
    • Events
      • Time - Elapsed game time is 0.20 seconds
    • Conditions
    • Actions
      • Trigger - Turn off (This trigger)
      • Player - Change color of Player 6 (Orange) to Gray, Changing color of existing units
      • Player Group - Add Player 1 (Red) to goodguy
      • Player Group - Add Player 2 (Blue) to goodguy
      • Player Group - Add Player 3 (Teal) to goodguy
      • Player Group - Add Player 9 (Gray) to goodguy
      • Player Group - Add Player 6 (Orange) to goodguy
      • Player Group - Add Player 5 (Yellow) to badguy
      • Player Group - Add Player 4 (Purple) to badguy
      • Player Group - Add Player 7 (Green) to badguy
      • Player Group - Add Player 8 (Pink) to badguy
      • Player Group - Add Player 10 (Light Blue) to badguy
      • Player Group - Add Player 11 (Dark Green) to badguy
      • Player - Change color of Player 11 (Dark Green) to Yellow, Changing color of existing units
      • Player - Change color of Player 10 (Light Blue) to Yellow, Changing color of existing units
      • Player Group - Pick every player in goodguy and do (Actions)
        • Loop - Actions
          • Player - Make (Picked player) treat Player 1 (Red) as an Ally with shared vision
          • Player - Make (Picked player) treat Player 2 (Blue) as an Ally with shared vision
          • Player - Make (Picked player) treat Player 3 (Teal) as an Ally with shared vision
          • Player - Make (Picked player) treat Player 9 (Gray) as an Ally with shared vision
          • Player - Make (Picked player) treat Player 6 (Orange) as an Ally with shared vision
          • Player - Make (Picked player) treat Player 4 (Purple) as an Enemy
          • Player - Make (Picked player) treat Player 5 (Yellow) as an Enemy
          • Player - Make (Picked player) treat Player 7 (Green) as an Enemy
          • Player - Make (Picked player) treat Player 8 (Pink) as an Enemy
          • Player - Make (Picked player) treat Player 10 (Light Blue) as an Enemy
          • Player - Make (Picked player) treat Player 11 (Dark Green) as an Enemy
      • Player Group - Pick every player in badguy and do (Actions)
        • Loop - Actions
          • Player - Make (Picked player) treat Player 1 (Red) as an Enemy
          • Player - Make (Picked player) treat Player 2 (Blue) as an Enemy
          • Player - Make (Picked player) treat Player 3 (Teal) as an Enemy
          • Player - Make (Picked player) treat Player 9 (Gray) as an Enemy
          • Player - Make (Picked player) treat Player 6 (Orange) as an Enemy
          • Player - Make (Picked player) treat Player 4 (Purple) as an Ally with shared vision
          • Player - Make (Picked player) treat Player 5 (Yellow) as an Ally with shared vision
          • Player - Make (Picked player) treat Player 7 (Green) as an Ally with shared vision
          • Player - Make (Picked player) treat Player 8 (Pink) as an Ally with shared vision
          • Player - Make (Picked player) treat Player 10 (Light Blue) as an Ally with shared vision
          • Player - Make (Picked player) treat Player 11 (Dark Green) as an Ally with shared vision

here is the information when these code activated (before) and deactivated (after)

1737055314825.png
1737055490739.png
 

Attachments

  • 1737055972345.png
    1737055972345.png
    95.1 KB · Views: 6
Last edited:

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
None of the above code runs more than once in the context of how we're seeing it. Trig_game_the_team_test_Func015A and Trig_game_the_team_test_Func016A don't even run at all here.

Can you post all of your related GUI triggers instead?
If you write something in Jass then share it as Jass, otherwise, just share the GUI triggers.
 
Last edited:
Level 3
Joined
Jan 7, 2025
Messages
18
None of the above code runs more than once in the context of how we're seeing it. Trig_game_the_team_test_Func015A and Trig_game_the_team_test_Func016A don't even run at all here.

Can you post all of your related GUI triggers instead?
If you write something in Jass then share it as Jass, otherwise, just share the GUI triggers.
😃 While debugging this trigger, I may have caused some confusion with the JASS code. I have edited the trigger in my post to use GUI (thank you for generously letting me know how to upload it in GUI form!).
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
Okay, so everything seems fine here, the issue is likely coming from other triggers that rely on these Player Groups. Look for triggers that use Periodic Interval / Timers that repeatedly run, or an Event that occurs often (Unit dies, Unit is attacked, Unit takes damage, etc). A common place for a Player Group leak is in Text Messages -> Display text to (Player Group(Owner of (Triggering unit)).

But I'll make some general suggestions. These may not apply to your map depending on how your map works:

1) Use Player Slots that make logical sense:
Players 1 to 5 = Good Guys. Players 6 to 11 = Bad Guys.
You can change their Player Colors later if that's all you're interested in. This allows you to take better advantage of For Loops and other order related logic.

2) Manage your Player Groups in a way that gives you full control over who is playing the game:
  • game the team
    • Events
      • Time - Elapsed game time is 0.20 seconds
    • Conditions
    • Actions
      • Player Group - Pick every player in (All players) and do (Actions)
        • Loop - Actions
          • -------- --------
          • -------- GOOD GUYS --------
          • If all Conditions are true then do (Actions)
            • If - Conditions
              • Or - Multiple conditions
                • (Player number of (Picked player)) Equal to 1
                • (Player number of (Picked player)) Equal to 2
                • (Player number of (Picked player)) Equal to 3
                • (Player number of (Picked player)) Equal to 9
                • (Player number of (Picked player)) Equal to 6
            • Then - Actions
              • Player Group - Add (Picked player) to goodguy_all
              • Set Variable Players_Team_All[(Player number of (Picked player))] = goodguy_all
              • If all Conditions are true then do (Actions)
                • If - Conditions
                  • ((Picked player) slot status) Equal to Is playing
                • Then - Actions
                  • Player Group - Add (Picked player) to goodguy_playing
                  • Set Variable Players_Team_Playing[(Player number of (Picked player))] = goodguy_playing
                • Else - Actions
            • Else - Actions
          • -------- --------
          • -------- BAD GUYS --------
          • If all Conditions are true then do (Actions)
            • If - Conditions
              • Or - Multiple conditions
                • (Player number of (Picked player)) Equal to 5
                • (Player number of (Picked player)) Equal to 4
                • (Player number of (Picked player)) Equal to 7
                • (Player number of (Picked player)) Equal to 8
                • (Player number of (Picked player)) Equal to 10
                • (Player number of (Picked player)) Equal to 11
            • Then - Actions
              • Player Group - Add (Picked player) to badguy
              • Set Variable Players_Team_All[(Player number of (Picked player))] = badguy_all
              • If all Conditions are true then do (Actions)
                • If - Conditions
                  • ((Picked player) slot status) Equal to Is playing
                • Then - Actions
                  • Player Group - Add (Picked player) to badguy_playing
                  • Set Variable Players_Team_Playing[(Player number of (Picked player))] = badguy_playing
                • Else - Actions
            • Else - Actions
all = Players in the team that are playing, or have left the game, or were unused.
playing = Players in the team that are actively playing the game.
  • Player Leaves
    • Events
      • Player - Player 1 (Red) leaves the game
      • Player - Player 2 (Blue) leaves the game
      • Player - Player 3 (Teal) leaves the game
      • Player - Player 4 (Purple) leaves the game
      • Player - Player 5 (Yellow) leaves the game
      • Player - Player 6 (Orange) leaves the game
      • Player - Player 7 (Green) leaves the game
      • Player - Player 8 (Pink) leaves the game
      • Player - Player 9 (Gray) leaves the game
      • Player - Player 10 (Light Blue) leaves the game
      • Player - Player 11 (Dark Green) leaves the game
    • Conditions
    • Actions
      • Player Group - Remove (Triggering player) from Players_Team_Playing[(Player number of (Triggering player))].
^ When someone leaves the game you can remove them from the playing Player Group. This could be useful if you wanted to exclude leavers from certain triggers. Additionally, unused player slots will not be in the playing group from the very beginning, which is a great way to have your map dynamically handle different player counts (1v1, 2v2, etc).

3) You can get rid of this, it does nothing good in your original trigger:
  • Trigger - Turn off (This trigger)
 
Last edited:
Level 3
Joined
Jan 7, 2025
Messages
18
Thank you very much
Okay, so everything seems fine here, the issue is likely coming from other triggers that rely on these Player Groups. Look for triggers that use Periodic Interval / Timers that repeatedly run, or an Event that occurs often (Unit dies, Unit is attacked, Unit takes damage, etc). A common place for a Player Group leak is in Text Messages -> Display text to (Player Group(Owner of (Triggering unit)).

But I'll make some general suggestions. These may not apply to your map depending on how your map works:

1) Use Player Slots that make logical sense:
Players 1 to 5 = Good Guys. Players 6 to 11 = Bad Guys.
You can change their Player Colors later if that's all you're interested in. This allows you to take better advantage of For Loops and other order related logic.

2) Manage your Player Groups in a way that gives you full control over who is playing the game:
  • game the team
    • Events
      • Time - Elapsed game time is 0.20 seconds
    • Conditions
    • Actions
      • Player Group - Pick every player in (All players) and do (Actions)
        • Loop - Actions
          • -------- --------
          • -------- GOOD GUYS --------
          • If all Conditions are true then do (Actions)
            • If - Conditions
              • Or - Multiple conditions
                • (Player number of (Picked player)) Equal to 1
                • (Player number of (Picked player)) Equal to 2
                • (Player number of (Picked player)) Equal to 3
                • (Player number of (Picked player)) Equal to 9
                • (Player number of (Picked player)) Equal to 6
            • Then - Actions
              • Player Group - Add (Picked player) to goodguy_all
              • Set Variable Players_Team_All[(Player number of (Picked player))] = goodguy_all
              • If all Conditions are true then do (Actions)
                • If - Conditions
                  • ((Picked player) slot status) Equal to Is playing
                • Then - Actions
                  • Player Group - Add (Picked player) to goodguy_playing
                  • Set Variable Players_Team_Playing[(Player number of (Picked player))] = goodguy_playing
                • Else - Actions
            • Else - Actions
          • -------- --------
          • -------- BAD GUYS --------
          • If all Conditions are true then do (Actions)
            • If - Conditions
              • Or - Multiple conditions
                • (Player number of (Picked player)) Equal to 5
                • (Player number of (Picked player)) Equal to 4
                • (Player number of (Picked player)) Equal to 7
                • (Player number of (Picked player)) Equal to 8
                • (Player number of (Picked player)) Equal to 10
                • (Player number of (Picked player)) Equal to 11
            • Then - Actions
              • Player Group - Add (Picked player) to badguy
              • Set Variable Players_Team_All[(Player number of (Picked player))] = badguy_all
              • If all Conditions are true then do (Actions)
                • If - Conditions
                  • ((Picked player) slot status) Equal to Is playing
                • Then - Actions
                  • Player Group - Add (Picked player) to badguy_playing
                  • Set Variable Players_Team_Playing[(Player number of (Picked player))] = badguy_playing
                • Else - Actions
            • Else - Actions
all = Players in the team that are playing, or have left the game, or were unused.
playing = Players in the team that are actively playing the game.
  • Player Leaves
    • Events
      • Player - Player 1 (Red) leaves the game
      • Player - Player 2 (Blue) leaves the game
      • Player - Player 3 (Teal) leaves the game
      • Player - Player 4 (Purple) leaves the game
      • Player - Player 5 (Yellow) leaves the game
      • Player - Player 6 (Orange) leaves the game
      • Player - Player 7 (Green) leaves the game
      • Player - Player 8 (Pink) leaves the game
      • Player - Player 9 (Gray) leaves the game
      • Player - Player 10 (Light Blue) leaves the game
      • Player - Player 11 (Dark Green) leaves the game
    • Conditions
    • Actions
      • Player Group - Remove (Triggering player) from Players_Team_Playing[(Player number of (Triggering player))].
^ When someone leaves the game you can remove them from the playing Player Group. This could be useful if you wanted to exclude leavers from certain triggers. Additionally, unused player slots will not be in the playing group from the very beginning, which is a great way to have your map dynamically handle different player counts (1v1, 2v2, etc).

3) You can get rid of this, it does nothing good in your original trigger:
  • Trigger - Turn off (This trigger)
Thank you very much !! :peasant-grin::peasant-grin::peasant-grin:
 
Level 3
Joined
Jan 7, 2025
Messages
18
Okay, so everything seems fine here, the issue is likely coming from other triggers that rely on these Player Groups. Look for triggers that use Periodic Interval / Timers that repeatedly run, or an Event that occurs often (Unit dies, Unit is attacked, Unit takes damage, etc). A common place for a Player Group leak is in Text Messages -> Display text to (Player Group(Owner of (Triggering unit)).

But I'll make some general suggestions. These may not apply to your map depending on how your map works:

1) Use Player Slots that make logical sense:
Players 1 to 5 = Good Guys. Players 6 to 11 = Bad Guys.
You can change their Player Colors later if that's all you're interested in. This allows you to take better advantage of For Loops and other order related logic.

2) Manage your Player Groups in a way that gives you full control over who is playing the game:
  • game the team
    • Events
      • Time - Elapsed game time is 0.20 seconds
    • Conditions
    • Actions
      • Player Group - Pick every player in (All players) and do (Actions)
        • Loop - Actions
          • -------- --------
          • -------- GOOD GUYS --------
          • If all Conditions are true then do (Actions)
            • If - Conditions
              • Or - Multiple conditions
                • (Player number of (Picked player)) Equal to 1
                • (Player number of (Picked player)) Equal to 2
                • (Player number of (Picked player)) Equal to 3
                • (Player number of (Picked player)) Equal to 9
                • (Player number of (Picked player)) Equal to 6
            • Then - Actions
              • Player Group - Add (Picked player) to goodguy_all
              • Set Variable Players_Team_All[(Player number of (Picked player))] = goodguy_all
              • If all Conditions are true then do (Actions)
                • If - Conditions
                  • ((Picked player) slot status) Equal to Is playing
                • Then - Actions
                  • Player Group - Add (Picked player) to goodguy_playing
                  • Set Variable Players_Team_Playing[(Player number of (Picked player))] = goodguy_playing
                • Else - Actions
            • Else - Actions
          • -------- --------
          • -------- BAD GUYS --------
          • If all Conditions are true then do (Actions)
            • If - Conditions
              • Or - Multiple conditions
                • (Player number of (Picked player)) Equal to 5
                • (Player number of (Picked player)) Equal to 4
                • (Player number of (Picked player)) Equal to 7
                • (Player number of (Picked player)) Equal to 8
                • (Player number of (Picked player)) Equal to 10
                • (Player number of (Picked player)) Equal to 11
            • Then - Actions
              • Player Group - Add (Picked player) to badguy
              • Set Variable Players_Team_All[(Player number of (Picked player))] = badguy_all
              • If all Conditions are true then do (Actions)
                • If - Conditions
                  • ((Picked player) slot status) Equal to Is playing
                • Then - Actions
                  • Player Group - Add (Picked player) to badguy_playing
                  • Set Variable Players_Team_Playing[(Player number of (Picked player))] = badguy_playing
                • Else - Actions
            • Else - Actions
all = Players in the team that are playing, or have left the game, or were unused.
playing = Players in the team that are actively playing the game.
  • Player Leaves
    • Events
      • Player - Player 1 (Red) leaves the game
      • Player - Player 2 (Blue) leaves the game
      • Player - Player 3 (Teal) leaves the game
      • Player - Player 4 (Purple) leaves the game
      • Player - Player 5 (Yellow) leaves the game
      • Player - Player 6 (Orange) leaves the game
      • Player - Player 7 (Green) leaves the game
      • Player - Player 8 (Pink) leaves the game
      • Player - Player 9 (Gray) leaves the game
      • Player - Player 10 (Light Blue) leaves the game
      • Player - Player 11 (Dark Green) leaves the game
    • Conditions
    • Actions
      • Player Group - Remove (Triggering player) from Players_Team_Playing[(Player number of (Triggering player))].
^ When someone leaves the game you can remove them from the playing Player Group. This could be useful if you wanted to exclude leavers from certain triggers. Additionally, unused player slots will not be in the playing group from the very beginning, which is a great way to have your map dynamically handle different player counts (1v1, 2v2, etc).

3) You can get rid of this, it does nothing good in your original trigger:
  • Trigger - Turn off (This trigger)
I hope this message finds you well. After conducting a thorough investigation, I have successfully replicated the scenario, It appears by the combination of the triggers listed and the Damage Engine.

Here’s what I did:
  1. Verified that the Damage Engine is newest version.
  2. Apart from triggers and map descriptions, I made no other changes to the map settings.
As a result, I managed to recreate the problem scenario, which appears to cause about 150 Force leaks per second!

Here is the map with leak, either turning off the Damage Engine or adjusting "game the team" stops the leaks; otherwise, it results in a significant number of Force leaks.

Thank you for your attention :wsmile:
 

Attachments

  • Clip.png
    Clip.png
    639.5 KB · Views: 9
  • leak test.w3m
    96.4 KB · Views: 4
Level 3
Joined
Jan 7, 2025
Messages
18
I am trying to control alliances using triggers. How can I properly create an alliance without causing any Force leaks?
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,286
Be aware that due to the local declared local agent variable reference count leak on return bug that forces can leak handle IDs even if their force object is destroyed due to many functions used by GUI running into the bug.

Would also like to point out the review for the "leak checker"...
Due to many reports of this system reporting incorrect leaks, I've decided to move this submission to Substandard. I recommend people learn how to find leaks themselves using many of the useful tutorials in the website (Memory Leaks).

How can I properly create an alliance without causing any Force leaks?
By not using GUI and instead using JASS or Lua directly. That way you can avoid creating forces, recycle forces and destroy forces if required to avoid all such leaks. If using GUI you need to lookup every function it calls to make sure it is not running into the bug and creating unexpected handle leaks.

For every force object you create, you need to consider its life cycle. For how long must you keep it alive? When will you destroy it? In a lot of cases you can pretty much destroy them the instant you are done using them.
 
Level 3
Joined
Jan 7, 2025
Messages
18
Be aware that due to the local declared local agent variable reference count leak on return bug that forces can leak handle IDs even if their force object is destroyed due to many functions used by GUI running into the bug.

Would also like to point out the review for the "leak checker"...



By not using GUI and instead using JASS or Lua directly. That way you can avoid creating forces, recycle forces and destroy forces if required to avoid all such leaks. If using GUI you need to lookup every function it calls to make sure it is not running into the bug and creating unexpected handle leaks.

For every force object you create, you need to consider its life cycle. For how long must you keep it alive? When will you destroy it? In a lot of cases you can pretty much destroy them the instant you are done using them.
Thank you! Regarding the triggers I mentioned earlier, what kind of optimizations are needed? They seem quite simple, and I can't pinpoint the reason why they are causing leaks. The map in #6 ( triggers in #1 or #4 combina with Damage Engine) cause large amount Force leak
 
Last edited:
Level 3
Joined
Jan 7, 2025
Messages
18
Are the triggers in 1 and 4 run more than once per map session? If not then you are probably micro optimising as even a few hundred leaks from them are likely insignificant.
The aforementioned alliance trigger was set to run only once without repetition, resulting in 150 Force leaks per second. They are persistent, will cause tens of thousands of force leaks.

I have uploaded it (map in #6). The attached map contains only the aforementioned alliance trigger, Damage Engine, and Memory Leaks Checker, with no other triggers or modified parameters.
 
Last edited:

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
The aforementioned alliance trigger was set to run only once without repetition, resulting in 150 Force leaks per second. They are persistent, will cause tens of thousands of force leaks (on my map).

I have uploaded it (map in #6). The attached map contains only the aforementioned alliance trigger, Damage Engine, and Memory Leaks Checker, with no other triggers or modified parameters.
If something only runs once, and it's not repeating the leaky functions, then it can't create more and more leaks per second.

You're clearly doing something weird, you should test this in a completely separate map.

There's also a good chance that the crashes are completely unrelated to memory leaks and you're being mislead.

Worst case, your patch is bugged, but I don't think that's ever the case.
 
Level 3
Joined
Jan 7, 2025
Messages
18
If something only runs once, and it's not repeating the leaky functions, then it can't create more and more leaks per second.

You're clearly doing something weird, you should test this in a completely separate map.

There's also a good chance that the crashes are completely unrelated to memory leaks and you're being mislead.

Worst case, your patch is bugged, but I don't think that's ever the case.
Yes, I have replicated them on a completely independent map:It does not contain any unrelated triggers or map content (only alliance trigger, Damage Engine, and Memory Leaks Checker), please take a look at it.

Sorry for the misunderstanding; I wasn't referring to this only happening on a specific map.
 

Attachments

  • leak test.w3m
    96.4 KB · Views: 4

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
The idea of testing in a separate map is to NOT include things like Damage Engine. But it was good that you did include it in this case, because that's the source of the Force leaks.

Without Damage Engine: 2 Force leaks. Maybe just a GUI issue like Dr suggested, but regardless it's not going to cause crashes.

With Damage Engine: Ever increasing Force leaks that could definitely cause a crash.

ASSUMING this leak checker is legit, @Bribe maybe there's an issue with the latest version of Damage Engine?
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,286
The aforementioned alliance trigger was set to run only once without repetition, resulting in 150 Force leaks per second. They are persistent, will cause tens of thousands of force leaks.
How can a trigger that runs only once generate per second force leaks? Per second force leaks can only be created by code that is running at least once per second.

I would also like to remind people of the review on the leak checker...
Due to many reports of this system reporting incorrect leaks, I've decided to move this submission to Substandard. I recommend people learn how to find leaks themselves using many of the useful tutorials in the website (Memory Leaks).
That review was added after the last update of the system. As such I would take anything reported by that "leak checker" with a pinch of salt.
 
Top