Moderators IIYAMA Posted March 25, 2014 Moderators Share Posted March 25, 2014 Does anybody knows how I can unload a loaded string? Or at least disable it. Advance stuff, pls only reply if you know what the loadstring function does. Link to comment
Tete omar Posted March 25, 2014 Share Posted March 25, 2014 It loads a code in a string; E.g: loadstring ( "outputChatBox ( \"Hello!\" )" ) Link to comment
Moderators IIYAMA Posted March 25, 2014 Author Moderators Share Posted March 25, 2014 But how can I destroy this string? Because if I load this function: loadstring("function thisFunction() outputChatBox(\"thisFunction\") end") It will stay active. Link to comment
Tete omar Posted March 25, 2014 Share Posted March 25, 2014 Why would you want to do that as long as you can just assign 'thisFunction' to nil Link to comment
Moderators IIYAMA Posted March 25, 2014 Author Moderators Share Posted March 25, 2014 Will loadstring execute the code ones and then it is gone? O_o Link to comment
Tete omar Posted March 25, 2014 Share Posted March 25, 2014 loadstring just acts like a normal code execution, it first loads a string and returns a function that executes the code on call. E.g: loadedString = loadstring ( "outputChatBox ( \"Hello!\" )" ) -- Here's our loaded code that will be executed when we call 'loadedString' function loadedString ( ) -- And here we call it -- Result: Hello! Note: I'm not sure about this explaination though, i might be wrong. Link to comment
Saml1er Posted March 25, 2014 Share Posted March 25, 2014 (edited) Its just a string. I use loadString for loading map scripts. I insert functions into tables and I didn't touch chatbox functions since its a string and loadString just executes it and its not saved in memory or something. <-- This is how much I know. btw if you have event handlers in the string then you'll need to insert it into tables and after that you'll need to use removeEventHandler same goes for timers. This might be helpful. i just created another resource which handles that. The code looks like: local objects = { } local _createObject = createObject -- save the original function function createObject(id,x,y,z,rx,ry,rz,islod) -- replace the original one if not id or not x or not y or not z then return false end local rx = rx or 0 local ry = ry or 0 local rz = rz or 0 local object = _createObject(id,x,y,z,rx,ry,rz,islod) -- use the old function if object then table.insert(objects,object) return object -- Don't forget to return the info of the original one end end function loadCode(content) local loaded = loadstring(content) local ex = pcall(loaded) if ex then outputChatBox("loaded the code") end end function unloadCode() for i,v in ipairs(objects) do if isElement(v) then destroyElement(v) end end end Edited March 25, 2014 by Guest Link to comment
Moderators Citizen Posted March 25, 2014 Moderators Share Posted March 25, 2014 you can just assign 'thisFunction' to nil loadstring("thisFunction = nil")() --Destroy the function 'thisFunction' EDIT: But you will get errors if the rest of the script is still calling it somewhere so just do this instead: loadstring("thisFunction = function () end")() Link to comment
arezu Posted March 25, 2014 Share Posted March 25, 2014 This is what I do: local func = loadstring("function thisFunction() end") pcall(func) -- and then later when I want to unload... func = nil Link to comment
Moderators Citizen Posted March 25, 2014 Moderators Share Posted March 25, 2014 This is what I do: local func = loadstring("function thisFunction() end") pcall(func) -- and then later when I want to unload... func = nil There is no need to use pcall function just call func like any other function: local func = loadstring("function thisFunction() end") func() --like this By the way as I said, setting the variable to nil will throw errors if the rest of the script is still trying to call that function. Link to comment
Tete omar Posted March 26, 2014 Share Posted March 26, 2014 By the way as I said, setting the variable to nil will throw errors if the rest of the script is still trying to call that function. In this case, you'll have to check the validity of the function before you call it. Link to comment
Moderators IIYAMA Posted March 26, 2014 Author Moderators Share Posted March 26, 2014 So I must use it like this? dataForTheFunctionInside = {"my name is IIYAMA","apple","what ever"}-- must be global local callThisFunction = "function myFunction(data) outputChatBox(next(data)) end myFunction(dataForTheFunctionInside)" local callLoadString = function () local loaded = loadstring(callThisFunction) local ex = pcall(loaded) if ex then outputChatBox("loaded the code") end end addCommandHandler("loadstring",callLoadString) (no tested) Link to comment
arezu Posted March 26, 2014 Share Posted March 26, 2014 There is no need to use pcall function just call func like any other function: local func = loadstring("function thisFunction() end") func() --like this By the way as I said, setting the variable to nil will throw errors if the rest of the script is still trying to call that function. Does anybody knows how I can unload a loaded string? I assume he wants to unload the whole code loaded with the string; and why would there be any reason to only "unload" some parts of it? Also it is better to use pcall if you want to have the same affect as mta's script loader, like there are hundreds of maps that try to call a misspelled function (SetCloudsEnabled), and mta script loader ignores that and makes other scripts in the map work, and that is only possible with pcall. So I must use it like this? dataForTheFunctionInside = {"my name is IIYAMA","apple","what ever"}-- must be global local callThisFunction = "function myFunction(data) outputChatBox(next(data)) end myFunction(dataForTheFunctionInside)" local callLoadString = function () local loaded = loadstring(callThisFunction) local ex = pcall(loaded) if ex then outputChatBox("loaded the code") end end addCommandHandler("loadstring",callLoadString) (no tested) Yes, that is the right way. It is recommended to use a "sandbox", but you can add later. Link to comment
Moderators IIYAMA Posted March 26, 2014 Author Moderators Share Posted March 26, 2014 I have never worked with Environments. I found this: http://lua-users.org/wiki/SandBoxes As far as I understand it is a kind of security for the code. What can happen if I don't use a sandbox? Link to comment
arezu Posted March 26, 2014 Share Posted March 26, 2014 I have never worked with Environments.I found this: http://lua-users.org/wiki/SandBoxes As far as I understand it is a kind of security for the code. What can happen if I don't use a sandbox? If you are using loadstring to load script from maps, then a player can add a script to their map which calls any function and ruin your server and steal maps (and possibly server-side scripts as well). Link to comment
Moderators IIYAMA Posted March 26, 2014 Author Moderators Share Posted March 26, 2014 aha, Well I am not going to save the scripts I load via loadstring on the players their harddrive. More like loading modus per map, which is send from the server. But if I do save them, how can a sandbox prevent this? Link to comment
arezu Posted March 26, 2014 Share Posted March 26, 2014 aha,Well I am not going to save the scripts I load via loadstring on the players their harddrive. More like loading modus per map, which is send from the server. But if I do save them, how can a sandbox prevent this? You got it wrong, saving to harddrive or not doesn't matter, it's the fact that you are loading scripts from a map that is not made by you. Using a sandbox will prevent the script from calling functions that you dont want it to be able to call. Link to comment
Moderators IIYAMA Posted March 26, 2014 Author Moderators Share Posted March 26, 2014 ok, local env = {} setfenv(user_script, env) pcall(user_script) Using setfenv I can prevent this? But after I set this environment. Will the code inside the environment able to access global variables (read or/and write)? Or just calling other functions disabled? Note: All the code that I am going to load is written by myself and doesn't come from the maps but from the gamemode. The gamemode sends the code which fits with the map. Link to comment
arezu Posted March 27, 2014 Share Posted March 27, 2014 Note: All the code that I am going to load is written by myself and doesn't come from the maps but from the gamemode. The gamemode sends the code which fits with the map. If you are only sending your own code to the player and loading it, then you don't need a sandbox. Anyways if you want to know, what you wrote is not enough, setmetatable is also needed, but you can read on how to do that somewhere else as there are already good tutorials for that (and i've also answered on how to do it properly on mta forum previously, so you can use search). Link to comment
Saml1er Posted March 27, 2014 Share Posted March 27, 2014 A very useful post by azeru. 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 have to copy only the functions and variables to the new environment; you can do that by doing this: -- env is a empty table -- Make sure to use pairs, not ipairs. for k, v in pairs(_G) do if(v ~= _G)then env[k] = v 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, k, value) local r = rawget(_G, "_SET_GLOBAL")(key, value) if(r)then return t end return rawset(t, k, value) 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 it 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). 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