• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

Things That Leak

Level 1
Joined
Sep 26, 2012
Messages
5
can i ask a Q. if you dont mind i want to know if the creation of a unit and i forgot to trigger it with an action "Remove (Bla bla) from game" will the unit cause a leak?
 
Level 14
Joined
Aug 8, 2010
Messages
1,022
Am... i wasn't able to find if this was answered before but... will there be a leak when i remove a unit with "Unit - Add generic expiration timer"? Will the unit leave a leak upon it's removal by the action? I know that unit leaks are pretty tricky..
Btw, when the generic expiration timer ends, the unit is removed from the game or simply dies as if the "Unit - Kill unit" action was used?

P.S. Sorry if this was answered before. If it has, please give me a link to the post.
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
I tested with creating 12000 units and adding expiration timer. I saw no major leaks anyway. If there was a leak it was very small. Don't worry about it.

I took 4 data points, at 4k, 6k, 8k and 10k. After pausing the test for a while and checking the memory reseved, it was exactly 94352 K at all data points.
 
Last edited:
Level 6
Joined
Jan 15, 2010
Messages
163
I am working on a map in which the main use is with groups who store units for different purposes, like dead units or teamed units etc. I wonder if leaks make it crash at all or the wc3 engine just cant handle it, but thats different question. So creating a group and destroying it makes it not leak, but:
1. What if I create a group and keep "picking every unit" from the same group with out destroying it? Is this leaking?
2. If it doesnt, then what if I add or remove units by some conditions in that group? Does that create some how like a new leaky memory or?

