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

Rednet messages being ignored

Started by Exerro, 06 November 2014 - 11:13 PM
Exerro #1
Posted 07 November 2014 - 12:13 AM
Hi,
I am writing a connection library for Nova, which uses rednet. I have been trying to get my head around it not working for about 3 hours now, and have only just narrowed it down to the problem.
It appears the messages being sent from computer A (which I have confirmed being sent) are actually queueing modem_message events on computer B, but not rednet_message events. For example, if I send the same string 3 times to the other computer, it gets the first one fine, but the next 2 are ignored by rednet.

Here is a screenshot which shows what is going on: http://puu.sh/cGb1V/9ca88d9cfb.png

As you can see, it gets a modem_message and rednet_message at the top. This is while the connection is being established, and that queues the http_connection event next.
After that, it gets another modem_message and rednet_message event, which is the first of the 3 messages it should be getting. The next modem_message isn't followed by a rednet_message, and same for the one after that.

I'm not sure if it's something I'm doing wrong, a rednet bug, or… well I really don't know.
Does anybody know what is going on here, or will I have to stop using rednet?

P.S.
The code sending the packets is identical. Literally nothing changes between them. It encrypts the data with the same key, and uses rednet.send( target, packet, "NovaNetwork" ) for both. The modem message events show this too, only the table id is changing.

Important sections of code:
SpoilerSending computer…

local connection, err = network.connection.establish( tonumber( response ), "http" )
  if connection then
   for i = 1, 3 do -- for debugging purposes, not any actual application.
	os.log( connection:send( message ) ) -- the log file shows that this does send. (and other computer gets the modem_message event)
	sleep( .5 )
   end
   app.display.alert "Message sent" ( )
  else
   app.display.alert( err ) ( )
  end

Receiving computer…

if event == "http_connection" then
  local connection = network.connection.get( cid ) -- gets the connection object, cid is event[2] basically
  table.insert( buffer, "connection established with " .. connection:getPeer( ) )
  app.newThread( function( ) -- runs in a seperate coroutine getting all events
   while true do
	local data, err = connection:receive( 1 )
	if data then
	 table.insert( buffer, data ) -- gets 1 message, but not the next 2
	else
	 table.insert( buffer, "error: " .. tostring( err ) )
	 break
	end
   end
  end )
end

I must point out that the problem here is the fact that rednet_message events aren't getting queued, not problems with the code above. If you would like to test for yourself, and see log files, etc, I can compile the latest version of Nova to give you, but I doubt it will help very much as you won't understand much of what is going on without further explanation.

Thank you for reading.

Edit:
After making a couple of changes to make it use modem.transmit() rather than rednet.send() it works fine. All 3 messages are received by receiving computer. In fact, even the ftp feature is working great (spoilers ;)/>). This is definitely a rednet problem, or at least a problem with how I'm using rednet. Is there some kind of spam protection? Because there are definitely messages that when encrypted are identical, so maybe rednet is ignoring them deliberately? There is nothing on the wiki about it, and I also tried just doing rednet.send(1, "hello") 3 times on different computers and that worked. I'm at a loss here.
Edited on 06 November 2014 - 11:29 PM
Bomb Bloke #2
Posted 07 November 2014 - 02:55 AM
The code you've provided here isn't the code you're using to receive the messages! This makes it difficult to comment on where you might've gone wrong with it. ;)/>

While there is code in CC1.6+ which could cause rednet messages to be missed, the odds of it happening to any given message are remote. Seeing it occur multiple times in a row would be something akin to winning every lottery ticket you ever buy.

