RETRO★PLAY Download the client
Network Store

The developer's guideFrom an account to a game on sale

Seven chapters, each true of the service as it runs. The reference for the bytes is docs/protocol.md; this is the path.

Awards and the daily

Retro Play knows no game's rules. Accounts, sessions, presence, the boards, the awards a player holds — those it keeps for every game. What a game's awards are, what its daily is, and what a score must look like to be believed — those are the game's, and there are two ways to give them to the service.

Declared on the game's page — no server

For most games this is the whole of it. Your organisation → Games → the game → Awards takes an awards.json:

{
  "awards": [
    { "key": "FIRST_RUN",    "name": "FIRST RUN",    "description": "A game finished",          "points": 50,  "hidden": false },
    { "key": "MARATHON_MAN", "name": "MARATHON MAN", "description": "Marathon to level 15",     "points": 100, "hidden": false },
    { "key": "GHOST",        "name": "???",          "description": "Something nobody expects", "points": 150, "hidden": true }
  ]
}
  • Up to twenty awards, 1000 points between them. key is 3–16 of A–Z0–9_, what the wire's AWARD names; name ≤ 16 and description ≤ 32 are what the pages and the client show; a hidden award shows as ??? until it is held.
  • Bake the same file into the build — the client and Tetriblox generate their award tables from it — so the two cannot disagree. An award is matched by key across edits, so its text may change; append, never reorder, because the bit in AWARDS is the award's place in the file.
  • Mark an award on the card the moment it is earned, so it works offline, and post AWARD <key> <mac> when you can; AWARDS at login brings the service's list back to a new card.

The daily comes with it. Send X DAILY and the service answers DAILY <yyyymmdd> <seed>: one seed a UTC day, SipHash-2-4 under your program key over the text DAILY <yyyymmdd>, the low 32 bits. Play the day's run on that seed and post it as a SCORE on mode D<yyyymmdd> — the day the seed named, taken today and yesterday — and the game's page shows DAILY — 19 SEP 2026 with a week of them. Everyone on the same seed, a board a day, nothing to host.

A game service of your own — rules beyond that

A game that wants to refuse a row its rules could not have produced ("too high for its lines", "a sprint is forty lines"), vouch for boards beyond its registered modes, or answer its own X verbs runs a small HTTP server that Retro Play forwards to — the game itself never dials it. Register it with the service's admins (rpm game service <ID> <url> <secret>), and implement four signed routes:

RouteRetro Play sendsYou answer
POST /x{user, game, platform, line}{"reply": "…"} — one line, passed to the game verbatim
POST /score{user, game, platform, mode, score, lines, level, seconds, initials, build}{"accept": true}, or {"accept": false, "reason": "…"} — the game sees ERR 422 SCORE: …
GET /boards—[{"mode": "D20260919", "title": "DAILY — 19 SEP 2026", "by_time": false}, …]
GET /awards—the awards.json shape above, with "game": "<ID>"

Every request carries X-RPM-Timestamp and X-RPM-Signature: hex(HMAC-SHA256(secret, timestamp ‖ "\n" ‖ path ‖ "\n" ‖ body)); verify it and refuse a stale timestamp. Two seconds to answer, or the game sees ERR 503 and a SCORE it should have judged is refused rather than taken unjudged. A game with a service must play as well with it unreachable as with it there. The reference implementation is tetriblox-server; the contract is docs/game-service.md.

Retro Play checks the MAC, the rate and the line's shape before your service is asked; your service judges the row, not the player.

← The wire The store page →