I am using groups, so I wont need to re-pick and re-check each unit on the map. I have seen that this way its more cpu friendly. Its a very good way on larger and even larger maps. I expect answer and hopefully this wont leak ... :(

And last question ...
Usually when Im kicked out of WC3 with errors like "out of memory" and such, it means the map leaked allot. What if the game kicks you out, with out even saying the error reason? Does that means the map leaked?
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
1. What if I create a group and keep "picking every unit" from the same group with out destroying it? Is this leaking?
2. If it doesnt, then what if I add or remove units by some conditions in that group? Does that create some how like a new leaky memory or?

Those should not leak. Note that if you destroy a group, or remove a unit, you should null the variable right away.

Dead/removed units aren't automatically removed from a group I've heard.

Usually when Im kicked out of WC3 with errors like "out of memory" and such, it means the map leaked allot. What if the game kicks you out, with out even saying the error reason? Does that means the map leaked?

It could be a trigger that causes infnite loop or some other faulty trigger, not necessarily leaks.
 
Level 6
Joined
Jan 15, 2010
Messages
163
So first I have to revive the hero and AFTER THAT to remove him from living and add him to grave. Ok tnx for this info.
Also you meant "destroy udg_var" and then with gui to make "set var = noone/0" etc. with null? Wont that completely screw the saved group if I null it after I remove a unit? -.- Im asking, cause thats the only way to actually keep a track of which hero is "dead". Regions seem to be somehow faulty for picking and destroying in groups.
 
Level 4
Joined
Nov 27, 2012
Messages
85
Do I need to destroy these triggers? Set the actions into a variable?

JASS:
function GM_GodMode_Exit takes nothing returns nothing 
    call DisableTrigger(GetTriggeringTrigger())
    call DisableTrigger(udg_t_godmode[1])
    call DisableTrigger(udg_t_godmode[2])
    call DisableTrigger(udg_t_godmode[3])
    call DisableTrigger(udg_t_godmode[4])
    call DisableTrigger(udg_t_godmode[5])
    call DisableTrigger(udg_t_godmode[6])
    call DisableTrigger(udg_t_godmode[7])
    call DisableTrigger(udg_t_godmode[8])
    call DisableTrigger(udg_t_godmode[9])
    call DisableTrigger(udg_t_godmode[10])
    call DisableTrigger(udg_t_godmode[11])
    call DisableTrigger(udg_t_godmode[12])
    call DisableTrigger(udg_t_godmode[13]) 
    call DisableTrigger(udg_t_godmode[14])

    call SetPlayerHandicap(udg_God,100.00*0.01)
    call EnableTrigger(gg_trg_God_Mode)
    call DisableTrigger(gg_trg_Restart)
    call DisableTrigger(gg_trg_MM_Restart)
    call DisplayTextToForce(bj_FORCE_ALL_PLAYERS,(udg_PlayerName[GetPlayerId(udg_God)+1]+" has disabled God Mode."))
endfunction
//===========================================================================
function InitTrig_God_Mode takes nothing returns nothing
    local integer i=1
    set gg_trg_God_Mode=CreateTrigger()
    
    loop
        exitwhen i>12
        call TriggerRegisterPlayerChatEvent(gg_trg_God_Mode,Player(i),"-godmode",true)
        set i=i+1
    endloop
    
    call TriggerAddCondition(gg_trg_God_Mode,Condition(function Trig_God_Mode_Conditions))
    call TriggerAddAction(gg_trg_God_Mode,function Trig_God_Mode_Actions)
endfunction

function God_Mode_Commands takes nothing returns nothing
    set udg_t_godmode[1]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[1],udg_God,"-control",true)
    call TriggerAddAction(udg_t_godmode[1],function GM_Control)
    
    set udg_t_godmode[2]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[2],udg_God,"-controloff",true)
    call TriggerAddAction(udg_t_godmode[2],function GM_ControlOff)
    
    set udg_t_godmode[3]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[3],udg_God,"-teamshare",false)
    call TriggerAddAction(udg_t_godmode[3],function GM_TeamShare)
    
    set udg_t_godmode[4]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[4],udg_God,"-teamremove",false)
    call TriggerAddAction(udg_t_godmode[4],function GM_TeamShareOff)
    
    set udg_t_godmode[5]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[5],udg_God,"-ally",false)
    call TriggerAddAction(udg_t_godmode[5],function GM_Ally)
    
    set udg_t_godmode[6]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[6],udg_God,"-enemy",false)
    call TriggerAddAction(udg_t_godmode[6],function GM_Enemy)
    
    set udg_t_godmode[7]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[7],udg_God,"-level",false)
    call TriggerAddAction(udg_t_godmode[7],function GM_Level)
    
    set udg_t_godmode[8]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[8],udg_God,"-vision",true)
    call TriggerAddAction(udg_t_godmode[8],function GM_Vision)     
    
    set udg_t_godmode[9]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[9],udg_God,"-visionoff",true)
    call TriggerAddAction(udg_t_godmode[9],function GM_VisionOff)
    
    set udg_t_godmode[10]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[10],udg_God,"-destroyall",true)
    call TriggerAddAction(udg_t_godmode[10],function GM_DestroyAll)   
    
    set udg_t_godmode[11]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[11],udg_God,"-reviveall",true)
    call TriggerAddAction(udg_t_godmode[11],function GM_ReviveAll)       
    
    set udg_t_godmode[12]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[12],udg_God,"-day",true)
    call TriggerAddAction(udg_t_godmode[12],function GM_Day)         
    
    set udg_t_godmode[13]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[13],udg_God,"-night",true)
    call TriggerAddAction(udg_t_godmode[13],function GM_Night)         
     
    set udg_t_godmode[14]=CreateTrigger()
    call TriggerRegisterPlayerEvent(udg_t_godmode[14],udg_God,EVENT_PLAYER_END_CINEMATIC)
    call TriggerAddAction(udg_t_godmode[14],function GM_Cooldown)  
   
    set udg_t_godmode[15]=CreateTrigger()
    call TriggerRegisterPlayerChatEvent(udg_t_godmode[15],udg_God,"-pe3komode",true)
    call TriggerAddAction(udg_t_godmode[15],function GM_GodMode_Exit)
endfunction
 
Level 11
Joined
Jun 21, 2007
Messages
505
Do I need to destroy these triggers? Set the actions into a variable?

If you destroy the trigger, it won't work. If you nullify variables, you won't be able to reference the trigger.

To remove the memory leak, first destroy the trigger and then nullify variables referencing it.

If you are going to reference to the trigger later, do not bother with memory leaks, removing it, etc.
 
Level 4
Joined
Jan 28, 2013
Messages
94
I think you'd have to be spawning at least a million units over time to notice an issue. Possibly millions, but I haven't got time to test that as my computer won't support more than 1000 created units at a time.

Most lag will be caused by corpse decay and the blood splatter of exploded units. The corpse decay was such a problem for me in WarChasers 2 that I set the decay time to 12 seconds or something.

