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

Help with chatroom program

Started by alez290, 25 January 2015 - 04:12 AM
alez290 #1
Posted 25 January 2015 - 05:12 AM
okay guys so I run a server in tekkit classic and ive set up the internet with some really cool programs, one of these is my chatroom server/client program which lets users talk to each other in real time in a chat room. The problem is, the computers running the client program are supposed to be able to connect to the server, then the server adds the clients computer ID to a table. Then the server will only send updates to computers in the table, but what happens is the server sends an update to all computers within range which messes up other programs and doesnt allow seperate chat rooms. The code is below. I have narrowed the problem down to the part where the server actually sends the update with the command rednet.send(clientList[x], msg) thanks in advanced, anyone interested in my internet can message me.
server code:

rednet.open("top") --opens connection to modem on top
chatTable = {"","","","",""} --table for storing chat history
clientList = {} --table for list of connected clients
myMessage = "" --dynamic variable for storing character input
function addClient() --Responsible for adding new clients to clientList
while true do --loop
  sleep(0) --pause system
  newClient = rednet.receive() --waits for signal from a client
  x = 1 --loop variable
  repeat --loop
   if clientList[x] == newClient then --checks if x client is already in the table
   newClient = false --if it is, tells server not to add client to the table
   end
   x = x + 1 --adds one to loop variable
  until clientList[x] == nil --checks if x corresponds to an existing line in the table
  if newClient ~= false then --makes sure client hasn't already been added
   table.insert(clientList, newClient) --inserts client id into clientList table
   serverEvent = "Computer "..newClient.." logged on" --sets variable announcing a new user has logged on
   chatTable[5] = chatTable[4]
   chatTable[4] = chatTable[3]
   chatTable[3] = chatTable[2]
   chatTable[2] = chatTable[1] --moves all chat history up
   chatTable[1] = serverEvent --adds announcement into the table
   msg = textutils.serialize(chatTable) --declares msg as a compressed version of the chat history
   x = 1 --variable loop
   repeat --loop
	rednet.send(clientList[x], msg) --sends chat history to client x
	x = x + 1 --adds 1 to loop variable
   until clientList[x] == nil --checks if client x exists
  end
end
end
function server() --handles the receiving and forwarding of new messages
while true do --loop
  sleep(0)
  id, message = rednet.receive() --waits for signal
  if message == "logout" then --checks if a logout message was received
  sleep(0)
   serverEvent = "Computer "..id.." logged off" --sets variable for logout announcement
   chatTable[5] = chatTable[4]
   chatTable[4] = chatTable[3]
   chatTable[3] = chatTable[2]
   chatTable[2] = chatTable[1] --updates chat history
   chatTable[1] = serverEvent --announces logout
   msg = textutils.serialize(chatTable) --compresses chat table
   x = 1 --loop variable
   repeat
	rednet.send(clientList[x], msg) --sends chat table to client x
	if clientList[x] == id then --checks if client x was the one who asked to log out
	 id = x --sets id to x (location of client in the table)
	end
	x = x + 1 --add one to loop variable
   until clientList[x] == nil --checks if client x exists
   table.remove(clientList, id) --removes client from client table
  end

  if message ~= "logout" then --checks if received message was not logout
   chatTable[5] = chatTable[4]
   chatTable[4] = chatTable[3]
   chatTable[3] = chatTable[2]
   chatTable[2] = chatTable[1] --update chat history
   chatTable[1] = message --adds message to chat table
   msg = textutils.serialize(chatTable) --compresses chat table
   x = 1 --loop variable
   repeat
	rednet.send(clientList[x], msg) --sends chat table to client x
	x = x + 1 --adds one to loop variable
   until clientList[x] == nil --checks if client x exists
  end
end
end
function rawread() --handles user input into server
while true do --loop
  sleep(0) --pause system
  sEvent, param = os.pullEvent("char") --checks for keypress
  myMessage = myMessage..param --adds keypress to current server input
end
end
function keys() --responsible for backspace and enter keypress
while true do --loop
  sleep(0)
  cEvent, par = os.pullEvent("key") --checks for keypress
  pressed = par --sets variable pressed equal to the keypress value
  if pressed == 14 then --checks if keypress was backspace
   myMessage = string.sub(myMessage,1, string.len(myMessage) - 1 ) --removes last character from current server input
  elseif pressed == 28 then --checks if keypress was enter
   if myMessage == "logout" then --checks if current server input is logout
	shell.run("reboot") --reboots computer
   else
	chatTable[5] = chatTable[4]
	chatTable[4] = chatTable[3]
	chatTable[3] = chatTable[2]
	chatTable[2] = chatTable[1] --update chat history
	chatTable[1] = "Server- "..myMessage --adds server input to chat table
	msg = textutils.serialize(chatTable) --compresses chat table
	x = 1 --loop variable
	repeat
	 rednet.send(clientList[x], msg) --sends chat table to client x
	 x = x + 1 --adds one to loop variable
	until clientList[x] == nil --checks if client x exists
	myMessage = "" --resets server input
   end
  end
