# GroveEngine - Framework C++ Modulaire **Status**: WIP **Type**: Game Engine Framework **Stack**: C++17, bgfx, SDL2, nlohmann_json, spdlog **Location**: `C:\Users\alexi\Documents\projects\groveengine` **Core Status**: Validé, Rendering en développement **Moved to WIP**: 30 novembre 2025 **Dernière activité**: 29 novembre 2025 (46 commits sur 3 semaines) --- ## Vue d'ensemble **GroveEngine** est un moteur de jeu C++17 modulaire conçu pour : - **Hot-reload ultra-rapide** : 0.4ms validé (5000x plus rapide qu'un cycle edit-build-test classique) - **AI-optimized** : Micro-modules (200-300 lignes) parfaits pour développement assisté Claude - **Scalabilité transparente** : Du debug local au cluster distribué sans modifier le code modules - **Data-driven** : Config/YAML/JSON pour tout le contenu (LLM peut créer sans toucher C++) **Nom** : "Grove" = bosquet où les modules poussent comme des arbres indépendants. --- ## Architecture Core ### Philosophie : 13 Interfaces L'architecture repose sur **13 interfaces abstraites** permettant de swap les implémentations sans modifier le code métier. ``` ┌─────────────────────────────────────────────────────────────┐ │ Application (ex: aissia-core.exe, warfactory.exe) │ │ └─ main() → EngineFactory::create() │ └──────────────────────────┬──────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ IEngine (orchestration) │ │ ├─ IModuleSystem (stratégie d'exécution) │ │ ├─ IIO (communication pub/sub) │ │ ├─ IDataTree (configuration hiérarchique) │ │ └─ Game loop │ └──────────────────────────┬──────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Modules (DLL/SO hot-reloadable, 200-300 lignes chacun) │ │ ├─ module_a.dll → IModule::process() │ │ ├─ module_b.dll → IModule::process() │ │ └─ module_c.dll → IModule::process() │ │ Communication via IIO (pub/sub topics) │ └─────────────────────────────────────────────────────────────┘ ``` ### Les 13 Interfaces | Interface | Rôle | Implémentations | |-----------|------|-----------------| | **IEngine** | Orchestration système, main loop | DebugEngine ✅, HighPerfEngine (planned), DataOrientedEngine (planned) | | **IModule** | Logique métier (micro-modules) | Custom modules (.dll/.so) | | **IModuleSystem** | Stratégie d'exécution modules | SequentialModuleSystem ✅, ThreadedModuleSystem (planned), ClusterModuleSystem (planned) | | **IIO** | Communication pub/sub | IntraIO ✅ (même process), LocalIO (planned), NetworkIO (planned) | | **IDataTree** | Racine configuration | JsonDataTree ✅ | | **IDataNode** | Noeud configuration | JsonDataNode ✅ | | **ITaskScheduler** | Délégation tâches async | (planned) | | **IUI** | Abstraction UI | ImGuiUI ✅ | | **ISerializable** | Sérialisation état | JSON-based | | **ICoordinationModule** | Coordination inter-modules | (planned) | ### Scalabilité Transparente ``` MVP Debug: DebugEngine + SequentialModuleSystem + IntraIO (Single-thread, même process, logging verbose) Production: HighPerfEngine + ThreadedModuleSystem + LocalIO (Multi-thread, named pipes/sockets) Cloud/MMO: DataOrientedEngine + ClusterModuleSystem + NetworkIO (Distribué, TCP/WebSocket) ``` **Aucun changement de code dans les modules** - Juste swap des implémentations via config. --- ## Hot-Reload System ### Performance Validée | Métrique | Valeur | |----------|--------| | **Temps moyen** | 0.4ms | | **Meilleur cas** | 0.055ms | | **State preservation** | 100% (via ISerializable) | | **Commit validation** | fc28009 (24 Sept 2025) | ### Mécanisme ```cpp // 1. Détection changement fichier DLL if (filesystem::last_write_time("module.dll") > last_load_time) { // 2. Sauvegarder état via ISerializable nlohmann::json state; old_module->serialize(state); // 3. Shutdown module actuel old_module->shutdown(); // 4. Unload DLL #ifdef _WIN32 FreeLibrary(dll_handle); #else dlclose(dll_handle); #endif // 5. Reload nouvelle DLL #ifdef _WIN32 dll_handle = LoadLibrary("module.dll"); #else dll_handle = dlopen("./module.so", RTLD_NOW); #endif // 6. Initialize nouveau module avec état restauré new_module->initialize(config, io); new_module->deserialize(state); // Total: 0.4ms } ``` ### Interface IModule ```cpp class IModule { public: virtual ~IModule() = default; // Lifecycle virtual void initialize(const IDataNode& config, IIO* io) = 0; virtual void process(float dt) = 0; virtual void shutdown() = 0; // Hot-reload state preservation virtual nlohmann::json getState() const = 0; virtual void setState(const nlohmann::json& state) = 0; // Metadata virtual std::string getName() const = 0; virtual int getVersion() const = 0; virtual std::vector getDependencies() const = 0; }; ``` ### Contraintes Micro-Modules - **200-300 lignes max** par module (AI-friendly) - **Zero parent dependencies** : Pas de `#include "../"` - **JSON-only inter-module** : Communication via IIO topics - **Build autonome** : `cmake .` depuis le dossier module --- ## Communication Inter-Modules (IIO) ### TopicTree : Pub/Sub Ultra-Rapide **Bibliothèque interne** : StillHammer::topictree **Performance** : O(k) où k = profondeur topic (pas O(n) patterns) ```cpp // Publisher (Module A) io->publish("game/player/position", position_json); io->publish("game/enemy/42/health", health_json); // Subscriber (Module B) - Wildcards supportés io->subscribe("game/player/*", [](const Message& msg) { // Reçoit tous les messages player }); io->subscribe("game/enemy/+/health", [](const Message& msg) { // + = single level wildcard // Reçoit health de tous les enemies }); ``` ### Performance TopicTree | Patterns | Temps matching | |----------|----------------| | 1,000 | < 1ms | | 10,000 | < 5ms | ### Design Pull-Based - **Synchrone** : Messages collectés, traités en batch - **Low-frequency** : Optimisé pour 60 FPS game loop, pas high-frequency trading - **Découplage total** : Modules ignorent l'existence des autres --- ## Data System (IDataTree / IDataNode) ### Configuration Hiérarchique Type-Safe ```cpp // Lecture config void MyModule::initialize(const IDataNode& config, IIO* io) { int width = config.get("window.width", 1280); std::string title = config.get("window.title", "GroveEngine"); bool vsync = config.get("rendering.vsync", true); // Navigation arbre auto& enemies = config.getChild("enemies"); for (const auto& enemy : enemies.getChildren()) { std::string type = enemy.get("type", "basic"); int hp = enemy.get("hp", 100); } } ``` ### Backed by JSON Files ```json { "window": { "width": 1280, "height": 720, "title": "My Game" }, "rendering": { "vsync": true, "clear_color": "#303030" }, "enemies": [ {"type": "basic", "hp": 100}, {"type": "heavy", "hp": 500} ] } ``` --- ## Structure Projet ``` GroveEngine/ ├── include/grove/ # 32 headers (interfaces + impls) │ ├── IEngine.h │ ├── IModule.h │ ├── IModuleSystem.h │ ├── IIO.h │ ├── IDataTree.h │ ├── IDataNode.h │ ├── ITaskScheduler.h │ ├── IUI.h │ ├── ISerializable.h │ └── ... │ ├── src/ # Implémentations concrètes │ ├── DebugEngine.cpp │ ├── SequentialModuleSystem.cpp │ ├── IntraIO.cpp │ ├── IntraIOManager.cpp │ ├── JsonDataNode.cpp │ ├── JsonDataTree.cpp │ ├── ModuleFactory.cpp │ ├── EngineFactory.cpp │ └── ... │ ├── modules/ # Modules hot-reloadables │ └── BgfxRenderer/ # Module rendering 2D (WIP) │ ├── BgfxRendererModule.cpp │ ├── RHI/ # Render Hardware Interface │ ├── Shaders/ # Pre-compiled (GL/Vulkan/DX11/Metal) │ ├── Passes/ # Clear, Sprite, Debug │ ├── RenderGraph/ # Dependency ordering │ └── Scene/ # Message collection from IIO │ ├── external/StillHammer/ # Libs internes │ ├── topictree/ # O(k) topic matching │ └── logger/ # spdlog wrapper │ ├── tests/ # Test suite (Catch2) │ ├── benchmarks/ │ ├── hotreload/ │ └── helpers/ │ ├── docs/ # Documentation │ ├── architecture/ │ ├── implementation/ │ └── plans/ │ └── CMakeLists.txt # Build system ``` --- ## Module Rendering (BgfxRenderer) ### Stack Rendering - **bgfx** : Abstraction multi-backend (Vulkan/DX12/Metal/OpenGL) - **SDL2** : Window + input management - **stb_image** : Texture loading ### Architecture RenderModule ```cpp class BgfxRendererModule : public grove::IModule { public: // IModule interface void initialize(const IDataNode& config, IIO* io) override; void process(float dt) override; void shutdown() override; // Rendering API (via IIO messages) // Autres modules publient sur "render/sprite", "render/rect", etc. // BgfxRenderer subscribe et render tout en batch private: SDL_Window* window; bgfx::ViewId main_view; // Render passes ClearPass clear_pass; SpritePass sprite_pass; DebugPass debug_pass; // Scene collector (from IIO) SceneCollector scene; }; ``` ### Rendering Pipeline ```cpp void BgfxRendererModule::process(float dt) { // 1. Collecter messages rendering depuis IIO scene.CollectFromIO(io); // 2. Exécuter passes clear_pass.Execute(main_view); sprite_pass.Execute(main_view, scene.sprites); debug_pass.Execute(main_view, scene.debug_shapes); // 3. Submit frame bgfx::frame(); // 4. Clear scene pour prochain frame scene.Clear(); } ``` ### Shaders Pre-Compilés Shaders compilés pour tous backends via `shaderc` : ``` Shaders/ ├── vs_sprite.bin.gl # OpenGL ├── vs_sprite.bin.vk # Vulkan ├── vs_sprite.bin.dx11 # DirectX 11 ├── vs_sprite.bin.mtl # Metal ├── fs_sprite.bin.gl ├── fs_sprite.bin.vk ├── fs_sprite.bin.dx11 └── fs_sprite.bin.mtl ``` ### Camera 2D ```cpp class Camera2D { public: Vec2 position{0, 0}; float zoom = 1.0f; Mat4 GetViewMatrix() const { return Mat4::Translation(-position.x, -position.y, 0); } Mat4 GetProjectionMatrix(int width, int height) const { float halfW = (width / zoom) * 0.5f; float halfH = (height / zoom) * 0.5f; return Mat4::Ortho(-halfW, halfW, -halfH, halfH, -1, 1); } }; ``` --- ## Architecture AI-Friendly (Data-Driven) ### Principe **LLM peut créer/modifier game content sans toucher au code C++.** ### Entity System (YAML) ```yaml # entities/wagons/wagon_armored.yaml entity: wagon_armored components: - type: visual sprite: wagon_armored.png size: [20, 6] - type: balance weight: 7.5 center_of_mass: [10, 3] - type: health hp_max: 200 armor: 50 - type: slots grid: [20, 6] specialization: military ``` ### UI Layouts (JSON) ```json { "screen": "train_builder", "elements": [ { "type": "panel", "id": "wagon_view", "rect": [0, 0, 800, 600], "background": "#2a2a2a" }, { "type": "gauge", "id": "balance_lateral", "rect": [820, 20, 160, 40], "label": "Balance G/D", "value_binding": "wagon.balance.lateral" } ] } ``` ### Missions/Events (YAML) ```yaml mission: id: scavenge_kyiv name: "Colonne russe détruite" difficulty: easy rewards: metal: [50, 100] electronics: [20, 40] events: - trigger: mission_start choices: - id: approach_cautious text: "Approche prudente" risk_modifier: -0.5 - id: rush text: "Rush rapide" risk_modifier: +0.8 ``` ### Asset Manifests (TOML) ```toml [wagon_armored] file = "textures/wagon_armored.png" size = [400, 120] pivot = [200, 60] tags = ["wagon", "armored", "heavy"] weight_modifier = 1.5 ``` --- ## Dépendances ### Core (FetchContent CMake) | Library | Version | Usage | |---------|---------|-------| | **nlohmann_json** | 3.11.3 | JSON processing | | **spdlog** | 1.12.0 | Logging | | **Catch2** | 3.5+ | Testing | ### Rendering (Optionnel) | Library | Usage | |---------|-------| | **bgfx** | Multi-backend rendering | | **bx** | Base library (bgfx dep) | | **bimg** | Image loading (bgfx dep) | | **SDL2** | Windowing + input | | **stb_image** | Texture loading | ### Internal (StillHammer) | Library | Usage | |---------|-------| | **topictree** | O(k) pub/sub topic matching | | **logger** | Domain-organized spdlog wrapper | ### Optionnel Futur | Library | Usage | |---------|-------| | **yaml-cpp** | YAML parsing (entities) | | **TOML++** | Config files | | **ImGui** | Debug UI | | **Lua** | Scripting (si nécessaire) | | **stb_truetype** / **FreeType** | Font rendering | --- ## Build System ### CMake Options ```cmake # Core GROVE_BUILD_IMPLEMENTATIONS # Compile implémentations core GROVE_BUILD_TESTS # Compile test suite # Modules GROVE_BUILD_MODULES # Compile hot-reloadable modules GROVE_BUILD_BGFX_RENDERER # Compile rendering module # Debug GROVE_ENABLE_TSAN # ThreadSanitizer GROVE_ENABLE_HELGRIND # Helgrind deadlock detection ``` ### Build Commands ```bash # Configuration cmake -B build -DGROVE_BUILD_IMPLEMENTATIONS=ON -DGROVE_BUILD_TESTS=ON # Build cmake --build build # Tests cd build && ctest # Module seul (build autonome) cd modules/BgfxRenderer && cmake . && make ``` ### Platforms Supportées | Platform | Status | Toolchain | |----------|--------|-----------| | **Windows** | ✅ Validé | MSVC, MinGW | | **Linux** | ✅ Validé | GCC, Clang | | **WSL2** | ✅ Validé | GCC | | **macOS** | ⏳ Non testé | Clang | | **WebAssembly** | 🔮 Futur | Emscripten | --- ## État Développement ### Validé (Production-Ready) - ✅ **Hot-reload 0.4ms** avec state preservation - ✅ **DebugEngine** avec logging complet - ✅ **SequentialModuleSystem** (single-thread) - ✅ **IntraIO** pub/sub avec TopicTree - ✅ **JsonDataNode/JsonDataTree** configuration - ✅ **Dynamic module loading** (dlopen/LoadLibrary) - ✅ **Test suite** (Catch2) ### En Développement - 🔄 **BgfxRenderer module** - ✅ Skeleton + RHI layer - ✅ Shaders pre-compiled - 🔄 SpritePass implementation - ⏳ TextPass, TilemapPass ### Planned - ⏳ **ThreadedModuleSystem** (each module own thread) - ⏳ **MultithreadedModuleSystem** (thread pool) - ⏳ **LocalIO** (named pipes/sockets) - ⏳ **NetworkIO** (TCP/WebSocket) - ⏳ **Entity Component System** (YAML-driven) - ⏳ **UI System** (JSON layouts) --- ## Projets Utilisateurs | Projet | Type | Status | |--------|------|--------| | **AISSIA** | AI Assistant | En développement | | **Pokrovsk: Iron Line** | Survival/Management | Concept | | **WarFactory** | Source originale | Extraction faite | --- ## Problèmes Techniques Connus ### 1. API Mismatch IDataTree **Situation** : Certaines implémentations utilisent l'ancienne API `nlohmann::json` au lieu de `IDataNode`. ```cpp // Ancien (certains fichiers) void initialize(const nlohmann::json& config, IIO* io); // Nouveau (interface actuelle) void initialize(const IDataNode& config, IIO* io); ``` **Status** : Migration en cours, wrapper temporaire disponible. ### 2. Cross-Platform Hot-Reload | Platform | API | Status | |----------|-----|--------| | Windows | LoadLibrary/FreeLibrary | ✅ Validé | | Linux | dlopen/dlclose | ✅ Validé | | macOS | dlopen/dlclose | ⏳ Non testé | | WASM | N/A | ⚠️ Architecture différente requise | ### 3. State Preservation Limits - **Pointeurs** : Invalides après reload → Utiliser ID-based references - **Handles externes** : (GPU textures, file handles) → Recréer après reload --- ## Commits Git Importants | Commit | Date | Description | |--------|------|-------------| | **fc28009** | 24 Sept 2025 | Hot-reload 0.4ms validé, DebugEngine fonctionnel | | **fb49fb2** | Sept 2025 | IntraIO implementation | | **f6c3b34** | 27 Sept 2025 | IDataTree ajouté (breaking change) | | **27 Oct 2025** | - | Extraction GroveEngine depuis WarFactory | --- ## Ressources ### Documentation Interne - `GroveEngine/docs/architecture/` : Guides architecture - `GroveEngine/docs/implementation/` : Guides implémentation - `GroveEngine/README.md` : Quick start ### Références Externes - **bgfx** : https://github.com/bkaradzic/bgfx - **bgfx docs** : https://bkaradzic.github.io/bgfx/ - **SDL2** : https://wiki.libsdl.org/ - **nlohmann/json** : https://github.com/nlohmann/json --- ## Liens Projets Associés - **AISSIA** : `Projects/WIP/AISSIA.md` - **Pokrovsk** : `Projects/CONCEPT/pokrovsk_iron_line_v2.md` --- *Créé : 27 octobre 2025* *Consolidé : 27 novembre 2025* *Stack : C++17, CMake, bgfx, nlohmann_json, spdlog* *Performance : Hot-reload 0.4ms validé*