Skip to main content

Overview

The @hyperscape/server package runs the authoritative game server:
  • Fastify 5 HTTP API with rate limiting
  • WebSocket real-time game state
  • PostgreSQL database with Drizzle ORM
  • LiveKit voice chat integration
  • CDN manifest loading at startup
  • Integrated frontend serving (production mode)

Package Location

Entry Point

src/index.ts initializes:
  1. Server configuration and environment loading
  2. Manifest fetching from CDN (production) or local cache (development)
  3. Database connection (PostgreSQL via Drizzle ORM)
  4. Fastify HTTP server with CORS, rate limiting, and static file serving
  5. WebSocket handlers for real-time multiplayer
  6. Game world and ECS systems
  7. Frontend serving (if client build exists in public/)

Manifest Loading

The server fetches game manifests from CDN at startup:
Behavior:
  • Production/CI: Always fetches from CDN, caches locally
  • Development: Skips fetch if local manifests exist
  • Caching: Manifests cached in world/assets/manifests/ with 5-minute TTL
This eliminates the need for Git LFS in production deployments.

Server Systems

TradingSystem

Located in src/systems/TradingSystem/: Modular player-to-player trading system with:
  • acceptance.ts: Accept/decline logic
  • items.ts: Add/remove items from offers
  • request.ts: Trade initiation with proximity checks
  • swap.ts: Screen transitions (Screen 1 ↔ Screen 2)
  • helpers.ts: Shared utilities and validation
  • types.ts: Type definitions
Features:
  • Two-screen confirmation flow (OSRS-accurate)
  • Wealth transfer indicators
  • Proximity validation (2-tile range)
  • Interface blocking (prevents exploits)
  • Audit logging for all trades

ServerNetwork Handlers

Located in src/systems/ServerNetwork/handlers/:

API Routes

Core Endpoints

Database

The server supports both PostgreSQL (production via Neon) and SQLite (local development). Schema is defined with Drizzle ORM.

Key Tables

Database Commands

Environment Variables

Core Configuration

Authentication

PUBLIC_PRIVY_APP_ID must match between client and server.

Assets & CDN

Manifest Loading:
  • Server fetches manifests from PUBLIC_CDN_URL/manifests/ at startup
  • Cached locally in world/assets/manifests/
  • In development, skips fetch if local manifests exist
  • In production/CI, always fetches fresh manifests

Optional Services

CORS Origins

The server automatically allows requests from:
Additional origins from environment variables:
  • CLIENT_URL
  • PUBLIC_APP_URL
  • ELIZAOS_URL / ELIZAOS_API_URL

Build Scripts

Model Bounds Extraction

The server includes build-time scripts for automatic collision footprint detection:
How it works:
  1. Scans world/assets/models/**/*.glb files
  2. Parses glTF position accessor min/max values
  3. Calculates bounding boxes and dimensions
  4. Computes tile footprints at scale 1.0
  5. Writes manifest for runtime use
Turbo Integration:
  • Runs automatically before build and dev commands
  • Cached based on GLB file changes
  • Only rebuilds when models are added/modified
Example Output:
Footprints are calculated from model dimensions × modelScale from stations.json. A furnace with raw dimensions 1.51×1.45 and scale 1.5 becomes 2.27×2.18 meters → 2×2 tiles.

Running

Development

Production

Server runs at http://localhost:5555 by default.

Dependencies

Docker Services

The server can use Docker for CDN and PostgreSQL:

Deployment

Railway (Production)

The server deploys to Railway using Nixpacks for automatic builds: Configuration Files:
  • nixpacks.toml - Build configuration (Bun provider, build commands)
  • railway.server.json - Service configuration (health checks, restart policy)
  • Dockerfile.server - Multi-stage Docker build (alternative to Nixpacks)
Deployment Process:
  1. Push to main branch triggers GitHub Actions
  2. Workflow calls Railway GraphQL API to trigger deployment
  3. Railway builds using Nixpacks configuration
  4. Server starts with cd packages/server && bun dist/index.js
  5. Manifests fetched from Cloudflare R2 CDN at startup
Environment Variables (Railway):
Health Check: Railway monitors /status endpoint every 30 seconds. Server must respond within 3 seconds or it will be restarted.

Manifest Fetching at Startup

In production, the server fetches manifests from the CDN instead of bundling them:
Fetched Manifests:
  • Root: npcs.json, stores.json, world-areas.json, etc.
  • Items: items/weapons.json, items/tools.json, etc.
  • Gathering: gathering/woodcutting.json, gathering/mining.json, etc.
  • Recipes: recipes/cooking.json, recipes/smithing.json, etc.
Caching:
  • Manifests cached locally in packages/server/world/assets/manifests/
  • Only re-fetched if content changes (HTTP ETag validation)
  • 5-minute cache headers (Cache-Control: public, max-age=300, must-revalidate)
This allows updating game content (items, NPCs, recipes) without redeploying the server—just upload new manifests to R2.

Key Files