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

[Solved]Rednet Chat System

Started by Roboguy99, 23 November 2013 - 09:28 AM
Roboguy99 #1
Posted 23 November 2013 - 10:28 AM
So I've just started looking into Rednet for the first time and have a very primitive system capable of sending and receiving messages between computers. What I'd like it to do is constantly check for messages, while still allowing you to check write one at the same time. I'm assuming this would involve the parallel API but I'm kinda stumped from there.

Here is my code so far (This is old code, please look further below for updated code):
Spoiler

function clear()
term.clear()
term.setCursorPos(1,1)
term.setTextColor(colors.white)
end

function sendMessage()
clear()
write("Enter computer ID of receiver: ")
local receiver = tonumber(read())
print("Enter text: ")
local text = read()

clear()
write("Sending message...")
rednet.send(receiver, text, true)
print("Sent")
end

function viewMessage()
clear()

local senderID, text, distance = rednet.receive()

term.setCursorColor(colors.red)
write("Text: ")
term.setCursorColor(colors.white)
print(text)

term.setCursorColor(colors.green)
print("Sender info:")

term.setCursorColor(colors.red)
write("Sender ID: ")
term.setCursorColor(colors.white)
print(senderID)

term.setCursorColor(colors.red)
write("Distance from sender: ")
term.setCursorColor(colors.white)
print(distance)
sleep(10)
end

rednet.open("top")
name = os.getComputerLabel()
id = os.computerID()

clear()

print("Available tasks: ")

term.setTextColor(colors.red)
print("send")

term.setTextColor(colors.white)
write("Please enter a task: ")
task = read()

if task == "send" then
sendMessage()
else
clear()
print("Please enter a valid task.")
sleep(1)
end
Edited on 26 November 2013 - 10:47 AM
Papa_Beans #2
Posted 23 November 2013 - 12:00 PM
Create a Timeout for Rednet.


function viewMessage()
clear()
-- Here
local timout = 0.5
local senderID, text, distance = rednet.receive(timeout)
-- And place it inside of the brackets in rednet.receive()
term.setCursorColor(colors.red)
write("Text: ")
term.setCursorColor(colors.white)
print(text)
term.setCursorColor(colors.green)
print("Sender info:")
term.setCursorColor(colors.red)
write("Sender ID: ")
term.setCursorColor(colors.white)
print(senderID)
term.setCursorColor(colors.red)
write("Distance from sender: ")
term.setCursorColor(colors.white)
print(distance)
sleep(10)
end

That should stop it from freezing, and keep waiting for a message, and thus allow you to loop back to your sendMessage() function. Hope I helped, Papa Beans
Roboguy99 #3
Posted 23 November 2013 - 12:18 PM
It's solved one issue, but how would I go about allowing a user to continue using other features of the program (e.g writing a message) and still check? Also, how would I make sure that the viewMessage() function only prints when it actually gets a message?
Roboguy99 #4
Posted 23 November 2013 - 05:29 PM
Bump.
Bomb Bloke #5
Posted 23 November 2013 - 05:56 PM
You'll likely find the parallel API to be your best bet. It allows you to run two functions side by side, each getting its own event queue - you need this because the "read()" commands otherwise discard the "rednet_message" events that come in while typing. Eg.
Roboguy99 #6
Posted 24 November 2013 - 05:11 AM
Ok so I've done an entire code overhaul to make it more of a chat system than an email one and I am trying to make it:
1) Have a separate message writing area to message displaying area (separated by a line)
2) Allow you to always send messages and always receive messages
3) Make it so that when the text reaches a certain point on the screen, it will remove the first line and then move every thing else up (effectively scrolling)

This has got a few errors however. I have labelled where in my new code below. I have also looked and cannot find any obvious reason for its failure.


rednet.open("top")
write("Enter the ID of your chatting partner: ")
id = read()

write("Enter their name too: ")
name = read()

function gui()
local width, height = term.getSize()
term.setCursorPos(1, height - 20) --It doesn't seem to do anything after this line, which it doesn't seem to do correctly either. The cursor goes to the top left corner and the program allows input but isn't terminated.
i = 1
repeat
write("-")
i = i+1
until i == width
end

function display(isRecieved)
--doStuff
end

function send()
write("Message: ")
sMsg = read()
rednet.send(id, sMsg)
display(false)
end

function receive()
local id, rMsg = rednet.recieve() --This line causes an error when the gui function isn't run. It displays an error to do with itself and parallel (attempt to call nil)
display(true)
end

gui()

while true do
parallel.waitForAny(send, receive)
end
Edited on 24 November 2013 - 04:36 AM
Bomb Bloke #7
Posted 24 November 2013 - 06:43 AM
The default ComputerCraft config will have "height" coming out as 19. 19 minus 20 is -1.

It's rednet.receive().

