# CLAUDE.md ## Build Commands Run 'make' to make cell. ### 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. ## Architecture Overview Prosperon is an actor-based game engine. ### 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 ```bash # 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 ```bash # 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 ### 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: ```javascript // 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: ```javascript // 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: ```javascript // 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 **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 ```javascript // 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