ATDT Demo board online

Documentation · for developers

The Door API

A door is one folder with a manifest and a PHP class. The board provides screen, input, storage and identity; the door returns frames. Nothing a door does can take the board down.

API version 1 · chapter 16 of the SysOp's Manual · ATDT 1.7.4

Section 01

#Anatomy of a door

doors/dicerun/ door.json the manifest door.php the game class cron.php optional: daily housekeeping admin.php optional: a panel inside the console

The manifest:

{ "api": 1, "slug": "dicerun", "title": "Dice Run", "entry": "door.php", "class": "\\Doors\\DiceRun\\Door", "cron": "cron.php", "admin": "admin.php" }
  • api must be 1. slug is the unique lowercase id naming the door's storage and its DOOR menu argument.
  • entry is the PHP file to include; class is the fully qualified class it defines. cron and admin are optional.
  • Install by uploading the folder into the doors/ directory of the board, alongside hilo and starmerchant, 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.
Section 02

#The lifecycle

The class implements three methods:

final class Door implements \Atdt\DoorInterface { public function enter(\Atdt\DoorContext $c): array; // first frames public function input(\Atdt\DoorContext $c, \Atdt\In $in): array; public function leave(\Atdt\DoorContext $c): void; // cleanup }
  • enter runs when the caller opens the door; return the opening frames.
  • input receives keys, lines, or editor payloads exactly like board screens. Check $in->kind for key, line or editor, then read the matching field.
  • To exit, include $c->exitDoor() in your returned frames; the board pops the caller to the menu and calls leave.
Section 03

#DoorContext: every service

CallGives 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->statePer-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.
Section 04

#Storage: tables that scope themselves

A door never touches the board's tables. $c->table() creates and returns door_<slug>_<name>:

$scores = $c->table('scores', [ 'id' => 'pk', 'user' => ['int'], 'points' => ['int'], 'name' => ['text', 'short' => true], ], [['user']]);

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.

$t->rows(['sector' => 9]); // = $t->rows(['points >' => 100]); // > (also != < <= >=) $t->rows(['name LIKE' => 'A%']); // LIKE $t->rows(['id IN' => [1, 2, 3]]); // IN

Everything is prepared statements underneath; identifiers are validated.

Section 05

#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.

A door can be wrong; it cannot be fatal. Write freely. The worst outcome of a bug is one annoyed caller and one log line.
Section 06

#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.

CallEffect
$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.
One rule: never call 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.
Section 07

#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.

Section 08

#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.

Section 09

#Learn from HILO, then ship

  • Read doors/hilo/door.php: about 120 lines using the state bag, kv, line mode and award. doors/README.md walks 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.

↑ Back to the top