"read()" returns strings. You can convert them to numerals with something along the lines of "tonumber(read())". This will return nil if the result couldn't be converted (eg, someone typed a word instead of an actual number).

"for" loops are worth looking into. This:

i = 1
repeat
write("-")
i = i+1
until i == width

… can be re-written as this:

for i=1,width do
  write("-")
end

Or, using a certain string repeating function, you could ditch loops altogether:

write( string.rep("-",width) )
Roboguy99 #8
Posted 24 November 2013 - 07:37 AM
Ok thank-you very much. Sometimes you've gotta hate the English language. I've also managed to easily solve the other problems.
Roboguy99 #9
Posted 24 November 2013 - 08:24 AM
2 More problems I need to solve:

1)How would I go about making the messages scroll when it they run out of room? It does a weird thing where the line slowly extends its way up the screen at the moment.
2)I've tried it with a second computer and it picks up the message and displays the name tag, but not the message. Local messages are displayed so I believe it is something to do with the actual receiving of the message.


term.clear()
term.setCursorPos(1,1)

lastMsg = 0
rednet.open("top")
write("Enter the ID of your chatting partner: ")
id = tonumber(read())

write("Enter their name too: ")
name = read()

term.clear()
term.setCursorPos(1,1)

function gui()
width, height = term.getSize()
term.setCursorPos(1, height - 3)

for i=1, width do
write("-")
end
end

function display(isReceived)
if isReceived == true then
nametag = name
else
nametag = "You"
end

term.setCursorPos(1, lastMsg+1)
write("[" .. nametag .. "]")

if isReceived == true then
print(rMsg)
else
print(sMsg)
end

lastMsg = lastMsg + 1
end

function send()
write("Message: ")
sMsg = read()
rednet.send(id, sMsg)
display(false)
end

function receive()
local id, rMsg = rednet.receive()
display(true)
end

gui()

while true do
gui()
parallel.waitForAny(send, receive)

term.setCursorPos(1, height - 2)
term.clearLine()
term.setCursorPos(1, height - 1)
term.clearLine()
term.setCursorPos(1, height)
term.clearLine()

end
The forum doesn't seem to like my indentation…

EDIT: I've done some testing, the variable is stored fine. It's something to do with the way it's printed.
Edited on 24 November 2013 - 07:30 AM
Bomb Bloke #10
Posted 24 November 2013 - 04:43 PM
The forum is much more indentation-friendly if you turn the rich-text editor off (use the light switch at the top left of the posting box).

"rMsg" is local to the "receive()" function, and so can only be accessed by that function. Either pass it to the display function, or make it local to the entire script.

Eg, something like:

Spoiler
local function display(isReceived,tMsg)
  if isReceived then
    nametag = name
  else
    nametag = "You"
  end

  term.setCursorPos(1, lastMsg+1)
  write("[" .. nametag .. "]")
  print(tMsg)

  lastMsg = lastMsg + 1
end

local function send()
  write("Message: ")
  local sMsg = read()
  rednet.send(id, sMsg)
  display(false,sMsg)
end

local function receive()
  local id, rMsg = rednet.receive()
  display(true,rMsg)
end

Or:

Spoiler
local id,sMsg,rMsg

.
.
.

local function display(isReceived)
  if isReceived then
    nametag = name
  else
    nametag = "You"
  end

  term.setCursorPos(1, lastMsg+1)
  write("[" .. nametag .. "]")

  if isReceived then
    print(rMsg)
  else
    print(sMsg)
  end

  lastMsg = lastMsg + 1
end

local function send()
  write("Message: ")
  sMsg = read()
  rednet.send(id, sMsg)
  display(false,sMsg)
end

local function receive()
  id, rMsg = rednet.receive()
  display(true)
end

Whatever code block you declare "local" variables in, discards them when that block ends. If you don't declare them as local anywhere in the script, then they stay loaded in RAM when the script ends! Same goes for your functions!

Note the use of "if isReceived then" - this simply checks to see if "isReceived" is true. "if isReceived == true then" checks to see if "isReceived" is true is true, which is a pointless extra step. If you wanted to check whether "isReceived" was false, you'd use "if not isReceived then".

There's no function that can scroll just part of the screen for you. I would use term.getCursorPos() after writing something to check where the cursor ended up - if its y-position was more then height-3, I'd term.scroll(ypos-(height-3)) then call gui() again.

This would still leave part of your line of dashes floating up the screen. If you switch to displaying your messages with "write" instead of "print", then you can probably see how to deal with this by drawing spaces to the end of the line if the last message happened to end with a cursor position on row height-3.
Roboguy99 #11
Posted 25 November 2013 - 11:36 AM
Ok thank you. I seem to have a habit of making variables local, but declaring them as local outside the function sounds like a good idea. As for the scrolling system, I will definitely use something similar.
Roboguy99 #12
Posted 25 November 2013 - 12:37 PM
I can't get the scrolling thing to work. Will move to another thread.