This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
ElvishJerricco's profile picture

Advanced function replacement

Started by ElvishJerricco, 01 August 2012 - 11:11 AM
ElvishJerricco #1
Posted 01 August 2012 - 01:11 PM
This tutorial will explain an advanced technique that should not be used by most programs except alternate operating systems that don't want to mess with the source code of bios.lua or the other built in APIs.

I've always wanted to create my own Computercraft OS without having to modify bios.lua and other files. The problem with that though, is that then I can't change any of the default behavior. The solution however, turned out to be fairly simple, for those of us with a better understanding of how lua works.


function test()
	print("testing!")
end

test()

The code above will simply print "testing!" correct? But what's actually happening in the code here?
  1. We declare the function test()
  2. We put a body in test()
  3. We end, and thus finish, test()
  4. We call test
There's something interesting about step 4 though. We're not simply calling a function. We're giving the location of the function with the "test" token, and telling lua to run the function stored there by including the brackets. What this means is that we can reassign the value of test to another function. Take a look at this


function test()
	print("testing 1!")
end

function testAgain()
	print("testing 2!")
end

test = testAgain
test()

Traditionally, you might expect to get an error on the "test = testAgain" line. But in fact, you're simply telling lua that the value of test is now the value of testAgain. So when you call test, you're pointing to testAgain, and testAgain gets run instead, outputting "testing 2!"

So now we can get ourselves into some advanced, dangerous trickery. Let's try messing with the systems APIs.


function test(side)
	print("I've stolen a function! Sorry " .. side .. " side, I'm not gonna read you")
	return false
end

redstone.getInput = test
truth = redstone.getInput("left")


Here we've chosen redstone.getInput as our "victim." We've replaced it with test! So when you call redstone.getInput("left"), you get the return of false and the output "I've stolen a function! Sorry left side, I'm not gonna read you". Now why exactly is this dangerous? Well it turns out, after your program exits, redstone.getInput keeps pointing to your function. So the next guy's program who tries to call redstone.getInput() is going to be out of luck. He's only going to get false, no matter what, because your function always returns false.

But there's some good in this technique, I swear! Let's imagine I were writing an OS without rewriting any of dan200's files. I've come across a problem that people's programs are printing on the screen when I don't want them to be able to print. I can fix this with function replacement!


function myPrint(msg)
	-- calculate condition
	condition = conditionFindingFunction()
	
	-- compare to see if printing is allowed
	if condition then
		--call the backup of print() that get's made before this function is called
		printBackup(msg)
	end
end

-- backup print to printBackup so that we don't lose the original print
printBackup = print

-- replace print with myPrint
print = myPrint
print("Testing!")

Here we do a more advanced set of replacements. We've got a function called printBackup. We never even declared it as a function, but we can still set its value to print. This ensures that as long as we don't lose or change printBackup(), we won't lose the original print(). Then we set print's value to our own custom function myPrint(). In here we calculate the condition to see if printing is currently allowed, throw it in an if statement, and call the printBackup that was made before this function was ever called. So now there's a system wide change to the print function.

One more application of this, again pertaining to the print function, would be forcing print to print into the text windows an OS might use. A lot of OSes for computercraft create a box out of text and call it a window. They might also make use of some class system (which uses tables to store functions and variables) to have objects represent those windows. Here, we'll assume both of those things are true. Let's try to force print to have different parameters and print out to the window.


function myPrint(msg, window)
	if window ~= nil then
		window:write(msg)
	end
end

print = myPrint

It's important to note here that we're assuming window is a standard window object, which is just a table full of functions and variables. Tables can store functions and you can access them using standard table.function() notation, but if you use a colon instead of a period (as shown here), it will automatically pass in the table object as an argument to the function, and if the function was declared with the colon notation, you can use the self keyword to reference that table passed in an invisible parameter. Otherwise you'd have to accept the table as a normal parameter.

