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

Rednet Messages

Started by OniNoSeishin, 09 February 2012 - 02:41 PM
OniNoSeishin #1
Posted 09 February 2012 - 03:41 PM
I've a computer which constantly receives/sends rednet messages, but i'd like to be able to use it as a terminal too (in my script, with my personal commands).
Is multithread the only solution?
If yes, can you explain me the basic commands for lua?

Thanks
Espen #2
Posted 09 February 2012 - 04:06 PM
If you're using read() for input then program execution is suspended until you press enter.
So in order for any events to have an influence even during a user inputting data, you can't use read()

But there are a couple of ways to achieve this.
  • You could write your own input method based on key/char events instead of using read(). Depending on the desired versatility of your text input that can be quite an undertaking though.
  • You could your event loop (which also handles your desired rednet events) only then ask for user input via read() if a certain key is pressed. That's a pretty quick and easy way of doing it. But the drawback here is that you have to make sure you actually input something, i.e. press ENTER, or else your code will not continue.
  • Then there's the parallels api (type "help parallel"). The help isn't really 'comprehensive', though.^^
I'd suggest using parallels (or coroutine directly).
Since it's built on top of the coroutine functions of LUA you can read up on them first to understand the basic functionality:
http://www.lua.org/pil/9.html

Then take a look at the source of the parallels api, located here:
.minecraft\mods\ComputerCraft\lua\rom\apis\parallel

K, hope it helps, Cheers! ;)/>/>
OniNoSeishin #3
Posted 09 February 2012 - 04:44 PM
I'm already using my own input method (based on char/key/rednet_message, as you said).
I also tried the 'parallel' api with a simple test script, but it doesn't execute functions concurrently. It executes first one, THEN the other.

I could make it working as you said in your second point, but i first need some info:
- what happens if the computer receives a message while i'm writing to the console? The message sits in a 'pipe' and cause a rednet_message event when i press enter or it get trashed?
- what happens if the computer receives 2 different messages at the same time (or the second one while receiving the first one)?

basicly, are messages sitting in a 'pipe', so if the script is busy doing things, it receives the message when it finish, or not?
I've looked inside rednet api, but i'm not so good with lua to understand how it works.

Thanks for your help ;)/>/>
Casper7526 #4
Posted 09 February 2012 - 04:49 PM
You cannot do 2 things at once ;)/>/>
There is no possible way to make 2 pieces of code run at the exact same time, you can only emulate two sections of code being ran at the same time using co-routines (but they just bounce back and forth)
Advert #5
Posted 09 February 2012 - 05:42 PM
I think the events are queued, and will fire one after the other as they arrive.

IIRC, rednet has collision detection, and will only allow one machine to send at a time.
If you have multiple bundles connected, then you can receive two at the same time, but the one that fires first will be arbitrary.


I think the code would look something like:


-- put local breakMainLoop = false at the top, above your functions, set it to true and return if you want to break the loop.
while true do
 if breakMainLoop then break end
 local event, p1, p2 = os.pullEvent()
 if event == "char" then
  yourCharEventFunction(p1, p2)
 elseif event == "rednet" then -- may be rong
  yourRedNetEventFunction(p1, p2)
 end
end

This should in theory work fine, assuming you make the functions.
OniNoSeishin #6
Posted 09 February 2012 - 07:48 PM
I think the events are queued, and will fire one after the other as they arrive.

IIRC, rednet has collision detection, and will only allow one machine to send at a time.
If you have multiple bundles connected, then you can receive two at the same time, but the one that fires first will be arbitrary.

only one machine at a time is allowed to send message, ok.

hypotetical scenario:
2+ clients are connected (through the same bundled cable) to a server. Clients send a message simultaneously to the server which is scripted like this:

while true do
  event, arg1 arg2 = os.pullEvent()
  if event == "rednet_message" then
    doSomething(arg1, arg2) --function
  end
end

