Uncle
Warcraft Moderator
- Joined
- Aug 10, 2018
- Messages
- 7,648
Does this still work with the current patch? I've been suggesting using this to new users and I'm worried that it might cause them problems.
Lua garbage collector synchronization should no longer work but also not be required.Does this still work with the current patch? I've been suggesting using this to new users and I'm worried that it might cause them problems.
Yes. It runs itself if the map is in Lua mode.Do I have to run this code in anyway? And thanks for the quick response.
Access to the functions that were used to track garbage collector stats is no longer possible since 1.32 since all the garbage collector stuff is now handled internally and is not a concern for the user.i'm using this System since you released it and it worked fine until reforged. The Debug Print pressing ESC does not work anymore since then (Debug is set to True).
Yes it still works but due to changes with Lua in Warcraft III I can no longer measure that it is working. Also the Network garbage collector is no longer needed.Does this still work? The test map doesnt even load with the "network test" trigger . it runs after i delete it , but i see no signs of it working. Also i created lots and lots of leaky triggers in my map and tested it with and without the garbage collector and my memory usage stayed the same .
Yes it does work with that situation since it uses Lua's garbage collector to detect when all references to one of the objects are lost.Does this work with the same variable changed multiple times in the same trigger(example below)?
This still calls DestroyGroup and such functions internally. That is the only way to clean up the leak. What this system does is automate the calls rather than requiring they be called explicitly.I wanted to change to this, and lua in general, because my map started giving desync problems after i added simple custom scripts as " call DestroyGroup (udg_group) "
It should work for all code. Just if one is using custom script one can explicitly remove the leaks easily anyway making this not so useful.If you don't use GUI triggers at all, do Locations, Groups, and Forces still leak? I've been wondering since this is specifically designed for eliminating GUI created triggers but I'm leaning towards almost 100% custom code.
Warning: This caused Desyncs for me. Last I tried it was about 2 months ago.View attachment 354703
Hey, can you help me?, i have lots of leaks in my map and i would like to use your sistem, but i cant put lua script lenguage , just JASS , why??
function ReplaceNatives()
local oldRemoveUnit=RemoveUnit
RemoveUnit=function(u)
for i=1,4 do
if (u==Select[i]) then
BlzFrameSetValue(BlzGetFrameByName("HPBarEx", i), 0)
BlzFrameSetText(BlzGetFrameByName("HPBarExText", i), " ")
Select[i]=nil
end
end
oldRemoveUnit(u)
u=nil
end
local oldRemoveDestructable=RemoveDestructable
RemoveDestructable=function(d)
oldRemoveDestructable(d)
d=nil
end
local oldDestroyTrigger=DestroyTrigger
DestroyTrigger=function(tr)
DisableTrigger(tr)
oldDestroyTrigger(tr)
tr=nil
end
local oldDestroyTimer=DestroyTimer
DestroyTimer=function(t)
PauseTimer(t)
oldDestroyTimer(t)
t=nil
end
end
Local variables do not have to be niled in Lua.I mean if we add that nilling, GC wouldn't need to worry about references to non existing wc3 agents, or did I fail to understand the problem properly ?
I still do not understand. Niling a local variable will not change all other references to the unit such as from global variables. One would have to nil all variables that referenced the unit, in which case I do not see how this is a check for a unit being alive anymore...But it will fix the fact that, after removing the unit, the reference still remains for a short amount of time (a 0.01s timer solves this for sure, but there can be circumstances where you wouldn't want to add a delay before checking if a unit is alive.
It potentially improves performance in the form of reducing leaks if there were any before. It does not improve performance directly. That said it was designed to be trivial to use since it works entirely transparently as long as Lua is the virtual machine.My question for you, would a GUI trigger with leaks automatically removed by this script see performance improvements over the same GUI trigger with manual leak removal? For ease of access the automatic leak removal sounds fantastic, but is it worthwhile to adopt this system to retrofit existing triggers?
collectgarbage
is nil
now. If it is true, then the first post should be edited.But why it is no longer required for online games? Can people use this anti-leak system without stopping GC and running it every few minutes?Lua garbage collector synchronization should no longer work but also not be required.
WARNING: Garbage collection is run locally for each client. Local object destruction will cause clients to go out of sync. The snippet below can be used to synchronize garbage collection at the cost of disabling incremental collection and potentially causing stutter with complex maps
Yes the approach does not work.I have heard thatcollectgarbage
isnil
now. If it is true, then the first post should be edited.
Blizzard should have added something like it.But why it is no longer required for online games?
I do not know how effective it is or if it remains in sync anymore.Can people use this anti-leak system without stopping GC and running it every few minutes?
And here I thought the wounds could be cut no deeper.From what I could gather automatic garbage collection of objects such as locations and groups was intended to have become a native feature. However due to issues with Warcraft III Reforged's development that never happened.
do
local loc = Location(0, 0)
local native_GetZ = GetLocationZ
local native_Move = MoveLocation
local function GetZ(x, y)
native_Move(loc)
return native_GetZ(loc)
end
function Location(x, y) return {x = x, y = y} end
function RemoveLocation() end
function MoveLocation(l, x, y)
l.x = x
l.y = y
end
function GetLocationX(l) return l.x end
function GetLocationY(l) return l.y end
function GetLocationZ(l) return GetZ(l.x, l.y) end
end
That is a pretty clever/good idea. There might be performance implications due to the required implicit packing/unpacking but I am sure a lot of GUI users would be happy to take it just so their map does not leak.Well, it is possible to rewrite all natives that returns and accepts locations/groups/forces to use Lua tables instead.
[Lua] - Leak Preventer is a better version of this. It replaces some natives with ones that don't generate new handles, and recycles others rather than destroying them. It also only picks up on Group (Unit group), Force (player group), Location (Position) and Rect (Region) leaks, so will still fail if you are leaking other kinds of things.So - unless this is something so basic it's enough for even a layman to just study the code a bit to figure it out - is there possibly a "slimmed down" version for this that just detects the leaks in a map?
The threads I found on the Hive so far that talked about detection methods were very old ones that said "the only tool made for this was broken, just use your brain"...
(just to make sure, I did download and use that tool, but while it looks nice and spits out some data about your triggers, the Leaks detected are always "0" even though I created some leaky triggers on purpose for testing...)
Do we really have no more support tool just for leak check in 2022?
And if not, what would it take to make one...?
Technically it is worse. This, when it used to work, had no limits at all and just worked in most cases.[Lua] - Leak Preventer is a better version of this.
Such a thing should be possible in theory. However it might only work reliably in single player.So - unless this is something so basic it's enough for even a layman to just study the code a bit to figure it out - is there possibly a "slimmed down" version for this that just detects the leaks in a map?
Well, it is possible to rewrite all natives that returns and accepts locations/groups/forces to use Lua tables instead.
For example:
Of course, any native which accepts/returns location must be rewritten.Lua:do local loc = Location(0, 0) local native_GetZ = GetLocationZ local native_Move = MoveLocation local function GetZ(x, y) native_Move(loc) return native_GetZ(loc) end function Location(x, y) return {x = x, y = y} end function RemoveLocation() end function MoveLocation(l, x, y) l.x = x l.y = y end function GetLocationX(l) return l.x end function GetLocationY(l) return l.y end function GetLocationZ(l) return GetZ(l.x, l.y) end end
I am not sure if multiplayer sync is an issue anymore. Just it may not work reliably as the GC sweep speed is fixed. If you create locations or groups faster than the GC can sweep the Lua heap then a GC cycle will never complete and so no garbage collection will occur. A very leaky map would still be very leaky because the garbage collector cannot clean it up fast enough.If only the multiplayer sync issue wasn't causing problems with this resource, as hooking onto __gc is a pretty novel way to do this.
do
GC={}
local debug = true
local objRef = setmetatable({}, {__mode = "k"})
local function registerType(name, remove, constructors)
local stats = debug and {
collected = 0,
manually = 0,
leaking = 0,
badfree = 0,
badconstruct = 0
}
local removefunc = _ENV[remove]
local collecting
_ENV[remove] = function(obj)
local table = objRef[obj]
if table then
removefunc(obj)
setmetatable(table, nil)
objRef[obj] = nil
if debug then
stats.manually = stats.manually + 1
stats.leaking = stats.leaking - 1
end
elseif debug then
if obj then
print("Tried to free an already freed object: " .. obj)
else
print("Tried to free nil of type: " .. name)
end
stats.badfree = stats.badfree + 1
end
end
local collectmetatable = {__gc = function(obj)
removefunc(obj[1])
if debug then
if not collecting then
collecting=true
print("Automatically garbage collecting up to " .. stats.leaking.. " leaks")
end
stats.collected = stats.collected + 1
stats.leaking = stats.leaking - 1
end
end}
for _,nativeName in ipairs(constructors) do
local nativeConstructor = _ENV[nativeName]
_ENV[nativeName] = function(...)
local obj = nativeConstructor(...)
if obj then
objRef[obj] = {obj}
setmetatable(objRef[obj], collectmetatable)
if debug then
if collecting then
collecting=false
print("Garbage Collection finished with " .. stats.leaking .. " leaks remaining.")
end
stats.leaking = stats.leaking + 1
end
elseif debug then
print(nativeName.." returned nil: ")
stats.badconstruct = stats.badconstruct + 1
end
return obj
end
end
GC[name.."Stats"] = debug and function(full)
print("Statistics for " .. name .. ":")
print("Garbage collected: " .. stats.collected)
print("Manually destroyed: " .. stats.manually)
print("Potential leaks: " .. stats.leaking)
if full then
print("Bad free calls: " .. stats.badfree)
print("Bad constructor calls: " .. stats.badconstruct)
end
end or function()
print("Can only print statistics when GC is running in debug mode.")
end
end
OnTrigInit(function()
registerType("location", "RemoveLocation", {
"BlzGetTriggerPlayerMousePosition",
"CameraSetupGetDestPositionLoc",
"GetCameraEyePositionLoc",
"GetCameraTargetPositionLoc",
"GetOrderPointLoc",
"GetSpellTargetLoc",
"GetStartLocationLoc",
"GetUnitLoc",
"GetUnitRallyPoint",
"Location"
})
registerType("group", "DestroyGroup", {"CreateGroup"})
registerType("force", "DestroyForce", {"CreateForce"})
registerType("rect", "RemoveRect", {
"Rect",
"RectFromLoc",
"GetWorldBounds"
})
end)
end
If it runs... As far as I am aware the issue is that if you create tables too fast the garbage collector will not be able to keep up and so they will effectively become permanent leaks.making the garbage collection process a bit lighter when it runs.
In the demo that I configured, it's creating tens of thousands of leaks per second, and the garbage collector is still running. A real-world scenario is not going to come anywhere near that amount.If it runs... As far as I am aware the issue is that if you create tables too fast the garbage collector will not be able to keep up and so they will effectively become permanent leaks.
Maybe this behaviour was improved by the recent update.In the demo that I configured, it's creating tens of thousands of leaks per second, and the garbage collector is still running. A real-world scenario is not going to come anywhere near that amount.