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

Error: Invalid key to 'next' - new weirdness - not working properly

Started by Goof, 19 February 2014 - 05:22 PM
Goof #1
Posted 19 February 2014 - 06:22 PM
Hello everyone.

I've been making a turtle quarry with mining wells and tesseracts…. but when my controller registers the turtles, i want them to be in order ( lowest computer id to highest )

EDIT: Look in the comment on 2nd page for my new problem

but this is ending in "invalid key to 'next'" on line 60 ( which is for k,v in pairs(database) )

my code:

local screen={term.getSize()}
local modem = peripheral.wrap("right")
modem.open(25563)
modem.open(25564)

local database={}
local databaseControllers={}
function split(s, delimiter)
	result = {};
	for match in (s..delimiter):gmatch("(.-)"..delimiter) do
		table.insert(result, match);
	end
	return result;
end
print("Remote-rebooting miners...")
modem.transmit(25564,25563,"reboot")
print("Waiting 2 seconds for miners to boot up...")
sleep(2)
term.clear()
term.setCursorPos(1,1)
print(string.rep("=",screen[1]))
--Register turtles...
print("Pinging...")
modem.transmit(25564,25563,"ping")
print("Pinged! Waiting for response...")
timeout=os.startTimer(10)
while true do
	local oldPos={term.getCursorPos()}
	term.setCursorPos(1,1)
	print(string.rep("=",screen[1]))
	term.setCursorPos(oldPos[1],oldPos[2])
	local event,t1,sender,receiver,response,distance=os.pullEvent()
	if event == "modem_message" then
		if sender == 25563 then
			tbl=split(response," ID=")
			--TYPE -- ID
			if tbl[1] == "miner" then
				database[tbl[2]]=tbl[2]
				print("Registered miner with ID "..tbl[2])
			elseif tbl[1] == "controller" then
				databaseControllers[tbl[2]] = tbl[2]
				print("Registered controller with ID "..tbl[2])
			end
			print(response)
		elseif sender == 00001 then
			print(response)
		end
	elseif event == "timer" then
		if t1 == timeout then
			break
		end
	end
end

-------------------------  Errors below this line --------------------------------------


print("DONE")
rTbl={}
local currentValue=20000
--print(textutils.serialize(database))
--sleep(3)
for k,v in pairs(database) do
	if tonumber(k) < currentValue then
		currentValue=k
		table.insert(rTbl,k)
		database[k]=nil
	end
end
for k,v in ipairs(rTbl) do
	print(k.." - "..v)
	sleep(0.5)
end


Thanks in Advance
Edited on 20 February 2014 - 04:05 PM
theoriginalbit #2
Posted 19 February 2014 - 07:19 PM
okay so the problem here is with this line

database[k] = nil

