Writing programs
Every mcos program is a Lua script. You write one with edit, run it by its
path, and stop it with Ctrl+C. This page is about the one thing that differs most from how Lua
programs are written for other Minecraft computers: you do not yield, and you do not need an event
loop. Your program is a plain program. When it has nothing to do, it waits.
Waiting is how a program idles
A call that has to wait blocks your program until it can finish. The computer runs other things meanwhile and wakes yours when it is time. There are three ways to wait, and they cover everything:
| To wait for | Use | Example |
|---|---|---|
| Some time | sys.sleep(seconds) |
sys.sleep(0.5) |
| One device or file | a read that blocks | sensor:read("l") gives the next line, when there is one |
| Several things at once, or a change | sys.poll(fds, seconds) |
wait for a card to change, a keypress and a timeout together |
A device’s file is the way into it, and an event is a line you read from it, so waiting for an event
is just reading. See /dev for the files, the sys library for
sys.sleep and sys.poll, and each device’s page for what its file says.
A loop that never ends is fine
This is a whole program. It blinks a lamp forever:
-- Blink whatever is on the redstone card's north face: on half a second, off half a second, forever.
--
-- There is no event loop and nothing to yield: this is a plain loop. sys.sleep waits (the
-- computer does other work meanwhile) and the loop goes on until you stop it with Ctrl+C.
local sys = require("sys")
local FACE = "north" -- any face: north, south, east, west, up, down (or front, back, left, right)
local HALF = 0.5 -- seconds on, and seconds off
-- A line written to the card's file is "face strength" and takes effect at once.
local card = io.open("/dev/redstone0", "w")
local function set(strength)
card:write(FACE, " ", strength, "\n")
card:flush()
end
while true do
set(15)
sys.sleep(HALF)
set(0)
sys.sleep(HALF)
end
Put it in a file, and with a redstone card in your computer and a lamp against that face of the case, run it:
edit /home/blink.lua
/home/blink.lua
What to notice:
- The loop is
while true do. Nothing is passed back to a scheduler and nothing callsyield. sys.sleepis the only place the program waits. Between two sleeps it runs at once.- It never ends on its own. Press Ctrl+C in the terminal to stop it.
Even a loop that never waits is not an error. The computer gives your program a fixed slice of work at a time, freezes it where it is when the slice is spent, and carries on with the server; your program is thawed in its next slice, exactly where it stopped. It does mean that a loop that does not wait uses all the time it is given, so wait when there is nothing to do.
Waiting for a change
A redstone card’s file also tells you when something changed. sys.poll waits for that, so this
program uses no time at all while the signal is steady:
-- Copy the signal coming in on the west face out of the east face, and keep doing it.
--
-- The card's file says "face level powered" for each face: the level of the signal there, and how
-- much of it is this card's own. sys.poll waits (it blocks, it does not yield) until the card
-- reports a change, so the program uses no time while nothing happens.
local sys = require("sys")
local FROM = "west"
local TO = "east"
local reader = io.open("/dev/redstone0")
local writer = io.open("/dev/redstone0", "w")
local watch = {[sys.fileno(reader)] = {PRI = true}} -- PRI: the device's state changed
-- Read the whole file from its start, and pick out one face's level.
local function level(face)
reader:seek("set", 0)
return tonumber(reader:read("a"):match(face .. " (%d+)")) or 0
end
local last = -1
while true do
local now = level(FROM)
if now ~= last then
writer:write(TO, " ", now, "\n")
writer:flush()
last = now
end
sys.poll(watch) -- block until the card changes
end
Power the west face of the case and the east face follows it. The PRI event means “the device’s
state changed”, which is how a redstone file says an input moved.
Waiting for an event
A sensor’s file has no state to read, only events: one line each. Reading it blocks until there is a line. This program sounds an alarm for each thing the motion sensor sees:
-- Sound an alarm (a redstone signal on the north face) whenever the motion sensor sees something move.
--
-- Reading a sensor's file blocks until it has an event, so the loop below is a plain while loop:
-- the program sleeps in read() until something moves, with no event.pull and no yield.
local sys = require("sys")
local ALARM = "north"
local PULSE = 1 -- seconds the alarm stays on
local motion = io.open("/dev/motion0")
local alarm = io.open("/dev/redstone0", "w")
local function set(strength)
alarm:write(ALARM, " ", strength, "\n")
alarm:flush()
end
while true do
local line = motion:read("l") -- blocks until the sensor has something to say
local mob, x, y, z = line:match("^(%S+) (%S+) (%S+) (%S+)$")
if mob then -- (a line "lost n" says n events were dropped: nothing to sound for)
print(mob .. " at " .. x .. " " .. y .. " " .. z)
set(15)
sys.sleep(PULSE)
set(0)
end
end
motion:read("l") is the program’s whole event loop. Events that arrive while the alarm sleeps wait
in the sensor for the next read; a sensor that fills up reports how many it dropped, which is the
lost line the program skips.
Doing two things at once
Two programs can run at once. End a command with & to start it in the background, list the
running ones with ps, and stop one with kill:
/home/blink.lua &
ps
Inside one program, sys.poll waits on several descriptors at once, and says which of them is
ready, so one loop can serve a sensor and the keyboard together.
If you know OpenComputers
| In OpenComputers | In mcos |
|---|---|
event.pull() in a loop |
A blocking read on the device’s file, or sys.poll |
os.sleep(t) |
sys.sleep(t) |
| A program that does not yield for a while is killed | No such limit: a busy program is frozen and thawed |
component.redstone.setOutput(sides.north, 15) |
redstone:write("north 15\n") on /dev/redstone0 |
sides.north |
the string "north": faces are strings |
| Components found by address | Devices found by their names |
The tutorials use these programs step by step.