Anyways. So we first check if window is nil. If it is, the programmer was trying to use print the regular way; by using print(msg). There's no specified window so we'll just throw it out and ignore the message entirely. (In a real OS, I wouldn't do this. I'd specify a specific place in which these messages would be logged or printed, but we're not going to spend that much time on this) If the window is not nil, we call the function write(msg) stored in the window object to print the message onto the window however the function specifies it to be printed. Notice that we didn't even back up the old print function. It's practically useless now. Windows are going to be using a custom write function that deals with the term API, and printing is supposed to bring them to our function. So there's no need for the old function.

EDIT: New application I just figured out (with the idea coming from KaoS). What if you want a password protected computer? Well as we all know, ctrl-T will close your password program. Now there's a very well known way around this that prevents the computer from ever terminating a program using ctrl-T.


os.pullEvent = os.pullEventRaw

Most people probably just copied this code into their password programs and forgot about it. But as we now know, it's a function replacement! You're replacing os.pullEvent with os.pullEventRaw. So now whenever ctrl-T is pressed, nothing happens. But This brings up a new problem. Once past your password protection, you can't use ctrl-T to terminate programs that you actually wan s to terminate. The solution to this might be obvious to many by now. But here's my implementation.


pullBackup = os.pullEvent
os.pullEvent = os.pullEventRaw

print("Enter password")
pass = read("*")

if pass ~= "password" then
	os.reboot()
else
	os.pullEvent = pullBackup
end

First, we backup os.pullEvent. Then we replace it with os.pullEventRaw so that any call to os.pullEvent actually calls os.pullEventRaw, which doesn't automatically handle the ctrl-T events. After that we use pretty standard procedure to read a password and reboot if what was read is not the password or finish the program if it is. But we do something a little different. We set os.pullEvent to pullBackup. This replaces os.pullEvent with the old one so that whenever programs call it in the future, it'll call the original os.pullEvent, which allows for ctrl-T to work..



I hope you've learned something here. I was just thinking about how you can set functions in javascript and wondered if it could be done in lua too. Luckily it can. This could become a wildly important feature of some OSes, but unfortunately a wildly dangerous feature in some malicious programs. I won't explain those though, as I want as low a chance of malicious code as possible. Either way, I'm glad you read this whole thing. Have fun with it. Happy crafting!
Pharap #2
Posted 06 August 2012 - 09:22 AM
I'm surprised this doesn't have any more replies, this could come in handy.
I suppose most people think it's blasphemy lol
Xhisor #3
Posted 06 August 2012 - 09:27 AM
I lost it after the second code, brb going to learn some more LUA.
acters124 #4
Posted 06 August 2012 - 10:27 AM
lost me with "condition = conditionFindingFunction()" xD
I need to learn more then .:P/>/>
wilcomega #5
Posted 06 August 2012 - 04:45 PM
nice tutorial

you can also use this for a virus, replace the print function to log everything into a file or something and then upload it to pastebin and from your computer download the log file. congratulations you just made a program to see what other programs have to say from a remote computer :P/>/>

you could also make a startup file that removes itself after it loaded everything and replace the os.shutdown function with a function that downloads the same startup file and then shuts down so the next time you startup the file is removed and Loaded at the same time ;)/>/>
Pharap #6
Posted 06 August 2012 - 05:27 PM
nice tutorial

you can also use this for a virus, replace the print function to log everything into a file or something and then upload it to pastebin and from your computer download the log file. congratulations you just made a program to see what other programs have to say from a remote computer :P/>/>

you could also make a startup file that removes itself after it loaded everything and replace the os.shutdown function with a function that downloads the same startup file and then shuts down so the next time you startup the file is removed and Loaded at the same time ;)/>/>

It won't get rid of the functions built in to the pc, it just overrides them within the program. Ok for hijacking the os though
ElvishJerricco #7
Posted 07 August 2012 - 06:48 AM
nice tutorial

you can also use this for a virus, replace the print function to log everything into a file or something and then upload it to pastebin and from your computer download the log file. congratulations you just made a program to see what other programs have to say from a remote computer :P/>/>

you could also make a startup file that removes itself after it loaded everything and replace the os.shutdown function with a function that downloads the same startup file and then shuts down so the next time you startup the file is removed and Loaded at the same time ;)/>/>

It won't get rid of the functions built in to the pc, it just overrides them within the program. Ok for hijacking the os though

Actually (as i'm pretty sure i mentioned in the post) I was surprised to find that replacing a function in your program replaces it for the entire system until you reboot. I'm pretty sure it's because you're messing with global variables. The reason your functions usually get unloaded is because there's no reference to them in the global table once your program ends, so Lua's garbage collection gets rid of them. But if you replace os.shutdown with your function, the global table now has a reference to your function. And if you backed up the original os.shutdown, your function has a reference to that! So neither get unloaded. Funky stuff, right? Anyways so once something's in the global table (in this case, our custom os.shutdown) it'll remain there until the global table is reloaded. And the only time the global table can be reloaded is when the OS reboots.

So the point is, the os.shutdown hack could theoretically work. You'd just have to write the download code (which shouldn't be too hard anyways)
ElvishJerricco #8
Posted 07 August 2012 - 06:55 AM
lost me with "condition = conditionFindingFunction()" xD
I need to learn more then .:P/>/>

conditionFindingFunction is just supposed to be some random dummy function that I never coded. It's purpose would have been to determine if printing should be allowed at that time or not. Again, I never coded that function, but it is assumed that in context, that function would have been made
wilcomega #9
Posted 07 August 2012 - 03:06 PM
nice tutorial

you can also use this for a virus, replace the print function to log everything into a file or something and then upload it to pastebin and from your computer download the log file. congratulations you just made a program to see what other programs have to say from a remote computer :P/>/>

you could also make a startup file that removes itself after it loaded everything and replace the os.shutdown function with a function that downloads the same startup file and then shuts down so the next time you startup the file is removed and Loaded at the same time ;)/>/>

It won't get rid of the functions built in to the pc, it just overrides them within the program. Ok for hijacking the os though

Actually (as i'm pretty sure i mentioned in the post) I was surprised to find that replacing a function in your program replaces it for the entire system until you reboot. I'm pretty sure it's because you're messing with global variables. The reason your functions usually get unloaded is because there's no reference to them in the global table once your program ends, so Lua's garbage collection gets rid of them. But if you replace os.shutdown with your function, the global table now has a reference to your function. And if you backed up the original os.shutdown, your function has a reference to that! So neither get unloaded. Funky stuff, right? Anyways so once something's in the global table (in this case, our custom os.shutdown) it'll remain there until the global table is reloaded. And the only time the global table can be reloaded is when the OS reboots.

So the point is, the os.shutdown hack could theoretically work. You'd just have to write the download code (which shouldn't be too hard anyways)

