375 lines
13 KiB
Markdown
375 lines
13 KiB
Markdown
# 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 |