Create: Computing AgesBrass Docs
Libraries

os

The computer itself: time, events, timers, name, memory, reboot.

All computers

The os library is about the computer that runs your program: how long it has been running, what time it is in the world, which number and which name it has, how much memory is left, and how to restart it.

Above all, it is the door to events. A program that calls os.pull_event waits without spending a single instruction, and wakes up when something happens: a lever is pulled, a key is pressed, the screen is clicked, a message arrives, a timer runs out. Almost every program that runs for a long time is built around this call.

Brass
os.label("Iron farm")
print("Computer #" .. os.id() .. " is called " .. os.label())
os.queue_event("parcel", "64 iron ingots")
local e = os.pull_event()
print("event: " .. e.name .. ", data: " .. e.data)
Screen
Computer #7 is called Iron farm
event: parcel, data: 64 iron ingots

Every computer has the os library, from the Tube Computer to the Modern Computer.

Functions
os.pull_event([name])Waits for the next event and returns it as a table.
os.queue_event(name [, data])Adds an event to the end of this computer's own queue.
os.start_timer(seconds)Starts a timer: after seconds, a timer event comes, with the id of the timer in e.id.
os.time()Ticks since the computer started: 20 per second, as a whole number.
os.clock()Seconds since the computer started: the same count as os.time, divided by 20 (so it moves in steps of 0.05).
os.day_time()The time of day in the Minecraft world, in ticks: from 0 to 23999, one full day lasting 20 minutes.
os.id()The number of this computer, unique in the world.
os.label([name])Reads or changes the name of the computer.
os.tier()The processor tier of the computer, the number shown at boot (Tier 4 - 131072 cells).
os.memory()The memory of the program, in cells: used is measured now, total is what the computer has.
os.reboot()Restarts the computer, like the Reboot button or Ctrl+R.
os.shutdown()Switches the computer off, like the Switch off button of the terminal.

Events and timers

An event is a small table that the computer puts in a queue when something happens. Its name field says what happened, and the other fields give the details. Your program takes them out of the queue, one at a time, with os.pull_event.

namewhenfields
timera timer started with os.start_timer runs outid
redstonea redstone signal arriving on a face of the computer changesnone: read the faces with rs.get
keya special key is pressed in the terminalkey: "enter", "backspace", "delete", "up", "down", "left", "right", "home", "end", "tab"
chara character is typed in the terminalchar: the character, like "a"
pasteCtrl+V in the terminaltext (4096 characters at most)
clicka click on the picture of the terminal, or a right-click on a monitorx, y, px, py, button, source
dragthe mouse moves with a button held, in the terminalthe same as click
diska storage medium is inserted or ejectedinserted: true or false
linka Redstone Link frequency the program uses changesa, b, power
messagea message from another computer (net)sender, channel, data, via, distance
your ownos.queue_eventdata

The Events page gives every field in detail, and Events explains how to design a program around them.

#

os.pull_event([name])

→ table⏸ Waits

Waits for the next event and returns it as a table.

Parameters
name string optional
only wait for events with this name; the others are thrown away
Returns
table
the event: name, and the fields of that kind of event

The program stops on this line. It uses no instructions while it waits, so a computer that waits for events costs nothing, even for hours. When an event comes, the call returns it: e.name tells you what happened.

Brass
-- Count the items a Mechanical Press finishes: an Observer watching it
-- sends a pulse to the left face of the computer.
local pressed = 0
while true do
  os.pull_event("redstone")
  if rs.get("left") > 0 then
    pressed = pressed + 1
    print("pressed: " .. pressed)
  end
end

A redstone event comes when the signal goes up and when it goes down, which is why the program checks rs.get("left") before counting.

With a name, the call waits for that kind of event only, and throws away every other event that was in the queue before it, like ComputerCraft does. That is perfect for a simple program that waits for one thing. In this example, the redstone event is lost:

Brass
os.queue_event("redstone")
os.queue_event("parcel", "iron")
os.queue_event("parcel", "gold")
local e = os.pull_event("parcel")
print(e.name, e.data)
e = os.pull_event()
print(e.name, e.data)
Screen
parcel  iron
parcel  gold

