Skip to main content

AGENTS.md

Instructions for AI coding assistants (GitHub Copilot, Cursor, etc.) working with this codebase.

CRITICAL: WebGPU Required (NO WebGL)

Hyperscape requires WebGPU. WebGL WILL NOT WORK. This is a hard requirement. DO NOT:
  • Add WebGL fallback code
  • Use --disable-webgpu flags
  • Use forceWebGL parameters
  • Create headless Chrome configurations without GPU/WebGPU support
  • Suggest WebGL as an alternative

Why WebGPU-Only?

  • All materials use TSL (Three Shading Language) which ONLY works with WebGPU
  • Post-processing effects use TSL-based node materials
  • There is NO WebGL fallback path - the game simply won’t render

Browser Requirements

  • Chrome 113+, Edge 113+, Safari 18+ (macOS 15+)
  • WebGPU must be available and working
  • Check: webgpureport.org
  • Note: Safari 17 support was removed - Safari 18+ (macOS 15+) is now required

Server/Streaming (Vast.ai)

  • NVIDIA GPU with Display Driver REQUIRED: Must have gpu_display_active=true on Vast.ai
  • Display Driver vs Compute: WebGPU requires GPU display driver support, not just compute access
  • Must run non-headless with Xorg or Xvfb (WebGPU requires window context)
  • Chrome Beta Channel: Use google-chrome-beta (Chrome Beta) for WebGPU streaming on Linux NVIDIA (best stability and WebGPU support)
  • ANGLE Backend: Use Vulkan ANGLE backend (--use-angle=vulkan) on Linux NVIDIA for WebGPU stability
  • Xvfb Virtual Display: scripts/deploy-vast.sh starts Xvfb before PM2 to ensure DISPLAY is available
  • PM2 Environment: ecosystem.config.cjs explicitly forwards DISPLAY=:99 and DATABASE_URL through PM2
  • Capture Mode: Default to STREAM_CAPTURE_MODE=cdp (Chrome DevTools Protocol) for reliable frame capture
  • FFmpeg: Prefer system ffmpeg over ffmpeg-static to avoid segfaults (resolution order: /usr/bin/ffmpeg/usr/local/bin/ffmpeg → PATH → ffmpeg-static)
  • Playwright: Block --enable-unsafe-swiftshader injection to prevent CPU software rendering
  • Health Check Timeouts: All curl commands use --max-time 10 to prevent indefinite hangs
  • If WebGPU cannot initialize, deployment MUST FAIL

Project Overview

Hyperscape is a RuneScape-style MMORPG built on Three.js WebGPURenderer with TSL shaders.

CRITICAL: Secrets and Private Keys

Never put private keys, seed phrases, API keys, tokens, RPC secrets, or wallet secrets into any file that could be committed.
  • ALWAYS use local untracked .env files for real secrets
  • NEVER hardcode secrets in source files, tests, docs, JSON fixtures, scripts, config files, or workflow YAML
  • NEVER put real secrets in .env.example; placeholders only
  • If a secret is needed in production or CI, use the platform secret store, not a tracked file
  • If a task requires a new secret, document the variable name and load it from .env, .env.local, or deployment secrets

Key Rules

  1. No any types - ESLint will reject them
  2. WebGPU only - No WebGL code or fallbacks
  3. No mocks in tests - Use real Playwright browser sessions
  4. Bun package manager - Use bun install, not npm
  5. Strong typing - Prefer classes over interfaces
  6. Secrets stay out of git - Real keys must only come from local .env files or secret managers

Tech Stack

  • Runtime:
    • Client/Build: Bun v1.3.10+ (upgraded from 1.1.38 for Vite 6+ compatibility)
    • Server: Node.js 22+ (migrated from Bun for V8 incremental GC - March 2026)
  • Rendering: WebGPU ONLY (Three.js WebGPURenderer + TSL)
  • Engine: Three.js 0.183.2, PhysX (WASM)
  • UI: React 19.2.0
  • Server: Fastify (HTTP), uWebSockets.js (game WebSocket), LiveKit (voice)
  • Database: PostgreSQL (production, connection pool: 20), Docker (local), sqlite3 6.0.1 (dev only)
  • Testing: Vitest 4.1.0+, Jest 30.3.0, Playwright (WebGPU-enabled browsers only)
  • Build: Vite 8.0.0, @vitejs/plugin-react 6.0.1, Turbo, esbuild
  • AI: ElizaOS alpha tag (aligned with latest alpha releases)
  • Streaming: FFmpeg (system preferred over ffmpeg-static), Playwright Chromium, RTMP
  • Mobile: Capacitor 8.2.0 (Android, iOS)
  • Smart Contracts: Hardhat 3.1.11+, @nomicfoundation/hardhat-ethers 4.0.6 (ethers.js v6)

Common Commands

File Structure

Note: The betting stack (gold-betting-demo, evm-contracts, sim-engine, market-maker-bot) has been split into a separate repository: HyperscapeAI/hyperbet

Recent Changes (March 2026)