In a lot of cases you can use dummy recycling or not even need to create extra dummies in the first place (like having one dummy caster for most tasks).
I suppose this might be a bit off-topic but, did you make WarChasers? I played that map with a friend on LAN! We didn't even finish 1/4 of the map before we were killed, heh. I did take a look at the triggers (just looking! I thought it was an official map like BlizzardTD or Death Sheep! I was just curios) and I noticed those trap triggers. I thought they looked easy to code, easier than some of the Damage Area with Dummy I have seen here and there. Knowing that it's your trigger now... should I refrain from using it in my own maps or give you credit somehow? (Tbh when I first started making my own traps my thoughts were similar but I wasn't able to make it work.)

Oh and to stay on-topic I have a query (or two):
I don't know if this has been answered before but if I have to use Pick Unit of Type or Pan Camera, what's the best way of doing it? Should I use variables and null them or is there some work around?
Preferably for a new GUI programmer like me.

Another thing, I got this trigger were you need to kill all units in a Unit Group. For the Quest to work I use a Pick every unit in Region action and add them to the Unit group. The player needs to kill all the Units in the group in order to complete the quest (which caused problems when I used Ghouls as attackers... they harvested the trees and returned to their Necropolis :vw_unimpressed:) How should I do to stop it from possibly leaking?

  • MainQuest Undead forces
    • Events
      • Unit - A unit owned by Player 3 (Teal) Dies
    • Conditions
      • (All units of CSundeadForces are dead) Equal to True
    • Actions
      • Trigger - Turn off (This trigger)
      • Game - Display to HumanArmy the text: Soldier: Return to ...
      • Quest - Mark SearchUndeadReq as Completed
      • Quest - Display to HumanArmy the Quest Requirement message: Quest requirment co...
      • Wait 2.00 seconds
      • Quest - Change the title of ScoutTown to Press forward
      • Quest - Change the description of ScoutTown to The perimeter is sa...
      • Quest - Create a quest requirement for ScoutTown with the description Scout the northern ...
      • Set NorthScoutReq = (Last created quest requirement)
      • Quest - Display to HumanArmy the Quest Update message: Update: Continue th...
      • Quest - Flash the quest dialog button
 
Level 4
Joined
Jan 28, 2013
Messages
94
Where do you set the group ?
I checked the triggers and maybe I was a bit inefficient with the group creating.

Here are the trigger(s):
  • Scout the town Create
    • Events
      • Destructible - Rock Chunks 0000 <gen> dies
    • Conditions
    • Actions
      • Quest - Create a Required quest titled Scout the town with the description We haven't heard fr..., using icon path ReplaceableTextures\PassiveButtons\PASBTNPillage.blp
      • Set ScoutTown = (Last created quest)
      • Quest - Create a quest requirement for ScoutTown with the description Go to the east to f...
      • Set ScoutTownReq = (Last created quest requirement)
      • Quest - Flash the quest dialog button
      • Quest - Display to HumanArmy the Quest Discovered message: Discovered quest : ...
      • Quest - Display to HumanArmy the Quest Requirement message: >> Quest requirment...
      • Wait 15.00 seconds
      • Quest - Display to HumanArmy the Hint message: Build an army first...
      • Destructible - Destroy Gate (Vertical) 0252 <gen>
      • Unit Group - Pick every unit in (Units in Arrival2SF <gen> owned by Player 3 (Teal)) and do (Unit Group - Add (Picked unit) to SFundeadforce)
      • Unit Group - Pick every unit in (Units in Silver Fall <gen> owned by Player 3 (Teal)) and do (Unit Group - Add (Picked unit) to SFundeadforce)
N.B this was a pre-patch map (it was version 1.00 for RoC and 1.07 for TFT) and therefore my possibilities were limited. I used the Destructible Kill event because when you get to a certain region on the map, the destructible is killed (via trigger) and this trigger runs. It's quite complicated. First you get a Dialog on Game Time and when you had made your choice an new trigger activates and it kills the Rock Chunks.

Edit.2: This trigger lies between the previously posted triggers:
  • ScoutSF arrival
    • Events
      • Unit - A unit enters Arrival2SF <gen>
    • Conditions
      • ((Owner of (Entering unit)) Equal to Player 1 (Red)) or ((Owner of (Entering unit)) Equal to Player 2 (Blue))
    • Actions
      • Trigger - Turn off (This trigger)
      • Game - Display to HumanArmy the text: Soldier: Light have...
      • Quest - Mark ScoutTownReq as Completed
      • Quest - Display to HumanArmy the Quest Requirement message: Quest requirment co...
      • Quest - Create a quest requirement for ScoutTown with the description Destroy the undeads
      • Set SFkillUndeads = (Last created quest requirement)
      • Quest - Display to HumanArmy the Quest Requirement message: New quest requirmen...
  • ScoutSF kill
    • Events
      • Unit - A unit owned by Player 3 (Teal) Dies
    • Conditions
      • (All units of SFundeadforce are dead) Equal to True
    • Actions
      • Trigger - Turn off (This trigger)
      • Game - Display to HumanArmy the text: Soldier: We won!
      • Quest - Mark SFkillUndeads as Completed
      • Quest - Display to HumanArmy the Quest Requirement message: Quest requirment co...
      • Wait 2.00 seconds
      • Quest - Change the title of ScoutTown to Search for more und...
      • Quest - Change the description of ScoutTown to Silver Fall has fal...
      • Quest - Create a quest requirement for ScoutTown with the description Continue the search
      • Set SearchUndeadReq = (Last created quest requirement)
      • Quest - Create a Optional quest titled Search for survivor... with the description There's a slight ch..., using icon path ReplaceableTextures\CommandButtons\BTNFarSight.blp
      • Set SFsurvivors = (Last created quest)
      • Quest - Create a quest requirement for SFsurvivors with the description Search the nearby a...
      • Set SurvSearchReq = (Last created quest requirement)
      • Quest - Display to HumanArmy the Quest Update message: Update: Scout the t...
      • Quest - Display to HumanArmy the Quest Discovered message: A new quest has bee...
      • Quest - Flash the quest dialog button
      • Trigger - Turn on CountrySide set up <gen>
      • Trigger - Turn on Side help a knight <gen>
  • CountrySide set up
    • Events
      • Unit - A unit enters Region 012 <gen>
    • Conditions
      • (SFkillUndeads is completed) Equal to True
    • Actions
      • Trigger - Turn off (This trigger)
      • Unit Group - Pick every unit in (Units in Mine Force <gen> owned by Player 3 (Teal)) and do (Unit Group - Add (Picked unit) to CSundeadForces)
      • Unit Group - Pick every unit in (Units in KeyEnter <gen> owned by Player 3 (Teal)) and do (Unit Group - Add (Picked unit) to CSundeadForces)
      • Trigger - Turn on MainQuest Undead forces <gen>
  • MainQuest Undead forces
    • Events
      • Unit - A unit owned by Player 3 (Teal) Dies
    • Conditions
      • (All units of CSundeadForces are dead) Equal to True
    • Actions
      • Trigger - Turn off (This trigger)
      • Game - Display to HumanArmy the text: Soldier: Return to ...
      • Quest - Mark SearchUndeadReq as Completed
      • Quest - Display to HumanArmy the Quest Requirement message: Quest requirment co...
      • Wait 2.00 seconds
      • Quest - Change the title of ScoutTown to Press forward
      • Quest - Change the description of ScoutTown to The perimeter is sa...
      • Quest - Create a quest requirement for ScoutTown with the description Scout the northern ...
      • Set NorthScoutReq = (Last created quest requirement)
      • Quest - Display to HumanArmy the Quest Update message: Update: Continue th...
      • Quest - Flash the quest dialog button
I believe I did that because the quest is a two-parter; first you kill all undead forces in the town and then you kill all undead forces on the country side. It can probably get more effective.
Edit 3: I just realized that it's in fact two different groups. One for the town "SFundeadForce" and one for the country side "CSundeadForce". Both might be leaking however.
 
Last edited:
  • Unit Group - Pick every unit in (Units in Arrival2SF <gen> owned by Player 3 (Teal)) and do (Unit Group - Add (Picked unit) to SFundeadforce)
  • Unit Group - Pick every unit in (Units in Silver Fall <gen> owned by Player 3 (Teal)) and do (Unit Group - Add (Picked unit) to SFundeadforce)
Those two groups leak. Put "set bj_wantDestroyGroup = true" after those, like this :
  • Unit Group - Pick every unit in (Units in Arrival2SF <gen> owned by Player 3 (Teal)) and do (Unit Group - Add (Picked unit) to SFundeadforce)
  • Custom script : set bj_wantDestroyGroup = true
  • Unit Group - Pick every unit in (Units in Silver Fall <gen> owned by Player 3 (Teal)) and do (Unit Group - Add (Picked unit) to SFundeadforce)
  • Custom script : set bj_wantDestroyGroup = true
 
Level 4
Joined
Jan 28, 2013
Messages
94
I got a quick question about removing leaks.

If you're going to use the same point multiple times, is it a good idea to have a RemoveLocation call? Or should I leave it as it is and not remove?

Btw the location is set as a variable at the start of the trigger.

Another question about special effects:

The question mark symbol for a quest... should I remove it after I have spoken to the NPC or keep it until the quest is finished, remove special effect and then replace it with the exclamation effect? (Then remove it after I have spoken to the NPC a second time.)
 
You can keep your location unreleased as long as you keep using it. However, you must acknowledge that locations and location variables are 2 different things. For instance :
  • Set LocVariable = Center of Region 001
  • Do some stuff with LocVariable
  • Set LocVariable = Center of Region 002
  • Do some other stuff with LocVariable
  • Custom script: call RemoveLocation(udg_LocVariable)
This leaks because there are 2 different locations involved and the 1st one is not removed.
If this is clear for you, then you don't have to remove locations between every uses.

About the special effects, the leak issue is irrelevant in your situation : you must remove the special effect when you don't want to display it anymore, period.
Special effect's leak occurs mostly for spells, when you use a model that disappears by itself, such as the "Steam Impact" model (which is just an explosion) : it displays its animation and then turns "invisible" (TITS it doesn't display anything anymore), so people may forget to destroy it.
 
