Write to script's source at runtime for compiling Visual Scripting Code

I don’t use Lua-in-Lua VMs and never have had a reason to, so no. Sorry.

1 Like

Okay, no worries. I’ll see if I can find anything anywhere online. I appreciate your help!

If your allowing users to add in scripts it could actually cause some issues. It would depend on what your allowing them to script. If done correctly though it would 100% be a game id be interested in playing.

I’ve seen a game called ‘void script builder’ allow users to put their scripts into the game, I’m not sure how it works behind the scenes but I’m sure its dependant on plugins. Its extremely abusable and most people just copy scripts.

Yeah, I plan on making only select functions, predefined, that only focus on the controlling of enemies and gameplay. Anything like cloning objects, referencing services and user data won’t be allowed.

Ive had a look around and I cant really find much information on the topic,

This page I think has some useful information:
It points to using LoadString(). I think this could be the solution you are looking for.
It basically turns the string into code. (Check the string first to add restrictions.)
Im going to check it out properly myself in studio to see if this is all correct.

Edit:
Ok so this seems to work. Just be sure you have error handling and format the script properly before its loaded. I tried “print(“It Works”)”. and the “” on either side of the brackets cause some issues as they didn’t pair up properly.

But here we go, a way to do it. Iv’e seen some concerns about security with this so make sure it has proper checks for the script being entered.

Note - The players will need to know how to access certain things. e.g game.workspace.Robots.BlokHampster34Robot

It would also be good to allow a player to save their scripts too.

I look forward to seeing how this turns out.

1 Like

Often times visual scripting is not too difficult to implement without converting it into some source code. You can treat it like a programming language does and create a tree of things essentially. You could have a table for each “block” and give some extra metadata, like parameters. Then you can just write out the logic for how each type of block will behave. For example, an if block might have two “child” blocks in it, and the way that block will behave will be to check the condition it has somehow, and if the condition is met, execute the child blocks.

A way you might lay this out is to define some sort of “execute” function for blocks, and then that execute function would do something based on the type of block it is. This is pretty much exactly how scratch does it.

If you do want to allow for the use of actual code and you want to allow the user to send it, as mentioned above, you can use loadstring to run code (but only on the server, on the client it isn’t available). Remember to think about your local code and the modules you use locally as if an exploiter could modify them. If your local code sends script source and your server code just runs it with loadstring, an exploiter can send anything they want to too. I ended up making a tool to allow you to sandbox code in a more secure way (assuming you implement things securely), but, it’s a bit outdated now, and its a bit of a pain to use (I’m working on a newer version but its been painfully slow going for a couple reasons unfortunately)

2 Likes

Indeed it will open the game up to exploiters. You can check the string but with so many different ways to abuse it, you could be there a while.

It might be a case of only allowing the script to change some of the robots properties and using your function for making the robot attack. This way players cant make their robot deal 1000 damage or run extremely fast.

1 Like

Mhm. That would definitely work as well. You could mimic Roblox’s model of sending and receiving client and server stuff. Generally, with block based programming, you don’t need to create real source code, you just need to interpret the blocks somehow, and run some code based on the block type.

1 Like

Yeah, my idea was to have a table of every function that contained the function to run and the code block that would be created. The first block you would use would be “Sequence Begin”, similar to Scratch’s “Start” flag, but you could also create custom events, these would be the equivalent of custom functions.

Like you said with an if statement, there would be one input and two output connectors. There would also be a condition input that would toggle whether output A or output B is fired.

I’m not sure if I am really open to introducing a proper script editor, like you said there’s way more stuff to be wary about when it comes to full flexibility. Plus, if you want younger people to be able to use the Advanced Programmer, you can’t make it too advanced

Thank you so much! Also, to get around formatting errors like you mentioned, you can always use [[ and ]] to get around switching between " and ':

local Loadstring 	= require(script.Loadstring)


local Function = [[
	
	print("Hello World!")
	wait(1)
	print("Goodbye World!")
	
]]


local test	= Loadstring(Function)
test()

Output is as expected. Perfect!
I’ll be doing some more tests like Instancing and getting services to see if those work as well, but it looks like this is exactly what i needed. Thank you!