are all the messages completely delivered to the server? The order is arbitrary, right (that's not a problem to me)?
Advert #7
Posted 09 February 2012 - 08:59 PM
I think the events are queued, and will fire one after the other as they arrive.

IIRC, rednet has collision detection, and will only allow one machine to send at a time.
If you have multiple bundles connected, then you can receive two at the same time, but the one that fires first will be arbitrary.

only one machine at a time is allowed to send message, ok.

hypotetical scenario:
2+ clients are connected (through the same bundled cable) to a server. Clients send a message simultaneously to the server which is scripted like this:

while true do
  event, arg1 arg2 = os.pullEvent()
  if event == "rednet_message" then
	doSomething(arg1, arg2) --function
  end
end

are all the messages completely delivered to the server? The order is arbitrary, right (that's not a problem to me)?

Hmm, I just tested this but to no avail, the red color keeps hanging. My setup is 3 computers running this code, with two of them set to listen (run the program then hit "l")



rednet.open("back")

os.startTimer(0.05)
local listen = false
while true do
 local event, p1, p2 = os.pullEvent()
 if event == "timer" then
  os.startTimer(0.05)
 else
  print("Event: " .. event, " p1: ", p1, " p2: ", p2)
 end
 if event == "rednet_message" then
  print(string.format("Recieved rednet message from %n, message: %s", p1, p2))
  if p2 == "start" and listen then
   print("Sleeping for 10 seconds, then sending message...")
   print(os.clock()) -- Both computers should show the same #
   sleep(10) -- Both machines should recieve this at the same tick;
   rednet.broadcast("I am computer #" .. os.computerID())
  end
 end
 if event == "char" then
  if p1 == "a" then
   rednet.announce()
  elseif p1 == "b" then
   print("broadcasting!")
   rednet.broadcast("start")
  elseif ep1 == "l" then
   listen = true
   print("Listen ON")
  end
 end
end

I'm not quite sure what's causing this, it could be faulty collision handling ;)/>/>
OniNoSeishin #8
Posted 09 February 2012 - 10:14 PM
i tested your code, pressing 'l' on 2 terminals and 'b' on the third
the two listening get the message, but don't execute the broadcast:

if p2 == "start" and listen then
   print("Sleeping for 10 seconds, then sending message...")
   print(os.clock()) -- Both computers should show the same #
   sleep(10) -- Both machines should recieve this at the same tick;
   rednet.broadcast("I am computer #" .. os.computerID())
  end

donnow why…

anyone else can help?
Espen #9
Posted 09 February 2012 - 10:57 PM
I'm not quite sure what's causing this, it could be faulty collision handling :)/>/>
Hey Advert, I didn't yet look deeply into the program logic, but I looked into the problem you were having where your program seems to keep hanging.

During the processing of the rednet_message event your timer runs out.
That means the next time event will not equal "timer" anymore and the else will always kick in, effectively preventing any further events from being processed.

EDIT: Pardon me, but that was not completely right. The else will always kick in, regardless of any other event.^^

EDIT 2: I'm sorry but please ignore the whole post for now, because it's wrong.
It seems I need to take a deeper look at the code and actually try it out myself.
I won't do it today anymore though, but I definitely will tomorrow.

Note to self: Don't post when you're too tired. ;)/>/>
Edited on 09 February 2012 - 11:13 PM
OniNoSeishin #10
Posted 09 February 2012 - 11:52 PM
EDIT: Pardon me, but that was not completely right. The else will always kick in, regardless of any other event.^^

ok, but after the 'else' there are other 'if', in fact it reach 'print(string.format("Recieved rednet message from %n, message: %s", p1, p2))' but stops just after it

i also tried removing everything about the timer
Espen #11
Posted 10 February 2012 - 12:08 AM
EDIT: Pardon me, but that was not completely right. The else will always kick in, regardless of any other event.^^
ok, but after the 'else' there are other 'if', in fact it reach 'print(string.format("Recieved rednet message from %n, message: %s", p1, p2))' but stops just after it i also tried removing everything about the timer
Omg, you're completely right. I confused myself heavily it seems. That means my original post was indeed correct and I overcorrected myself.
Gnah, I should really get some sleep. Editing my last post, again… ^^

Thx for the keen eyes, OniNoSeishin. ;)/>/>
Casper7526 #12
Posted 10 February 2012 - 12:38 AM
Have you tried running my debugger on it? My debugger works pretty well with programs that have issues of not going where they are suppose to.

If my debugger loads up your program (it should hopefully), simply run your program for as long as you can then terminate it or stop it or whatever.

then check out the debugtraceback file, it will tell you EXACTLY where your code start and ended including every line inbetween.
Espen #13
Posted 10 February 2012 - 02:57 AM
Alright, I've experimented a bit with your code, Advert.
One thing beforehand: You had a little typo in your code if you didn't notice already: ep1 instead of p1.

To the main problem:
When using only one listener, then everything would work fine.
That is: 10 seconds after the sender broadcasts "start" the listening computer would send back its ID, which the sender would receive.
But as soon as I used more than one listener, things didn't work as they were supposed to.

First of all, I only ever got one reply, which seemed to always come from the same computer.
But it showed me an ID that couldn't possibly be.
Now, I did suspect from the beginning, that there might be the possibility of crosstalk over the bundled cable, because of how the rednet api sends messages across the cable.
So I made a quick test to confirm it, just to be sure (and get a peace of mind^^), and set up the following:

