Files
cell/CLAUDE.md
John Alanbrook efd98460c5
Some checks failed
Build and Deploy / build-macos (push) Failing after 4s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
-u is no longer available as a global, only within the running actor code; enable passing args to use
2025-05-22 18:59:57 -05:00

13 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Build Commands

Build variants

  • make debug - Build debug version (uses meson debug configuration)
  • make fast - Build optimized version
  • make release - Build release version with LTO and optimizations
  • make small - Build minimal size version
  • make web - Build for web/emscripten platform
  • make crosswin - Cross-compile for Windows using mingw32

Testing

  • meson test -C build_dbg - Run all tests in debug build
  • meson test -C build_<variant> - Run tests in specific build variant
  • ./build_dbg/prosperon tests/<testname>.js - Run specific test
  • Available tests: spawn_actor, empty, nota, wota, portalspawner, overling, send, delay

Common development commands

  • meson setup build_<variant> - Configure build directory
  • meson compile -C build_<variant> - Compile in build directory
  • ./build_dbg/prosperon examples/<example> - Run example from build directory
  • Copy prosperon to game directory and run: cp build_dbg/prosperon <game-dir>/ && cd <game-dir> && ./prosperon

Architecture Overview

Prosperon is an actor-based game engine inspired by Douglas Crockford's Misty system. Key architectural principles:

Actor Model

  • Each actor runs on its own thread
  • Communication only through message passing (no shared JavaScript objects)
  • Hierarchical actor system with spawning/killing
  • Actor lifecycle: awake, update, draw, garbage collection

JavaScript Style Guide

  • Use use() function for imports (Misty-style, not ES6 import/export)
  • Prefer closures and javascript objects and prototypes over ES6 style classes
  • Follow existing JavaScript patterns in the codebase
  • Functions as first-class citizens
  • Do not use const or let; only var

Core Systems

  1. Actor System (scripts/core/engine.js)

    • Message passing via send(), $_.receive()
    • Actor spawning/management
    • Register-based component system (update, draw, gui, etc.)
  2. Module System

    • use() function for loading modules
    • Module paths: scripts/modules/, scripts/modules/ext/
    • Custom QuickJS build with embedded C modules
  3. Build System

    • Meson build configuration (Makefile is convenience wrapper)
    • Multiple platform targets (Windows, macOS, Linux, Web)
    • Custom QuickJS build in subprojects/
    • Uses SDL3 for cross-platform support

Engine Entry Points

  • source/prosperon.c - Main C entry point
  • scripts/core/engine.js - JavaScript engine initialization for system
  • scripts/core/base.js has modifications to this Javascript runtime (for example, additions to the base Array, String, etc)

Subprojects

  • C code has many subprojects, who's source and sometimes documentation can be found in subprojects. subprojects/quickjs/doc has documentation for quickjs

Resource System

  • Scripts are bundled into core.zip during build
  • Runtime module loading via PhysFS
  • Resource paths checked in order: /, scripts/modules/, scripts/modules/ext/

Notable Dependencies

  • QuickJS (custom build) - JavaScript runtime
  • SDL3 - Platform abstraction
  • Chipmunk2D - Physics
  • ENet - Networking
  • Soloud - Audio
  • Tracy - Profiling (when enabled)

Development Tips

Running Games

# Build first
make debug

# Run example from build directory
./build_dbg/prosperon examples/chess

# Or copy to game directory
cp build_dbg/prosperon examples/chess/
cd examples/chess
./prosperon

Documentation

  • Documentation is found in docs
  • Documentation for the JS modules loaded with 'use' is docs/api/modules
  • .md files directly in docs gives a high level overview
  • docs/dull is what this specific Javascript system is (including alterations from quickjs/es6)

Shader Development

  • Shaders are in shaders/ directory as HLSL
  • Compile script: shaders/compile.sh
  • Outputs to platform-specific formats: dxil/, msl/, spv/

Example Games

Located in examples/ directory:

  • chess - Chess implementation (has its own Makefile)
  • pong - Classic pong game
  • snake - Snake game
  • tetris - Tetris clone
  • bunnymark - Performance test