end
end
function draw() --displays chat table and connected clients
while true do --loop
sleep(0) --pause system
  term.clear() --clears screen
  x = 1 --loop variable
  repeat
  print(clientList[x]) --displays id of client x
  x = x + 1 --adds one to loop variable
  until clientList.x == nil --checks if client x exists
  print(chatTable[5])
  print(chatTable[4])
  print(chatTable[3])
  print(chatTable[2])
  print(chatTable[1]) --prints chat history
  print(myMessage) --shoes current server input
end
end
parallel.waitForAny(addClient, server, rawread, keys, draw) --allows all functions to run at the same time
client code:

rednet.open("top") --activates wireless modem on top of computer
print("What cmputer do you want to talk to") --asks what computer you would like to connect to
status, destination = pcall(read) --gets input of selected computer
destination = tonumber(destination) --turns your input into an integer (number)
myMessage = "" --dynamic character input variable
chatTable = {} --chat table (stores chat log)
function rawread() --handles keypress event
while true do --loop
  sleep(0) --pause system
  sEvent, param = os.pullEvent("char") --waits for keypress
  myMessage = myMessage..param --adds key pressed to current user input
end
end
function receive() --handles signals from server
while true do --loop
  sleep(0)
  id, msg = rednet.receive() --waits for signal
  chatTable = textutils.unserialize(msg) --decompresses received chat table
end
end
function keys() --handles backspace and enter keypress
while true do --loop
  sleep(0)
  cEvent, par = os.pullEvent("key") --waits for keypress event
  pressed = par --sets variable pressed to keypress value
  if pressed == 14 then --checks if pressed is backspace
   myMessage = string.sub(myMessage,1, string.len(myMessage) - 1 ) --removes last character from user input
  elseif pressed == 28 then --checks if pressed is enter
   if myMessage == "logout" then --checks if input is logout
	rednet.send(destination, "logout") --sends logout message to server
	shell.run("reboot") --reboots computer
   else
	rednet.send(destination, user.."- "..myMessage) --sends input to server
   myMessage = "" --resets user input
   end
  end
end
end
function draw() --displays chat log
while true do --loop
  sleep(0) --pause system
  term.clear() --clears screen
  print(chatTable[5])
  print(chatTable[4])
  print(chatTable[3])
  print(chatTable[2])
  print(chatTable[1]) --prints chat table
  print(myMessage) --displays current user input
end
end
parallel.waitForAny(rawread, draw, keys, receive) --allows all functions to run at once
Bomb Bloke #2
Posted 25 January 2015 - 08:51 AM
At least part of the problem is that the server adds any system that happens to message it into its "clients" table. It doesn't matter if that system messaged the server specifically, or if it was just a general broadcast triggered by eg a rednet.lookup() request - if the server sees a message, then as far as it's concerned, it's from a client.

If you're on CC 1.6, you could use protocols to avoid this. In earlier versions, it may be easiest to send messages in the form of tables - give them two keys, one with the message, and one with a string containing "alexChat" or something. When the server receives a message, have it check that it's 1) in the form of a table (using the type() function) and 2) that the table contains your special string. Have the server ignore the message if these conditions aren't true.

Another thing, this sort of structure:

        x = 1 --loop variable
        repeat
         rednet.send(clientList[x], msg) --sends chat table to client x
         x = x + 1 --adds one to loop variable
        until clientList[x] == nil --checks if client x exists

… is better phrased as a "for" loop:

        for x = 1, #clientList do  -- Sticking a hash in front of your table name returns the number of entries in it.
         rednet.send(clientList[x], msg) --sends chat table to client x
        end

You can also use the "break" keyword to escape a loop early.
Edited on 25 January 2015 - 07:52 AM
alez290 #3
Posted 25 January 2015 - 06:19 PM
Thanks so much! I'm not sure how that looping thing was broadcasting to all computers but the for loop corrected it, thanks for the advice on protocols, I'm on tekkit classic so i'll have to use tables (version 1.3.2 or something), but this ends 5 hours worth of troubleshooting Dx
Lyqyd #4
Posted 26 January 2015 - 02:38 AM
If you're on a version that old, you can't even send tables via rednet–you are limited to sending strings only.
Bomb Bloke #5
Posted 26 January 2015 - 05:15 AM
Of course, it helps to know that tables can be represented as strings, and converted back later. There's even a built-in function for the purpose - pass it a message, see if it manages to return a table, then proceed accordingly.