Create: Computing AgesDoc Brass
Bibliothèques

os

L'ordinateur lui-même : temps, évènements, minuteries, nom, mémoire, redémarrage.

Tous les ordinateurs

La bibliothèque os parle de l'ordinateur qui exécute votre programme : depuis combien de temps il tourne, quelle heure il est dans le monde, quel numéro et quel nom il porte, combien de mémoire il reste, et comment le redémarrer.

C'est surtout la porte d'entrée des évènements. Un programme qui appelle os.pull_event attend sans dépenser une seule instruction, et se réveille quand quelque chose arrive : un levier qu'on tire, une touche qu'on presse, un clic sur l'écran, un message, une minuterie qui sonne. Presque tous les programmes qui tournent longtemps sont construits autour de cet appel.

Brass
os.label("Ferme de fer")
print("L'ordinateur n°" .. os.id() .. " s'appelle " .. os.label())
os.queue_event("colis", "64 lingots de fer")
local e = os.pull_event()
print("évènement : " .. e.name .. ", données : " .. e.data)
Écran
L'ordinateur n°7 s'appelle Ferme de fer
évènement : colis, données : 64 lingots de fer

Tous les ordinateurs ont la bibliothèque os, du Calculateur à tubes à l'Ordinateur moderne.

Fonctions
os.pull_event([name])Attend le prochain évènement et le renvoie sous forme de table.
os.queue_event(name [, data])Ajoute un évènement à la fin de la file de cet ordinateur.
os.start_timer(seconds)Lance une minuterie : au bout de seconds, un évènement timer arrive, avec le numéro de la minuterie dans e.id.
os.time()Les ticks depuis le démarrage de l'ordinateur : 20 par seconde, en nombre entier.
os.clock()Les secondes depuis le démarrage de l'ordinateur : le même compte que os.time, divisé par 20 (il avance donc par pas de 0,05).
os.day_time()L'heure du monde de Minecraft, en ticks : de 0 à 23999, une journée complète durant 20 minutes.
os.id()Le numéro de cet ordinateur, unique dans le monde.
os.label([name])Lit ou change le nom de l'ordinateur.
os.tier()La génération du processeur de l'ordinateur, le nombre affiché au démarrage (Tier 4 - 131072 cells).
os.memory()La mémoire du programme, en cellules : used est mesuré à l'instant, total est ce dont dispose l'ordinateur.
os.reboot()Redémarre l'ordinateur, comme le bouton Redémarrer ou Ctrl+R.
os.shutdown()Éteint l'ordinateur, comme le bouton Éteindre du terminal.

Évènements et minuteries

Un évènement est une petite table que l'ordinateur range dans une file d'attente quand quelque chose se produit. Son champ name dit ce qui s'est passé, les autres champs donnent les détails. Votre programme les sort de la file, un par un, avec os.pull_event.

nomquandchamps
timerune minuterie lancée par os.start_timer arrive à son termeid
redstoneun signal de redstone qui arrive sur une face de l'ordinateur changeaucun : lisez les faces avec rs.get
keyune touche spéciale est pressée dans le terminalkey : "enter", "backspace", "delete", "up", "down", "left", "right", "home", "end", "tab"
charun caractère est tapé dans le terminalchar : le caractère, comme "a"
pasteCtrl+V dans le terminaltext (4096 caractères au plus)
clickun clic sur l'image du terminal, ou un clic droit sur un moniteurx, y, px, py, button, source
dragla souris bouge avec un bouton enfoncé, dans le terminalles mêmes que click
diskun support de stockage est inséré ou éjectéinserted : true ou false
linkune fréquence de Liaison de redstone utilisée par le programme changea, b, power
messageun message d'un autre ordinateur (net)sender, channel, data, via, distance
les vôtresos.queue_eventdata

La page Évènements détaille chaque champ, et le guide Évènements explique comment construire un programme autour des évènements.

#

os.pull_event([name])

→ table⏸ Attend

Attend le prochain évènement et le renvoie sous forme de table.