Testing

# Run all tests
meson test -C build_dbg

# Run specific test  
./build_dbg/prosperon tests/spawn_actor.js

Debugging

  • Use debug build: make debug
  • Tracy profiler support when enabled
  • Console logging available via console.log(), console.error(), etc.
  • Log files written to .prosperon/log.txt

Project Structure Notes

Core JavaScript Modules

  • JavaScript modules are defined using the MISTUSE macro in jsffi.c
  • The js_os_funcs, js_io_funcs, etc. arrays define the available functions for each module
  • New functions are added with MIST_FUNC_DEF(module, function, args_count)

File I/O

  • io.slurp(path) - Reads a file as text
  • io.slurpbytes(path) - Reads a file as an ArrayBuffer
  • io.slurpwrite(path, data) - Writes data (string or ArrayBuffer) to a file
  • io.exists(path) - Checks if a file exists

Script Loading

  • The use(path) function in engine.js loads JavaScript modules
  • Script loading happens in prosperon.c and the engine.js script
  • jsffi.c contains the C hooks for the QuickJS JavaScript engine
  • Added functionality for bytecode compilation and loading:
    • os.compile_bytecode(source, filename) - Compiles JS to bytecode, returns ArrayBuffer
    • os.eval_bytecode(bytecode) - Evaluates bytecode from an ArrayBuffer
    • compile(scriptPath) - Compiles a JS file to a .jso bytecode file
    • Modified use() to check for .jso files before loading .js files

QuickJS Bytecode API

  • JS_Eval with JS_EVAL_FLAG_COMPILE_ONLY - Compiles without executing
  • JS_WriteObject with JS_WRITE_OBJ_BYTECODE - Serializes to bytecode
  • JS_ReadObject with JS_READ_OBJ_BYTECODE - Deserializes and loads bytecode
  • Bytecode files use .jso extension alongside .js files

Available JavaScript APIs

Core APIs

  • actor - Base prototype for all actor objects
  • $_ - Special global for actor messaging
  • prosperon - Global engine interface
  • console - Logging and debugging interface

Framework APIs

  • moth - Higher-level game framework that simplifies Prosperon usage
    • Handles window creation, game loop, and event dispatching
    • Provides simple configuration via config.js
    • Auto-initializes systems like rendering and input
    • Manages camera, resolution, and FPS automatically

Rendering

  • draw2d - 2D drawing primitives
  • render - Low-level rendering operations
  • graphics - Higher-level graphics utilities
  • camera - Camera controls and transformations
  • sprite - Sprite rendering and management

Physics and Math

  • math - Mathematical utilities
  • geometry - Geometric calculations and shapes
  • transform - Object transformations

Input and Events

  • input - Mouse, keyboard, and touch handling
  • event - Event management system

Networking

  • enet - Networking through ENet library
  • http - HTTP client capabilities

Audio

  • sound - Audio playback using SoLoud

Utility Modules

  • time - Time management and delays
  • io - File I/O operations
  • json - JSON parsing and serialization
  • util - General utilities
  • color - Color manipulation
  • miniz - Compression utilities
  • nota - Structured data format
  • wota - Serialization format
  • qr - QR code generation/reading
  • tween - Animation tweening
  • spline - Spline calculations
  • imgui - Immediate mode GUI

Game Development Patterns

Project Structure

  • Game config is typically in config.js
  • Main entry point is main.js
  • Resource loading through resources.js

Actor Pattern Usage

  • Create actors with actor.spawn(script, config)
  • Manage actor hierarchy with overlings and underlings
  • Schedule actor tasks with delay() method
  • Clean up with kill() and garbage()

Game Loop Registration

  • Register functions like update, draw, gui, etc.
  • Set function.layer property to control execution order
  • Use Register system to manage callbacks

Program vs Module Pattern

  • Programs are actor scripts that don't return values, they execute top-to-bottom
  • Modules are files that return single values (usually objects) that get frozen
  • Programs can spawn other programs as underlings
  • Programs have lifecycle hooks: awake, update, draw, garbage, etc.

