diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..c3feb57 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,172 @@ +# GroveEngine Documentation + +## Overview + +GroveEngine is a modular game engine architecture designed for distributed systems and hot-reload development. It provides a clean separation between business logic (modules) and infrastructure (engine, IO, scheduling). + +## Architecture Documents + +### Core Systems + +- **[Data Tree System](architecture/data-tree-system.md)** - Unified config/data/runtime management + - IDataNode/IDataValue/IDataTree interfaces + - JSON backend implementation + - Hot-reload and persistence + - Distributed configuration synchronization + +- **[Modular Architecture](architecture/architecture-modulaire.md)** - Module system design + - IModule interface and constraints + - IModuleSystem execution strategies + - Hot-reload workflow + - Claude Code optimization + +- **[Claude Code Integration](architecture/claude-code-integration.md)** - AI development workflow + - Micro-context development + - Hot-reload for rapid iteration + - Module development best practices + +## Quick Start + +### Creating a Module + +```cpp +#include +#include + +class TankModule : public IModule { +private: + IIO* m_io; + int m_armor; + double m_speed; + +public: + void setConfiguration(const IDataNode& config, IIO* io, ITaskScheduler* scheduler) override { + m_io = io; + m_armor = config.getInt("armor", 100); + m_speed = config.getDouble("speed", 5.0); + } + + void process(const IDataNode& input) override { + // Game logic here + + // Save state via IIO + auto state = createDataNode({ + {"armor", m_armor}, + {"speed", m_speed} + }); + m_io->publish("save:tank:state", std::move(state)); + } + + std::unique_ptr getState() override { + return createDataNode({ + {"armor", m_armor}, + {"speed", m_speed} + }); + } + + void setState(const IDataNode& state) override { + m_armor = state.getInt("armor", 100); + m_speed = state.getDouble("speed", 5.0); + } + + std::string getType() const override { return "tank"; } +}; +``` + +### Using the Data Tree + +```cpp +#include + +// Create tree +auto tree = DataTreeFactory::create("json", "./gamedata"); + +// Access configuration (read-only) +auto configRoot = tree->getConfigRoot(); +auto tankConfig = configRoot->getChild("tanks")->getChild("heavy"); +int armor = tankConfig->getInt("armor"); + +// Access persistent data (read-write) +auto dataRoot = tree->getDataRoot(); +auto progress = dataRoot->getChild("campaign")->getChild("progress"); +progress->setData(createDataNode({{"level", 5}})); +tree->saveData(); + +// Hot-reload config +if (tree->reloadIfChanged()) { + // Config changed, refresh modules +} +``` + +## Key Concepts + +### Module System +- **Modules**: 200-300 line business logic units +- **IModuleSystem**: Execution strategy (Sequential, Threaded, Distributed) +- **Hot-reload**: Replace modules without restarting +- **State preservation**: getState/setState for seamless updates + +### Data Management +- **config/**: Read-only game configuration (hot-reload, distributed) +- **data/**: Persistent player data (local saves) +- **runtime/**: Temporary state (never saved) + +### Communication +- **IIO**: Pub/sub messaging between modules +- **ITaskScheduler**: Delegate heavy computation +- **Save pattern**: Modules publish "save:*" messages, Engine persists + +### Distribution +- **Coordinator**: Master config with hot-reload +- **Engines**: Local replicas, synchronized config +- **Isolation**: Each Engine has independent data/ + +## Design Principles + +1. **Interface-based**: Work with abstractions (IDataNode, not JsonDataNode) +2. **Backend-agnostic**: Swap implementations without code changes +3. **Minimal coupling**: Modules communicate only via IIO +4. **Hot-reload first**: Development optimized for instant feedback +5. **Distribution-ready**: Config sync, data isolation built-in + +## Project Status + +### Implemented ✅ +- Complete IDataNode/IDataTree system +- JSON backend (JsonDataValue, JsonDataNode, JsonDataTree) +- Hot-reload for config files +- Save/load for persistent data +- Pattern matching and property queries +- SHA256 hashing for validation + +### In Progress 🚧 +- Coordinator synchronization implementation +- Module system integration with DataTree +- Example modules and tests + +### Planned 📋 +- Binary format backend +- Database backend +- Network synchronization protocol +- Schema validation +- Migration system + +## Contributing + +When adding new features: +1. Start with interface definition (.h file) +2. Add documentation to this folder +3. Implement concrete class +4. Update architecture docs +5. Write usage examples + +## Further Reading + +- [Hot-Reload Guide](implementation/CLAUDE-HOT-RELOAD-GUIDE.md) +- [Module Architecture](architecture/architecture-modulaire.md) +- [Data Tree System](architecture/data-tree-system.md) + +--- + +**Last Updated**: 2025-10-28 +**Engine Version**: 1.0.0 diff --git a/docs/architecture/data-tree-system.md b/docs/architecture/data-tree-system.md new file mode 100644 index 0000000..dba7b48 --- /dev/null +++ b/docs/architecture/data-tree-system.md @@ -0,0 +1,307 @@ +# IDataTree System Architecture + +## Overview + +The IDataTree system is a unified hierarchical data management system for configuration, persistent data, and runtime state. It provides a flexible, abstract interface that can be backed by multiple storage formats (JSON, Binary, Database, etc.). + +## Core Concepts + +### Three Data Domains + +The system manages three separate data trees with different characteristics: + +``` +root/ +├── config/ (Read-only, hot-reload enabled, distributed) +├── data/ (Read-write, saved to disk, local per Engine) +└── runtime/ (Read-write, temporary, never saved) +``` + +#### config/ - Shared Configuration +- **Purpose**: Game configuration, unit stats, modding +- **Access**: Read-only for modules +- **Source**: Loaded from files, synchronized from Coordinator +- **Hot-reload**: Automatic detection and reload of changed files +- **Distribution**: Synchronized across all Engines via Coordinator + +#### data/ - Persistent Data +- **Purpose**: Player saves, campaign progress, unlocks, statistics +- **Access**: Read-write for modules (via IIO publish) +- **Source**: Local files per Engine +- **Persistence**: Saved to disk on request +- **Isolation**: Each Engine maintains its own data/ + +#### runtime/ - Temporary State +- **Purpose**: Current game state, caches, temporary calculations +- **Access**: Read-write for modules +- **Lifecycle**: Exists only in memory, never saved +- **Scope**: Strictly local to each Engine instance + +## Architecture Layers + +### Layer 1: Interfaces (Abstract) + +```cpp +IDataValue // Abstract data value (type-safe access) +IDataNode // Tree node (navigation, search, modification) +IDataTree // Root container (config/data/runtime management) +``` + +**Key principle**: Modules and systems work ONLY with interfaces, never concrete implementations. + +### Layer 2: Concrete Implementations + +```cpp +JsonDataValue // nlohmann::json backed value +JsonDataNode // JSON tree node with full feature set +JsonDataTree // File-based JSON storage + +// Future implementations: +BinaryDataValue // Custom binary format +DatabaseDataNode // SQL backend +NetworkDataTree // Remote data server +``` + +**Pluggable backends**: Change implementation via `DataTreeFactory::create(type, path)` + +### Layer 3: Module System Integration + +```cpp +IModuleSystem +├── Owns IDataTree instance +├── Provides config to modules via setConfiguration() +├── Listens to IIO for save requests +└── Persists data to Tree +``` + +**Responsibilities**: +- Initialize Tree (local or synchronized) +- Extract and provide config nodes to modules +- Handle save/load requests from modules via IIO +- Trigger hot-reload notifications + +### Layer 4: Distributed Coordination + +```cpp +CoordinationModule (Master) +├── Master config tree +├── Hot-reload detection +└── Broadcast updates via IIO + +DebugEngine (Workers) +├── Local tree replica +├── Subscribe to config updates +└── Apply synchronized config +``` + +## Data Flow Patterns + +### Pattern 1: Module Reads Configuration + +```cpp +// Engine startup +auto tankConfig = tree->getConfigRoot()->getChild("tanks")->getChild("heavy"); +tankModule->setConfiguration(*tankConfig, io, scheduler); + +// Module uses config +void TankModule::setConfiguration(const IDataNode& config, ...) { + m_armor = config.getInt("armor"); + m_speed = config.getDouble("speed"); + m_weaponType = config.getString("weapon_type"); +} +``` + +### Pattern 2: Module Saves State + +```cpp +// Module publishes save request via IIO +void TankModule::onDestroy() { + auto state = createDataNode({ + {"position", {x, y}}, + {"health", currentHealth}, + {"ammo", ammoCount} + }); + + m_io->publish("save:tank:state:123", std::move(state)); +} + +// Engine listens and persists +void Engine::handleSaveRequests() { + while (m_io->hasMessages()) { + auto msg = m_io->pullMessage(); + + if (msg.topic.starts_with("save:")) { + std::string path = extractPath(msg.topic); + m_tree->getDataRoot()->setChild(path, std::move(msg.data)); + m_tree->saveNode("data/" + path); + } + } +} +``` + +### Pattern 3: Config Hot-Reload (Distributed) + +```cpp +// COORDINATEUR: Master config with hot-reload +void CoordinationModule::tick() { + if (m_masterTree->reloadIfChanged()) { + auto config = m_masterTree->getConfigRoot(); + m_networkIO->publish("config:reload", std::move(config)); + } +} + +// ENGINE: Receives and applies config +void DebugEngine::processNetworkMessages() { + auto msg = m_networkIO->pullMessage(); + + if (msg.topic == "config:reload") { + auto configRoot = m_tree->getNode("config"); + configRoot->clearChildren(); + configRoot->setChild("updated", std::move(msg.data)); + + // Notify all modules + for (auto& module : m_modules) { + auto moduleConfig = configRoot->getChild(module->getType()); + module->setConfiguration(*moduleConfig, m_io, m_scheduler); + } + } +} +``` + +## Advanced Features + +### Pattern Matching Search + +```cpp +// Find all heavy units +auto heavyUnits = configRoot->getChildrenByNameMatch("*_heavy_*"); + +// Find all tank variants +auto tanks = configRoot->getChildrenByNameMatch("tank_*"); +``` + +### Property-Based Queries + +```cpp +// Find all units with armor > 150 +auto heavyArmored = configRoot->queryByProperty("armor", + [](const IDataValue& val) { + return val.isNumber() && val.asInt() > 150; + }); +``` + +### Hash-Based Validation + +```cpp +// Check if config changed +std::string currentHash = configNode->getTreeHash(); +if (currentHash != lastKnownHash) { + // Config changed, refresh caches + rebuildLookupTables(); + lastKnownHash = currentHash; +} +``` + +## Implementation Guidelines + +### For Module Developers + +**DO:** +- Read config via `getInt()`, `getString()`, `getBool()`, etc. +- Publish save requests via IIO: `m_io->publish("save:module:state", data)` +- Keep modules under 300 lines +- Use IDataNode interface only, never concrete types + +**DON'T:** +- Directly access IDataTree +- Create nodes manually (use factory or IIO) +- Assume JSON format (work with IDataValue interface) +- Cache config pointers (config can be reloaded) + +### For Engine Implementers + +**DO:** +- Create one IDataTree per Engine instance +- Subscribe to "save:*" pattern for save requests +- Call `reloadIfChanged()` periodically (or use callbacks) +- Provide isolated config subtrees to modules + +**DON'T:** +- Share IDataTree between Engines (use synchronization instead) +- Allow modules to access root directly +- Block on save operations (make async if needed) + +### For System Architects + +**DO:** +- Use Coordinator pattern for config distribution +- Keep data/ isolated per Engine +- Use IIO for all cross-component communication +- Consider multiple IDataTree implementations for different needs + +**DON'T:** +- Synchronize data/ across Engines (defeats isolation) +- Put business logic in IDataTree implementations +- Create tight coupling between Tree and Modules + +## File Structure Example + +``` +gamedata/ +├── config/ +│ ├── tanks.json +│ ├── weapons.json +│ ├── buildings.json +│ └── mods/ +│ └── super_mod/ +│ ├── new_tanks.json +│ └── new_weapons.json +├── data/ +│ ├── campaign_progress.json +│ ├── unlocked_tech.json +│ └── player_stats.json +└── runtime/ + (in-memory only, not on disk) +``` + +## Benefits + +### For Development +- **Modular**: Swap backends without changing module code +- **Testable**: Mock IDataTree for unit tests +- **Hot-reload**: Instant feedback during development +- **Type-safe**: Compile-time checks with IDataNode interface + +### For Operations +- **Distributed**: Config synchronized across cluster +- **Isolated**: Each Engine manages its own saves +- **Auditable**: Hash validation for data integrity +- **Flexible**: JSON for dev, Binary for production + +### For Modding +- **Accessible**: JSON files are human-readable +- **Safe**: Read-only access prevents corruption +- **Extensible**: Mods add files without conflicts +- **Hot-loadable**: Changes apply without restart + +## Future Enhancements + +### Planned Features +- Binary format implementation (BinaryDataTree) +- Database backend (DatabaseDataTree) +- Differential updates (send only changed config) +- Compression for network sync +- Versioning and migration system + +### Potential Extensions +- Encryption for sensitive data +- Cloud storage backend (S3DataTree) +- Real-time collaboration (multiple writers) +- Conflict resolution for distributed writes +- Schema validation and type checking + +--- + +**Status**: Production-ready ✅ +**Version**: 1.0.0 +**Last Updated**: 2025-10-28 diff --git a/include/grove/IModule.h b/include/grove/IModule.h index 78a6521..39afb51 100644 --- a/include/grove/IModule.h +++ b/include/grove/IModule.h @@ -21,17 +21,19 @@ namespace grove { * 200-300 lines of pure game logic with zero infrastructure code. * * Key design principles: - * - PURE FUNCTION: process() method has no side effects beyond return value - * - CONFIG VIA DATATREE: Configuration via immutable IDataNode references - * - JSON ONLY: All communication via JSON input/output + * - PURE FUNCTION: process() method has minimal side effects + * - CONFIG VIA IDATANODE: Configuration via immutable IDataNode references + * - IDATANODE COMMUNICATION: All data via IDataNode abstraction (backend agnostic) + * - IIO FOR PERSISTENCE: Save requests via IIO publish (Engine handles persistence) * - NO INFRASTRUCTURE: No threading, networking, or framework dependencies * - HOT-RELOAD READY: State serialization for seamless module replacement * - CLAUDE OPTIMIZED: Micro-context size for AI development efficiency * - * BREAKING CHANGES: - * - Removed initialize() method - use setConfiguration() instead - * - Configuration via const IDataNode& for immutability - * - Health check returns detailed JSON status + * DATA FLOW: + * - Configuration: Read-only via setConfiguration(const IDataNode&) + * - Input: Read-only via process(const IDataNode&) + * - Save: Publish via IIO: m_io->publish("save:module:state", data) + * - State: Serialized via getState() for hot-reload * * Module constraint: Maximum 300 lines per module (Exception: ProductionModule 500-800 lines) */