diff --git a/examples/v0.0.1-alpha/ecs_rendering/Namespaces.h b/examples/v0.0.1-alpha/ecs_rendering/Namespaces.h index e54385aa1f..eb297ba321 100644 --- a/examples/v0.0.1-alpha/ecs_rendering/Namespaces.h +++ b/examples/v0.0.1-alpha/ecs_rendering/Namespaces.h @@ -92,8 +92,8 @@ using namespace helios::platform::opengl; using namespace helios::gameplay::scoring; using namespace helios::gameplay::scoring::systems; using namespace helios::gameplay::scoring::types; -using namespace helios::gameplay::timing; -using namespace helios::gameplay::timing::systems; +using namespace helios::runtime::timing; +using namespace helios::runtime::timing::systems; using namespace helios::gameplay::combat::components; using namespace helios::gameplay::combat::systems; diff --git a/examples/v0.0.1-alpha/ecs_rendering/main.cpp b/examples/v0.0.1-alpha/ecs_rendering/main.cpp index 3b58b3a4d0..b95b448afb 100644 --- a/examples/v0.0.1-alpha/ecs_rendering/main.cpp +++ b/examples/v0.0.1-alpha/ecs_rendering/main.cpp @@ -4,7 +4,8 @@ import helios.ext; import helios.examples.ecs_rendering.GameLoop; #include "Namespaces.h" -#include + + int main() { @@ -30,7 +31,7 @@ int main() { // ========================================================== // inputmanager - auto deadzoneStrategy = std::make_unique(); + auto deadzoneStrategy = RadialDeadzoneStrategy(); /*const auto inputManager = std::make_unique( std::make_unique(std::move(deadzoneStrategy)) );*/ @@ -51,7 +52,9 @@ int main() { ); // register additional managers - gameWorld.registerManager>(); + gameWorld.registerManager>( + gameWorld.platformWorld(), gameWorld.resourceRegistry().commandBufferRegistry() + ); gameWorld.registerManager>(gameWorld.renderResourceWorld()); //gameWorld.registerManager>(shaderSourcePool); @@ -152,13 +155,10 @@ int main() { // ======================================== float DELTA_TIME = 0.0f; - auto stopwatch = std::make_unique(); - auto framePacer = FramePacer(std::move(stopwatch)); + auto framePacer = FramePacer(); framePacer.setTargetFps(0.0f); FrameStats frameStats{}; - gameLoop.init(gameWorld.init()); - // ---------------------------------------- // GameLoop Config // ---------------------------------------- @@ -168,12 +168,12 @@ int main() { .addCommitPoint(CommitPoint::Structural) .addPass(GameState::Booting) - .addSystem>() + .addSystem>() .addCommitPoint(CommitPoint::Structural) .addPass(GameState::Booted | GameState::Live) - .addSystem>() - .addSystem>() + .addSystem>() + .addSystem>() .addCommitPoint(CommitPoint::Structural) .addPass(GameState::Warmup) @@ -201,9 +201,9 @@ int main() { // Clear, bufferswapping .addPass(GameState::Live) .addSystem>() - .addSystem>() - .addSystem>() - .addSystem>() + .addSystem>() + .addSystem>() + .addSystem>() .addCommitPoint(CommitPoint::Structural) .addPass(GameState::Shutdown) @@ -211,6 +211,9 @@ int main() { ; + gameLoop.init(gameWorld.init()); + + while (gameLoop.isRunning(gameWorld)) { framePacer.beginFrame(); diff --git a/examples/v0.0.1-alpha/ecs_rendering/resources/cube.frag b/examples/v0.0.1-alpha/ecs_rendering/resources/cube.frag index 6148adbfdc..104d8afa19 100644 --- a/examples/v0.0.1-alpha/ecs_rendering/resources/cube.frag +++ b/examples/v0.0.1-alpha/ecs_rendering/resources/cube.frag @@ -1,8 +1,8 @@ -#version 450 core +#version 410 core out vec4 FragColor; -layout (location=4) uniform vec4 color; +uniform vec4 color; void main() { diff --git a/examples/v0.0.1-alpha/ecs_rendering/resources/cube.vert b/examples/v0.0.1-alpha/ecs_rendering/resources/cube.vert index 67d1209c30..9c961fdb90 100644 --- a/examples/v0.0.1-alpha/ecs_rendering/resources/cube.vert +++ b/examples/v0.0.1-alpha/ecs_rendering/resources/cube.vert @@ -1,10 +1,10 @@ -#version 450 core +#version 410 core -layout (location=0) in vec3 aPos; +in vec3 aPos; -layout (location=1) uniform mat4 modelMatrix; -layout (location=2) uniform mat4 viewMatrix; -layout (location=3) uniform mat4 projectionMatrix; +uniform mat4 modelMatrix; +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; void main() { diff --git a/include/ext/opengl/rendering/OpenGLDevice.ixx b/include/ext/opengl/rendering/OpenGLDevice.ixx index 6a94586472..bab116265a 100644 --- a/include/ext/opengl/rendering/OpenGLDevice.ixx +++ b/include/ext/opengl/rendering/OpenGLDevice.ixx @@ -10,6 +10,7 @@ module; #include #include #include +#include export module helios.ext.opengl.rendering.OpenGLDevice; @@ -253,7 +254,7 @@ export namespace helios::ext::opengl::rendering { * * @see OpenGLGlyphTextRenderer::addFontFamily() */ - [[nodiscard]] helios::rendering::text::TextRenderer& textRenderer() const noexcept { + [[nodiscard]] helios::rendering::text::TextRenderer& textRenderer() const noexcept override { return *textRenderer_; } diff --git a/include/ext/opengl/rendering/OpenGLGlyphTextRenderer.ixx b/include/ext/opengl/rendering/OpenGLGlyphTextRenderer.ixx index bd5c1198ff..b3e7824d90 100644 --- a/include/ext/opengl/rendering/OpenGLGlyphTextRenderer.ixx +++ b/include/ext/opengl/rendering/OpenGLGlyphTextRenderer.ixx @@ -56,29 +56,6 @@ export namespace helios::ext::opengl::rendering { - friend class OpenGLDevice; - - /** - * @brief Initializes OpenGL resources (VAO and VBO) for text rendering. - * - * Creates a vertex array object and vertex buffer for rendering glyph quads. - * The VBO is configured for dynamic updates (one quad per character). - * - * Must be called before any rendering operations. - */ - void init() { - glGenVertexArrays(1, &vao_); - glGenBuffers(1, &vbo_); - glBindVertexArray(vao_); - glBindBuffer(GL_ARRAY_BUFFER, vbo_); - // 2D quad for a texture requires 6 vertices with 4 floats each => 6*4 - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6*4, nullptr, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - } - @@ -166,7 +143,30 @@ export namespace helios::ext::opengl::rendering { } + public: + + /** + * @brief Initializes OpenGL resources (VAO and VBO) for text rendering. + * + * Creates a vertex array object and vertex buffer for rendering glyph quads. + * The VBO is configured for dynamic updates (one quad per character). + * + * Must be called before any rendering operations. + */ + void init() { + glGenVertexArrays(1, &vao_); + glGenBuffers(1, &vbo_); + glBindVertexArray(vao_); + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + // 2D quad for a texture requires 6 vertices with 4 floats each => 6*4 + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6*4, nullptr, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + } + /** * @brief Resets cached rendering state at the beginning of a render pass. * @@ -183,8 +183,6 @@ export namespace helios::ext::opengl::rendering { lastVao_ = 0; } - public: - /** * @brief Destroys the OpenGLGlyphTextRenderer and releases all OpenGL resources. diff --git a/include/ext/opengl/rendering/OpenGLMeshRenderer.ixx b/include/ext/opengl/rendering/OpenGLMeshRenderer.ixx index 5f98380d58..1ad0757814 100644 --- a/include/ext/opengl/rendering/OpenGLMeshRenderer.ixx +++ b/include/ext/opengl/rendering/OpenGLMeshRenderer.ixx @@ -59,11 +59,6 @@ export namespace helios::ext::opengl::rendering { */ class OpenGLMeshRenderer { - - - friend class OpenGLDevice; - friend class OpenGLBackend; - /** * @brief Cached pointer to the last used shader for state optimization. * @@ -79,6 +74,9 @@ export namespace helios::ext::opengl::rendering { */ mutable unsigned int lastVao_ = 0; + + public: + /** * @brief Initializes the renderer. * @@ -106,8 +104,6 @@ export namespace helios::ext::opengl::rendering { lastVao_ = 0; } - public: - /** * @brief Destructor that unbinds any currently bound VAO. diff --git a/include/helios/bootstrap.ixx b/include/helios/bootstrap.ixx index 03e1b10ddc..ccd50ede51 100644 --- a/include/helios/bootstrap.ixx +++ b/include/helios/bootstrap.ixx @@ -16,6 +16,7 @@ import helios.runtime.gameloop; import helios.runtime.world; import helios.state.Bindings; +import helios.state.types.StateTransitionContext; import helios.runtime.messaging.command; import helios.platform; @@ -32,7 +33,7 @@ import helios.rendering.registry; import helios.runtime.registry; import helios.gameplay.lifecycle; -import helios.gameplay.timing; +import helios.runtime.timing; import helios.gameplay.gamestate; import helios.gameplay.matchstate; @@ -165,12 +166,13 @@ export namespace helios::bootstrap { gameWorld->registerManager( helios::gameplay::matchstate::rules::DefaultMatchStateTransitionRules::rules()); - gameWorld->registerManager(); + gameWorld->registerManager(); gameWorld->session().trackState(); gameWorld->session().trackState(); gameWorld->registerCommandBuffer(); + gameWorld->registerCommandBuffer(); gameWorld->registerCommandBuffer(); gameWorld->registerCommandBuffer(); diff --git a/include/helios/ecs/EntityManager.ixx b/include/helios/ecs/EntityManager.ixx index 70d15a394d..bb7fa5bf9e 100644 --- a/include/helios/ecs/EntityManager.ixx +++ b/include/helios/ecs/EntityManager.ixx @@ -37,6 +37,8 @@ export namespace helios::ecs { * - **Entity Destruction:** Removes all components and invalidates the handle. * - **Component Storage:** Maintains a vector of `SparseSet` instances, one per * component type, indexed by `TypeIndexer`. + * - **Ownership Semantics:** Copy construction/assignment are deleted; + * `EntityManager` is move-enabled only. * * ## Usage * @@ -79,6 +81,15 @@ export namespace helios::ecs { using ComponentTypeId_type = ComponentTypeId; using ComponentOpsRegistry_type = ComponentOpsRegistry; + /** + * @brief Non-copyable: copying an EntityManager is explicitly disabled. + */ + EntityManager(const EntityManager&) = delete; + EntityManager& operator=(const EntityManager&) = delete; + + EntityManager(EntityManager&&) noexcept = default; + EntityManager& operator=(EntityManager&&) noexcept = default; + /** * @brief Constructs an EntityManager with the given capacity. * diff --git a/include/helios/ecs/components/BindingComponent.ixx b/include/helios/ecs/components/BindingComponent.ixx new file mode 100644 index 0000000000..f94276047d --- /dev/null +++ b/include/helios/ecs/components/BindingComponent.ixx @@ -0,0 +1,56 @@ +module; + +#include + +export module helios.ecs.components.BindingComponent; + +export namespace helios::ecs::components { + + /** + * @brief Generic component that stores a handle reference to another entity. + * + * @details `BindingComponent` models a directed relationship from an owner entity + * to a target entity via the target's handle type. It is used as a lightweight + * link primitive for higher-level components such as scene, camera, and viewport + * bindings. + * + * @tparam TOwnerHandle Handle type of the entity owning this component. + * @tparam TTargetHandle Handle type of the referenced target entity. + */ + template + class BindingComponent { + + TTargetHandle targetHandle_{}; + + public: + + /** + * @brief Creates a binding from an explicit target handle. + * + * @param targetHandle Handle of the referenced target entity. + */ + explicit BindingComponent(const TTargetHandle targetHandle) : targetHandle_(targetHandle) {}; + + /** + * @brief Creates a binding from a target entity instance. + * + * @details This constructor is enabled only if the entity's `Handle_type` + * matches `TTargetHandle`. + * + * @tparam TTargetEntity Entity type exposing `Handle_type` and `handle()`. + * @param targetEntity Referenced target entity. + */ + template + requires std::same_as + explicit BindingComponent(const TTargetEntity targetEntity) : targetHandle_(targetEntity.handle()) {}; + + /** + * @brief Returns the bound target handle. + * + * @return Handle of the referenced target entity. + */ + [[nodiscard]] TTargetHandle targetHandle() const noexcept { + return targetHandle_; + } + }; +} \ No newline at end of file diff --git a/include/helios/ecs/components/_module.ixx b/include/helios/ecs/components/_module.ixx index cc4e2f1f6f..06bc0f8657 100644 --- a/include/helios/ecs/components/_module.ixx +++ b/include/helios/ecs/components/_module.ixx @@ -6,4 +6,5 @@ export module helios.ecs.components; export import helios.ecs.components.HierarchyComponent; export import helios.ecs.components.Active; -export import helios.ecs.components.Inactive; \ No newline at end of file +export import helios.ecs.components.Inactive; +export import helios.ecs.components.BindingComponent; \ No newline at end of file diff --git a/include/helios/gameplay/_module.ixx b/include/helios/gameplay/_module.ixx index 2dfcbfdae1..299f6f7512 100644 --- a/include/helios/gameplay/_module.ixx +++ b/include/helios/gameplay/_module.ixx @@ -20,5 +20,4 @@ export import helios.gameplay.damage; export import helios.gameplay.health; export import helios.gameplay.scoring; export import helios.gameplay.gamestate; -export import helios.gameplay.matchstate; -export import helios.gameplay.timing; \ No newline at end of file +export import helios.gameplay.matchstate; \ No newline at end of file diff --git a/include/helios/gameplay/bounds/systems/LevelBoundsBehaviorSystem.ixx b/include/helios/gameplay/bounds/systems/LevelBoundsBehaviorSystem.ixx index 62b3679174..2e2542a516 100644 --- a/include/helios/gameplay/bounds/systems/LevelBoundsBehaviorSystem.ixx +++ b/include/helios/gameplay/bounds/systems/LevelBoundsBehaviorSystem.ixx @@ -82,6 +82,7 @@ export namespace helios::gameplay::bounds::systems { public: using Entity_type = THandle::Entity_type; using EngineRoleTag = helios::runtime::tags::SystemRole; + using CommandBuffer_type = TCommandBuffer; /** * @brief Updates all entities that may have left level bounds. @@ -92,7 +93,7 @@ export namespace helios::gameplay::bounds::systems { * * @param updateContext Context containing deltaTime and other frame data. */ - void update(helios::runtime::world::UpdateContext& updateContext) noexcept { + void update(helios::runtime::world::UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { using namespace helios::physics::collision::types; @@ -140,7 +141,7 @@ export namespace helios::gameplay::bounds::systems { auto* sbp = entity.template get>(); assert(sbp && "Unexpected missing SpawnProfile"); - updateContext.queueCommand>( + cmdBuffer.template add>( entity.handle(), sbp->spawnProfileId() ); diff --git a/include/helios/gameplay/builder/entity/EntityFactory.ixx b/include/helios/gameplay/builder/entity/EntityFactory.ixx index 4e22b5073d..3aaac060aa 100644 --- a/include/helios/gameplay/builder/entity/EntityFactory.ixx +++ b/include/helios/gameplay/builder/entity/EntityFactory.ixx @@ -28,7 +28,7 @@ import helios.gameplay.health; import helios.gameplay.scoring; import helios.gameplay.gamestate; import helios.gameplay.matchstate; -import helios.gameplay.timing; +import helios.runtime.timing; import helios.util.Guid; import helios.core.units; diff --git a/include/helios/gameplay/builder/entity/builders/configs/Move2DConfig.ixx b/include/helios/gameplay/builder/entity/builders/configs/Move2DConfig.ixx index 5b7079e381..71a9b8c7fb 100644 --- a/include/helios/gameplay/builder/entity/builders/configs/Move2DConfig.ixx +++ b/include/helios/gameplay/builder/entity/builders/configs/Move2DConfig.ixx @@ -52,7 +52,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { * @return Reference to this config for chaining. */ Move2DConfig& speed(const float movementSpeed) { - entity_.get>() + entity_.template get>() ->setMovementSpeed(movementSpeed); return *this; @@ -66,7 +66,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { * @return Reference to this config for chaining. */ Move2DConfig& acceleration(const float movementAcceleration) { - entity_.get>() + entity_.template get>() ->setMovementAcceleration(movementAcceleration); return *this; @@ -82,7 +82,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { * @return Reference to this config for chaining. */ Move2DConfig& instantAcceleration(const bool useInstantAcceleration) { - entity_.get>() + entity_.template get>() ->setUseInstantAcceleration(useInstantAcceleration); return *this; diff --git a/include/helios/gameplay/builder/entity/builders/configs/ObserverConfig.ixx b/include/helios/gameplay/builder/entity/builders/configs/ObserverConfig.ixx index 329789499c..25d61a4261 100644 --- a/include/helios/gameplay/builder/entity/builders/configs/ObserverConfig.ixx +++ b/include/helios/gameplay/builder/entity/builders/configs/ObserverConfig.ixx @@ -14,7 +14,7 @@ import helios.gameplay.matchstate.components; import helios.gameplay.scoring.components; import helios.gameplay.scoring.types; -import helios.gameplay.timing; +import helios.runtime.timing; export namespace helios::gameplay::builder::entity::builders::configs { @@ -26,7 +26,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { * * @see ScoreObserverComponent * @see MaxScoreObserverComponent - * @see GameTimerBindingComponent + * @see TimerBindingComponent */ template class ObserverConfig { @@ -71,7 +71,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { */ ObserverConfig& runningScore(const helios::gameplay::scoring::types::ScorePoolId scorePoolId) { - auto* soc = entity_.get>(); + auto* soc = entity_.template get>(); assert(!soc && "ScoreObserverComponent already available."); @@ -89,7 +89,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { */ ObserverConfig& lives(const Entity entity) { - auto* loc = entity_.get< + auto* loc = entity_.template get< helios::gameplay::matchstate::components::LivesBindingComponent >(); @@ -110,7 +110,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { */ ObserverConfig& maxScore(const helios::gameplay::scoring::types::ScorePoolId scorePoolId) { - auto* soc = entity_.get>(); + auto* soc = entity_.template get>(); assert(!soc && "MaxScoreObserverComponent already available."); @@ -120,20 +120,20 @@ export namespace helios::gameplay::builder::entity::builders::configs { } /** - * @brief Adds a GameTimerBindingComponent bound to the specified timer. + * @brief Adds a TimerBindingComponent bound to the specified timer. * - * @param gameTimerId The ID of the game timer to observe. + * @param timerId The ID of the game timer to observe. * * @return Reference to this config for chaining. */ - ObserverConfig& time(const helios::gameplay::timing::types::GameTimerId gameTimerId) { + ObserverConfig& time(const helios::runtime::timing::types::TimerId timerId) { - auto* toc = entity_.get>(); + auto* toc = entity_.template get>(); - assert(!toc && "GameTimerBindingComponent already available."); + assert(!toc && "TimerBindingComponent already available."); - entity_.template add>() - .setGameTimerId(gameTimerId); + entity_.template add>() + .setTimerId(timerId); return *this; } }; diff --git a/include/helios/gameplay/builder/entity/builders/configs/SceneNodeConfig.ixx b/include/helios/gameplay/builder/entity/builders/configs/SceneNodeConfig.ixx index 56e17424a4..0f02142c8a 100644 --- a/include/helios/gameplay/builder/entity/builders/configs/SceneNodeConfig.ixx +++ b/include/helios/gameplay/builder/entity/builders/configs/SceneNodeConfig.ixx @@ -52,7 +52,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { * @brief Validates that a RenderableComponent exists. */ void ensureRenderableComponent() { - const auto* renderableComponent = entity_.get>(); + const auto* renderableComponent = entity_.template get>(); assert(renderableComponent && "Unexpected nullptr for RenderableComponent."); } @@ -62,7 +62,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { * @param shouldBeAvailable Whether the component should exist. */ void ensureSceneNode(const bool shouldBeAvailable) { - const auto* snc = entity_.get>(); + const auto* snc = entity_.template get>(); if (shouldBeAvailable) { assert(snc && "Unexpected nullptr for SceneNodeComponent."); @@ -92,7 +92,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { ensureSceneNode(false); ensureRenderableComponent(); - const auto* renderableComponent = entity_.get>(); + const auto* renderableComponent = entity_.template get>(); auto renderable = renderableComponent->shareRenderable(); @@ -124,13 +124,13 @@ export namespace helios::gameplay::builder::entity::builders::configs { parent.template getOrAdd>().addChild(entity_.handle()); entity_.template getOrAdd>().setParent(parent.handle()); - const auto* renderableComponent = entity_.get>(); + const auto* renderableComponent = entity_.template get>(); auto renderable = renderableComponent->shareRenderable(); auto node = std::make_unique(renderable); - auto* psn = parent.get>(); + auto* psn = parent.template get>(); assert(psn && "Unexpected missing SceneNodeComponent for parent Entity"); assert(psn->sceneNode() && "Unexpected missing SceneNode for parent Entity"); @@ -151,7 +151,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { SceneNodeConfig& inherit(helios::math::TransformType transformType) { ensureSceneNode(true); - entity_.get>() + entity_.template get>() ->sceneNode() ->setInheritance(transformType) ; diff --git a/include/helios/gameplay/builder/entity/builders/configs/SteeringConfig.ixx b/include/helios/gameplay/builder/entity/builders/configs/SteeringConfig.ixx index df4025332b..408905901d 100644 --- a/include/helios/gameplay/builder/entity/builders/configs/SteeringConfig.ixx +++ b/include/helios/gameplay/builder/entity/builders/configs/SteeringConfig.ixx @@ -52,7 +52,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { * @return Reference to this config for chaining. */ SteeringConfig& instantSteering(const bool useInstantRotation) { - entity_.get>() + entity_.template get>() ->setUseInstantRotation(useInstantRotation); return *this; @@ -66,7 +66,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { * @return Reference to this config for chaining. */ SteeringConfig& steeringSetsDirection(const bool directionFromSteering) { - entity_.get>() + entity_.template get>() ->setDirectionFromSteering(directionFromSteering); return *this; diff --git a/include/helios/gameplay/builder/entity/builders/configs/UiTransformConfig.ixx b/include/helios/gameplay/builder/entity/builders/configs/UiTransformConfig.ixx index 8927aef2a4..b9b3936a89 100644 --- a/include/helios/gameplay/builder/entity/builders/configs/UiTransformConfig.ixx +++ b/include/helios/gameplay/builder/entity/builders/configs/UiTransformConfig.ixx @@ -57,7 +57,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { * @return Reference to this config for chaining. */ UiTransformConfig& anchor(const helios::ui::layout::Anchor anchor) { - entity_.get>() + entity_.template get>() ->setAnchor(anchor); return *this; } @@ -70,7 +70,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { * @return Reference to this config for chaining. */ UiTransformConfig& viewport(const helios::rendering::viewport::types::ViewportHandle viewportHandle) { - entity_.get>() + entity_.template get>() ->setViewportHandle(viewportHandle); return *this; } @@ -83,7 +83,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { * @return Reference to this config for chaining. */ UiTransformConfig& pivot(const helios::ui::layout::Anchor anchor) { - entity_.get>() + entity_.template get>() ->setPivot(anchor); return *this; } @@ -96,7 +96,7 @@ export namespace helios::gameplay::builder::entity::builders::configs { * @return Reference to this config for chaining. */ UiTransformConfig& offsets(const helios::math::vec4f offsets) { - entity_.get>() + entity_.template get>() ->setOffsets(offsets); return *this; } diff --git a/include/helios/gameplay/builder/spawnSystem/SpawnSystemFactory.ixx b/include/helios/gameplay/builder/spawnSystem/SpawnSystemFactory.ixx index 19a5a38928..b6ece132b5 100644 --- a/include/helios/gameplay/builder/spawnSystem/SpawnSystemFactory.ixx +++ b/include/helios/gameplay/builder/spawnSystem/SpawnSystemFactory.ixx @@ -45,7 +45,7 @@ export namespace helios::gameplay::builder::spawnSystem { * @see SpawnRuleConfig * @see SpawnSystemConfigurator */ - template + template class SpawnSystemFactory { public: @@ -58,11 +58,11 @@ export namespace helios::gameplay::builder::spawnSystem { * * @return A SpawnSystemConfigurator for chained pool() calls. */ - static helios::gameplay::builder::spawnSystem::builders::configs::SpawnSystemConfigurator configure( + static helios::gameplay::builder::spawnSystem::builders::configs::SpawnSystemConfigurator configure( helios::runtime::pooling::EntityPoolManager& poolManager, - helios::gameplay::spawn::SpawnManager& spawnManager + helios::gameplay::spawn::SpawnManager& spawnManager ) { - return helios::gameplay::builder::spawnSystem::builders::configs::SpawnSystemConfigurator{ + return helios::gameplay::builder::spawnSystem::builders::configs::SpawnSystemConfigurator{ poolManager, spawnManager }; } diff --git a/include/helios/gameplay/builder/spawnSystem/builders/SchedulerBuilder.ixx b/include/helios/gameplay/builder/spawnSystem/builders/SchedulerBuilder.ixx index 6f8377cd0c..e45302b4b1 100644 --- a/include/helios/gameplay/builder/spawnSystem/builders/SchedulerBuilder.ixx +++ b/include/helios/gameplay/builder/spawnSystem/builders/SchedulerBuilder.ixx @@ -27,13 +27,13 @@ export namespace helios::gameplay::builder::spawnSystem::builders { * Provides factory methods for creating default and cyclic * schedulers, and adding rules to them via fluent SchedulerConfig. */ - template + template class SchedulerBuilder { /** * @brief The spawn manager to register schedulers with. */ - helios::gameplay::spawn::SpawnManager& spawnManager_; + helios::gameplay::spawn::SpawnManager& spawnManager_; /** * @brief Adds rules from a parameter pack to a scheduler. @@ -58,7 +58,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders { * @param spawnManager The spawn manager to register schedulers with. */ explicit SchedulerBuilder( - helios::gameplay::spawn::SpawnManager& spawnManager + helios::gameplay::spawn::SpawnManager& spawnManager ) : spawnManager_(spawnManager) {} /** @@ -70,7 +70,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders { template void defaultScheduler(Configs&&... configs) { auto scheduler = std::make_unique< - helios::gameplay::spawn::scheduling::DefaultSpawnScheduler>(); + helios::gameplay::spawn::scheduling::DefaultSpawnScheduler>(); addRules(*scheduler, std::forward(configs)...); @@ -90,7 +90,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders { constexpr std::size_t N = sizeof...(Configs); auto scheduler = std::make_unique< - helios::gameplay::spawn::scheduling::CyclicSpawnScheduler>(); + helios::gameplay::spawn::scheduling::CyclicSpawnScheduler>(); addRules(*scheduler, std::forward(configs)...); @@ -103,7 +103,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders { * @param scheduler Ownership is transferred. */ void customScheduler( - std::unique_ptr> scheduler + std::unique_ptr> scheduler ) { spawnManager_.addScheduler(std::move(scheduler)); } diff --git a/include/helios/gameplay/builder/spawnSystem/builders/configs/SchedulerConfig.ixx b/include/helios/gameplay/builder/spawnSystem/builders/configs/SchedulerConfig.ixx index 9e0875c41e..0306ba4714 100644 --- a/include/helios/gameplay/builder/spawnSystem/builders/configs/SchedulerConfig.ixx +++ b/include/helios/gameplay/builder/spawnSystem/builders/configs/SchedulerConfig.ixx @@ -6,6 +6,7 @@ module; #include #include +#include export module helios.gameplay.builder.spawnSystem.builders.configs.SchedulerConfig; @@ -115,8 +116,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * @return Reference to this config for chaining. */ SchedulerConfig& fixedAmount(const size_t count) { - amountProvider_ = std::make_unique< - helios::gameplay::spawn::policy::amount::FixedSpawnAmount>(count); + amountProvider_ = std::make_unique>(count); return *this; } diff --git a/include/helios/gameplay/builder/spawnSystem/builders/configs/SpawnPoolConfig.ixx b/include/helios/gameplay/builder/spawnSystem/builders/configs/SpawnPoolConfig.ixx index eae1a5756e..7ee29ef7bd 100644 --- a/include/helios/gameplay/builder/spawnSystem/builders/configs/SpawnPoolConfig.ixx +++ b/include/helios/gameplay/builder/spawnSystem/builders/configs/SpawnPoolConfig.ixx @@ -31,11 +31,11 @@ using namespace helios::gameplay::spawn::types; export namespace helios::gameplay::builder::spawnSystem::builders::configs { // Forward declarations for nested builder return types. - template + template class SpawnPoolConfig; - template + template class SpawnProfileConfig; - template + template class SpawnRuleConfig; /** @@ -44,7 +44,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * Returned by commit() and commitCyclic() to allow starting * the next pool() call. Holds only the two manager references. */ - template + template class SpawnSystemConfigurator { /** @@ -55,7 +55,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { /** * @brief The spawn manager for profile and scheduler registration. */ - helios::gameplay::spawn::SpawnManager& spawnManager_; + helios::gameplay::spawn::SpawnManager& spawnManager_; public: @@ -67,7 +67,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { */ SpawnSystemConfigurator( helios::runtime::pooling::EntityPoolManager& poolManager, - helios::gameplay::spawn::SpawnManager& spawnManager + helios::gameplay::spawn::SpawnManager& spawnManager ) noexcept : poolManager_(poolManager), spawnManager_(spawnManager) {} /** @@ -79,7 +79,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * * @return SpawnPoolConfig for fluent configuration. */ - [[nodiscard]] SpawnPoolConfig pool( + [[nodiscard]] SpawnPoolConfig pool( helios::runtime::pooling::types::EntityPoolId poolId, helios::gameplay::common::types::PrefabId prefabId, size_t poolSize @@ -92,13 +92,13 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * Configures the condition and amount for a scheduled spawn rule. * Returns to the parent SpawnProfileConfig via done(). */ - template + template class SpawnRuleConfig { /** * @brief Parent profile this rule belongs to. */ - SpawnProfileConfig& parent_; + SpawnProfileConfig& parent_; /** * @brief Unique identifier for this rule. @@ -124,7 +124,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * @param ruleId Unique identifier for this rule. */ SpawnRuleConfig( - SpawnProfileConfig& parent, + SpawnProfileConfig& parent, helios::gameplay::spawn::types::SpawnRuleId ruleId ) : parent_(parent), ruleId_(ruleId), condition_(nullptr), amountProvider_(nullptr) {} @@ -182,7 +182,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { */ SpawnRuleConfig& fixedAmount(const size_t count) { amountProvider_ = std::make_unique< - helios::gameplay::spawn::policy::amount::FixedSpawnAmount>(count); + helios::gameplay::spawn::policy::amount::FixedSpawnAmount>(count); return *this; } @@ -218,7 +218,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * * @return Reference to the parent SpawnProfileConfig. */ - SpawnProfileConfig& done(); + SpawnProfileConfig& done(); }; /** @@ -228,18 +228,18 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * for a specific spawn profile. Returns to the parent SpawnPoolConfig * via done(). */ - template + template class SpawnProfileConfig { /** * @brief Parent pool this profile belongs to. */ - SpawnPoolConfig& parent_; + SpawnPoolConfig& parent_; /** * @brief The spawn manager to register with. */ - helios::gameplay::spawn::SpawnManager& spawnManager_; + helios::gameplay::spawn::SpawnManager& spawnManager_; /** * @brief Profile identifier. @@ -264,7 +264,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { /** * @brief Scheduled rules for this profile. */ - std::vector>> rules_; + std::vector>> rules_; public: @@ -277,8 +277,8 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * @param poolId Pool from which entities are acquired. */ SpawnProfileConfig( - SpawnPoolConfig& parent, - helios::gameplay::spawn::SpawnManager& spawnManager, + SpawnPoolConfig& parent, + helios::gameplay::spawn::SpawnManager& spawnManager, helios::gameplay::spawn::types::SpawnProfileId profileId, helios::runtime::pooling::types::EntityPoolId poolId ) : parent_(parent), spawnManager_(spawnManager), @@ -291,9 +291,9 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { */ SpawnProfileConfig& emitterPlacement() { placer_ = std::make_unique< - helios::gameplay::spawn::behavior::placements::EmitterSpawnPlacer>(); + helios::gameplay::spawn::behavior::placements::EmitterSpawnPlacer>(); initializer_ = std::make_unique< - helios::gameplay::spawn::behavior::initializers::EmitterInitializer>(); + helios::gameplay::spawn::behavior::initializers::EmitterInitializer>(); return *this; } @@ -385,8 +385,8 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * * @return Reference to the new rule config for chaining. */ - SpawnRuleConfig& scheduledBy(helios::gameplay::spawn::types::SpawnRuleId ruleId) { - rules_.push_back(std::make_unique>(*this, ruleId)); + SpawnRuleConfig& scheduledBy(helios::gameplay::spawn::types::SpawnRuleId ruleId) { + rules_.push_back(std::make_unique>(*this, ruleId)); return *rules_.back(); } @@ -431,7 +431,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * * @return Reference to the parent SpawnPoolConfig. */ - SpawnPoolConfig& done(); + SpawnPoolConfig& done(); }; /** @@ -446,7 +446,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * @see SpawnRuleConfig * @see SpawnSystemFactory */ - template + template class SpawnPoolConfig { /** @@ -457,7 +457,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { /** * @brief The spawn manager to register profiles with. */ - helios::gameplay::spawn::SpawnManager& spawnManager_; + helios::gameplay::spawn::SpawnManager& spawnManager_; /** * @brief Pool identifier. @@ -477,7 +477,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { /** * @brief Profile configurations attached to this pool. */ - std::vector>> profiles_; + std::vector>> profiles_; @@ -494,7 +494,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { */ SpawnPoolConfig( helios::runtime::pooling::EntityPoolManager& poolManager, - helios::gameplay::spawn::SpawnManager& spawnManager, + helios::gameplay::spawn::SpawnManager& spawnManager, helios::runtime::pooling::types::EntityPoolId poolId, helios::gameplay::common::types::PrefabId prefabId, size_t poolSize @@ -509,8 +509,8 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * * @return Reference to the new profile config for chaining. */ - SpawnProfileConfig& profile(helios::gameplay::spawn::types::SpawnProfileId profileId) { - profiles_.push_back(std::make_unique>( + SpawnProfileConfig& profile(helios::gameplay::spawn::types::SpawnProfileId profileId) { + profiles_.push_back(std::make_unique>( *this, spawnManager_, profileId, poolId_ )); return *profiles_.back(); @@ -524,10 +524,10 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * * @return Configurator for chaining the next pool() call. */ - SpawnSystemConfigurator commit() { + SpawnSystemConfigurator commit() { commitPool(); commitProfiles(false); - return SpawnSystemConfigurator{poolManager_, spawnManager_}; + return SpawnSystemConfigurator{poolManager_, spawnManager_}; } /** @@ -557,10 +557,10 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * * @return Configurator for chaining the next pool() call. */ - SpawnSystemConfigurator commitProfilesOnly() { + SpawnSystemConfigurator commitProfilesOnly() { commitPool(); commitProfiles(true); - return SpawnSystemConfigurator{poolManager_, spawnManager_}; + return SpawnSystemConfigurator{poolManager_, spawnManager_}; } /** @@ -575,11 +575,11 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { * @return Configurator for chaining the next pool() call. */ template - SpawnSystemConfigurator commitCyclic() { + SpawnSystemConfigurator commitCyclic() { commitPool(); auto scheduler = std::make_unique< - helios::gameplay::spawn::scheduling::CyclicSpawnScheduler>(); + helios::gameplay::spawn::scheduling::CyclicSpawnScheduler>(); for (auto& profileConfig : profiles_) { auto rules = profileConfig->commit(); @@ -589,7 +589,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { } spawnManager_.addScheduler(std::move(scheduler)); - return SpawnSystemConfigurator{poolManager_, spawnManager_}; + return SpawnSystemConfigurator{poolManager_, spawnManager_}; } private: @@ -616,7 +616,7 @@ export namespace helios::gameplay::builder::spawnSystem::builders::configs { auto rules = profileConfig->commit(); if (!skipSchedulers && !rules.empty()) { auto scheduler = std::make_unique< - helios::gameplay::spawn::scheduling::DefaultSpawnScheduler>(); + helios::gameplay::spawn::scheduling::DefaultSpawnScheduler>(); for (auto& [profileId, rule] : rules) { scheduler->addRule(profileId, std::move(rule)); } diff --git a/include/helios/gameplay/combat/commands/Aim2DCommand.ixx b/include/helios/gameplay/combat/commands/Aim2DCommand.ixx index 439c0ab1a2..eaa5d121c4 100644 --- a/include/helios/gameplay/combat/commands/Aim2DCommand.ixx +++ b/include/helios/gameplay/combat/commands/Aim2DCommand.ixx @@ -80,7 +80,7 @@ export namespace helios::gameplay::combat::commands { return; } - auto* aimComponent = entity->get>(); + auto* aimComponent = entity->template get>(); if (aimComponent) { aimComponent->aim(direction_, freqFactor_); diff --git a/include/helios/gameplay/combat/systems/ProjectileSpawnSystem.ixx b/include/helios/gameplay/combat/systems/ProjectileSpawnSystem.ixx index c6ba1f3409..ba06a90228 100644 --- a/include/helios/gameplay/combat/systems/ProjectileSpawnSystem.ixx +++ b/include/helios/gameplay/combat/systems/ProjectileSpawnSystem.ixx @@ -32,6 +32,7 @@ import helios.runtime.world.tags.SystemRole; using namespace helios::gameplay::spawn::types; using namespace helios::runtime::messaging::command; +using namespace helios::runtime::messaging::command::concepts; export namespace helios::gameplay::combat::systems { /** @@ -74,7 +75,7 @@ export namespace helios::gameplay::combat::systems { * @see EmitterContext */ template - requires IsCommandBufferLike + requires helios::runtime::messaging::command::concepts::IsCommandBufferLike class ProjectileSpawnSystem { /** @@ -89,6 +90,7 @@ export namespace helios::gameplay::combat::systems { public: using EngineRoleTag = helios::runtime::tags::SystemRole; + using CommandBuffer_type = TCommandBuffer; /** * @brief Constructs a ProjectileSpawnSystem with the specified spawn profile. @@ -116,7 +118,7 @@ export namespace helios::gameplay::combat::systems { * * @param updateContext The current frame's update context. */ - void update(helios::runtime::world::UpdateContext& updateContext) noexcept { + void update(helios::runtime::world::UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { for (auto [entity, tsc, ac, sc, active] : updateContext.view< THandle, @@ -161,9 +163,7 @@ export namespace helios::gameplay::combat::systems { assert(aimDirection.isNormalized() && "Unexpected aimDirection.length()"); for (unsigned int i = 0; i < amount; i++) { - updateContext.queueCommand( + cmdBuffer.template add( spawnProfileId_, SpawnContext{ EmitterContext{ diff --git a/include/helios/gameplay/damage/systems/DamageOnCollisionSystem.ixx b/include/helios/gameplay/damage/systems/DamageOnCollisionSystem.ixx index ca9ad5929f..fb095c7f44 100644 --- a/include/helios/gameplay/damage/systems/DamageOnCollisionSystem.ixx +++ b/include/helios/gameplay/damage/systems/DamageOnCollisionSystem.ixx @@ -72,6 +72,7 @@ export namespace helios::gameplay::damage::systems { public: using EngineRoleTag = helios::runtime::tags::SystemRole; + using CommandBuffer_type = TCommandBuffer; /** @@ -79,7 +80,7 @@ export namespace helios::gameplay::damage::systems { * * @param updateContext The current frame's update context. */ - void update(helios::runtime::world::UpdateContext& updateContext) noexcept { + void update(helios::runtime::world::UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { auto eventPass = updateContext.readPass< helios::physics::collision::events::SolidCollisionEvent>(); @@ -92,7 +93,7 @@ export namespace helios::gameplay::damage::systems { continue; } - auto* ddc = go->get(); + auto* ddc = go->template get(); if (!ddc) { continue; } @@ -106,7 +107,7 @@ export namespace helios::gameplay::damage::systems { if (!target) { continue; } - auto* hc = target->get(); + auto* hc = target->template get(); if (!hc) { continue; } @@ -122,7 +123,7 @@ export namespace helios::gameplay::damage::systems { auto instigator= go->handle(); auto causer = go->handle(); - auto* ebc = go->get(); + auto* ebc = go->template get(); if (ebc) { // else the source is assigned to the go emitted // by the go @@ -141,7 +142,7 @@ export namespace helios::gameplay::damage::systems { .damage = damageApplied }; - updateContext.queueCommand(dc); + cmdBuffer.template add(dc); } diff --git a/include/helios/gameplay/gamestate/systems/GameFlowSystem.ixx b/include/helios/gameplay/gamestate/systems/GameFlowSystem.ixx index 229b18e5b9..4aec1aea25 100644 --- a/include/helios/gameplay/gamestate/systems/GameFlowSystem.ixx +++ b/include/helios/gameplay/gamestate/systems/GameFlowSystem.ixx @@ -10,7 +10,7 @@ import helios.gameplay.gamestate.types; import helios.state.Bindings; -import helios.runtime.concepts; +import helios.runtime.messaging.command.concepts; import helios.state.commands; import helios.state.types.StateTransitionRequest; diff --git a/include/helios/gameplay/gamestate/systems/GameStateInputResponseSystem.ixx b/include/helios/gameplay/gamestate/systems/GameStateInputResponseSystem.ixx index d6b5d1d0aa..fad6dc6a5c 100644 --- a/include/helios/gameplay/gamestate/systems/GameStateInputResponseSystem.ixx +++ b/include/helios/gameplay/gamestate/systems/GameStateInputResponseSystem.ixx @@ -48,13 +48,14 @@ export namespace helios::gameplay::gamestate::systems { public: using EngineRoleTag = helios::runtime::tags::SystemRole; + using CommandBuffer_type = TCommandBuffer; /** * @brief Processes input and submits state transition commands. * * @param updateContext The current update context. */ - void update(helios::runtime::world::UpdateContext& updateContext) noexcept { + void update(helios::runtime::world::UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { auto gameState = updateContext.session().state(); @@ -62,13 +63,13 @@ export namespace helios::gameplay::gamestate::systems { switch (gameState) { case GameState::Title: - updateContext.queueCommand>( + cmdBuffer.template add>( StateTransitionRequest(GameState::Title, GameStateTransitionId::ReadyMatchRequest) ); break; case GameState::Running: - updateContext.queueCommand>( + cmdBuffer.template add>( StateTransitionRequest(GameState::Running, GameStateTransitionId::TogglePause) ); break; diff --git a/include/helios/gameplay/health/HealthManager.ixx b/include/helios/gameplay/health/HealthManager.ixx index c2e1788105..d60a042aa6 100644 --- a/include/helios/gameplay/health/HealthManager.ixx +++ b/include/helios/gameplay/health/HealthManager.ixx @@ -8,7 +8,7 @@ module; export module helios.gameplay.health.HealthManager; -import helios.runtime.world.GameWorld; +import helios.runtime.messaging.command.CommandHandlerRegistry; import helios.runtime.world.UpdateContext; import helios.gameplay.damage.commands.ApplyDamageCommand; @@ -100,7 +100,7 @@ export namespace helios::gameplay::health { auto target = updateContext.find(interactionContext.target); - auto* hc = target->get(); + auto* hc = target->template get(); if (!hc) { continue; } @@ -109,7 +109,7 @@ export namespace helios::gameplay::health { auto damageApplied = damageContext.damage; hc->takeDamage(damageApplied); - auto* lac = target->get(); + auto* lac = target->template get(); if (lac && damageApplied > 0) { lac->setDamageContext(damageContext); } @@ -125,7 +125,7 @@ export namespace helios::gameplay::health { auto healthDepletedBehavior = hc->healthDepletedBehavior(); if (hasHealthDepletedFlag(healthDepletedBehavior, HealthDepletedBehavior::DeadTag)) { - target->add(); + target->template add(); } updateContext.pushPass(target->entityHandle(), damageContext); @@ -152,10 +152,10 @@ export namespace helios::gameplay::health { /** * @brief Registers this manager as the damage command handler. * - * @param gameWorld The game world to register with. + * @param commandHandlerRegistry The command-handler registry to register with. */ - void init(GameWorld& gameWorld) { - gameWorld.template registerCommandHandler>(*this); + void init(helios::runtime::messaging::command::CommandHandlerRegistry& commandHandlerRegistry) { + commandHandlerRegistry.registerHandler>(*this); } /** diff --git a/include/helios/gameplay/input/systems/TwinStickInputSystem.ixx b/include/helios/gameplay/input/systems/TwinStickInputSystem.ixx index c1f2366f77..89eb1e019c 100644 --- a/include/helios/gameplay/input/systems/TwinStickInputSystem.ixx +++ b/include/helios/gameplay/input/systems/TwinStickInputSystem.ixx @@ -4,8 +4,6 @@ */ module; -#include - export module helios.gameplay.input.systems.TwinStickInputSystem; import helios.math.types; @@ -67,6 +65,7 @@ export namespace helios::gameplay::input::systems { public: using EngineRoleTag = helios::runtime::tags::SystemRole; + using CommandBuffer_type = TCommandBuffer; /** * @brief Constructs a TwinStickInputSystem for the specified Entity. @@ -81,7 +80,7 @@ export namespace helios::gameplay::input::systems { * * @param updateContext Context containing input snapshot and command buffer. */ - void update(helios::runtime::world::UpdateContext& updateContext) noexcept { + void update(helios::runtime::world::UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { auto& inputSnapshot = updateContext.inputSnapshot(); @@ -97,11 +96,11 @@ export namespace helios::gameplay::input::systems { float finalFreq = 0.0f; auto rdir = helios::math::vec2f{0.0f, 0.0f}; - if (entityHandle_.has()) { - updateContext.queueCommand>( + if (entityHandle_.template has()) { + cmdBuffer.template add>( entityHandle_, ldir, finalSpeed ); - updateContext.queueCommand>( + cmdBuffer.template add>( entityHandle_, rdir, finalFreq ); return; @@ -115,11 +114,11 @@ export namespace helios::gameplay::input::systems { * @todo DO NOT POST IF input is already inactive in shootComponent * and no input was detected (after normalizing) */ - updateContext.queueCommand>( + cmdBuffer.template add>( entityHandle_, ldir, finalSpeed ); - updateContext.queueCommand>( + cmdBuffer.template add>( entityHandle_, ldir, finalSpeed ); @@ -128,7 +127,7 @@ export namespace helios::gameplay::input::systems { finalFreq = freq; } - updateContext.queueCommand>( + cmdBuffer.template add>( entityHandle_, rdir, finalFreq ); @@ -136,13 +135,13 @@ export namespace helios::gameplay::input::systems { // right trigger: shooting const auto rightTrigger = inputSnapshot.gamepadState().triggerRight(); if (rightTrigger > 0.0f) { - updateContext.queueCommand>( + cmdBuffer.template add>( entityHandle_, rightTrigger ); } } else { if (finalFreq > 0.0f) { - updateContext.queueCommand>( + cmdBuffer.template add>( entityHandle_, finalFreq ); } diff --git a/include/helios/gameplay/lifecycle/WorldLifecycleManager.ixx b/include/helios/gameplay/lifecycle/WorldLifecycleManager.ixx index 5e06341029..cb1d7a45d3 100644 --- a/include/helios/gameplay/lifecycle/WorldLifecycleManager.ixx +++ b/include/helios/gameplay/lifecycle/WorldLifecycleManager.ixx @@ -5,10 +5,11 @@ module; #include +#include export module helios.gameplay.lifecycle.WorldLifecycleManager; -import helios.runtime.world.GameWorld; +import helios.runtime.messaging.command.CommandHandlerRegistry; import helios.runtime.world.UpdateContext; import helios.gameplay.lifecycle.types; @@ -18,7 +19,7 @@ import helios.runtime.world.tags.ManagerRole; using namespace helios::runtime::world; using namespace helios::gameplay::lifecycle::commands; using namespace helios::gameplay::lifecycle::types; - +using namespace helios::runtime::messaging::command; export namespace helios::gameplay::lifecycle { /** @@ -44,10 +45,6 @@ export namespace helios::gameplay::lifecycle { */ std::vector pending_; - /** - * @brief Cached pointer to the GameWorld, set during init(). - */ - GameWorld* gameWorld_ = nullptr; public: @@ -70,9 +67,8 @@ export namespace helios::gameplay::lifecycle { * * @param gameWorld The GameWorld to register with. */ - void init(GameWorld& gameWorld) { - gameWorld_ = &gameWorld; - gameWorld.registerCommandHandler(*this); + void init(CommandHandlerRegistry& commandHandlerRegistry) { + commandHandlerRegistry.registerHandler(*this); } /** @@ -86,7 +82,7 @@ export namespace helios::gameplay::lifecycle { * @param updateContext The current frame's update context. */ void flush(UpdateContext& updateContext) noexcept { - if (pending_.empty() || gameWorld_ == nullptr) { + if (pending_.empty()) { return; } @@ -96,7 +92,11 @@ export namespace helios::gameplay::lifecycle { for (const auto& cmd : queue) { switch (cmd.action()) { case WorldLifecycleAction::Reset: - gameWorld_->reset(); + /** + * @todo make GameWorkd CommandHandler for WorldLifecycleAction::Reset + * + */ + assert(false && "Needs to call GameWorld reset()"); // reset complete, do not process further commands. return; } diff --git a/include/helios/gameplay/lifecycle/systems/EntityLifecycleSystem.ixx b/include/helios/gameplay/lifecycle/systems/EntityLifecycleSystem.ixx index 2c185d4148..1736bd610d 100644 --- a/include/helios/gameplay/lifecycle/systems/EntityLifecycleSystem.ixx +++ b/include/helios/gameplay/lifecycle/systems/EntityLifecycleSystem.ixx @@ -18,6 +18,7 @@ import helios.state.Bindings; import helios.runtime.world.Manager; import helios.runtime.world.GameWorld; import helios.runtime.world.UpdateContext; +import helios.runtime.world.tags.SystemRole; import helios.runtime.messaging.command.NullCommandBuffer; import helios.runtime.messaging.command.concepts.IsCommandBufferLike; @@ -44,7 +45,6 @@ using namespace helios::gameplay::health::events; using namespace helios::gameplay::spawn::components; using namespace helios::gameplay::spawn::commands; -import helios.runtime.world.tags.SystemRole; export namespace helios::gameplay::lifecycle::systems { @@ -64,13 +64,14 @@ export namespace helios::gameplay::lifecycle::systems { public: using EngineRoleTag = helios::runtime::tags::SystemRole; + using CommandBuffer_type = TCommandBuffer; /** * @brief Processes health depletion events and enqueues despawn commands. * * @param updateContext Current frame context. */ - void update(helios::runtime::world::UpdateContext& updateContext) noexcept { + void update(helios::runtime::world::UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { auto events = updateContext.readPass>(); @@ -79,13 +80,13 @@ export namespace helios::gameplay::lifecycle::systems { if (go) { - auto* hc = go->get(); + auto* hc = go->template get(); if (hc) { auto healthDepletedBehavior = hc->healthDepletedBehavior(); if (hasHealthDepletedFlag(healthDepletedBehavior, HealthDepletedBehavior::Despawn)) { if (auto* sbp = go->template get()) { assert(sbp->spawnProfileId().value() != 0 && "Entity has no SpawnProfileId."); - updateContext.queueCommand>(go->handle(), sbp->spawnProfileId()); + cmdBuffer.template add>(go->handle(), sbp->spawnProfileId()); } else { go->setActive(false); /** diff --git a/include/helios/gameplay/matchstate/systems/MatchFlowSystem.ixx b/include/helios/gameplay/matchstate/systems/MatchFlowSystem.ixx index d6f6ffb8d9..146cd25935 100644 --- a/include/helios/gameplay/matchstate/systems/MatchFlowSystem.ixx +++ b/include/helios/gameplay/matchstate/systems/MatchFlowSystem.ixx @@ -23,6 +23,8 @@ import helios.gameplay.gamestate.types; import helios.ecs; import helios.runtime; import helios.runtime.concepts; +import helios.runtime.world.tags.SystemRole; + using namespace helios::state::commands; using namespace helios::state::types; @@ -31,9 +33,6 @@ using namespace helios::gameplay::matchstate::types; using namespace helios::runtime::world; using namespace helios::runtime::messaging::command::concepts; using namespace helios::runtime::messaging::command; - -import helios.runtime.world.tags.SystemRole; - export namespace helios::gameplay::matchstate::systems { /** @@ -54,13 +53,14 @@ export namespace helios::gameplay::matchstate::systems { public: using EngineRoleTag = helios::runtime::tags::SystemRole; + using CommandBuffer_type = TCommandBuffer; /** * @brief Processes match state and issues transition commands. * * @param updateContext The current update context. */ - void update(helios::runtime::world::UpdateContext& updateContext) noexcept { + void update(helios::runtime::world::UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { auto& session = updateContext.session(); @@ -78,21 +78,21 @@ export namespace helios::gameplay::matchstate::systems { case MatchState::Undefined: { - updateContext.queueCommand>( + cmdBuffer.template add>( StateTransitionRequest(matchState, MatchStateTransitionId::WarmupRequest) ); break; } case MatchState::Warmup: { - updateContext.queueCommand>( + cmdBuffer.template add>( StateTransitionRequest(matchState, MatchStateTransitionId::StartRequest) ); break; } case MatchState::Start: { - updateContext.queueCommand>( + cmdBuffer.template add>( StateTransitionRequest(matchState, MatchStateTransitionId::CountdownRequest) ); break; diff --git a/include/helios/gameplay/matchstate/systems/MatchRuleSystem.ixx b/include/helios/gameplay/matchstate/systems/MatchRuleSystem.ixx index ccd253d328..89964741ed 100644 --- a/include/helios/gameplay/matchstate/systems/MatchRuleSystem.ixx +++ b/include/helios/gameplay/matchstate/systems/MatchRuleSystem.ixx @@ -14,11 +14,9 @@ import helios.input.types.Gamepad; import helios.ecs; import helios.runtime; - -using namespace helios::input::types; - import helios.runtime.world.tags.SystemRole; +using namespace helios::input::types; export namespace helios::gameplay::matchstate::systems { /** diff --git a/include/helios/gameplay/scoring/ScorePoolManager.ixx b/include/helios/gameplay/scoring/ScorePoolManager.ixx index bc4d1e317f..399a517cec 100644 --- a/include/helios/gameplay/scoring/ScorePoolManager.ixx +++ b/include/helios/gameplay/scoring/ScorePoolManager.ixx @@ -6,9 +6,10 @@ module; #include #include -#include +#include #include + export module helios.gameplay.scoring.ScorePoolManager; import helios.gameplay.scoring.ScorePool; @@ -20,7 +21,7 @@ import helios.gameplay.scoring.types.ScorePoolId; import helios.runtime.world.UpdateContext; -import helios.runtime.world.GameWorld; +import helios.runtime.messaging.command.CommandHandlerRegistry; import helios.runtime.pooling.EntityPool; import helios.core.types; @@ -106,7 +107,6 @@ export namespace helios::gameplay::scoring { * Processes all pending ScoreValueContext entries and adds them to * the appropriate score pools based on their scorePoolId. * - * @param gameWorld Reference to the game world. * @param update_context Reference to the update context. */ void flush( @@ -146,10 +146,10 @@ export namespace helios::gameplay::scoring { /** * @brief Initializes the manager and registers it as the score command handler. * - * @param gameWorld Reference to the game world. + * @param commandHandlerRegistry The command-handler registry. */ - void init(helios::runtime::world::GameWorld& gameWorld) { - gameWorld.registerCommandHandler(*this); + void init(helios::runtime::messaging::command::CommandHandlerRegistry& commandHandlerRegistry) { + commandHandlerRegistry.registerHandler(*this); } diff --git a/include/helios/gameplay/scoring/systems/CombatScoringSystem.ixx b/include/helios/gameplay/scoring/systems/CombatScoringSystem.ixx index 921628c26c..ca30fa0aab 100644 --- a/include/helios/gameplay/scoring/systems/CombatScoringSystem.ixx +++ b/include/helios/gameplay/scoring/systems/CombatScoringSystem.ixx @@ -21,6 +21,8 @@ import helios.gameplay.scoring.types; import helios.runtime.world.GameWorld; import helios.runtime.world.UpdateContext; + +import helios.runtime.world.tags.SystemRole; import helios.runtime.messaging.command.NullCommandBuffer; import helios.runtime.messaging.command.concepts.IsCommandBufferLike; @@ -49,7 +51,6 @@ using namespace helios::runtime::messaging::command; using namespace helios::runtime::messaging::command::concepts; #define HELIOS_LOG_SCOPE "helios::gameplay::scoring::systems::CombatScoringSystem" -import helios.runtime.world.tags.SystemRole; export namespace helios::gameplay::scoring::systems { @@ -70,6 +71,7 @@ export namespace helios::gameplay::scoring::systems { public: using EngineRoleTag = helios::runtime::tags::SystemRole; + using CommandBuffer_type = TCommandBuffer; /** @@ -77,7 +79,7 @@ export namespace helios::gameplay::scoring::systems { * * @param updateContext The current frame's update context. */ - void update(helios::runtime::world::UpdateContext& updateContext) noexcept { + void update(helios::runtime::world::UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { for (auto& event : updateContext.readPass>()) { @@ -96,12 +98,12 @@ export namespace helios::gameplay::scoring::systems { continue; } - auto* svc = enemy->get>(); + auto* svc = enemy->template get>(); if (!svc) { continue; } - auto* scc = instigator->get(); + auto* scc = instigator->template get(); if (!scc) { continue; } @@ -118,7 +120,7 @@ export namespace helios::gameplay::scoring::systems { svc->score().value()) ); - updateContext.queueCommand( + cmdBuffer.template add( std::move(scoreContext) ); } diff --git a/include/helios/gameplay/spawn/SpawnManager.ixx b/include/helios/gameplay/spawn/SpawnManager.ixx index 34eee4b32b..60a7231089 100644 --- a/include/helios/gameplay/spawn/SpawnManager.ixx +++ b/include/helios/gameplay/spawn/SpawnManager.ixx @@ -34,7 +34,7 @@ import helios.spatial.transform.components.RotationStateComponent; import helios.physics.collision.Bounds; import helios.scene.components.SceneNodeComponent; -import helios.runtime.world.GameWorld; +import helios.runtime.messaging.command.CommandHandlerRegistry; import helios.runtime.world.UpdateContext; import helios.runtime.pooling.EntityPoolManager; @@ -50,6 +50,7 @@ import helios.math; import helios.runtime.world.tags.ManagerRole; +using namespace helios::runtime::messaging::command; using namespace helios::gameplay::spawn::commands; using namespace helios::gameplay::spawn::types; export namespace helios::gameplay::spawn { @@ -74,7 +75,7 @@ export namespace helios::gameplay::spawn { * @see ScheduledSpawnPlanCommand * @see SpawnProfile */ - template + template class SpawnManager { @@ -86,7 +87,7 @@ export namespace helios::gameplay::spawn { * ScheduledSpawnPlan instances for execution. Multiple schedulers allow * grouping spawn rules by category (e.g., enemies, powerups, projectiles). */ - std::vector>> spawnSchedulers_; + std::vector>> spawnSchedulers_; /** * @brief Queue of pending spawn commands. @@ -195,19 +196,19 @@ export namespace helios::gameplay::spawn { auto go = entityPoolManager_->acquire(entityPoolId); assert(go && "Failed to acquire Entity"); - auto* tsc = go->get(); + auto* tsc = go->template get(); - auto* sbp = go->get(); + auto* sbp = go->template get(); assert(sbp && "unexpected missing SpawnedByProfileComponent"); - auto* aabb = go->get(); + auto* aabb = go->template get(); assert(aabb && "unexpected missing AabbColliderComponent"); auto spawnCursor = SpawnPlanCursor{spawnCount, i}; const auto& spawnContext = scheduledSpawnPlanCommand.spawnContext(); const auto& emitter = spawnContext.emitterContext; - auto* ebc = go->get(); + auto* ebc = go->template get(); if (emitter.has_value() && ebc) { ebc->setSource(emitter.value().source); } @@ -342,7 +343,8 @@ export namespace helios::gameplay::spawn { /** * @brief Default constructor. */ - SpawnManager() = default; + explicit SpawnManager(helios::runtime::pooling::EntityPoolManager entityPoolManager) + : entityPoolManager_(entityPoolManager){} /** * @brief Submits a spawn command for deferred processing. @@ -365,7 +367,7 @@ export namespace helios::gameplay::spawn { * * @param scheduler The scheduler to add. Ownership is transferred. */ - void addScheduler(std::unique_ptr> scheduler) { + void addScheduler(std::unique_ptr> scheduler) { spawnSchedulers_.push_back(std::move(scheduler)); } @@ -471,12 +473,9 @@ export namespace helios::gameplay::spawn { * * @param gameWorld Game world used for resource lookup and handler registration. */ - void init(helios::runtime::world::GameWorld& gameWorld) noexcept { + void init(CommandHandlerRegistry& commandHandlerRegistry) noexcept { - assert(gameWorld.hasManager>() && "Unexpected missing EntityPoolManager"); - entityPoolManager_ = gameWorld.tryManager>(); - - gameWorld.registerCommandHandler< + commandHandlerRegistry.registerHandler< SpawnCommand, DespawnCommand, ScheduledSpawnPlanCommand @@ -512,7 +511,7 @@ export namespace helios::gameplay::spawn { * * @return Span of spawn scheduler unique pointers. */ - std::span>> spawnSchedulers() { + std::span>> spawnSchedulers() { return spawnSchedulers_; } }; diff --git a/include/helios/gameplay/spawn/scheduling/CyclicSpawnScheduler.ixx b/include/helios/gameplay/spawn/scheduling/CyclicSpawnScheduler.ixx index a2b4503fcb..31c6527a6b 100644 --- a/include/helios/gameplay/spawn/scheduling/CyclicSpawnScheduler.ixx +++ b/include/helios/gameplay/spawn/scheduling/CyclicSpawnScheduler.ixx @@ -90,8 +90,8 @@ export namespace helios::gameplay::spawn::scheduling { * @see DefaultSpawnScheduler * @see RuleConfig */ - template - class CyclicSpawnScheduler : public SpawnScheduler { + template + class CyclicSpawnScheduler : public SpawnScheduler { /** * @brief Fixed-size ring buffer of rule configurations. @@ -119,7 +119,7 @@ export namespace helios::gameplay::spawn::scheduling { /** * @brief Processor for evaluating individual rules. */ - DefaultRuleProcessor ruleProcessor_; + DefaultRuleProcessor ruleProcessor_; public: @@ -135,20 +135,20 @@ export namespace helios::gameplay::spawn::scheduling { * @param spawnContext Context for spawn operations. */ void evaluate( - const GameWorld& gameWorld, + const TWorld& world, const UpdateContext& updateContext, const SpawnContext& spawnContext ) noexcept override { - SpawnScheduler::scheduledSpawnPlans_.clear(); + SpawnScheduler::scheduledSpawnPlans_.clear(); // Process queue auto& [spawnProfileId, spawnRule] = ringBuffer_[cursor_]; auto spawnPlan = ruleProcessor_.processRule( - gameWorld, updateContext, spawnContext, spawnProfileId, *spawnRule, + world, updateContext, spawnContext, spawnProfileId, *spawnRule, spawnRuleStates_[spawnRule->spawnRuleId()]); if (spawnPlan.amount > 0) { - SpawnScheduler::scheduledSpawnPlans_.push_back({ + SpawnScheduler::scheduledSpawnPlans_.push_back({ spawnProfileId, std::move(spawnPlan), spawnContext diff --git a/include/helios/gameplay/spawn/scheduling/DefaultRuleProcessor.ixx b/include/helios/gameplay/spawn/scheduling/DefaultRuleProcessor.ixx index d604274380..016701d6ba 100644 --- a/include/helios/gameplay/spawn/scheduling/DefaultRuleProcessor.ixx +++ b/include/helios/gameplay/spawn/scheduling/DefaultRuleProcessor.ixx @@ -15,7 +15,6 @@ import helios.gameplay.spawn.scheduling.RuleProcessor; import helios.runtime.world.UpdateContext; import helios.gameplay.spawn.SpawnManager; import helios.gameplay.spawn.types.SpawnContext; -import helios.runtime.world.GameWorld; import helios.gameplay.spawn.scheduling.SpawnPlan; import helios.gameplay.spawn.scheduling.ScheduledSpawnPlan; import helios.gameplay.spawn.types; @@ -44,7 +43,7 @@ export namespace helios::gameplay::spawn::scheduling { * @see DefaultSpawnScheduler * @see CyclicSpawnScheduler */ - template + template class DefaultRuleProcessor : public RuleProcessor { public: @@ -65,15 +64,15 @@ export namespace helios::gameplay::spawn::scheduling { * @return SpawnPlan indicating how many entities to spawn. */ SpawnPlan processRule( - const GameWorld& gameWorld, + const TWorld& world, const UpdateContext& updateContext, const SpawnContext& spawnContext, const SpawnProfileId spawnProfileId, SpawnRule& spawnRule, SpawnRuleState& spawnRuleState ) noexcept override { - const auto* poolManager = gameWorld.tryManager>(); - const auto* spawnManager = gameWorld.tryManager>(); + const auto* poolManager = world.template tryManager>(); + const auto* spawnManager = world.template tryManager>(); const auto* spawnProfile = spawnManager->spawnProfile(spawnProfileId); assert(spawnProfile != nullptr); @@ -88,7 +87,7 @@ export namespace helios::gameplay::spawn::scheduling { return spawnRule.evaluate( entityPoolId, poolSnapshot, spawnRuleState, - gameWorld, + world, updateContext ); } diff --git a/include/helios/gameplay/spawn/scheduling/DefaultSpawnScheduler.ixx b/include/helios/gameplay/spawn/scheduling/DefaultSpawnScheduler.ixx index f8f548ceff..5581c2be6e 100644 --- a/include/helios/gameplay/spawn/scheduling/DefaultSpawnScheduler.ixx +++ b/include/helios/gameplay/spawn/scheduling/DefaultSpawnScheduler.ixx @@ -65,8 +65,8 @@ export namespace helios::gameplay::spawn::scheduling { * @see ScheduledSpawnPlan * @see GameObjectSpawnSystem */ - template - class DefaultSpawnScheduler : public SpawnScheduler { + template + class DefaultSpawnScheduler : public SpawnScheduler { protected: @@ -88,7 +88,7 @@ export namespace helios::gameplay::spawn::scheduling { /** * @brief Processor for evaluating individual rules. */ - DefaultRuleProcessor ruleProcessor_{}; + DefaultRuleProcessor ruleProcessor_{}; public: @@ -98,7 +98,7 @@ export namespace helios::gameplay::spawn::scheduling { * @param initialSpanPlanSize Initial capacity for the spawn plan buffer. */ DefaultSpawnScheduler(const size_t initialSpanPlanSize = 20) { - SpawnScheduler::scheduledSpawnPlans_.reserve(initialSpanPlanSize); + SpawnScheduler::scheduledSpawnPlans_.reserve(initialSpanPlanSize); } /** @@ -112,21 +112,21 @@ export namespace helios::gameplay::spawn::scheduling { * @param spawnContext Context for spawn operations. */ void evaluate( - const GameWorld& gameWorld, + const TWorld& world, const UpdateContext& updateContext, const SpawnContext& spawnContext) noexcept override{ - SpawnScheduler::scheduledSpawnPlans_.clear(); + SpawnScheduler::scheduledSpawnPlans_.clear(); for (auto& [spawnProfileId, rule] : spawnRules_) { auto spawnPlan = ruleProcessor_.processRule( - gameWorld, updateContext, spawnContext, spawnProfileId, *rule, + world, updateContext, spawnContext, spawnProfileId, *rule, spawnRuleStates_[rule->spawnRuleId()] ); if (spawnPlan.amount > 0) { - SpawnScheduler::scheduledSpawnPlans_.push_back({ + SpawnScheduler::scheduledSpawnPlans_.push_back({ spawnProfileId, std::move(spawnPlan), spawnContext diff --git a/include/helios/gameplay/spawn/scheduling/SpawnScheduler.ixx b/include/helios/gameplay/spawn/scheduling/SpawnScheduler.ixx index 6955706406..cd7481800c 100644 --- a/include/helios/gameplay/spawn/scheduling/SpawnScheduler.ixx +++ b/include/helios/gameplay/spawn/scheduling/SpawnScheduler.ixx @@ -52,7 +52,7 @@ export namespace helios::gameplay::spawn::scheduling { * @see CyclicSpawnScheduler * @see ScheduledSpawnPlan */ - template + template class SpawnScheduler { protected: @@ -86,7 +86,7 @@ export namespace helios::gameplay::spawn::scheduling { * @param spawnContext Optional spawn context for the operation. */ virtual void evaluate( - const GameWorld& gameWorld, + const TWorld world, const UpdateContext& updateContext, const SpawnContext& spawnContext = {}) noexcept = 0; diff --git a/include/helios/gameplay/spawn/systems/EntitySpawnSystem.ixx b/include/helios/gameplay/spawn/systems/EntitySpawnSystem.ixx index b64363e9d7..a46c9652be 100644 --- a/include/helios/gameplay/spawn/systems/EntitySpawnSystem.ixx +++ b/include/helios/gameplay/spawn/systems/EntitySpawnSystem.ixx @@ -36,7 +36,7 @@ export namespace helios::gameplay::spawn::systems { class EntitySpawnSystem { - helios::gameplay::spawn::SpawnManager& spawnManager_; + helios::gameplay::spawn::SpawnManager& spawnManager_; TWorld* world_ = nullptr; @@ -44,15 +44,12 @@ export namespace helios::gameplay::spawn::systems { using EngineRoleTag = helios::runtime::tags::SystemRole; + using CommandBuffer_type = TCommandBuffer; - explicit EntitySpawnSystem(helios::gameplay::spawn::SpawnManager& spawnManager) noexcept - : spawnManager_{spawnManager} {} + explicit EntitySpawnSystem(helios::gameplay::spawn::SpawnManager& spawnManager, TWorld& tworld) noexcept + : spawnManager_{spawnManager}, world_(&tworld) {} - void init(TWorld& world) noexcept { - world_ = &world; - } - /** * @brief Processes spawn scheduling and enqueues spawn commands. * @@ -64,7 +61,7 @@ export namespace helios::gameplay::spawn::systems { * * @param updateContext The current frame's update context. */ - void update(helios::runtime::world::UpdateContext& updateContext) noexcept { + void update(helios::runtime::world::UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { const auto& events = updateContext.readFrame< helios::gameplay::spawn::events::SpawnPlanCommandExecutedEvent @@ -81,14 +78,12 @@ export namespace helios::gameplay::spawn::systems { spawnScheduler->commit(event.spawnRuleId, event.spawnCount); } - spawnScheduler->evaluate(*world_, updateContext); + spawnScheduler->evaluate(world_, updateContext); auto scheduledPlans = spawnScheduler->drainScheduledPlans(); for (auto& plan : scheduledPlans) { - updateContext.queueCommand - >( + cmdBuffer.template add>( plan.spawnProfileId, plan.spawnPlan, plan.spawnContext ); } diff --git a/include/helios/gameplay/timing/_module.ixx b/include/helios/gameplay/timing/_module.ixx deleted file mode 100644 index c50c2266f9..0000000000 --- a/include/helios/gameplay/timing/_module.ixx +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @file _module.ixx - * @brief Aggregate module for helios::gameplay::timing namespace. - * - */ -export module helios.gameplay.timing; - -export import helios.gameplay.timing.types; -export import helios.gameplay.timing.commands; -export import helios.gameplay.timing.systems; -export import helios.gameplay.timing.components; - - -export import helios.gameplay.timing.GameTimer; -export import helios.gameplay.timing.TimerManager; \ No newline at end of file diff --git a/include/helios/gameplay/timing/commands/_module.ixx b/include/helios/gameplay/timing/commands/_module.ixx deleted file mode 100644 index 18a6655293..0000000000 --- a/include/helios/gameplay/timing/commands/_module.ixx +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @file _module.ixx - * @brief Aggregator for timer command types. - */ -export module helios.gameplay.timing.commands; - -export import helios.gameplay.timing.commands.TimerControlCommand; diff --git a/include/helios/gameplay/timing/components/_module.ixx b/include/helios/gameplay/timing/components/_module.ixx deleted file mode 100644 index 5bcb9b768d..0000000000 --- a/include/helios/gameplay/timing/components/_module.ixx +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @file _module.ixx - * @brief Aggregator for timer components. - */ -export module helios.gameplay.timing.components; - -export import helios.gameplay.timing.components.GameTimerBindingComponent; \ No newline at end of file diff --git a/include/helios/gameplay/timing/systems/_module.ixx b/include/helios/gameplay/timing/systems/_module.ixx deleted file mode 100644 index 28fe86bc3c..0000000000 --- a/include/helios/gameplay/timing/systems/_module.ixx +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @file _module.ixx - * @brief Aggregator for timer systems. - */ -export module helios.gameplay.timing.systems; - -export import helios.gameplay.timing.systems.GameTimerUpdateSystem; -export import helios.gameplay.timing.systems.GameTimerClearSystem; diff --git a/include/helios/gameplay/timing/types/GameTimerId.ixx b/include/helios/gameplay/timing/types/GameTimerId.ixx deleted file mode 100644 index 74849401b2..0000000000 --- a/include/helios/gameplay/timing/types/GameTimerId.ixx +++ /dev/null @@ -1,32 +0,0 @@ -/** -* @file GameTimerId.ixx - * @brief Strongly-typed identifier for gameTimers. - */ -module; - -#include -#include - -export module helios.gameplay.timing.types.GameTimerId; - -import helios.core.types.FuncDefs; -import helios.core.types; -import helios.core.types.StrongId; - -export namespace helios::gameplay::timing::types { - - /** - * @brief Tag type for GameTimerId. - */ - struct GameTimerIdTag{}; - - /** - * @brief Strongly-typed identifier for gameTimers. - * - * @details Used to uniquely identify gameTimers. - * - * @see helios::core::types::StrongId - */ - using GameTimerId = helios::core::types::StrongId; - -} diff --git a/include/helios/gameplay/timing/types/TimerRevision.ixx b/include/helios/gameplay/timing/types/TimerRevision.ixx deleted file mode 100644 index 580ccb0430..0000000000 --- a/include/helios/gameplay/timing/types/TimerRevision.ixx +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @file TimerRevision.ixx - * @brief Type alias for timer revision counters. - */ -module; - -#include - -export module helios.gameplay.timing.types.TimerRevision; - - -export namespace helios::gameplay::timing::types { - - /** - * @brief Monotonically increasing revision counter for GameTimer updates. - */ - using TimerRevision = uint32_t; - -} \ No newline at end of file diff --git a/include/helios/gameplay/timing/types/_module.ixx b/include/helios/gameplay/timing/types/_module.ixx deleted file mode 100644 index b895a710c7..0000000000 --- a/include/helios/gameplay/timing/types/_module.ixx +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @file _module.ixx - * @brief Aggregator for timer-related type definitions. - */ -export module helios.gameplay.timing.types; - -export import helios.gameplay.timing.types.TimerControlContext; -export import helios.gameplay.timing.types.TimerState; -export import helios.gameplay.timing.types.TimerRevision; -export import helios.gameplay.timing.types.GameTimerId; - diff --git a/include/helios/helios_config.h b/include/helios/helios_config.h index fd82712482..864a8ead0b 100644 --- a/include/helios/helios_config.h +++ b/include/helios/helios_config.h @@ -2,6 +2,13 @@ #define HELIOS_DEBUG 1 #endif +#if defined(_MSC_VER) + #define HELIOS_FUNCTION_SIGNATURE __FUNCSIG__ +#elif defined(__clang__) || defined(__GNUC__) + #define HELIOS_FUNCTION_SIGNATURE __PRETTY_FUNCTION__ +#else + #define HELIOS_FUNCTION_SIGNATURE __func__ +#endif #define RENDERQUEUE_MESHRENDER_COMMANDS_SIZE 100 #define RENDERQUEUE_TEXTRENDER_COMMANDS_SIZE 100 diff --git a/include/helios/physics/collision/systems/CollisionStateClearSystem.ixx b/include/helios/physics/collision/systems/CollisionStateClearSystem.ixx index add214ae23..2b738e079f 100644 --- a/include/helios/physics/collision/systems/CollisionStateClearSystem.ixx +++ b/include/helios/physics/collision/systems/CollisionStateClearSystem.ixx @@ -28,14 +28,11 @@ import helios.gameplay.spawn.commands.DespawnCommand; import helios.ecs.components.Active; +import helios.runtime.world.tags.SystemRole; + using namespace helios::physics::collision::components; using namespace helios::physics::collision::types; using namespace helios::gameplay::spawn::commands; - - - -import helios.runtime.world.tags.SystemRole; - export namespace helios::physics::collision::systems { /** diff --git a/include/helios/physics/collision/systems/CollisionStateResponseSystem.ixx b/include/helios/physics/collision/systems/CollisionStateResponseSystem.ixx index 5bb26d09e9..ea2f938b2b 100644 --- a/include/helios/physics/collision/systems/CollisionStateResponseSystem.ixx +++ b/include/helios/physics/collision/systems/CollisionStateResponseSystem.ixx @@ -35,15 +35,13 @@ import helios.gameplay.spawn.components.SpawnedByProfileComponent; import helios.ecs.components.Active; +import helios.runtime.world.tags.SystemRole; + using namespace helios::physics::collision::components; using namespace helios::physics::collision::types; using namespace helios::gameplay::spawn::commands; using namespace helios::runtime::messaging::command; using namespace helios::runtime::messaging::command::concepts; - - -import helios.runtime.world.tags.SystemRole; - export namespace helios::physics::collision::systems { /** @@ -69,6 +67,7 @@ export namespace helios::physics::collision::systems { public: using EngineRoleTag = helios::runtime::tags::SystemRole; + using CommandBuffer_type = TCommandBuffer; /** * @brief Processes collision states and issues response commands. @@ -79,7 +78,7 @@ export namespace helios::physics::collision::systems { * * @param updateContext Context providing access to the command buffer and world. */ - void update(helios::runtime::world::UpdateContext& updateContext) noexcept { + void update(helios::runtime::world::UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { for (auto [entity, csc, sbp, active] : updateContext.view< THandle, @@ -113,7 +112,7 @@ export namespace helios::physics::collision::systems { if (hasFlag(collisionBehavior, CollisionBehavior::Despawn)) { - updateContext.queueCommand>( + cmdBuffer.template add>( entity.handle(), sbp->spawnProfileId()); } } diff --git a/include/helios/physics/collision/systems/GridCollisionDetectionSystem.ixx b/include/helios/physics/collision/systems/GridCollisionDetectionSystem.ixx index 3fcd4bb77c..13ed247326 100644 --- a/include/helios/physics/collision/systems/GridCollisionDetectionSystem.ixx +++ b/include/helios/physics/collision/systems/GridCollisionDetectionSystem.ixx @@ -592,8 +592,8 @@ export namespace helios::physics::collision::systems { for (size_t i = 0; i < candidates.size(); i++) { CollisionCandidate& candidate = candidates[i]; - CollisionComponent* cc = candidate.collisionComponent; - CollisionStateComponent* csc = candidate.collisionStateComponent; + CollisionComponent * cc = candidate.collisionComponent; + CollisionStateComponent* csc = candidate.collisionStateComponent; auto hitPolicy = cc->hitPolicy(); if (hitPolicy == helios::physics::collision::types::HitPolicy::OneHit && csc->hasCollision()) { @@ -606,8 +606,8 @@ export namespace helios::physics::collision::systems { auto& [gameObject, aabbColliderComponent, collisionComponent, collisionStateComponent] = candidates[j]; - CollisionComponent* matchCC = collisionComponent; - CollisionStateComponent* matchCSC = collisionStateComponent; + CollisionComponent* matchCC = collisionComponent; + CollisionStateComponent* matchCSC = collisionStateComponent; const auto collisionStruct = findCollisionType(cc, matchCC); diff --git a/include/helios/platform/environment/systems/PollEventsSystem.ixx b/include/helios/platform/environment/systems/PollEventsSystem.ixx index 480ce040bf..44113ff35e 100644 --- a/include/helios/platform/environment/systems/PollEventsSystem.ixx +++ b/include/helios/platform/environment/systems/PollEventsSystem.ixx @@ -31,6 +31,8 @@ export namespace helios::platform::environment::systems { public: + using CommandBuffer_type = TCommandBuffer; + /** * @brief Engine role marker used by runtime system registries. */ @@ -41,8 +43,8 @@ export namespace helios::platform::environment::systems { * * @param updateContext Frame-local update context. */ - void update(UpdateContext& updateContext) noexcept { - updateContext.queueCommand(); + void update(UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { + cmdBuffer.template add(); } }; diff --git a/include/helios/platform/glfw/GLFWPlatformManager.ixx b/include/helios/platform/glfw/GLFWPlatformManager.ixx index 1230a43ea1..90d78442fd 100644 --- a/include/helios/platform/glfw/GLFWPlatformManager.ixx +++ b/include/helios/platform/glfw/GLFWPlatformManager.ixx @@ -13,6 +13,8 @@ module; export module helios.platform.glfw.GLFWPlatformManager; +import helios.runtime.world.UpdateContext; +import helios.runtime.world.Session; import helios.util.log; import helios.ecs.types.EntityHandle; @@ -20,14 +22,17 @@ import helios.core.types; import helios.state.Bindings; +import helios.runtime.messaging.command.concepts.IsPlatformCommandBuffer; +import helios.runtime.messaging.command.CommandHandlerRegistry; +import helios.runtime.messaging.command.CommandBufferRegistry; + import helios.state.commands; import helios.state.types; import helios.gameplay.gamestate.types; +import helios.runtime.world.EngineWorld; import helios.runtime.world.tags.ManagerRole; -import helios.runtime.world; - import helios.platform.environment.commands; import helios.platform.lifecycle.commands; import helios.platform.environment.components; @@ -82,11 +87,9 @@ export namespace helios::platform::glfw { template requires IsWindowHandle && IsCommandBufferLike - && IsCommandBufferLike + && IsPlatformCommandBuffer class GLFWPlatformManager { - GameWorld* gameWorld_ = nullptr; - std::vector> pendingResizeCommands_; std::vector> windowCreateCommands_; @@ -110,15 +113,17 @@ export namespace helios::platform::glfw { inline static const helios::util::log::Logger& logger_ = helios::util::log::LogManager::loggerForScope( HELIOS_LOG_SCOPE); + PlatformWorld* platformWorld_; + /** * @brief Initializes GLFW and transitions runtime/session from booting to boot request. * * @param updateContext Frame-local update context. */ - void initPlatform(UpdateContext& updateContext) noexcept { + bool initPlatform(UpdateContext& updateContext) noexcept { if (!shouldInit_ || initialized_) { - return; + return false; } if (glfwInit() == GLFW_FALSE) { @@ -126,7 +131,7 @@ export namespace helios::platform::glfw { } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); assert(updateContext.session().state() == GameState::Booting && @@ -134,15 +139,10 @@ export namespace helios::platform::glfw { initialized_ = updateContext.session().initialize() && updateContext.runtimeEnvironment().initialize(); - - updateContext.queueCommand>( - StateTransitionRequest( - updateContext.session().state(), - GameStateTransitionId::BootRequest - ) - ); - + shouldInit_ = false; + + return initialized_; } @@ -156,8 +156,6 @@ export namespace helios::platform::glfw { */ bool createWindow(UpdateContext& updateContext, const WindowCreateCommand& cmd) noexcept { - assert(gameWorld_ && "GameWorld not initialized"); - auto window = updateContext.find(cmd.windowHandle); if (!window) { @@ -203,9 +201,12 @@ export namespace helios::platform::glfw { window->template add>(); window->template add>(); - window->template add>(GLFWWindowUserPointer(cmd.windowHandle, gameWorld_)); + window->template add>( + GLFWWindowUserPointer( + cmd.windowHandle, commandBufferRegistry_->template item() + )); - installResizeListener(gameWorld_, cmd.windowHandle); + installResizeListener(cmd.windowHandle); return true; } @@ -249,7 +250,7 @@ export namespace helios::platform::glfw { for (auto& handle : currentContexts_) { auto go = updateContext.find (handle); if (go) { - go->remove>(); + go->template remove>(); } } } @@ -260,23 +261,23 @@ export namespace helios::platform::glfw { * @param gameWorld Owning game world used by callback command submission. * @param handle Window handle for which the listener is installed. */ - void installResizeListener(GameWorld* gameWorld, THandle handle) noexcept { + void installResizeListener(THandle handle) noexcept { - auto entity = gameWorld->find(handle); + auto entity = platformWorld_->findEntity(handle); if (!entity) { logger_.warn("Entity was not found"); return; } - const auto* glfw = entity->get>(); + const auto* glfw = entity->template get>(); if (!glfw) { logger_.error("Entity does not have GLFWWindowHandleComponent"); assert(false && "Entity does not have GLFWWindowHandleComponent"); return; } - auto* wuptrComponent = entity->get>(); + auto* wuptrComponent = entity->template get>(); if (!wuptrComponent) { logger_.error("Entity does not have GLFWWindowUserPointerComponent"); assert(false && "Entity does not have GLFWWindowUserPointerComponent"); @@ -289,10 +290,10 @@ export namespace helios::platform::glfw { glfwSetFramebufferSizeCallback( glfw->handle, [] (GLFWwindow* nativeHandle, const int width, const int height) { - const auto* ptr = static_cast*>(glfwGetWindowUserPointer(nativeHandle)); + const auto* ptr = static_cast*>(glfwGetWindowUserPointer(nativeHandle)); - if (ptr && ptr->gameWorld) { - ptr->gameWorld->commandBuffer().add>( + if (ptr && ptr->platformCommandBuffer) { + ptr->platformCommandBuffer->template add>( ptr->windowHandle, WindowSize(width, height) ); @@ -316,7 +317,7 @@ export namespace helios::platform::glfw { return; } - const auto* glfw = entity->get>(); + const auto* glfw = entity->template get>(); if (!glfw) { logger_.error("Entity does not have GLFWWindowHandleComponent"); @@ -374,7 +375,7 @@ export namespace helios::platform::glfw { auto entity = updateContext.find(windowHandle); if (entity) { - if (auto* wc = entity->get>()) { + if (auto* wc = entity->template get>()) { wc->size = windowSize; } } @@ -431,14 +432,14 @@ export namespace helios::platform::glfw { continue; } - const auto* glfw = entity->get>(); + const auto* glfw = entity->template get>(); if (!glfw) { logger_.warn("Entity does not have GLFWWindowHandleComponent"); continue; } glfwDestroyWindow(glfw->handle); - bool destroyed = gameWorld_->destroy(cmd.windowHandle); + bool destroyed = platformWorld_->destroy(cmd.windowHandle); assert(destroyed && "Failed to destroy entity"); } @@ -455,7 +456,7 @@ export namespace helios::platform::glfw { glfwTerminate(); - updateContext.queueCommand>( + commandBufferRegistry_->template item()->template add>( StateTransitionRequest( updateContext.session().state(), GameStateTransitionId::ShutdownRequest @@ -465,7 +466,8 @@ export namespace helios::platform::glfw { } - + CommandBufferRegistry* commandBufferRegistry_ = nullptr; + public: @@ -474,6 +476,9 @@ export namespace helios::platform::glfw { */ using EngineRoleTag = ManagerRole; + explicit GLFWPlatformManager(PlatformWorld& platformWorld, CommandBufferRegistry& commandBufferRegistry) + : platformWorld_(&platformWorld), commandBufferRegistry_(&commandBufferRegistry) {}; + /** * @brief Processes queued platform/window work for the current frame. @@ -487,7 +492,14 @@ export namespace helios::platform::glfw { return; } - initPlatform(updateContext); + if (initPlatform(updateContext)) { + commandBufferRegistry_->template item()->template add>( + StateTransitionRequest( + updateContext.session().state(), + GameStateTransitionId::BootRequest + ) + ); + } pollEvents(updateContext); const bool isContextAvailable = createWindows(updateContext); @@ -596,11 +608,9 @@ export namespace helios::platform::glfw { * * @param gameWorld Runtime world used for command-handler registration. */ - void init(helios::runtime::world::GameWorld& gameWorld) noexcept { - - gameWorld_ = &gameWorld; + void init(CommandHandlerRegistry& commandHandlerRegistry) noexcept { - gameWorld.registerCommandHandler< + commandHandlerRegistry.handleCommands< WindowCreateCommand, PlatformInitCommand, WindowResizeCommand, diff --git a/include/helios/platform/glfw/components/GLFWWindowUserPointerComponent.ixx b/include/helios/platform/glfw/components/GLFWWindowUserPointerComponent.ixx index a1e251568c..451cef2afe 100644 --- a/include/helios/platform/glfw/components/GLFWWindowUserPointerComponent.ixx +++ b/include/helios/platform/glfw/components/GLFWWindowUserPointerComponent.ixx @@ -11,10 +11,12 @@ export module helios.platform.glfw.components.GLFWWindowUserPointerComponent; import helios.runtime.world.GameWorld; import helios.platform.glfw.types.GLFWWindowUserPointer; import helios.platform.window.concepts.IsWindowHandle; +import helios.runtime.messaging.command.concepts.IsPlatformCommandBuffer; using namespace helios::runtime::world; using namespace helios::platform::window::concepts; using namespace helios::platform::glfw::types; +using namespace helios::runtime::messaging::command::concepts; export namespace helios::platform::glfw::components { @@ -23,11 +25,11 @@ export namespace helios::platform::glfw::components { * * @tparam THandle Window handle type. */ - template - requires IsWindowHandle + template + requires IsWindowHandle && IsPlatformCommandBuffer struct GLFWWindowUserPointerComponent { /** @brief Typed payload exposed to GLFW callbacks via `glfwSetWindowUserPointer`. */ - GLFWWindowUserPointer userPointer; + GLFWWindowUserPointer userPointer; }; diff --git a/include/helios/platform/glfw/systems/GLFWWindowCloseSystem.ixx b/include/helios/platform/glfw/systems/GLFWWindowCloseSystem.ixx index 3f5a24ad1f..f0fe739780 100644 --- a/include/helios/platform/glfw/systems/GLFWWindowCloseSystem.ixx +++ b/include/helios/platform/glfw/systems/GLFWWindowCloseSystem.ixx @@ -46,6 +46,8 @@ export namespace helios::platform::glfw::systems { public: + using CommandBuffer_type = TCommandBuffer; + /** * @brief Engine role marker used by runtime registries. */ @@ -56,7 +58,7 @@ export namespace helios::platform::glfw::systems { * * @param updateContext Frame-local update context. */ - void update(UpdateContext& updateContext) noexcept { + void update(UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { for (auto [entity, wc, glfw, wsc, active]: updateContext.view< THandle, @@ -66,7 +68,7 @@ export namespace helios::platform::glfw::systems { Active >().whereEnabled()) { if (glfwWindowShouldClose(glfw->handle)) { - updateContext.queueCommand>( + cmdBuffer.template add>( entity.handle() ); } diff --git a/include/helios/platform/glfw/types/GLFWWindowUserPointer.ixx b/include/helios/platform/glfw/types/GLFWWindowUserPointer.ixx index 0dc86206d2..95bc7a5994 100644 --- a/include/helios/platform/glfw/types/GLFWWindowUserPointer.ixx +++ b/include/helios/platform/glfw/types/GLFWWindowUserPointer.ixx @@ -7,12 +7,14 @@ module; export module helios.platform.glfw.types.GLFWWindowUserPointer; import helios.ecs.types.EntityHandle; -import helios.runtime.world.GameWorld; +import helios.runtime.world.EngineWorld; import helios.platform.window.concepts.IsWindowHandle; +import helios.runtime.messaging.command.concepts; using namespace helios::ecs::types; using namespace helios::runtime::world; using namespace helios::platform::window::concepts; +using namespace helios::runtime::messaging::command::concepts; export namespace helios::platform::glfw::types { /** @@ -20,14 +22,14 @@ export namespace helios::platform::glfw::types { * * @tparam THandle Window handle type. */ - template - requires IsWindowHandle + template + requires IsWindowHandle && IsPlatformCommandBuffer struct GLFWWindowUserPointer { /** @brief Window entity handle associated with the native window. */ THandle windowHandle; - /** @brief Runtime world used by callbacks to enqueue or mutate state. */ - GameWorld* gameWorld = nullptr; + /** @brief Runtime platform world used by callbacks to enqueue or mutate state. */ + TPlatformCommandBuffer* platformCommandBuffer = nullptr; }; } diff --git a/include/helios/platform/lifecycle/systems/PlatformInitSystem.ixx b/include/helios/platform/lifecycle/systems/PlatformInitSystem.ixx index 3cc52286b2..d6ff48b9fa 100644 --- a/include/helios/platform/lifecycle/systems/PlatformInitSystem.ixx +++ b/include/helios/platform/lifecycle/systems/PlatformInitSystem.ixx @@ -38,6 +38,8 @@ export namespace helios::platform::lifecycle::systems { public: + using CommandBuffer_type = TCommandBuffer; + /** * @brief Engine role marker used by runtime system registries. */ @@ -48,10 +50,10 @@ export namespace helios::platform::lifecycle::systems { * * @param updateContext Frame-local update context. */ - void update(UpdateContext& updateContext) noexcept { + void update(UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { if (!updateContext.session().isInitialized()) { - updateContext.queueCommand(); + cmdBuffer.template add(); } } diff --git a/include/helios/platform/lifecycle/systems/WarmupDoneSystem.ixx b/include/helios/platform/lifecycle/systems/WarmupDoneSystem.ixx index 7c597f7505..968fcbd35f 100644 --- a/include/helios/platform/lifecycle/systems/WarmupDoneSystem.ixx +++ b/include/helios/platform/lifecycle/systems/WarmupDoneSystem.ixx @@ -53,6 +53,8 @@ export namespace helios::platform::lifecycle::systems { public: + using CommandBuffer_type = TCommandBuffer; + /** * @brief Engine role marker used by runtime registries. */ @@ -63,7 +65,7 @@ export namespace helios::platform::lifecycle::systems { * * @param updateContext Frame-local update context. */ - void update(UpdateContext& updateContext) noexcept { + void update(UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { if (updateContext.view< THandle, @@ -71,9 +73,7 @@ export namespace helios::platform::lifecycle::systems { Active >().whereEnabled().empty()) { - updateContext.queueCommand< - TCommandBuffer, - StateCommand>( + cmdBuffer.template add>( StateTransitionRequest( updateContext.session().state(), GameStateTransitionId::WarmupDoneSignal diff --git a/include/helios/platform/lifecycle/systems/WindowBasedShutdownSystem.ixx b/include/helios/platform/lifecycle/systems/WindowBasedShutdownSystem.ixx index 1fd5ae778a..de25c97d1f 100644 --- a/include/helios/platform/lifecycle/systems/WindowBasedShutdownSystem.ixx +++ b/include/helios/platform/lifecycle/systems/WindowBasedShutdownSystem.ixx @@ -52,6 +52,8 @@ export namespace helios::platform::lifecycle::systems { public: + using CommandBuffer_type = TCommandBuffer; + /** * @brief Engine role marker used by runtime registries. */ @@ -61,11 +63,12 @@ export namespace helios::platform::lifecycle::systems { * @brief Checks window activity and queues shutdown when the set is empty. * * @param updateContext Frame-local update context. + * @param cmdBuffer Command buffer for submitting shutdown commands. */ - void update(UpdateContext& updateContext) noexcept { + void update(UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { if (updateContext.view, Active>().whereEnabled().empty()) { - updateContext.queueCommand(); + cmdBuffer.template add(); } diff --git a/include/helios/platform/opengl/OpenGLShaderCompileManager.ixx b/include/helios/platform/opengl/OpenGLShaderCompileManager.ixx index 6411b37ba1..ca6745f448 100644 --- a/include/helios/platform/opengl/OpenGLShaderCompileManager.ixx +++ b/include/helios/platform/opengl/OpenGLShaderCompileManager.ixx @@ -30,7 +30,9 @@ import helios.rendering.shader.commands; import helios.platform.opengl.components.OpenGLShaderComponent; import helios.runtime.world.EngineWorld; +import helios.runtime.messaging.command.concepts; import helios.runtime.messaging.command.NullCommandBuffer; +import helios.runtime.messaging.command.CommandHandlerRegistry; import helios.runtime.concepts; using namespace helios::runtime::world; @@ -245,14 +247,11 @@ export namespace helios::platform::opengl { /** * @brief Registers compile command handlers in the runtime world. * - * @param gameWorld Runtime world used for command-handler registration. + * @param commandHandlerRegistry Registry used for command-handler registration. */ - void init(helios::runtime::world::GameWorld& gameWorld) noexcept { - - gameWorld.registerCommandHandler< - ShaderCompileCommand, - ShaderBatchCompileCommand - >(*this); + void init(helios::runtime::messaging::command::CommandHandlerRegistry& commandHandlerRegistry) noexcept { + commandHandlerRegistry.registerHandler>(*this); + commandHandlerRegistry.registerHandler>(*this); } }; diff --git a/include/helios/platform/window/systems/SwapBuffersSystem.ixx b/include/helios/platform/window/systems/SwapBuffersSystem.ixx index 1e9895cc47..0a7072a9a4 100644 --- a/include/helios/platform/window/systems/SwapBuffersSystem.ixx +++ b/include/helios/platform/window/systems/SwapBuffersSystem.ixx @@ -51,6 +51,8 @@ export namespace helios::platform::window::systems { public: + using CommandBuffer_type = TCommandBuffer; + /** * @brief Engine role marker used by runtime registries. */ @@ -61,14 +63,14 @@ export namespace helios::platform::window::systems { * * @param updateContext Frame-local update context. */ - void update(UpdateContext& updateContext) noexcept { + void update(UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { for (auto [entity, wc, wsc, active]: updateContext.view< THandle, WindowComponent, WindowShownComponent, Active >().whereEnabled()) { - updateContext.queueCommand>(entity.handle()); + cmdBuffer.template add>(entity.handle()); } } diff --git a/include/helios/platform/window/systems/WindowCreateSystem.ixx b/include/helios/platform/window/systems/WindowCreateSystem.ixx index 00a1ad9da3..5d55379abc 100644 --- a/include/helios/platform/window/systems/WindowCreateSystem.ixx +++ b/include/helios/platform/window/systems/WindowCreateSystem.ixx @@ -42,6 +42,8 @@ export namespace helios::platform::window::systems { public: + using CommandBuffer_type = TCommandBuffer; + /** * @brief Engine role marker used by runtime registries. */ @@ -52,14 +54,14 @@ export namespace helios::platform::window::systems { * * @param updateContext Frame-local update context. */ - void update(UpdateContext& updateContext) noexcept { + void update(UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { for (auto [entity, win, active]: updateContext.view< THandle, WindowCreateRequestComponent, Active >().whereEnabled()) { - updateContext.queueCommand>( + cmdBuffer.template add>( entity.handle(), win->windowConfig ); diff --git a/include/helios/rendering/framebuffer/Framebuffer.ixx b/include/helios/rendering/framebuffer/Framebuffer.ixx index f0a322ce29..a5b3c2b22d 100644 --- a/include/helios/rendering/framebuffer/Framebuffer.ixx +++ b/include/helios/rendering/framebuffer/Framebuffer.ixx @@ -8,7 +8,6 @@ module; export module helios.rendering.framebuffer.Framebuffer; -import helios.rendering.viewport; export namespace helios::rendering::framebuffer { @@ -31,7 +30,7 @@ export namespace helios::rendering::framebuffer { * * @todo The list should be sorted after a meaningful key, like the viewport's z-Index. */ - std::vector viewports_; + // std::vector viewports_; bool isDirty_ = true; diff --git a/include/helios/rendering/shader/systems/ShaderCompileSystem.ixx b/include/helios/rendering/shader/systems/ShaderCompileSystem.ixx index 311ee86b58..84ee533cea 100644 --- a/include/helios/rendering/shader/systems/ShaderCompileSystem.ixx +++ b/include/helios/rendering/shader/systems/ShaderCompileSystem.ixx @@ -54,6 +54,7 @@ export namespace helios::rendering::shader::systems { public: using EngineRoleTag = SystemRole; + using CommandBuffer_type = TCommandBuffer; explicit ShaderCompileSystem(size_t capacity = TCapacity) : capacity_(capacity) { shaderHandles_.reserve(capacity); @@ -64,7 +65,7 @@ export namespace helios::rendering::shader::systems { * * @param updateContext Frame update context. */ - void update(UpdateContext& updateContext) noexcept { + void update(UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { for (auto [entity, scc, ac] : updateContext.view< THandle, @@ -74,10 +75,7 @@ export namespace helios::rendering::shader::systems { shaderHandles_.push_back(entity.handle()); } - updateContext.queueCommand< - TCommandBuffer, - ShaderBatchCompileCommand>(std::move(shaderHandles_) - ); + cmdBuffer.template add>(std::move(shaderHandles_)); shaderHandles_.clear(); shaderHandles_.reserve(capacity_); diff --git a/include/helios/rendering/viewport/FramebufferFwd.ixx b/include/helios/rendering/viewport/FramebufferFwd.ixx deleted file mode 100644 index 5c3b677aa7..0000000000 --- a/include/helios/rendering/viewport/FramebufferFwd.ixx +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file FramebufferFwd.ixx - * @brief Forward declaration partition for `helios::rendering::framebuffer::Framebuffer`. - * - * @details This partition exists to break include/import coupling between - * viewport-related code and the full framebuffer module implementation. - * It is intentionally lightweight and should only provide type forward - * declarations. - * - * This partition is intentionally **not** part of global aggregate exports - * (for example `helios` or `helios.rendering`) because: - * - it is an internal dependency-management artifact, - * - exporting it globally would leak implementation structure, - * - and consumers that need full framebuffer functionality should import - * `helios.rendering.framebuffer` directly. - */ -module; - -export module helios.rendering.viewport.Viewport:FramebufferFwd; - -export namespace helios::rendering::framebuffer { - - /** - * @brief Forward declaration of the framebuffer runtime type. - */ - class Framebuffer; -} \ No newline at end of file diff --git a/include/helios/rendering/viewport/Viewport.ixx b/include/helios/rendering/viewport/Viewport.ixx index 81d26e0ad2..e231dca858 100644 --- a/include/helios/rendering/viewport/Viewport.ixx +++ b/include/helios/rendering/viewport/Viewport.ixx @@ -13,36 +13,19 @@ module; export module helios.rendering.viewport.Viewport; import helios.rendering.viewport.ViewportSnapshot; +import helios.rendering.framebuffer.Framebuffer; import helios.rendering.ClearFlags; import helios.core.types; import helios.rendering.viewport.types.ViewportId; import helios.math.types; import helios.scene.CameraSceneNode; -import :FramebufferFwd; import helios.util.log.LogManager; import helios.util.log.Logger; #define HELIOS_LOG_SCOPE "helios::rendering::viewport::Viewport" export namespace helios::rendering::viewport { - - /** - * @brief A passkey used to establish a parent-child relationship between a Framebuffer and a Viewport. - * - * This struct uses the passkey idiom to restrict the calling of `Viewport::setFramebuffer` to - * friend classes (specifically `helios::rendering::Framebuffer`), ensuring that the ownership - * hierarchy is managed correctly. - * - * @see `Viewport::setFramebuffer` - * @see https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2893r3.html#passkey-idiom - */ - struct ViewportKey { - private: - friend class helios::rendering::framebuffer::Framebuffer; - ViewportKey() = default; - }; - /** * @brief Represents a rectangular area within a Framebuffer where a scene is rendered. * @@ -267,7 +250,7 @@ export namespace helios::rendering::viewport { * * @todo The Viewport should observe the Framebuffer for state changes (e.g., resize). */ - Viewport& setFramebuffer(const helios::rendering::framebuffer::Framebuffer* framebuffer, ViewportKey key) noexcept { + Viewport& setFramebuffer(const helios::rendering::framebuffer::Framebuffer* framebuffer) noexcept { framebuffer_ = framebuffer; return *this; } diff --git a/include/helios/runtime/_module.ixx b/include/helios/runtime/_module.ixx index 2030f745d9..a2d76e1b4d 100644 --- a/include/helios/runtime/_module.ixx +++ b/include/helios/runtime/_module.ixx @@ -9,3 +9,4 @@ export import helios.runtime.world; export import helios.runtime.pooling; export import helios.runtime.messaging; export import helios.runtime.concepts; +export import helios.runtime.timing; diff --git a/include/helios/runtime/concepts/_module.ixx b/include/helios/runtime/concepts/_module.ixx index f1e466b1c3..293ce19a96 100644 --- a/include/helios/runtime/concepts/_module.ixx +++ b/include/helios/runtime/concepts/_module.ixx @@ -6,14 +6,7 @@ */ export module helios.runtime.concepts; -// Local concepts (runtime/concepts/) export import helios.runtime.concepts.HasTag; export import helios.runtime.concepts.HasReset; export import helios.runtime.concepts.HasClear; export import helios.runtime.concepts.HasSubmit; - -// World concepts (runtime/world/concepts/) -export import helios.runtime.world.concepts; - -// Messaging command concepts (runtime/messaging/command/concepts/) -export import helios.runtime.messaging.command.concepts; diff --git a/include/helios/runtime/gameloop/CommitPoint.ixx b/include/helios/runtime/gameloop/CommitPoint.ixx index 695f6f30e1..9e7524b517 100644 --- a/include/helios/runtime/gameloop/CommitPoint.ixx +++ b/include/helios/runtime/gameloop/CommitPoint.ixx @@ -7,7 +7,7 @@ module; #include #include -export module helios.runtime.gameloop.CommitPoint; +export module helios.runtime.gameloop:CommitPoint; export namespace helios::runtime::gameloop { diff --git a/include/helios/runtime/gameloop/GameLoop.ixx b/include/helios/runtime/gameloop/GameLoop.ixx index 4881f96c4f..919a4abd6f 100644 --- a/include/helios/runtime/gameloop/GameLoop.ixx +++ b/include/helios/runtime/gameloop/GameLoop.ixx @@ -9,7 +9,7 @@ module; #include #include -export module helios.runtime.gameloop.GameLoop; +export module helios.runtime.gameloop:GameLoop; import helios.runtime.world.GameWorld; @@ -29,16 +29,15 @@ import helios.state.Bindings; import helios.gameplay.gamestate.types; import helios.gameplay.matchstate.types; -import helios.runtime.gameloop.CommitPoint; -import helios.runtime.gameloop.Phase; +import :CommitPoint; +import :Phase; +import :PassCommitListener; import helios.runtime.world.Manager; import helios.input.InputSnapshot; import helios.rendering.viewport.ViewportSnapshot; -import helios.runtime.gameloop.PassCommitListener; - import helios.runtime.world.GameWorld; using namespace helios::runtime::world; @@ -378,7 +377,6 @@ export namespace helios::runtime::gameloop { totalTime_ += deltaTime; auto updateContext = UpdateContext( - gameWorld.resourceRegistry(), gameWorld.session(), gameWorld.runtimeEnvironment(), deltaTime, diff --git a/include/helios/runtime/gameloop/Pass.ixx b/include/helios/runtime/gameloop/Pass.ixx index bc9811cf04..f31cc0b47f 100644 --- a/include/helios/runtime/gameloop/Pass.ixx +++ b/include/helios/runtime/gameloop/Pass.ixx @@ -9,14 +9,14 @@ module; #include #include -export module helios.runtime.gameloop.Pass; +export module helios.runtime.gameloop:Pass; -import helios.runtime.gameloop.CommitPoint; +import :CommitPoint; import helios.runtime.world.GameWorld; import helios.runtime.world.SystemRegistry; import helios.runtime.world.System; -import helios.runtime.concepts; +import helios.runtime.world.concepts; import helios.runtime.world.UpdateContext; diff --git a/include/helios/runtime/gameloop/PassCommitListener.ixx b/include/helios/runtime/gameloop/PassCommitListener.ixx index 50ec12a0e2..a93a6b4329 100644 --- a/include/helios/runtime/gameloop/PassCommitListener.ixx +++ b/include/helios/runtime/gameloop/PassCommitListener.ixx @@ -5,9 +5,9 @@ module; -export module helios.runtime.gameloop.PassCommitListener; +export module helios.runtime.gameloop:PassCommitListener; -import helios.runtime.gameloop.CommitPoint; +import :CommitPoint; import helios.runtime.world.GameWorld; import helios.runtime.world.UpdateContext; diff --git a/include/helios/runtime/gameloop/Phase.ixx b/include/helios/runtime/gameloop/Phase.ixx index 7f7f027663..ebf90947e9 100644 --- a/include/helios/runtime/gameloop/Phase.ixx +++ b/include/helios/runtime/gameloop/Phase.ixx @@ -7,18 +7,18 @@ module; #include #include -export module helios.runtime.gameloop.Phase; +export module helios.runtime.gameloop:Phase; -import helios.runtime.gameloop.PassCommitListener; -import helios.runtime.gameloop.Pass; -import helios.runtime.gameloop.TypedPass; +import :PassCommitListener; +import :Pass; +import :TypedPass; +import :CommitPoint; import helios.runtime.world.UpdateContext; import helios.runtime.world.GameWorld; import helios.runtime.world.Session; -import helios.runtime.gameloop.CommitPoint; import helios.gameplay.gamestate.types; diff --git a/include/helios/runtime/gameloop/TypedPass.ixx b/include/helios/runtime/gameloop/TypedPass.ixx index e9314cf209..0089e77cfb 100644 --- a/include/helios/runtime/gameloop/TypedPass.ixx +++ b/include/helios/runtime/gameloop/TypedPass.ixx @@ -7,16 +7,13 @@ module; #include #include -export module helios.runtime.gameloop.TypedPass; +export module helios.runtime.gameloop:TypedPass; -import helios.runtime.gameloop.CommitPoint; - -import helios.runtime.gameloop.Pass; +import :CommitPoint; +import :Pass; import helios.runtime.world.SystemRegistry; - -import helios.runtime.world.GameWorldFwd; import helios.runtime.world.UpdateContext; import helios.runtime.world.Session; @@ -87,9 +84,7 @@ export namespace helios::runtime::gameloop { * @param gameWorld Reference to the game world. */ void init(helios::runtime::world::GameWorld& gameWorld) override { - for (auto& sys : systemRegistry_.items()) { - sys->init(gameWorld); - } + } diff --git a/include/helios/runtime/gameloop/_module.ixx b/include/helios/runtime/gameloop/_module.ixx index b663e349b7..584e6ee8fa 100644 --- a/include/helios/runtime/gameloop/_module.ixx +++ b/include/helios/runtime/gameloop/_module.ixx @@ -4,8 +4,8 @@ */ export module helios.runtime.gameloop; -export import helios.runtime.gameloop.CommitPoint; -export import helios.runtime.gameloop.GameLoop; -export import helios.runtime.gameloop.Phase; -export import helios.runtime.gameloop.Pass; +export import :CommitPoint; +export import :GameLoop; +export import :Phase; +export import :Pass; diff --git a/include/helios/runtime/messaging/command/CommandBuffer.ixx b/include/helios/runtime/messaging/command/CommandBuffer.ixx index fcc0f1042b..93a3be40e8 100644 --- a/include/helios/runtime/messaging/command/CommandBuffer.ixx +++ b/include/helios/runtime/messaging/command/CommandBuffer.ixx @@ -9,12 +9,14 @@ module; export module helios.runtime.messaging.command.CommandBuffer; - -import helios.runtime.world.GameWorldFwd; -import helios.runtime.world.UpdateContextFwd; +import helios.runtime.world.UpdateContext; import helios.runtime.messaging.command.concepts.IsCommandBufferLike; +import helios.runtime.timing.TimerManager; +import helios.runtime.messaging.command.CommandHandlerRegistry; + +using namespace helios::runtime::timing; using namespace helios::runtime::messaging::command::concepts; using namespace helios::runtime::world; export namespace helios::runtime::messaging::command { @@ -55,8 +57,9 @@ export namespace helios::runtime::messaging::command { class Concept { public: virtual ~Concept() = default; - virtual void flush(GameWorld& gameWorld, UpdateContext& updateContext) noexcept = 0; + virtual void flush(UpdateContext& updateContext) noexcept = 0; virtual void clear() noexcept = 0; + virtual void init(CommandHandlerRegistry& commandHandlerRegistry, TimerManager& timerManager) noexcept = 0; [[nodiscard]] virtual void* underlying() noexcept = 0; [[nodiscard]] virtual const void* underlying() const noexcept = 0; @@ -79,8 +82,12 @@ export namespace helios::runtime::messaging::command { explicit Model(T cmdBuffer) : cmdBuffer_(std::move(cmdBuffer)) {} - void flush(GameWorld& gameWorld, UpdateContext& updateContext) noexcept override { - cmdBuffer_.flush(gameWorld, updateContext); + void flush(UpdateContext& updateContext) noexcept override { + cmdBuffer_.flush(updateContext); + } + + void init(CommandHandlerRegistry& commandHandlerRegistry, TimerManager& timerManager) noexcept override { + cmdBuffer_.init(commandHandlerRegistry, timerManager); } void clear() noexcept override { @@ -137,9 +144,9 @@ export namespace helios::runtime::messaging::command { * * @pre The CommandBuffer must be initialized (not default-constructed). */ - void flush(GameWorld& gameWorld, UpdateContext& updateContext) noexcept { + void flush(UpdateContext& updateContext) noexcept { assert(pimpl_ && "CommandBuffer not initialized"); - pimpl_->flush(gameWorld, updateContext); + pimpl_->flush(updateContext); } /** @@ -152,6 +159,11 @@ export namespace helios::runtime::messaging::command { pimpl_->clear(); } + void init(CommandHandlerRegistry& commandHandlerRegistry, TimerManager& timerManager) noexcept { + assert(pimpl_ && "CommandBuffer not initialized"); + pimpl_->init(commandHandlerRegistry, timerManager); + } + /** * @brief Returns a type-erased pointer to the owned buffer instance. * diff --git a/include/helios/runtime/messaging/command/CommandHandlerRegistry.ixx b/include/helios/runtime/messaging/command/CommandHandlerRegistry.ixx index 24d14906b1..c8561bcc00 100644 --- a/include/helios/runtime/messaging/command/CommandHandlerRegistry.ixx +++ b/include/helios/runtime/messaging/command/CommandHandlerRegistry.ixx @@ -132,6 +132,11 @@ export namespace helios::runtime::messaging::command { }; } + template + void handleCommands(OwningT& owner) { + (registerHandler(owner), ...); + } + /** * @brief Checks if a handler is registered for the specified command type. * diff --git a/include/helios/runtime/messaging/command/EngineCommandBuffer.ixx b/include/helios/runtime/messaging/command/EngineCommandBuffer.ixx index e96120283f..4e86bb4d9b 100644 --- a/include/helios/runtime/messaging/command/EngineCommandBuffer.ixx +++ b/include/helios/runtime/messaging/command/EngineCommandBuffer.ixx @@ -20,20 +20,16 @@ import helios.platform.lifecycle.commands; import helios.runtime.messaging.command.TypedCommandBuffer; import helios.ui.widgets.commands; -import helios.gameplay.timing.commands; +import helios.runtime.timing.commands; import helios.gameplay.combat.commands; import helios.gameplay.damage.commands; import helios.gameplay.scoring.commands; import helios.physics.motion.commands; import helios.gameplay.lifecycle.commands.WorldLifecycleCommand; -import helios.rendering.shader.commands; -import helios.platform.window.types.WindowHandle; import helios.runtime.world.types; import helios.runtime.messaging.command.tags.CommandBufferRole; -using namespace helios::platform::environment::types; -using namespace helios::platform::window::types; using namespace helios::runtime::world; using namespace helios::runtime::world::types; export namespace helios::runtime::messaging::command { @@ -45,7 +41,7 @@ export namespace helios::runtime::messaging::command { helios::ui::widgets::commands::UiActionCommand, helios::gameplay::scoring::commands::UpdateScoreCommand, - helios::gameplay::timing::commands::TimerControlCommand, + helios::runtime::timing::commands::TimerControlCommand, helios::gameplay::lifecycle::commands::WorldLifecycleCommand, helios::gameplay::combat::commands::Aim2DCommand, helios::gameplay::combat::commands::ShootCommand, @@ -53,19 +49,8 @@ export namespace helios::runtime::messaging::command { helios::gameplay::spawn::commands::ScheduledSpawnPlanCommand, helios::gameplay::spawn::commands::SpawnCommand, - helios::gameplay::spawn::commands::DespawnCommand, + helios::gameplay::spawn::commands::DespawnCommand - - // window - helios::platform::window::commands::WindowCreateCommand, - helios::platform::window::commands::WindowResizeCommand, - helios::platform::window::commands::SwapBuffersCommand, - helios::platform::window::commands::WindowCloseCommand, - - // runtime platform - helios::platform::lifecycle::commands::PlatformInitCommand, - helios::platform::environment::commands::PollEventsCommand, - helios::platform::lifecycle::commands::ShutdownCommand >; } diff --git a/include/helios/runtime/messaging/command/NullCommandBuffer.ixx b/include/helios/runtime/messaging/command/NullCommandBuffer.ixx index 11e1969e68..05e8cb6c0c 100644 --- a/include/helios/runtime/messaging/command/NullCommandBuffer.ixx +++ b/include/helios/runtime/messaging/command/NullCommandBuffer.ixx @@ -7,8 +7,7 @@ module; export module helios.runtime.messaging.command.NullCommandBuffer; import helios.runtime.messaging.command.tags.CommandBufferRole; -import helios.runtime.world.GameWorldFwd; -import helios.runtime.world.UpdateContextFwd; +import helios.runtime.world.UpdateContext; using namespace helios::runtime::tags; using namespace helios::runtime::world; @@ -23,12 +22,12 @@ export namespace helios::runtime::messaging::command { public: - using EngineRoleTag = helios::runtime::tags::CommandBufferRole; + using EngineRoleTag = CommandBufferRole; template void add(Args&&... args) {/*intentionally noop*/} - void flush(GameWorld& gameWorld, UpdateContext& updateContext) noexcept {/*intentionally noop*/} + void flush(UpdateContext& updateContext) noexcept {/*intentionally noop*/} void clear() noexcept {/*intentionally noop*/} }; diff --git a/include/helios/runtime/messaging/command/PlatformCommandBuffer.ixx b/include/helios/runtime/messaging/command/PlatformCommandBuffer.ixx new file mode 100644 index 0000000000..98ae95ef0b --- /dev/null +++ b/include/helios/runtime/messaging/command/PlatformCommandBuffer.ixx @@ -0,0 +1,47 @@ +/** + * @file PlatformCommandBuffer.ixx + * @brief Typed command buffer for platform/window command dispatch. + */ +module; + +export module helios.runtime.messaging.command.PlatformCommandBuffer; + + + +import helios.platform.environment.types; +import helios.platform.environment.commands; +import helios.platform.window.commands; + +import helios.platform.lifecycle.commands; + +import helios.runtime.messaging.command.TypedCommandBuffer; + +import helios.platform.window.types.WindowHandle; + +using namespace helios::platform::environment::types; +using namespace helios::platform::window::types; +export namespace helios::runtime::messaging::command { + + /** + * @brief Platform-focused `TypedCommandBuffer` specialization. + * + * @details Bundles window lifecycle commands (create/resize/swap/close) + * and runtime platform commands (init/poll/shutdown) into a single + * concrete command buffer type. + */ + using PlatformCommandBuffer = helios::runtime::messaging::command::TypedCommandBuffer< + + // window + helios::platform::window::commands::WindowCreateCommand, + helios::platform::window::commands::WindowResizeCommand, + helios::platform::window::commands::SwapBuffersCommand, + helios::platform::window::commands::WindowCloseCommand, + + // runtime platform + helios::platform::lifecycle::commands::PlatformInitCommand, + helios::platform::environment::commands::PollEventsCommand, + helios::platform::lifecycle::commands::ShutdownCommand + >; + +} + diff --git a/include/helios/runtime/messaging/command/TypedCommandBuffer.ixx b/include/helios/runtime/messaging/command/TypedCommandBuffer.ixx index a330b30216..7c6ad2b672 100644 --- a/include/helios/runtime/messaging/command/TypedCommandBuffer.ixx +++ b/include/helios/runtime/messaging/command/TypedCommandBuffer.ixx @@ -4,7 +4,7 @@ */ module; - +#include "helios/helios_config.h" #include #include #include @@ -16,20 +16,21 @@ export module helios.runtime.messaging.command.TypedCommandBuffer; import helios.state.components; -import helios.runtime.world.GameWorld; import helios.runtime.world.UpdateContext; +import helios.runtime.messaging.command.CommandHandlerRegistry; import helios.state.commands.DelayedStateCommand; -import helios.gameplay.timing.TimerManager; -import helios.gameplay.timing.types; +import helios.runtime.timing.TimerManager; +import helios.runtime.timing.types; + +import helios.runtime.messaging.command.tags.CommandBufferRole; using namespace helios::runtime::world; using namespace helios::state::commands; -using namespace helios::gameplay::timing; -using namespace helios::gameplay::timing::types; +using namespace helios::runtime::timing; +using namespace helios::runtime::timing::types; -import helios.runtime.messaging.command.tags.CommandBufferRole; export namespace helios::runtime::messaging::command { /** @@ -50,7 +51,7 @@ export namespace helios::runtime::messaging::command { * @brief Concept constraining commands that carry a timer gate. * * @details A command satisfies DelayedCommandLike if it provides a - * noexcept `gameTimerId()` accessor returning a GameTimerId. Such + * noexcept `timerId()` accessor returning a TimerId. Such * commands are held in a scratch queue until their associated timer * reaches `TimerState::Finished`. * @@ -58,7 +59,7 @@ export namespace helios::runtime::messaging::command { */ template concept DelayedCommandLike = requires(Cmd const& c) { - {c.gameTimerId() } noexcept; + {c.timerId() } noexcept; }; /** @@ -92,6 +93,10 @@ export namespace helios::runtime::messaging::command { template class TypedCommandBuffer { + TimerManager* timerManager_; + + CommandHandlerRegistry* commandHandlerRegistry_; + /** * @brief Per-type command queues stored as a tuple of vectors. */ @@ -185,8 +190,8 @@ export namespace helios::runtime::messaging::command { * In both branches, if `CommandType` satisfies `DelayedCommandLike`, * an additional timer check is performed per command: * - * 1. The associated `GameTimer` is looked up via the command's - * `gameTimerId()`. + * 1. The associated `Timer` is looked up via the command's + * `timerId()`. * 2. If the timer is still running (`shouldDelayCommand` returns * true), the command is moved into the scratch queue and * survives the current flush cycle. @@ -206,9 +211,9 @@ export namespace helios::runtime::messaging::command { * @param updateContext The current frame's update context. */ template - void flushCommandQueue(GameWorld& gameWorld, UpdateContext& updateContext) noexcept { + void flushCommandQueue(UpdateContext& updateContext) noexcept { - auto& timerManager = gameWorld.manager(); + //auto& timerManager = gameWorld.manager(); auto& queue = commandQueue(); auto& delayed = delayedQueue(); @@ -218,28 +223,26 @@ export namespace helios::runtime::messaging::command { return; } - auto& commandHandlerRegistry = gameWorld.commandHandlerRegistry(); - - if (commandHandlerRegistry.has()) { + if (commandHandlerRegistry_->has()) { for (auto& cmd : queue) { if constexpr (DelayedCommandLike) { - auto* gameTimer = timerManager.gameTimer(cmd.gameTimerId()); - if (!gameTimer) { - assert(gameTimer && "Unexpected null game timer"); - commandHandlerRegistry.submit(cmd); + auto* timer = timerManager_->getTimer(cmd.timerId()); + if (!timer) { + assert(timer && "Unexpected null game timer"); + commandHandlerRegistry_->submit(cmd); continue; } - if (shouldDelayCommand(gameTimer->state())) { + if (shouldDelayCommand(timer->state())) { delayed.push_back(std::move(cmd)); - } else if (isDelayedCommandReady(gameTimer->state())) { - commandHandlerRegistry.submit(cmd); - } else if (shouldDiscardCommand(gameTimer->state())) { + } else if (isDelayedCommandReady(timer->state())) { + commandHandlerRegistry_->submit(cmd); + } else if (shouldDiscardCommand(timer->state())) { // cancelled? Discard! intentionally noop } } else { - commandHandlerRegistry.submit(cmd); + commandHandlerRegistry_->submit(cmd); } } @@ -249,18 +252,18 @@ export namespace helios::runtime::messaging::command { for (auto& cmd : queue) { if constexpr (DelayedCommandLike) { - auto* gameTimer = timerManager.gameTimer(cmd.gameTimerId()); - if (!gameTimer) { - assert(gameTimer && "Unexpected null game timer"); + auto* timer = timerManager_->getTimer(cmd.timerId()); + if (!timer) { + assert(timer && "Unexpected null game timer"); cmd.execute(updateContext); continue; } - if (shouldDelayCommand(gameTimer->state())) { + if (shouldDelayCommand(timer->state())) { delayed.push_back(std::move(cmd)); - } else if (isDelayedCommandReady(gameTimer->state())) { + } else if (isDelayedCommandReady(timer->state())) { cmd.execute(updateContext); - } else if (shouldDiscardCommand(gameTimer->state())) { + } else if (shouldDiscardCommand(timer->state())) { // cancelled? Discard! intentionally noop } } else { @@ -270,7 +273,7 @@ export namespace helios::runtime::messaging::command { } } else { - std::cerr << "Command type is not executable" << __FUNCSIG__ << std::endl; + std::cerr << "Command type is not executable" << HELIOS_FUNCTION_SIGNATURE << std::endl; assert(false && "Command type is not executable"); } @@ -299,6 +302,11 @@ export namespace helios::runtime::messaging::command { queue.emplace_back(std::forward(args)...); } + void init(CommandHandlerRegistry& commandHandlerRegistry, TimerManager& timerManager) noexcept { + commandHandlerRegistry_ = &commandHandlerRegistry; + timerManager_ = &timerManager; + } + /** * @brief Discards all queued commands without executing them. */ @@ -315,8 +323,8 @@ export namespace helios::runtime::messaging::command { * @param gameWorld The game world for which the queue should be flushed. * @param updateContext The current frame's update context. */ - void flush(GameWorld& gameWorld, UpdateContext& updateContext) noexcept { - (flushCommandQueue(gameWorld, updateContext), ...); + void flush(UpdateContext& updateContext) noexcept { + (flushCommandQueue(updateContext), ...); } diff --git a/include/helios/runtime/messaging/command/_module.ixx b/include/helios/runtime/messaging/command/_module.ixx index 5cfd92f244..358f6e1bf0 100644 --- a/include/helios/runtime/messaging/command/_module.ixx +++ b/include/helios/runtime/messaging/command/_module.ixx @@ -11,6 +11,7 @@ export import helios.runtime.messaging.command.tags; export import helios.runtime.messaging.command.EngineCommandBuffer; export import helios.runtime.messaging.command.StateCommandBuffer; export import helios.runtime.messaging.command.RenderCommandBuffer; +export import helios.runtime.messaging.command.PlatformCommandBuffer; export import helios.runtime.messaging.command.NullCommandBuffer; export import helios.runtime.messaging.command.TypedCommandBuffer; export import helios.runtime.messaging.command.CommandBuffer; diff --git a/include/helios/runtime/messaging/command/concepts/IsCommandBufferLike.ixx b/include/helios/runtime/messaging/command/concepts/IsCommandBufferLike.ixx index 29a404e025..10130bd34b 100644 --- a/include/helios/runtime/messaging/command/concepts/IsCommandBufferLike.ixx +++ b/include/helios/runtime/messaging/command/concepts/IsCommandBufferLike.ixx @@ -11,9 +11,7 @@ export module helios.runtime.messaging.command.concepts.IsCommandBufferLike; import helios.runtime.concepts.HasTag; import helios.runtime.concepts.HasClear; - -import helios.runtime.world.GameWorldFwd; -import helios.runtime.world.UpdateContextFwd; +import helios.runtime.world.UpdateContext; import helios.runtime.messaging.command.tags.CommandBufferRole; @@ -37,7 +35,7 @@ export namespace helios::runtime::messaging::command::concepts { * @see HasClear */ template - concept IsCommandBufferLike = requires(T& t, GameWorld& gameWorld, UpdateContext& updateContext) { - {t.flush(gameWorld, updateContext) } -> std::same_as; + concept IsCommandBufferLike = requires(T& t, UpdateContext& updateContext) { + {t.flush(updateContext) } -> std::same_as; } && HasClear && HasTag; } diff --git a/include/helios/runtime/messaging/command/concepts/IsPlatformCommandBuffer.ixx b/include/helios/runtime/messaging/command/concepts/IsPlatformCommandBuffer.ixx new file mode 100644 index 0000000000..8c913979b7 --- /dev/null +++ b/include/helios/runtime/messaging/command/concepts/IsPlatformCommandBuffer.ixx @@ -0,0 +1,31 @@ +/** + * @file IsCommandBufferLike.ixx + * @brief Concept constraining types usable as command buffers. + */ +module; + +#include + +export module helios.runtime.messaging.command.concepts.IsPlatformCommandBuffer; + +import helios.runtime.messaging.command.PlatformCommandBuffer; + +using namespace helios::runtime::messaging::command; +export namespace helios::runtime::messaging::command::concepts { + + /** + * @brief Constrains types that can serve as concrete command buffers. + * + * @details Requires `flush(GameWorld&, UpdateContext&) noexcept` and + * `clear() noexcept` (via HasClear). Types satisfying this concept + * can be wrapped by the type-erased CommandBuffer wrapper. + * + * @tparam T The type to constrain. + * + * @see CommandBuffer + * @see CommandBufferRole + * @see HasClear + */ + template + concept IsPlatformCommandBuffer = std::same_as; +} diff --git a/include/helios/runtime/messaging/command/concepts/_module.ixx b/include/helios/runtime/messaging/command/concepts/_module.ixx index a2a1be50b7..627d92b765 100644 --- a/include/helios/runtime/messaging/command/concepts/_module.ixx +++ b/include/helios/runtime/messaging/command/concepts/_module.ixx @@ -5,5 +5,6 @@ export module helios.runtime.messaging.command.concepts; export import helios.runtime.messaging.command.concepts.IsCommandBufferLike; +export import helios.runtime.messaging.command.concepts.IsPlatformCommandBuffer; export import helios.runtime.messaging.command.concepts.IsCommandHandlerLike; diff --git a/include/helios/runtime/pooling/EntityPoolManager.ixx b/include/helios/runtime/pooling/EntityPoolManager.ixx index 0b09ebcc19..97d3f5a3bd 100644 --- a/include/helios/runtime/pooling/EntityPoolManager.ixx +++ b/include/helios/runtime/pooling/EntityPoolManager.ixx @@ -19,19 +19,23 @@ import helios.runtime.pooling.types.EntityPoolId; import helios.runtime.world.UpdateContext; -import helios.runtime.world.GameWorld; +import helios.runtime.world.EngineWorld; import helios.runtime.pooling.EntityPool; import helios.runtime.pooling.EntityPoolRegistry; import helios.runtime.pooling.EntityPoolConfig; import helios.runtime.pooling.components.PrefabIdComponent; +import helios.runtime.messaging.command.CommandHandlerRegistry; + + import helios.ecs.types.EntityHandle; import helios.core.types; import helios.runtime.world.tags; import helios.runtime.pooling.EntityPoolSnapshot; +using namespace helios::runtime::messaging::command; export namespace helios::runtime::pooling { /** @@ -107,7 +111,7 @@ export namespace helios::runtime::pooling { class EntityPoolManager { using Handle_type = typename TEntity::Handle_type; - using EntityManager_type = TEntity::EntityManager_type; + using Entity_type = TEntity; /** * @brief Registry of EntityPools for entity recycling. @@ -123,7 +127,7 @@ export namespace helios::runtime::pooling { * @details Set during `init()`. Used for cloning prefabs and looking up * Entities by their EntityHandle. */ - helios::runtime::world::GameWorld* gameWorld_ = nullptr; + helios::runtime::world::EngineWorld* engineWorld_ = nullptr; /** * @brief Pending pool configurations awaiting initialization. @@ -167,7 +171,7 @@ export namespace helios::runtime::pooling { const size_t space = used < entityPool->size() ? entityPool->size() - used : 0; for (size_t i = 0; i < space; i++) { - EntityManager_type go = gameWorld_->clone(entityPrefab); + Entity_type go = engineWorld_->clone(entityPrefab.handle()); go.setActive(false); go.onRelease(); entityPool->addInactive(go.handle()); @@ -179,6 +183,9 @@ export namespace helios::runtime::pooling { public: using EngineRoleTag = helios::runtime::tags::ManagerRole; + + explicit EntityPoolManager(helios::runtime::world::EngineWorld& engineWorld) : engineWorld_(&engineWorld) {} + /** * @brief Registers a pool configuration for later initialization. * @@ -241,7 +248,7 @@ export namespace helios::runtime::pooling { ) { auto* entityPool = pool(entityPoolId); - auto worldGo = gameWorld_->findEntity(entityHandle); + auto worldGo = engineWorld_->find(entityHandle); if (worldGo) { if (entityPool->release(entityHandle)) { @@ -277,7 +284,7 @@ export namespace helios::runtime::pooling { while (entityPool->acquire(entityHandle)) { - auto worldGo = gameWorld_->find(entityHandle); + auto worldGo = engineWorld_->find(entityHandle); if (worldGo) { worldGo->onAcquire(); @@ -302,9 +309,8 @@ export namespace helios::runtime::pooling { * * @param gameWorld The GameWorld to associate with this manager. */ - void init(helios::runtime::world::GameWorld& gameWorld) { + void init(CommandHandlerRegistry& commandHandlerRegistry) { - gameWorld_ = &gameWorld; for (const auto& [entityPoolId, poolConfig] : poolConfigs_) { @@ -313,7 +319,7 @@ export namespace helios::runtime::pooling { pools_.addPool(entityPoolId, std::move(pool)); - for (auto [entity, pic] : gameWorld.view< + for (auto [entity, pic] : engineWorld_->view< Handle_type, helios::runtime::pooling::components::PrefabIdComponent>().whereEnabled()) { if (pic->prefabId() == poolConfig->prefabId) { diff --git a/include/helios/gameplay/timing/README.md b/include/helios/runtime/timing/README.md similarity index 100% rename from include/helios/gameplay/timing/README.md rename to include/helios/runtime/timing/README.md diff --git a/include/helios/gameplay/timing/GameTimer.ixx b/include/helios/runtime/timing/Timer.ixx similarity index 82% rename from include/helios/gameplay/timing/GameTimer.ixx rename to include/helios/runtime/timing/Timer.ixx index bde16a84af..2a56452a79 100644 --- a/include/helios/gameplay/timing/GameTimer.ixx +++ b/include/helios/runtime/timing/Timer.ixx @@ -1,30 +1,30 @@ /** - * @file GameTimer.ixx + * @file Timer.ixx * @brief A game timer that tracks elapsed time and supports state transitions. */ module; -export module helios.gameplay.timing.GameTimer; +export module helios.runtime.timing.Timer; -import helios.gameplay.timing.types.GameTimerId; +import helios.runtime.timing.types.TimerId; -import helios.gameplay.timing.types; +import helios.runtime.timing.types; -using namespace helios::gameplay::timing::types; -using namespace helios::gameplay::timing::types; +using namespace helios::runtime::timing::types; +using namespace helios::runtime::timing::types; -export namespace helios::gameplay::timing { +export namespace helios::runtime::timing { /** - * @brief A game timer identified by a GameTimerId. + * @brief A game timer identified by a TimerId. * * Tracks elapsed time while in the Running state. Each update increments * a revision counter that observers can use to detect changes. * * @see TimerManager - * @see GameTimerBindingComponent + * @see TimerBindingComponent */ - class GameTimer { + class Timer { /** * @brief Monotonically increasing revision counter, incremented on each update. @@ -34,7 +34,7 @@ export namespace helios::gameplay::timing { /** * @brief Unique identifier for this timer. */ - GameTimerId gameTimerId_; + TimerId timerId_; /** * @brief Accumulated elapsed time in seconds. @@ -54,20 +54,20 @@ export namespace helios::gameplay::timing { public: /** - * @brief Constructs a GameTimer with the given identifier. + * @brief Constructs a Timer with the given identifier. * - * @param gameTimerId The unique identifier for this timer. + * @param timerId The unique identifier for this timer. */ - explicit GameTimer(const helios::gameplay::timing::types::GameTimerId gameTimerId) - : gameTimerId_{gameTimerId} {} + explicit Timer(const helios::runtime::timing::types::TimerId timerId) + : timerId_{timerId} {} /** * @brief Returns the timer identifier. * - * @return The GameTimerId assigned to this timer. + * @return The TimerId assigned to this timer. */ - [[nodiscard]] helios::gameplay::timing::types::GameTimerId gameTimerId() const noexcept { - return gameTimerId_; + [[nodiscard]] helios::runtime::timing::types::TimerId timerId() const noexcept { + return timerId_; } /** diff --git a/include/helios/gameplay/timing/TimerManager.ixx b/include/helios/runtime/timing/TimerManager.ixx similarity index 61% rename from include/helios/gameplay/timing/TimerManager.ixx rename to include/helios/runtime/timing/TimerManager.ixx index 0de429cdfc..eb077d7910 100644 --- a/include/helios/gameplay/timing/TimerManager.ixx +++ b/include/helios/runtime/timing/TimerManager.ixx @@ -11,28 +11,27 @@ module; #include #include -export module helios.gameplay.timing.TimerManager; +export module helios.runtime.timing.TimerManager; -import helios.gameplay.timing.types; -import helios.gameplay.timing.commands; +import helios.runtime.timing.types; +import helios.runtime.timing.commands; -import helios.gameplay.timing.GameTimer; +import helios.runtime.timing.Timer; -import helios.gameplay.timing.types.GameTimerId; +import helios.runtime.timing.types.TimerId; import helios.runtime.world.UpdateContext; - -import helios.runtime.world.GameWorld; +import helios.runtime.messaging.command.CommandHandlerRegistry; import helios.core.types; import helios.util.Guid; import helios.runtime.world.tags.ManagerRole; -using namespace helios::gameplay::timing::commands; -using namespace helios::gameplay::timing::types; -using namespace helios::gameplay::timing::types; +using namespace helios::runtime::timing::commands; +using namespace helios::runtime::timing::types; +using namespace helios::runtime::timing::types; using namespace helios::runtime::world; -export namespace helios::gameplay::timing { +export namespace helios::runtime::timing { /** * @brief Manager that owns game timers and processes timer control commands. @@ -41,7 +40,7 @@ export namespace helios::gameplay::timing { * Pending control commands are collected via submit() and applied during * flush() at the beginning of each frame. * - * @see GameTimer + * @see Timer * @see TimerCommandHandler * @see Manager */ @@ -50,7 +49,7 @@ export namespace helios::gameplay::timing { /** * @brief Collection of game timers managed by this manager. */ - std::vector gameTimers_; + std::vector timers_; /** * @brief Pending timer control contexts to be applied during flush. @@ -64,41 +63,22 @@ export namespace helios::gameplay::timing { * * @return True if a timer with the given id is registered. */ - [[nodiscard]] bool has(const GameTimerId timerId) noexcept { - return getGameTimer(timerId) != nullptr; + [[nodiscard]] bool has(const TimerId timerId) noexcept { + return getTimer(timerId) != nullptr; } - /** - * @brief Looks up a timer by its id. - * - * @param timerId The id to search for. - * - * @return Pointer to the timer, or nullptr if not found. - */ - GameTimer* getGameTimer(const GameTimerId timerId) { - const auto timer = std::ranges::find_if( - gameTimers_, - [&](const auto& gameTimer) { - return gameTimer.gameTimerId() == timerId; - }); - - if (timer == gameTimers_.end()) { - return nullptr; - } - return &*timer; - } /** * @brief Creates and appends a new timer. * - * @param gameTimerId The id for the new timer. + * @param timerId The id for the new timer. * * @return Reference to the newly created timer. */ - GameTimer& add(const GameTimerId gameTimerId) noexcept { - gameTimers_.emplace_back(GameTimer(gameTimerId)); + Timer& add(const TimerId timerId) noexcept { + timers_.emplace_back(Timer(timerId)); - return gameTimers_.back(); + return timers_.back(); } public: @@ -109,34 +89,43 @@ export namespace helios::gameplay::timing { * * Asserts that no timer with the given id already exists. * - * @param gameTimerId The unique id for the new timer. + * @param timerId The unique id for the new timer. * - * @return Reference to the newly created GameTimer. + * @return Reference to the newly created Timer. */ - GameTimer& addGameTimer(GameTimerId gameTimerId) noexcept { - assert(!has(gameTimerId) && "GameTimer with GameTimerId already registered"); + Timer& addTimer(TimerId timerId) noexcept { + assert(!has(timerId) && "Timer with TimerId already registered"); - return add(gameTimerId); + return add(timerId); } /** - * @brief Returns a pointer to the timer with the given id. + * @brief Looks up a timer by its id. * - * @param gameTimerId The id to look up. + * @param timerId The id to search for. * - * @return Pointer to the GameTimer, or nullptr if not found. + * @return Pointer to the timer, or nullptr if not found. */ - [[nodiscard]] GameTimer* gameTimer(const GameTimerId gameTimerId) noexcept { - return getGameTimer(gameTimerId); + Timer* getTimer(const TimerId timerId) { + const auto timerIt = std::ranges::find_if( + timers_, + [&](const auto& timer) { + return timer.timerId() == timerId; + }); + + if (timerIt == timers_.end()) { + return nullptr; + } + return &*timerIt; } /** * @brief Returns a span over all registered timers. * - * @return A span of GameTimer instances. + * @return A span of Timer instances. */ - [[nodiscard]] std::span gameTimers() noexcept { - return gameTimers_; + [[nodiscard]] std::span timers() noexcept { + return timers_; } /** @@ -153,7 +142,7 @@ export namespace helios::gameplay::timing { ) noexcept { for (const auto& controlContext : pendingControlContexts_) { - auto* timer = gameTimer(controlContext.gameTimerId); + auto* timer = getTimer(controlContext.timerId); if (timer) { if (controlContext.resetElapsed) { timer->reset(controlContext.timerState); @@ -182,16 +171,16 @@ export namespace helios::gameplay::timing { * * @param gameWorld The game world to register with. */ - void init(helios::runtime::world::GameWorld& gameWorld) { - gameWorld.template registerCommandHandler(*this); + void init(helios::runtime::messaging::command::CommandHandlerRegistry& commandHandlerRegistry) { + commandHandlerRegistry.registerHandler(*this); } /** * @brief Resets all managed timers. */ void reset() { - for (auto& gameTimer : gameTimers_) { - gameTimer.reset(); + for (auto& timer : timers_) { + timer.reset(); } } }; diff --git a/include/helios/runtime/timing/_module.ixx b/include/helios/runtime/timing/_module.ixx new file mode 100644 index 0000000000..b564331085 --- /dev/null +++ b/include/helios/runtime/timing/_module.ixx @@ -0,0 +1,15 @@ +/** + * @file _module.ixx + * @brief Aggregate module for helios::runtime::timing namespace. + * + */ +export module helios.runtime.timing; + +export import helios.runtime.timing.types; +export import helios.runtime.timing.commands; +export import helios.runtime.timing.systems; +export import helios.runtime.timing.components; + + +export import helios.runtime.timing.Timer; +export import helios.runtime.timing.TimerManager; \ No newline at end of file diff --git a/include/helios/gameplay/timing/commands/README.md b/include/helios/runtime/timing/commands/README.md similarity index 100% rename from include/helios/gameplay/timing/commands/README.md rename to include/helios/runtime/timing/commands/README.md diff --git a/include/helios/gameplay/timing/commands/TimerControlCommand.ixx b/include/helios/runtime/timing/commands/TimerControlCommand.ixx similarity index 66% rename from include/helios/gameplay/timing/commands/TimerControlCommand.ixx rename to include/helios/runtime/timing/commands/TimerControlCommand.ixx index 51ac8c006a..03345cc63c 100644 --- a/include/helios/gameplay/timing/commands/TimerControlCommand.ixx +++ b/include/helios/runtime/timing/commands/TimerControlCommand.ixx @@ -7,14 +7,14 @@ module; #include #include -export module helios.gameplay.timing.commands.TimerControlCommand; +export module helios.runtime.timing.commands.TimerControlCommand; -import helios.gameplay.timing.types; +import helios.runtime.timing.types; -export namespace helios::gameplay::timing::commands { +export namespace helios::runtime::timing::commands { /** * @brief Command that carries a TimerControlContext to the timer subsystem. @@ -22,15 +22,15 @@ export namespace helios::gameplay::timing::commands { * TimerControlCommand is dispatched through the command buffer to request * timer state transitions (start, pause, stop). * - * @see helios::gameplay::timing::types::TimerControlContext - * @see helios::gameplay::timing::TimerCommandDispatcher + * @see helios::runtime::timing::types::TimerControlContext + * @see helios::runtime::timing::TimerCommandDispatcher */ class TimerControlCommand { /** * @brief The timer control context describing the requested state transition. */ - helios::gameplay::timing::types::TimerControlContext timerContext_; + helios::runtime::timing::types::TimerControlContext timerContext_; public: @@ -40,7 +40,7 @@ export namespace helios::gameplay::timing::commands { * @param timerContext The context describing which timer to control and the target state. */ explicit TimerControlCommand( - helios::gameplay::timing::types::TimerControlContext timerContext + helios::runtime::timing::types::TimerControlContext timerContext ) : timerContext_(std::move(timerContext)) { } @@ -49,7 +49,7 @@ export namespace helios::gameplay::timing::commands { * * @return The TimerControlContext describing the requested state transition. */ - [[nodiscard]] helios::gameplay::timing::types::TimerControlContext timerControlContext() const noexcept { + [[nodiscard]] helios::runtime::timing::types::TimerControlContext timerControlContext() const noexcept { return timerContext_; } diff --git a/include/helios/runtime/timing/commands/_module.ixx b/include/helios/runtime/timing/commands/_module.ixx new file mode 100644 index 0000000000..cf01afe001 --- /dev/null +++ b/include/helios/runtime/timing/commands/_module.ixx @@ -0,0 +1,7 @@ +/** + * @file _module.ixx + * @brief Aggregator for timer command types. + */ +export module helios.runtime.timing.commands; + +export import helios.runtime.timing.commands.TimerControlCommand; diff --git a/include/helios/gameplay/timing/components/README.md b/include/helios/runtime/timing/components/README.md similarity index 100% rename from include/helios/gameplay/timing/components/README.md rename to include/helios/runtime/timing/components/README.md diff --git a/include/helios/gameplay/timing/components/GameTimerBindingComponent.ixx b/include/helios/runtime/timing/components/TimerBindingComponent.ixx similarity index 50% rename from include/helios/gameplay/timing/components/GameTimerBindingComponent.ixx rename to include/helios/runtime/timing/components/TimerBindingComponent.ixx index 867c801904..6a2c7fdb2d 100644 --- a/include/helios/gameplay/timing/components/GameTimerBindingComponent.ixx +++ b/include/helios/runtime/timing/components/TimerBindingComponent.ixx @@ -1,40 +1,40 @@ /** - * @file GameTimerBindingComponent.ixx + * @file TimerBindingComponent.ixx * @brief Component that observes a specific game timer. */ module; -export module helios.gameplay.timing.components.GameTimerBindingComponent; +export module helios.runtime.timing.components.TimerBindingComponent; -import helios.gameplay.timing.types; +import helios.runtime.timing.types; -import helios.gameplay.timing.types.GameTimerId; +import helios.runtime.timing.types.TimerId; import helios.core.types; -using namespace helios::gameplay::timing::types; -using namespace helios::gameplay::timing::types; +using namespace helios::runtime::timing::types; +using namespace helios::runtime::timing::types; -export namespace helios::gameplay::timing::components { +export namespace helios::runtime::timing::components { /** - * @brief Component that tracks the revision of a specific GameTimer. + * @brief Component that tracks the revision of a specific Timer. * - * Entities with this component observe a GameTimer identified by its - * GameTimerId. The stored revision can be compared against the timer's + * Entities with this component observe a Timer identified by its + * TimerId. The stored revision can be compared against the timer's * current revision to detect updates. * - * @see GameTimer - * @see GameTimerUpdateSystem + * @see Timer + * @see TimerUpdateSystem */ template - class GameTimerBindingComponent { + class TimerBindingComponent { private: /** * @brief The id of the observed game timer. */ - GameTimerId gameTimerId_; + TimerId timerId_; /** * @brief Last known revision of the observed timer. @@ -45,36 +45,36 @@ export namespace helios::gameplay::timing::components { public: - GameTimerBindingComponent() = default; + TimerBindingComponent() = default; /** * @brief Copy constructor. * * @param other The component to copy from. */ - GameTimerBindingComponent(const GameTimerBindingComponent& other) : - gameTimerId_(other.gameTimerId_) {} + TimerBindingComponent(const TimerBindingComponent& other) : + timerId_(other.timerId_) {} - GameTimerBindingComponent& operator=(const GameTimerBindingComponent&) = default; - GameTimerBindingComponent(GameTimerBindingComponent&&) noexcept = default; - GameTimerBindingComponent& operator=(GameTimerBindingComponent&&) noexcept = default; + TimerBindingComponent& operator=(const TimerBindingComponent&) = default; + TimerBindingComponent(TimerBindingComponent&&) noexcept = default; + TimerBindingComponent& operator=(TimerBindingComponent&&) noexcept = default; /** * @brief Sets the id of the game timer to observe. * - * @param gameTimerId The timer id. + * @param timerId The timer id. */ - void setGameTimerId(const helios::gameplay::timing::types::GameTimerId gameTimerId) noexcept { - gameTimerId_ = gameTimerId; + void setTimerId(const helios::runtime::timing::types::TimerId timerId) noexcept { + timerId_ = timerId; } /** * @brief Returns the observed game timer id. * - * @return The GameTimerId. + * @return The TimerId. */ - [[nodiscard]] helios::gameplay::timing::types::GameTimerId gameTimerId() const noexcept { - return gameTimerId_; + [[nodiscard]] helios::runtime::timing::types::TimerId timerId() const noexcept { + return timerId_; } /** diff --git a/include/helios/runtime/timing/components/_module.ixx b/include/helios/runtime/timing/components/_module.ixx new file mode 100644 index 0000000000..f71f776427 --- /dev/null +++ b/include/helios/runtime/timing/components/_module.ixx @@ -0,0 +1,7 @@ +/** + * @file _module.ixx + * @brief Aggregator for timer components. + */ +export module helios.runtime.timing.components; + +export import helios.runtime.timing.components.TimerBindingComponent; \ No newline at end of file diff --git a/include/helios/gameplay/timing/systems/README.md b/include/helios/runtime/timing/systems/README.md similarity index 100% rename from include/helios/gameplay/timing/systems/README.md rename to include/helios/runtime/timing/systems/README.md diff --git a/include/helios/gameplay/timing/systems/GameTimerClearSystem.ixx b/include/helios/runtime/timing/systems/TimerClearSystem.ixx similarity index 65% rename from include/helios/gameplay/timing/systems/GameTimerClearSystem.ixx rename to include/helios/runtime/timing/systems/TimerClearSystem.ixx index de723ef67a..3aabf2b744 100644 --- a/include/helios/gameplay/timing/systems/GameTimerClearSystem.ixx +++ b/include/helios/runtime/timing/systems/TimerClearSystem.ixx @@ -1,26 +1,27 @@ /** - * @file GameTimerClearSystem.ixx + * @file TimerClearSystem.ixx * @brief System that resets finished game timers each frame. */ module; +#include -export module helios.gameplay.timing.systems.GameTimerClearSystem; +export module helios.runtime.timing.systems.TimerClearSystem; -import helios.gameplay.timing.GameTimer; -import helios.gameplay.timing.TimerManager; +import helios.runtime.timing.Timer; +import helios.runtime.timing.TimerManager; import helios.runtime.world.UpdateContext; import helios.runtime.world.tags.SystemRole; -import helios.gameplay.timing.types; +import helios.runtime.timing.types; -using namespace helios::gameplay::timing; +using namespace helios::runtime::timing; -using namespace helios::gameplay::timing::types; +using namespace helios::runtime::timing::types; -export namespace helios::gameplay::timing::systems { +export namespace helios::runtime::timing::systems { /** * @brief Resets finished game timers to an undefined state. @@ -32,9 +33,9 @@ export namespace helios::gameplay::timing::systems { * logic more than once. * * @see TimerManager - * @see GameTimer + * @see Timer */ - class GameTimerClearSystem { + class TimerClearSystem { /** * @brief Reference to the TimerManager owning the timers. @@ -51,7 +52,7 @@ export namespace helios::gameplay::timing::systems { * * @param timerManager The manager whose timers are cleared. */ - explicit GameTimerClearSystem(TimerManager& timerManager) + explicit TimerClearSystem(TimerManager& timerManager) : timerManager_(timerManager) {} /** @@ -61,9 +62,9 @@ export namespace helios::gameplay::timing::systems { */ void update(helios::runtime::world::UpdateContext& updateContext) noexcept { - for (auto& gameTimer : timerManager_.gameTimers()) { - if (gameTimer.state() == TimerState::Finished || gameTimer.state() == TimerState::Cancelled) { - gameTimer.setState(TimerState::Undefined); + for (auto& timer : timerManager_.timers()) { + if (timer.state() == TimerState::Finished || timer.state() == TimerState::Cancelled) { + timer.setState(TimerState::Undefined); } } } diff --git a/include/helios/gameplay/timing/systems/GameTimerUpdateSystem.ixx b/include/helios/runtime/timing/systems/TimerUpdateSystem.ixx similarity index 59% rename from include/helios/gameplay/timing/systems/GameTimerUpdateSystem.ixx rename to include/helios/runtime/timing/systems/TimerUpdateSystem.ixx index 85735d6061..cc9b681f82 100644 --- a/include/helios/gameplay/timing/systems/GameTimerUpdateSystem.ixx +++ b/include/helios/runtime/timing/systems/TimerUpdateSystem.ixx @@ -1,14 +1,15 @@ /** - * @file GameTimerUpdateSystem.ixx + * @file TimerUpdateSystem.ixx * @brief System that advances all game timers each frame. */ module; +#include -export module helios.gameplay.timing.systems.GameTimerUpdateSystem; +export module helios.runtime.timing.systems.TimerUpdateSystem; -import helios.gameplay.timing.GameTimer; -import helios.gameplay.timing.TimerManager; +import helios.runtime.timing.Timer; +import helios.runtime.timing.TimerManager; import helios.state.Bindings; @@ -19,18 +20,18 @@ import helios.runtime.messaging.command.concepts.IsCommandBufferLike; import helios.runtime.world.tags.SystemRole; -import helios.gameplay.timing.types; -import helios.gameplay.timing.commands; +import helios.runtime.timing.types; +import helios.runtime.timing.commands; -using namespace helios::gameplay::timing; +using namespace helios::runtime::timing; -using namespace helios::gameplay::timing::types; -using namespace helios::gameplay::timing::commands; +using namespace helios::runtime::timing::types; +using namespace helios::runtime::timing::commands; using namespace helios::runtime::world; using namespace helios::runtime::messaging::command; using namespace helios::runtime::messaging::command::concepts; -export namespace helios::gameplay::timing::systems { +export namespace helios::runtime::timing::systems { /** * @brief Updates all game timers managed by the TimerManager. @@ -39,11 +40,11 @@ export namespace helios::gameplay::timing::systems { * advances their elapsed time by the current delta time. * * @see TimerManager - * @see GameTimer + * @see Timer */ template requires IsCommandBufferLike - class GameTimerUpdateSystem { + class TimerUpdateSystem { /** * @brief Reference to the TimerManager owning the timers. @@ -54,6 +55,7 @@ export namespace helios::gameplay::timing::systems { using EngineRoleTag = helios::runtime::tags::SystemRole; + using CommandBuffer_type = TCommandBuffer; /** @@ -61,7 +63,7 @@ export namespace helios::gameplay::timing::systems { * * @param timerManager The manager whose timers are updated. */ - explicit GameTimerUpdateSystem(TimerManager& timerManager) + explicit TimerUpdateSystem(TimerManager& timerManager) : timerManager_(timerManager) {} /** @@ -69,16 +71,16 @@ export namespace helios::gameplay::timing::systems { * * @param updateContext The current frame's update context. */ - void update(helios::runtime::world::UpdateContext& updateContext) noexcept { + void update(helios::runtime::world::UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { - for (auto& gameTimer : timerManager_.gameTimers()) { - if (gameTimer.state() == TimerState::Running) { + for (auto& timer : timerManager_.timers()) { + if (timer.state() == TimerState::Running) { - gameTimer.update(updateContext.deltaTime()); + timer.update(updateContext.deltaTime()); - if (gameTimer.duration() != 0.0f && gameTimer.elapsed() >= gameTimer.duration()) { - auto context = TimerControlContext{gameTimer.gameTimerId(), TimerState::Finished}; - updateContext.queueCommand(context); + if (timer.duration() != 0.0f && timer.elapsed() >= timer.duration()) { + auto context = TimerControlContext{timer.timerId(), TimerState::Finished}; + cmdBuffer.template add(context); } } } diff --git a/include/helios/runtime/timing/systems/_module.ixx b/include/helios/runtime/timing/systems/_module.ixx new file mode 100644 index 0000000000..42fc62ac28 --- /dev/null +++ b/include/helios/runtime/timing/systems/_module.ixx @@ -0,0 +1,8 @@ +/** + * @file _module.ixx + * @brief Aggregator for timer systems. + */ +export module helios.runtime.timing.systems; + +export import helios.runtime.timing.systems.TimerUpdateSystem; +export import helios.runtime.timing.systems.TimerClearSystem; diff --git a/include/helios/gameplay/timing/types/README.md b/include/helios/runtime/timing/types/README.md similarity index 100% rename from include/helios/gameplay/timing/types/README.md rename to include/helios/runtime/timing/types/README.md diff --git a/include/helios/gameplay/timing/types/TimerControlContext.ixx b/include/helios/runtime/timing/types/TimerControlContext.ixx similarity index 65% rename from include/helios/gameplay/timing/types/TimerControlContext.ixx rename to include/helios/runtime/timing/types/TimerControlContext.ixx index 978cdfc99a..74b63e223f 100644 --- a/include/helios/gameplay/timing/types/TimerControlContext.ixx +++ b/include/helios/runtime/timing/types/TimerControlContext.ixx @@ -5,19 +5,19 @@ module; -export module helios.gameplay.timing.types.TimerControlContext; +export module helios.runtime.timing.types.TimerControlContext; -import helios.gameplay.timing.types.TimerState; -import helios.gameplay.timing.types.GameTimerId; +import helios.runtime.timing.types.TimerState; +import helios.runtime.timing.types.TimerId; -using namespace helios::gameplay::timing::types; +using namespace helios::runtime::timing::types; -export namespace helios::gameplay::timing::types { +export namespace helios::runtime::timing::types { /** * @brief Context describing a requested timer state transition. * - * Carries the target TimerState and the GameTimerId of the timer + * Carries the target TimerState and the TimerId of the timer * to be controlled. * * @see TimerControlCommand @@ -28,7 +28,7 @@ export namespace helios::gameplay::timing::types { /** * @brief The id of the timer to control. */ - const GameTimerId gameTimerId; + const TimerId timerId; /** * @brief The target state for the timer. diff --git a/include/helios/runtime/timing/types/TimerId.ixx b/include/helios/runtime/timing/types/TimerId.ixx new file mode 100644 index 0000000000..cba5104e23 --- /dev/null +++ b/include/helios/runtime/timing/types/TimerId.ixx @@ -0,0 +1,32 @@ +/** +* @file TimerId.ixx + * @brief Strongly-typed identifier for timers. + */ +module; + +#include +#include + +export module helios.runtime.timing.types.TimerId; + +import helios.core.types.FuncDefs; +import helios.core.types; +import helios.core.types.StrongId; + +export namespace helios::runtime::timing::types { + + /** + * @brief Tag type for TimerId. + */ + struct TimerIdTag{}; + + /** + * @brief Strongly-typed identifier for timers. + * + * @details Used to uniquely identify timers. + * + * @see helios::core::types::StrongId + */ + using TimerId = helios::core::types::StrongId; + +} diff --git a/include/helios/runtime/timing/types/TimerRevision.ixx b/include/helios/runtime/timing/types/TimerRevision.ixx new file mode 100644 index 0000000000..2de7c5fe4e --- /dev/null +++ b/include/helios/runtime/timing/types/TimerRevision.ixx @@ -0,0 +1,19 @@ +/** + * @file TimerRevision.ixx + * @brief Type alias for timer revision counters. + */ +module; + +#include + +export module helios.runtime.timing.types.TimerRevision; + + +export namespace helios::runtime::timing::types { + + /** + * @brief Monotonically increasing revision counter for Timer updates. + */ + using TimerRevision = uint32_t; + +} \ No newline at end of file diff --git a/include/helios/gameplay/timing/types/TimerState.ixx b/include/helios/runtime/timing/types/TimerState.ixx similarity index 80% rename from include/helios/gameplay/timing/types/TimerState.ixx rename to include/helios/runtime/timing/types/TimerState.ixx index 83941b30c1..cf25fe83b6 100644 --- a/include/helios/gameplay/timing/types/TimerState.ixx +++ b/include/helios/runtime/timing/types/TimerState.ixx @@ -6,12 +6,12 @@ module; -export module helios.gameplay.timing.types.TimerState; +export module helios.runtime.timing.types.TimerState; -export namespace helios::gameplay::timing::types { +export namespace helios::runtime::timing::types { /** - * @brief Represents the state of a GameTimer. + * @brief Represents the state of a Timer. */ enum class TimerState { diff --git a/include/helios/runtime/timing/types/_module.ixx b/include/helios/runtime/timing/types/_module.ixx new file mode 100644 index 0000000000..ca2687f06f --- /dev/null +++ b/include/helios/runtime/timing/types/_module.ixx @@ -0,0 +1,11 @@ +/** + * @file _module.ixx + * @brief Aggregator for timer-related type definitions. + */ +export module helios.runtime.timing.types; + +export import helios.runtime.timing.types.TimerControlContext; +export import helios.runtime.timing.types.TimerState; +export import helios.runtime.timing.types.TimerRevision; +export import helios.runtime.timing.types.TimerId; + diff --git a/include/helios/runtime/world/GameWorld.ixx b/include/helios/runtime/world/GameWorld.ixx index c59fd6f10c..d8c32e2db9 100644 --- a/include/helios/runtime/world/GameWorld.ixx +++ b/include/helios/runtime/world/GameWorld.ixx @@ -18,11 +18,14 @@ export module helios.runtime.world.GameWorld; import helios.runtime.world.Session; +import helios.runtime.timing.TimerManager; + import helios.ecs.Entity; import helios.runtime.world.RuntimeEnvironment; import helios.platform.environment.types; import helios.runtime.messaging.command.CommandHandlerRegistry; +import helios.runtime.messaging.command.CommandBufferRegistry; import helios.runtime.world.ResourceRegistry; @@ -42,11 +45,12 @@ import helios.ecs.EntityManager; import helios.ecs.EntityRegistry; import helios.ecs.View; -import helios.runtime.concepts; +import helios.runtime.messaging.command.concepts; import helios.runtime.world.EngineWorld; +import helios.runtime.world.concepts; - +using namespace helios::runtime::timing; using namespace helios::runtime::messaging::command::concepts; using namespace helios::runtime::messaging::command; using namespace helios::runtime::world::concepts; @@ -99,8 +103,9 @@ export namespace helios::runtime::world { * @brief Registry mapping command types to their handler function pointers. * * @details Used by TypedCommandBuffer during flush to route commands - * to the correct handler. Handlers are registered via - * `registerCommandHandler(owner)`. + * to the correct handler. Handlers are usually registered by managers + * in `init(CommandHandlerRegistry&)`; direct registration via + * `registerCommandHandler(owner)` is still supported. */ CommandHandlerRegistry commandHandlerRegistry_; @@ -162,15 +167,22 @@ export namespace helios::runtime::world { /** - * @brief Initializes all registered managers. + * @brief Initializes managers and command buffers. * - * @details Should be called after all managers have been added and before - * the game loop starts. Each manager's init() method is invoked with a - * reference to this GameWorld. + * @details Should be called after all resources have been registered and + * before the game loop starts. Manager `init()` receives the + * `CommandHandlerRegistry` so managers can register command handlers + * without a hard GameWorld dependency. Command buffers are initialized + * afterward and bound to the same handler registry. */ GameWorld& init() { for (auto& mgr : resourceRegistry_.managers()) { - mgr->init(*this); + mgr->init(commandHandlerRegistry_); + } + + assert(resourceRegistry_.tryGet() && "TimerManager must be registered before initializing command buffers"); + for (auto& buff : resourceRegistry_.commandBuffers()) { + buff->init(commandHandlerRegistry_, resourceRegistry_.get()); } return *this; @@ -379,14 +391,14 @@ export namespace helios::runtime::world { * @brief Flushes all registered CommandBuffers. * * @details Iterates over all CommandBuffers in registration order and - * invokes `flush(*this, updateContext)` on each. Called by the GameLoop - * at commit points before Managers are flushed. + * invokes `flush(updateContext)` on each. Called by the GameLoop at + * commit points before Managers are flushed. * * @param updateContext The current frame's update context. */ void flushCommandBuffers(UpdateContext& updateContext) { for (auto& buff : resourceRegistry_.commandBuffers()) { - buff->flush(*this, updateContext); + buff->flush(updateContext); } } @@ -526,6 +538,15 @@ export namespace helios::runtime::world { return engineWorld_.template destroy(handle); } + /** + * @brief Returns direct access to the command-buffer registry. + * + * @return Reference to the internal CommandBufferRegistry. + */ + helios::runtime::messaging::command::CommandBufferRegistry& commandBufferRegistry() noexcept { + return resourceRegistry().commandBufferRegistry(); + } + }; } diff --git a/include/helios/runtime/world/GameWorldFwd.ixx b/include/helios/runtime/world/GameWorldFwd.ixx deleted file mode 100644 index 05532438ec..0000000000 --- a/include/helios/runtime/world/GameWorldFwd.ixx +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @file GameWorldFwd.ixx - * @brief Forward declaration of `GameWorld`. - */ -module; - -export module helios.runtime.world.GameWorldFwd; - -export namespace helios::runtime::world { - - /** - * @brief Forward declaration for the runtime world coordinator. - */ - class GameWorld; - -} \ No newline at end of file diff --git a/include/helios/runtime/world/Manager.ixx b/include/helios/runtime/world/Manager.ixx index 2c9b6a22d1..3ceb978c1d 100644 --- a/include/helios/runtime/world/Manager.ixx +++ b/include/helios/runtime/world/Manager.ixx @@ -11,21 +11,21 @@ export module helios.runtime.world.Manager; import helios.runtime.world.concepts.IsManagerLike; -import helios.runtime.world.UpdateContextFwd; -import helios.runtime.world.GameWorldFwd; - +import helios.runtime.world.UpdateContext; +import helios.runtime.messaging.command.CommandHandlerRegistry; +using namespace helios::runtime::messaging::command; using namespace helios::runtime::world::concepts; export namespace helios::runtime::world { /** - * @brief Concept detecting an optional `init(GameWorld&)` method on a manager. + * @brief Concept detecting an optional `init(CommandHandlerRegistry&)` method on a manager. * * @tparam T The manager type to inspect. */ template - concept HasInit = requires(T& t, GameWorld& gameWorld) { - {t.init(gameWorld) } -> std::same_as; + concept HasInit = requires(T& t, CommandHandlerRegistry& commandHandlerRegistry) { + {t.init(commandHandlerRegistry) } -> std::same_as; }; /** @@ -70,7 +70,7 @@ export namespace helios::runtime::world { public: virtual ~Concept() = default; virtual void flush(UpdateContext& updateContext) noexcept = 0; - virtual void init(GameWorld& gameWorld) noexcept = 0; + virtual void init(CommandHandlerRegistry& commandHandlerRegistry) noexcept = 0; virtual void reset() noexcept = 0; [[nodiscard]] virtual void* underlying() noexcept = 0; @@ -93,9 +93,9 @@ export namespace helios::runtime::world { void flush(UpdateContext& updateContext) noexcept override { manager_.flush(updateContext); } - void init(GameWorld& gameWorld) noexcept override { + void init(CommandHandlerRegistry& commandHandlerRegistry) noexcept override { if constexpr (HasInit) { - manager_.init(gameWorld); + manager_.init(commandHandlerRegistry); } } void reset() noexcept override { @@ -162,15 +162,15 @@ export namespace helios::runtime::world { * * @details If the concrete type satisfies `HasInit`, its `init()` is * called. Otherwise this is a no-op. Typically used by managers to - * register their TypedCommandHandlers with the GameWorld. + * register command handlers in the command-handler registry. * - * @param gameWorld The GameWorld for one-time initialization. + * @param commandHandlerRegistry Registry used for one-time handler registration. * * @pre Manager must be initialized (pimpl_ != nullptr). */ - void init(GameWorld& gameWorld) noexcept { + void init(CommandHandlerRegistry& commandHandlerRegistry) noexcept { assert(pimpl_ && "Manager not initialized"); - pimpl_->init(gameWorld); + pimpl_->init(commandHandlerRegistry); } /** diff --git a/include/helios/runtime/world/ResourceRegistry.ixx b/include/helios/runtime/world/ResourceRegistry.ixx index 66df876833..145471314b 100644 --- a/include/helios/runtime/world/ResourceRegistry.ixx +++ b/include/helios/runtime/world/ResourceRegistry.ixx @@ -21,7 +21,7 @@ import helios.runtime.messaging.command.CommandHandlerRegistry; import helios.runtime.world.ManagerRegistry; -import helios.runtime.concepts; +import helios.runtime.world.concepts; import helios.runtime.messaging.command.concepts; import helios.runtime.world.types; @@ -186,6 +186,15 @@ export namespace helios::runtime::world { std::span commandBuffers() noexcept { return commandBufferRegistry_.items(); } + + /** + * @brief Returns direct access to the underlying command-buffer registry. + * + * @return Reference to the internal CommandBufferRegistry. + */ + CommandBufferRegistry& commandBufferRegistry() noexcept { + return commandBufferRegistry_; + } }; } diff --git a/include/helios/runtime/world/Session.ixx b/include/helios/runtime/world/Session.ixx index aa57798f81..c64a733b26 100644 --- a/include/helios/runtime/world/Session.ixx +++ b/include/helios/runtime/world/Session.ixx @@ -16,7 +16,7 @@ import helios.gameplay.lifecycle.components; import helios.gameplay.gamestate.types; import helios.gameplay.matchstate.types; -import helios.state.types; +import helios.state.types.StateTransitionContext; import helios.state.components; import helios.state.types.StateTransitionId; @@ -33,11 +33,11 @@ using namespace helios::gameplay::matchstate::types; using namespace helios::gameplay::gamestate::types; -using namespace helios::state::types; using namespace helios::state::components; using namespace helios::rendering::viewport::components; +using namespace helios::state::types; using namespace helios::rendering::viewport::types; using namespace helios::gameplay::lifecycle::components; using namespace helios::runtime::world; diff --git a/include/helios/runtime/world/System.ixx b/include/helios/runtime/world/System.ixx index 84f3b4a985..fe3cc83981 100644 --- a/include/helios/runtime/world/System.ixx +++ b/include/helios/runtime/world/System.ixx @@ -13,7 +13,7 @@ export module helios.runtime.world.System; import helios.runtime.world.UpdateContext; import helios.runtime.world.GameWorld; -import helios.runtime.concepts; +import helios.runtime.world.concepts; using namespace helios::runtime::world; using namespace helios::runtime::world::concepts; @@ -27,9 +27,8 @@ export namespace helios::runtime::world { * system type. Concrete systems are plain classes that satisfy * `IsSystemLike` and do not inherit from System. * - * The internal `Concept` base defines the virtual interface, and + * The internal `Concept` base defines the virtual update interface, and * `Model` adapts the concrete type T, owning it by value. - * `init()` is conditionally forwarded if `HasInit` is satisfied. * * If a system exposes `CommandBuffer_type`, `System` calls * `update(UpdateContext&, CommandBuffer_type&)` and uses the injected @@ -38,7 +37,6 @@ export namespace helios::runtime::world { * System is move-only (non-copyable). * * @see IsSystemLike - * @see HasInit * @see SystemRegistry * * @see https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Type_Erasure @@ -54,7 +52,6 @@ export namespace helios::runtime::world { virtual ~Concept() = default; virtual void update(UpdateContext& updateContext) noexcept = 0; - virtual void init(GameWorld& gameWorld) noexcept = 0; virtual void* underlying() noexcept = 0; virtual const void* underlying() const noexcept = 0; }; @@ -82,12 +79,6 @@ export namespace helios::runtime::world { } } - void init(GameWorld& gameWorld) noexcept override { - if constexpr (HasInit) { - system_.init(gameWorld); - } - } - void* underlying() noexcept override { return &system_; } @@ -141,20 +132,6 @@ export namespace helios::runtime::world { pimpl_->update(updateContext); } - /** - * @brief Delegates to the wrapped system's `init()` method, if present. - * - * @details If the concrete type satisfies `HasInit`, its `init()` is - * called. Otherwise this is a no-op. - * - * @param gameWorld The GameWorld for one-time initialization. - * - * @pre System must be initialized (pimpl_ != nullptr). - */ - void init(GameWorld& gameWorld) noexcept { - assert(pimpl_ && "System not initialized"); - pimpl_->init(gameWorld); - } /** * @brief Returns a type-erased pointer to the wrapped system instance. diff --git a/include/helios/runtime/world/UpdateContext.ixx b/include/helios/runtime/world/UpdateContext.ixx index 8137652c1a..c3ca2945a7 100644 --- a/include/helios/runtime/world/UpdateContext.ixx +++ b/include/helios/runtime/world/UpdateContext.ixx @@ -15,12 +15,11 @@ import helios.rendering.viewport.ViewportSnapshot; import helios.runtime.world.Level; import helios.runtime.messaging.event.GameLoopEventBus; -import helios.runtime.world.ResourceRegistry; - import helios.runtime.world.RuntimeEnvironment; import helios.ecs.types.EntityHandle; import helios.ecs.View; +import helios.runtime.world.Session; import helios.rendering.concepts.IsRenderResourceHandle; @@ -28,8 +27,6 @@ import helios.runtime.world.EngineWorld; export namespace helios::runtime::world { - class Session; - /** * @brief Per-frame context passed to systems during game loop updates. @@ -37,10 +34,10 @@ export namespace helios::runtime::world { * @details UpdateContext bundles frame-scoped data and services used by * system updates: timing values, immutable input/viewport snapshots, * session/runtime environment access, typed entity access via `EngineWorld`, - * event-bus read/write channels, and command-buffer submission. + * event-bus read/write channels, and typed ECS access. * - * Commands are submitted with `queueCommand(...)` - * and are flushed at configured game-loop commit points. + * Command submission is handled by systems through injected command buffers + * (`CommandBuffer_type`) using `cmdBuffer.template add(...)`. * * @see GameLoop * @see Session @@ -121,12 +118,6 @@ export namespace helios::runtime::world { */ std::span viewportSnapshots_; - /** - * @brief Reference to the ResourceRegistry used for command-buffer lookup. - */ - helios::runtime::world::ResourceRegistry& resourceRegistry_; - - /** * @brief Pointer to the active Level, or nullptr if no level is loaded. */ @@ -142,7 +133,6 @@ export namespace helios::runtime::world { /** * @brief Constructs an UpdateContext with all per-frame dependencies. * - * @param resourceRegistry Reference to the resource registry. * @param session Reference to current session state. * @param runtimeEnvironment Reference to runtime-environment state. * @param deltaTime Time since last frame in seconds. @@ -156,7 +146,6 @@ export namespace helios::runtime::world { * @param engineWorld Aggregate typed world for entity operations. */ UpdateContext( - helios::runtime::world::ResourceRegistry& resourceRegistry, helios::runtime::world::Session& session, helios::runtime::world::RuntimeEnvironment& runtimeEnvironment, const float deltaTime, @@ -169,7 +158,6 @@ export namespace helios::runtime::world { const Level* level, EngineWorld& engineWorld ) : - resourceRegistry_(resourceRegistry), session_(session), runtimeEnvironment_(runtimeEnvironment), deltaTime_(deltaTime), @@ -250,22 +238,6 @@ export namespace helios::runtime::world { } - /** - * @brief Submits a command to a typed command buffer. - * - * @tparam TCmdBuffer Target command-buffer type. - * @tparam T Command type to enqueue. - * @tparam Args Command constructor argument types. - * - * @param args Arguments forwarded to the command constructor. - */ - template - void queueCommand(Args&&...args) const noexcept { - auto* cmdBuffer = resourceRegistry_.tryGet(); - assert(cmdBuffer && "Command buffer not found in registry"); - cmdBuffer->template add(std::forward(args)...); - } - /** * @brief Returns the session for game/match state access. * diff --git a/include/helios/runtime/world/UpdateContextFwd.ixx b/include/helios/runtime/world/UpdateContextFwd.ixx deleted file mode 100644 index 5fbd3eda4a..0000000000 --- a/include/helios/runtime/world/UpdateContextFwd.ixx +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @file UpdateContextFwd.ixx - * @brief Forward declaration of `UpdateContext`. - */ -module; - -export module helios.runtime.world.UpdateContextFwd; - -export namespace helios::runtime::world { - - /** - * @brief Forward declaration for frame-local update context. - */ - class UpdateContext; - -} \ No newline at end of file diff --git a/include/helios/runtime/world/concepts/HasInit.ixx b/include/helios/runtime/world/concepts/HasInit.ixx deleted file mode 100644 index 6294e3a26e..0000000000 --- a/include/helios/runtime/world/concepts/HasInit.ixx +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file HasInit.ixx - * @brief Concept detecting an optional init(GameWorld&) method. - */ -module; - -#include - -export module helios.runtime.world.concepts.HasInit; - -import helios.runtime.world.GameWorldFwd; - -export namespace helios::runtime::world::concepts { - - /** - * @brief Detects whether T provides a `void init(GameWorld&)` method. - * - * @details Used by the System type-erasure wrapper to conditionally - * forward one-time initialization calls. If a concrete system does - * not satisfy this concept, the init step is a no-op. - * - * @tparam T The type to inspect. - * - * @see System - */ - template - concept HasInit = requires(T& t, helios::runtime::world::GameWorld& gameWorld) { - {t.init(gameWorld) } -> std::same_as; - }; - - -} \ No newline at end of file diff --git a/include/helios/runtime/world/concepts/HasUpdate.ixx b/include/helios/runtime/world/concepts/HasUpdate.ixx index 683642276c..c663959210 100644 --- a/include/helios/runtime/world/concepts/HasUpdate.ixx +++ b/include/helios/runtime/world/concepts/HasUpdate.ixx @@ -8,7 +8,7 @@ module; export module helios.runtime.world.concepts.HasUpdate; -import helios.runtime.world.UpdateContextFwd; +import helios.runtime.world.UpdateContext; export namespace helios::runtime::world::concepts { diff --git a/include/helios/runtime/world/concepts/IsManagerLike.ixx b/include/helios/runtime/world/concepts/IsManagerLike.ixx index 7dfc1061d9..551962ea41 100644 --- a/include/helios/runtime/world/concepts/IsManagerLike.ixx +++ b/include/helios/runtime/world/concepts/IsManagerLike.ixx @@ -11,7 +11,7 @@ export module helios.runtime.world.concepts.IsManagerLike; import helios.runtime.concepts.HasTag; import helios.runtime.world.tags.ManagerRole; -import helios.runtime.world.UpdateContextFwd; +import helios.runtime.world.UpdateContext; using namespace helios::runtime::concepts; export namespace helios::runtime::world::concepts { diff --git a/include/helios/runtime/world/concepts/IsSystemLike.ixx b/include/helios/runtime/world/concepts/IsSystemLike.ixx index a294746062..7f77b0a10e 100644 --- a/include/helios/runtime/world/concepts/IsSystemLike.ixx +++ b/include/helios/runtime/world/concepts/IsSystemLike.ixx @@ -11,7 +11,7 @@ export module helios.runtime.world.concepts.IsSystemLike; import helios.runtime.concepts.HasTag; import helios.runtime.world.tags.SystemRole; -import helios.runtime.world.UpdateContextFwd; +import helios.runtime.world.UpdateContext; using namespace helios::runtime::concepts; export namespace helios::runtime::world::concepts { diff --git a/include/helios/runtime/world/concepts/_module.ixx b/include/helios/runtime/world/concepts/_module.ixx index a22cc1bd23..e5b4cd746c 100644 --- a/include/helios/runtime/world/concepts/_module.ixx +++ b/include/helios/runtime/world/concepts/_module.ixx @@ -6,7 +6,6 @@ export module helios.runtime.world.concepts; export import helios.runtime.world.concepts.IsGameObjectHandle; export import helios.runtime.world.concepts.HasUpdate; -export import helios.runtime.world.concepts.HasInit; export import helios.runtime.world.concepts.IsManagerLike; export import helios.runtime.world.concepts.IsSystemLike; diff --git a/include/helios/scene/concepts/IsFrustumCullerLike.ixx b/include/helios/scene/concepts/IsFrustumCullerLike.ixx index 5b0ba1a2b6..ba67f6fccb 100644 --- a/include/helios/scene/concepts/IsFrustumCullerLike.ixx +++ b/include/helios/scene/concepts/IsFrustumCullerLike.ixx @@ -24,8 +24,8 @@ export namespace helios::scene::concepts { * @tparam T Candidate culling strategy type. * @tparam TStrongId type for the EntityHandle's StrongId. */ - template - concept IsFrustumCullerLike = requires(T& t, const EntityHandle entityHandle, const PerspectiveCameraContext& cameraContext) + template + concept IsFrustumCullerLike = requires(T& t, const THandle entityHandle, const PerspectiveCameraContext& cameraContext) { {t.cull(cameraContext, entityHandle)}-> std::same_as; diff --git a/include/helios/scene/systems/SceneMemberRenderExtractionSystem.ixx b/include/helios/scene/systems/SceneMemberRenderExtractionSystem.ixx index 68c8e82ede..99f92c0225 100644 --- a/include/helios/scene/systems/SceneMemberRenderExtractionSystem.ixx +++ b/include/helios/scene/systems/SceneMemberRenderExtractionSystem.ixx @@ -58,7 +58,7 @@ export namespace helios::scene::systems { * @tparam CullingStrategy */ template - requires IsFrustumCullerLike && IsCommandBufferLike + requires IsFrustumCullerLike && IsCommandBufferLike class SceneMemberRenderExtractionSystem { CullingStrategy cullingStrategy_; @@ -66,9 +66,10 @@ export namespace helios::scene::systems { public: using EngineRoleTag = helios::runtime::tags::SystemRole; + using CommandBuffer_type = TCommandBuffer; - void update(helios::runtime::world::UpdateContext& updateContext) noexcept { + void update(helios::runtime::world::UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { for (auto [entity, vc, active] : updateContext.view< @@ -94,9 +95,9 @@ export namespace helios::scene::systems { >().whereEnabled()) { if (smc->sceneHandle() == sceneHandle) { - auto* mcOverride = innerEntity.get(); + auto* mcOverride = innerEntity.template get(); - updateContext.queueCommand( + cmdBuffer.template add( SceneMemberRenderContext{ innerEntity.handle(), viewportHandle, diff --git a/include/helios/spatial/transform/components/SizeComponent.ixx b/include/helios/spatial/transform/components/SizeComponent.ixx new file mode 100644 index 0000000000..8725e8b213 --- /dev/null +++ b/include/helios/spatial/transform/components/SizeComponent.ixx @@ -0,0 +1,126 @@ +module; + +#include + +export module helios.spatial.transform.components.SizeComponent; + +import helios.runtime.world.GameObject; +import helios.math.types; + +import helios.core.spatial.Transform; +import helios.core.units.Unit; +import helios.math.concepts; + +using namespace helios::math; +using namespace helios::math::concepts; +export namespace helios::spatial::transform::components { + + /** + * @brief Component storing 2D size and dirty-state tracking. + * + * @details `SizeComponent` is used for entities that expose width/height-like + * extents in a domain-specific coordinate system. Any mutation marks the + * component as dirty so update systems can recalculate dependent state lazily. + * + * @tparam TDomainTag Domain marker type (for strong typing across spaces). + * @tparam THandle Entity handle type this component is attached to. + * @tparam TNumericType Scalar type of the 2D size vector. + */ + template + requires IsNumeric + class SizeComponent { + + helios::math::vec2 size_{}; + + bool isDirty_ = true; + + public: + + /** + * @brief Constructs the component with an initial size. + * + * @param size Initial size vector. + */ + explicit SizeComponent(const vec2 size) : size_(size){} + + /** + * @brief Copy constructor. + * + * @details Copies the size value and forces the copied component into a + * dirty state to ensure dependent systems refresh cached data. + * + * @param other The component to copy from. + */ + SizeComponent(const SizeComponent& other) : + size_(other.size_), + isDirty_(true) {} + + /** @brief Default copy assignment. */ + SizeComponent& operator=(const SizeComponent&) = default; + /** @brief Default move constructor. */ + SizeComponent(SizeComponent&&) noexcept = default; + /** @brief Default move assignment. */ + SizeComponent& operator=(SizeComponent&&) noexcept = default; + + /** + * @brief Lifecycle hook called when the component is acquired. + * + * @details Marks the component dirty to trigger downstream recomputation. + */ + void onAcquire() noexcept { + isDirty_ = true; + } + + /** + * @brief Lifecycle hook called when the component is released. + * + * @details Marks the component dirty to trigger downstream recomputation. + */ + void onRelease() noexcept { + isDirty_ = true; + } + + /** + * @brief Clears the dirty flag after dependent systems consumed updates. + */ + void clearDirty() noexcept { + isDirty_ = false; + } + + /** + * @brief Returns whether the component requires a refresh pass. + * + * @return `true` if size changed or lifecycle hooks marked dirty. + */ + [[nodiscard]] bool isDirty() const noexcept { + return isDirty_; + } + + /** + * @brief Returns the current size. + * + * @return Current 2D size vector. + */ + [[nodiscard]] vec2 size() const noexcept { + return size_; + } + + /** + * @brief Updates the size and marks the component dirty on change. + * + * @param size New size vector. + */ + void setSize(const vec2 size) noexcept { + + if (size_.same(size)) { + return; + } + + size_ = size; + isDirty_ = true; + }; + + + }; + +} diff --git a/include/helios/spatial/transform/components/_module.ixx b/include/helios/spatial/transform/components/_module.ixx index 9820b23a5e..e4d7b969e7 100644 --- a/include/helios/spatial/transform/components/_module.ixx +++ b/include/helios/spatial/transform/components/_module.ixx @@ -5,6 +5,7 @@ export module helios.spatial.transform.components; export import helios.spatial.transform.components.ScaleStateComponent; +export import helios.spatial.transform.components.SizeComponent; export import helios.spatial.transform.components.DimensionComponent; export import helios.spatial.transform.components.ComposeTransformComponent; export import helios.spatial.transform.components.TranslationStateComponent; diff --git a/include/helios/state/CombinedStateToIdMapPair.ixx b/include/helios/state/CombinedStateToIdMapPair.ixx index 8d36f076fd..4f08cc3581 100644 --- a/include/helios/state/CombinedStateToIdMapPair.ixx +++ b/include/helios/state/CombinedStateToIdMapPair.ixx @@ -13,6 +13,7 @@ module; #include #include #include +#include export module helios.state.CombinedStateToIdMapPair; diff --git a/include/helios/state/StateManager.ixx b/include/helios/state/StateManager.ixx index 542c23a04f..657ae5e8c1 100644 --- a/include/helios/state/StateManager.ixx +++ b/include/helios/state/StateManager.ixx @@ -25,7 +25,7 @@ import helios.runtime.world.GameObject; import helios.runtime.world.UpdateContext; -import helios.runtime.world.GameWorld; +import helios.runtime.messaging.command.CommandHandlerRegistry; import helios.runtime.world.Session; import helios.runtime.world.Session; @@ -255,13 +255,13 @@ export namespace helios::state { }; /** - * @brief Initializes the manager and registers with GameWorld. + * @brief Initializes the manager and registers command handlers. * - * @param gameWorld The game world to register with. + * @param commandHandlerRegistry The command-handler registry to register with. */ - void init(helios::runtime::world::GameWorld& gameWorld) { - gameWorld.registerCommandHandler>(*this); - gameWorld.registerCommandHandler>(*this); + void init(helios::runtime::messaging::command::CommandHandlerRegistry& commandHandlerRegistry) { + commandHandlerRegistry.registerHandler>(*this); + commandHandlerRegistry.registerHandler>(*this); } /** diff --git a/include/helios/state/StateToIdMap.ixx b/include/helios/state/StateToIdMap.ixx index 2a66179e0e..9128ef0ea2 100644 --- a/include/helios/state/StateToIdMap.ixx +++ b/include/helios/state/StateToIdMap.ixx @@ -11,6 +11,7 @@ module; #include #include #include +#include export module helios.state.StateToIdMap; diff --git a/include/helios/state/commands/DelayedStateCommand.ixx b/include/helios/state/commands/DelayedStateCommand.ixx index eaed0eaba6..f87bd1d7d4 100644 --- a/include/helios/state/commands/DelayedStateCommand.ixx +++ b/include/helios/state/commands/DelayedStateCommand.ixx @@ -9,14 +9,14 @@ module; export module helios.state.commands.DelayedStateCommand; -import helios.gameplay.timing.types.GameTimerId; +import helios.runtime.timing.types.TimerId; import helios.state.Bindings; import helios.state.types.StateTransitionRequest; using namespace helios::state::types; -using namespace helios::gameplay::timing::types; +using namespace helios::runtime::timing::types; export namespace helios::state::commands { @@ -26,7 +26,7 @@ export namespace helios::state::commands { * * @details Unlike StateCommand, which triggers a transition immediately * during the next flush, DelayedStateCommand associates the request with - * a GameTimerId. The TypedCommandBuffer holds the command until the + * a TimerId. The TypedCommandBuffer holds the command until the * referenced timer expires, then forwards it to the StateManager. * * When the StateManager receives a DelayedStateCommand via submit(), @@ -50,7 +50,7 @@ export namespace helios::state::commands { /** * @brief The timer that gates this command. */ - GameTimerId timerId_; + TimerId timerId_; public: @@ -60,7 +60,7 @@ export namespace helios::state::commands { * @param transitionRequest The transition to perform when the timer fires. * @param timerId Identifier of the timer that triggers this command. */ - explicit DelayedStateCommand(StateTransitionRequest transitionRequest, GameTimerId timerId) + explicit DelayedStateCommand(StateTransitionRequest transitionRequest, TimerId timerId) : transitionRequest_(transitionRequest), timerId_(timerId) {} @@ -77,9 +77,9 @@ export namespace helios::state::commands { /** * @brief Returns the associated timer identifier. * - * @return The GameTimerId that gates this command. + * @return The TimerId that gates this command. */ - [[nodiscard]] GameTimerId gameTimerId() const noexcept { + [[nodiscard]] TimerId timerId() const noexcept { return timerId_; } diff --git a/include/helios/tooling/FramePacer.ixx b/include/helios/tooling/FramePacer.ixx index 6c7115a61a..220cb36a54 100644 --- a/include/helios/tooling/FramePacer.ixx +++ b/include/helios/tooling/FramePacer.ixx @@ -47,7 +47,7 @@ export namespace helios::tooling { /** * @brief The stopwatch used for high-resolution time measurement. */ - std::unique_ptr stopwatch_; + helios::util::time::Stopwatch stopwatch_{}; /** * @brief The target frame rate in Frames Per Second (FPS). @@ -55,18 +55,6 @@ export namespace helios::tooling { float targetFps_ = 0.0f; public: - /** - * @brief Constructs a FramePacer with the given stopwatch. - * - * Initializes a new FramePacer instance in unlimited FPS mode (targetFps = 0.0f). - * - * @param stopwatch Unique pointer to a valid `Stopwatch` instance. Ownership - * is transferred to the FramePacer. - */ - explicit FramePacer(std::unique_ptr stopwatch) : - stopwatch_(std::move(stopwatch)) { - assert(stopwatch_ && "FramePacer requires a valid Stopwatch (non-null)"); - } /** * @brief Sets the desired target frame rate. @@ -101,7 +89,7 @@ export namespace helios::tooling { * before any game logic, physics, or rendering operations. */ void beginFrame() { - stopwatch_->start(); + stopwatch_.start(); } /** @@ -121,7 +109,7 @@ export namespace helios::tooling { * to improve timing precision and mitigate OS scheduler wake-up latency. */ [[nodiscard]] FrameStats sync() { - float workTime = stopwatch_->elapsedSeconds(); + float workTime = stopwatch_.elapsedSeconds(); float waitTime = 0.0f; float totalTime = workTime; @@ -132,7 +120,7 @@ export namespace helios::tooling { auto requestedWaitTime = targetTime - workTime; auto sleepDuration = std::chrono::duration(requestedWaitTime); std::this_thread::sleep_for(sleepDuration); - totalTime = stopwatch_->elapsedSeconds(); + totalTime = stopwatch_.elapsedSeconds(); waitTime = totalTime - workTime; } } diff --git a/include/helios/ui/UiActionCommandManager.ixx b/include/helios/ui/UiActionCommandManager.ixx index 636ea37c9c..cae7d2fcd6 100644 --- a/include/helios/ui/UiActionCommandManager.ixx +++ b/include/helios/ui/UiActionCommandManager.ixx @@ -20,7 +20,7 @@ import helios.runtime.world.GameObject; import helios.runtime.world.UpdateContext; -import helios.runtime.world.GameWorld; +import helios.runtime.messaging.command.CommandHandlerRegistry; import helios.runtime.world.tags.ManagerRole; @@ -68,7 +68,6 @@ export namespace helios::ui { /** * @brief Processes all pending commands by invoking their registered policies. * - * @param gameWorld The game world. * @param update_context The current update context. */ void flush( @@ -119,12 +118,12 @@ export namespace helios::ui { } /** - * @brief Initializes the manager and registers it with the game world. + * @brief Initializes the manager and registers it in the command-handler registry. * - * @param gameWorld The game world to register with. + * @param commandHandlerRegistry The command-handler registry to register with. */ - void init(helios::runtime::world::GameWorld& gameWorld) { - gameWorld.registerCommandHandler>(*this); + void init(helios::runtime::messaging::command::CommandHandlerRegistry& commandHandlerRegistry) { + commandHandlerRegistry.registerHandler>(*this); } }; diff --git a/include/helios/ui/binding/systems/GameTimer2UiTextUpdateSystem.ixx b/include/helios/ui/binding/systems/GameTimer2UiTextUpdateSystem.ixx index 06a244accc..330b2720c1 100644 --- a/include/helios/ui/binding/systems/GameTimer2UiTextUpdateSystem.ixx +++ b/include/helios/ui/binding/systems/GameTimer2UiTextUpdateSystem.ixx @@ -1,5 +1,5 @@ /** - * @file GameTimer2UiTextUpdateSystem.ixx + * @file Timer2UiTextUpdateSystem.ixx * @brief System for binding game timer values to UI text components. */ module; @@ -9,13 +9,14 @@ module; -export module helios.ui.binding.systems.GameTimer2UiTextUpdateSystem; +export module helios.ui.binding.systems.Timer2UiTextUpdateSystem; -import helios.gameplay.timing.TimerManager; -import helios.gameplay.timing.components; +import helios.runtime.timing.TimerManager; +import helios.runtime.timing.components; import helios.runtime.world.GameWorld; import helios.runtime.world.UpdateContext; +import helios.runtime.world.tags.SystemRole; import helios.ui.widgets; @@ -23,25 +24,24 @@ import helios.ui.layout; import helios.ecs.components.Active; -using namespace helios::gameplay::timing; +using namespace helios::runtime::timing; -import helios.runtime.world.tags.SystemRole; export namespace helios::ui::binding::systems { /** * @brief System for binding game timer values to UI text components. * - * Queries entities with GameTimerBindingComponent, TimeFormatterComponent, + * Queries entities with TimerBindingComponent, TimeFormatterComponent, * and UiTextComponent. When the observed timer's revision changes, the * formatted time string is propagated to the text component for display. * * @see TimerManager - * @see GameTimerBindingComponent + * @see TimerBindingComponent * @see TimeFormatterComponent */ template - class GameTimer2UiTextUpdateSystem { + class Timer2UiTextUpdateSystem { /** * @brief Reference to the TimerManager that owns the game timers. @@ -56,7 +56,7 @@ export namespace helios::ui::binding::systems { * * @param timerManager The TimerManager providing game timer state. */ - explicit GameTimer2UiTextUpdateSystem(TimerManager& timerManager) + explicit Timer2UiTextUpdateSystem(TimerManager& timerManager) : timerManager_(timerManager) {} @@ -74,13 +74,13 @@ export namespace helios::ui::binding::systems { for (auto [entity, gtc, dfc, txt, active] : updateContext.view< THandle, - helios::gameplay::timing::components::GameTimerBindingComponent, + helios::runtime::timing::components::TimerBindingComponent, helios::ui::layout::components::TimeFormatterComponent, helios::ui::widgets::components::UiTextComponent, helios::ecs::components::Active >().whereEnabled()) { - if (const auto* timer = timerManager_.gameTimer(gtc->gameTimerId()); + if (const auto* timer = timerManager_.getTimer(gtc->timerId()); timer->timerRevision() != gtc->timerRevision()) { txt->setText(dfc->format(timer->elapsed(), timer->duration())); gtc->setTimerRevision(timer->timerRevision()); diff --git a/include/helios/ui/binding/systems/Lives2UiTextUpdateSystem.ixx b/include/helios/ui/binding/systems/Lives2UiTextUpdateSystem.ixx index 8218db1b05..631a38d032 100644 --- a/include/helios/ui/binding/systems/Lives2UiTextUpdateSystem.ixx +++ b/include/helios/ui/binding/systems/Lives2UiTextUpdateSystem.ixx @@ -16,6 +16,7 @@ import helios.gameplay.matchstate.types; import helios.runtime.world.GameWorld; import helios.runtime.world.UpdateContext; +import helios.runtime.world.tags.SystemRole; import helios.ui.widgets; @@ -26,7 +27,6 @@ import helios.ecs.components.Active; using namespace helios::gameplay::matchstate::components; using namespace helios::gameplay::matchstate::types; -import helios.runtime.world.tags.SystemRole; export namespace helios::ui::binding::systems { @@ -65,7 +65,7 @@ export namespace helios::ui::binding::systems { continue; } - if (auto* lc = go->get()) { + if (auto* lc = go->template get()) { if (lbc->livesRevision() != lc->livesRevision()) { txt->setText(dfc->format(lc->lives())); lbc->setLivesRevision(lc->livesRevision()); diff --git a/include/helios/ui/binding/systems/MaxScore2UiTextUpdateSystem.ixx b/include/helios/ui/binding/systems/MaxScore2UiTextUpdateSystem.ixx index 4fbabdee63..56d73bd32b 100644 --- a/include/helios/ui/binding/systems/MaxScore2UiTextUpdateSystem.ixx +++ b/include/helios/ui/binding/systems/MaxScore2UiTextUpdateSystem.ixx @@ -23,10 +23,9 @@ import helios.ui.widgets.components.UiTextComponent; import helios.ecs.components.Active; -using namespace helios::gameplay::scoring; - import helios.runtime.world.tags.SystemRole; +using namespace helios::gameplay::scoring; export namespace helios::ui::binding::systems { /** @@ -70,7 +69,7 @@ export namespace helios::ui::binding::systems { continue; } - txt->setText(nfc->format(soc->maxScore())); + txt->setText(nfc->template format(soc->maxScore())); } } diff --git a/include/helios/ui/binding/systems/Score2UiTextUpdateSystem.ixx b/include/helios/ui/binding/systems/Score2UiTextUpdateSystem.ixx index aa22d90c1f..f398badad2 100644 --- a/include/helios/ui/binding/systems/Score2UiTextUpdateSystem.ixx +++ b/include/helios/ui/binding/systems/Score2UiTextUpdateSystem.ixx @@ -64,7 +64,7 @@ export namespace helios::ui::binding::systems { continue; } - txt->setText(nfc->format(soc->totalScore())); + txt->setText(nfc->template format(soc->totalScore())); } } diff --git a/include/helios/ui/binding/systems/_module.ixx b/include/helios/ui/binding/systems/_module.ixx index ea8615c81f..b665ba8363 100644 --- a/include/helios/ui/binding/systems/_module.ixx +++ b/include/helios/ui/binding/systems/_module.ixx @@ -6,5 +6,5 @@ export module helios.ui.binding.systems; export import helios.ui.binding.systems.Score2UiTextUpdateSystem; export import helios.ui.binding.systems.MaxScore2UiTextUpdateSystem; -export import helios.ui.binding.systems.GameTimer2UiTextUpdateSystem; +export import helios.ui.binding.systems.Timer2UiTextUpdateSystem; export import helios.ui.binding.systems.Lives2UiTextUpdateSystem; diff --git a/include/helios/ui/layout/components/TimeFormatterComponent.ixx b/include/helios/ui/layout/components/TimeFormatterComponent.ixx index 9c9149ab7d..75cea436f2 100644 --- a/include/helios/ui/layout/components/TimeFormatterComponent.ixx +++ b/include/helios/ui/layout/components/TimeFormatterComponent.ixx @@ -22,7 +22,7 @@ export namespace helios::ui::layout::components { * The format string receives two integer arguments: minutes and seconds. * * @see TimeDisplayMode - * @see GameTimer2UiTextUpdateSystem + * @see Timer2UiTextUpdateSystem */ template class TimeFormatterComponent { diff --git a/include/helios/ui/transform/systems/UiTransformSystem.ixx b/include/helios/ui/transform/systems/UiTransformSystem.ixx index e70f254799..56a6cc82d6 100644 --- a/include/helios/ui/transform/systems/UiTransformSystem.ixx +++ b/include/helios/ui/transform/systems/UiTransformSystem.ixx @@ -6,6 +6,7 @@ module; #include #include +#include export module helios.ui.transform.systems.UiTransformSystem; @@ -101,6 +102,9 @@ export namespace helios::ui::transform::systems { case helios::ui::layout::Anchor::BottomLeft: return unanchored; + + default: + break; } assert(false && "Unreachable!"); @@ -183,7 +187,7 @@ export namespace helios::ui::transform::systems { using Handle = typename std::remove_cvref_t::Handle_type; - auto* hc = entity.get>(); + auto* hc = entity.template get>(); if (!hc || !hc->parent()) { continue; @@ -191,8 +195,8 @@ export namespace helios::ui::transform::systems { // we rely on the parent entity so we do not have to wait for the SceneGraph sync if (auto parentGo = updateContext.find(hc->parent().value())) { - auto* pmaabbcc = parentGo->get>(); - auto* pctc = parentGo->get>(); + auto* pmaabbcc = parentGo->template get>(); + auto* pctc = parentGo->template get>(); auto size = pmaabbcc->aabb().size() * pctc->localScaling(); diff --git a/include/helios/ui/widgets/systems/MenuDisplaySystem.ixx b/include/helios/ui/widgets/systems/MenuDisplaySystem.ixx index 1400968764..7fb2338077 100644 --- a/include/helios/ui/widgets/systems/MenuDisplaySystem.ixx +++ b/include/helios/ui/widgets/systems/MenuDisplaySystem.ixx @@ -109,7 +109,7 @@ export namespace helios::ui::widgets::systems { if (mc->menuItems().size() > mc->selectedIndex()) { auto menuItem = updateContext.find(mc->menuItems()[mc->selectedIndex()]); - menuItem->getOrAdd(); + menuItem->template getOrAdd(); } break; } @@ -198,7 +198,7 @@ export namespace helios::ui::widgets::systems { for (auto& handle : inactiveItems_) { if (auto entity = updateContext.find(handle)) { - entity->remove(); + entity->template remove(); } } diff --git a/include/helios/ui/widgets/systems/MenuNavigationSystem.ixx b/include/helios/ui/widgets/systems/MenuNavigationSystem.ixx index 387ae42684..7ca03f3c9e 100644 --- a/include/helios/ui/widgets/systems/MenuNavigationSystem.ixx +++ b/include/helios/ui/widgets/systems/MenuNavigationSystem.ixx @@ -24,6 +24,7 @@ import helios.ui.widgets.components.UiActionComponent; import helios.ui.widgets.commands.UiActionCommand; import helios.runtime.world; +import helios.runtime.world.tags.SystemRole; import helios.runtime.messaging.command.NullCommandBuffer; import helios.runtime.messaging.command.concepts.IsCommandBufferLike; @@ -49,9 +50,7 @@ using namespace helios::runtime::messaging::command::concepts; using namespace helios::ui::widgets::components; using namespace helios::ecs::components; using namespace helios::input::gamepad; - -import helios.runtime.world.tags.SystemRole; - +using namespace helios::ui::widgets::commands; export namespace helios::ui::widgets::systems { /** @@ -98,10 +97,10 @@ export namespace helios::ui::widgets::systems { return; } - UiStateComponent* usc = nullptr; + UiStateComponent* usc = nullptr; if (index != prevIndex) { - usc = updateContext.find(menuItems[prevIndex])->get(); + usc = updateContext.find(menuItems[prevIndex])->template get(); if (usc) { usc->setSelected(false); } @@ -109,7 +108,7 @@ export namespace helios::ui::widgets::systems { mc->setSelectedIndex(index); // update ui state - usc = updateContext.find(menuItems[index])->get(); + usc = updateContext.find(menuItems[index])->template get(); if (usc) { usc->setSelected(true); } @@ -121,6 +120,7 @@ export namespace helios::ui::widgets::systems { public: using EngineRoleTag = helios::runtime::tags::SystemRole; + using CommandBuffer_type = TCommandBuffer; /** @@ -132,7 +132,7 @@ export namespace helios::ui::widgets::systems { * * @param updateContext The current frame's update context. */ - void update(helios::runtime::world::UpdateContext& updateContext) noexcept { + void update(helios::runtime::world::UpdateContext& updateContext, TCommandBuffer& cmdBuffer) noexcept { MenuComponent* focusedMenu = nullptr; @@ -140,8 +140,8 @@ export namespace helios::ui::widgets::systems { THandle, UiFocusComponent, HierarchyComponent, Active >().whereEnabled()) { - assert(hc->parent() && updateContext.find(*hc->parent()) && updateContext.find(*hc->parent())->get>() && "Item expected to have parent menu component."); - focusedMenu = updateContext.find(*hc->parent())->get>(); + assert(hc->parent() && updateContext.find(*hc->parent()) && updateContext.find(*hc->parent())->template get>() && "Item expected to have parent menu component."); + focusedMenu = updateContext.find(*hc->parent())->template get>(); break; } @@ -174,12 +174,11 @@ export namespace helios::ui::widgets::systems { } if (gamepadState.isButtonPressed(GamepadInput::A)) { - auto* uac = updateContext.find(focusedMenu->menuItems()[focusedMenu->selectedIndex()])->get< - helios::ui::widgets::components::UiActionComponent - >(); + auto* uac = updateContext.find( + focusedMenu->menuItems()[focusedMenu->selectedIndex()])->template get>(); if (uac) { - updateContext.queueCommand( + cmdBuffer.template add( focusedMenu->menuItems()[focusedMenu->selectedIndex()], uac->actionId() ); }