Thank you for the response I guess I missed the part of the upvalues being removed from debug. If widely used functions are staying that fine but I still would want the full functionality of the debug lib on the client (at least upvalues).
There are a few use cases for upvalues as I have used them so far.
- being able to update variables (global AND local) outside of scope or stack lock. I was finally able to get a working variable updater without a callback by utilizing the upvalues. This might not seem like it would be helpful but creating a framework and a function to do this helps devs down the line.
-- File 1
local weaponlist = {}
Citizen.CreateThread(function() -- Needs to be in a thread or a function block with the first upvalue being the fucker changed
weaponlist = {} -- value needs to be an upvalue of the function (fuck lua for this but still the best pl there is)
ExecuteVariableChanger('xinv:getSharedTable', 2, _ENV, "weaponlist", "weaponNames")() -- this will get the value and update the variable
end)
-- File 2
ExecuteVariableChanger = function(typeof, level, env, VarName, ...)
if not debug.setupvalue or not debug.upvaluejoin then return false end -- fuck fivem new change
local x -- fuck you lua and stacks and debug lib
local finder = setmetatable({__env = env, __level = level, __cb = debug.getinfo(level).func}, {
__index = function(a,b)
--print('i', a,b)
local val = a.__env[b]
if val == nil then
-- look through locals
local i = 1
while true do
local name, value = debug.getlocal(a.__level, i)
if not name then break end
if name == b then
return value
end
i = i + 1
end
end
if val == nil and rawget(a, "__cb") then
-- use upvalues
local i = 1
while true do
local name, value = debug.getupvalue(a.__cb, i)
if not name then break end
if name == b then
return value
end
i = i + 1
end
end
return val
end,
__newindex = function(a,b,c)
--print('n', a,b,c)
if rawget(a.__env, b) then
a.__env[b] = c
return
else
-- look for locals
local i = 1
while true do
local name, value = debug.getlocal(a.__level, i)
--print('nv', name, value)
if not name then break end
if name == b then
debug.setlocal(a.__level, i, c)
i = -1
return
end
i = i + 1
end
if i == -1 then return end
-- look for upvalues
if rawget(a, "__cb") then
i = 1
while true do
local name, value = debug.getupvalue(a.__cb, i)
--print('nv_cb', name, value)
if not name then break end
if name == b then
debug.setupvalue(a.__cb, i, c)
return
end
i = i + 1
end
end
end
rawset(a, b, c)
end
}) -- this line is the only level increase needed
env.passVal = finder[VarName] -- finds the current value of the var
print(finder[VarName])
local typeStr = PGX.Vars.GetUpdateVarTypeString(typeof, VarName, ...)
x = load([[
local n, v = debug.getupvalue(debug.getinfo(2).func, 1)
local isLocalValue = false;
if n and n == ']]..VarName..[[' then
isLocalValue = true
end
local ]]..VarName..[[_tmp = _ENV.passVal -- this upvalue is joined with the one from the caller function
local y_ = function(p1)
local _setValue = function(value)
_ENV.passVal = value -- I HAVE NO IDEA WHY THIS IS NEEDED SOMETHING WITH MEMORY AS WE NEED TO CALL 'VALUE' BEFORE USING IT AS A VALUE
]]..VarName..[[_tmp = value -- if the value is a local we set the joined upvalue
if _ENV.]]..VarName..[[ then -- if the value is a global (or in a table) we set the value through the ENV
_ENV.]]..VarName..[[ = value
end
end
]]..typeStr..[[
end
if isLocalValue then -- to ensure we dont override the _ENV of the calling chunk
debug.upvaluejoin(y_, 2, debug.getinfo(2).func, 1)
end
return y_()
]], typeof..":"..VarName, "t", setmetatable({}, {
__index = env,
__newindex = env,
}))
return x
end
This is really good for being able to be used for things like configs. Lets say you have a function to get a value from a centralized config, and set the value to a variable in a script (local or global). Then lets say that config was changed, most of the time you would have to create some sort of hook (Event handler or callback func) to catch the change and update the value, but this would mean each time, in each script, you would need to recreate this hook instead of some easy ~1 line function call to update the variables elsewhere. Saves dev time which does help.
Also since fivem events, promises, and func refs are all closed you are unable to lets say do debug.getinfo(2) inside of them to get the info of the function that called it (as we have cross resource communication). However with upvalues you can somewhat replicate it and modify values outside of a event handler using upvalues and the debug functions. Not to mention being able to update local values outside of scope (which I had thought impossible until I was finally able to do it with the code above).
I get that it is not a HUGE use case for the need of debug lib but it was helpful especially for devs.
Since I dont like complaining without suggesting solutions,
-
make the client sandbox optional for servers to opt-out of like the upcoming changes to NUI-callbacks
-
Add back at least setupvalue, upvaluejoin, upvalueid. Can also add some sort of tacking to it for modder abuse.
-
Add workaround natives to be used to replicate some of the features loss, I would be willing to look into potentially trying to get this to work if it would actually be considered for a merge