My guess is that you'd somehow disabled the rednet.run() function via your threading mechanism. Rednet message events don't occur "automatically", a bit of code that generally runs in parallel with CraftOS spits them out.
Exerro #3
Posted 07 November 2014 - 11:09 AM
The threading basically just adds a coroutine to a list, so I don't see how that could stop rednet.run() as that still runs on a higher level than the OS itself. The only thing I can think of is coroutine.yield() not returning rednet_messages, although that wouldn't explain why the first one is caught.
Receiver code
Spoiler

  if not self.open then return false, "connection closed" end
  local timer
  if timeout then
   timer = os.startTimer( timeout )
  end
  while true do
   local ev = { coroutine.yield() }
   if ev[1] == "timer" and ev[2] == timer then
	return false, "connection timed out"
   -- elseif ev[1] == "rednet_message" and type( ev[3] ) == "table" and ev[4] == "NovaNetwork" then
   elseif ev[1] == "modem_message" and ev[3] == 2514 and type( ev[5] ) == "table" then
   -- local data = self:decode( ev[3] )
	local data = self:decode( ev[5] ) -- this function logs an "incoming message", which is only seen the first time
	if data then
	 if data == "close" then
	  self.open = false
	  return false, "connection closed"
	 end
	 return data
	end
   end
  end

I commented out a couple of lines so I could replace them with detecting modem_message events. Using modem_message events, it works perfectly, and gets all the messages, so I doubt it is a problem with my code.

Sending code…
Spoiler

-- I believe this is the only important line:
rednet.send( target, packet, "NovaNetwork" )

Thanks for replying
Edited on 07 November 2014 - 10:09 AM
Bomb Bloke #4
Posted 08 November 2014 - 12:50 AM
Can't see anything obvious there. Got the code for managing your co-routine list?
Exerro #5
Posted 08 November 2014 - 05:12 PM
Probably not the easiest thing to read on the forums, sorry…
Spoiler

--[[type="class"]]
local threads = { }
Thread.public "environment"
function Thread.public.environment:read( )
return self.env
end
function Thread.public.environment:write( value )
if type( value ) ~= "table" then
  error( "expected table", 3 )
end
self.env = value
end
Thread.public "onException" "function"
Thread.public "onFinish" "function"
Thread.public "doesUpdate" "boolean"
function Thread:Thread( thread )
self.state = "running"
self.thread = thread
self.env = setmetatable( { wait = coroutine.yield, thread = self.public }, { __index = getfenv( ) } )
setfenv( self.thread, setmetatable( { }, { __index = function( _, k ) return self.env[k] end, __newindex = function( _, k, v ) self.env[k] = v end } ) )
self.co = coroutine.create( thread )
table.insert( threads, self.public )
return self.public
end
function Thread.public:pause( )
if self.state == "running" then
  self.state = "paused"
  return true
end
return false
end
function Thread.public:resume( )
if self.state == "paused" then
  self.state = "running"
  return true
end
return false
end
function Thread.public:restart( )
self.co = coroutine.create( self.thread )
self.state = "running"
end
function Thread.public:kill( )
self.state = "stopped"
end
function Thread.public:isRunning( )
return self.state == "running"
end
function Thread.public:isPaused( )
return self.state == "paused"
end
function Thread.public:update( args )
if self.state == "stopped" then
  return false, "finished"
end
if self.state ~= "running" then
  return false, "not running"
end
local ok, data = coroutine.resume( self.co, unpack( args ) )
if not ok then
  self.state = "stopped"
  if type( self.onException ) == "function" then
   pcall( self.onException, self.public, data )
  end
  if self.state == "stopped" then
   return false, "finished"
  end
  return false, "errored"
end
if coroutine.status( self.co ) == "dead" then -- give it a chance to restart
  if type( self.onFinish ) == "function" then
   pcall( self.onFinish, self.public )
  end
end
if coroutine.status( self.co ) == "dead" then
  self.state = "stopped"
  return false, "finished"
end
return true, data
end
function Thread.static.update( args, all )
for i = #threads, 1, -1 do
  local ok, data = threads[i]:update( args )
  if not ok and data == "finished" then
   table.remove( threads, i )
  end
end
end
Thread.update() is called in the main update loop at every event or update timer. (with the event of course)

Any idea why a modem_message event wouldn't trigger a rednet_message event though? There's definitely something fishy going on with rednet here. I don't think it's a problem with my code because when I switched it to use modem_message events instead it worked absolutely fine. If it was my code that was the problem, it would be that the multitasking bit was filtering rednet_message events, which as you can see it doesn't do.