A bit of an explanation
so when you make a table that does not have numerical indexes what happens is each element (node) is linked together — in a data structure called a Linked List, although LuaJ uses a higher level Linked List called a HashMap, but I digress — the reason the node are linked together is so that once it comes time to move on to the next node it knows where to go — since the loop cannot just simply increment a numerical index 'cause they're not stored like that — this means that each node normally has at least a pointer to the `next` node; in some occasions it can also have a pointer to the `previous` node. What this means is that when it comes time to move on in an iterator or generic for loop the current node is queried for its next node, however what you're doing here is you're removing the current node before you move on, so when the generic for loop (pairs) tries to query the current node it no longer exists; hence the "invalid key to 'next'" error (which could also read "cannot get 'next' of `nil`" and mean the same thing.

Now the solution
so when it comes to making sure you can move on, you should make a backup of the keys (obviously this does introduce a few problems, but none that you should come across)… the easiest way of doing this is to define a new pairs function like so


local function safePairs( _t )
  local tKeys = {}
  for key in pairs(_t) do
    table.insert(tKeys, key)
  end
  local currentIndex = 0
  return function()
    currentIndex = currentIndex + 1
    local key = tKeys[currentIndex]
    return key, _t[key]
  end
end
so what the above code does is it makes a backup of all the tables keys into a new table, it then defines a function which if returned to the for loop that will increment the index (that refers to the keys backup table) each time it is invoked, returning the key and the entry for that key in the original table.

I hope this helps and if you have any questions, don't hesitate to ask :)/>
Goof #3
Posted 19 February 2014 - 07:26 PM
Spoiler
okay so the problem here is with this line

database[k] = nil

A bit of an explanation
so when you make a table that does not have numerical indexes what happens is each element (node) is linked together — in a data structure called a Linked List, although LuaJ uses a higher level Linked List called a HashMap, but I digress — the reason the node are linked together is so that once it comes time to move on to the next node it knows where to go — since the loop cannot just simply increment a numerical index 'cause they're not stored like that — this means that each node normally has at least a pointer to the `next` node; in some occasions it can also have a pointer to the `previous` node. What this means is that when it comes time to move on in an iterator or generic for loop the current node is queried for its next node, however what you're doing here is you're removing the current node before you move on, so when the generic for loop (pairs) tries to query the current node it no longer exists; hence the "invalid key to 'next'" error (which could also read "cannot get 'next' of `nil`" and mean the same thing.

Now the solution
so when it comes to making sure you can move on, you should make a backup of the keys (obviously this does introduce a few problems, but none that you should come across)… the easiest way of doing this is to define a new pairs function like so


local function safePairs( _t )
  local tKeys = {}
  for key in pairs(_t) do
	table.insert(tKeys, key)
  end
  local currentIndex = 0
  return function()
	currentIndex = currentIndex + 1
	local key = tKeys[currentIndex]
	return key, _t[key]
  end
end
so what the above code does is it makes a backup of all the tables keys into a new table, it then defines a function which if returned to the for loop that will increment the index (that refers to the keys backup table) each time it is invoked, returning the key and the entry for that key in the original table.

I hope this helps and if you have any questions, don't hesitate to ask :)/>/>/>/>
Hmmm Im a little confused, but i understand mostly everything. :)/>/>/>/>


So… To call that function I just do:


rTbl={}
local currentValue=20000

for k,v in safePairs(database) do
	if tonumber(k) < currentValue then
		currentValue=tonumber(k)
		table.insert(rTbl,k)
		database[k]=nil
	end
end
for k,v in ipairs(rTbl) do
	print(k.." - "..v)
	sleep(0.5)
end
?

Edit: Oh well… it seems like the way i put the table ( min to max )ID's doesnt work as expected.. xD its totally the opposite xD…. but what? ehh… aaargh help
EDIT2: xD I think i got that min to max working… not totally sure.. but i think… ( well still coding loops xD )
EDIT3: Tried the following xD.. ( didn't work ) .. ( yet )


local rTbl={}
local newTbl={}
local currentValue=0
--print(textutils.serialize(database))
--sleep(3)
for k,v in safePairs(database) do
	table.insert(rTbl,k)
end
print(math.min(unpack(rTbl)).."\n")
for i = 1, #rTbl do
	local min=math.min(unpack(rTbl))
	print(min)
	table.insert(newTbl,min)
	table.remove(rTbl,min)
end
--print(textutils.serialize(rTbl))
for k,v in ipairs(newTbl) do
	print(k.." - "..v)
	sleep(0.5)
end


Thanks for the quick response :D/>/>/>/>
Edited on 19 February 2014 - 06:42 PM
theoriginalbit #4
Posted 19 February 2014 - 07:41 PM
So… To call that function I just do:
Oh sorry I did forget to provide a usage example didn't I… oops… yeah you just replace what's normally pairs with safePairs.

As for the new problem, I'll need some more information. is the database table an indexed table? 'cause from the quick glance through I had it looked like a key/value table meaning that you're not inserting numbers into rTbl but in fact strings. Maybe I'm missing something, please provide a little more detailed info :)/>
Goof #5
Posted 19 February 2014 - 07:47 PM
Hmm… Yeah..
I'm not getting any errors during my current code:
Spoiler

local screen={term.getSize()}
local modem = peripheral.wrap("right")
modem.open(25563)
modem.open(25564)

local database={}
local databaseControllers={}
function split(s, delimiter)
    result = {};
    for match in (s..delimiter):gmatch("(.-)"..delimiter) do
        table.insert(result, match);
    end
    return result;
end
print("Remote-rebooting miners...")
modem.transmit(25564,25563,"reboot")
print("Waiting 2 seconds for miners to boot up...")
sleep(2)
term.clear()
term.setCursorPos(1,1)
print(string.rep("=",screen[1]))
--Register turtles...
print("Pinging...")
modem.transmit(25564,25563,"ping")
print("Pinged! Waiting for response...")
timeout=os.startTimer(10)
while true do
	local oldPos={term.getCursorPos()}
	term.setCursorPos(1,1)
	print(string.rep("=",screen[1]))
	term.setCursorPos(oldPos[1],oldPos[2])
	local event,t1,sender,receiver,response,distance=os.pullEvent()
	if event == "modem_message" then
		if sender == 25563 then
			tbl=split(response," ID=")
			--TYPE -- ID
			if tbl[1] == "miner" then
				database[tbl[2]]=tbl[2]
				print("Registered miner with ID "..tbl[2])
			elseif tbl[1] == "controller" then
				databaseControllers[tbl[2]] = tbl[2]
				print("Registered controller with ID "..tbl[2])
			end
			print(response)
		elseif sender == 00001 then
			print(response)
		end
	elseif event == "timer" then
		if t1 == timeout then
			break
		end
	end
end
local function safePairs( _t )
  local tKeys = {}
  for key in pairs(_t) do
    table.insert(tKeys, key)
  end
  local currentIndex = 0
  return function()
    currentIndex = currentIndex + 1
    local key = tKeys[currentIndex]
    return key, _t[key]
  end
end
print("DONE")
local rTbl={}
local newTbl={}
local currentValue=0
--print(textutils.serialize(database))
--sleep(3)
for k,v in safePairs(database) do
	table.insert(rTbl,k)
end
print(math.min(unpack(rTbl)).."\n")
for i = 1, #rTbl do
	local min=math.min(unpack(rTbl))
	print(min)
	table.insert(newTbl,min)
	table.remove(rTbl)
end
--print(textutils.serialize(rTbl))
for k,v in ipairs(newTbl) do
	print(k.." - "..v)
	sleep(0.5)
end

But the code above, when it is listing the IDs at the end, its like showing 5-8 of the same ID which should not even be possible…
theoriginalbit #6
Posted 19 February 2014 - 08:01 PM
But the code above, when it is listing the IDs at the end, its like showing 5-8 of the same ID which should not even be possible…
I'd assume by the looks of it, that it would be caused by this bit

for i = 1, #rTbl do
	local min=math.min(unpack(rTbl))
	print(min)
	table.insert(newTbl,min)
	table.remove(rTbl)
end

tell me what the purpose of these 3 loops are at the end of the script, like what is your aim with them?
Edited on 19 February 2014 - 07:01 PM
Goof #7
Posted 19 February 2014 - 08:04 PM
Well First of all i just didn't want to delete any, if they somehow messed up my code and made a bunch of bugs…

so instead i tried to make a loop which loops through the listed IDS / registered IDS in the table..

then i was going to find the lowest value.. and add that into a new table with table.insert ( so it gets ordered )
Then i deleted the lowest value from the table ( which certainly doesn't work properly ).

The last loop is for showing me the result

my main aim for the last part of the code, is to get a ordered list ( min to max ) of the IDS registered…
like:


[1] = 26; -- lowest ID EVER received
[2] = 28; -- the second lowest ID EVER received
[3] = 57; --- etc....
[4] = 582;
etc… all the way until no more ids were registered


I hope that made sense xD
Edited on 19 February 2014 - 07:05 PM
theoriginalbit #8
Posted 19 February 2014 - 08:11 PM
then i was going to find the lowest value.. and add that into a new table with table.insert ( so it gets ordered )
why not just use table.sort?

Then i deleted the lowest value from the table ( which certainly doesn't work properly ).
no 'cause you're deleting the first one every time, when an index isn't provided to table.remove it will remove the first element.

my main aim for the last part of the code, is to get a ordered list ( min to max ) of the IDS registered…
oh, then why were you nilling out the entry earlier; which is what made me suggest the new safePairs function. if all you're wanting to do is get an order list of registered IDs do the following (I am assuming that the keys in the database table are the IDs for the Turtles)

local registeredIds = {}
for id in pairs(database) do
  table.insert(registeredIds, id)
end
--# they should be in order, pairs accesses indexes in order, but if you really want to make sure, do the next line
table.sort(registeredIds)
Edited on 19 February 2014 - 07:12 PM
Goof #9
Posted 19 February 2014 - 08:17 PM
then i was going to find the lowest value.. and add that into a new table with table.insert ( so it gets ordered )
why not just use table.sort?

OH… Im a completely idiot! why the heck didn't i think of table.sort… like.. I want to sort something, and i start to think about so many tables and stuff :o/>/>/>/>
wooow… Thanks for helping my brain xD

my main aim for the last part of the code, is to get a ordered list ( min to max ) of the IDS registered…
oh, then why were you nilling out the entry earlier; which is what made me suggest the new safePairs function. if all you're wanting to do is get an order list of registered IDs do the following (I am assuming that the keys in the database table are the IDs for the Turtles)

local registeredIds = {}
for id in pairs(database) do
  table.insert(registeredIds, id)
end
--# they should be in order, pairs accesses indexes in order, but if you really want to make sure, do the next line
table.sort(registeredIds)

Well The IDS is the turtle's ID, which they send, during a ping-pong transmission.
Well lemme check after i've rebooted my computer ( Graphics card update )..

And I am back…

Checking now…
Booting local server…
Starting minecraft with my custom modpack…


Oh yeah! It works like a charm :D/> Thanks for your help :D/> It is very appreciated!
Edited on 19 February 2014 - 07:29 PM
theoriginalbit #10
Posted 19 February 2014 - 08:24 PM
I want to sort something, and i start to think about so many tables and stuff :o/>
Well performing a Bubble sort would have probably been your best choice. But wanting to implement your own sorting algorithm is not exactly a bad thing, it can be a good practice in Lua and programming concepts…
Goof #11
Posted 19 February 2014 - 08:30 PM
Well xD Thats just derpy me, hehe :)/>

Oh yeah! It works like a charm :D/>/> Thanks for your help :D/>/> It is very appreciated!
Edited on 19 February 2014 - 07:30 PM
theoriginalbit #12
Posted 19 February 2014 - 08:48 PM
No problems :)/>
Goof #13
Posted 19 February 2014 - 09:05 PM
Hmmm New problem encountered…


local function waitForResponse(whatToReceive,turtles)
	timeout=os.startTimer(15)
	local maxReceives=turtles
	receives=0
	while true do
		local event,t1,sender,receiver,response,distance=os.pullEvent()
		if event == "modem_message" then
			if response == whatToReceive then
				if receives &amp;lt;= maxReceives then
					receives=receives+1
				elseif receives &amp;gt;= maxReceives then
					return true
				end
			end
		elseif event == "timer" then
			if t1 == timeout then
				return false
			end
		end
	end
end

It is weird… the turtles ( how many turtles there are ) is set to #registeredIds ( which is 14)
then it receives all messages ( all responses (which is 14 messages) )
but… the weird thing is that it returns false, when it should return true..

Explanation:

This functions is going to wait for all the registered turtles to respond to the controller….
Its main part is to get how many turtles are registered, loop through how many messages were received, then compare,
and last return true/false wether there has been received the amount of registered turtles…

Thanks in advance

Full Code:
Edited broken link
Edited on 19 February 2014 - 08:13 PM
theoriginalbit #14
Posted 19 February 2014 - 09:15 PM
your problem is with this code

if receives <= maxReceives then
  receives=receives+1
elseif receives >= maxReceives then
  return true
end
I suggest the following code

local function waitForResponse(whatToReceive, maxReceives)
  local timeout = os.startTimer(15)
  local receives = 0
  while true do
	local event = {os.pullEventRaw()}
	if event[1] == "modem_message" and event[5] == whatToReceive then
	  receives = receives + 1
	  if receives >= maxReceives then
		return true
	  end
	elseif event[1] == "timer" and event[2] == timeout then
	  return false
	end
  end
end
Edited on 19 February 2014 - 08:15 PM
Goof #15
Posted 19 February 2014 - 09:16 PM
Errrrhm? what is this? never heard of multi-variable-table-pull-eventraw-thingy

local event = {os.pullEventRaw()},t1,sender,receiver,response,distance=os.pullEvent()
?

EDIT: nevermind xD
Edited on 19 February 2014 - 08:18 PM
theoriginalbit #16
Posted 19 February 2014 - 09:17 PM
Errrrhm? what is this? never heard of multivariabletablepulleventrawthingy

local event = {os.pullEventRaw()},t1,sender,receiver,response,distance=os.pullEvent()
?
it was a mistake, I fixed it hoped you'd see the fix :P/>
Goof #17
Posted 19 February 2014 - 10:13 PM
Oh wooooow!

When this is running on my local server, the server console says:
Spoiler

04:19:17 [INFO] CONSOLE: Forcing save..
04:19:17 [INFO] CONSOLE: Save complete.
04:19:24 [INFO] computercraft: Error running task.
04:19:24 [INFO] computercraft: Error running task.
04:19:24 [SEVERE] java.lang.ThreadDeath
04:19:24 [SEVERE]	   at java.lang.Thread.stop(Unknown Source)
04:19:24 [SEVERE]	   at dan200.computer.core.ComputerThread$1.run(ComputerThr
ead.java:153)
04:19:24 [SEVERE]	   at java.lang.Thread.run(Unknown Source)
04:19:24 [SEVERE] java.lang.ThreadDeath
04:19:24 [SEVERE]	   at java.lang.Thread.stop(Unknown Source)
04:19:24 [SEVERE]	   at dan200.computer.core.ComputerThread$1.run(ComputerThr
ead.java:153)
04:19:24 [SEVERE]	   at java.lang.Thread.run(Unknown Source)
04:19:53 [SEVERE] java.lang.NullPointerException
04:19:57 [SEVERE] java.lang.NullPointerException
04:20:15 [INFO] [SERVER] Saving worlds\DIM_MYST8...
04:20:20 [INFO] [MineBackup]  * saving worlds\DIM_MYST8
04:20:20 [INFO] [SERVER] Done!
04:20:45 [INFO] computercraft: Error running task.
04:20:45 [SEVERE] java.lang.ThreadDeath
04:20:45 [SEVERE]	   at java.lang.Thread.stop(Unknown Source)
04:20:45 [SEVERE]	   at dan200.computer.core.ComputerThread$1.run(ComputerThr
ead.java:153)
04:20:45 [SEVERE]	   at java.lang.Thread.run(Unknown Source)
04:20:52 [INFO] computercraft: Error running task.
04:20:52 [SEVERE] java.lang.ThreadDeath
04:20:52 [SEVERE]	   at java.lang.Thread.stop(Unknown Source)
04:20:52 [SEVERE]	   at dan200.computer.core.ComputerThread$1.run(ComputerThr
ead.java:153)
04:20:52 [SEVERE]	   at java.lang.Thread.run(Unknown Source)
04:21:07 [INFO] computercraft: Error running task.
04:21:07 [SEVERE] java.lang.ThreadDeath
04:21:07 [SEVERE]	   at java.lang.Thread.stop(Unknown Source)
04:21:07 [SEVERE]	   at dan200.computer.core.ComputerThread$1.run(ComputerThr
ead.java:153)
04:21:07 [SEVERE]	   at java.lang.Thread.run(Unknown Source)
updated log:
Spoiler

2014-02-20 04:15:02 [SEVERE] java.lang.NullPointerException
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.lib.DebugLib$DebugState.callHookFunc(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.lib.DebugLib.debugBytecode(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.LuaClosure.execute(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.LuaClosure.onInvoke(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.LuaClosure.invoke(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.LuaClosure.execute(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.LuaClosure.onInvoke(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.LuaClosure.invoke(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.LuaClosure.execute(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.LuaClosure.call(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.LuaClosure.execute(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.LuaClosure.onInvoke(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.LuaClosure.invoke(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at org.luaj.vm2.LuaThread$State.run(Unknown Source)
2014-02-20 04:15:02 [SEVERE] 	at java.lang.Thread.run(Unknown Source)
2014-02-20 04:15:54 [SEVERE] java.lang.NullPointerException
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.lib.DebugLib$DebugState.callHookFunc(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.lib.DebugLib.debugBytecode(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaClosure.execute(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaClosure.onInvoke(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaClosure.invoke(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaClosure.execute(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaClosure.call(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaClosure.execute(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaClosure.call(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaClosure.execute(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaClosure.onInvoke(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaClosure.invoke(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaClosure.execute(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaClosure.onInvoke(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaClosure.invoke(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at org.luaj.vm2.LuaThread$State.run(Unknown Source)
2014-02-20 04:15:54 [SEVERE] 	at java.lang.Thread.run(Unknown Source)
2014-02-20 04:16:46 [SEVERE] java.lang.NullPointerException
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.lib.DebugLib$DebugState.callHookFunc(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.lib.DebugLib.debugBytecode(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.LuaClosure.execute(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.LuaClosure.onInvoke(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.LuaClosure.invoke(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.LuaClosure.execute(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.LuaClosure.onInvoke(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.LuaClosure.invoke(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.LuaClosure.execute(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.LuaClosure.call(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.LuaClosure.execute(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.LuaClosure.onInvoke(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.LuaClosure.invoke(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at org.luaj.vm2.LuaThread$State.run(Unknown Source)
2014-02-20 04:16:46 [SEVERE] 	at java.lang.Thread.run(Unknown Source)
2014-02-20 04:17:55 [SEVERE] java.lang.NullPointerException
2014-02-20 04:18:01 [SEVERE] java.lang.NullPointerException
2014-02-20 04:18:11 [SEVERE] java.lang.NullPointerException
2014-02-20 04:18:12 [SEVERE] java.lang.NullPointerException
2014-02-20 04:18:13 [SEVERE] java.lang.NullPointerException
2014-02-20 04:18:16 [SEVERE] java.lang.NullPointerException
2014-02-20 04:18:20 [SEVERE] java.lang.NullPointerException
2014-02-20 04:18:27 [SEVERE] java.lang.NullPointerException
2014-02-20 04:18:35 [SEVERE] java.lang.NullPointerException
2014-02-20 04:18:37 [SEVERE] java.lang.NullPointerException
2014-02-20 04:18:40 [SEVERE] java.lang.NullPointerException
2014-02-20 04:18:49 [SEVERE] java.lang.NullPointerException
2014-02-20 04:18:52 [SEVERE] java.lang.NullPointerException
2014-02-20 04:18:54 [SEVERE] java.lang.NullPointerException
2014-02-20 04:19:17 [INFO] CONSOLE: Forcing save..
2014-02-20 04:19:17 [INFO] CONSOLE: Save complete.
2014-02-20 04:19:24 [INFO] computercraft: Error running task.
2014-02-20 04:19:24 [SEVERE] java.lang.ThreadDeath
2014-02-20 04:19:24 [INFO] computercraft: Error running task.
2014-02-20 04:19:24 [SEVERE] 	at java.lang.Thread.stop(Unknown Source)
2014-02-20 04:19:24 [SEVERE] 	at dan200.computer.core.ComputerThread$1.run(ComputerThread.java:153)
2014-02-20 04:19:24 [SEVERE] 	at java.lang.Thread.run(Unknown Source)
2014-02-20 04:19:24 [SEVERE] java.lang.ThreadDeath
2014-02-20 04:19:24 [SEVERE] 	at java.lang.Thread.stop(Unknown Source)
2014-02-20 04:19:24 [SEVERE] 	at dan200.computer.core.ComputerThread$1.run(ComputerThread.java:153)
2014-02-20 04:19:24 [SEVERE] 	at java.lang.Thread.run(Unknown Source)
2014-02-20 04:19:53 [SEVERE] java.lang.NullPointerException
2014-02-20 04:19:57 [SEVERE] java.lang.NullPointerException
2014-02-20 04:20:15 [INFO] [SERVER] Saving worlds\DIM_MYST8...
2014-02-20 04:20:20 [INFO] [MineBackup]  * saving worlds\DIM_MYST8
2014-02-20 04:20:20 [INFO] [SERVER] Done!
2014-02-20 04:20:45 [INFO] computercraft: Error running task.
2014-02-20 04:20:45 [SEVERE] java.lang.ThreadDeath
2014-02-20 04:20:45 [SEVERE] 	at java.lang.Thread.stop(Unknown Source)
2014-02-20 04:20:45 [SEVERE] 	at dan200.computer.core.ComputerThread$1.run(ComputerThread.java:153)
2014-02-20 04:20:45 [SEVERE] 	at java.lang.Thread.run(Unknown Source)
2014-02-20 04:20:52 [INFO] computercraft: Error running task.
2014-02-20 04:20:52 [SEVERE] java.lang.ThreadDeath
2014-02-20 04:20:52 [SEVERE] 	at java.lang.Thread.stop(Unknown Source)
2014-02-20 04:20:52 [SEVERE] 	at dan200.computer.core.ComputerThread$1.run(ComputerThread.java:153)
2014-02-20 04:20:52 [SEVERE] 	at java.lang.Thread.run(Unknown Source)
2014-02-20 04:21:07 [INFO] computercraft: Error running task.
2014-02-20 04:21:07 [SEVERE] java.lang.ThreadDeath
2014-02-20 04:21:07 [SEVERE] 	at java.lang.Thread.stop(Unknown Source)
2014-02-20 04:21:07 [SEVERE] 	at dan200.computer.core.ComputerThread$1.run(ComputerThread.java:153)
2014-02-20 04:21:07 [SEVERE] 	at java.lang.Thread.run(Unknown Source)
No Crash report…. All computers just immediately shutdown.-….. Why? (going to bed for now…)
Edited on 19 February 2014 - 09:18 PM
theoriginalbit #18
Posted 19 February 2014 - 10:15 PM
who knows, it shouldn't be anything the script is doing. have you rebooted the server and tried again?
Goof #19
Posted 19 February 2014 - 10:19 PM
What about the new log i provided?. ( i think its more in details…)

And btw.. im goin to bed for now… so i'll test later… Bye
Goof #20
Posted 20 February 2014 - 04:01 AM
Well After restart and everything… then the first 10 minutes everything works… but then comes the weirdness…

Spoilerzk6F01TK <-Pastebin with server.log

This stupid threadDeath error is so annoying…

I cant use any computers in my world!….


as another example. a guy've posted this:
pastebin
Edited on 20 February 2014 - 03:07 AM
theoriginalbit #21
Posted 20 February 2014 - 04:02 AM
is this while running your program?
Goof #22
Posted 20 February 2014 - 04:08 AM
Yup…

I have the following setup ( taking pic and uploading in a moment )

And then all thjis is running / talkiing to each other ( the center turtles are talking to the turtle , furthest away.. and that turtle furthest away is talking to the center turtles AND the turtle in front.

Uploading pic in a minute

ALBUM (imgur)


http://imgur.com/a/5ro9E#0
Edited on 20 February 2014 - 03:15 AM
Bomb Bloke #23
Posted 20 February 2014 - 04:21 AM
May or may not be related, but on my group's last server (with CC 1.57) I wrote a program designed to do something similar to this one - wait for a bunch of turtles to "report in" - only to find that after implementing it, the MineCraft server had an average uptime of a few hours before stalling (very similar to the behaviour I signed up to this forum to ask about).

We eventually decided to switch packs, but I hadn't finished isolating (I was convinced it was a turtle triggering it, but my last test involved killing ALL of them and it STILL did it). I should be able to get my hands on a world save eventually and keep poking at it until I know for sure. Maybe it's the peripheral-based lockup people mentioned in the bugs section, I dunno.

If I understand you correctly, when this happens you're still able to interact with the world? Mine blocks, place blocks, etc? You just lack ComputerCraft functionality until the MineCraft server is rebooted? What versions of everything are you running?
Goof #24
Posted 20 February 2014 - 04:35 AM
If I understand you correctly, when this happens you're still able to interact with the world? Mine blocks, place blocks, etc? You just lack ComputerCraft functionality until the MineCraft server is rebooted? What versions of everything are you running?
Correct.. Correct. Correct…

And all my mods / things are up to date with 1.6.4 ..

I have 108 mods…
How can i copy a list of them? like is there a modlist on a .txt file? ( technic mod pack )
Edited on 20 February 2014 - 03:36 AM
Bomb Bloke #25
Posted 20 February 2014 - 04:45 AM
Well, that's a bit different to what I've run into in my travels, then - in my cases the entire server was affected, not just the CC components.

Probably worth listing out just the CC-related mods in your case. And actual versions of each, for good measure - "up to date" may not mean to you what it means to us.

Could you also maybe setup your Pastebin link with the current code your turtle server is running? Along with maybe another link for the turtle's code (assuming I haven't missed one already here)?
Goof #26
Posted 20 February 2014 - 04:53 AM
current code my server is running? like the full log??

Well you can get all codes right here:
SpoilerENGINEER: ( 4 turtles on top )
Spoiler77iUTNjV
MINER: ( long row of turtles )
SpoilerdTV0FLVH
chunkLoader: ( front turtle )
SpoilerBfHz7Y6K
MAIN: (back turtle)
Spoiler8WD2dyeD
Edited on 20 February 2014 - 03:54 AM
theoriginalbit #27
Posted 20 February 2014 - 04:56 AM
does it do it without running the program?
Goof #28
Posted 20 February 2014 - 04:58 AM
No… Only when the turtles / the little "quarry" is online / booted up the server starts to be slow and ends up throwing java errors due to ComputerCraft
Bomb Bloke #29
Posted 20 February 2014 - 05:20 AM
I'm sorta guessing for now while I read through, but I'm suspecting maybe you've got a loop of modem transmissions going. Filling up the event queues that store them to the point where it all falls over.

A few quick notes in the mean time:

The "header()" function your miners run is going to slow them down no end, as it attempts to write full lines to their display flat out and non-stop. Assuming you need that line it draws, I recommend rigging things so it's only redrawn when the last one was disturbed.

I don't get why your miners sleep before replying to pings. I'm suspecting you're thinking that the messages will somehow "collide" if you don't - that shouldn't be a problem. They should all get loaded into the server's event queue for it to pull out at its leisure.

Assuming you really want them to sleep in that staggered style, something like this would be more efficient then tons of if/then/elseif statements:

sleep((os.getComputerID() - 24) / 10)
Edited on 20 February 2014 - 04:35 AM
Goof #30
Posted 20 February 2014 - 05:27 AM
The "header()" function your miners run is going to slow them down no end, as it attempts to write full lines to their display flat out and non-stop. Assuming you need that line it draws, I recommend rigging things so it's only redrawn when the last one was disturbed.

well.. what would happen if i just deleted the header from the turtles, and only display information on the main turtle ( the turtle in the back )?

then the miners and engineers wont have to update that?..

Well… the sleep - pong was actually just for testing, when i wanted to sort the turtles depending on their ID… ( Thanks for noticing xD i needed to delete that )

I only need like a sleep of 0.5 seconds to be sure the main computer will've sent the "ping" packet to all computers and be ready to receive…

my todolist right now:
Delete "header()" from miners and engineers.
Remove random sleeping stupid function-weirdness
Make only the Main turtle display anything related to the quarry.

Going to work on that now…
I'll report the result when im done.

Be right back…

REPORT:

Ehh This is weird… everything except the chunkloaderturtle works…. like all miners and engineers work fine…

But the console outputs javaNullPointerException all the time… ( the turtles still work )

BUT!!! ( again but )

I cannot control the chunkloaderturtle at all… I cant reboot, shutdown, terminate it…
Report 2:

Console spams java.lang.NullPointerException and "Computercraft: Error running task"
Edited on 20 February 2014 - 04:41 AM
Bomb Bloke #31
Posted 20 February 2014 - 05:41 AM
I'd posted this as an edit to my post above, but since you might've missed it I'm shifting it down here…

Your "handler()" function on the chunk loader turtle really should be crashing that turtle unless it's getting spammed with messages. When that function runs, if none of the booleans it checks are true, then it'll run indefinitely without yielding. Which means no other function gets a chance to do anything, so the "listener()" function never gets to set the booleans to true, and… well I'd expect ComputerCraft to crash that turtle pretty quickly unless it's getting a ton of messages coming in (at least one for every time "listener()" gets to do anything).

Try adding a sleep at the bottom of that while loop in "handler()".

It also gives me a thought, in that you could try letting ComputerCraft "stall", then start pickaxing turtles. With each one removed, check a spare computer to see if it suddenly "wakes up".
Goof #32
Posted 20 February 2014 - 05:45 AM
When that function runs, if none of the booleans it checks are true, then it'll run indefinitely without yielding. Which means no other function gets a chance to do anything, so the "listener()" function never gets to set the booleans to true, and… well I'd expect ComputerCraft to crash that turtle pretty quickly unless it's getting a ton of messages coming in (at least one for every time "listener()" gets to do anything).

Try adding a sleep at the bottom of that while loop in "handler()".

Oh my! Why… Why did i forget a SLEEP?"!##
Well maybe because i was coding 4 hours straight last night….


Im gonna edit that xD

report in a minute :D/>/>/>

Report:

Everything seems stable ( No java errors in console so far… )
I am going to add the tesseract, and all the things they need, then im gonna report and tell you if its going to crash again…

Report 2:
Just found out that the main turtle is exitting the program after it've registered the turtles… wait whaaAT?
Pastebin
Edited on 20 February 2014 - 04:59 AM
Goof #33
Posted 20 February 2014 - 05:59 AM
Yup… Reported ^^ Im not sure why it quits… its like quitting just before the while true do loop…
(line 148 in pastebin code)

Where did i Ever set true to false?
Edited on 20 February 2014 - 05:00 AM
Bomb Bloke #34
Posted 20 February 2014 - 06:32 AM
So it successfully prints "Booting…" (as per line 141), but then fails to print "Commander forced to stop the chunk" (per line 155)?

Can't say I can see anything that'd cause that behaviour.
Goof #35
Posted 20 February 2014 - 06:34 AM
So it successfully prints "Booting…" (as per line 141), but then fails to print "Commander forced to stop the chunk" (per line 155)?

Yep…

And i have no idea why… The console don't output errors during this… so its a weird problem…it can't be a bug


EDIT: Everything works now…
The server just needed a restart after all the errors happening before xD Why didn't i think of that before now?


EDIT2: Hmmm
I have a question about applied energistics with computercraft….
I want to read how much energy the ME controller is having , due to my core Redstone energy system is crashing and has to go online again(

(well like sometimes the redstone energy cell, seems not to output any power, therefore my sorting system is destroyed ( because ender chest is full ) )

Whenever i connect a ME BRIDGE to a cable then the server crashes….

Is there any other methods, for reading how much power there is in a ME controller / network?
Edited on 20 February 2014 - 08:28 AM
Goof #36
Posted 20 February 2014 - 03:08 PM
Ehhh.. New Issue….


When i set the quarry up, and left the mystcraft world, then after a while the turtles stop working, and sometimes the enderchests is a long way further behind than the turtles current position.

My setup is:
Album

Images uploaded in 10 minutes…

I have no idea why, this is happening, but its happening very much..

And btw… My method of dumping items in the miner's code is pretty weird? can you help me make, so it waits until it've cleared all its inventory except the protected slots?

my code:

engineer: http://pastebin.com/77iUTNjV
miner: http://pastebin.com/dTV0FLVH
chunkloader: http://pastebin.com/BfHz7Y6K
main: http://pastebin.com/8WD2dyeD


EDIT: Updated album with an example of the unsynchronized turtles…
Edited on 20 February 2014 - 05:06 PM
Bomb Bloke #37
Posted 20 February 2014 - 09:02 PM
Sorry, I've no idea about the ME crash.

When i set the quarry up, and left the mystcraft world, then after a while the turtles stop working, and sometimes the enderchests is a long way further behind than the turtles current position.
Beats me, but some safety checking in your movement calls might be worth a shot. I've found that, at random, turtle movement calls (turning, travelling, whatever) and also network transmissions simply don't work - even if they should've. It's rare, but happens often enough that any script that runs for more then a few hours should be taking it into account.

For example, "turtle.forward()", on its own, attempts to move the turtle forward… but if it fails the script just keeps rolling onwards.

"while not turtle.forward() do end", on the other hand, is a very simple loop that doesn't really do anything except check "turtle.forward()" over and over again until it reports success. The turtle moves forward once, but the script doesn't move on until that happens.

If a network transmission you send "needs" to be received, then consider setting up an acknowledgement system so that messages that don't appear to get through are re-sent until you're sure they did.

And btw… My method of dumping items in the miner's code is pretty weird? can you help me make, so it waits until it've cleared all its inventory except the protected slots?
That IS a bit weird - didn't even know you could use a "repeat" without an "until"…

I'd use something like this:

function dump()
  -- Assuming at this point you've already ensured the ender chest is under the turtle;

  local droppable = true

  for i=1,16 do if turtle.getItemCount(i) > 0 then
    for item,slot in pairs(slots) do if i == slot then
      droppable = false
      break
    end end

    if droppable then
      turtle.select(i)
      turtle.dropDown()
    else droppable = true end
  end end
end

Or, if you've no intention of changing your reserved slots:

function dump()
  for i=1,12 do if turtle.getItemCount(i) > 0 then
    turtle.select(i)
    turtle.dropDown()
  end end
end

This all assumes the ender chest is being emptied at a sufficient pace.
Goof #38
Posted 21 February 2014 - 05:55 AM
I'd use something like this:

function dump()
  -- Assuming at this point you've already ensured the ender chest is under the turtle;

  local droppable = true

  for i=1,16 do if turtle.getItemCount(i) > 0 then
	for item,slot in pairs(slots) do if i == slot then
	  droppable = false
	  break
	end end

	if droppable then
	  turtle.select(i)
	  turtle.dropDown()
	else droppable = true end
  end end
end

Or, if you've no intention of changing your reserved slots:

function dump()
  for i=1,12 do if turtle.getItemCount(i) > 0 then
	turtle.select(i)
	turtle.dropDown()
  end end
end


This all assumes the ender chest is being emptied at a sufficient pace.
But… What if the enderchest is full? if the dropdown function fails because the inventory cant accept more items?
Then it has to do a while inventoryHasElements do —- blah blah blah — end?




When i set the quarry up, and left the mystcraft world, then after a while the turtles stop working, and sometimes the enderchests is a long way further behind than the turtles current position.
Beats me, but some safety checking in your movement calls might be worth a shot. I've found that, at random, turtle movement calls (turning, travelling, whatever) and also network transmissions simply don't work - even if they should've. It's rare, but happens often enough that any script that runs for more then a few hours should be taking it into account.

For example, "turtle.forward()", on its own, attempts to move the turtle forward… but if it fails the script just keeps rolling onwards.

"while not turtle.forward() do end", on the other hand, is a very simple loop that doesn't really do anything except check "turtle.forward()" over and over again until it reports success. The turtle moves forward once, but the script doesn't move on until that happens.

If a network transmission you send "needs" to be received, then consider setting up an acknowledgement system so that messages that don't appear to get through are re-sent until you're sure they did.


Oh.. Im gonna edit that right now…
Goof #39
Posted 21 February 2014 - 06:35 AM
Well … Now i can show you the example of the chests falling behind….

<– I had to forcequit all turtles to prevent it from destroying everything


…. And my current code…


Look at pastebin xD

SpoilerMiner Pastebin1
Main Pastebin2
Chunkloader Pastebin3
Engineer Pastebin4
Edited on 21 February 2014 - 05:36 AM
Bomb Bloke #40
Posted 21 February 2014 - 07:23 AM
When removing chests, you do this at around line 58:

    elseif _ID=="chest" then
      turtle.digDown()

You could make that something like this:

    elseif _ID=="chest" then
      if not turtle.digDown() then
        print("I attempted to dig an enderchest but failed!! Press return to continue...")
        read()
      end

This should tell you if a turtle truly attempted to dig a chest it missed, and also cause it to pause at the location where it tried the dig.

It may just turn out that the turtles occasionally fail to dig up chests - maybe they're indestructable when items are going in/out of them? Dunno. I suspect your ultimate answer may just be to turn line 59 into "while not turtle.digDown() do end".

But… What if the enderchest is full? if the dropdown function fails because the inventory cant accept more items?
Then it has to do a while inventoryHasElements do —- blah blah blah — end?
What if? Well, that depends on what you WANT to happen if the chest is full… Do you want the turtles to just carry on with the script (in which case they'll be discarding items one way or another), or specifically wait until they can empty into the chest?

In the case of the latter, you should just be able to use "while not turtle.dropDown() do end" instead of "turtle.dropDown()".
Goof #41
Posted 21 February 2014 - 11:31 AM
Hmmm.. I made a single player test world.. and it seems like it works quite fine…

And for another question…


I was trying to make a miner-synchronize function so the turtles would line up, if something went totally wrong…

but when multiple turtles listen to a broadcaster it seems like they get bugged and go too far… ( this only happens after the first sync)

EDIT: I will upload a video on Youtube…
Brb

sry for bad quality… ( i wanted to post it asap )
[media]http://www.youtube.com/watch?v=Ao3-xKtE4FM&feature=youtu.be[/media]
my broadcaster code ( this is a testing world… so i have to change this code to be in all single turtles so they can match eachother. )

Spoiler

modem=peripheral.wrap("right")
modem.open(2)
term.clear()
term.setCursorPos(1,1)
print("Service running...")
services=0
while true do
	--term.setCursorPos(1,2)
	--term.clearLine()
	--term.write("("..tostring(services)..") services served!")
	local event,side,sender,receiver,msg,distance= os.pullEvent()
	if msg == "ping_client" then
		services=services+1
		modem.transmit(receiver,sender,"ping_broadcast")
	elseif msg =="stop" then
		break
	end
end
print("Served "..services.." services!")

And the synchronizing turtle

Spoiler

if not os.getComputerLabel() then
  os.setComputerLabel("MiningSync")
end
if not fs.exists("startup") then
  if turtle then
	fs.copy("disk/startup", "startup")
	for i = 1, 10 do
	  local option = math.random(1,4)
	  if option==1 then
		turtle.forward()
	  elseif option==2 then
		turtle.back()
	  elseif option==3 then
		turtle.up()
	  elseif option==4 then
		turtle.down()
	  end
	end
	os.reboot()
  end
end

modem=peripheral.wrap("right")
modem.open(2) -- synchronizing channel
local lowestDistance=0
local currentDistance=1000
local round=0


function start()
modem.transmit(2,2,"ping_client")
while true do
  local event,side,sender,receiver,msg,distance=os.pullEvent("modem_message")
  if msg=="ping_broadcast" then
	currentDistance=distance
	turtle.forward()
	modem.transmit(2,2,"ping_client")
	local event,side,sender,receiver,msg,distance=os.pullEvent("modem_message")
	if distance < currentDistance then
	  currentDistance = distance
	  to="forward"
	elseif distance > currentDistance then
	  turtle.back()
	  to="back"
	  modem.transmit(2,2,"ping_client")
	  local event,side,sender,receiver,msg,distance=os.pullEvent("modem_message")
	  currentDistance = distance
	end
	break
  end
end
print(to)
while true do
  if to=="back" then
	if round == 0 then
	  modem.transmit(2,2,"ping_client")
	  while true do
		local event,side,sender,receiver,msg,distance1=os.pullEvent("modem_message")
		if msg=="ping_broadcast" then
		  print("Broadcaster is "..distance1.." blocks away!")
		  turtle.back()
		  modem.transmit(2,2,"ping_client")
		  while true do
			local event,side,sender,receiver,msg,distance2=os.pullEvent("modem_message")
			if msg=="ping_broadcast" then
			  print("Broadcaster is "..distance2.." blocks away!")
			  if distance2<distance1 then="" to="back" round="0" elseif="" distance2="">distance1 then
				to="forward"
				round=1
			  end
			  break
			end
		  end
		  break
		end
	  end
	else
	  turtle.back()
	  break
	end
  end
  if to=="forward" then
	if round==0 then
	  modem.transmit(2,2,"ping_client")
	  while true do
		local event,side,sender,receiver,msg,distance1=os.pullEvent("modem_message")
		if msg=="ping_broadcast" then
		  print("Broadcaster is "..distance1.." blocks away!")
		  turtle.forward()
		  modem.transmit(2,2,"ping_client")
		  while true do
			local event,side,sender,receiver,msg,distance2=os.pullEvent("modem_message")
			if msg=="ping_broadcast" then
			  print("Broadcaster is "..distance2.." blocks away!")
			  if distance2<distance1 then="" to="forward" round="0" elseif="" distance2="">distance1 then
				to="back"
				round=1
			  end
			  break
			end
		  end
		  break
		end
	  end
	else
	  turtle.forward()
	  break
	end
  end
end
end

while true do
  local event,side,sender,receiver,msg,dist=os.pullEvent()
  if msg=="sync" then
	start()
	modem.transmit(receiver,sender,"synchronizing ended!")
	lowestDistance=0
	currentDistance=1000
	round=0
  end
end

</distance1></distance1>
Edited on 21 February 2014 - 11:07 AM
Bomb Bloke #42
Posted 21 February 2014 - 09:02 PM
I've sorta skimmed through, but it looks like you're not always checking the messages as they come in - for eg, line 38 of the turtle's code will react to ANY message from ANY of the systems in the swarm, meaning the distance values you act on are quite random! Ditto for 46.

Personally I'd prefer a system whereby each turtle sticks to its own channel - consider having them open a channel equal to their ID, specifying that same value when recommending the "reply" channel, and having the server respond on that channel. In this way each turtle should only ever get incoming messages that're specifically intended to reach them.

(Basically - mimic how the rednet API expects you to use channels.)