• Using Lua_run mapping entity to access and run code hosted online
    21 replies, posted
Would it be possible to use the [I]lua_run[/I] Hammer entity to access another lua file from an online source hosted on a server and run the code? This way, certain multiplayer map variables could be changed via the hosted lua file whenever the mapper feels like altering the code. I could see so many cool things being done with this... Lua_run only supports one line of code, but I remember reading somewhere awhile ago that a guy successfully executed a larger lua file with many lines of code via [I]lua_run[/I] accessing it online. I will say right now that I only have a basic understanding of Lua and am more of a mapper, so I could not do this by myself. However Lua is something that I am interested in and I am in the process of learning.
It would be fairly straightforward, actually: [code]http.Fetch( "file on server", function( body ) RunString( body ) end, ErrorNoHalt )[/code] Something like that is what you'd want.
Hmm i see... This is good news! Are there any restrictions placed within multiplayer servers to prevent Lua code from being accessed from a remote online location?
Unless you have something between you and the website it asks for that checks for things, none that I am aware of. Realistically, it probably isn't a good idea to embed requests in the map itself for a few reasons, like how much of a back door this is and that you will lose functionality if the host goes down or gets moved.
[QUOTE=Kogitsune;44661845]Unless you have something between you and the website it asks for that checks for things, none that I am aware of. Realistically, it probably isn't a good idea to embed requests in the map itself for a few reasons, like how much of a back door this is and that you will lose functionality if the host goes down or gets moved.[/QUOTE] In the case of the lua file being taken down or not being accessed, I would have a fallback of some sort in the [I]lua_run[/I] map entity to have it do something else I suppose. Realistically, if the [I]lua_run[/I] entity only points to a specific URL for the other lua code, how vulnerable could this be?
In this case? Unless someone is desperate enough to take over your domain to spread tainted scripts, not very much danger. In general? I don't know about others, but the idea of any map in my list being able to run any code the mapper wanted without my initiating it is a problem.
[QUOTE=Kogitsune;44661936]In this case? Unless someone is desperate enough to take over your domain to spread tainted scripts, not very much danger. In general? I don't know about others, but the idea of any map in my list being able to run any code the mapper wanted without my initiating it is a problem.[/QUOTE] Fair enough. If the mapper has malicious intent, then yes. In theory though, any mapper with such knowledge could have already done this without anyone knowing about it, but no one has yet. I don't think it would be much of an issue, seeing as most mappers want their work to be respected. It would most likely only be used for practical purposes. I see what you're saying though.
That code worked! Thank you Kogitsune! I have a lua_run referencing a Lua file stored in my public Drobox folder! :dance: On the lua_run I have: [CODE]http.Fetch( 'https://dl.dropboxusercontent.com/u/mydropboxid/lua_test_onlinecode_1.lua', function( body ) RunString( body ) end, ErrorNoHalt )[/CODE] And in the Lua file stored on my dropbox I have: [CODE]for _, x in pairs(ents.FindByClass('logic_relay')) do if x:GetName() == 'relay_onlinecode_1' then x:Fire('Trigger') end end[/CODE] When I press a button that triggers the lua_run entity, the logic_relay is successfully triggered via the online code! Now how could I do a fallback in the lua_run code that triggers a different relay when the online code isn't found?
In the second space, I have ErrorNoHalt - which would cause an error to be listed in the console if the page fails to load. You would simply put something like: [code]http.Fetch( 'x', function( body ) RunString( body ) end, function( ) <other stuff here> end )[/code] Which would cause the second block of code to run if there were any errors loading the page ( eg, not HTTP 2XX ). This should cover most cases - file missing, over your bandwidth limit, etc - but may vary depending on how the host ( dropbox in this case ) handles the return value. As a side note, you should be able to use ents.FindByName( "relay_onlinecode_1" ) instead of looping through all your relays and then checking the name.
I'll get this out of the way: I've never worked with lua_run, so I'm unsure of how it works and its restrictions. Could you have the lua_run reference a local lua file which then references the hosted code?
[QUOTE=Kogitsune;44663526]In the second space, I have ErrorNoHalt - which would cause an error to be listed in the console if the page fails to load. You would simply put something like: [code]http.Fetch( 'x', function( body ) RunString( body ) end, function( ) <other stuff here> end )[/code] Which would cause the second block of code to run if there were any errors loading the page ( eg, not HTTP 2XX ). This should cover most cases - file missing, over your bandwidth limit, etc - but may vary depending on how the host ( dropbox in this case ) handles the return value. As a side note, you should be able to use ents.FindByName( "relay_onlinecode_1" ) instead of looping through all your relays and then checking the name.[/QUOTE] I've conducted some tests, with interesting results! When I use this code: [CODE]http.Fetch( 'https://dl.dropboxusercontent.com/u/mydropboxid/lua_test_onlinecode_1.lua', function( body ) RunString( body ) end, function( ) for _, x in pairs(ents.FindByClass('logic_relay')) do if x:GetName() == 'relay_onlinecode_1_fail' then x:Fire('Trigger') end end end )[/CODE] The file is found and everything runs just fine. However if I remove the file from Dropbox, instead of the fallback logic_relay being triggered, I get this Lua error: [CODE][ERROR] RunString:2: unexpected symbol near '<' 1. unknown - RunString:0[/CODE] ^This happens even when ErrorNoHalt is used. [I]BUT[/I] when I erase the URL in the code or put random stuff there, it works fine and the fallback relay is triggered like it should be. Does this have something to do with the way Dropbox handles not finding a requested file? Does the code try to use the 404 data returned from Dropbox as code to be run or something? Also, how would I go about replacing [CODE]for _, x in pairs(ents.FindByClass('logic_relay')) do if x:GetName() == 'relay_onlinecode_1' then x:Fire('Trigger') end end[/CODE] with ents.FindByName instead of using this method? I'm having a bit of trouble getting that to work. [editline]28th April 2014[/editline] Actually does [CODE]for _, x in pairs(ents.FindByName('relay_onlinecode_1')) do x:Fire('Trigger') end[/CODE] in essence completely replace this: [CODE]for _, x in pairs(ents.FindByClass('logic_relay')) do if x:GetName() == 'relay_onlinecode_1' then x:Fire('Trigger') end end[/CODE] Because that seems to be working!
Like I said earlier, it all depends on how the server responds to the request if that will work at all - you might need to put some logic in there to figure it out. http.Fetch has a few other values in the callbacks, so you might want to do something like [code]http.Fetch( 'x', function( body, _, _, code ) print( code ) RunString( body ) end ), otherstuff )[/code] The middle arguments are len and headers, respectively, which probably won't be needed. Does dropbox change the code if the file is missing? If so, you'd then want to put something like [code]( 'x', function( body, _, _, code ) if code == code_when_present then RunString( body ) else fallbackstuff end end, otherstuff )[/code] However, this is reaching a complexity point where you would be better off packing a script in the map in lua/autorun/server that does this for you since squishing this all into a single line is going to be problematic. And to answer your second question, yes, it looks for entities named that so you don't have to loop through all of them yourself and check for names.
How would i use his with a specific person who activates the trigger? I trying to have a map that plays music client side based on what area your in. I know lua_run is serverside so figuring out how to play the sound on the client that triggered the event is another story :(
Either [code]ACTIVATOR:SendLua[[surface.PlaySound('something')]][/code] or [code]CALLER:SendLua[[surface.PlaySound('something')]][/code] All depends on how the trigger passes sender/caller to lua_run.
[QUOTE=Kogitsune;44677579]Either [code]ACTIVATOR:SendLua[[surface.PlaySound('something')]][/code] or [code]CALLER:SendLua[[surface.PlaySound('something')]][/code] All depends on how the trigger passes sender/caller to lua_run.[/QUOTE] So for my lua_run trigger I have [CODE] http.Fetch( "https://dl.dropboxusercontent.com/u/243533372/garrysmod/lua/lua_run_test.lua", function( body ) RunString( body ) end, ErrorNoHalt )[/CODE] And the lua_run_test.lua just has a test function in it (for testing :3) [CODE]print("Test")[/CODE] Now in game when I walk through the trigger I get an error in console stating [CODE][ERROR] gamemodes/base/entities/entities/lua_run.lua:61: bad argument #1 to 'RunString' (string expected, got nil) 1. RunString - [C]:-1 2. RunCode - gamemodes/base/entities/entities/lua_run.lua:61 3. unknown - gamemodes/base/entities/entities/lua_run.lua:73[/CODE] Also, and i dont know if this is related but im on Linux.
In this case, it means that body is nil. I can't really help there since I'm not on linux, but you can try putting this in the console: [code]lua_run http.Fetch( 'x', print, print )[/code] Where x is the url, of course. It should give you a little more info on what is happening, like any status codes or things like that.
Just a heads up: If your dropbox file gets too much traffic it gets restricted. Don't use dropbox when you release this.
Well I got everything working the way I want it to! I really appreciate the help, Kogitsune. My final code for executing a hosted Lua file is: [CODE]http.Fetch( 'https://dl.dropboxusercontent.com/u/mydropboxid/Mapping/map_lua/ttt_weapons_factory/lua_test_onlinecode_1.lua', function( body, _, _, code ) if code == 200 then RunString( body ) else print('Cannot get hosted map Lua. Error: '.. code ) for _, x in pairs(ents.FindByName('relay_onlinecode_1_fail')) do x:Fire('Trigger') end end end, function( ) for _, x in pairs(ents.FindByName('relay_onlinecode_1_fail')) do x:Fire('Trigger') end print( 'Cannot get hosted map lua. Error: Unsuccessful' ) end )[/CODE] At 504 characters, this [I]barely[/I] fits within the one-line, 512 character limit that the lua_run entity has, but it does the job well! If I need to make the URL longer, I can just remove some of the printing to console stuff. I have it set up to print appropriate error messages to the console when it doesn't work. Dropbox sends a code 200 (success) when the file is found, so any code returned besides that will cause it to do the fallback stuff. It all seems to work great, but since I'm a beginner coder could someone check over this? [QUOTE=BayLife;44678333]Just a heads up: If your dropbox file gets too much traffic it gets restricted. Don't use dropbox when you release this.[/QUOTE] This shouldn't be a problem in the case of a small Lua file should it? If dropbox measures traffic by bandwidth, hundreds of thousands of people would need to download it for it to even equate to 1GB. If they do it by number of downloads, I guess that's a little different. [B]Edit [/B] Looks like Dropbox has a 20GB cap on traffic, so I should be good! This has so many cool applications. For example, I can fix bugs in the map without even releasing a new version, change seasons on an outdoor map throughout the year, have a message of the day in the map that I can change at will, switch up the round ending music, or have different pre-set themes for the map that I could change every now and then. So cool to think about the possibilities!
How could I have the code check the multiplayer game server's ip, and if it matches, it does something? For example, if I wanted to make something happen but only have it work on my favorite server and nowhere else.
[QUOTE=Matt2468rv;44683036]How could I have the code check the multiplayer game server's ip, and if it matches, it does something? For example, if I wanted to make something happen but only have it work on my favorite server and nowhere else.[/QUOTE] You should be able to get the current ip via the convar 'hostip' ([URL="http://facepunch.com/showthread.php?t=1332285&p=43136356&viewfull=1#post43136356"]source[/URL])
[QUOTE=wh1t3rabbit;44683157]You should be able to get the current ip via the convar 'hostip' ([URL="http://facepunch.com/showthread.php?t=1332285&p=43136356&viewfull=1#post43136356"]source[/URL])[/QUOTE] Hmm... When I try: [CODE]function GetHostIP() hostip = GetConVarString("hostip") print(hostip) end GetHostIP()[/CODE] Some seemingly random 10 digit negative number is printed to the console. I'm simply trying this through starting a multiplayer server within Garry's Mod, not an actual SRCDS server, but it should still function the same and spit out the IP if it's working correctly, right? When I replace 'hostip' with 'hostname', it seems to work properly and prints 'Garry's Mod' to the console.
[url]http://facepunch.com/showthread.php?t=1332285&p=43136356&viewfull=1#post43136356[/url]
Sorry, you need to Log In to post a reply to this thread.