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 (production) / SQLite (local) database
  • LiveKit voice chat integration

Package Location

packages/server/
├── src/
│   ├── database/       # Database schema and queries
│   ├── infrastructure/ # Docker, CDN configuration
│   ├── scripts/        # Utility scripts
│   ├── shared/         # Shared server utilities
│   ├── startup/        # Server initialization
│   ├── systems/        # Server-specific systems
│   ├── types/          # TypeScript types
│   ├── types.ts        # Core type definitions
│   └── index.ts        # Entry point
├── scripts/
│   ├── extract-model-bounds.ts  # Build-time model bounds extraction
│   └── inject-model-collision.ts # GLB collision data injection
├── world/
│   └── assets/         # Game assets (Git LFS)
│       ├── models/     # 3D models (GLB files)
│       └── manifests/  # Generated manifests
│           ├── model-bounds.json  # Auto-generated model bounds
│           └── stations.json      # Station configurations
├── docker-compose.yml  # Docker services
├── turbo.json          # Turbo build configuration
└── .env.example        # Environment template

Entry Point

src/index.ts initializes:
  1. Database connection (PostgreSQL or SQLite)
  2. Asset CDN (Docker nginx, optional)
  3. Fastify HTTP server with plugins
  4. WebSocket handlers
  5. Game world and systems

API Routes

RouteMethodPurpose
/healthGETHealth check
/authPOSTAuthentication
/wsWSGame WebSocket connection

Database

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

Key Tables

// From packages/server/src/database/schema.ts
export const characters = pgTable("characters", {
  id: text("id").primaryKey(),
  accountId: text("accountId").notNull(),
  name: text("name").notNull(),
  // Combat stats
  combatLevel: integer("combatLevel").default(3),
  attackLevel: integer("attackLevel").default(1),
  strengthLevel: integer("strengthLevel").default(1),
  defenseLevel: integer("defenseLevel").default(1),
  constitutionLevel: integer("constitutionLevel").default(10),
  rangedLevel: integer("rangedLevel").default(1),
  // Gathering skills
  woodcuttingLevel: integer("woodcuttingLevel").default(1),
  fishingLevel: integer("fishingLevel").default(1),
  firemakingLevel: integer("firemakingLevel").default(1),
  cookingLevel: integer("cookingLevel").default(1),
  // XP for all skills...
  health: integer("health").default(100),
  coins: integer("coins").default(0),
  positionX: real("positionX").default(0),
  positionY: real("positionY").default(10),
  positionZ: real("positionZ").default(0),
});
TablePurpose
usersAccount authentication with Privy/Farcaster IDs
charactersFull character data with all skills and XP
inventoryPlayer items (28 slots with quantities)
equipmentWorn items by slot type
itemsItem definitions and stats
worldChunksPersistent world modifications
playerSessionsLogin/logout tracking
playerDeathsDeath locks to prevent duplication
npcKillsKill statistics per player

Database Commands

cd packages/server

# Using Drizzle Kit
bunx drizzle-kit push      # Apply schema to database
bunx drizzle-kit generate  # Generate migrations
bunx drizzle-kit studio    # Open Drizzle Studio GUI

Environment Variables

# Database (choose one)
DATABASE_URL=postgresql://user:pass@host/db    # PostgreSQL (production)
# or SQLite is used automatically for local development

# PostgreSQL Connection Pool (increased March 2026)
POSTGRES_POOL_MAX=20                           # Up from 10 (commit 24fa8a5)
POSTGRES_POOL_MIN=2                            # Minimum idle connections

# Database Mode (auto-detected from DATABASE_URL hostname)
# DUEL_DATABASE_MODE=remote                    # or local (auto-detected)

# Authentication (required for persistent accounts)
PRIVY_APP_ID=your-privy-app-id
PRIVY_APP_SECRET=your-privy-app-secret
JWT_SECRET=your-jwt-secret

# Server Configuration
PORT=5555
NODE_ENV=development

# CDN (optional)
PUBLIC_CDN_URL=http://localhost:8080
DUEL_PUBLIC_CDN_URL=https://assets.hyperscape.club  # Production CDN (commit 2b3cbcb)

# Voice Chat (optional)
LIVEKIT_API_KEY=your-livekit-key
LIVEKIT_API_SECRET=your-livekit-secret
LIVEKIT_URL=wss://your-livekit-url