Paramètres
name string facultatif
n'attendre que les évènements de ce nom ; les autres sont jetés
Renvoie
table
l'évènement : name, et les champs propres à ce genre d'évènement

Le programme s'arrête sur cette ligne. Il ne consomme aucune instruction pendant l'attente : un ordinateur qui attend des évènements ne coûte rien, même pendant des heures. Quand un évènement arrive, l'appel le renvoie : e.name vous dit ce qui s'est passé.

Brass
-- Compte les objets qu'une Presse mécanique termine : un observateur
-- qui la surveille envoie une impulsion sur la face gauche de l'ordinateur.
local pressages = 0
while true do
  os.pull_event("redstone")
  if rs.get("left") > 0 then
    pressages = pressages + 1
    print("pressés : " .. pressages)
  end
end

Un évènement redstone arrive quand le signal monte et quand il redescend : c'est pourquoi le programme vérifie rs.get("left") avant de compter.

Avec un nom, l'appel n'attend que ce genre d'évènement, et jette tous les autres évènements qui étaient dans la file avant lui, comme dans ComputerCraft. C'est parfait pour un programme simple qui n'attend qu'une chose. Dans cet exemple, l'évènement de redstone est perdu :

Brass
os.queue_event("redstone")
os.queue_event("colis", "fer")
os.queue_event("colis", "or")
local e = os.pull_event("colis")
print(e.name, e.data)
e = os.pull_event()
print(e.name, e.data)
Écran
colis   fer
colis   or

Sans nom, l'appel renvoie le prochain évènement, quel qu'il soit. Dès qu'un programme attend deux sortes de choses (une minuterie et un levier, un clic et un message), appelez-le sans nom et regardez e.name : avec un nom, les évènements de l'autre sorte seraient perdus. net.receive attend de la même façon les évènements message, et jette lui aussi les autres ; read prend les évènements de touches dont il a besoin et laisse les autres dans la file.

Note

Les évènements qui arrivent pendant que le programme calcule ou dort (sleep) ne sont pas perdus : ils attendent dans la file, dans leur ordre d'arrivée. La file contient 256 évènements ; quand elle est pleine, le plus ancien est abandonné.

Les clics sur un moniteur

Un clic droit sur un moniteur ouvre normalement le terminal. Dès que le programme a attendu des évènements click, ou n'importe quel évènement (os.pull_event() sans nom), un clic droit sur son moniteur appuie sur l'écran à la place, et un évènement click arrive avec source = "monitor". Accroupissez-vous pour ouvrir quand même le terminal. Cela dure jusqu'à la fin du programme.

Un programme qui réagit à un levier, à un bouton dessiné à l'écran et à une minuterie, le tout dans une seule boucle :

startup
-- Une ligne de Presses mécaniques pilotée par un Embrayage sur la face arrière :
-- un levier à gauche ou le bouton à l'écran la démarre et l'arrête.
local en_marche = false
local rafraichir = os.start_timer(5)

local function dessiner()
  term.clear()
  term.set_cursor(1, 1)
  print("Ligne de presses : " .. (en_marche and "en marche" or "arrêtée"))
  print("Allumé depuis " .. math.floor(os.clock()) .. " s")
  term.set_cursor(1, 4)
  write("[ MARCHE / ARRÊT ]")
end

local function mettre_en_marche(oui)
  en_marche = oui
  rs.set("back", not oui)  -- un Embrayage alimenté arrête la ligne
  dessiner()
end

mettre_en_marche(false)
while true do
  local e = os.pull_event()
  if e.name == "timer" and e.id == rafraichir then
    dessiner()
    rafraichir = os.start_timer(5)
  elseif e.name == "redstone" then
    mettre_en_marche(rs.get("left") > 0)
  elseif e.name == "click" and e.y == 4 and e.x <= 18 then
    mettre_en_marche(not en_marche)
  end
end

Il n'existe pas d'évènement terminate : Ctrl+T (le bouton Stopper) arrête toujours le programme, sans qu'il puisse l'intercepter.

Voir aussi os.start_timer() os.queue_event() Évènements Évènements

#

os.queue_event(name [, data])