Without a name, the call returns the next event, whatever it is. As soon as a program waits for two kinds of things (a timer and a lever, a click and a message), call it without a name and look at e.name: with a name, the events of the other kind would be lost. net.receive waits the same way for message events, and throws the others away too; read takes the key events it needs and leaves the others in the queue.

Note

Events that arrive while the program computes or sleeps (sleep) are not lost: they wait in the queue, in their order of arrival. The queue holds 256 events; when it is full, the oldest one is dropped.

Clicks on a monitor

A right-click on a monitor normally opens the terminal. Once the program has waited for click events, or for any event (os.pull_event() without a name), a right-click on its monitor presses the screen instead, and a click event comes with source = "monitor". Sneak and right-click to open the terminal anyway. This lasts until the program ends.

A program that reacts to a lever, to buttons drawn on the screen and to a timer, all in one loop:

startup
-- A Mechanical Press line driven through a clutch on the back face:
-- a lever on the left or the button on the screen starts and stops it.
local running = false
local refresh = os.start_timer(5)

local function draw()
  term.clear()
  term.set_cursor(1, 1)
  print("Press line: " .. (running and "running" or "stopped"))
  print("Up for " .. math.floor(os.clock()) .. " s")
  term.set_cursor(1, 4)
  write("[ START / STOP ]")
end

local function set_running(on)
  running = on
  rs.set("back", not on)  -- a powered clutch stops the line
  draw()
end

set_running(false)
while true do
  local e = os.pull_event()
  if e.name == "timer" and e.id == refresh then
    draw()
    refresh = os.start_timer(5)
  elseif e.name == "redstone" then
    set_running(rs.get("left") > 0)
  elseif e.name == "click" and e.y == 4 and e.x <= 16 then
    set_running(not running)
  end
end

There is no terminate event: Ctrl+T (the Stop button) always stops the program, and cannot be caught.

See also os.start_timer() os.queue_event() Events Events

#

os.queue_event(name [, data])

Adds an event to the end of this computer's own queue.

Parameters
name string
the name of the event
data any optional
a value delivered in the data field of the event

The event comes back through os.pull_event like any other, after the events already waiting. The value given as data (a number, a text, a table...) comes in e.data.

Brass
os.queue_event("order", {item = "minecraft:iron_ingot", count = 32})
local e = os.pull_event("order")
print(e.data.count .. " x " .. e.data.item)
Screen
32 x minecraft:iron_ingot

A common use: make the main loop do its work once right at the start. A program that updates a lamp on each redstone event would otherwise show nothing until the first change of the lever:

Brass
os.queue_event("redstone")  -- a fake first event: read the lever now
while true do
  os.pull_event("redstone")
  rs.set("top", rs.get("left") > 0)
end

The name must be a text: os.queue_event(5) stops the program with bad argument #1 to 'queue_event' (string expected, got number). To send an event to another computer, use net.send.

See also os.pull_event()

#

os.start_timer(seconds)

→ number

Starts a timer: after seconds, a timer event comes, with the id of the timer in e.id.

Parameters
seconds number
the delay, in seconds
Returns
number
the id of the timer

Unlike sleep, the program does not stop: it goes on, and the event waits in the queue until the program pulls it. That is how a program can wait for a lever and do something every second at the same time.

Brass
local id = os.start_timer(2)
local e = os.pull_event("timer")
print(e.name, e.id == id)
Screen
timer   true
  • The delay is rounded up to whole ticks (a twentieth of a second), at least one tick: os.start_timer(0) fires on the next tick, os.start_timer(0.12) after 3 ticks.
  • The time counts while the computer runs. A computer without rotation is frozen, and so are its timers.
  • Each timer gets a new id. Compare e.id with the id you kept, so that an old timer does not confuse you.
  • At most 256 timers can wait at the same time; one more stops the program with too many timers.
  • There is no function to cancel a timer: forget its id, and ignore its event when it comes.
  • A reboot clears every timer.

A heartbeat: the lamp on top blinks once a second to show that the program is alive, while the computer counts the pulses arriving on the left:

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

A wait with a time limit: wait for a train to press the detector rail on the left, but give up after 30 seconds. Since a function returns one value in Brass, the result is true or false:

Brass
local function wait_for_train(seconds)
  local deadline = os.start_timer(seconds)
  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 == deadline then
      return false
    end
  end
end

if wait_for_train(30) then
  print("Train arrived")