# ElizaCloud AI (for duel arena agents - March 2026)
ELIZAOS_CLOUD_API_KEY=your-elizacloud-api-key  # Access to 13 frontier models

# Streaming (optional - auto-detected from available keys)
TWITCH_STREAM_KEY=live_123456789_abcdefghij    # or TWITCH_RTMP_STREAM_KEY
KICK_STREAM_KEY=your-kick-stream-key
STREAM_ENABLED_DESTINATIONS=twitch,kick        # Auto-detected if not set

# Streaming Capture (March 2026)
STREAM_CAPTURE_MODE=mediarecorder              # Changed from cdp (commit 72c667a)
STREAM_CAPTURE_CHANNEL=chrome-beta             # Changed from chrome-unstable (commit 547714e)
STREAM_CAPTURE_ANGLE=default                   # Changed from vulkan (commit 547714e)
DISPLAY=:99                                    # Xvfb virtual display

# Duel Arena Oracle (optional)
DUEL_ARENA_ORACLE_ENABLED=true
DUEL_ARENA_ORACLE_PROFILE=testnet              # or mainnet
DUEL_ARENA_ORACLE_EVM_PRIVATE_KEY=0x...
DUEL_ARENA_ORACLE_SOLANA_AUTHORITY_SECRET=...

Build Scripts

Model Bounds Extraction

The server includes build-time scripts for automatic collision footprint detection:
# Extract bounding boxes from GLB models
bun run extract-bounds

# Generates: world/assets/manifests/model-bounds.json
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:
{
  "generatedAt": "2026-01-15T11:25:00.000Z",
  "tileSize": 1.0,
  "models": [
    {
      "id": "furnace",
      "assetPath": "asset://models/furnace/furnace.glb",
      "bounds": {
        "min": { "x": -0.755, "y": 0.0, "z": -0.725 },
        "max": { "x": 0.755, "y": 2.1, "z": 0.725 }
      },
      "dimensions": { "x": 1.51, "y": 2.1, "z": 1.45 },
      "footprint": { "width": 2, "depth": 1 }
    }
  ]
}
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

bun run dev:server    # With hot reload and auto-restart

Production

bun run build:server  # Build to dist/ (runs extract-bounds automatically)
bun start             # Start production server
Server runs at http://localhost:5555 by default.

Dependencies

PackagePurpose
fastifyHTTP server (v5)
@fastify/websocketWebSocket support
@fastify/corsCORS handling
@fastify/rate-limitRate limiting
pgPostgreSQL client
@hyperscape/sharedCore engine
@privy-io/server-authAuthentication
livekit-server-sdkVoice chat
msgpackrBinary serialization

Docker Services

The server can use Docker for CDN and PostgreSQL:
# Asset CDN
bun run cdn:up       # Start nginx CDN container
bun run cdn:down     # Stop CDN
bun run cdn:logs     # View CDN logs
bun run cdn:verify   # Verify CDN is working

# Asset Management
bun run assets:sync   # Sync assets from Git LFS
bun run assets:deploy # Deploy to R2 (production)

Deployment

Cloudflare Workers

bun run deploy        # Deploy to staging
bun run deploy:prod   # Deploy to production

Railway

The railway.server.json in the project root configures Railway deployment.

ElizaCloud AI Integration (March 2026)

All duel arena AI agents now use @elizaos/plugin-elizacloud for unified model access (commit 4d1eb53). 13 Frontier Models: American Models:
  • openai/gpt-5 - GPT-5
  • anthropic/claude-sonnet-4.6 - Claude Sonnet 4.6
  • anthropic/claude-opus-4.6 - Claude Opus 4.6
  • google/gemini-3.1-pro-preview - Gemini 3.1 Pro
  • xai/grok-4 - Grok 4
  • meta/llama-4-maverick - Llama 4 Maverick
  • mistral/magistral-medium - Magistral Medium
Chinese Models:
  • deepseek/deepseek-v3.2 - DeepSeek V3.2
  • alibaba/qwen3-max - Qwen 3 Max
  • minimax/minimax-m2.5 - Minimax M2.5
  • zai/glm-5 - GLM-5
  • moonshotai/kimi-k2.5 - Kimi K2.5
  • bytedance/seed-1.8 - Seed 1.8
Configuration:
# Single API key for all models
ELIZAOS_CLOUD_API_KEY=your-elizacloud-api-key
Benefits:
  • Simplified configuration (one API key instead of multiple provider keys)
  • Access to 13 frontier models from 13 different providers
  • Consistent model routing and error handling
  • Reduced dependency complexity