i guess you are right. lol. its just http.get stuff and then content readall() and just put it there. the only way to detect it is with a costum boot disk that the startup program doesnt get executed. btw i am trying to get 50 posts because i want to be a script kiddie as title. btw i am way better that the title script kiddie discribes. :(/>/>
Pharap #10
Posted 08 August 2012 - 04:05 AM
nice tutorial

you can also use this for a virus, replace the print function to log everything into a file or something and then upload it to pastebin and from your computer download the log file. congratulations you just made a program to see what other programs have to say from a remote computer ;)/>/>

you could also make a startup file that removes itself after it loaded everything and replace the os.shutdown function with a function that downloads the same startup file and then shuts down so the next time you startup the file is removed and Loaded at the same time :(/>/>

It won't get rid of the functions built in to the pc, it just overrides them within the program. Ok for hijacking the os though

Actually (as i'm pretty sure i mentioned in the post) I was surprised to find that replacing a function in your program replaces it for the entire system until you reboot. I'm pretty sure it's because you're messing with global variables. The reason your functions usually get unloaded is because there's no reference to them in the global table once your program ends, so Lua's garbage collection gets rid of them. But if you replace os.shutdown with your function, the global table now has a reference to your function. And if you backed up the original os.shutdown, your function has a reference to that! So neither get unloaded. Funky stuff, right? Anyways so once something's in the global table (in this case, our custom os.shutdown) it'll remain there until the global table is reloaded. And the only time the global table can be reloaded is when the OS reboots.

So the point is, the os.shutdown hack could theoretically work. You'd just have to write the download code (which shouldn't be too hard anyways)

I stopped reading after the list and just read the code since I thought you were probably just explaining what the code does for those less versed in the programming arts.
KaoS #11
Posted 10 August 2012 - 01:34 PM
if you don't want to break other programs just make it a local change


local print=nil
ElvishJerricco #12
Posted 10 August 2012 - 10:22 PM
if you don't want to break other programs just make it a local change


local print=nil

Well yea but the point of doing it this way is to mess with the whole system. Otherwise you could very easily just create a local print function that called the original print function. But that's no fun and serves little purpose besides a helper function.
KaoS #13
Posted 13 August 2012 - 02:11 PM
what I mean is that for example in your password function you swop os.pullEvent with os.PullEventRaw but make it a local change so when your password accepting program ends everything works as normal
ElvishJerricco #14
Posted 13 August 2012 - 09:07 PM
what I mean is that for example in your password function you swop os.pullEvent with os.PullEventRaw but make it a local change so when your password accepting program ends everything works as normal

Unfortunately that's not actually the case. If you create a local os.pullEvent() in your program, it is not available to anything but your program. This means APIs and standard system functions don't see it as the existing os.pullEvent(). So in fact, they will still call the standard os.pullEvent. There is, however, a way to do what you're describing.


pullBackup = os.pullEvent
os.pullEvent = os.pullEventRaw

print("Enter password")
pass = read("*")

if pass ~= "password" then
	os.reboot()
else
	os.pullEvent = pullBackup
end

What this does should be fairly obvious by now. Backup the original pullEvent, read a password, if it isn't right reboot. Else revert pullEvent to the backup and that's the end of the program.

This is actually a really good application of this technique. I'm gonna put it in my tutorial now.
KaoS #15
Posted 16 August 2012 - 09:07 AM
I wonder if any functions you call from your program also get the local variables, something to investigate because if they do then if you call read() etc it will be using the local pullevent as well and should prevent termination, not sure
ElvishJerricco #16
Posted 17 August 2012 - 12:29 AM
I wonder if any functions you call from your program also get the local variables, something to investigate because if they do then if you call read() etc it will be using the local pullevent as well and should prevent termination, not sure

Local variables cannot be referenced outside their scope. But anything in the same scope as them can reference them. And as long as there's any thing that references any variable (even local ones), that variable stays in memory. And since you're calling a function that's in the same scope as the variable, it will access them and keep them in memory.
immibis #17
Posted 17 August 2012 - 09:08 AM
You should probably mention that functions are values (technically "first class objects") in Lua. That's why

function foo()
print("Hi!")
end
and

foo = function()
print("Hi!")
end
are exactly equivalent (in fact, the first gets translated into the second when Lua compiles it)

There's something interesting about step 4 though. We're not simply calling a function. We're giving the location of the function with the "test" token,
You are reading the value from the variable "test", and calling it. There is nothing special about variables that happen to contain functions.



function myPrint(msg)
	-- calculate condition
	condition = conditionFindingFunction()
	
	-- compare to see if printing is allowed
	if condition then
		--call the backup of print() that get's made before this function is called
		printBackup(msg)
	end
end

-- backup print to printBackup so that we don't lose the original print
printBackup = print

-- replace print with myPrint
print = myPrint
print("Testing!")
If printBackup was local (to the file, or some other scope) then people couldn't just call printBackup to get around your restriction.
For some reason when calling a function stored in a table, you have to use a colon instead of a period before the key. I think it's because a period would ask for the value (for a function, the location of the function).

Wrong. Because functions are just values, you can get a function from a table the same way you get any other element in the table. window.print(msg) works fine, if the window object is programmed to work that way. However, in Lua it's very common for functions in tables to have the table itself as the first parameter, as there's no implicit "this" parameter like in Java.
window:print("hi") is just a shortcut for writing window.print(window, msg).

I wonder if any functions you call from your program also get the local variables, something to investigate because if they do then if you call read() etc it will be using the local pullevent as well and should prevent termination, not sure
I'm guessing you mean something like

local os.pullEvent = os.pullEventRaw
Not only will that not work, it's a syntax error.
KaoS #18
Posted 17 August 2012 - 04:56 PM
How is that a syntax error?
Cranium #19
Posted 17 August 2012 - 05:11 PM
How is that a syntax error?
It might be the lack of () in that line…
KaoS #20
Posted 17 August 2012 - 05:18 PM
there should be no () as we are not calling the function, we are changing it
Cranium #21
Posted 17 August 2012 - 05:20 PM
Ah…I gotcha. I did not see that. I just saw the last few posts, and assumed…. I fail.
KaoS #22
Posted 17 August 2012 - 05:21 PM
ah I know, you cannot apply a local change to a specific table value, what you do is simply


local os=os --making a local table that you can now modify
os.pullEvent=os.pullEventRaw

and that should do it
Cranium #23
Posted 17 August 2012 - 05:27 PM
Makes sense. My brain is hurting from other things today, and I can't focus on much else…
ElvishJerricco #24
Posted 17 August 2012 - 08:11 PM
You should probably mention that functions are values (technically "first class objects") in Lua. That's why

function foo()
print("Hi!")
end
and

foo = function()
print("Hi!")
end
are exactly equivalent (in fact, the first gets translated into the second when Lua compiles it)

There's something interesting about step 4 though. We're not simply calling a function. We're giving the location of the function with the "test" token,
You are reading the value from the variable "test", and calling it. There is nothing special about variables that happen to contain functions.



function myPrint(msg)
	-- calculate condition
	condition = conditionFindingFunction()
	
	-- compare to see if printing is allowed
	if condition then
		--call the backup of print() that get's made before this function is called
		printBackup(msg)
	end
end

-- backup print to printBackup so that we don't lose the original print
printBackup = print

-- replace print with myPrint
print = myPrint
print("Testing!")
If printBackup was local (to the file, or some other scope) then people couldn't just call printBackup to get around your restriction.
For some reason when calling a function stored in a table, you have to use a colon instead of a period before the key. I think it's because a period would ask for the value (for a function, the location of the function).

Wrong. Because functions are just values, you can get a function from a table the same way you get any other element in the table. window.print(msg) works fine, if the window object is programmed to work that way. However, in Lua it's very common for functions in tables to have the table itself as the first parameter, as there's no implicit "this" parameter like in Java.
window:print("hi") is just a shortcut for writing window.print(window, msg).

I wonder if any functions you call from your program also get the local variables, something to investigate because if they do then if you call read() etc it will be using the local pullevent as well and should prevent termination, not sure
I'm guessing you mean something like

local os.pullEvent = os.pullEventRaw
Not only will that not work, it's a syntax error.

Yes after i wrote this tutorial i discovered why the colon is required for some things. I'll go ahead and update it. And

local os.pullEvent = os.pullEventRaw
is actually a syntax error. The new local os table had never been declared as a table so you can't index it with .pullEvent.

Also, i understand that functions are first class objects, but that's just a fancy term for object with a pointer, which is a reference to a spot in memory that holds the actual object. My wording was less technical, but that was on purpose because not everyone is a programming pro.
Cloudy #25
Posted 17 August 2012 - 09:18 PM
ah I know, you cannot apply a local change to a specific table value, what you do is simply


local os=os --making a local table that you can now modify
os.pullEvent=os.pullEventRaw

and that should do it

Except that won't work. You'll be creating a local called os that is a reference to the real os - meaning changes that happen to the local happen to the main table.
KaoS #26
Posted 17 August 2012 - 09:58 PM
Whoah, that is the wierdest thing I have ever seen, I thought that if you declare a variable (like os), then set another variable to be equal to the first (like temp or whatever)
, then change temp os will remain the same as it is not equal to temp. it is the other way around…

in variables this is correct but in tables if you do the following

tableone={"01","02","03","04"}
tabletwo=tableone
tabletwo[3]=nil
then tableone[3] becomes nil as well…. all I have to say is wtf… the only way to change this is manually going through each value

local temp={}
for k in pairs(os) do
temp[k]=os[k]
end
local os=temp
temp=nil
then finally all changes made to os during the function are local only

I have no explanation for the first observation :(/>/> but atleast have finally worked out how to do it :)/>/> I tested it so I know it works
immibis #27
Posted 18 August 2012 - 01:02 PM
How is that a syntax error?
Because os.pullEvent is not a valid identifer (as it contains a dot)

ah I know, you cannot apply a local change to a specific table value, what you do is simply


local os=os --making a local table that you can now modify
os.pullEvent=os.pullEventRaw

and that should do it

That makes "os" local, so if you do os=something then the change is not visible outside your program.
ElvishJerricco #28
Posted 18 August 2012 - 08:03 PM
How is that a syntax error?
Because os.pullEvent is not a valid identifer (as it contains a dot)

ah I know, you cannot apply a local change to a specific table value, what you do is simply


local os=os --making a local table that you can now modify
os.pullEvent=os.pullEventRaw

and that should do it

That makes "os" local, so if you do os=something then the change is not visible outside your program.

You are not correct on either account. The reason it's a syntax error is because the local table "os" hasn't been declared as a table yet, so you can't use dot notation yet. And local os = os will not make os local. It'll give you a reference to the real os stored on the system. It doesn't copy it or anything. so local os = os; os.pullEvent = os.pullEventRaw will work EXACTLY as if you were using the regular table. Tables variables are references to objects in memory, so to set one table to another is to provide a reference. For example:


t = {"one", "two", "three"}


t2 = t

print(t2[1])

This will output "one", because t2 is now a reference to the same object as t. Similarly,


t = {"one", "two", "three"}

t2 = t

t2[4] = "four"

print(t[4])

should output "four" because you're using t2 to set the table's 4 value to "four", then referencing the same table to get the value of 4
immibis #29
Posted 19 August 2012 - 01:06 PM
You are not correct on either account. The reason it's a syntax error is because the local table "os" hasn't been declared as a table yet, so you can't use dot notation yet. And local os = os will not make os local. It'll give you a reference to the real os stored on the system. It doesn't copy it or anything. so local os = os; os.pullEvent = os.pullEventRaw will work EXACTLY as if you were using the regular table. Tables variables are references to objects in memory, so to set one table to another is to provide a reference. For example:

I think I know how pointers/references work. "local os = os" makes a local variable called os that points to the same table as os did before, and changing its contents will change the contents of the global os table. I simply pointed out that "os=5" would not set the global os table to 5 after that.

And the reason "local os.pullEvent = os.pullEventRaw" is a syntax error is because os.pullEvent is not a valid identifier (or Name, as the reference manual defines it)
Syntax of a local declaration: stat ::= local namelist ['=' explist]
[url=http://www.lua.org/manual/5.1/manual.html#2.4.5]Definition of a namelist[/url] (about halfway through the section): namelist ::= Name {',' Name}
Definition of a Name

"os.pullEvent" is not a valid name, so "os.pullEvent" is not a valid namelist, so "local os.pullEvent = os.pullEventRaw" is not a valid local declaration (noting that "os.pullEventRaw" is a valid explist)
KaoS #30
Posted 24 August 2012 - 12:12 PM
@ElvishJerrico Yes, I understand that now, the variable used to access a table is not the table itself, merely a reference to an object that is actually the table, that is why this works


Whoah, that is the wierdest thing I have ever seen, I thought that if you declare a variable (like os), then set another variable to be equal to the first (like temp or whatever)
, then change temp os will remain the same as it is not equal to temp. it is the other way around…

in variables this is correct but in tables if you do the following

tableone={"01","02","03","04"}
tabletwo=tableone
tabletwo[3]=nil
then tableone[3] becomes nil as well…. all I have to say is wtf… the only way to change this is manually going through each value

local temp={}
for k in pairs(os) do
temp[k]=os[k]
end
local os=temp
temp=nil
then finally all changes made to os during the function are local only

I have no explanation for the first observation but atleast have finally worked out how to do it I tested it so I know it works
ElvishJerricco #31
Posted 24 August 2012 - 07:53 PM
You are not correct on either account. The reason it's a syntax error is because the local table "os" hasn't been declared as a table yet, so you can't use dot notation yet. And local os = os will not make os local. It'll give you a reference to the real os stored on the system. It doesn't copy it or anything. so local os = os; os.pullEvent = os.pullEventRaw will work EXACTLY as if you were using the regular table. Tables variables are references to objects in memory, so to set one table to another is to provide a reference. For example:

I think I know how pointers/references work. "local os = os" makes a local variable called os that points to the same table as os did before, and changing its contents will change the contents of the global os table. I simply pointed out that "os=5" would not set the global os table to 5 after that.

And the reason "local os.pullEvent = os.pullEventRaw" is a syntax error is because os.pullEvent is not a valid identifier (or Name, as the reference manual defines it)
Syntax of a local declaration: stat ::= local namelist ['=' explist]
[url="http://www.lua.org/manual/5.1/manual.html#2.4.5"]Definition of a namelist[/url] (about halfway through the section): namelist ::= Name {',' Name}
Definition of a Name

"os.pullEvent" is not a valid name, so "os.pullEvent" is not a valid namelist, so "local os.pullEvent = os.pullEventRaw" is not a valid local declaration (noting that "os.pullEventRaw" is a valid explist)

You said it was because it had a dot in it. You can see where confusion might be drawn from that.
immibis #32
Posted 25 August 2012 - 07:25 AM
You are not correct on either account. The reason it's a syntax error is because the local table "os" hasn't been declared as a table yet, so you can't use dot notation yet. And local os = os will not make os local. It'll give you a reference to the real os stored on the system. It doesn't copy it or anything. so local os = os; os.pullEvent = os.pullEventRaw will work EXACTLY as if you were using the regular table. Tables variables are references to objects in memory, so to set one table to another is to provide a reference. For example:

I think I know how pointers/references work. "local os = os" makes a local variable called os that points to the same table as os did before, and changing its contents will change the contents of the global os table. I simply pointed out that "os=5" would not set the global os table to 5 after that.

And the reason "local os.pullEvent = os.pullEventRaw" is a syntax error is because os.pullEvent is not a valid identifier (or Name, as the reference manual defines it)
Syntax of a local declaration: stat ::= local namelist ['=' explist]
[url="http://www.lua.org/manual/5.1/manual.html#2.4.5"]Definition of a namelist[/url] (about halfway through the section): namelist ::= Name {',' Name}
Definition of a Name

"os.pullEvent" is not a valid name, so "os.pullEvent" is not a valid namelist, so "local os.pullEvent = os.pullEventRaw" is not a valid local declaration (noting that "os.pullEventRaw" is a valid explist)

You said it was because it had a dot in it. You can see where confusion might be drawn from that.
The dot is the reason os.pullEvent is not a valid identifier/Name.
KaoS #33
Posted 25 August 2012 - 07:34 AM
the dot is not the main problem here, the fact is that there is a table, in order to locally make changes to the table we have to make a new local table which cannot be achieved by using

local os=non-local-table
as this still makes a reference to the same table object and makes global changes, in order to do this we have to make a new, identical table that is used in the place of the os table locally by doing this

local temp={}
for k in pairs(os) do
temp[k]=os[k]
end --this is creating a copy table called temp


local os=temp
temp=nil
ElvishJerricco #34
Posted 25 August 2012 - 11:44 PM
You are not correct on either account. The reason it's a syntax error is because the local table "os" hasn't been declared as a table yet, so you can't use dot notation yet. And local os = os will not make os local. It'll give you a reference to the real os stored on the system. It doesn't copy it or anything. so local os = os; os.pullEvent = os.pullEventRaw will work EXACTLY as if you were using the regular table. Tables variables are references to objects in memory, so to set one table to another is to provide a reference. For example:

I think I know how pointers/references work. "local os = os" makes a local variable called os that points to the same table as os did before, and changing its contents will change the contents of the global os table. I simply pointed out that "os=5" would not set the global os table to 5 after that.

And the reason "local os.pullEvent = os.pullEventRaw" is a syntax error is because os.pullEvent is not a valid identifier (or Name, as the reference manual defines it)
Syntax of a local declaration: stat ::= local namelist ['=' explist]
[url="http://www.lua.org/manual/5.1/manual.html#2.4.5"]Definition of a namelist[/url] (about halfway through the section): namelist ::= Name {',' Name}
Definition of a Name

"os.pullEvent" is not a valid name, so "os.pullEvent" is not a valid namelist, so "local os.pullEvent = os.pullEventRaw" is not a valid local declaration (noting that "os.pullEventRaw" is a valid explist)

You said it was because it had a dot in it. You can see where confusion might be drawn from that.
The dot is the reason os.pullEvent is not a valid identifier/Name.

Sorta. The dot makes lua try to interpret the os identifier as a table. Then when it finds out that it's not a table it throws that "attempt to index ?" error.
immibis #35
Posted 26 August 2012 - 06:38 AM
You are not correct on either account. The reason it's a syntax error is because the local table "os" hasn't been declared as a table yet, so you can't use dot notation yet. And local os = os will not make os local. It'll give you a reference to the real os stored on the system. It doesn't copy it or anything. so local os = os; os.pullEvent = os.pullEventRaw will work EXACTLY as if you were using the regular table. Tables variables are references to objects in memory, so to set one table to another is to provide a reference. For example:

I think I know how pointers/references work. "local os = os" makes a local variable called os that points to the same table as os did before, and changing its contents will change the contents of the global os table. I simply pointed out that "os=5" would not set the global os table to 5 after that.

And the reason "local os.pullEvent = os.pullEventRaw" is a syntax error is because os.pullEvent is not a valid identifier (or Name, as the reference manual defines it)
Syntax of a local declaration: stat ::= local namelist ['=' explist]
[url="http://www.lua.org/manual/5.1/manual.html#2.4.5"]Definition of a namelist[/url] (about halfway through the section): namelist ::= Name {',' Name}
Definition of a Name

"os.pullEvent" is not a valid name, so "os.pullEvent" is not a valid namelist, so "local os.pullEvent = os.pullEventRaw" is not a valid local declaration (noting that "os.pullEventRaw" is a valid explist)

You said it was because it had a dot in it. You can see where confusion might be drawn from that.
The dot is the reason os.pullEvent is not a valid identifier/Name.

Sorta. The dot makes lua try to interpret the os identifier as a table. Then when it finds out that it's not a table it throws that "attempt to index ?" error.
I'm referring to the reason "local os.pullEvent=os.pullEvent" has a syntax error…………………………………………………
Hazrock #36
Posted 18 September 2012 - 01:06 AM
This is how I made my first password program, before I knew about pullEvent=pullEventRaw.

I replaced shell.run with:

local loggedIn = false
local run = shell.run
function shell.run(prog, ...)
 if loggedIn or prog=="startup" then
  run(prog,...)
 elseif prog==login then
  pass = ...
  loggedIn = pass==cpass
  if loggedIn then
   print("Logged in!")
  else
   print("Access denied.")
  end
 else
  print("You have to log in!")
 end
end

I set up startup to just open my door, and could ctrl+t out and login with "login <password>" to do anything else :)/>/>
K_Schumacher #37
Posted 18 September 2012 - 06:06 PM
My problem with disabling Ctrl-T with pullEvent=pullEventRaw is that even if you store pullEvent and recall it when program is complete if the program ever crashes or the server shuts down it will be forever disabled. So reading this post made me think of another way. Could you make a local function that is the same as pullEventRaw? What do you think? I'm thinking that way if the program ever ended in a unexpected way you would be covered.
GopherAtl #38
Posted 18 September 2012 - 06:30 PM
if you want a given program you run to ignore ctrl-T, you can use this program I just invented while reading this thread!

unstoppable


--grab command-line params
local args={...}
--create the coroutine
local routine=coroutine.create(function() shell.run(unpack(args)) end)
local e,p1,p2,p3,p4,p5
--run once to get it started
local _,waitingFor=coroutine.resume(routine,e,p1,p2,p3,p4,p5)
while coroutine.status(routine)~="dead"
  --wait for an event
  e,p1,p2,p3,p4,p5=os.pullEventRaw()  
  --if it's the right event...
  if waitingFor==nil or e==waitingFor then
    --resume routine passing in event and params
    _,waitingFor=coroutine.resume(routine,e,p1,p2,p3,p4,p5)
  end
end

usage (at command line):
> unstoppable myprogram <any parameters here>

if the program you call exits or errors out, so will unstoppable, and ctrl-t will be back to normal.
Jajnick #39
Posted 18 September 2012 - 07:15 PM
This article in a nutshell:
Functions are values
I don't really see the point of this wall of text.
Cranium #40
Posted 18 September 2012 - 07:52 PM
This article in a nutshell:
Functions are values
I don't really see the point of this wall of text.
Not everyone knows this. If you don't have anything useful to add, why did you post? Please stop flaming, and go be helpful somewhere.
KaoS #41
Posted 19 September 2012 - 06:24 AM
Spoiler
This article in a nutshell:
Functions are values
I don't really see the point of this wall of text.
Not everyone knows this. If you don't have anything useful to add, why did you post? Please stop flaming, and go be helpful somewhere.

I must admit to being behind Cranium on this one… please post to help or ask, that is pretty much the point of the forum
Jajnick #42
Posted 19 September 2012 - 10:46 AM
Uh… Did I say something wrong?
I'm just asking why the original post is so long, while it can be shortened and be even more understandable… Why are you raging at me?
KaoS #43
Posted 19 September 2012 - 10:57 AM
This article in a nutshell:
Functions are values
I don't really see the point of this wall of text.

I apologise if I offended you, we were just looking at various methods of replacing existing functions, most functions are stored in a table and I wanted to be able to locally change a function, not globally. this caused multiple issues, I have worked it out now though. that was the point for me, not sure about the other people. what I was saying is that these forums are here so we can assist each other in learning CC and showing others what we have learned. please try to avoid criticising other threads. that was pretty much my point
ElvishJerricco #44
Posted 05 October 2012 - 08:32 PM
Uh… Did I say something wrong?
I'm just asking why the original post is so long, while it can be shortened and be even more understandable… Why are you raging at me?

Sorry about being late to this conversation…

The reason it's so long is to show examples and give a very clear understanding of the concept. In most compiled languages, functions and methods aren't values in the same way they are in lua. It's a concept that's weird to grasp coming from C or Java. But lua is a scripting language, and in most scripting languages functions work the way they do here. If i told a novice lua programmer that functions are values and said the symbols can be redefined, they wouldn't really know what to do with it. The tutorials forum is meant to have deep explanation of each topic. That's what i tried to do.
FunshineX #45
Posted 05 October 2012 - 10:18 PM
Be careful when you replace functions and save a backup pointer. If you run the program multiple times you'll get unexpected results. Best to os.reboot() in between to restore the originals
ElvishJerricco #46
Posted 06 October 2012 - 05:43 AM
Be careful when you replace functions and save a backup pointer. If you run the program multiple times you'll get unexpected results. Best to os.reboot() in between to restore the originals

Where would the issue arise? Only thing I can think of is if you don't check to see if someFunction already equals myReplacementFunction, then when you do the replacement again it ends up replacement calling old (identical besides the pointer to the function) replacement calling original. And yea I guess that's a pretty big problem…
FunshineX #47
Posted 07 October 2012 - 09:00 AM
Where would the issue arise? Only thing I can think of is if you don't check to see if someFunction already equals myReplacementFunction, then when you do the replacement again it ends up replacement calling old (identical besides the pointer to the function) replacement calling original. And yea I guess that's a pretty big problem…

The 4th block of code in the original post suffers from this
Tiin57 #48
Posted 07 October 2012 - 09:39 AM
As long as this post is revived, good job, ElvishJerrico.
ElvishJerricco #49
Posted 08 October 2012 - 06:56 PM
Where would the issue arise? Only thing I can think of is if you don't check to see if someFunction already equals myReplacementFunction, then when you do the replacement again it ends up replacement calling old (identical besides the pointer to the function) replacement calling original. And yea I guess that's a pretty big problem…

The 4th block of code in the original post suffers from this

In that case it's actually intentional. You'll notice I said that I'd only do that code if I were writing my own OS, where I want to manage all the Apis and I want to control the print statement for all of runtime. The replacement should only get called once for the entire lifetime of the computer from bootup to shutdown

As long as this post is revived, good job, ElvishJerrico.

Thanks :3
Cabu #50
Posted 21 November 2012 - 04:02 AM
Hi,

I am new to lua and computercraft, but i am writing an API for advanced turtle functions like turnTo(direction) and moveTo(X, Y, Z, direction). They work pretty well now. But i want to integrate my API deeper into the system.

For now to keep track of the turtle position, I read its GPS position once and the api user should not use the turtle.forward() / turtle.back() / turtle.up() / turtle.down() / turtle.turnLeft() / turtle.turnRight() anymore but the my API equivalents one.

I would like to override the turtle functions by my own, but i still need in my own function be able to call the original function and the local variable of my API.

Here is an extract of my API:

-- Cache of the current turtle position and direction
local cachedX, cachedY, cachedZ, cachedDir
-- Directions
North, West, South, East, Up, Down = 0, 1, 2, 3, 4, 5
local deltas = {[North] = {0, 0, -1}, [West] = {-1, 0, 0}, [South] = {0, 0, 1}, [East] = {1, 0, 0}, [Up] = {0, 1, 0}, [Down] = {0, -1, 0}}
-- cache world geometry
local cachedWorld = {}
...
function forward()
  local D = deltas[cachedDir]
  local x, y, z = cachedX + D[1], cachedY + D[2], cachedZ + D[3]
  local idx_pos = x..":"..y..":"..z

  if turtle.forward() then
	cachedX, cachedY, cachedZ = x, y, z -- update the position cache
	cachedWorld[idx_pos] = 0 -- clear the world cache
	return true
  else
	cachedWorld[idx_pos] = (turtle.detect() and 1 or 0.5)  -- update the world cache
	return false
  end
end
...

An you use it like that:

os.loadAPI("egps")
if egps.startGPS() then
  local x, y, z, d = egps.locate()
  print(x, ' ', y, ' ', z, ' ', d)
  egps.forward()
  x, y, z, d = egps.cachedLocate()
  print(x, ' ', y, ' ', z, ' ', d)
end
louitzie #51
Posted 22 November 2012 - 10:09 AM
for my new os I want to override shell.run globaly so i can apply the right display settings depending on my own programs or default (rom) programs

tried code


local run=shell.run
_G.shell.run=function(command,...)
--configuration code here
return run(command,...)

i also tried without the _G

but it only changes localy.

any ideas are welcome.

found it.

if you ever want to do this override os.run instead
Orwell #52
Posted 22 November 2012 - 11:25 AM
for my new os I want to override shell.run globaly so i can apply the right display settings depending on my own programs or default (rom) programs

tried code


local run=shell.run
_G.shell.run=function(command,...)
--configuration code here
return run(command,...)

i also tried without the _G

but it only changes localy.

any ideas are welcome.

found it.

if you ever want to do this override os.run instead
You're missing an end. ;)/>/> And shell.run overrides the global environment with an empty one (except for a reference to the parent shell). Maybe that's your problem?
Expenox #53
Posted 25 November 2012 - 11:23 AM
Thank you for this! :D/>/>
CoolisTheName007 #54
Posted 25 November 2012 - 03:06 PM
snip]
do something like