Level 4
Joined
Jan 28, 2013
Messages
94
Thanks for the quick answer!
I'm not sure I understand though. So when I first store the location in the variable it starts leaking? But if I then use the same variable for another location the first location still leaks because it wasn't destroyed? Even though I put another value in the variable? As I'll use the variable for different locations, will I have to destroy the last variable location before storing another location in it? However it'll be in different trigger...

This is a part of the trigger chain (It's a revival trigger which will revive the players hero at different places in the map, if needed):
  • Revive pl1 at First Stone
    • Events
      • Time - Pl1_respawn expires
    • Conditions
    • Actions
      • Set TempLoc = (Center of ReviveStone1 <gen>)
      • Countdown Timer - Destroy Pl1_countdownWin
      • Hero - Instantly revive TempHero at TempLoc, Show revival graphics
      • Animation - Play FirstRevive's stand alternate animation
The thing is that when Hero dies a timer will count down and when it reaches zero, the player losses 1 life and the Hero respawns in the Revival zone.

Btw, should I use different variables for each player even though they'll respawn at the same location?
 
You can either :

- use 1 location variable for each player, but you have to put the following line into a "map initialization" trigger.
  • Set TempLoc = (Center of ReviveStone1 <gen>)
- or use only 1 location variable for all, but you have to remove the location every time.

The important thing is that the actions that create a location ("Center of region", "Polar position", "Target location of spell", etc...) are not fired too often compared to the action that clears the leak ("RemoveLocation"). In your trigger right there, there is 1 location created every time the trigger fires.

"Center of Region" always create a new location, regardless a location with the same coordinates has already been used before.
 
Level 4
Joined
Jan 28, 2013
Messages
94
You can either :

- use 1 location variable for each player, but you have to put the following line into a "map initialization" trigger.
  • Set TempLoc = (Center of ReviveStone1 <gen>)
- or use only 1 location variable for all, but you have to remove the location every time.

The important thing is that the actions that create a location ("Center of region", "Polar position", "Target location of spell", etc...) are not fired too often compared to the action that clears the leak ("RemoveLocation"). In your trigger right there, there is 1 location created every time the trigger fires.

"Center of Region" always create a new location, regardless a location with the same coordinates has already been used before.
Blimey! I didn't know it was such a big leak! Thanks for telling me! I will edit that ASAP.

Just to be sure I got you right:
I could either have RevivePlayer1 = Center of Region at Map Inti and then use that in the respawn trigger (Revive at RevivePlayer1). This means that I could only have 1 location or have to destroy the variable and then set a new location in the variable at some setup trigger instead of the timer trigger, right?
Or I can end the Timer trigger with DestroyLocation thereby create and destroying the Location in the same trigger, correct?

Thanks for the help!
 
Level 3
Joined
Dec 3, 2011
Messages
23
Unclear

I don't understand.

  • RedGranaries spawn
    • Events
      • Time - Spawn expires
    • Conditions
      • (Count non-structure units controlled by Player 10 (Light Blue) (Exclude incomplete units)) Less than or equal to LightblueCap
    • Actions
      • Unit Group - Pick every unit in (Units owned by Player 1 (Red) of type Infected Granary) and do (Actions)
        • Loop - Actions
          • Set Point = (Position of (Picked unit))
      • Set GroupVar = (Units owned by Player 1 (Red) of type Infected Granary)
      • Unit Group - Pick every unit in GroupVar and do (Unit - Create 1 Zombie for Player 10 (Light Blue) at Point facing Default building facing degrees)
      • Custom script: call DestroyGroup(udg_GroupVar)
      • Custom script: call RemoveLocation(udg_Point)
So yeah, with my knowledge this doesn't leak. but ehh, since this happens over and over again, is there need for variables and destroying them? Would it be leak-free without those?

I read that every time SOMETHING happens, like unit Y is moved to point XY, a memory is created and if it's not destroyed or used again it becomes a leak.
But this is used again.

And ehh, I can't figure out difference between nulling and destroying. Could someone clarify that for me?

also, if you set Group = X Unit-type ,then destroy it,

can you set Group= X unit-type again (in another trigger) and it will function well?

Thanks for any replies!
 
  • RedGranaries spawn
    • Events
      • Time - Spawn expires
    • Conditions
      • (Count non-structure units controlled by Player 10 (Light Blue) (Exclude incomplete units)) Less than or equal to LightblueCap
    • Actions
      • Custom script: set bj_wantDestroyGroup = true
      • Unit Group - Pick every unit in (Units owned by Player 1 (Red) of type Infected Granary) and do (Actions)
        • Loop - Actions
          • Set Point = (Position of (Picked unit))
          • Unit - Create 1 Zombie for Player 10 (Light Blue) at Point facing Default building facing degrees
          • Custom script: call RemoveLocation(udg_Point)
^This doesn't leak and works (which wasn't the case of your trigger).
 
