
net
Des messages entre ordinateurs, par câble, radio ou Wi-Fi.
La bibliothèque net permet aux ordinateurs de se parler. Un ordinateur envoie une donnée (un nombre, un mot, toute une table de mesures), et l'autre la reçoit sous la forme d'un évènement message. Grâce à elle, un capteur de l'entrepôt peut dire à l'usine combien de fer il reste, un tableau de bord peut afficher toutes les machines de la base sur un seul Moniteur LCD, et une télécommande peut ouvrir une porte à l'autre bout de la carte.
-- sur l'ordinateur de l'entrepôt : dire à l'ordinateur n°12 combien il y a de fer
net.send(12, {objet = "minecraft:iron_ingot", nombre = 1830}, "stock")
-- sur l'ordinateur n°12 : attendre le prochain message et l'afficher
local m = net.receive()
print(m.data.objet .. " : " .. m.data.nombre)Quels ordinateurs l'ont, et comment ils se joignent :
| ordinateur | net | liaisons |
|---|---|---|
| Calculateur à tubes, Ordinateur à transistors | non | aucune : net vaut nil |
| Mini-ordinateur | oui | Câble de données seulement |
| Micro-ordinateur, Microcontrôleur, Ordinateur moderne | oui | Câble de données, Modem radio, Routeur Wi-Fi |
La façon dont câbles, radios et routeurs relient les ordinateurs, avec leurs portées, est expliquée dans Réseaux. Cette page-ci parle des fonctions. Un programme fait pour tourner partout peut d'abord vérifier que la bibliothèque existe :
if net == nil then
print("Cet ordinateur n'a pas de port réseau.")
return
end
print("Réseau prêt")Cet ordinateur n'a pas de port réseau.
(C'est ce qu'affiche un Calculateur à tubes. Un Mini-ordinateur ou plus récent affiche Réseau prêt.)
net.id() | L'adresse de cet ordinateur, le numéro que les autres utilisent pour lui envoyer un message. C'est la même chose que os.id(). |
net.send(id, data [, channel]) | Envoie data à l'ordinateur dont l'adresse est id. |
net.broadcast(data [, channel]) | Envoie data à tous les ordinateurs joignables, et renvoie combien l'ont accepté. |
net.receive([timeout]) | Attend le prochain message et le renvoie. Avec un timeout, abandonne au bout de ce nombre de secondes et renvoie nil. |
net.computers() | Les ordinateurs auxquels celui-ci peut envoyer un message en ce moment, chacun sous forme de table : |
net.networks() | Les réseaux Wi-Fi dont fait partie cet ordinateur : ceux de ses propres Routeurs Wi-Fi, et ceux des routeurs que ses radios atteignent. Chacun est une table : |
net.wireless() | Dit si l'ordinateur a une radio : un Modem radio ou un Routeur Wi-Fi qui le touche, ou n'importe où sur son réseau de Câbles de données. Toujours false sur un Mini-ordinateur, qui n'utilise que les câbles. |
Adresses
Chaque ordinateur a une adresse : un numéro reçu la première fois qu'il est posé, le même que renvoie os.id() et qu'affiche la commande id du shell. Il reste attaché à l'ordinateur quand vous le cassez ou le ramassez à la clé pour le poser ailleurs. C'est à une adresse qu'on envoie un message.
L'adresse de cet ordinateur, le numéro que les autres utilisent pour lui envoyer un message. C'est la même chose que os.id().
- number
- l'adresse de cet ordinateur
print("Mon adresse : " .. net.id())Notez-la sur une pancarte à côté de l'ordinateur, ou donnez un nom à l'ordinateur avec la commande label du shell (ou os.label) : net.computers montre le nom des ordinateurs joignables, et un programme peut ainsi trouver « l'entrepôt » sans connaître son numéro.
Voir aussi net.computers()
Envoyer
Un message transporte une valeur, la donnée : nil, un booléen, un nombre, une chaîne, ou une table faite de tout cela (des tables dans des tables, aucun souci). Une fonction ne voyage pas : le programme s'arrête avec cannot send a function. Le destinataire reçoit une copie : modifier votre table après l'envoi ne change pas ce que l'autre ordinateur a reçu.
Un message a aussi un canal, un petit mot de votre choix ("stock", "porte", "chat"), "default" si vous n'en donnez pas. Les canaux permettent à un ordinateur de mener plusieurs conversations et d'ignorer celles qui ne le concernent pas.
La taille d'un message est limitée, et vérifiée à l'envoi :
- 2048 valeurs au plus, où une table compte une, chacune de ses valeurs une, et chaque clé d'une table à noms une de plus (
{x = 1, y = 2}compte 5, une liste de 1000 nombres compte 1001) ; - 32768 caractères au plus dans l'ensemble de ses chaînes, clés comprises ;
- 32 tables imbriquées au plus.
Au-delà, le programme s'arrête avec message too large (ou table is too deep (or cyclic) to convert pour une table qui se contient elle-même). Pour envoyer plus, découpez la donnée en plusieurs messages.
L'envoi est immédiat : le message est dans la file du destinataire dès le même tick. Il n'est accepté que si :
- le destinataire est joignable : sur le même réseau de Câbles de données, ou relié par radio ou Wi-Fi (voir
Réseaux) ; - il est allumé, dans un chunk chargé et actif ;
- son tampon de réception n'est pas plein : les messages en attente dans sa file peuvent occuper jusqu'au quart de sa mémoire (4 K cellules sur un Microcontrôleur, 8 K sur un Mini-ordinateur, 32 K sur un Micro-ordinateur, 256 K sur un Ordinateur moderne). Au-delà, les nouveaux messages sont refusés, comme par un tampon réseau plein, au lieu de faire planter le programme qui reçoit.
true veut dire « le message est dans la file du destinataire », pas « le destinataire l'a traité ». Un ordinateur qui attend à l'invite du shell, sans programme lancé, jette les messages qu'il reçoit, bien que net.send ait renvoyé true : un destinataire doit tourner en programme, en général son startup. Pour savoir qu'un ordre a été exécuté, faites répondre le destinataire (voir les motifs plus bas et Réseaux).
Envoie data à l'ordinateur dont l'adresse est id.
idnumber- l'adresse du destinataire
dataany- la valeur à envoyer : nil, booléen, nombre, chaîne ou table de ces valeurs
channelstring facultatif- le canal,
"default"s'il est omis
- boolean
truesi le destinataire a accepté le message,falsesinon
local USINE = 12
local ok = net.send(USINE, {type = "commande", objet = "minecraft:iron_ingot", nombre = 64}, "commandes")
if not ok then
print("L'usine ne répond pas : hors de portée, éteinte ou saturée.")
endnet.send renvoie false quand aucun ordinateur de cette adresse n'est joignable (trop loin, dans un chunk non chargé, dans une autre dimension, ou c'est cet ordinateur lui-même), quand il est éteint, ou quand son tampon de réception est plein. Il n'attend pas : le programme continue tout de suite.
La donnée peut être omise, pour un message dont le canal dit tout :
net.send(12, nil, "ping")De mauvais arguments arrêtent le programme : bad argument #1 to 'send' (number expected, got string) quand l'adresse n'est pas un nombre, bad argument #3 to 'send' (string expected, got number) quand le canal n'est pas une chaîne.
En plus de ses 32 instructions, un envoi coûte une instruction par valeur du message et une tous les 16 caractères (la copie), plus la recherche du destinataire : une instruction par case autour des Câbles de données de l'ordinateur et, sur un Micro-ordinateur, un Microcontrôleur ou un Ordinateur moderne, une par ordinateur chargé dans le monde. Sur un grand réseau, envoyez peu de gros messages plutôt que beaucoup de petits.
Voir aussi net.broadcast() net.receive()
Envoie data à tous les ordinateurs joignables, et renvoie combien l'ont accepté.
dataany- la valeur à envoyer, avec les mêmes règles que
net.send channelstring facultatif- le canal,
"default"s'il est omis
- number
- le nombre d'ordinateurs qui ont accepté le message
Une diffusion convient aux nouvelles qui peuvent intéresser n'importe qui : une mesure, une alarme, un « je suis là ». Les ordinateurs que ça ne concerne pas ignorent simplement le canal.
local nombre = net.broadcast("creeper à la porte nord !", "alarme")
print(nombre .. " ordinateurs prévenus")Un ordinateur éteint, hors de portée ou saturé est sauté : il n'est pas compté, et il n'y a pas d'erreur. 0 veut dire que personne ne vous a entendu. Une diffusion coûte 64 instructions, plus la copie et la recherche comme net.send, plus 8 par ordinateur joignable.
Ne répondez jamais à une diffusion par une diffusion sur le même canal : deux ordinateurs qui font ça se renvoient le message sans fin. Répondez avec net.send(m.sender, ...), et voyez Réseaux pour les relais.
Voir aussi net.send()
Recevoir
Les messages arrivent dans la file d'évènements de l'ordinateur, sous forme d'évènements message, et y attendent que le programme les lise, même pendant qu'il dort. Un message est une table :
| champ | contenu |
|---|---|
name | toujours "message" |
sender | l'adresse de l'ordinateur qui l'a envoyé |
channel | le canal sur lequel il a été envoyé |
data | une copie de la donnée (absente si c'est nil qui a été envoyé) |
via | le chemin suivi : "cable", "radio" ou "wifi" |
distance | la distance entre les deux ordinateurs, en blocs, au dixième |
Attend le prochain message et le renvoie. Avec un timeout, abandonne au bout de ce nombre de secondes et renvoie nil.
timeoutnumber facultatif- l'attente maximale en secondes ; sans elle, attend aussi longtemps qu'il faut
- table|nil
- le message, ou
nilsi le temps est écoulé
print("J'écoute, je suis le n°" .. net.id())
while true do
local m = net.receive()
print("n°" .. m.sender .. " sur " .. m.channel .. " : " .. tostring(m.data))
endLes champs d'un message, écrits ici à la main pour montrer ce que reçoit un programme :
local m = {name = "message", sender = 7, channel = "stock", via = "wifi", distance = 182.4,
data = {objet = "minecraft:iron_ingot", nombre = 1830}}
print("du n°" .. m.sender .. " par " .. m.via .. ", à " .. m.distance .. " blocs")
print(m.data.objet .. " : " .. m.data.nombre)du n°7 par wifi, à 182.4 blocs minecraft:iron_ingot : 1830
Le délai est arrondi au tick suivant (un vingtième de seconde), et vaut au moins un tick : net.receive(0) renvoie un message déjà en attente, ou nil un tick plus tard. Mettez un délai dès qu'une réponse risque de ne jamais venir, pour que le programme ne reste pas bloqué :
net.send(12, nil, "ping")
local m = net.receive(2)
if m == nil then
print("Pas de réponse en 2 secondes")
endPendant qu'il attend, net.receive jette tous les autres évènements : minuteurs, touches, clics, changements de redstone. Un programme qui en a aussi besoin utilise os.pull_event() sans filtre et teste e.name, comme ci-dessous.
Les mêmes messages sortent de os.pull_event, qui permet d'attendre des messages et autre chose. os.pull_event("message") fait exactement comme net.receive(). Un ordinateur d'entrepôt qui annonce son stock toutes les 10 secondes et répond aux questions à tout moment :
local coffre = peripheral.wrap("back")
local minuteur = os.start_timer(10)
while true do
local e = os.pull_event()
if e.name == "message" and e.channel == "stock" and e.data == "combien ?" then
net.send(e.sender, coffre.count("minecraft:iron_ingot"), "stock")
elseif e.name == "timer" and e.id == minuteur then
net.broadcast(coffre.count("minecraft:iron_ingot"), "stock")
minuteur = os.start_timer(10)
end
endFiltrez ce que vous acceptez. N'importe quel ordinateur joignable peut envoyer sur n'importe quel canal : vérifiez le canal, l'expéditeur et la forme de la donnée avant d'agir. Une porte qui n'obéit qu'aux deux salles de contrôle :
local DE_CONFIANCE = {[4] = true, [9] = true}
local function accepter(m)
return m.channel == "porte" and DE_CONFIANCE[m.sender] == true
end
print(accepter({sender = 4, channel = "porte", data = "ouvrir"}))
print(accepter({sender = 5, channel = "porte", data = "ouvrir"}))
print(accepter({sender = 9, channel = "chat", data = "salut"}))true false false
Dans le vrai programme, accepter se place dans la boucle de réception : if accepter(m) then rs.set("top", m.data == "ouvrir") end. m.via == "cable" est un autre test utile : seul un ordinateur câblé au vôtre peut le passer.
Voir aussi net.send() os.pull_event() Évènements
Qui est joignable
Les ordinateurs auxquels celui-ci peut envoyer un message en ce moment, chacun sous forme de table :
- table
- une liste de
{id, via, distance, label}, une entrée par ordinateur joignable
| champ | contenu |
|---|---|
id | son adresse |
via | "cable", "radio" ou "wifi" |
distance | la distance en blocs, au dixième |
label | son nom (donné avec label ou os.label), absent s'il n'en a pas |
Les ordinateurs du même câble viennent en premier. Un ordinateur éteint est dans la liste (il est joignable), mais net.send vers lui renvoie false.
for _, o in ipairs(net.computers()) do
print("n°" .. o.id, o.via, o.distance .. " blocs", o.label or "")
endRetrouvez un ordinateur par son nom plutôt que d'écrire son adresse dans le programme : il continuera de marcher le jour où vous remplacerez un ordinateur.
local function trouver(nom)
for _, o in ipairs(net.computers()) do
if o.label == nom then return o.id end
end
return nil
end
local entrepot = trouver("entrepot")
if entrepot then
net.send(entrepot, "combien ?", "stock")
endnet.computers coûte 64 instructions plus la recherche (voir net.send) : appelez-la une fois au démarrage, pas à chaque tour de boucle.
Voir aussi net.send() net.networks()
Les réseaux Wi-Fi dont fait partie cet ordinateur : ceux de ses propres Routeurs Wi-Fi, et ceux des routeurs que ses radios atteignent. Chacun est une table :
- table
- une liste de
{ssid, routers, distance, own}, une entrée par réseau Wi-Fi joignable
| champ | contenu |
|---|---|
ssid | le nom du réseau |
routers | le nombre de routeurs de ce nom qui y participent, dans toute la dimension |
distance | la distance du plus proche d'entre eux, en blocs, au dixième |
own | true si l'un d'eux appartient à cet ordinateur (il le touche ou il est sur son câble) |
for _, r in ipairs(net.networks()) do
local genre = "voisin"
if r.own then genre = "à moi" end
print(r.ssid .. " : " .. r.routers .. " routeurs, le plus proche à " .. r.distance .. " blocs (" .. genre .. ")")
endLa liste est vide sur un Mini-ordinateur, sans routeur à portée, et avec des routeurs qui n'ont pas encore de SSID : un routeur reçoit son nom de réseau d'un programme, avec radio.set_ssid().
Voir aussi net.computers() radio.set_ssid()
Dit si l'ordinateur a une radio : un Modem radio ou un Routeur Wi-Fi qui le touche, ou n'importe où sur son réseau de Câbles de données. Toujours false sur un Mini-ordinateur, qui n'utilise que les câbles.
- boolean
truesi un Modem radio ou un Routeur Wi-Fi est branché
if not net.wireless() then
print("Pas de radio : seuls les ordinateurs du câble m'entendront.")
endMotifs courants
Une messagerie. Chaque ligne tapée part vers tous les ordinateurs joignables ; les messages reçus entre-temps s'affichent après chaque ligne (appuyez sur Entrée sur une ligne vide pour voir les nouveaux). Les messages sont des tables avec le nom de l'auteur, pour que les autres sachent qui parle :
local moi = os.label() or ("n°" .. net.id())
print("Messagerie : vous êtes " .. moi .. ". Tapez /quitter pour sortir.")
while true do
write("> ")
local ligne = read()
if ligne == "/quitter" then break end
if ligne ~= "" then
local auditeurs = net.broadcast({de = moi, texte = ligne}, "chat")
print("(" .. auditeurs .. " à l'écoute)")
end
local m = net.receive(0)
while m do
if m.channel == "chat" and type(m.data) == "table" then
print(tostring(m.data.de) .. " : " .. tostring(m.data.texte))
end
m = net.receive(0)
end
endLes messages qui arrivent pendant que read() attend votre ligne restent dans la file : aucun n'est perdu.
Une question avec délai et nouvelles tentatives. Un message peut se perdre : l'autre ordinateur était éteint, saturé, ou son chunk n'était pas chargé. Un client qui demande sa vitesse à l'usine essaie trois fois, et reconnaît la réponse grâce à un id qu'il met dans la question :
local USINE = 12
local prochain_id = 1
local function demander(question)
local id = prochain_id
prochain_id = prochain_id + 1
for essai = 1, 3 do
net.send(USINE, {type = "requete", id = id, question = question}, "usine")
local limite = os.clock() + 2
while os.clock() < limite do
local m = net.receive(limite - os.clock())
if m and m.sender == USINE and type(m.data) == "table"
and m.data.type == "reponse" and m.data.id == id then
return m.data.reponse
end
end
end
return nil
end
local tr_min = demander("vitesse")
if tr_min == nil then print("L'usine ne répond pas") else print("Usine à " .. tr_min .. " tr/min") endCôté usine, chaque requête reçoit une réponse avec le même id :
local moteur = peripheral.wrap("top") -- un Contrôleur de vitesse de rotation
while true do
local m = net.receive()
local d = m.data
if m.channel == "usine" and type(d) == "table" and d.type == "requete" then
local reponse = nil
if d.question == "vitesse" then reponse = moteur.speed() end
net.send(m.sender, {type = "reponse", id = d.id, reponse = reponse}, "usine")
end
endUne nouvelle tentative peut atteindre l'usine deux fois (quand seule la réponse s'est perdue) : gardez des requêtes qu'on peut répéter sans risque, « règle la vitesse à 64 » plutôt que « ajoute 16 tr/min ».
Des capteurs et un tableau de bord. Chaque capteur diffuse sa mesure toutes les quelques secondes sur le canal "capteurs" ; le tableau de bord garde la dernière mesure de chacun et se redessine chaque seconde, ce qui montre aussi l'âge de chaque valeur :
-- sur chaque capteur (un Microcontrôleur à côté d'un Coffre à objet renforcé)
local coffre = peripheral.wrap("back")
while true do
net.broadcast({quoi = "fer", valeur = coffre.count("minecraft:iron_ingot")}, "capteurs")
sleep(5)
end-- sur le tableau de bord
local mesures = {}
while true do
local m = net.receive(1) -- nil au bout d'une seconde : de quoi redessiner quand même
if m and m.channel == "capteurs" and type(m.data) == "table" then
mesures[m.sender] = {quoi = m.data.quoi, valeur = m.data.valeur, quand = os.clock()}
end
term.clear()
term.set_cursor(1, 1)
for id, mesure in pairs(mesures) do
local age = math.floor(os.clock() - mesure.quand)
print("n°" .. id .. " " .. tostring(mesure.quoi) .. " : " .. tostring(mesure.valeur) .. " (il y a " .. age .. " s)")
end
endUn capteur dont l'âge ne cesse de grandir s'est tu : cassé, éteint, ou son chunk déchargé. Une version complète à trois ordinateurs, avec des ordres et des accusés de réception, se trouve dans Réseaux. Les programmes intégrés talk, listen et pong sont d'autres exemples complets.
Voir aussi net.networks()