VRM Material Isolation Fix (March 17, 2026)

Change (PR #1061, Commit 364d0a5): Isolated VRM clone materials to prevent highlight bleed across mob instances. Problem: SkeletonUtils.clone() shares material instances across all VRM clones, causing hover highlight on one mob to affect all mobs of the same type. When hovering over a goblin, all goblins in the world would highlight simultaneously. Fix: Create fresh MeshStandardNodeMaterial per mesh in cloneGLB() so each entity has independent outputNode/uniforms. Textures remain shared by reference for memory efficiency. Implementation (packages/shared/src/rendering/materials/cloneGLB.ts):
Impact:
  • Each mob instance now has independent highlight state
  • Hovering over one goblin no longer highlights all goblins
  • Textures remain shared for memory efficiency
  • Fixes visual bug where all VRM mobs of same type would highlight together

Mob AI Tick Processing Fix (March 17, 2026)

Change (PR #1060, Commit a55079e): Wired mob AI tick processing into server tick loop to enable mob state machine transitions. Problem: MobEntity.serverUpdate() defers AI to GameTickProcessor.runAITick(), but GameTickProcessor was never instantiated — so mob AI state machines never received update() calls. Goblins entered IDLE on spawn and never transitioned to WANDER, CHASE, or ATTACK. Fix: Register mob AI tick handler at MOVEMENT priority in ServerNetwork, before mob tile movement, so AI decides movement targets and the movement system executes paths on the same tick. Implementation (packages/server/src/systems/ServerNetwork/index.ts):
Impact:
  • Mob AI state machines now function correctly
  • Goblins and other mobs properly transition through IDLE → WANDER → CHASE → ATTACK states
  • Deterministic OSRS-style tick ordering (AI decides, movement executes, same tick)
  • Fixes mobs standing idle forever after spawn

Dev Server Watcher CPU Fix (March 16, 2026)

Change (PR #1034, Commit 7b5bf08): Fixed dev server watcher burning 100% CPU when idle. Problem: Two compounding issues caused the dev script to consume 100% CPU core while completely idle:
  1. awaitWriteFinish polls every watched file at 100ms — redundant since the script already debounces rebuilds itself
  2. Polling fallback does a full recursive directory walk every 1s
Fix (packages/server/scripts/dev.mjs):
Impact:
  • Eliminates 100% CPU usage when dev server is idle
  • Reduces unnecessary file system polling
  • Better developer experience with lower resource consumption
  • No impact on rebuild responsiveness (200ms debounce still active)

Docker Build Improvements (March 15, 2026)

Change (PR #1033, Commit 7519105): Major Dockerfile improvements for production deployment. Key Changes:
  • Bun 1.3.10 Upgrade: Updated from 1.1.38 to support Vite 6+ builds
  • Client Build: Added packages/client build to Docker image (required for multi-service deployments)
  • Workspace Symlinks: Manually recreate Bun workspace symlinks after Docker COPY (COPY flattens symlinks)
  • Per-Package node_modules: Bun 1.3 no longer hoists all deps to root - explicitly copy package-level node_modules
  • better-sqlite3 Removal: Strip from manifests before install (segfaults under QEMU cross-compilation)
  • Manifest Embedding: Copy manifests from builder stage to ensure cleaned versions are used
Implementation (packages/server/Dockerfile):
Impact:
  • Production Docker images now build successfully with Vite 6+
  • Client and server can run from same image (multi-service deployments)
  • Workspace dependencies resolve correctly at runtime
  • No more QEMU segfaults from better-sqlite3

Dependency Updates (March 19, 2026)

Major Updates:
  • Vite: 6.4.1 → 8.0.0 (major version bump for build system)
  • @vitejs/plugin-react: 5.2.0 → 6.0.1 (React plugin compatibility)
  • @types/three: 0.182.0 → 0.183.1 (TypeScript definitions for Three.js 0.183.2)
  • @vitest/coverage-v8: 4.0.18 → 4.1.0 (test coverage tooling)
  • jsdom: 28.1.0 → 29.0.0 (testing environment)
  • jest: 29.7.0 → 30.3.0 (testing framework)
  • @nomicfoundation/hardhat-ethers: 3.1.3 → 4.0.6 (smart contract tooling)
  • @pixiv/three-vrm: 3.4.3 → 3.5.1 (VRM avatar support)
  • @solana-mobile/wallet-standard-mobile: 0.4.4 → 0.5.0 (mobile wallet integration)
  • sqlite3: 5.1.7 → 6.0.1 (SQLite database driver)
Impact:
  • Latest build tooling with improved performance and faster builds
  • Better React 19 compatibility with new Fast Refresh implementation
  • Updated testing environment with Jest 30.x and jsdom 29.x
  • Latest VRM avatar features and improvements
  • Improved mobile wallet support for Solana
  • Updated TypeScript definitions matching Three.js 0.183.2
  • Enhanced test coverage reporting with Vitest 4.1
  • SQLite 6.x with performance improvements and bug fixes
See CLAUDE.md for complete documentation.