
net
Messages between computers, by cable, radio or Wi-Fi.
The net library lets computers talk to each other. One computer sends a piece of data (a number, a word, a whole table of readings), and the other one receives it as a message event. With it, a sensor in the warehouse can tell the factory how much iron is left, a dashboard can show every machine of the base on one LCD Monitor, and a remote control can open a door on the other side of the map.
-- on the warehouse computer: tell computer #12 how much iron there is
net.send(12, {item = "minecraft:iron_ingot", count = 1830}, "stock")
-- on computer #12: wait for the next message and show it
local m = net.receive()
print(m.data.item .. ": " .. m.data.count)Which computers have it, and how they reach each other:
| computer | net | links |
|---|---|---|
| Tube Computer, Transistor Mainframe | no | none: net is nil |
| Minicomputer | yes | Data Cable only |
| Personal Computer, Microcontroller, Modern Computer | yes | Data Cable, Radio Modem, Wi-Fi Router |
How cables, radios and routers connect computers, with their ranges, is explained in Networks. This page is about the functions. A program meant to run anywhere can test for the library first:
if net == nil then
print("This computer has no network port.")
return
end
print("Network ready")This computer has no network port.
(This is what a Tube Computer shows. A Minicomputer or newer prints Network ready.)
net.id() | The address of this computer, the number other computers use to send it a message. It is the same as os.id(). |
net.send(id, data [, channel]) | Sends data to the computer whose address is id. |
net.broadcast(data [, channel]) | Sends data to every computer in reach, and returns how many accepted it. |
net.receive([timeout]) | Waits for the next message and returns it. With a timeout, gives up after that many seconds and returns nil. |
net.computers() | The computers this one can send to right now, each one as a table: |
net.networks() | The Wi-Fi networks this computer is on: those of its own Wi-Fi Routers, and those of the routers its radios reach. Each one is a table: |
net.wireless() | Tells whether the computer has a radio: a Radio Modem or a Wi-Fi Router touching it, or anywhere on its Data Cable network. Always false on a Minicomputer, which only uses cables. |
Addresses
Every computer has an address: a number given the first time it is placed, the same number os.id() returns and the shell command id prints. It stays with the computer when you break it or pick it up with a wrench and place it somewhere else. Messages are sent to an address.
The address of this computer, the number other computers use to send it a message. It is the same as os.id().
- number
- the address of this computer
print("My address: " .. net.id())Write it on a sign next to the computer, or give the computer a name with the shell command label (or os.label): net.computers shows the names of the computers in reach, so a program can find "the warehouse" without knowing its number.
See also net.computers()
Sending
A message carries one value, the data: nil, a boolean, a number, a string, or a table made of those (tables in tables are fine). A function cannot travel: the program stops with cannot send a function. The receiver gets a copy: changing your table after sending it does not change what the other computer got.
A message also has a channel, a short word you choose ("stock", "door", "chat"), "default" when you give none. Channels let one computer run several conversations and ignore the ones it does not care about.
A message has a size limit, checked when you send it:
- at most 2048 values, where a table counts one, each of its values one, and each key of a table with names one more (
{x = 1, y = 2}counts 5, a list of 1000 numbers counts 1001); - at most 32768 characters in all its strings, keys included;
- at most 32 tables inside each other.
Past those limits the program stops with message too large (or table is too deep (or cyclic) to convert for a table that contains itself). For more, split the data into several messages.
Sending is instant: the message is in the receiver's queue in the same tick. It is accepted only when:
- the receiver is in reach: on the same Data Cable network, or linked by radio or Wi-Fi (see
Networks); - it is switched on, in a chunk that is loaded and ticking;
- its receive buffer is not full: the messages waiting in its queue may take up to a quarter of its memory (4 K cells on a Microcontroller, 8 K on a Minicomputer, 32 K on a Personal Computer, 256 K on a Modern Computer). Past that, new messages are refused, like a full network buffer, instead of crashing the receiving program.
true means "the message is in the receiver's queue", not "the receiver handled it". A computer that sits at the shell prompt, with no program running, throws away the messages it gets, although net.send returned true: a receiver must run as a program, usually its startup. If you must know that an order was carried out, have the receiver answer (see the patterns below and Networks).
Sends data to the computer whose address is id.
idnumber- the address of the receiver
dataany- the value to send: nil, boolean, number, string or a table of those
channelstring optional- the channel,
"default"when left out
- boolean
truewhen the receiver accepted the message,falseotherwise
local FACTORY = 12
local ok = net.send(FACTORY, {type = "order", item = "minecraft:iron_ingot", count = 64}, "orders")
if not ok then
print("The factory does not answer: out of reach, switched off or overloaded.")
endnet.send returns false when no computer with that address is in reach (it is too far, in an unloaded chunk, in another dimension, or it is this very computer), when it is switched off, or when its receive buffer is full. It does not wait: the program goes on at once.
The data can be left out, for a message whose channel says everything:
net.send(12, nil, "ping")Wrong arguments stop the program: bad argument #1 to 'send' (number expected, got string) when the address is not a number, bad argument #3 to 'send' (string expected, got number) when the channel is not a string.
Besides its 32 instructions, a send costs one instruction for each value of the message and one for every 16 characters (the copy), plus the search for the receiver: one instruction per block space around the computer's Data Cables and, on a Personal Computer, a Microcontroller or a Modern Computer, one per computer loaded in the world. On a large network, send a few big messages rather than many small ones.
See also net.broadcast() net.receive()
Sends data to every computer in reach, and returns how many accepted it.
dataany- the value to send, with the same rules as
net.send channelstring optional- the channel,
"default"when left out
- number
- how many computers accepted the message
A broadcast suits news that anyone may want: a sensor reading, an alarm, "I am here". The computers that do not care simply ignore the channel.
local count = net.broadcast("creeper at the north gate!", "alarm")
print("Warned " .. count .. " computers")A computer that is off, out of reach or overloaded is skipped: it is not counted, and there is no error. 0 means nobody heard you. A broadcast costs 64 instructions, plus the copy and the search like net.send, plus 8 per computer in reach.
Never answer a broadcast with a broadcast on the same channel: two computers doing that bounce the message between them forever. Reply with net.send(m.sender, ...), and see Networks for relays.
See also net.send()
Receiving
Messages arrive in the computer's event queue, as message events, and wait there until the program reads them, even while it sleeps. A message is a table:
| field | content |
|---|---|
name | always "message" |
sender | the address of the computer that sent it |
channel | the channel it was sent on |
data | a copy of the data (absent when nil was sent) |
via | how it came: "cable", "radio" or "wifi" |
distance | the distance between the two computers, in blocks, to a tenth |
Waits for the next message and returns it. With a timeout, gives up after that many seconds and returns nil.
timeoutnumber optional- the longest wait in seconds; without it, waits as long as it takes
- table|nil
- the message, or
nilwhen the time ran out
print("Listening as #" .. net.id())
while true do
local m = net.receive()
print("#" .. m.sender .. " on " .. m.channel .. ": " .. tostring(m.data))
endThe fields of a message, written here by hand to show what a program receives:
local m = {name = "message", sender = 7, channel = "stock", via = "wifi", distance = 182.4,
data = {item = "minecraft:iron_ingot", count = 1830}}
print("from #" .. m.sender .. " by " .. m.via .. ", " .. m.distance .. " blocks away")
print(m.data.item .. ": " .. m.data.count)from #7 by wifi, 182.4 blocks away minecraft:iron_ingot: 1830
The timeout is rounded up to the next tick (a twentieth of a second), and is at least one tick: net.receive(0) returns a message already waiting, or nil one tick later. Use a timeout whenever an answer may never come, so that the program does not hang:
net.send(12, nil, "ping")
local m = net.receive(2)
if m == nil then
print("No answer within 2 seconds")
endWhile it waits, net.receive throws away every other event: timers, keys, clicks, redstone changes. A program that needs those too uses os.pull_event() without a filter and tests e.name, as below.
The same messages come out of os.pull_event, which is the way to wait for messages and something else. os.pull_event("message") does exactly what net.receive() does. A warehouse computer that reports its stock every 10 seconds and answers questions at any moment:
local vault = peripheral.wrap("back")
local timer = os.start_timer(10)
while true do
local e = os.pull_event()
if e.name == "message" and e.channel == "stock" and e.data == "how much?" then
net.send(e.sender, vault.count("minecraft:iron_ingot"), "stock")
elseif e.name == "timer" and e.id == timer then
net.broadcast(vault.count("minecraft:iron_ingot"), "stock")
timer = os.start_timer(10)
end
endFilter what you accept. Any computer in reach can send on any channel, so check the channel, the sender and the shape of the data before acting. A door that only obeys the two control rooms:
local TRUSTED = {[4] = true, [9] = true}
local function accept(m)
return m.channel == "door" and TRUSTED[m.sender] == true
end
print(accept({sender = 4, channel = "door", data = "open"}))
print(accept({sender = 5, channel = "door", data = "open"}))
print(accept({sender = 9, channel = "chat", data = "hi"}))true false false
In the real program, accept sits in the receive loop: if accept(m) then rs.set("top", m.data == "open") end. m.via == "cable" is another useful test: only a computer wired to yours can pass it.
See also net.send() os.pull_event() Events
Who is in reach
The computers this one can send to right now, each one as a table:
- table
- a list of
{id, via, distance, label}, one per computer in reach
| field | content |
|---|---|
id | its address |
via | "cable", "radio" or "wifi" |
distance | the distance in blocks, to a tenth |
label | its name (given with label or os.label), absent when it has none |
The computers on the same cable come first. A computer that is switched off is listed (it is in reach), but net.send to it returns false.
for _, c in ipairs(net.computers()) do
print("#" .. c.id, c.via, c.distance .. " blocks", c.label or "")
endFind a computer by its label instead of writing its address in the program, so that it keeps working when you replace a computer:
local function find(label)
for _, c in ipairs(net.computers()) do
if c.label == label then return c.id end
end
return nil
end
local warehouse = find("warehouse")
if warehouse then
net.send(warehouse, "how much?", "stock")
endnet.computers costs 64 instructions plus the search (see net.send): call it once at start, not in every turn of a loop.
See also net.send() net.networks()
The Wi-Fi networks this computer is on: those of its own Wi-Fi Routers, and those of the routers its radios reach. Each one is a table:
- table
- a list of
{ssid, routers, distance, own}, one per Wi-Fi network in reach
| field | content |
|---|---|
ssid | the name of the network |
routers | how many routers of that name take part, across the dimension |
distance | the distance to the nearest of them, in blocks, to a tenth |
own | true when one of them belongs to this computer (touching it or on its cable) |
for _, n in ipairs(net.networks()) do
local kind = "nearby"
if n.own then kind = "mine" end
print(n.ssid .. ": " .. n.routers .. " routers, nearest " .. n.distance .. " blocks (" .. kind .. ")")
endThe list is empty on a Minicomputer, without any router in reach, and with routers that have no SSID yet: a router gets its network name from a program, with radio.set_ssid().
See also net.computers() radio.set_ssid()
Tells whether the computer has a radio: a Radio Modem or a Wi-Fi Router touching it, or anywhere on its Data Cable network. Always false on a Minicomputer, which only uses cables.
- boolean
truewhen a Radio Modem or a Wi-Fi Router is attached
if not net.wireless() then
print("No radio: only computers on the cable will hear me.")
endCommon patterns
A chat. Each line you type goes to every computer in reach; the messages received meanwhile are shown after each line (press Enter on an empty line to look for new ones). The messages are tables with the author's name, so that the others know who speaks:
local me = os.label() or ("#" .. net.id())
print("Chat as " .. me .. ". Type /quit to leave.")
while true do
write("> ")
local line = read()
if line == "/quit" then break end
if line ~= "" then
local heard = net.broadcast({from = me, text = line}, "chat")
print("(" .. heard .. " listening)")
end
local m = net.receive(0)
while m do
if m.channel == "chat" and type(m.data) == "table" then
print(tostring(m.data.from) .. ": " .. tostring(m.data.text))
end
m = net.receive(0)
end
endMessages that arrive while read() waits for your line are kept in the queue, so none is lost.
A question with a timeout and retries. A message can be lost: the other computer was off, overloaded, or its chunk was not loaded. A client that asks the factory for its speed tries three times, and recognises the answer by an id it puts in the question:
local FACTORY = 12
local next_id = 1
local function ask(question)
local id = next_id
next_id = next_id + 1
for attempt = 1, 3 do
net.send(FACTORY, {type = "request", id = id, question = question}, "factory")
local deadline = os.clock() + 2
while os.clock() < deadline do
local m = net.receive(deadline - os.clock())
if m and m.sender == FACTORY and type(m.data) == "table"
and m.data.type == "reply" and m.data.id == id then
return m.data.answer
end
end
end
return nil
end
local rpm = ask("speed")
if rpm == nil then print("The factory does not answer") else print("Factory at " .. rpm .. " RPM") endThe factory side answers each request with the same id:
local motor = peripheral.wrap("top") -- a Rotation Speed Controller
while true do
local m = net.receive()
local d = m.data
if m.channel == "factory" and type(d) == "table" and d.type == "request" then
local answer = nil
if d.question == "speed" then answer = motor.speed() end
net.send(m.sender, {type = "reply", id = d.id, answer = answer}, "factory")
end
endA retry may reach the factory twice (when only the answer was lost), so keep requests safe to repeat: "set the speed to 64" rather than "add 16 RPM".
Sensors and a dashboard. Each sensor broadcasts its reading every few seconds on the "sensors" channel; the dashboard keeps the last reading of each one and redraws every second, which also shows how old each value is:
-- on each sensor (a Microcontroller next to a vault)
local vault = peripheral.wrap("back")
while true do
net.broadcast({what = "iron", value = vault.count("minecraft:iron_ingot")}, "sensors")
sleep(5)
end-- on the dashboard
local readings = {}
while true do
local m = net.receive(1) -- nil after one second: time to redraw anyway
if m and m.channel == "sensors" and type(m.data) == "table" then
readings[m.sender] = {what = m.data.what, value = m.data.value, at = os.clock()}
end
term.clear()
term.set_cursor(1, 1)
for id, r in pairs(readings) do
local age = math.floor(os.clock() - r.at)
print("#" .. id .. " " .. tostring(r.what) .. ": " .. tostring(r.value) .. " (" .. age .. " s ago)")
end
endA sensor whose age keeps growing has stopped talking: broken, switched off, or its chunk unloaded. A complete three-computer version of this, with commands and acknowledgements, is in Networks. The built-in programs talk, listen and pong are other complete examples.
See also net.networks()