ATDT Demo board online

Documentation · for developers

The Door API

A door is one folder with a manifest and a PHP class. The board gives it a screen, input, storage and player identity; the door gives back frames. Nothing a door does can take the board down. Every crash is caught, logged, and turned into a polite apology while the caller returns to the menu.

API version 1 · chapter 16 of the Sysop's Manual

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" }
  • api must be 1. slug is the unique lowercase id; it names the door's storage and its DOOR menu argument.
  • entry is the PHP file to include; class is the fully qualified class it defines.
  • Install by uploading the folder to data/doors/ (or doors/) and pressing Scan on the console Doors page.
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, optional work }
  • enter runs when the caller opens the door; return the opening frames.
  • input runs per keystroke, line, or editor payload, 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 back to the menu and calls leave.
Section 03

#DoorContext: every service

CallGives you
$c->out()An ANSI builder: ->cls(), ->at(row, col), ->txt(), ->fg(), ->bg(), ->reset(), ->str().
$c->pipe(s)Render pipe codes in a string to ANSI.
$c->center(s)Center a line on the 80-column screen.
$c->ansi(s)Wrap a raw ANSI string as a frame.
$c->bell()A terminal bell frame.
$c->hotkey()Ask for single-key input mode.
$c->line(...)Ask for line input mode, with a prompt and options.
$c->editor(...)Ask for the full-screen editor.
$c->exitDoor()The leave-the-door frame.
$c->user()The caller row: id, handle, level.
$c->stateA per-visit scratch array, persisted between requests automatically.
$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>/ for flat files. $c->path(name) resolves one file inside it.
$c->award(n)Add to this caller's score for the board-wide DOOR CHAMPIONS ladder on the Stats screen.
$c->log(msg)A line in the board events log, tagged with your slug.
$c->localStamp(...)Format a UTC timestamp in the board timezone.
$c->boardRumors(n)A few current board rumors, for flavor text.
$c->dropfile()Path of the classic-style 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 a table named door_<slug>_<name> from a compact schema:

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

Column types: pk (auto id), int, text (add 'short' => true for indexable varchars), datetime. The third argument lists indexes. The object then offers:

insert(row)Insert one row; returns the new id. insertMany(rows) for a batch.
rows(...)Select many, with where, order and limit.
one(where)Select one row or null.
count(where)Aggregate. sum(col, where) too.
update(set, where)Update matching rows.
delete(where)Delete matching rows. [] means all.

The where grammar is an array: a bare column means equals, or suffix an operator inside the key.

$u->rows(['sector' => 9]); // sector = 9 $u->rows(['points >' => 100]); // greater than $u->rows(['name LIKE' => 'A%']); // LIKE $u->rows(['id IN' => [1, 2, 3]]); // IN list

Operators: =, !=, <, <=, >, >=, IN, LIKE. Everything is a prepared statement underneath; identifiers are validated, so injection has no doorway.

Section 05

#The crash wall

Any uncaught exception or PHP error inside a door is caught by the board: the caller sees a short in-character apology and lands back at the menu, and the full error goes to the events log for you.

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

If the folder has a cron.php, the board's daily tick includes it (each enabled door, each day, wrapped in its own try/catch) with $doorCron in scope:

$doorCron->kv(); // same key/value store $doorCron->table(...); // same tables $doorCron->tx(fn () => ...); // a transaction $doorCron->boardToday(); // the board-local date string $doorCron->log('day-tick complete');

Make it idempotent: record the last day you ran in kv and return early on a repeat. Star Merchant's cron.php is the worked example.

Section 07

#The admin panel hook

If the folder has an admin.php, the console Doors page grows a panel link for it. Your file renders inside the console page with $doorAdmin (kv and tables, plus the db) and $csrfField to drop into every form you print. Handle your own POSTs at the top, exactly like Star Merchant's panel does.

Section 08

#The drop file

On entry the board writes a JSON drop file, with its path from $c->dropfile(), carrying the board name and version, base URL, node, and the caller's id, handle and level. The spiritual DOOR.SYS, for doors that want a file interface or external tooling.

Section 09

#Learn from HILO, then ship

  • Read doors/hilo/door.php: about 120 lines using the state bag, kv, line mode and award. The SDK README at doors/README.md walks it line by line.
  • Keep every visible row inside 80 columns; position with at() rather than trailing on wraps.
  • Never touch superglobals or board tables. Everything you need arrives through the context.
  • To distribute: zip the folder. Another sysop drops it in data/doors/, presses Scan, done.

↑ Back to the top