Game Protocol
Overview
Section titled “Overview”Ludus is an open protocol — anyone can build a game and submit it to the arena. The @ludus/game-protocol package provides the complete submission pipeline: package validation, automated quality gates, catalog management, versioning, and revenue distribution.
Third-party developers implement the Game<TState, TAction> interface, package their game as a .ludus.tar.gz bundle, and submit it through the protocol. Automated quality gates verify determinism, SDK compliance, security, and performance. Once approved, the game is listed in the catalog where players discover it, play it, and rate it — while the developer earns 70% of platform fees.
Package Format
Section titled “Package Format”Games are distributed as .ludus.tar.gz archives containing:
my-game/├── ludus.manifest.json # Required — game descriptor├── dist/│ └── index.js # Compiled game module├── public/ # Optional assets│ ├── board.png│ └── sounds/└── README.md # Optional — displayed in the catalogGameManifest
Section titled “GameManifest”The manifest (ludus.manifest.json) describes the game to the protocol:
{ "name": "my-game", "version": "1.0.0", "displayName": "My Game", "description": "A short description (max 256 chars)", "author": { "name": "Developer Name", "wallet": "0x1234..." }, "game": { "file": "dist/index.js", "export": "default" }, "playerRange": { "min": 2, "max": 6 }, "category": "strategy", "tags": ["strategy", "territory", "multiplayer"], "license": "MIT"}| Field | Rule |
|---|---|
name | Kebab-case, 3-64 chars (/^[a-z][a-z0-9-]{2,63}$/) |
version | Semver: X.Y.Z |
displayName | Non-empty string |
description | Non-empty, max 256 chars |
author.name | Required |
author.wallet | Optional (required for revenue payouts) |
game.file | Path to compiled JS entry point |
game.export | Exported symbol name |
playerRange | min and max as positive integers |
category | One of: strategy, economic, social, card, dice, territory, puzzle, party, other |
tags | Max 10 items, max 32 chars each |
license | SPDX identifier (e.g., "MIT", "Apache-2.0") |
assets | Optional array of { path, type } (MIME types) |
Quality Gates
Section titled “Quality Gates”Every submission passes through four automated quality gates. All must pass before the game reaches human review.
1. SDK Compliance
Section titled “1. SDK Compliance”Verifies that the game object implements every method in the Game interface:
metadata(property)initialize()getValidActions()executeAction()isGameOver()getWinner()getRankings()describeAction()
How to pass: Implement every method. No stubs that throw.
2. Security
Section titled “2. Security”Scans source code for 13 banned patterns including:
- Arbitrary code execution functions
Function()constructorrequire()calls- File system access (
fs.,readFile,writeFile) - Network requests (
fetch,XMLHttpRequest,http.) process.envaccesschild_processusage__proto__/ prototype pollution- Dynamic
import()statements
How to pass: Keep game logic pure. No I/O, no network, no dynamic code execution.
3. Determinism
Section titled “3. Determinism”Runs the game N times (default: 3) with the same seed and verifies identical outputs. Configuration:
determinismRuns: 3testSeed: 42testPlayerCount: 2
How to pass: Use SeededRNG for all randomness. Never call Math.random() or Date.now().
4. Performance
Section titled “4. Performance”Profiles executeAction() for N turns (default: 50). Each turn must complete within the timeout:
performanceTurns: 50performanceTimeoutMs: 5000 (5 seconds per turn)
How to pass: Keep game logic efficient. Avoid exponential algorithms.
Running Gates Locally
Section titled “Running Gates Locally”import { QualityGateRunner } from "@ludus/game-protocol";
const runner = new QualityGateRunner();const results = runner.runAll(manifest, packageContents, () => MyGame);const summary = runner.summarize(results);
if (!summary.passed) { console.error("Failed gates:", summary.failedGates); for (const result of results) { if (!result.passed) { console.error(` ${result.gate}: ${result.details}`); } }}Submission Lifecycle
Section titled “Submission Lifecycle” DRAFT → PENDING → TESTING → REVIEW → LISTED | | +→ REJECTED ←+| Status | What Happens |
|---|---|
DRAFT | Package uploaded, manifest validated |
PENDING | Developer submits for review |
TESTING | Quality gates run automatically |
REVIEW | All gates passed — awaiting human/DAO review |
LISTED | Approved — live in the catalog |
REJECTED | Gate failure or manual rejection (with reason) |
Programmatic Submission
Section titled “Programmatic Submission”import { SubmissionService, InMemorySubmissionStore,} from "@ludus/game-protocol";
const store = new InMemorySubmissionStore();const service = new SubmissionService(store);
// Step 1: Upload package → DRAFTconst submission = service.submit(packageContents, "your-wallet-address");
// Step 2: Submit for review → PENDING → TESTING → REVIEW or REJECTEDconst reviewed = service.submitForReview( submission.id, packageContents, () => MyGameImplementation,);
// Step 3: Check resultif (reviewed.status === "REVIEW") { console.log("Passed all quality gates! Awaiting review.");} else if (reviewed.status === "REJECTED") { console.error("Rejected:", reviewed.rejectionReason);}
// Step 4: After human reviewservice.approve(submission.id, "Great game!");Duplicate detection prevents submitting the same name + version if an active submission already exists (unless the previous one was REJECTED).
Catalog Service
Section titled “Catalog Service”Once listed, games appear in the catalog with search, filtering, and popularity scoring.
Search and Discovery
Section titled “Search and Discovery”import { CatalogService, InMemoryCatalogStore } from "@ludus/game-protocol";
const catalog = new CatalogService(new InMemoryCatalogStore());
// List a game from an approved submissioncatalog.listGame(approvedSubmission);
// Search by text, category, or tagsconst results = catalog.search({ query: "strategy", category: "territory", sortBy: "popularity", limit: 20,});
// Browse by categoryconst strategyGames = catalog.listByCategory("strategy");
// Get trending gamesconst popular = catalog.getPopular(10);const recent = catalog.getRecent(10);Popularity Scoring
Section titled “Popularity Scoring”Each game has a composite popularity score (0-100) based on weighted factors:
| Factor | Weight | Scale |
|---|---|---|
| Downloads | 0.4 | Log scale (1000 downloads = max) |
| Rating | 0.3 | Direct (5.0 = max) |
| Recency | 0.2 | Linear decay over 365 days |
| Completions | 0.1 | Log scale (500 completions = max) |
The catalog tracks downloads, completions, and ratings:
catalog.recordDownload("my-game");catalog.recordCompletion("my-game");catalog.updateRating("my-game", 4.5);Version Manager
Section titled “Version Manager”Games use semver versioning with deprecation support and migration path analysis.
Semver Rules
Section titled “Semver Rules”- Patch (1.0.0 → 1.0.1): Bug fixes, no gameplay changes
- Minor (1.0.0 → 1.1.0): New features, backward-compatible
- Major (1.0.0 → 2.0.0): Breaking changes to game state or actions
Version Operations
Section titled “Version Operations”import { VersionManager, InMemoryVersionStore } from "@ludus/game-protocol";
const manager = new VersionManager(new InMemoryVersionStore());
// Register a new versionmanager.registerVersion("my-game", "1.0.0", "manifest-hash-abc");
// Publish an updatemanager.registerVersion("my-game", "1.1.0", "manifest-hash-def");
// Deprecate an old versionmanager.deprecateVersion("my-game", "1.0.0", "Security fix in 1.1.0");
// Check migration pathconst path = manager.getMigrationPath("my-game", "1.0.0", "2.0.0");// path.breaking: true (major version jump)// path.intermediateVersions: number of versions between
// Rollback if neededmanager.rollback("my-game", "1.0.0", "immediate");Deprecation
Section titled “Deprecation”Deprecated versions remain playable for a grace period (default: 30 days), then are unlisted. Players see a warning and a pointer to the latest version.
Revenue Engine
Section titled “Revenue Engine”The protocol handles fee collection, revenue splitting, and developer payouts.
Fee Split
Section titled “Fee Split”| Recipient | Share |
|---|---|
| Game developer | 70% |
| Ludus protocol | 30% |
Revenue Tracking
Section titled “Revenue Tracking”import { RevenueEngine, InMemoryRevenueStore } from "@ludus/game-protocol";
const engine = new RevenueEngine(new InMemoryRevenueStore());
// Platform records fees from match entry/bettingengine.recordFee("my-game", "dev-wallet", 100, "USDC");
// Generate revenue reportconst report = engine.getRevenueReport( "my-game", new Date("2026-01-01"), new Date("2026-03-31"),);// report.totalFees, report.developerShare, report.protocolShare
// Create a developer payoutconst payout = engine.createPayout("dev-wallet", periodStart, periodEnd);// payout.status: "pending" → "processing" → "completed"
// Mark payout as completedengine.completePayout(payout.id, "0xtxhash...");Payout Lifecycle
Section titled “Payout Lifecycle”- Fees accumulate from match entries, betting, and in-game transactions
- Developer requests a payout for a period
- Protocol calculates the 70/30 split
- Minimum payout threshold must be met (configurable)
- Payout is created as
pending→ markedcompletedafter on-chain settlement
Set author.wallet in your manifest to receive payouts. Without a wallet address, fees accumulate but cannot be paid out.
See Also
Section titled “See Also”- Build a Game — step-by-step development guide
- Deploy to Arena — submission and publishing walkthrough
- Protocol Overview — full Ludus architecture