Ajoute un évènement à la fin de la file de cet ordinateur.

Paramètres
name string
le nom de l'évènement
data any facultatif
une valeur livrée dans le champ data de l'évènement

L'évènement revient par os.pull_event comme n'importe quel autre, après ceux qui attendent déjà. La valeur passée en data (un nombre, un texte, une table...) arrive dans e.data.

Brass
os.queue_event("commande", {objet = "minecraft:iron_ingot", nombre = 32})
local e = os.pull_event("commande")
print(e.data.nombre .. " x " .. e.data.objet)
Écran
32 x minecraft:iron_ingot

Un usage courant : faire travailler la boucle principale une fois dès le départ. Un programme qui met à jour une lampe à chaque évènement redstone n'afficherait rien avant le premier mouvement du levier :

Brass
os.queue_event("redstone")  -- un faux premier évènement : lire le levier tout de suite
while true do
  os.pull_event("redstone")
  rs.set("top", rs.get("left") > 0)
end

Le nom doit être un texte : os.queue_event(5) arrête le programme avec bad argument #1 to 'queue_event' (string expected, got number). Pour envoyer un évènement à un autre ordinateur, utilisez net.send.

Voir aussi os.pull_event()

#

os.start_timer(seconds)

→ number

Lance une minuterie : au bout de seconds, un évènement timer arrive, avec le numéro de la minuterie dans e.id.

Paramètres
seconds number
le délai, en secondes
Renvoie
number
le numéro de la minuterie

Contrairement à sleep, le programme ne s'arrête pas : il continue, et l'évènement attend dans la file jusqu'à ce que le programme le tire. C'est ainsi qu'un programme peut attendre un levier et faire quelque chose chaque seconde en même temps.

Brass
local id = os.start_timer(2)
local e = os.pull_event("timer")
print(e.name, e.id == id)
Écran
timer   true
  • Le délai est arrondi au tick supérieur (un vingtième de seconde), un tick au moins : os.start_timer(0) sonne au tick suivant, os.start_timer(0.12) après 3 ticks.
  • Le temps compte pendant que l'ordinateur tourne. Un ordinateur sans rotation est figé, ses minuteries aussi.
  • Chaque minuterie reçoit un nouveau numéro. Comparez e.id au numéro que vous avez gardé, pour qu'une vieille minuterie ne vous trompe pas.
  • 256 minuteries au plus peuvent attendre en même temps ; une de plus arrête le programme avec too many timers.
  • Aucune fonction n'annule une minuterie : oubliez son numéro, et ignorez son évènement quand il arrive.
  • Un redémarrage efface toutes les minuteries.

Un battement de cœur : la lampe du dessus clignote une fois par seconde pour montrer que le programme est vivant, pendant que l'ordinateur compte les impulsions qui arrivent à gauche :

Brass
local lampe = false
local impulsions = 0
local battement = os.start_timer(1)
while true do
  local e = os.pull_event()
  if e.name == "timer" and e.id == battement then
    lampe = not lampe
    rs.set("top", lampe)
    battement = os.start_timer(1)
  elseif e.name == "redstone" and rs.get("left") > 0 then
    impulsions = impulsions + 1
    print("impulsion " .. impulsions)
  end
end

Une attente avec limite de temps : attendre qu'un train appuie sur le rail détecteur à gauche, mais abandonner au bout de 30 secondes. Comme une fonction ne renvoie qu'une valeur en Brass, le résultat est true ou false :

Brass
local function attendre_train(secondes)
  local limite = os.start_timer(secondes)
  while true do
    local e = os.pull_event()
    if e.name == "redstone" and rs.get("left") > 0 then
      return true
    elseif e.name == "timer" and e.id == limite then
      return false
    end
  end
end

if attendre_train(30) then
  print("Train arrivé")
else
  print("Train en retard !")
  rs.set("top", true)  -- la lampe d'alarme
end

Voir aussi os.pull_event() sleep()

Temps