old_turtle=turtle
turtle={}
turtle.forward= function (args)
stuff
-here you want to go forward, so you do:
  old_turtle.forward()
ect

Also, for the main discussion of altering code locally, I would arg that just replacing functions is incomplete, because internal functions in the API won't refer to the new one, but to the one that is still in the environment. A complete solution would be a dofile and tampering with the environment directly.
KaoS #55
Posted 25 November 2012 - 10:44 PM
also: I should mention that you should be careful with stuff like this because sometimes you will run your program twice and it will double the code on the turtle commands. here is an example to explain


local oldT=turtle
turtle.forward=function(...)
  print('whoah! you just moved forwards!')
  return oldT.forward(...) --you want to always return whatever the oldT command would do so you still get back the output of the move CMD
end

then if you say turtle.forward() it would move forward and output 'whoah! you just moved forwards!', then if you ran the above code again it would take the current turtle.forward() (including the print bit) and append another print onto it, then if you move forward it will print 'whoah! you just moved forwards!' twice which of course is a problem, the way to get around this is to make a variable recording if you have changed the command and prevent it from modifying it again


if not mod then
  local oldT=turtle
  mod=true
  turtle.forward=function(...)
    print('whoah! you just moved forwards!')
    return oldT.forward(...) --you want to always return whatever the oldT command would do so you still get back the output of the move CMD
  end