LISTENER PC:
rednet.open("back")
rednet.receive(50)
CROSSTALKER PC:
while true do
  rs.setBundledOutput("back", 3)
  sleep(0.05)
  rs.setBundledOutput("back", 0)
end
SENDER PC:
rednet.open("back")
rednet.broadcast("hello")
On the listener I then always received something different on each run.
It always resembled something like "?kekhello".
I guess if you'd randomize the output values of the crosstalker you would garble the message completely.

So it definitely seems that you can't have more than one computer send a message over the wire at the same time. At least not this way.
One would have to implement a protocol to queue the messages, etc.
But I feel there's some easier way I'm just not seeing at the moment.
My brain feels like mush, I really have to get some sleep now!

Good night & take care! ;)/>/>
Casper7526 #14
Posted 10 February 2012 - 03:46 AM
Nope, you can't send 2 messages on the same wire at the same time. Wait for 1.3 and wifi ;)/>/>
OniNoSeishin #15
Posted 10 February 2012 - 04:19 AM
i saw the typo but i was too sleepy to test (it's 5.20 am here), so thanks Espen for testing ;)/>/>

thanks Casper too for letting me know that wifi will solve this issue. Seems i just have to wait.
FuzzyPurp #16
Posted 11 February 2012 - 01:28 AM
Lordy lordy 1.3 hi-speed modems…you should see how fast this shit flies.
Espen #17
Posted 11 February 2012 - 01:56 AM
Lordy lordy 1.3 hi-speed modems…you should see how fast this shit flies.
Aw that's not fair, stop teasing us! :D/>/>


EDIT:
Ok, I've taken another look at the possibility of running rednet concurrently to other code.
I could remember that when one exited the shell from within one's program, then the program would still continue to run and as soon as the program ended, then the computer would be shut down, because there was no shell to return to anymore.
Thinking about that fact and this thread I came to think that it should somehow be possible to run your rednet event listener in the background of any read() or other method of input that seemingly "stops" execution.
So I looked a little deeper into the "bios.lua" to see how the shell is started and there you'll find this piece of code:

-- Run the shell
local ok, err = pcall( function()
    parallel.waitForAny(
        function()
            rednet.run()
        end,
        function()
            os.run( {}, "rom/programs/shell" )
        end
    )
end )
if not ok then
    print( err )
end
As you can see, both rednet.run, as well as the shell program are run in "parallel".
That means you should be able to to the same in your code using the parallel api.

using the code from "bios.lua" I made a little test-run:

function cmdInput()
    term.clear()
    term.setCursorPos( 1, 2 )
    while true do
	    write(">: ")
	    local input = read()
	    if input == "hi" then print("Hi yourself!") end
	    if ( string.lower( input ) == "quit" ) or ( string.lower( input ) == "exit" ) then break end
    end
end

function rednetListener( side )
    rednet.open( side )
    
    while true do
	    local event, id, message = os.pullEvent()
	    
	    if event == "rednet_message" then
		    local currX, currY = term.getCursorPos()    -- Note current cursor position.
		    term.setCursorPos( 1, 1 )
		    term.clearLine()
		    write( "Message from ID#"..id..": "..message )
		    term.setCursorPos( currX, currY )   -- Reset cursor position to where it was before.
		    rs.setOutput( "top", true )
		    sleep(1)
		    rs.setOutput( "top", false )
	    end
	    --if event == "char" and string.lower( id ) == "q" then
		    --rednet.close( side )
		    --break
	    --end
    end
end

local ok, err = pcall( function()
    parallel.waitForAny(
        function()
            rednetListener( "back" )
        end,
        function()
            cmdInput()
        end
    )
end )
if not ok then
    print( err )
end
If you execute this program, then you will be presented with a text input, which is running from within the function cmdInput.
But in the background the function rednetListener is listening to events and will react on 'rednet_message', even if you still haven't entered anything into the read() yet.
In the example above the program will write any incoming rednet message from the "back" of the computer and print it into the first line.

As far as I was able to test it from within SSP, it does work.
Give it a try and see if it's working for you.

Oh, and if you're wondering why I have this in the code:
rs.setOutput( "top", true )
I had a lamp on top of the computer and wanted to see ingame if the incoming rednet events are indeed being processed the moment the event fires, and not after I produced another event by interacting with the computer.

Hope it works for you!
Cheers ^_^/>/>
Edited on 12 February 2012 - 05:11 PM