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

[Lua][Red Net][Programming] Red Net Question

Started by EmTeaKay, 21 June 2012 - 12:59 AM
EmTeaKay #1
Posted 21 June 2012 - 02:59 AM
How can I type a message and check for a rednet message at the same time?
BigSHinyToys #2
Posted 21 June 2012 - 09:52 AM
it is possible using coroutines here is an example of a simple send receive program. just type the message you want to send and it will send it for you. if you leave the to : section blank it will broadcast.

(THIS has only been tested on off line) Please report any bugs to me and i will do what i can to fix them.
Spoiler

function readADV( _sReplaceChar, _tHistory ) -- slightly modified read function credit to dan200 for original
    term.setCursorBlink( true )
    local sLine = ""
    local nHistoryPos = nil
    local nPos = 0
    if _sReplaceChar then
	    _sReplaceChar = string.sub( _sReplaceChar, 1, 1 )
    end
   
    local w, h = term.getSize()
    local sx, sy = term.getCursorPos()   
    local function redraw()
	    local nScroll = 0
	    if sx + nPos >= w then
		    nScroll = (sx + nPos) - w
	    end
		   
	    term.setCursorPos( sx, sy )
	    term.write( string.rep(" ", w - sx + 1) )
	    term.setCursorPos( sx, sy )
	    if _sReplaceChar then
		    term.write( string.rep(_sReplaceChar, string.len(sLine) - nScroll) )
	    else
		    term.write( string.sub( sLine, nScroll + 1 ) )
	    end
	    term.setCursorPos( sx + nPos - nScroll, sy )
    end
   
    while true do
	    local sEvent, param = os.pullEvent()
	    if sEvent == "char" then
		    sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
		    nPos = nPos + 1
		    redraw()
		   
	    elseif sEvent == "key" then
		    if param == 28 then
			    -- Enter
			    break
			   
		    elseif param == 203 then
			    -- Left
			    if nPos > 0 then
				    nPos = nPos - 1
				    redraw()
			    end
			   
		    elseif param == 205 then
			    -- Right			   
			    if nPos < string.len(sLine) then
				    nPos = nPos + 1
				    redraw()
			    end
		   
		    elseif param == 200 or param == 208 then
			    -- Up or down
			    if _tHistory then
				    if param == 200 then
					    -- Up
					    if nHistoryPos == nil then
						    if #_tHistory > 0 then
							    nHistoryPos = #_tHistory
						    end
					    elseif nHistoryPos > 1 then
						    nHistoryPos = nHistoryPos - 1
					    end
				    else
					    -- Down
					    if nHistoryPos == #_tHistory then
						    nHistoryPos = nil
					    elseif nHistoryPos ~= nil then
						    nHistoryPos = nHistoryPos + 1
					    end					   
				    end
				   
				    if nHistoryPos then
					    sLine = _tHistory[nHistoryPos]
					    nPos = string.len( sLine )
				    else
					    sLine = ""
					    nPos = 0
				    end
				    redraw()
			    end
		    elseif param == 14 then
			    -- Backspace
			    if nPos > 0 then
				    sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 )
				    nPos = nPos - 1				   
				    redraw()
			    end
		    end
	    else
		    redraw()
	    end
    end
    term.setCursorBlink( false )
    term.setCursorPos( w + 1, sy )
    return sLine
end
local function writer()
    while true do
	    coroutine.yield()
	    term.setCursorPos(1,1)
	    term.clearLine()
	    if stat == "to" then
		    write("To  :")
	    elseif stat == "mes" then
		    write("Mes :")
	    end
    end
end
function writer2()
    term.setCursorPos(1,1)
    term.clearLine()
    if stat == "to" then
	    write("To  :")
    elseif stat == "mes" then
	    write("Mes :")
    end
