Files
cell/CLAUDE.md
2025-07-05 10:24:09 -05:00

14 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 - Make and install debug version. Usually all that's needed.
  • 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

After install with 'make', just run 'cell' and point it at the actor you want to launch. "cell tests/toml" runs the actor "tests/toml.js"

Scripting language

This is called "cell", a variant of JavaScript with important differences. See docs/cell.md for detailed language documentation.

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

Cell Language 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
  • Use def for constants (not const)
  • Use var for variables (block-scoped like let)
  • Check for null with == null (no undefined in Cell)
  • Use == for equality (always strict, no ===)
  • See docs/cell.md for complete language reference

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/cell.md documents the Cell language (JavaScript variant used in Prosperon)

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 log.console(), log.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
    • Must be imported with use('time')
    • No time.now() function - use:
      • time.number() - Number representation of current time
      • time.record() - Struct representation of current time
      • time.text() - Text representation of current time
  • 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)
  • Start actors with $_.start(callback, script) - the system automatically sends a greeting, callback receives {type: 'greet', actor: actor_ref}
    • No need to manually send greetings - $_.start handles this automatically
  • Manage actor hierarchy with overlings and underlings
  • Schedule actor tasks with $_.delay() method
  • Clean up with kill() and garbage()

Actor Messaging with Callbacks

When sending a message with a callback, respond by sending to the message itself:

// Sender side:
send(actor, {type: 'status'}, response => {
  log.console(response); // Handle the response
});

// Receiver side:
$_.receiver(msg => {
  if (msg.type === 'status') {
    send(msg, {status: 'ok'}); // Send response to the message itself
  }
});

Critical Rules for Message Callbacks:

  • A message can only be used ONCE as a send target - after sending a response to a message, it cannot be used again
  • If you need to send multiple updates (like progress), only the download request message should be used for the final response
  • Status requests should each get their own individual response
  • Actor objects and message headers are completely opaque - never try to access internal properties
  • Never access msg.__HEADER__ or similar - the actor system handles routing internally
  • Use $_.delay() to schedule work and avoid blocking the message receiver

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 cell.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 Handling Best Practices

Messages should be treated as opaque objects with your application data:

// CORRECT: Store actor references separately
var players = {};
$_.receiver(msg => {
  if (msg.type === 'join_game' && msg.player_id) {
    // Store the message for later response
    players[msg.player_id] = msg;
    // Later, respond to the stored message
    send(players[msg.player_id], {type: 'game_start'});
  }
});

// WRONG: Trying to access internal message properties
$_.receiver(msg => {
  var sender = msg.__HEADER__.replycc;  // Never do this!
});

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