Technical Capabilities

Graphics Pipeline

  • Supports multiple render backends (Direct3D, Metal, Vulkan via SDL3)
  • Custom shader system with cross-platform compilation
  • Sprite batching for efficient 2D rendering
  • Camera systems for both 2D and 3D

Asset Support

  • Images: PNG, JPG, QOI, etc.
  • Audio: Various formats through SoLoud
  • Models: Basic 3D model support
  • Custom formats: Aseprite animations, etc.

Developer Tools

  • Built-in documentation system with prosperon.DOC
  • Tracy profiler integration for performance monitoring
  • Imgui debugging tools
  • Console logging with various severity levels

Misty Networking Patterns

Prosperon implements the Misty actor networking model. Understanding these patterns is critical for building distributed applications.

Portal Reply Pattern

Portals must reply with an actor object, not application data:

// CORRECT: Portal replies with actor
$_.portal(e => {
  send(e, $_);  // Reply with server actor
}, 5678);

// WRONG: Portal sends application data  
$_.portal(e => {
  send(e, {type: 'game_start'});  // This breaks the pattern
}, 5678);

Two-Phase Connection Protocol

Proper Misty networking follows a two-phase pattern:

Phase 1: Actor Connection

  • Client contacts portal using $_.contact()
  • Portal replies with an actor object
  • This establishes the communication channel

Phase 2: Application Communication

  • Client sends application messages to the received actor
  • Normal bidirectional messaging begins
  • Application logic handles game/service initialization

Message Header Management

Messages contain __HEADER__ information that can cause issues:

// CORRECT: Extract clean actor reference
$_.receiver(e => {
  if (e.type === 'join_game') {
    var opponent = e.__HEADER__.replycc;  // Clean actor reference
    send(opponent, {type: 'game_start'});
  }
});

// WRONG: Using message object directly
$_.receiver(e => {
  opponent = e;  // Contains return headers that pollute future sends
});

Return ID Lifecycle

  • Each reply callback gets a unique return ID
  • Return IDs are consumed once and then deleted
  • Reusing message objects with return headers causes "Could not find return function" errors
  • Always create clean actor references for ongoing communication

Actor Object Transparency

Actor objects must be completely opaque black boxes that work identically regardless of transport:

// Actor objects work transparently for:
// - Same-process communication (fastest - uses mailbox)
// - Inter-process communication (uses mailbox) 
// - Network communication (uses ENet)

// The actor shouldn't know or care about the transport mechanism
send(opponent, {type: 'move', from: [0,0], to: [1,1]});

Key Implementation Details:

  • actor_send() in scripts/core/engine.js handles routing based on available actor data
  • Actor objects sent in message data automatically get address/port populated when received over network
  • Three communication pathways: os.mailbox_exist() check → mailbox send → network send
  • Actor objects must contain all necessary routing information for transparent messaging

Common Networking Bugs

  1. Portal sending application data: Portal should only establish actor connections
  2. Return ID collision: Reusing messages with return headers for multiple sends
  3. Mixed phases: Trying to do application logic during connection establishment
  4. Header pollution: Using received message objects as actor references
  5. Missing actor address info: Actor objects in message data need network address population (fixed in engine.js:746-766)

Example: Correct Chess Networking

// Server: Portal setup
$_.portal(e => {
  send(e, $_);  // Just reply with actor
}, 5678);

// Client: Two-phase connection
$_.contact((actor, reason) => {
  if (actor) {
    opponent = actor;
    send(opponent, {type: 'join_game'});  // Phase 2: app messaging
  }
}, {address: "localhost", port: 5678});

// Server: Handle application messages
$_.receiver(e => {
  if (e.type === 'join_game') {
    opponent = e.__HEADER__.replycc;
    send(opponent, {type: 'game_start', your_color: 'black'});
  }
});

Memory Management

  • When working with a conversational AI system like Claude, it's important to maintain a clean and focused memory
  • Regularly review and update memories to ensure they remain relevant and helpful
  • Delete or modify memories that are no longer accurate or useful
  • Prioritize information that can genuinely assist in future interactions