Gallardo9944 Posted January 30, 2014 Share Posted January 30, 2014 If you want to understand how it works, then you have to create __index and __newindex metamethods inside the metatable of your environment and call a function which returns the localPlayer, source, etc dending on the keyname. You can use rawget to get that. Link to comment
Bonsai Posted January 30, 2014 Author Share Posted January 30, 2014 Unbelievable, I got it working! Now where I know how it works it's not complicated anymore at all. I'm just not sure about __newindex. Everything seems to work fine without it. In what situations is this being needed? Link to comment
Gallardo9944 Posted January 30, 2014 Share Posted January 30, 2014 __newindex is used to scope _G access to avoid hacking within loadstring part. Any hacker can unlock triggerServerEvent and trigger that. __newindex can block setting variables which are defined globally. Link to comment
Bonsai Posted January 30, 2014 Author Share Posted January 30, 2014 So its just for security purposes but does not affect if its working? Sounds important, but in my opinion maps with compiled scripts shouldn't be uploaded anyway. There are things possible much worse then changing element data or triggering events and it doesn't even have to be in acl. Link to comment
Gallardo9944 Posted January 30, 2014 Share Posted January 30, 2014 it's not about maps with compiled stuff but with loading a custom DLL into GTA memory (MTA uses GTA so...). So loading a custom code would be possible with unsecure loadstrings. Link to comment
arezu Posted January 30, 2014 Share Posted January 30, 2014 (edited) Using setmetatable(env, {__index = _G}) means if a variable/function doesn't exist in the environment, then search in the global environment (of the resource), which means if you are trying to disallow triggerServerEvent, then a map can simply have script that does this: triggerServerEvent = nil and the map will have access to the real triggerServerEvent function. To fix this problem, you should copy variables instead: function copyTable(tbl) local t = {} for k, v in pairs(tbl) do if(type(v) ~= "table")then t[k] = v else t[k] = copyTable(v) end end return t end -- Define env as an empty table before this. -- Make sure to use pairs, not ipairs. for k, v in pairs(_G) do if(v ~= _G)then if(type(v) ~= "table")then env[k] = v else env[k] = copyTable(v) end end end env._G = env The only problem left is that source, resourceRoot, localPlayer, resource etc are global variables and are only set in the default environment (resource global environment), so you need to do to so that if you are trying to get/set the value of any of those variables, then you should access the global environment (of the resource) instead, but at the same time, your environment should not have access to that environment. This can be done by taking advantage of 'variable capturing', which means refrences to variables are copied into functions that are defined in a scope (for example if a function is defined in another function, then that function will have access to variables that the function that created it, has access to too), for example: function f(v) local d = 23 local f2 = function() outputChatBox(d) -- will output 23 outputChatBox(v) -- will output the value of 'v' from the function v end end We can take advantage of this method to give metamethod __index and __newindex of the new environment access to global environment without giving the actual environment access to it. An example of how the metamethod looks like (this should be called in the global environment before copying variables/functions): setmetatable(env, { __index = function(t, key) local r = rawget(_G, "_GET_GLOBAL")(key) if(r)then return r end return rawget(t, key) end, __newindex = function(t, key, value) local r = rawget(_G, "_SET_GLOBAL")(key, value) if(not r)then rawset(t, key, value) end end }) _GET_GLOBAL and _SET_GLOBAL are functions defined in the global environment, and what they simply do is check if the key you called it with is "source", "resource", "resourceRoot" etc, then return the value, or set the value and return true; otherwise get the value from the new environment or set it in the new environment. The reason you need __newindex as well is because __index is only called when trying to access a variable that doesn't exist (we dont want source, resourceRoot etc to exist in the new environment), and if a script in a map for example has: source = aValue and you dont have __newindex set, then source will be saved as a new variable in the environment, and __index will not be called when trying to access source, and source will always have the same value until you manually override it again. The order you should do everything in is: Create a empty table (env in this case). Set the metamethods for __index and __newindex for it, to get/set values from global environment instead. Loop the global environment and copy variables. Set the environments _G table to itself (env._G = env in this case). (Set the environments debug table to nil, in this case env.debug = nil, to prevent a user from doing stuff that they should not able to do. Do the same for loadstring and load). Load your code with loadstring and use setfenv on the returned function Use pcall to call the function returned from loadstring. Edited March 20, 2015 by Guest Link to comment
Bonsai Posted January 30, 2014 Author Share Posted January 30, 2014 Oh wow, thats one perfect answer I did it a bit different, but its pretty much the same. And so far everything is working good. Sometimes I get errors in debuglog caused by map scripts, but I guess that are general problems in those scripts, not related to the loading progress. At least I didn't find a map yet thats impossible to finish because of missing marker, objects etc. Thanks for your detailed answer. Link to comment
Saml1er Posted January 31, 2014 Share Posted January 31, 2014 Oh wow, thats one perfect answer I did it a bit different, but its pretty much the same. And so far everything is working good. Sometimes I get errors in debuglog caused by map scripts, but I guess that are general problems in those scripts, not related to the loading progress. At least I didn't find a map yet thats impossible to finish because of missing marker, objects etc. Thanks for your detailed answer. Finally, its solved. Link to comment
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now