Trois horloges. os.time et os.clock mesurent depuis combien de temps l'ordinateur tourne, pour chronométrer une opération ou calculer un débit. os.day_time lit l'heure du monde de Minecraft, pour agir la nuit ou à midi. Le guide Temps et minuteries les compare avec sleep et les minuteries.

#

os.time()

→ number

Les ticks depuis le démarrage de l'ordinateur : 20 par seconde, en nombre entier.

Renvoie
number
le nombre de ticks depuis le démarrage de l'ordinateur

Le compte part de 0 quand l'ordinateur démarre (posé, rallumé, redémarré) et n'avance que pendant qu'il tourne : un ordinateur sans rotation est figé, et son horloge s'arrête avec lui. C'est un chronomètre, pas un calendrier.

Brass
local debut = os.time()
sleep(1.5)
print("attendu " .. os.time() - debut .. " ticks")
Écran
attendu 30 ticks

Voir aussi os.clock() os.day_time()

#

os.clock()

→ number

Les secondes depuis le démarrage de l'ordinateur : le même compte que os.time, divisé par 20 (il avance donc par pas de 0,05).

Renvoie
number
le nombre de secondes depuis le démarrage de l'ordinateur

C'est la plus pratique pour les durées et les débits. Par exemple, les objets par minute d'une ligne de production, d'après les impulsions d'un observateur sur la face gauche :

Brass
local debut = os.clock()
local objets = 0
while true do
  os.pull_event("redstone")
  if rs.get("left") > 0 then
    objets = objets + 1
    local minutes = (os.clock() - debut) / 60
    print(math.round(objets / minutes) .. " objets par minute")
  end
end

Voir aussi os.time()

#

os.day_time()

→ number

L'heure du monde de Minecraft, en ticks : de 0 à 23999, une journée complète durant 20 minutes.

Renvoie
number
l'heure du monde, de 0 à 23999
valeurheuredans le monde
06 hlever du soleil
10007 hmatin (/time set day)
600012 hmidi
1200018 hcoucher du soleil
1300019 hnuit (/time set night)
180000 hminuit
230005 haube

La valeur est la même pour tous les ordinateurs du monde. Elle saute quand les joueurs dorment dans un lit ou utilisent /time, et reste figée quand le cycle jour/nuit est désactivé.

Des lampes qui s'allument la nuit, comme dans le projet Éclairage jour et nuit :

Brass
while true do
  local t = os.day_time()
  rs.set("top", t >= 13000 and t < 23000)  -- la nuit : lampes allumées
  sleep(10)
end

Pour afficher l'heure comme une horloge (le programme clock le fait), décalez la valeur de 6000, puisque le tick 0 correspond à 6 h du matin :

Brass
local function texte_heure(t)
  local depuis_minuit = (t + 6000) % 24000
  local h = math.floor(depuis_minuit / 1000)
  local m = math.floor(depuis_minuit % 1000 * 60 / 1000)
  return string.format("%02d:%02d", h, m)
end
print(texte_heure(6000))
print(texte_heure(13000))
print(texte_heure(23500))
Écran
12:00
19:00
05:30

Une routine quotidienne : une fois par jour, à midi, l'ordinateur sonne une cloche sur sa face arrière. Il retient la mesure précédente pour repérer le moment où l'heure dépasse 6000 :

Brass
local avant = os.day_time()
while true do
  sleep(5)
  local maintenant = os.day_time()
  if avant < 6000 and maintenant >= 6000 then
    rs.set("back", true)   -- midi : on sonne
    sleep(1)
    rs.set("back", false)
  end
  avant = maintenant
end

Voir aussi os.time()

Identité

Chaque ordinateur a un numéro donné par le monde, et peut avoir un nom donné par vous.

#

os.id()

→ number

Le numéro de cet ordinateur, unique dans le monde.

Renvoie
number
le numéro de cet ordinateur

Le monde distribue les numéros dans l'ordre, à partir de 0, la première fois qu'un ordinateur tourne. Le numéro reste attaché à l'ordinateur : cassez-le à la pioche ou ramassez-le avec la Clé de Create, posez-le ailleurs, il garde son numéro (et son nom). Sur un ordinateur en réseau, net.id renvoie le même numéro : c'est l'adresse à laquelle les autres ordinateurs envoient leurs messages.