Level 3
Joined
Dec 3, 2011
Messages
23
If you set Point as a unit, then to destroy it you use Location
But if the point is set on center of region, do you need Rect to destroy it?
 
If you set Point as a unit, then to destroy it you use Location
But if the point is set on center of region, do you need Rect to destroy it?
What are you talking about ?

You don't set Point as a unit. You create a Unit at a Location.

If the point is set on center of region, the variable will still be a Location, not a Rect.
 
Level 11
Joined
Jun 21, 2007
Messages
505
I don't understand.
And ehh, I can't figure out difference between nulling and destroying. Could someone clarify that for me?

The difference between nulling and destroying is as follows.
Let's say you've got a Point A with coordinates X,Y. In fact, Point A is an object which exists somewhere in computer memory, and you can refer to it. For example, if you say that variable VAR=Point A, that means VAR referring to Point A. If you say VAR2=Point A, that means VAR2 refers to Point A, too.
VAR is Equal to VAR2. Both refer to same object somewhere in memory.

Here our pathway comes at crossroads and branches off in 2 directions:
1) You can destroy an object.
2) You can nullify a variable.
Let's look closely into each branch.

1) To destroy Point A, you must type call RemoveLocation(VAR). What does this do? RemoveLocation destroys Point A if you provide a reference to it. VAR is a reference, and you give VAR to the function, and the object being referenced by VAR is removed (assuming it's a point) from computer memory forever without being able to get restored back. Then the previously occupied computer memory by the object becomes reusable, and you've cleared a memory leak. But only partially. Read on.

2) To nullify a variable referencing some object (whether it exists or was destroyed), you type set VAR=null. This makes VAR not refer to anything. If no variables refer to Point A, then Point A is inaccessible to you and lost forever, EVEN if it still exists. However, due to Point A still existing, there's a leak. So first destroy Point A, then nullify variables.
Now, I am not IT expert, computer engineer or anything yet, but Warcraft III uses reference-counting for garbage collection. It works this way - Warcraft III counts how many references a place in memory has, and if 0, then it garbage-collects that place. I'm not sure what's actually happening here. The glitch is that Warcraft III does not automatically decrease reference count for local variables. That is, if you do not manually type set temp_var=null then Warcraft III will not do that for you.