end

even easier is just to use the oldT table


if not oldT then
  oldT=turtle
  turtle.forward=function(...)
    print('whoah! you just moved forwards!')
    return oldT.forward(...) --you want to always return whatever the oldT command would do so you still get back the output of the move CMD
  end
end

some would call that un-neat as the oldT table is no longer local but I find it useful in case you need to use the original commands
CoolisTheName007 #56
Posted 25 November 2012 - 11:56 PM
snip
You can save oldT/mod in the turtle table; that way, everything is still contained and os.loadAPI, which always reloads the API, won't result in problems:

if not turtle.old then
   local old=turtle
   turtle={}
   turtle.old=old
--modifications
end
KaoS #57
Posted 26 November 2012 - 02:19 AM
AHAH, and behold I present unto you unparalleled genius. thanks for that idea CoolisTheName007, just remember not to do that is you are going to use a for loop to parse the turtle table and replace all functions unless you are careful and include a type(v)=='function' in it
CoolisTheName007 #58
Posted 26 November 2012 - 03:16 AM
snip
Your previous code does not work, in fact it creates an infinite loop, because you never create a new table for turtle and consequently oldT.forward==turtle.forward.
When using an old table , if you don't want to copy the API manually you may be interested in a deepcopy function. However, those often leave details such as metatables out, so I would rather use a replace on necessity method, such as
old_forward=turtle.forward
turtle.forward=function …
KaoS #59
Posted 26 November 2012 - 03:22 AM
yes, you are most correct…. an unforgivably n00b mistake on my side. don't forget to create a new table for turtle guys, I'm gonna go end my stupid misery lol, can't believe I just did that… when it comes to copying the other functions I would just use a metatable like so

turtle=setmetatable({},{__index=oldT})
ChunLing #60
Posted 30 November 2012 - 12:36 PM
Why not just check if oldT already exists, so "if not oldT then oldT=turtle …andotherstuff… end". Also, that way oldT is global so you don't lose it by going out of scope/unexpected termination.

No, nevermind, I see you already discussed all that.
Edited on 30 November 2012 - 11:40 AM