Files:
  • src/eliza/agentHelpers.ts - Added elizacloud provider
  • src/eliza/ModelAgentSpawner.ts - Updated model agent spawning
  • packages/plugin-hyperscape/src/index.ts - Added ElizaCloud plugin types

Duel Arena Oracle (March 2026)

The server publishes duel outcomes to EVM and Solana oracle contracts for verifiable results (commit aecab58). New Oracle Fields:
  • damageA - Total damage dealt by participant A
  • damageB - Total damage dealt by participant B
  • winReason - Detailed win reason (knockout, timeout, forfeit, draw)
  • seed - Cryptographic seed for replay verification
  • replayHashHex - Hash of replay data for integrity verification
  • resultHashHex - Combined hash of all duel outcome data
Configuration:
# Enable oracle publishing
DUEL_ARENA_ORACLE_ENABLED=true
DUEL_ARENA_ORACLE_PROFILE=testnet  # or mainnet

# Metadata API base URL
DUEL_ARENA_ORACLE_METADATA_BASE_URL=https://api.hyperscape.gg/api/duel-arena/oracle

# EVM signer (works across Base, BSC, Avalanche)
DUEL_ARENA_ORACLE_EVM_PRIVATE_KEY=0x...

# Solana signers
DUEL_ARENA_ORACLE_SOLANA_AUTHORITY_SECRET=...
DUEL_ARENA_ORACLE_SOLANA_REPORTER_SECRET=...
Database Schema: Oracle records are stored in the arena_rounds table with comprehensive outcome data for betting market settlement and replay verification. Files:
  • src/oracle/DuelArenaOraclePublisher.ts - Oracle publishing logic
  • src/oracle/config.ts - Oracle configuration and target management
  • src/oracle/types.ts - Oracle type definitions

Duel System

The server includes a comprehensive duel arena system with AI combat, streaming, and betting integration.

Combat Roles (PR #933, commit 82ff784)

Duel agents are assigned weighted random combat roles:
  • Melee (50%): Bronze weapons (longsword, scimitar, 2h sword)
  • Ranged (25%): Shortbow + bronze arrows (500 qty)
  • Mage (25%): Staff of air + wind strike autocast + runes
Implementation:
  • DuelOrchestrator.pickCombatRole(): Weighted random selection
  • DuelOrchestrator.ensureAgentCombatSetup(): Role-specific gear equipping
  • DuelOrchestrator.cleanupAgentCombatSetup(): Full gear removal after duel
  • DuelCombatAI: Adapts style switching based on combat role
Gear Lifecycle:
  1. Pre-duel: Role selected → gear equipped → food filled → health restored
  2. Post-duel: Gear removed → food removed → health restored → teleport back

Critical Bug Fixes (PR #933)

Combat State Key Mismatch:
  • Issue: CombatStateService syncs abbreviated keys (data.c/data.ct) but getGameState() only read full keys
  • Impact: DuelCombatAI always saw inCombat=false and flooded executeAttack every tick
  • Fix: EmbeddedHyperscapeService now reads both abbreviated and full keys
  • File: packages/server/src/eliza/EmbeddedHyperscapeService.ts
Magic Attack TOCTOU Race:
  • Issue: Cooldown checked early but claimed after async consumeRunesForSpell call
  • Impact: Duplicate magic projectiles, double rune consumption
  • Fix: Moved cooldown claim and enterCombat before async rune consumption
  • File: packages/shared/src/systems/shared/combat/CombatSystem.ts

Terrain Flat Zones (commit 7a60135)

DuelArenaVisualsSystem registers flat zones programmatically for all 8 floor areas (6 arenas + lobby + hospital):
  • Prevents players/agents from sinking ~0.4m into arena floors
  • Terrain height queries return correct floor-level values
  • Terrain mesh carved under floors to prevent grass/vegetation clipping
  • File: packages/shared/src/systems/client/DuelArenaVisualsSystem.ts

Key Files

FilePurpose
src/index.tsServer entry point
src/database/Database schema and queries
src/startup/Initialization logic
src/arena/Duel arena system (betting, combat AI, market maker)
src/eliza/ElizaOS AI agent integration
src/systems/StreamingDuelScheduler/Streaming duel orchestration
docker-compose.ymlDocker service configuration
.env.exampleEnvironment template