end
local function send()
    while true do
	    local sizX,sizY = term.getSize()
	    term.setCursorPos(6,1)
	    stat = "to"
	    writer2()
	    local id = tonumber(readADV())
	    term.setCursorPos(6,1)
	    stat = "mes"
	    writer2()
	    local message = readADV()
	    rednet.send(id,message)
    end
end
local function recive()
    local lastX,lastY = 1,2
    while true do
	    term.setCursorBlink( true )
	    local event = {coroutine.yield()}
	    term.setCursorBlink( false )
	    if event[1] == "rednet_message" then
		    local sizX,sizY = term.getSize()
		    term.setCursorPos(1,lastY)
		    print("Frm: "..event[2].." Dist: "..event[4].."M Mes: "..event[3])
		    lastX,lastY = term.getCursorPos()
	    end
    end
end
local function openRednet()
    local listOfSides = rs.getSides()
    for i = 1,6 do
	    if peripheral.isPresent(listOfSides[i]) and peripheral.getType(listOfSides[i]) == "modem" then
		    rednet.open(listOfSides[i])
		    return listOfSides[i]
	    end
    end
end
modemOn = openRednet()
if modemOn == nil then
    print("No WIFI Modem")
    error()
else
    print("Opened wifi on "..modemOn.." side")
end
term.clear()
term.setCursorPos(1,1)
local stat = nil
local reciveHandel = coroutine.create(recive)
local writerHandel = coroutine.create(writer)
local sendHandel = coroutine.create(send)
while true do -- start a loop
    local e,e1,e2,e3,e4,e5 = os.pullEvent()
    coroutine.resume(reciveHandel,e,e1,e2,e3,e4,e5)
    coroutine.resume(writerHandel)
    coroutine.resume(sendHandel,e,e1,e2,e3,e4,e5)
end
EmTeaKay #3
Posted 21 June 2012 - 02:27 PM
Wow! That's big and long. That's what I wanted, but not that long. So I'm not going to use that. But, it looks excellent.
tfoote #4
Posted 21 June 2012 - 02:50 PM
Looks Great! I will have to use it. EmTeaKay, That length is nothing when you get into servers, FTP, Chat, E-mail…
BigSHinyToys #5
Posted 21 June 2012 - 02:58 PM
Wow! That's big and long. That's what I wanted, but not that long. So I'm not going to use that. But, it looks excellent.
There is not a lot I can do to shorten it. It needs the custom readADV() script to function correctly. I have posted it on http://pastebin.com/ here is the link. http://pastebin.com/EP6TyqtV

in computer craft copy past this in. if the http api is active your computer will automatically download my program to the computer you are using.

pastebin get EP6TyqtV BasicIRC
the main problem is that read() is not designed to work in a coroutine and you need your own ver to make it interruptible (when a message arrives.)


Looks Great! I will have to use it. EmTeaKay, That length is nothing when you get into servers, FTP, Chat, E-mail…
Thanks.
BigSHinyToys #6
Posted 21 June 2012 - 05:09 PM
I have timed some lines that are not requited (sort of)

IRC lite
Spoiler

function readADV() -- slightly modified read function credit to dan200 for original
    term.setCursorBlink( true )
    local sLine = ""
    local nPos = 0
    local w, h = term.getSize()
    local sx, sy = term.getCursorPos()   
    local function redraw()
	    local nScroll = 0
	    if sx + nPos >= w then
		    nScroll = (sx + nPos) - w
	    end
		   
	    term.setCursorPos( sx, sy )
	    term.write( string.rep(" ", w - sx + 1) )
	    term.setCursorPos( sx, sy )
	    term.write( string.sub( sLine, nScroll + 1 ) )
	    term.setCursorPos( sx + nPos - nScroll, sy )
    end
   
    while true do
	    local sEvent, param = os.pullEvent()
	    if sEvent == "char" then
		    sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
		    nPos = nPos + 1
		    redraw()
		   
	    elseif sEvent == "key" then
		    if param == 28 then
			    -- Enter
			    break
			   
		    elseif param == 203 then
			    -- Left
			    if nPos > 0 then
				    nPos = nPos - 1
				    redraw()
			    end
			   
		    elseif param == 205 then
			    -- Right			   
			    if nPos < string.len(sLine) then
				    nPos = nPos + 1
				    redraw()
			    end
			   
		    elseif param == 14 then
			    -- Backspace
			    if nPos > 0 then
				    sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 )
				    nPos = nPos - 1				   
				    redraw()
			    end
		    end
	    else
		    redraw()
	    end
    end
    term.setCursorBlink( false )
    term.setCursorPos( w + 1, sy )
    return sLine
