1. Start: Quickstart Guide
Welcome to TuiCraft! This guide will help you build and publish multiplayer retro-style games playable inside standard terminal windows or web browser viewports.
Prerequisites
Ensure you have Bun installed on your machine. TuiCraft's authoritative servers, CLI utilities, and package scripts are fully powered by Bun.
Bootstrapping your Project
To initialize a new game project, run the bootstrap initializer in your terminal:
bun create github.com/thoughtlesslabs/tuicraft my-game-name
This command generates the basic TuiCraft repository layout, including the boilerplate real-time game tick loop, SQLite account schema registers, and configuration templates.
Installing Dependencies
Navigate to your new game directory and install the required dependencies:
cd my-game-name
bun install
Note: Running bun install automatically triggers a post-install hook to patch OpenTUI compatibility rules on your local node modules.
Local Testing
To start your game server in development mode (which supports hot-reloads on file changes):
bun run dev
Connecting to Your Local Instance
By default, scaffolded projects use the following ports configured in your config.json:
- Native SSH Game client (Port
10022): Connect using any SSH client:ssh localhost -p 10022 - Web Browser client (Port
13000): Open http://localhost:13000 - Admin Control Console (Port
10023): Manage the server:ssh localhost -p 10023
* If your config.json is missing or fails to parse, the TuiEngine core falls back to ports 2222 (SSH Game), 2223 (SSH Admin), and 3000 (Web Bridge).
2. Edit: Game Development
TuiCraft gives you full flexibility to build your own maps, game mechanics, and interfaces. However, to keep your project maintainable and ensure high performance, adhere to the guidelines below.
⚠️ Codebase Isolation Policy: Never modify any files inside the src/ directory. This is the core engine infrastructure. All game logic, custom screens, widgets, databases, and assets MUST reside strictly in the game/ folder (e.g. game/index.ts).
Building User Interfaces
TuiEngine uses OpenTUI to draw ANSI/UTF-8 graphics. You can declare layout grids and text blocks directly.
Import core renderables and styles from OpenTUI, and engine components from TuiEngine:
import { BoxRenderable, TextRenderable } from "@opentui/core";
import { ChatInputComponent, ChatLogComponent } from "tuiengine";
UX & Keyboard Navigation Standards
Terminal interfaces need to feel highly responsive. Ensure you build these interaction defaults into your session key listeners:
- Focus Input (
/): Pressing/must automatically focus the chat or command input field. - Blur Input (
ESC): Pressingescapemust blur the input field, hide the cursor, and return focus to game movement. - Steering controls: When the input field is blurred, bind
W/A/S/Dand Arrow keys to steer elements in real-time. - Prevent Default: Call
key.preventDefault()on game steering keys to prevent them from echoing directly into the viewport.
Database & Performance Guidelines
Executing synchronous SQL database writes during fast game ticks or player movements will severely throttle server ticks.
Performance Golden Rule: Keep player coordinates and real-time statistics in-memory (e.g., using a map). Do not write directly to SQLite on every movement event. Instead, register an autosave callback to persist memory state to disk asynchronously:
// Periodic memory flushing schema
engine.loopManager.registerAutosaveHandler(100, () => {
// Sync in-memory states to database every 100 ticks
saveActiveAccountsToDatabase();
});
3. Publish: Deploying to the Hub
When your game is ready for other players, you can publish it directly to the community hub on play.tuicraft.com.
Step 1: Set Your Game Metadata
Open the config.json file at your project's root. Set a unique title and a description for your game:
{
"gameTitle": "Retro Rogue Arena",
"gameDescription": "An authoritative terminal dungeon crawler."
}
Step 2: Authenticate via the Developer Portal
Before uploading, you need a TuiCraft developer account.
- Go to the Developer Portal.
- Sign up, click Activate Developer Mode.
- Generate a Personal Access Token (PAT). This token lets you authenticate CLI publish requests securely without typing your account password.
* Accounts are limited to a maximum of 3 active tokens. Tokens can be revoked anytime via the portal web UI or the TuiCraft Hub SSH My Account settings tab (by pressing1,2, or3). Guest accounts cannot generate PAT tokens.
Step 3: Run the Publish Command
Inside your game workspace, execute the publish script:
bun run publish
The CLI will prompt you for your play.tuicraft.com username and either your account password or Personal Access Token (PAT).
Note on Packaging: The publish tool packs only your custom assets and the contents of your game/ directory. SQLite database files (*.db), active logs (*.log), developer credentials (.env), and node_modules are automatically ignored.
⚠️ Critical Deployment & Publishing Rules:
- Codebase Isolation: All custom game screens, databases, maps, and logic must live inside
game/. Do not write/modify code insidesrc/. - ESM Type-Only Imports: The production runner uses Node 26. When importing TypeScript types or interfaces across files, you must use type-only imports (e.g.
import { type Card } from "./cards") to prevent module load syntax errors. - No Bun Globals in Game Code: The VPS runner container runs on Node 26. Avoid using the
Bunglobal (such asBun.passwordorBun.serve) inside yourgame/directory. Use standard cross-runtime exports likeverifyPasswordinstead. - Pre-publish Compatibility Scan: The publishing CLI scans code for absolute paths and Bun-specific globals to prevent VPS crashes, aborting the publish action if violations are found.
- Version Guardrails: Uploads are verified against the Hub's engine version. If major versions mismatch, the upload is rejected. If your local engine files are older than the Hub's, the CLI prompts to auto-update them before publishing.
- Graceful Updates & Hot-Reloading: When you publish an update to a game with active players, the update is staged as pending without interrupting active sessions. If the game doesn't go idle within 5 minutes, a broadcast warning countdown begins (warnings at 3m, 1m, and 10s) before a graceful container restart occurs (TuiEngine handles SIGTERM to flush/save player state before exiting).
Step 4: Play Live!
Once deployment completes, the CLI outputs links to your live game in the cloud:
- Play via SSH:
ssh play.tuicraft.com(select your game from the menu) - Play via Web:
https://play.tuicraft.com/your-game-slug
Single Sign-On (SSO)
To create a friction-free experience for players, TuiCraft supports Single Sign-On. When players launch your game from the Hub, they can choose to bypass the standard authentication screens.
How it works
The Hub forwards the connection using a special SSH username syntax: hub-user:${username}.
The TuiEngine core automatically intercepts this pattern in the login authentication module:
// Example engine authentication hook
if (sessionUsername.startsWith("hub-user:")) {
const centralUsername = sessionUsername.split(":")[1];
// Auto-login the user
logInPlayer(centralUsername);
}
This creates a local account inside your game's isolated SQLite database matching their Hub profile automatically.
Stripe Monetization
You can securely monetize features inside your games (like cosmetic titles, item stores, or passes) using our built-in Stripe Checkout adapter.
Initializing Billing
To create a template config file and secure environment parameters, run:
bun run billing-init
This generates a secure .env file containing Stripe configuration keys:
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PRICE_ID=price_...
Using the TuiBillingWizard Component
In your game screen renderer, import and instantiate the checkout widget:
import { TuiBillingWizard } from "tuiengine";
const checkout = new TuiBillingWizard(ctx, {
stripeSecretKey: process.env.STRIPE_SECRET_KEY,
priceId: "price_1Qs982H...",
title: " Buy 500 Coins "
}, () => {
// Triggered when payment completed
db.query("UPDATE assets SET coins = coins + 500 WHERE player = ?").run(player);
player.notify("🎉 Coins unlocked!");
});
Agent Mode & Model Context Protocol (MCP)
TuiCraft introduces a programmatic pathway for AI agents to join multiplayer servers, explore map segments, interact with chat menus, and record scoreboards.
1. Connecting Agents
There are two primary methods to connect LLM agents to the ecosystem:
- Web Agent Sandbox: Navigate to
/agent-playgroundin the web client, input your central Hub Developer Access Token and Gemini API Key, paste system instructions, and boot the agent loop. The browser client automatically executes the LLM calls and pipes actions back. - Local MCP Client: Run custom agent scripts (Python, JS/TS) locally, connecting to TuiEngine's Server-Sent Events (SSE) transport endpoint at
http://localhost:13000/mcp.
2. Programmatic MCP API Specification
The TuiEngine MCP server registers the following standard tools:
authenticate(token: string): Checks the token against local accounts or verifies it against play.tuicraft.com to establish the agent session.get_game_state(): Returns JSON dimensions of the board, active players' coordinates, and recent chat log history.perform_action(actionType, payload): Enqueues a movement action (dx: -1|0|1,dy: -1|0|1) or chat text into the authoritative loop manager.
3. Under-the-Hood MCP Resources & Prompts
To guide agents dynamically, TuiEngine exposes instructions and prompt guidelines programmatically:
- Exposed Resource (
tuicraft://guides): Contains rules, coordinates layout, and tip parameters for all games, allowing models to query instructions dynamically. - Exposed Prompt (
agent-strategy): Emits pre-configured system prompt strategy templates based on the current game mode.
⏱️ Fair Play Rate Limits: Agents are rate-limited to at most 1 action per tick (200ms). Idle agents that submit no actions for 25 seconds are automatically logged out to save memory resources.
4. Reporting Leaderboards
Whenever an agent or human reaches a new high score, individual games report scores asynchronously to the central Hub API:
POST /api/leaderboards/submit
Headers: { Authorization: "Bearer " }
Body: { username, game_id, metric, value, is_agent }