
os
The computer itself: time, events, timers, name, memory, reboot.
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.
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)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.
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.
| name | when | fields |
|---|---|---|
timer | a timer started with os.start_timer runs out | id |
redstone | a redstone signal arriving on a face of the computer changes | none: read the faces with rs.get |
key | a special key is pressed in the terminal | key: "enter", "backspace", "delete", "up", "down", "left", "right", "home", "end", "tab" |
char | a character is typed in the terminal | char: the character, like "a" |
paste | Ctrl+V in the terminal | text (4096 characters at most) |
click | a click on the picture of the terminal, or a right-click on a monitor | x, y, px, py, button, source |
drag | the mouse moves with a button held, in the terminal | the same as click |
disk | a storage medium is inserted or ejected | inserted: true or false |
link | a Redstone Link frequency the program uses changes | a, b, power |
message | a message from another computer (net) | sender, channel, data, via, distance |
| your own | os.queue_event | data |
The Events page gives every field in detail, and Events explains how to design a program around them.
Waits for the next event and returns it as a table.
namestring optional- only wait for events with this name; the others are thrown away
- 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.
-- 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
endA 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:
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)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.
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.
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:
-- 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
endThere 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.
namestring- the name of the event
dataany optional- a value delivered in the
datafield 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.
os.queue_event("order", {item = "minecraft:iron_ingot", count = 32})
local e = os.pull_event("order")
print(e.data.count .. " x " .. e.data.item)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:
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)
endThe 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()
Starts a timer: after seconds, a timer event comes, with the id of the timer in e.id.
secondsnumber- the delay, in seconds
- 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.
local id = os.start_timer(2)
local e = os.pull_event("timer")
print(e.name, e.id == id)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.idwith 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:
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
endA 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:
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
endSee 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.
Ticks since the computer started: 20 per second, as a whole number.
- 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.
local start = os.time()
sleep(1.5)
print("waited " .. os.time() - start .. " ticks")waited 30 ticks
See also os.clock() os.day_time()
Seconds since the computer started: the same count as os.time, divided by 20 (so it moves in steps of 0.05).
- 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:
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
endSee also os.time()
The time of day in the Minecraft world, in ticks: from 0 to 23999, one full day lasting 20 minutes.
- number
- the time of day in the world, from 0 to 23999
| value | clock | in the world |
|---|---|---|
| 0 | 6:00 | sunrise |
| 1000 | 7:00 | morning (/time set day) |
| 6000 | 12:00 | noon |
| 12000 | 18:00 | sunset |
| 13000 | 19:00 | night (/time set night) |
| 18000 | 0:00 | midnight |
| 23000 | 5:00 | dawn |
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:
while true do
local t = os.day_time()
rs.set("top", t >= 13000 and t < 23000) -- night: lamps on
sleep(10)
endTo show the time like a clock (the clock program does it), shift the value by 6000, since tick 0 is 6 in the morning:
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))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:
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
endSee also os.time()
Identity
Every computer has a number given by the world, and can have a name given by you.
The number of this computer, unique in the world.
- 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.
print("Computer #" .. os.id())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()
Reads or changes the name of the computer.
namestring optional- the new name, 32 characters at most;
nilor""removes it
- string|nil
- the name of the computer, or
nilif 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.
os.label("North gate")
print(os.label())
os.label(nil)
print(os.label())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.
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")
endOn 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:
for _, c in ipairs(net.computers()) do
print("#" .. c.id .. " " .. (c.label or "(no name)") .. " via " .. c.via)
endSee also os.id() net.computers()
The processor tier of the computer, the number shown at boot (Tier 4 - 131072 cells).
- number
- the processor tier, from 1 to 5
| computer | tier |
|---|---|
| Tube Computer | 1 |
| Transistor Mainframe | 2 |
| Minicomputer | 3 |
| Personal Computer, Microcontroller | 4 |
| Modern Computer | 5 |
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.
if os.tier() >= 2 then
gfx.clear("black")
gfx.text(4, 4, "STOCK", "yellow", 2)
else
print("STOCK")
endMemory
The memory of the program, in cells: used is measured now, total is what the computer has.
- table
usedandtotal, in memory cells
| computer | total |
|---|---|
| Tube Computer | 2,048 |
| Transistor Mainframe | 8,192 |
| Minicomputer | 32,768 |
| Personal Computer | 131,072 |
| Microcontroller | 16,384 |
| Modern Computer | 1,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:
local before = os.memory().used
local readings = {}
for i = 1, 1000 do
readings[i] = i * 0.5
end
print(os.memory().used - before .. " cells")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:
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")
endSee 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.
local new_version = 'print("Smelter v2 ready")'
fs.write("startup", new_version)
print("updated, restarting")
os.reboot()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).
-- 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:
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
endSeveral timers at once. Keep each id in a variable named after its job, and compare:
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
endHere waiting for "timer" only is fine, because the program listens to nothing else.