Just don't bother with nullifying variables if you do not use local variables.
I believe if you use global variables, then you will inevitably change their values to some other objects or just leave them alone. Now, if you set a global variable, it will decrease reference count to old value of the variable, and increase reference count of the new value, fixing the leak. So to say, you will always have as many variables not nulled, as you have global variables in total, and as many leaks. That's too small of a reason to worry about nullifying global variables. However, Unit Group loops do spawn a Group object (if it is not a variable but a function like All Units in some Region) which must be destroyed and all variables referencing it nullified by typing set bj_wantDestroyGroup=true
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
Just don't bother with nullifying variables if you do not use local variables.
I believe if you use global variables, then you will inevitably change their values to some other objects or just leave them alone. Now, if you set a global variable, it will decrease reference count to old value of the variable, and increase reference count of the new value, fixing the leak. So to say, you will always have as many variables not nulled, as you have global variables in total, and as many leaks. That's too small of a reason to worry about nullifying global variables.

Globals leak the pointer, better to null them also. The leak is really minor.
 
Globals leak the pointer, better to null them also. The leak is really minor.
Just wondering; how can a global pointer ever be leaked? A pointer is just a memory adress. As soon as the pointer/memory adress gets overwritten (which is the nature of globals, unless you never use this global again), the old pointer is gone.
It's just as kkots said: you can only have as many global leaks as you have globals. And technically, even then it isn't really a leak by definition, as the variable can still always be accessed.