Brass
print("Ordinateur n°" .. os.id())
Écran
Ordinateur n°7

La commande id du shell l'affiche aussi, tout comme l'infobulle des Lunettes d'ingénieur (Ordinateur n°7).

Voir aussi os.label() net.id()

#

os.label([name])

→ string|nil

Lit ou change le nom de l'ordinateur.

Paramètres
name string facultatif
le nouveau nom, 32 caractères au plus ; nil ou "" l'efface
Renvoie
string|nil
le nom de l'ordinateur, ou nil s'il n'en a pas

Sans argument, elle lit seulement le nom. Avec un argument, elle le change, puis renvoie le nouveau. Un nombre est converti en texte.

Brass
os.label("Portail nord")
print(os.label())
os.label(nil)
print(os.label())
Écran
Portail nord
nil

Le nom apparaît dans l'infobulle des Lunettes d'ingénieur (Étiquette : Portail nord), dans la commande id du shell, et dans le champ label de net.computers sur les autres ordinateurs du réseau. Comme le numéro, il reste sur l'ordinateur quand vous le cassez et le reposez. La commande du shell label Portail nord fait la même chose.

Un nom de plus de 32 caractères arrête le programme avec bad argument #1 to 'label' (at most 32 characters).

Le nom permet aussi de donner un rôle à un ordinateur : copiez le même programme sur plusieurs ordinateurs, et laissez chacun agir selon son nom.

startup
local role = os.label() or "sans nom"
if role == "Portail nord" then
  rs.set("left", true)    -- ce portail s'ouvre par la face gauche
elseif role == "Portail sud" then
  rs.set("right", true)
else
  print("Donnez-moi d'abord un nom : label Portail nord")
end

Sur un réseau, les noms rendent la liste des ordinateurs lisible. Nommez chaque ordinateur une fois (label Fonderie, label Presses...), puis un ordinateur central les liste :

Brass
for _, c in ipairs(net.computers()) do
  print("n°" .. c.id .. " " .. (c.label or "(sans nom)") .. " par " .. c.via)
end

Voir aussi os.id() net.computers()

#

os.tier()

→ number

La génération du processeur de l'ordinateur, le nombre affiché au démarrage (Tier 4 - 131072 cells).

Renvoie
number
la génération du processeur, de 1 à 5
ordinateurgénération
Calculateur à tubes1
Ordinateur à transistors2
Mini-ordinateur3
Micro-ordinateur, Microcontrôleur4
Ordinateur moderne5

Un programme emporté sur un support d'un ordinateur à l'autre peut s'adapter : dessiner avec gfx seulement à partir de la génération 2 (le Calculateur à tubes imprime sur papier), utiliser le réseau seulement à partir de la 3.

Brass
if os.tier() >= 2 then
  gfx.clear("black")
  gfx.text(4, 4, "STOCK", "yellow", 2)
else
  print("STOCK")
end

Mémoire

#

os.memory()

→ table

La mémoire du programme, en cellules : used est mesuré à l'instant, total est ce dont dispose l'ordinateur.

Renvoie
table
used et total, en cellules de mémoire
ordinateurtotal
Calculateur à tubes2 048
Ordinateur à transistors8 192
Mini-ordinateur32 768
Micro-ordinateur131 072
Microcontrôleur16 384
Ordinateur moderne1 048 576

Tout ce que le programme garde en vie compte : chaque table prend 4 cellules plus 2 par entrée, un texte 1 cellule plus 1 par tranche de 8 caractères, le code du programme, les évènements qui attendent dans la file. Quand un programme a besoin de plus que total, il s'arrête avec out of memory. Une table de 1000 mesures prend environ 2000 cellules :

Brass
local avant = os.memory().used
local mesures = {}
for i = 1, 1000 do
  mesures[i] = i * 0.5
end
print(os.memory().used - avant .. " cellules")
Écran
2005 cellules