else
  print("Train late!")
  rs.set("top", true)  -- the alarm lamp
end

See also os.pull_event() sleep()

Time

Three clocks. os.time and os.clock measure how long the computer has been running, to time a process or compute a rate. os.day_time reads the time of day of the Minecraft world, to act at night or at noon. The Time and timers guide compares them with sleep and timers.

#

os.time()

→ number

Ticks since the computer started: 20 per second, as a whole number.

Returns
number
the number of ticks since the computer started

The count starts at 0 when the computer boots (placing it, switching it on, a reboot) and only moves while the computer runs: a computer without rotation is frozen, and its clock stops too. It is a stopwatch, not a calendar.

Brass
local start = os.time()
sleep(1.5)
print("waited " .. os.time() - start .. " ticks")
Screen
waited 30 ticks

See also os.clock() os.day_time()

#

os.clock()

→ number

Seconds since the computer started: the same count as os.time, divided by 20 (so it moves in steps of 0.05).

Returns
number
the number of seconds since the computer started

It is the handy one for durations and rates. For example, the items per minute of a production line, from the pulses of an Observer on the left face:

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

See also os.time()

#

os.day_time()

→ number

The time of day in the Minecraft world, in ticks: from 0 to 23999, one full day lasting 20 minutes.

Returns
number
the time of day in the world, from 0 to 23999
valueclockin the world
06:00sunrise
10007:00morning (/time set day)
600012:00noon
1200018:00sunset
1300019:00night (/time set night)
180000:00midnight
230005:00dawn

The value is the same for every computer of the world. It jumps when players sleep in a bed or use /time, and stands still when the daylight cycle is turned off.

Lamps that light up at night, like the Day and night lighting project:

Brass
while true do
  local t = os.day_time()
  rs.set("top", t >= 13000 and t < 23000)  -- night: lamps on
  sleep(10)
end

To show the time like a clock (the clock program does it), shift the value by 6000, since tick 0 is 6 in the morning:

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

A daily routine: once a day, at noon, the computer rings a bell on the back face. It remembers the previous reading to notice the moment the time passes 6000:

Brass
local last = os.day_time()
while true do
  sleep(5)
  local now = os.day_time()
  if last < 6000 and now >= 6000 then
    rs.set("back", true)   -- noon: ring
    sleep(1)
    rs.set("back", false)
  end
  last = now
end

See also os.time()

Identity

Every computer has a number given by the world, and can have a name given by you.

#

os.id()

→ number

The number of this computer, unique in the world.

Returns
number
the number of this computer

The world gives numbers in order, from 0, the first time a computer runs. The number stays with the computer: break it with a pickaxe or pick it up with a Create Wrench, place it elsewhere, it keeps its number (and its name). On a networked computer, net.id returns the same number: it is the address other computers send messages to.

Brass
print("Computer #" .. os.id())
Screen
Computer #7

The shell command id shows it too, and so does the tooltip of the Engineer's Goggles (Computer #7).

See also os.label() net.id()

#

os.label([name])

→ string|nil

Reads or changes the name of the computer.

Parameters
name string optional
the new name, 32 characters at most; nil or "" removes it
Returns
string|nil
the name of the computer, or nil if it has none

Without an argument, it only reads the name. With an argument, it changes it, then returns the new one. A number is turned into text.

Brass
os.label("North gate")
print(os.label())
os.label(nil)
print(os.label())
Screen
North gate
nil

The name shows in the tooltip of the Engineer's Goggles (Label: North gate), in the shell command id, and in the label field of net.computers on the other computers of the network. Like the number, it stays on the computer when you break it and place it again. The shell command label North gate does the same thing.

A name longer than 32 characters stops the program with bad argument #1 to 'label' (at most 32 characters).

The name is also a way to give a role to a computer: copy the same program onto several computers, and let each one act according to its name.

startup
local role = os.label() or "unnamed"
if role == "North gate" then
  rs.set("left", true)    -- this gate opens with the left face
elseif role == "South gate" then
  rs.set("right", true)
else
  print("Name me first: label North gate")
end

On a network, the names make a list of computers readable. Name each computer once (label Smelter, label Press line...), then a central computer lists them:

Brass
for _, c in ipairs(net.computers()) do
  print("#" .. c.id .. " " .. (c.label or "(no name)") .. " via " .. c.via)