The only way a global variable can leak is when you are able to dynamically allocate new global variable adresses on runtime. And JASS can not do that, as arrays have a fixed size.


The only "globals" that can leak are hashtable entries, but even then, hashtables have a size limit due to integers having a max size.
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
Maker just meant that global pointers keep agents from freeing their id and in case you do not reuse it.

Hashtables are flushable (+every cell remains selectable). Unless you lose the hashtable object, they do not leak either in theory. But practically you might refrain from doing so. Also integer size squared is enough to kill everything.
 
Maker just meant that global pointers keep agents from freeing their id and in case you do not reuse it.
Hmm, I'm not sure wether I got you right.

When I assign a global variable to a unit and the unit is removed, but I never null/overwrite the global variable, is the GetHandleId() of that unit never put back to the handle id stack?
If so, what would WC3 do in case the unit handle stack is depleted?
I'm curious; i never heard of that before.

Hashtables are flushable (+every cell remains selectable). Unless you lose the hashtable object, they do not leak either in theory. But practically you might refrain from doing so. Also integer size squared is enough to kill everything.
Yes. I was more talking about the integer square thing.

Practically, this means that unexperienced coders could have an incrementing variable for the parent/child key (for example a kill count) and write stuff into every key (like unit that killed the unit), but never cleaning it up again afterwards.
When using an array for that, the thread will just crash as soon as 4096 kills are reached. The hashtable will just continue accumulating data over the runtime.
Technically yes, it's not a leak by definition. But it does dynamically allocate more and more memory.
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
When I assign a global variable to a unit and the unit is removed, but I never null/overwrite the global variable, is the GetHandleId() of that unit never put back to the handle id stack?

Yes, same as with local variables. Only that local variables can really get lost.

If so, what would WC3 do in case the unit handle stack is depleted?
I'm curious; i never heard of that before.

The id pool can deplete? Well, of course, you can crack the integer limit. I think it throws an insufficient memory fatal even before.

Array bounds are 0-8190/8191 (last index is bugged).

edit: But it's difficult to achieve a lot of unfreed ids with globals alone, maybe with hashtable entries.
 
The id pool can deplete? Well, of course, you can crack the integer limit. I think it throws an insufficient memory fatal even before.
Didn't ID pools have unique ranges depending on the type of handle? I recall them all being in the same range, like floating texts being 0-99 (okay, bad example, as floating texts do not extend handle, but you get the idea).

Array bounds are 0-8190/8191 (last index is bugged).
Right, I confused that with something else.

edit: But it's difficult to achieve a lot of unfreed ids with globals alone, maybe with hashtable entries.
Interesting. However, I can't come up with a practical example where this could ever be a problem except with incrementing keys, so basicly, we come back to the point where we can say that nulling globals is never neccessary (except for hashtables).
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
Didn't ID pools have unique ranges depending on the type of handle? I recall them all being in the same range, like floating texts being 0-99 (okay, bad example, as floating texts do not extend handle, but you get the idea).

Yes but only agents are reference-counted and they lie in the same range.

Clearing/Not clearing a global could be a problem in case you work with client-async agents because it's another possibility to desync the id pool. Though this scenario is dangerous in the first place.
 
Yes but only agents are reference-counted and they lie in the same range.

Clearing/Not clearing a global could be a problem in case you work with client-async agents because it's another possibility to desync the id pool. Though this scenario is dangerous in the first place.
There are only very few client-async agents in the game. I think images and floating texts and that's about it.
The only case where not nulling a global containing such an agent could lead to desyncs is when you have a hashtable storing data to the GetHandleId(texttag) and then using that data with net traffic operations. But even then, as long as you use GetHandleId(texttag) again (which is basicly the only way to get that specific ID) to Load the values from the hashtable, it should still be in sync, as the data loaded is the same despite the key being different!

Only if you Flush the table at some other point of the script globally, it might cause problems.
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
Images, Texttags are not agents. They have their custom id ranges, therefore do not pose a problem. My point was that you can create units and co. in an async manner IF you avoid all false transmissions. For example it should be possible to create a special effect for one player and fill the missing id with a location for the other player. Then when releasing the effect, you should do so too for the location.
 