end
local function writer()
    while true do
	    coroutine.yield()
	    writer2()
    end
end
function writer2()
    term.setCursorPos(1,1)
    term.clearLine()
    if stat == "to" then
	    write("To  :")
    elseif stat == "mes" then
	    write("Mes :")
    end
end
local function send()
    while true do
	    local sizX,sizY = term.getSize()
	    term.setCursorPos(6,1)
	    stat = "to"
	    writer2()
	    local id = tonumber(readADV())
	    term.setCursorPos(6,1)
	    stat = "mes"
	    writer2()
	    local message = readADV()
	    rednet.send(id,message)
    end
end
local function recive()
    local lastX,lastY = 1,2
    while true do
	    term.setCursorBlink( true )
	    local event = {coroutine.yield()}
	    term.setCursorBlink( false )
	    if event[1] == "rednet_message" then
		    local sizX,sizY = term.getSize()
		    term.setCursorPos(1,lastY)
		    print("Frm: "..event[2].." Dist: "..event[4].."M Mes: "..event[3])
		    lastX,lastY = term.getCursorPos()
	    end
    end
end
local function openRednet()
    local listOfSides = rs.getSides()
    for i = 1,6 do
	    if peripheral.isPresent(listOfSides[i]) and peripheral.getType(listOfSides[i]) == "modem" then
		    rednet.open(listOfSides[i])
		    return listOfSides[i]
	    end
    end
end
modemOn = openRednet()
if not modemOn then
    print("No WIFI Modem")
    error()
else
    print("Opened wifi on "..modemOn.." side")
end
term.clear()
term.setCursorPos(1,1)
local stat = nil
local reciveHandel = coroutine.create(recive)
local writerHandel = coroutine.create(writer)
local sendHandel = coroutine.create(send)
while true do -- start a loop
    local e,e1,e2,e3,e4,e5 = os.pullEvent()
    coroutine.resume(reciveHandel,e,e1,e2,e3,e4,e5)
    coroutine.resume(writerHandel)
    coroutine.resume(sendHandel,e,e1,e2,e3,e4,e5)
end

that is the smallest I can make if I'm shore that other's could make a shorter ver But for now this is as small as I can make it without sacrificing functionality. I have saved 44 lines from 185 to 141 .
tfoote #7
Posted 21 June 2012 - 05:10 PM
Nice… so what does this do again? i want to know if i can impliment it
BigSHinyToys #8
Posted 21 June 2012 - 05:17 PM
It allows you to send messages over rednet to any user that is running this while being able to receive messages at the same time. the receiving computers id is specified or left blank for broadcasting. It is a simple system.

It does as this request requires.
How can I type a message and check for a rednet message at the same time?

Nice… so what does this do again? i want to know if i can impliment it
As for you implementing it go ahead I release this under no licensing do what ever the hell you like with it.
MysticT #9
Posted 21 June 2012 - 05:57 PM
It would be a lot easier if there were some kind of api to run two functions at the same time…

Oh! wait, there is one: the parallel api.
It would look something like this:

local function send()
  local msg = read()
  rednet.broadcast(msg) -- you can change it to send to an specific id
end

local function receive()
  local id, msg = rednet.receive()
  -- do whatever you want with the message
end