Edit: What’s even better is that they can still return values! A setup like so works as expected:

local test	 = Loadstring(Function)
local result = test()
if result then print('finished') end

Instead of creating a script and trying to set its source (which is impossible at runtime), you could have a pre-made script containing a StringValue and having this code:

loadstring(script.Code.Value)()

You would clone the script, insert the code into the stringvalue then just un-disable the script.

1 Like

Yes, this is probably what I’ll end up doing. A large module script with every function contained inside, with a return that provides the source code necessary, that gets referenced by each node of the visual editor. Put that source code into a container inside a script, enable it and then let it be removed when it’s not needed anymore.

It’s just formatting so that I can have as many parameters as necessary per function that might cause problems, although I could just append the parameters to the returned string, but string.format might work too

To be honest they will only really need the ability to locate players, calc speeds and stuff so projectiles meet their target. And control :MoveTo() on the robot so they can basically control how it moves and where its shooting at. (Id like pathfinding)

Yes, there will be MoveTo() (typical humanoid:MoveTo()) and MoveToAdvanced(), which uses the pathfindign service. It’s nice to have both options, e.g. if the player is in direct line of sight you wouldn’t want them to walk like they are a robot, you want them to just move directly to you.

There will be some other functions too but those are for other services. AI for enemies is probably just going to be stuff like movement, damage and player detection

So how many robots does each player have, if its multiple it might be good to allow players to control how they interact with one another.
e.g each take a seperate target or shoot same target from dif position

also are they using projectiles or rays? And whats the game all about if you dont mind telling everyone, do the robots go around collecting stuff?

Perhaps have some sort of message service for communication between robots and the player. This means robots could give out a warning or a player could ask for robots to do a task.

Also heres how you can check for certain things being used in the script:

function CheckForBadScript(String)
	
	local Matches = string.match(String,"DataStoreService")
	if Matches == nil then
		-- Its Fine
	else
		-- Not Allowed
		return "BadScript"
	end
end

Maybe you could possibly use a coroutine to create a new thread?

Well, the game is a Game Designer, where you can create first person shooter style games. Technically you dont have to make a shooter, the options for weapons is just there.
In the case you mentioned, you would insert an Enemy Actor and then assign a new Sequence to it (the sequence is what the visual code is going to be called) so in reality you can have as many as you want, although there will probably be an internal cap on the numbers.

For projectiles and rays, both are going to be available. A ray will be a standard cast ray function, and there will be a FireProjectileFromPoint when you want to shoot physical bullets.

For communication between player and actors, I’m not sure if there will be the functions available to program something like that to begin with but I’ll probably expand the table of functions later to add support for this. Because of its nature, you can basically program them to do whatever you want (with some restrictions)

Also, thank you for informing me of string.match! My previous method was to split the string by spaces and then do table.find, your way is much better lol. Will be useful to check that people arent putting suspicious inputs into the available parameters.

Also, marked one of your previous answer as the solution as it told me how to use loadstring()

I don’t think loadstring is the right move here. Feels like a sledgehammer of a solution, and opens you up to all sorts of fun exploits.

I agree with @Hexcede – presumably you have some sort of tree data structure. Instead of converting that into actual lua, and then running that lua with loadstring, you can just evaluate the tree directly.

Surely though if I’m using loadstring in the server and the only code that can be run is predefined, that prevents most exploits, right? I’m not using the typical Loadstring() but using a “Lua in Lua VM” Module in server storage, so the client shouldn’t even be able to see it. Not that the client can run loadstring, anyway, but I suppose it’s still better that way as the player doesnt know the indexes for functions.

Especially as the code being run is set by the server and can only be chosen by specific indexes, anything that isn’t a valid index is ignored.

I suppose it’s all down to how I handle the visual compiler to prevent anything that shouldn’t be run. Also, values sent as parameters will be overridden by the server if they are unusual e.g. if you try to make a walk speed be 5000 it will be clamped to 50 or some max value.

If that is the case, why not just have visual blocks map directly to functions, rather than mapping them to strings, creating valid lua code from those strings, and passing the whole string to a VM? Cut out the middleman, in other words.

1 Like