Images, Texttags are not agents. They have their custom id ranges, therefore do not pose a problem. My point was that you can create units and co. in an async manner IF you avoid all false transmissions. For example it should be possible to create a special effect for one player and fill the missing id with a location for the other player. Then when releasing the effect, you should do so too for the location.
This would only work if we assume that the handle stack simply takes the next handle ID from the stack. But that isn't the case, for some reason.
It seems that all types of handles have a certain range of IDs inside the handle ID stack.

Creating handles locally is never a good idea. But I don't see how a global variable pointing towards a handle should ever cause a local handle?
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
Agents are in the same range. This can easily be tested.

References decide when an object's id is released after it has been destroyed. To keep the id pool in sync (else the next independent snippet of code could make it go haywire), variables should not be added to/removed from the object asyncly unless you have a substitute.

Ex:
Code:
if (GetLocalPlayer() == Player(0)) then
    call DestroyEffect(e)

    set e = null
else
    call RemoveLocation(l)
endif

Assuming effect and location had the same id, nulling e would free the id for player 0 but the l reference remains, therefore its still blocked for the other players.

The reference-counting of objects is most probably for security reasons. References allow you to access an object. If the ids were released before all pointers are gone, you could falsely get the next object with the same id, which is practically abused for getting rid of unit shadows for example (which works since images are not agents, not ref-counted).

There are types that have really no need to be in the upper id pool though like effects, multiboards, multiboarditems, quests. They do not cause net-traffic nor do they have read functions. Or functions like IsQuestCompleted are actually superfluous. You set this attribute yourself, so you should avoid determining a sync decision from it. For all I care, such functions could be removed but this would break backwards compatibility. Having those types in the upper id pool makes them async-unfriendly although it would make sense to have them for specific clients. If you could create them locally, this would also benefit performance.
 
Ex:
Code:
if (GetLocalPlayer() == Player(0)) then
    call DestroyEffect(e)

    set e = null
else
    call RemoveLocation(l)
endif
That agents should not be destroyed or created inside local code is nothing new and only logical. I don't really see a problem with that and your example doesn't prove anything here because the desync is not caused by a global variable leaking a reference, but actual differences in IDs due to local creation of agents.

I'll shamelessly edit your example because I'm a lazy bitch:
JASS:
local effect e = AddSpecialEffect(bla)
call DestroyEffect(e)
if (GetLocalPlayer() == Player(0)) then
    set e = null
endif
This would be a much better example of what you are trying to prove.

If what you said about reference count is correct, then this should cause a desync as soon as a new effect is created. Why?
Because the reference count of the special effect is zero for Player(0) but 1 for Player(1), which should prevent the ID from being put back to the stack.

However, I did a quick test and this did not desync for me.
The reason for that is maybe that:
1) effects leak memory even when destroyed properly, which means that there HAS to be a reference to that effect somewhere in the WC3 code, no matter what the reference count is. Which basicly renders the whole argumentation of IDs not getting freed when there is a reference somewhere pointless
2) References to removed agents do not get freed in the game memory (hence they leak memory), but still reduce the reference count
3) As soon as an ID is allocated again, the pointer to that removed agent becomes a nullpointer

I'm not sure about those, it's just my assumptions.
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
I don't really see a problem with that and your example doesn't prove anything here because the desync is not caused by a global variable leaking a reference, but actual differences in IDs due to local creation of agents.

The same id via before async-creation was assumed. Your example is more evident.

If what you said about reference count is correct, then this should cause a desync as soon as a new effect is created.

*As soon as the id becomes relevant to net-traffic.

Because the reference count of the special effect is zero for Player(0) but 1 for Player(1), which should prevent the ID from being put back to the stack.

That is the case.

However, I did a quick test and this did not desync for me.

Neither does it for me, although the unit I create after comes with a local-dependent id.

1) effects leak memory even when destroyed properly, which means that there HAS to be a reference to that effect somewhere in the WC3 code, no matter what the reference count is. Which basicly renders the whole argumentation of IDs not getting freed when there is a reference somewhere pointless

While I cannot make any statements about wc3s internals, we can at least say it influences the jass control.

2) References to removed agents do not get freed in the game memory (hence they leak memory), but still reduce the reference count
3) As soon as an ID is allocated again, the pointer to that removed agent becomes a nullpointer

I can verify that the ids are blocked. Since it did not cause a desync (game kill) in my current test, which surprises me too at the moment, I wonder if objects/agents have another id they use for net-traffic that is not exposed to jass (which of course may alter the life cycle).
 
Top