#Anatomy of a door
The manifest:
apimust be 1.slugis the unique lowercase id naming the door's storage and its DOOR menu argument.entryis the PHP file to include;classis the fully qualified class it defines.cronandadminare optional.- Install by uploading the folder into the
doors/directory of the board, alongsidehiloandstarmerchant, then pressing Scan on the console Doors page. - Registering does not put the door in front of anyone. Add a DOOR-action item in the menu designer; its argument list offers every registered door.
#The lifecycle
The class implements three methods:
enterruns when the caller opens the door; return the opening frames.inputreceives keys, lines, or editor payloads exactly like board screens. Check$in->kindforkey,lineoreditor, then read the matching field.- To exit, include
$c->exitDoor()in your returned frames; the board pops the caller to the menu and callsleave.
#DoorContext: every service
| Call | Gives you |
|---|---|
| $c->out() | ANSI builder: cls(), at(r,c),
txt(), fg(), bg(), reset(),
str(). |
| $c->pipe(s) | Render pipe codes to ANSI. |
| $c->center(s) | Center on the 80-column screen. |
| $c->ansi(s) | Wrap raw ANSI as a frame. |
| $c->bell() | Terminal bell frame. |
| $c->hotkey() | Request single-key input mode. Also
$c->line(...) and $c->editor(...). |
| $c->exitDoor() | The leave-the-door frame. |
| $c->user() | Caller row: id, handle, level. |
| $c->state | Per-visit scratch array, persisted between requests. |
| $c->kv() | The door's private key/value store (get, set, del, all-by-prefix). |
| $c->table(...) | A private database table. See below. |
| $c->files() | A jailed folder under data/doorstore/<slug>/.
$c->path(name) resolves one file inside it. |
| $c->award(points) | Score on the board-wide DOOR CHAMPIONS ladder (Stats screen). |
| $c->log(msg) | An events-log line tagged with your slug. |
| $c->localStamp(utc, fmt) | Format UTC in the board timezone. |
| $c->boardRumors(n) | A few current rumors, for flavor. |
| $c->dropfile() | Path of the JSON drop file written for this session. |
#Storage: tables that scope themselves
A door never touches the board's tables. $c->table() creates and returns
door_<slug>_<name>:
Types: pk, int, text (add
'short' => true for indexable varchars), datetime. Methods:
insert, insertMany, rows(where, order, limit),
one, count, sum, update,
delete.
The where-grammar is an array: a bare column means equals, or suffix an operator in the key.
Everything is prepared statements underneath; identifiers are validated.
#The crash wall
Any uncaught exception or PHP error inside a door is caught: the caller sees a short in-character apology and lands back at the menu; the full error goes to the events log.
#The cron hook
Declare "cron": "cron.php" in the manifest and the board's daily tick includes it
(per enabled door, per board-day, each in its own try/catch) with $doorCron in
scope: kv(), table(), tx(), boardToday(),
log(). Make it idempotent: record the last day you ran in kv and
return early on repeats. Star Merchant's cron.php is the worked example.
Speaking to the board
New in 1.5.5. The cron context can also speak to the board on the door's behalf: two mediated calls, so doors still never touch a board table.
| Call | Effect |
|---|---|
| $doorCron->postMessage($areaTag, $subject, $lines) | Posts a real message into
the message area named by tag (GENERAL, say), authored by the SysOp account, body lines in
pipe color codes. For announcements a game generates on its own clock; Suzerain's
end-of-round Chronicle is the worked example. Returns false, posting nothing, if
the area or the SysOp account cannot be found. |
| $doorCron->boardRumors($n) | A few recent approved rumors, for flavor in rendered briefings. |
postMessage inside your own
tx(). Posting opens its own transaction for the message numbering. Queue the
text in kv during the tick and post after your transaction commits.#The admin panel hook
Declare "admin": "admin.php" and the console Doors page grows a panel link. Your
file renders inside the console with $doorAdmin (kv and tables) and
$csrfField to drop into every form; handle your own POSTs at the top, as Star
Merchant's panel does.
The admin context carries the same postMessage() call as cron, for announcements a
SysOp action should publish: a round opening, a season reset.
#The drop file
On entry the board writes a JSON drop file (path from $c->dropfile()) carrying
board name and version, base URL, node, and the caller's id, handle and level: the spiritual
DOOR.SYS, for doors wanting a file interface.
#Learn from HILO, then ship
- Read
doors/hilo/door.php: about 120 lines using the state bag, kv, line mode and award.doors/README.mdwalks it line by line. - Keep every visible row inside 80 columns and position with
at(). - Never touch superglobals or board tables. Everything you need arrives through the context.
- To distribute: zip the folder. Another SysOp unzips it into their
doors/directory, presses Scan, adds a DOOR menu item, done.