Lets say I take this code and turn it into a string:
local function Yeah(arg)
print(arg)
end
while true do
print("a")
end
while true do
if true then
print("b")
end
end
The string would look like this: local aString = 'local function Yeah(arg) print(arg) end while true do print("a") end while true do if true then print("b") end end'
Now I want to edit the string and add the text “task.wait()” in the right places, which would be before the end that encloses the while loop, like this:
-- As string
local aString = 'local function Yeah(arg) print(arg) end while true do print("a") task.wait() end while true do if true then print("b") end task.wait() end'
-- Example code
while true do
print("a")
task.wait() -- here
end
while true do
if true then
print("b")
end
task.wait() -- here
end
I tried doing it in many possible ways, and I ended with a very ugly code that kinda does what I want, kinda hardcoded and not doing the check on the entire string…
I tried splitting words in a table, counting how many “if” are in between the while and end in order to skip the required ends until find the end that belongs to the while loop, then adding the “task.wait()” text
I tried using the string library functions but Im not getting good results.
Anyone could give me an example on how I could achieve this?
I would like to answer to this question but can you provide the reason behind doing all this? This question is very unrelated with game development purposes. What would you like to achieve for doing all this?
Its for an admin console that let admins to directly run code, like using the /console commandBar.
I just want to perform some checks in the code they are about to run before actually run it, if it contains stuff like a while loop without a wait that could crash the game, I add it into the string before running it
The best solution to running the code on the server would be to convert the code to an abstract syntax tree. Then adding a wait in the right place would be easy and you could even use the AST to run the code without needing to use loadstring. (so you can just add a wait after every so many “steps”)
However the overall/actual best solution would be to run the code on the client and have it communicate updates that it wants to make to the server. So that if the admin makes an infinite loop they only crash theirselves. Because people will always be able to get around any checks you have in place for checking for infinite loops.
Honestly I dont have any idea on how to convert the code that they typed into an abstract syntax tree in order to edit it.
If running the code client sided, theres services and stuff that cant be performed on client side, even if I send replication to other clients, they cant run the code if uses serverSided functions/services, I think
Well these ppl is not like they will try to get around the checks, its more like a safe check before someone perform an epic error by mistake
local Pattern = "while %D+ do%D*end"
--[[ i guessed it randomly and it works so it works ]]
local function AddText(String, Pos, Add)
return string.sub(String, 1, Pos) .. Add .. string.sub(String, Pos + 1, -1)
end
local function MalnipulateThingy(Source)
local Find = {string.find(Source, Pattern)}
if Find[1] and Find[2] then
local Init = 0
while true do
Find = {string.find(Source, Pattern, Init)}
local Start, End = table.unpack(Find)
if Start and End then
local CurrentLine = string.split(string.sub(Source, Start, -1), "\n")[1]
local StringBefore = string.sub(CurrentLine, 1, Start - 1)
local TabsCount = ({string.gsub(StringBefore, " ", "")})[2] + 1
Source = AddText(Source, End - 4, "\n" .. string.rep(" ", TabsCount) .. "task.wait()")
else
break
end
Init += 1
end
end
return Source
end
print(MalnipulateThingy([[
while true do
print("a")
end
]]))
You can look at the AST of different programming languages including lua on https://astexplorer.net/ for inspiration.
What if someone does
for i = 1, os.time() do end
or maybe
for i = 0, math.huge do end
or even harder to detect
local big = 1.8e308
local i = 0
while i < big do i += 1 end
or even worse
function doSomething() return doSomething() end
or nests a lot of shorter loops?
It’s impossible to detect every possible infinite loop. So your only option is to completely circumvent the entire issue and just run the code on the client or at least wait every so many statements with your own lua vm.
local aString = 'local function Yeah(arg) print(arg) end while true do print("a") end while true do if true then print("b") end end'
local function replaceFunc(str:string)
local useSpace = false
if str:sub(#str) == " " then
str = str:sub(1,#str-1)
useSpace = true
end
str = str:sub(1,#str-3) .. "task.wait() end" .. if useSpace then " " else ""
return str
end
function readString(str:string)
local output = str:gsub("while %w+ do .- end[%send]*",replaceFunc)
print(output)
end
readString(aString)
This will output local function Yeah(arg) print(arg) end while true do print("a") task.wait() end while true do if true then print("b") end task.wait() end
which is what you want, I think. If I got that right, then this is solved pretty easily I by using the - string quantifier. This allows us to just use gsubs to replace existing while loops with loops that have waits in them, before the last end.
I didn’t want to try so hard on making it pretty so the spacing thing is just because it matched spaces at the end of the gsub capture sometimes, but the bulk of the work is just the expression being passed in readString
This works pretty well thank you, manipulating strings confuses me a lot… your code does it very well, and simplified.
Even when I will take a different approach, and probably turn this stuff client sided, idk, I will mark your answer as the solution cause it actually does exactly what I was looking for, thank you!
I understand what you mean, and actually you are right, would be better to run it in client…
I’ve been reading about the approach you suggest of creating a syntax tree and the AST. This is pretty useful https://astexplorer.net/ thank you so much. And honestly yup, sounds complicated, something new to learn about for me.
Its great to get in deep with this topic, seems helpful, but for now, just for a cheap admin console, probably the best idea is client sided as you suggested.
Thank you! Yup it works too, but works similar to mine, it fails if the string has more while loops, so it should be fired more than once to edit the entire string, the code is actually placing all the waits stacked at the end of the string the more loops it has the string