Operator Card
An operator card runs server commands, as a command block does. It exists in creative mode only.
Facts
| | |
|—|—|
| Type | operator |
| Address | An expansion slot path such as exp:0, or a hub’s |
| Availability | Creative only |
| Interrupt | None |
| Info | kind (operator) |
The card runs a command at its own block (in a case, or in an
expansion hub, at the hub), with a command block’s permission: time,
weather, give, title and the like (a game master’s), but not op or stop. Its commands come
from “Operator Card”, and each command is logged on the server, so its admins see what ran. It
returns how the command went and what it said. What a command says goes back to the program, not to
any player.
In mcos
/dev/operator0 (operator1 for the next card) runs commands.
- Write: a line written runs one, at once (a leading
/is allowed). A write whose command failed fails (EINVAL), and the file then says why. An empty line fails the same way. - Read: what the last command said, its lines. The text is taken at the first read; seek back to the start to read the newest.
- Poll: always ready for
INandOUT. The card has no interrupt, so neverPRI. - ioctls: its methods below, by name, in any open mode:
sys.ioctl(fd, "command", "time set day"). A failure isnil, its message andEINVAL.setLabelsets its label, as on every device file.
echo time set day > /dev/operator0
cat /dev/operator0
The second command prints what the server said, such as Set the time to 1000.
Methods
command(text) → {success, result, output}
Runs a server command, with or without its leading /, and answers how it went. success says
whether it worked, result is the command’s value (0 when it failed), and output is what it said
(its lines, each ending in a new line). A command that fails still answers (with success false);
only an empty command fails the call, with no command. A card not in a world answers
success = false, output = "not in a world".
get() → string
What the last command said (its output), or "" before the first.
set({command = text}) → {success, result, output}
Runs a command, as command does: what a line written to the file does. A command that fails makes
this call fail, with what it said (it failed when it said nothing). A table without a command
string fails with no command.
Events
None.
Examples
Endless day: at dusk the program writes two commands to the card’s file:
-- Endless day: at dusk, set the world's time back to morning and clear the weather.
-- lua operator-daylight.lua &
--
-- The operator card's file runs a line written to it as a server command, as a command block
-- would, at once. A command that fails fails the write, and reading the file then says what the
-- server answered. The world's clock is the computer's (os.date's hours are the game's), so the
-- program looks once a minute of game time and sleeps in between.
local sys = require("sys")
local DUSK = 18 -- the hour (of the game's 24) after which it is time to act
local CHECK = 50 -- seconds between looks (a game hour is 50 seconds)
local COMMANDS = {"time set day", "weather clear"}
local card = assert(io.open("/dev/operator0", "w"))
while true do
if os.date("*t").hour >= DUSK then
for _, command in ipairs(COMMANDS) do
card:write(command, "\n")
local ok = card:flush()
if not ok then
local answer = io.open("/dev/operator0")
io.stderr:write("daylight: ", command, ": ", answer:read("a"))
answer:close()
end
end
end
sys.sleep(CHECK)
end
A file of commands run through the command ioctl, each logged, stopping at the first that fails:
-- Run a file of server commands, a line each, and log how each went:
-- lua operator-script.lua setup.txt [log]
--
-- Blank lines and lines starting with # are skipped. Each command goes to the operator card's
-- command method (an ioctl on its file, /dev/operator0), which answers a table: success, result
-- (the command's value) and output (what the server said). The log gets a line per command; the
-- first command that fails stops the run, and the exit status is 1, so a shell script can tell.
local sys = require("sys")
local LOG = "/home/operator.log" -- where the log goes, unless named
local script, logpath = arg[1], arg[2] or LOG
if not script then
io.stderr:write("usage: operator-script.lua FILE [LOG]\n")
os.exit(2)
end
local input = assert(io.open(script))
local log = assert(io.open(logpath, "a"))
local card = assert(sys.open("/dev/operator0"))
local status = 0
local number = 0
for line in input:lines() do
number = number + 1
local command = line:match("^%s*(.-)%s*$")
if command ~= "" and command:sub(1, 1) ~= "#" then
local answer, why = sys.ioctl(card, "command", command)
if not answer then
log:write(("%d: %s: error: %s\n"):format(number, command, why))
status = 1
else
local said = answer.output:gsub("\n", " "):gsub("%s+$", "")
log:write(("%d: %s: %s %d %s\n"):format(number, command, answer.success and "ok" or "FAILED", answer.result, said))
if not answer.success then
status = 1
end
end
if status ~= 0 then
io.stderr:write("operator-script: stopped at line ", number, "\n")
break
end
end
end
sys.close(card)
input:close()
log:close()
os.exit(status)
A greeter at the door: a redstone card hears a pressure plate, set runs a title command, and get
says who it was shown to:
-- Greet whoever steps on the pressure plate at the door, and keep a visitors' log.
-- lua operator-greeter.lua &
--
-- The plate's signal comes in on a redstone card (/dev/redstone0, a line a face: "face level
-- powered"); sys.poll waits for its change (PRI). On each step the program runs a server command
-- through the operator card's set method (as a line written to its file would: a command that
-- fails fails the call, with what the server said), then its get method gives what the command
-- said (who was shown the greeting), which goes in the log with the world's date and time.
local sys = require("sys")
local PLATE = "south" -- the face the plate's signal comes in on
-- The command run for each visitor: a title on the screens of the players within 5 blocks of the
-- card (a command runs at its block). The server answers "Showing new title for <player>".
local GREETING = 'title @a[distance=..5] title "Welcome home!"'
local LOG = "/home/visitors.log"
local redstone = assert(io.open("/dev/redstone0"))
local card = assert(sys.open("/dev/operator0"))
local watch = {[sys.fileno(redstone)] = {PRI = true}}
-- Whether the plate is pressed now: the file read again from its start.
local function pressed()
redstone:seek("set", 0)
return (tonumber(redstone:read("a"):match(PLATE .. " (%d+)")) or 0) > 0
end
local was = pressed()
while true do
sys.poll(watch) -- block until the redstone card changes
local now = pressed()
if now and not was then
local done, why = sys.ioctl(card, "set", {command = GREETING})
local log = assert(io.open(LOG, "a"))
if done then
log:write(os.date("%c"), " visitor: ", (sys.ioctl(card, "get"):gsub("\n$", "")), "\n")
else
log:write(os.date("%c"), " greeting failed: ", tostring(why), "\n")
end
log:close()
end
was = now
end