end

See also os.id() net.computers()

#

os.tier()

→ number

The processor tier of the computer, the number shown at boot (Tier 4 - 131072 cells).

Returns
number
the processor tier, from 1 to 5
computertier
Tube Computer1
Transistor Mainframe2
Minicomputer3
Personal Computer, Microcontroller4
Modern Computer5

A program carried on a medium from one computer to another can adapt itself: draw with gfx only from tier 2 (the Tube Computer prints on paper), use the network only from tier 3.

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

Memory

#

os.memory()

→ table

The memory of the program, in cells: used is measured now, total is what the computer has.

Returns
table
used and total, in memory cells
computertotal
Tube Computer2,048
Transistor Mainframe8,192
Minicomputer32,768
Personal Computer131,072
Microcontroller16,384
Modern Computer1,048,576

Everything the program keeps alive counts: each table takes 4 cells and 2 more per entry, a text 1 cell plus 1 per 8 characters, the code of the program, the events waiting in the queue. When a program needs more than total, it stops with out of memory. A table of 1000 readings takes about 2000 cells:

Brass
local before = os.memory().used
local readings = {}
for i = 1, 1000 do
  readings[i] = i * 0.5
end
print(os.memory().used - before .. " cells")
Screen
2005 cells

The call walks through all the live data of the program to count it: it costs about one instruction per 16 cells in use. Check the memory now and then (every few seconds, or before loading a big file), not on every tick. A gauge for a data logger that must not overflow a Tube Computer:

Brass
local m = os.memory()
local percent = math.floor(m.used * 100 / m.total)
print("memory: " .. percent .. " %")
if percent > 80 then
  print("too many readings kept, dropping the oldest")
end

See also Speed, memory and limits Limits

Power

Both functions end the program at once: the lines after the call never run. The computer acts at the end of the tick.

#

os.reboot()

Restarts the computer, like the Reboot button or Ctrl+R.

At the end of the tick, the computer releases its outputs (redstone faces back to 0, nothing sent on Redstone Link frequencies any more, the lines of display.set cleared), clears the screen, shows its startup banner and runs startup again. Variables, timers and waiting events are lost; files, name and number stay. os.time starts again from 0.

Only a boot, a reboot, a shutdown or the unloading of its chunk releases the outputs. When a program simply ends, crashes or is stopped with Ctrl+T, its redstone faces and Redstone Link frequencies stay as they were.

A typical use: an updater that writes a new version of the program, then restarts on it.

Brass
local new_version = 'print("Smelter v2 ready")'
fs.write("startup", new_version)
print("updated, restarting")
os.reboot()
Watch out

A computer boots at most twice a second: a reboot asked less than 10 ticks after the previous boot waits for that moment. A startup that always calls os.reboot() therefore loops forever, twice a second. Eject the medium to break the loop.

See also os.shutdown() The computers

#

os.shutdown()

Switches the computer off, like the Switch off button of the terminal.

At the end of the tick, the program stops, the screen goes blank and every output is released. The computer stays off, even after the world is reloaded, until a player opens its terminal and presses Switch on (or Reboot).

Brass
-- End of the night shift: lamps off, then the computer switches itself off.
rs.set("top", false)
print("Shift over, good night")
sleep(2)
os.shutdown()

See also os.reboot()

Common patterns

Run every N seconds while staying responsive. sleep freezes the program: a click during a sleep(60) waits a minute for an answer. A timer gives the same rhythm, and the program still reacts to everything else at once:

Brass
local report = os.start_timer(60)
while true do
  local e = os.pull_event()
  if e.name == "timer" and e.id == report then
    print("report, world time " .. os.day_time())
    report = os.start_timer(60)
  elseif e.name == "char" and e.char == "q" then
    print("bye")
    break
  end
end

Several timers at once. Keep each id in a variable named after its job, and compare:

Brass
local blink = os.start_timer(0.5)
local save = os.start_timer(30)
local lamp = false
while true do
  local e = os.pull_event("timer")
  if e.id == blink then
    lamp = not lamp
    rs.set("top", lamp)
    blink = os.start_timer(0.5)
  elseif e.id == save then
    fs.write("lamp_state", tostring(lamp))
    save = os.start_timer(30)
  end
end

Here waiting for "timer" only is fine, because the program listens to nothing else.