L'appel parcourt toutes les données vivantes du programme pour les compter : il coûte environ une instruction par tranche de 16 cellules utilisées. Vérifiez la mémoire de temps en temps (toutes les quelques secondes, ou avant de charger un gros fichier), pas à chaque tick. Une jauge pour un enregistreur de données qui ne doit pas déborder d'un Calculateur à tubes :

Brass
local m = os.memory()
local pourcent = math.floor(m.used * 100 / m.total)
print("mémoire : " .. pourcent .. " %")
if pourcent > 80 then
  print("trop de mesures gardées, on jette les plus vieilles")
end

Voir aussi Vitesse, mémoire et limites Limites

Alimentation

Les deux fonctions terminent le programme sur-le-champ : les lignes qui suivent l'appel ne s'exécutent jamais. L'ordinateur agit à la fin du tick.

#

os.reboot()

Redémarre l'ordinateur, comme le bouton Redémarrer ou Ctrl+R.

À la fin du tick, l'ordinateur relâche ses sorties (faces de redstone remises à 0, plus rien d'émis sur les fréquences des Liaisons de redstone, lignes de display.set effacées), efface l'écran, affiche sa bannière de démarrage et relance startup. Les variables, les minuteries et les évènements en attente sont perdus ; les fichiers, le nom et le numéro restent. os.time repart de 0.

Seuls un démarrage, un redémarrage, un arrêt ou le déchargement de son chunk relâchent les sorties. Quand un programme se termine simplement, plante ou est arrêté avec Ctrl+T, ses faces de redstone et ses fréquences de Liaisons de redstone restent comme elles étaient.

Un usage typique : un programme de mise à jour qui écrit une nouvelle version du programme, puis redémarre dessus.

Brass
local nouvelle_version = 'print("Fonderie v2 prête")'
fs.write("startup", nouvelle_version)
print("mis à jour, redémarrage")
os.reboot()
Attention

Un ordinateur démarre au plus deux fois par seconde : un redémarrage demandé moins de 10 ticks après le précédent attend ce moment. Un startup qui appelle toujours os.reboot() tourne donc en boucle sans fin, deux fois par seconde. Éjectez le support pour casser la boucle.

Voir aussi os.shutdown() Les ordinateurs

#

os.shutdown()

Éteint l'ordinateur, comme le bouton Éteindre du terminal.

À la fin du tick, le programme s'arrête, l'écran devient noir et toutes les sorties sont relâchées. L'ordinateur reste éteint, même après un rechargement du monde, jusqu'à ce qu'un joueur ouvre son terminal et appuie sur Allumer (ou Redémarrer).

Brass
-- Fin de l'équipe de nuit : lampes éteintes, puis l'ordinateur s'éteint tout seul.
rs.set("top", false)
print("Fin de service, bonne nuit")
sleep(2)
os.shutdown()

Voir aussi os.reboot()

Motifs courants

Agir toutes les N secondes sans cesser de répondre. sleep fige le programme : un clic pendant un sleep(60) attend une minute sa réponse. Une minuterie donne le même rythme, et le programme réagit toujours tout de suite au reste :

Brass
local rapport = os.start_timer(60)
while true do
  local e = os.pull_event()
  if e.name == "timer" and e.id == rapport then
    print("rapport, heure du monde " .. os.day_time())
    rapport = os.start_timer(60)
  elseif e.name == "char" and e.char == "q" then
    print("au revoir")
    break
  end
end

Plusieurs minuteries à la fois. Gardez chaque numéro dans une variable nommée d'après son travail, et comparez :

Brass
local clignoter = os.start_timer(0.5)
local sauver = os.start_timer(30)
local lampe = false
while true do
  local e = os.pull_event("timer")
  if e.id == clignoter then
    lampe = not lampe
    rs.set("top", lampe)
    clignoter = os.start_timer(0.5)
  elseif e.id == sauver then
    fs.write("etat_lampe", tostring(lampe))
    sauver = os.start_timer(30)
  end
end

Ici, n'attendre que "timer" ne pose pas de problème, puisque le programme n'écoute rien d'autre.