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!
If you use any variable in jass or GUI that is not an integer, real, boolean or string, it is a "pointer".
A "pointer" points to a place in your RAM memory. Some pointers point to locations, which is a piece of RAM memory that contains a real X coordinate and a real Y coordinate.
Those X and Y coordinates thus take up memory in your RAM. If you want to remove it (because you no longer need it and want to use the memory for more useful purposes), you must manually do so. This is done through:
call RemoveLocation(pointer)
So our "location" (in jass) or "Point" (in GUI) is a pointer to the X and Y coordinate in your RAM memory. RemoveLocation will follow this pointer and then remove the X and Y from your memory.
Whenever we call RemoveLocation on such a pointer, the memory is cleared of the ADRESS where the pointer points to. The actual pointer is STILL there! (ofcourse, because if it WOULD be removed, you would never be able to use a "Point" variable in GUI again once you removed it!)
Whenever you use set aPointvariable = anotherpoint, you are actually changing the pointer so it now points to a different place in memory that contains a different X and Y coordinate.
This means we have following situation in your memory:
Adress --- value
1 --- X coordinate of location 1
----- Y coordinate of location 1
3 --- X coordinate of location 2
----- Y coordinate of location 2
...
112 - pointer to 1 --> this points to location 1. We'll call it "APointVariable"
If we use set APointVariable = location2, you ONLY change the contents of the pointer, this is the variable at adress 112. So you do not change the contents of adress 1 or 3!
==> Adress --- Value
...
112 - pointer to 3 --> this now points to location 2.
Note that location 1 and location 2 are STILL the same locations, they have NOT changed. But if you now use "APointVariable", it will obviously work with the X and Y coordinates of location 2.
However, if we'll never use location 2 again, we're wasting the memory used on adress 3. If we forget about this, we now have a memory leak. To avoid memory leaks, we can use functions such as call RemoveLocation(APointVariable)
What happens now is: your computer will follow the pointer in APointVariable, which still points to adress 3. Then, it removes the contents of adress 3 so it can be used again.
Our new situation is: Adress --- value
1 --- X coordinate of location 1
----- Y coordinate of location 1
3 --- NULL
...
112 - pointer to 3 --> this points to location 2. Location 2 no longer exists, so if you do something with "APointVariable" at THIS moment, nothing will happen, simply because there IS no location variable where the pointer points to.
This is why we need to remove locations with "RemoveLocation()".
Ofcourse, it's a very simplistic view, in a real situation memory management is much more complex. One of the things that makes it more complex is the so-called "Reference counter".
Whenever any new variable is created, a unique id is assigned to it. Your computer needs to be able to distinguish 2 variables of the same type, so we do this by adding an id to each variable. This id essentially is an "integer", unique for each variable.
Each new variable ALSO has an additional variable called the "Reference counter". The idea behind the reference counter is that it counts the amount of pointers that point to this variable. If it reaches 0, it means nothing points to the variable anymore. When this happens, our variable is no longer used and in theory it could automatically be removed from the memory (so you no longer have to use "RemoveLocation" to remove a location from the memory).
Blizzard forgot to implement this, so you still must use "RemoveLocation" to clear your memory.
The reference counter is also used for something else. If it reaches zero, not only can the memory be removed, but this "unique" id can also be reused now by another variable! After all, the id is no longer in use now so another variable can use it! This is to avoid running out of unique id's if you keep creating new variables. I don't know what happens if you DO run out of unique id's, but it can't be good. Probably a crash.
Blizzard DID implement this, so if the ref counter reaches zero, the id is free to be used by another variable now.
Yet again, blizzard forgot to do something. You see, when you use "RemoveLocation" on a variable, you are removing the location from the memory. The unique ID and reference counter are still there though, and the reference counter is NOT zero yet, because the actual pointer STILL points to the location. It would be common sense if blizzard would automatically decrease the reference counter when the location is removed so the unique id is free to be used again, but they forgot to do so. So now aside from the fact we need to remove variables manually, we ALSO need to decrease the reference counter manually. We do so simply by letting our pointer point to "nothing". This is what happens when you say:
JASS:
set APointVariable = null
The actual pointer (APointVariable itself) is still there, it still uses memory. However, it now points to "nothing". Lucky for us, the actual pointer does get automatically removed when it's no longer needed". This is in the case of local variables.
JASS:
call RemoveLocation(aLocationPointer) // remove the memory used by the location where aLocationPointer points to
set aLocationPointer = null // we make aLocationPointer point to "nothing" now, so our reference counter is decreased and the unique id is freed.
One important note: when a global pointer variable is used, the actual pointer variable will always be in the memory. After all, you will ALWAYS be able to use your global variable. The "actual location" behind this pointer might be removed from the memory, but since the pointer is global, the pointer itself is never removed. Thus, this means that there's no need to decrease the reference counter of global variables! Why? Simply because the pointer will always stay in memory.
So, recapitulation:
JASS:
//for local variables:
local location localpointer = GetSpellTargetLoc() // create the location
call RemoveLocation(localpointer) // destroy the location
set localpointer = null // set pointer to our destroyed location = nothing
// ==> we need to set localpointer = null so the reference counter is decreased. localpointer is a local variable which is removed when it's no longer needed.
// for global variables:
call RemoveLocation(udg_globalpointer)
// ==> we don't need to set udg_globalpointer = null, because it's a global variable, which is NEVER removed. If it isn't removed, why bother?
p.s. wow, this post became longer than I thought it was going to be...
Ugh, you're teaching him the wrong way to do it. Since it's a global variable, you don't need to null. Only in the case of local variables, such as this:
JASS:
local location l = GetUnitLoc(GetTriggerUnit()
//do something
call RemoveLocation(l)
set l = null
since destroying the GetPlayersAll() results in many bugs, is it ok if I do this?
JASS:
function example takes nothing returns nothing
local group local_group = GetPlayersAll()
//do stuff..
call DestroyForce(local_group)
set local_group = null
endfunction
No.
First of all that is a force.
There is no point in destroying all players force as that will result in a game crash. If you are using a local variable for it, all you have to do is null it.
No.
First of all that is a force.
There is no point in destroying all players force as that will result in a game crash. If you are using a local variable for it, all you have to do is null it.
No game crash I think. You just will never be able to use the "All players" force again.
The reason is:
GetPlayersAll() returns a global variable. It doesn't create a new force each time it's called, it's only 1 initialized variable. By setting another variable to GetPlayersAll(), you merely set pointers to the same force object. So since both pointers point to the same force variable, RemovingForce on any of those pointers will still remove the same actual force variable.
where can i find array leak tutorial....no i dont need it just tell me does leak this when i use point array.... how can i remove that point by using custom script
can you tell me how to make spell MUI or MPI..... i mean i dont know what is mui or mpi can you tell me what do i need to do to make somespell at least MPI
MPI is Multi Player Instancable, meaning that all 16 players (since 16 is the maximum in wc3) can cast the spell at the same time without bugs, with a single unit per player.
MUI is Multi Unit Instancable, meaning that an infinite amount of units can cast it at the same time. Most spells are capped to 8191 because of array, but I've never seen a map where a spell is casted so often.
I find this Kinda hard to understand, can you write WHAT a leak is? and why to prevent them? i don't either quite get all those leak codes... i think i'll just copy them into my maps... but i think what i mentioned before, is vital information.
(Ability being cast) Equal to (==) |cffadff2fTree Blink|r (Demon Tree)
Actions
Set points = (Position of (Casting unit))
Destructible - Create a Icecrown Tree Wall at points facing (Random angle) with scale 1.00 and variation (Random integer number between 1 and 10)
Destructible - Create a Northrend Tree Wall at (points offset by ((Random real number between -200.00 and 200.00), (Random real number between -200.00 and 200.00))) facing (Random angle) with scale 1.00 and variation (Random integer number between 1 and 10)
Destructible - Create a Felwood Tree Wall at (points offset by ((Random real number between -200.00 and 200.00), (Random real number between -200.00 and 200.00))) facing (Random angle) with scale 1.00 and variation (Random integer number between 1 and 5)
Point - Remove points
i just figured the spell would be more powerful if i added more trees so but i'm not sure how to use offset, is this ok?
(Ability being cast) Equal to (==) |cffadff2fTree Blink|r (Demon Tree)
Actions
Set points = (Position of (Casting unit))
Set Point2 =(points offset by ((Random real number between -200.00 and 200.00)
Destructible - Create a Icecrown Tree Wall at points facing (Random angle) with scale 1.00 and variation (Random integer number between 1 and 10)
Destructible - Create a Northrend Tree Wall at Point2, (Random real number between -200.00 and 200.00))) facing (Random angle) with scale 1.00 and variation (Random integer number between 1 and 10)
Custom script: call RemoveLocation(udg_Point2)
Set Point2= (points offset by ((Random real number between -200.00 and 200.00)
Destructible - Create a Felwood Tree Wall at Point2, (Random real number between -200.00 and 200.00))) facing (Random angle) with scale 1.00 and variation (Random integer number between 1 and 5)
good to know...uhm by the way you left out the y reals...
ok so how's this for a JASS rewrite of the spell?
JASS:
scope teleportTree initializer I
private function C takes nothing returns boolean
return GetSpellAbilityId() == 'A00K'
endfunction
private function A takes nothing returns nothing
local unit u = GetTriggerUnit()
local real ux = GetUnitX(u)
local real uy = GetUnitY(u)
local real rx
local real ry
call CreateDestructable('ITtw',ux,uy,GetRandomReal(0.00, 359.00),1,GetRandomInt(1, 10) )
set rx = ux + GetRandomReal(-200.00,200.00)
set ry = uy + GetRandomReal(-200.00,200.00)
call CreateDestructable('NTtw',rx,ry,GetRandomReal(0.00, 359.00),1,GetRandomInt(1, 10) )
set rx = ux + GetRandomReal(-200.00,200.00)
set ry = uy + GetRandomReal(-200.00,200.00)
call CreateDestructable('CTtr',rx,ry,GetRandomReal(0.00, 359.00),1,GetRandomInt(1, 10) )
set u = null
endfunction
//===========================================================================
private function I takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerAddCondition( t, Condition( function C ) )
call TriggerAddAction( t, function A )
endfunction
endscope
edit: well obviously i broke something because now it's not working...
maybe it has something to do with the fact that it's a scope and private functions and that handles the gui trigger add event function differently?
public function InitTrig takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerAddCondition( t, Condition( function C ) )
call TriggerAddAction( t, function A )
endfunction
private function I takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function C))
call TriggerAddAction(t, function A)
endfunction
If you are adding event to trigger dynamically, trigger must be a global variable.
Also, there is no such need in some local variables (rx, ry - due to inline, u - due to only two uses of function value)
JASS:
private function A takes nothing returns nothing
local real ux = GetUnitX(GetTriggerUnit())
local real uy = GetUnitY(GetTriggerUnit())
call CreateDestructable('ITtw', ux, uy, GetRandomReal(0., 359.), 1, GetRandomInt(1, 10))
call CreateDestructable('NTtw', ux + GetRandomReal(-200., 200.), uy + GetRandomReal(-200., 200.), GetRandomReal(0., 359.), 1, GetRandomInt(1, 10))
call CreateDestructable('CTtr', ux + GetRandomReal(-200., 200.), uy + GetRandomReal(-200., 200.), GetRandomReal(0., 359.), 1, GetRandomInt(1, 10))
endfunction
private function I takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function C))
call TriggerAddAction(t, function A)
endfunction
If you are adding event to trigger dynamically, trigger must be a global variable.
Also, there is no such need in some local variables (rx, ry - due to inline, u - due to only two uses of function value)
JASS:
private function A takes nothing returns nothing
local real ux = GetUnitX(GetTriggerUnit())
local real uy = GetUnitY(GetTriggerUnit())
call CreateDestructable('ITtw', ux, uy, GetRandomReal(0., 359.), 1, GetRandomInt(1, 10))
call CreateDestructable('NTtw', ux + GetRandomReal(-200., 200.), uy + GetRandomReal(-200., 200.), GetRandomReal(0., 359.), 1, GetRandomInt(1, 10))
call CreateDestructable('CTtr', ux + GetRandomReal(-200., 200.), uy + GetRandomReal(-200., 200.), GetRandomReal(0., 359.), 1, GetRandomInt(1, 10))
endfunction
There is no need for triggers to be global. True that WE automatically generates a global trigger for every trigger you create in the format gg_trg_*trigger name*.
But the idea of scopes is that the scope can be implemented in any map by just copy pasteing the code and changing only the scope name, so it is just simpler to use a local variable.
The code can indeed be reduced to this:
JASS:
scope teleportTree
private function C takes nothing returns boolean
return GetSpellAbilityId() == 'A00K'
endfunction
private function A takes nothing returns nothing
local real ux = GetUnitX(GetTriggerUnit())
local real uy = GetUnitY(GetTriggerUnit())
call CreateDestructable('ITtw', ux, uy, GetRandomReal(0., 359.), 1, GetRandomInt(1, 10))
call CreateDestructable('NTtw', ux + GetRandomReal(-200., 200.), uy + GetRandomReal(-200., 200.), GetRandomReal(0., 359.), 1, GetRandomInt(1, 10))
call CreateDestructable('CTtr', ux + GetRandomReal(-200., 200.), uy + GetRandomReal(-200., 200.), GetRandomReal(0., 359.), 1, GetRandomInt(1, 10))
endfunction
//===========================================================================
public function InitTrig takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerAddCondition( t, Condition( function C ) )
call TriggerAddAction( t, function A )
endfunction
endscope
well obviously i broke something because now it's not working...
maybe it has something to do with the fact that it's a scope and private functions and that handles the gui trigger add event function differently
So I had a thought that he uses GUI action "Trigger - Add Event To <trigger>". That's why I noticed two variants.
But... without event registration the trigger would not fire.
And, by the way, "public function InitTrig" would work as initializer only if trigger has the same name, as the scope within it. But it is a matter of taste...
yeah maybe we're getting a little off-topic with this vjass with globals and stuff but i don't really understand the concept...and thankyou for clarifying how to redo the tree placement.
so that's leakless right? wow, sweet.
now what's all this i hear about global triggers and such?
here was the trigger i was using to add the event...i'll cut out the irrelevant information...
JASS:
scope pickmain initializer I
private function treedemonskills takes nothing returns nothing
call TriggerRegisterUnitEvent( gg_trg_teleportTree, GetTrainedUnit(), EVENT_UNIT_SPELL_EFFECT )
endfunction
private function A takes nothing returns nothing
if GetUnitTypeId(GetTrainedUnit()) == 'unec' then
call treedemonskills()
endif
endfunction
//===========================================================================
private function I takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_TRAIN_FINISH )
call TriggerAddAction( t, function A )
endfunction
endscope
i think it's faster to add the specific unit does the ability rather than any unit for checking conditions takes up valuable processing i'm right about that, right? that's why enfos uses this with their system...but i didn't learn how to add them to vjass trigger because they only used gui triggers so now that i'm learning vjass i can't add the event the same way anymore... so it's at least slightly on topic because i want to use vjass to make cleaning leaks easier but it's hard using it when i run into a snag such as this.
and while we're talking about off topic stuff what's up with the reals? sometimes they can be 0.0 sometimes 0.00 sometimes 0 like the 1 just before the random integer right? and then so finally here i see 0. which i never saw before. are the various functions picky about how you type your reals?
No, reals are reals. You can type them however you want. Some people add .0 because they want to remind themselves its a real or something. I dont see the need, personally.
Not always. If you store it in a variable it is unimportant.
But if you make a calculation like:
JASS:
myReal = 3.7-(5/3)
This would be 2.7, because 5/3 is an integer devision with the result 1.
JASS:
myReal = 3.7-(5./3.)
This would be ~2,033
But that's the only example with problems because of the dot, Mooglefrooglian is right.
However, I'm always typing 1. and so on to remember that it's a real
so is this kindof what you mean by making the trigger a global variable?
i'm guessing no, but you didn't explain that one yet. i don't think i know what you were talking about. please show me. the only thing i can think of would be to take the functions and actions and leave their names alone after converting to custom text, and then replace the functions' bodies with the code we just came up with.
so like this:
JASS:
function Trig_teleportDemontree_Conditions takes nothing returns boolean
return GetSpellAbilityId() == 'A00K'
endfunction
function Trig_teleportDemontree_Actions takes nothing returns nothing
local real ux = GetUnitX(GetSpellAbilityUnit())
local real uy = GetUnitY(GetSpellAbilityUnit())
call CreateDestructable('ITtw',ux,uy,GetRandomReal(0.00, 359.00),1,GetRandomInt(1, 10) )
call CreateDestructable('NTtw',ux + GetRandomReal(-200.00,200.00),uy + GetRandomReal(-200.00,200.00),GetRandomReal(0.00, 359.00),1,GetRandomInt(1, 10) )
call CreateDestructable('CTtr',ux + GetRandomReal(-200.00,200.00),uy + GetRandomReal(-200.00,200.00),GetRandomReal(0.00, 359.00),1,GetRandomInt(1, 5) )
endfunction
//===========================================================================
function InitTrig_teleportDemontree takes nothing returns nothing
set gg_trg_teleportDemontree = CreateTrigger( )
call TriggerAddCondition( gg_trg_teleportDemontree, Condition( function Trig_teleportDemontree_Conditions ) )
call TriggerAddAction( gg_trg_teleportDemontree, function Trig_teleportDemontree_Actions )
endfunction
I can't really figure out if you can add the event to the private functions or not. If not, then the above is what I want? But I thought you were talking about something else perhaps that I don't really understand. If that's the case, please show me.
Of course.
GUI action "convert player to player group" is "GetForceOfPlayer" function in Jass:
JASS:
function GetForceOfPlayer takes player whichPlayer returns force
local force f = CreateForce()
call ForceAddPlayer(f, whichPlayer)
return f
endfunction
Yepp, it creates force, but doesn't destroy it => it leaks, obviously.
Yeah, every non-constant player group leaks if not destroyed.
For destroying it in GUI, just do something like:
Custom script: call DestroyForce(udg_<variable name here>)
Without <>. Btw, player group is called "force" in JASS (in case you didn't figure it out). Do that when you're finished with the player group (but don't do that if you intend to use that same player group continuously ).
I know this post was pretty useless, but I felt like saying something .
If the function below gets called alot, it creates lag and even freezes the game:
JASS:
function Protector_TakeDmg takes unit att, unit def, real dmg returns nothing
// Add mana based on damage taken
call SetUnitState(def, UNIT_STATE_MANA, (GetUnitState(def, UNIT_STATE_MANA) + (dmg * 0.5)))
// If Retribution is activated and attacker is enemy
if (GetUnitAbilityLevel(def, 'prb3') > 0 and IsUnitEnemy(att, GetOwningPlayer(def))) then
// Reflect 75% of the damage back to attacker
call UnitDamageTarget(def, att, (dmg * 0.75), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
// Play GFX on target
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Human\\Slow\\SlowCaster.mdl", att, "origin"))
endif
endfunction
But as soon, as I remove the special effect, everything is fine... Any hints for me?
Probably! I'm pretty sure, the function call doesn't leak.. but if 50 or more special effects are created at once, performance still drops drasticallay
I wouldn't say so, but apparently it does (sometimes). What we do is we usually remove units with the combination of expiration timers and set unit explode on death.
You want to remove a unit, then you add to him an expiration timer of, lets say, 1.5 seconds, then you make him explode on death (all via triggers), and preferably hide him if you want him removed immediately. That way the unit won't show, but will still be on the map for 1.5 seconds, and when it dies it "explodes", which is not really explosion, but a death without a corpse (which is what you want). So no problems.
That first line you showed doesn't leak. Something else must be the problem.
I wouldn't say so, but apparently it does (sometimes). What we do is we usually remove units with the combination of expiration timers and set unit explode on death.
You want to remove a unit, then you add to him an expiration timer of, lets say, 1.5 seconds, then you make him explode on death (all via triggers), and preferably hide him if you want him removed immediately. That way the unit won't show, but will still be on the map for 1.5 seconds, and when it dies it "explodes", which is not really explosion, but a death without a corpse (which is what you want). So no problems.
Is it enought to hide and kill the unit?
But the problem is that unit will die.....and i have some triggers that are doing something when unit dies but i think i can solve it with condition - killing unit is alive equal to true
Is it enought to hide and kill the unit?
But the problem is that unit will die.....and i have some triggers that are doing something when unit dies but i think i can solve it with condition - killing unit is alive equal to true
Well, it will leave the corpse that way, which might be affected by something else. If you really don't want to use an expiration timer, you can kill the unit, but I suggest you set him to explode on death (there's an action for that), bc that way it won't leave a corpse.
Well, it will leave the corpse that way, which might be affected by something else. If you really don't want to use an expiration timer, you can kill the unit, but I suggest you set him to explode on death (there's an action for that), bc that way it won't leave a corpse.
Ok i will do it and i wanna know what is the problem with corpse?
Is there any way to remove leak with costum script code and are you shure it leaks?
In game Vampirism Fire 5.06 there is about 700-1500 units removed every game but i didnt see it lagging accept it lags a bit more after 50 minutes but i think that happens in every game.....
Ok i will do it and i wanna know what is the problem with corpse?
Is there any way to remove leak with costum script code and are you shure it leaks?
In game Vampirism Fire 5.06 there is about 700-1500 units removed every game but i didnt see it lagging accept it lags a bit more after 50 minutes but i think that happens in every game.....
Well, nothing is wrong with the corpse, I'm just saying there might be problems with raise dead and abilities like that. I'm sure you wouldn't want an invisible corpse to be raised. If you don't have anything that might cause problems like that, than you can do whatever you want.
Remove with custom script? Well yeah, but it is same as Unit - Remove unit in GUI. I'm just saying there are some reports on leaks related to that.
Maybe in Vampirism Fire units are removed in a different way? I don't know, there are tons of ways to remove a unit and non of them differs much visually.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.