Core
The top-level functions on the rune table. For a task-oriented
introduction, see Scripting Basics.
Quick reference
Section titled “Quick reference”rune.send(text) -- process command syntax and aliases, then sendrune.send_raw(text) -- game lines, bypassing command processingrune.echo(text) -- print to the local display onlyrune.connect(address) -- "host:port", optional tls:// schemerune.disconnect() -- close the connectionrune.load(path) -- run a Lua script; true, or nil + errorrune.reload() -- reload core + user scriptsrune.quit() -- exit the client
rune.config_dir -- path to the config directory (data, not a function)rune.version -- client version stringrune.debug -- set true to enable rune.dbg outputrune.dbg(msg) -- print msg, but only while rune.debug is true
rune.config.get(key) -- read a typed configuration valuerune.config.set(key, value) -- validate and update a configuration valuerune.echo prints locally and never touches the server — pair it with
rune.style for colored messages. rune.reload
reloads the core plus your scripts; rune.session
state survives it.
rune.send
Section titled “rune.send”rune.send(text)text(string) — command text to process and send.
Before sending the text, Rune processes the configured command separator (;
by default) and #N repeats, then checks each resulting command against your
aliases.
Doubling the separator makes it literal: rune.send("say one;;two") sends
say one;two as one command. Escapes are decoded before aliases run. An alias
returning a string starts a new command-processing pass; use rune.send_raw
inside an alias callback to send its arguments without further interpretation.
#N command repeats one command or alias: #3 north;look sends north
three times, then look once. say #3 cheers is ordinary chat text.
For a sequence, repeat an alias or use a Lua loop. Alias expansions are
processed recursively, with a depth limit to catch loops.
Unlike text you type into the input line, rune.send does not run input hooks,
save the command in history, or perform history expansion. It still applies
the command separator, #N repeats, and aliases as described above.
for _ = 1, 2 do rune.send("get bread bag;eat bread")endrune.send_raw
Section titled “rune.send_raw”rune.send_raw(text) -> true | nil, errtext(string) — sent as game lines without aliases, command-separator splitting, or#Nrepeats. Text containing newlines is split and sent one physical line at a time. LF, CRLF, and bare CR are line breaks; empty lines are preserved.
Despite its name, send_raw sends MUD text, not Telnet or GMCP protocol
data. Returns true, or nil plus an error message (which is also echoed)
when the send fails — typically because you’re disconnected. This is
what alias and trigger string actions ultimately call.
Before processing any user submission, Rune finishes an active partial line: it commits the prompt overlay and closes open multiline spans before input hooks and any local echo, history, aliases, or slash commands. This is independent of local echo, connection state, whether a hook consumes the submission, and whether it eventually sends anything.
A game line sent programmatically by an alias, trigger, timer, or other Lua callback also finishes the partial line, once the connection accepts it for writing. A failed programmatic send leaves the line open. GMCP and other Telnet protocol traffic never finish it. If the partial line was really part of an ordinary line, Rune commits the visible prefix and treats later bytes as a new line.
rune.connect
Section titled “rune.connect”rune.connect(address)address(string) —host:portwith an optional scheme prefix:
| Form | Connection |
|---|---|
host:port |
Plain telnet (default) |
tls://host:port |
TLS, certificate verified |
tls+insecure://host:port |
TLS, no verification (self-signed certs) |
The full address, scheme included, is what
rune.state.address reports and what
the core stores for /reconnect. Connecting is asynchronous — the
"connecting" and "connected" hook events
report progress.
rune.connect("tls://mud.example.com:4000")rune.load
Section titled “rune.load”rune.load(path) -> true | nil, errpath(string) — path to a Lua script;~expands to your home directory.
Runs the script immediately and returns true, or nil plus an error
message. While the script runs, its directory temporarily joins
package.path, so it can require() files relative to its own
location:
~/.config/rune/├── init.lua -- main script├── combat.lua -- require("combat")└── utils/ └── helpers.lua -- require("utils.helpers")-- In init.lua:local combat = require("combat") -- loads combat.lualocal helpers = require("utils.helpers") -- loads utils/helpers.luaStandard Lua require() semantics apply: modules are cached after the
first load, and should return a table of exports.
Data fields
Section titled “Data fields”rune.config_dir and rune.version are plain strings set by the
client. rune.config_dir reflects --config-dir, RUNE_CONFIG_DIR, or
the platform default, in that order. Set rune.debug to true to make
rune.dbg print messages with a [dbg] prefix. When it is false,
rune.dbg does nothing:
rune.debug = truerune.dbg("trigger fired for " .. name)rune.config
Section titled “rune.config”Rune checks every configuration value against the type and rules below. Read
and change settings through get and set; direct property assignment is not
supported.
rune.config.get(key) -- valuerune.config.set(key, value)| Key | Type | Default | Meaning |
|---|---|---|---|
command_separator |
non-empty string | ";" |
Text that separates multiple commands entered on one line |
history_character |
empty or one visible, non-space character | "!" |
Character used for history expansion (!, !!, !prefix); empty disables it |
keep_input |
boolean | false |
After Enter, leave the text you typed selected; Enter repeats it and typing replaces it |
numpad |
boolean | false |
Enable terminal support for physical numpad bindings; see Numpad keys |
mouse |
boolean | false |
Capture the mouse so its wheel scrolls Rune; see Scrolling and the mouse |
An unknown key, a value of the wrong type, an empty command_separator, or an
invalid history character raises an error and leaves the configuration unchanged.
rune.config.set("keep_input", true)rune.config.set("command_separator", "|")rune.config.set("history_character", "^")rune.config.set("numpad", true)rune.config.set("mouse", true)assert(rune.config.get("keep_input") == true)Set history_character to "" to turn history expansion off. Rune recognizes
command separators before history expansion. For example, if !! is your
command separator and ! is your history character, the !! between two
commands remains a separator.
Double the configured separator to include it literally in a command (see
command separators). Verbatim input
and rune.send_raw bypass command processing and leave doubled separators unchanged.
A successful set takes effect immediately. On /reload, settings start from
their defaults and your init.lua applies your choices again. Put settings you
want to keep in init.lua; any key it no longer sets returns to its default.
Rune applies the finished settings together, so the interface does not briefly
switch to defaults while reload is running.
Related: Scripting Basics · State & Lines · rune.alias · Storage