parallel.waitForAny(send, receive)
So good Dan added this to CC :P/>/>
BigSHinyToys #10
Posted 21 June 2012 - 06:12 PM
It would be a lot easier if there were some kind of api to run two functions at the same time…

Oh! wait, there is one: the parallel api.
It would look something like this:

local function send()
  local msg = read()
  rednet.broadcast(msg) -- you can change it to send to an specific id
end

local function receive()
  local id, msg = rednet.receive()
  -- do whatever you want with the message
end

parallel.waitForAny(send, receive)
So good Dan added this to CC :P/>/>

a this only broadcasts
b have you tryed to use read in a coroutine before also what happens if you are mid message and you recive a message. there are many things you are not taking into consideration there.

I would like to expand my programing skill and knowledge could you please post a working system using the above that doesn't cut you off mid message and updates the screen correctly.
MysticT #11
Posted 21 June 2012 - 06:31 PM
That was just an example of how you can use it (actually it would just work to send/receive once, since there's no loop), but it wouldn't be to hard to make it work correctly.
This is a more complete example:

local nW, nH = term.getSize()

local function send()
  while true do
    term.setCursorPos(1, nH - 1)
    term.clearLine()
    local msg = read()
    rednet.broadcast(msg) -- you could change this to rednet.send() and use a predefined id, or make the user input the id.
  end
end

local function receive()
  while true do
    local id, msg = rednet.receive()
    local x, y = term.getCursorPos()
    term.setCursorPos(1, 1) -- write messages on the first line
    print(id, ": ", msg)
    term.setCurorPos(x, y)
  end
end

parallel.waitForAny(send, receive)
There's still some things that would need to be changed, but it should work. It would greatly depend on what you'r trying to do, if you want a chat system, you need to print the messages in the right place, but for other things you might just need to do something for the received message, in that case it's not necesary.
BigSHinyToys #12
Posted 21 June 2012 - 07:18 PM
That was just an example of how you can use it (actually it would just work to send/receive once, since there's no loop), but it wouldn't be to hard to make it work correctly.
This is a more complete example:

local nW, nH = term.getSize()

local function send()
  while true do
	term.setCursorPos(1, nH - 1)
	term.clearLine()
	local msg = read()
	rednet.broadcast(msg) -- you could change this to rednet.send() and use a predefined id, or make the user input the id.
  end
end

local function receive()
  while true do
	local id, msg = rednet.receive()
	local x, y = term.getCursorPos()
	term.setCursorPos(1, 1) -- write messages on the first line
	print(id, ": ", msg)
	term.setCurorPos(x, y)
  end
end

parallel.waitForAny(send, receive)
There's still some things that would need to be changed, but it should work. It would greatly depend on what you'r trying to do, if you want a chat system, you need to print the messages in the right place, but for other things you might just need to do something for the received message, in that case it's not necesary.

He was looking for a program that does what the program I produced does a basic IRC
Wow! That's big and long. That's what I wanted, but not that long. So I'm not going to use that. But, it looks excellent.

so if i add a loop around the "parallel.waitForAny(send, receive)" line the program will run till it hits a time limit on the rednet.receive() function or a message is received or a message is typed and sent. Going by your code if you receive a message or the time limit runs down while in the process of sending the message you were writing would be lost. This is why I chose to use coroutines directly instead of the parallel system witch is based on them anyway.

Could you please try my program and then try to replicate its functionality with parallel function. By my understanding of lua and coroutine it will not work as you expect it to.
MysticT #13
Posted 21 June 2012 - 07:44 PM
Here's an even more complete example:


local nScreenW, nScreenH = term.getSize()
local nLine = 0

local function clear()
  term.clear()
  term.setCursorPos(1, 1)
end

local function connect()
  for _,s in ipairs(rs.getSides()) do
    if peripheral.isPresent(s) and peripheral.getType(s) == "modem" then
      rednet.open(side)
      return true
    end
  end
  return false
end

local function send()
  while true do
    term.setCursorPos(1, nScreenH - 1)
    local msg = read()
    term.setCursorPos(1, nScreenH - 1)
    term.clearLine()
    write("To: ")
    local id = tonumber(read())
    if id then
      rednet.send(id, msg)
    end
  end
end

local function receive()
  while true do
    local id, msg = rednet.receive()
    local x, y = term.getCursorPos()
    if nLine >= nScreenH then
      for i = 1, nScreenH - 2 do
        term.setCursorPos(1, i)
        term.clearLine()
      end
      nLine = 1
    end
    term.setCursorPos(1, nLine)
    nLine = nLine + print(id, ": ", msg)
    term.setCursorPos(x, y)
  end
end

if not connect() then
  print("No modem found")
  return
end

clear()
parallel.waitForAny(send, receive)
Still not perfect, and needs some changes, but I don't have time now to do it.
It's not tested, bit it should let you send and receive messages. The received messages are wrote to a new line. If the screen is full, it clears it and starts writing messages from the start (not the best, but it's the easiest solution I found).
BigSHinyToys #14
Posted 21 June 2012 - 08:28 PM
one bug rednet.open(side) should be rednet.open(s) and text over writes the test you are typing.
I concede it is possible to use parallel.waitForAny() for this but equally it would work with parallel.waitForAll()

this program demonstrates some of the short falls for the reed function. it doesn't redraw a the screen unless you have made a change. so it will not update when test is dumped on top of it. this is why my code is so long as i have to include a modified reed function.

I would like to see a finished polished ver of this code. also I request you try mine to see how I coped with the various problems like text over write.
MysticT #15
Posted 21 June 2012 - 09:20 PM
I concede it is possible to use parallel.waitForAny() for this but equally it would work with parallel.waitForAll()
In this case, it's the same, since none of the functions stop/returns.

this program demonstrates some of the short falls for the reed function. it doesn't redraw a the screen unless you have made a change. so it will not update when test is dumped on top of it. this is why my code is so long as i have to include a modified reed function.
You'r right, read() has some problems when doing something like this, that's why I don't think this is the best way to do a chat program (neither coroutines nor parallel), since all you have to do is handle some events. I made a chat program and it doesn't use coroutines or parallel (wich are basically the same), it's on the program library if you want to take a look at it.
For other things it would be better/easier to use parallel. Mostly when you don't need to use read() or redraw the screen.

I would like to see a finished polished ver of this code. also I request you try mine to see how I coped with the various problems like text over write.
I don't think I will finish that code, since it was just an example, and I don't think it's the best way to do this.
I readed your code, and there's a lot of things that could be improved, and some things that has to be removed. Like:


local function writer()
	while true do
			coroutine.yield()
			writer2()
	end
end
why do you even need that function?
Bossman201 #16
Posted 21 June 2012 - 10:59 PM
Not sure why your readADV function is so long. Here's how Bosschat handles multitasking and writing characters.

function writeChar(char)  --Future update will be able to make longer messages
 term.setCursorPos(charNum + string.len(username) + 3, cursorY) --right now limit is 48 minus username length and 2 extra characters
 if charNum <= 48 - (string.len(username) + 2) then --so it all fits on-screen
  term.write(char)
  message[charNum] = char
  charNum = charNum + 1
 end
end

function handleEvent() 
 local evt, p1, p2 = os.pullEvent()
 if evt == "char" then
  writeChar(p1)
 elseif evt == "key" then
  handleKey(p1)
 elseif evt == "rednet_message" then
  messageReceive(p1, p2)
 elseif evt == "timer" then
  handleTimers(p1)
 elseif evt == "peripheral" then
  handleModem(p1)
  updatePast("Modem detected on " .. p1) --custom function that will display messages, pass message as parameter.
  updatePast("Network restored.")
 elseif evt == "peripheral_detach" then
  updatePast("Modem detached on " .. p1)
  updatePast("No network connectivity.")
 end
end
Also, small preview for next update.
BigSHinyToys #17
Posted 22 June 2012 - 04:01 AM
In this case, it's the same, since none of the functions stop/returns.

You'r right, read() has some problems when doing something like this, that's why I don't think this is the best way to do a chat program (neither coroutines nor parallel), since all you have to do is handle some events. I made a chat program and it doesn't use coroutines or parallel (wich are basically the same), it's on the program library if you want to take a look at it.
For other things it would be better/easier to use parallel. Mostly when you don't need to use read() or redraw the screen.

I don't think I will finish that code, since it was just an example, and I don't think it's the best way to do this.
I readed your code, and there's a lot of things that could be improved, and some things that has to be removed. Like:


local function writer()
	while true do
			coroutine.yield()
			writer2()
	end
end
why do you even need that function?
this function is run as a coroutine .It prints the to : or the mes : on the screen so it over writes every update instead of just ounce.This is where it is activated.

local reciveHandel = coroutine.create(recive)
local writerHandel = coroutine.create(writer) -- writer function starts run  here
local sendHandel = coroutine.create(send)
while true do -- start a loop
	local e,e1,e2,e3,e4,e5 = os.pullEvent()
	coroutine.resume(reciveHandel,e,e1,e2,e3,e4,e5)
	coroutine.resume(writerHandel) -- write function over writes test printed by recive function
	coroutine.resume(sendHandel,e,e1,e2,e3,e4,e5) -- reed over writes all end result all information stays where I want it on the screen.
end

so you could remove it but the to : or mes : would be over write by part of a received message as the messages scroll up.


Not sure why your readADV function is so long. Here's how Bosschat handles multitasking and writing characters.
readADV() is a direct copy of the read function with a few pieces removed (unnecessary in this task) and a change made to how it redraws the screen allowing it to redraw always instead of just after chars are added or removed. so it is huge because of this. if read was more coroutine friendlily i would not have had to include that at all. also my system can Handel large messages up to the full screen size - one row. There is no user name thought as this is low level basic communications / network testing.
MysticT #18
Posted 22 June 2012 - 05:03 PM
In this case, it's the same, since none of the functions stop/returns.

You'r right, read() has some problems when doing something like this, that's why I don't think this is the best way to do a chat program (neither coroutines nor parallel), since all you have to do is handle some events. I made a chat program and it doesn't use coroutines or parallel (wich are basically the same), it's on the program library if you want to take a look at it.
For other things it would be better/easier to use parallel. Mostly when you don't need to use read() or redraw the screen.

I don't think I will finish that code, since it was just an example, and I don't think it's the best way to do this.
I readed your code, and there's a lot of things that could be improved, and some things that has to be removed. Like:


local function writer()
	while true do
			coroutine.yield()
			writer2()
	end
end
why do you even need that function?
this function is run as a coroutine .It prints the to : or the mes : on the screen so it over writes every update instead of just ounce.This is where it is activated.

local reciveHandel = coroutine.create(recive)
local writerHandel = coroutine.create(writer) -- writer function starts run  here
local sendHandel = coroutine.create(send)
while true do -- start a loop
	local e,e1,e2,e3,e4,e5 = os.pullEvent()
	coroutine.resume(reciveHandel,e,e1,e2,e3,e4,e5)
	coroutine.resume(writerHandel) -- write function over writes test printed by recive function
	coroutine.resume(sendHandel,e,e1,e2,e3,e4,e5) -- reed over writes all end result all information stays where I want it on the screen.
end

so you could remove it but the to : or mes : would be over write by part of a received message as the messages scroll up.
I know when it's used and what it does, but it's still unnecesary. If you handle the rewrite, you don't need that. If you'r going to rewrite the read function, then just don't use coroutines and handle the events yourself, it's easier and shorter to write.