Skip to main content

GameWorld.ixx File

Central game state container for entities, resources, and the active level. More...

Included Headers

#include <cassert> #include <format> #include <helios/helios_config.h> #include <memory> #include <optional> #include <span> #include <string> #include <unordered_map> #include <helios.engine.common.concepts> #include <helios.engine.runtime.world.Session> #include <helios.engine.runtime.messaging.command.CommandHandlerRegistry> #include <helios.util.log.LogManager> #include <helios.util.log.Logger> #include <helios.engine.ecs.View> #include <helios.engine.ecs.GameObject> #include <helios.engine.runtime.world.Manager> #include <helios.engine.runtime.world.ResourceRegistry> #include <helios.util.Guid> #include <helios.engine.ecs.EntityManager> #include <helios.engine.runtime.world.UpdateContext> #include <helios.engine.runtime.world.Level> #include <helios.engine.ecs.EntityHandle> #include <helios.engine.ecs.EntityRegistry>

Namespaces Index

namespacehelios
namespaceengine

Main engine module aggregating core infrastructure and game systems. More...

namespaceruntime

Runtime infrastructure for game execution and lifecycle orchestration. More...

namespaceworld

World state management, resource registry, and per-frame update context. More...

Classes Index

classGameWorld

Central game state container for entities, resources, and the active level. More...

Macro Definitions Index

#defineHELIOS_LOG_SCOPE   "GameWorld"

Description

Central game state container for entities, resources, and the active level.

Macro Definitions

HELIOS_LOG_SCOPE

#define HELIOS_LOG_SCOPE   "GameWorld"

Definition at line 46 of file GameWorld.ixx.

46#define HELIOS_LOG_SCOPE "GameWorld"

File Listing

The file content with the documentation metadata removed is:

1/**
2 * @file GameWorld.ixx
3 * @brief Central game state container for entities, resources, and the active level.
4 */
5module;
6
7
8#include <cassert>
9#include <format>
10#include <helios/helios_config.h>
11#include <memory>
12#include <optional>
13#include <span>
14#include <string>
15#include <unordered_map>
16
17export module helios.engine.runtime.world.GameWorld;
18
19import helios.engine.runtime.world.Session;
20
21import helios.engine.runtime.messaging.command.CommandHandlerRegistry;
22
23import helios.engine.runtime.world.ResourceRegistry;
24
25import helios.engine.runtime.world.UpdateContext;
26import helios.engine.ecs.GameObject;
27import helios.engine.runtime.world.Manager;
28
29import helios.util.Guid;
30import helios.util.log.Logger;
31import helios.util.log.LogManager;
32import helios.engine.runtime.world.Level;
33
34import helios.engine.ecs.EntityHandle;
35import helios.engine.ecs.EntityManager;
36import helios.engine.ecs.EntityRegistry;
37import helios.engine.ecs.View;
38
39import helios.engine.common.concepts;
40
41
42
45
46#define HELIOS_LOG_SCOPE "GameWorld"
47export namespace helios::engine::runtime::world {
48
49
50 /**
51 * @brief Central game state container for entities, resources, and the active level.
52 *
53 * @details
54 * GameWorld is the root container for all runtime game state. It owns the
55 * EntityRegistry/EntityManager for entity lifecycle, a ResourceRegistry for
56 * type-indexed O(1) access to Managers, CommandBuffers, and CommandHandlers,
57 * a Session for cross-frame state tracking, and the current Level.
58 *
59 * ## Key Responsibilities
60 *
61 * - **Entity Management:** Creates entities via `addGameObject()` returning
62 * lightweight (~16 bytes) `GameObject` wrappers. Entities are identified by
63 * versioned `EntityHandle` for stale reference detection.
64 * - **Resource Registry:** Provides `resourceRegistry()` for registering and
65 * looking up Managers, CommandBuffers, and CommandHandlers with O(1) access.
66 * - **Component Queries:** Efficient iteration via `view<Components...>()`
67 * with optional `.whereEnabled()` and `.exclude<T>()` filtering.
68 * - **Entity Cloning:** Deep-copies entities with all components via `clone()`.
69 * - **Manager Coordination:** Initializes, flushes, and resets Managers that
70 * handle cross-cutting concerns (spawning, scoring, pooling).
71 * - **Session:** Holds per-run state (tracked game/match states, scores).
72 * - **Level Management:** Holds the active Level with arena bounds.
73 *
74 * ## Usage with GameLoop
75 *
76 * Systems access the GameWorld indirectly via UpdateContext. Entity queries
77 * use views, while mutations are performed through commands submitted via
78 * `UpdateContext::queueCommand<T>()`.
79 *
80 * ```cpp
81 * void update(UpdateContext& ctx) noexcept {
82 * for (auto [entity, transform, velocity, active] : ctx.view<
83 * TransformComponent,
84 * VelocityComponent,
85 * Active
86 * >().whereEnabled()) {
87 * // Process matching entities
88 * }
89 *
90 * ctx.queueCommand<DespawnCommand>(handle, profileId);
91 * }
92 * ```
93 *
94 * ## Resource Registration
95 *
96 * ```cpp
97 * auto& poolMgr = gameWorld.registerManager<GameObjectPoolManager>();
98 * auto& spawnMgr = gameWorld.registerManager<SpawnManager>();
99 * auto& cmdBuf = gameWorld.registerCommandBuffer<EngineCommandBuffer>();
100 *
101 * gameWorld.init(); // Calls init() on all Managers in registration order
102 * ```
103 *
104 * ## Entity Lifecycle
105 *
106 * ```cpp
107 * auto player = gameWorld.addGameObject();
108 * player.add<TransformComponent>(position);
109 * player.add<HealthComponent>(100.0f);
110 * player.setActive(true);
111 *
112 * if (auto entity = gameWorld.find(handle)) {
113 * entity->get<HealthComponent>()->takeDamage(10.0f);
114 * }
115 *
116 * auto clone = gameWorld.clone(player);
117 * ```
118 *
119 * @see ResourceRegistry
120 * @see GameObject
121 * @see EntityHandle
122 * @see EntityManager
123 * @see View
124 * @see UpdateContext
125 * @see Manager
126 * @see Session
127 * @see Level
128 */
129 class GameWorld {
130
131
132
133 protected:
134
135
136 /**
137 * @brief The logger used with this GameWorld instance.
138 *
139 * Defaults to HELIOS_LOG_SCOPE.
140 */
143
144 /**
145 * @brief The current level loaded in the game world.
146 *
147 * @details Can be null if no level is currently active.
148 */
149 std::unique_ptr<Level> level_ = nullptr;
150
151
152 /**
153 * @brief Type-indexed registry for Managers and CommandBuffers.
154 *
155 * @details Provides O(1) type-based access via ManagerRegistry and
156 * CommandBufferRegistry. Owns all registered Manager and CommandBuffer
157 * instances via ConceptModelRegistry.
158 */
160
161 /**
162 * @brief Registry mapping command types to their handler function pointers.
163 *
164 * @details Used by TypedCommandBuffer during flush to route commands
165 * to the correct handler. Handlers are registered via
166 * `registerCommandHandler<CommandTypes...>(owner)`.
167 */
169
170 /**
171 * @brief Entity registry for handle allocation and validation.
172 *
173 * @details Manages entity lifecycle including creation, destruction,
174 * and stale handle detection via versioning.
175 */
177
178 /**
179 * @brief Entity manager for component storage.
180 *
181 * @details Stores components in type-indexed SparseSets and provides
182 * methods for component manipulation. Marked `mutable` to allow const
183 * methods to use it without const_cast.
184 */
186
187 /**
188 * @brief The current game session holding state data.
189 */
191
192
193
194 public:
195
196 /**
197 * @brief Constructs the GameWorld.
198 *
199 * @details Initializes the EntityManager with the internal EntityRegistry
200 * and creates a Session backed by a dedicated GameObject.
201 *
202 * @param capacity Initial capacity for the underlying SparseSets.
203 * Must be large enough to accommodate the total number of
204 * entities (including pooled clones) to avoid reallocation
205 * during cloning. Defaults to ENTITY_MANAGER_DEFAULT_CAPACITY.
206 */
207 explicit GameWorld(const size_t capacity = ENTITY_MANAGER_DEFAULT_CAPACITY)
208 : em_(helios::engine::ecs::EntityManager(entityRegistry_, capacity)),
210 {};
211
212 /// @brief Non-copyable.
213 GameWorld(const GameWorld&) = delete;
214 /// @brief Non-copyable.
215 GameWorld operator=(const GameWorld&) = delete;
216
217 /// @brief Non-movable.
218 GameWorld(const GameWorld&&) = delete;
219 /// @brief Non-movable.
220 GameWorld operator=(const GameWorld&&) = delete;
221
222 /**
223 * @brief Returns a reference to the current game session.
224 *
225 * @return Reference to the Session.
226 */
227 [[nodiscard]] Session& session() {
228 return session_;
229 }
230
231 /**
232 * @brief Returns a reference to the underlying EntityManager.
233 *
234 * @return Reference to the EntityManager.
235 */
237 return em_;
238 }
239
240 /**
241 * @brief Initializes all registered managers.
242 *
243 * @details Should be called after all managers have been added and before
244 * the game loop starts. Each manager's init() method is invoked with a
245 * reference to this GameWorld.
246 */
247 void init() {
248 for (auto& mgr : resourceRegistry_.managers()) {
249 mgr->init(*this);
250 }
251 }
252
253 /**
254 * @brief Sets the current level for the game world.
255 *
256 * @param level Unique pointer to the Level instance. Ownership is transferred to the GameWorld.
257 */
258 void setLevel(std::unique_ptr<Level> level) noexcept {
259 level_ = std::move(level);
260 }
261
262 /**
263 * @brief Checks if a level is currently loaded.
264 *
265 * @return True if a level is set, false otherwise.
266 */
267 [[nodiscard]] bool hasLevel() const noexcept{
268 return level_ != nullptr;
269 }
270
271 /**
272 * @brief Retrieves the currently loaded level.
273 *
274 * @return Const pointer to the active Level.
275 *
276 * @warning Calling this method when hasLevel() returns false results in undefined behavior.
277 */
278 [[nodiscard]] const Level* level() const noexcept{
279 return level_.get();
280 }
281
282 /**
283 * @brief Checks whether a Manager of type T is registered.
284 *
285 * @tparam T The Manager type. Must satisfy IsManagerLike.
286 *
287 * @return True if the Manager is registered.
288 */
289 template<typename T>
290 requires IsManagerLike<T>
291 [[nodiscard]] bool hasManager() const {
292 return resourceRegistry_.has<T>();
293 }
294
295 /**
296 * @brief Checks whether a CommandBuffer of type T is registered.
297 *
298 * @tparam T The CommandBuffer type. Must satisfy IsCommandBufferLike.
299 *
300 * @return True if the CommandBuffer is registered.
301 */
302 template<typename T>
303 requires IsCommandBufferLike<T>
304 [[nodiscard]] bool hasCommandBuffer() const {
305 return resourceRegistry_.has<T>();
306 }
307
308 /**
309 * @brief Registers and constructs a Manager of type T.
310 *
311 * @details Delegates to ResourceRegistry::emplace. The Manager is
312 * constructed in-place with forwarded arguments and owned by the
313 * ManagerRegistry.
314 *
315 * @tparam T The Manager type. Must satisfy IsManagerLike.
316 * @tparam Args Constructor argument types.
317 *
318 * @param args Arguments forwarded to the T constructor.
319 *
320 * @return Reference to the newly registered Manager.
321 */
322 template<typename T, typename... Args>
323 requires IsManagerLike<T>
324 T& registerManager(Args&&... args) {
325 return resourceRegistry_.emplace<T>(std::forward<Args>(args)...);
326 }
327
328 /**
329 * @brief Registers and constructs a CommandBuffer of type T.
330 *
331 * @details Delegates to ResourceRegistry::emplace. The buffer is
332 * constructed in-place with forwarded arguments and owned by the
333 * CommandBufferRegistry.
334 *
335 * @tparam T The CommandBuffer type. Must satisfy IsCommandBufferLike.
336 * @tparam Args Constructor argument types.
337 *
338 * @param args Arguments forwarded to the T constructor.
339 *
340 * @return Reference to the newly registered CommandBuffer.
341 */
342 template<typename T, typename... Args>
343 requires IsCommandBufferLike<T>
344 T& registerCommandBuffer(Args&&... args) {
345 return resourceRegistry_.emplace<T>(std::forward<Args>(args)...);
346 }
347
348 /**
349 * @brief Retrieves a registered Manager by type.
350 *
351 * @tparam T The Manager type. Must satisfy IsManagerLike.
352 *
353 * @return Reference to the Manager.
354 *
355 * @pre A Manager of type T must be registered.
356 */
357 template<typename T>
358 requires IsManagerLike<T>
359 T& manager() const noexcept {
360 assert(resourceRegistry_.has<T>(), "Manager not registered");
361 return resourceRegistry_.get<T>();
362 }
363
364 /**
365 * @brief Retrieves a registered Manager by type, or nullptr if not found.
366 *
367 * @tparam T The Manager type. Must satisfy IsManagerLike.
368 *
369 * @return Pointer to the Manager, or nullptr if not registered.
370 */
371 template<typename T>
372 requires IsManagerLike<T>
373 T* tryManager() const noexcept {
374 return resourceRegistry_.tryGet<T>();
375 }
376
377 /**
378 * @brief Retrieves a registered CommandBuffer by type, or nullptr if not found.
379 *
380 * @tparam T The CommandBuffer type. Must satisfy IsCommandBufferLike.
381 *
382 * @return Pointer to the CommandBuffer, or nullptr if not registered.
383 */
384 template<typename T>
385 requires IsCommandBufferLike<T>
386 T* tryCommandBuffer() const noexcept {
387 return resourceRegistry_.tryGet<T>();
388 }
389
390 /**
391 * @brief Retrieves a registered CommandBuffer by type.
392 *
393 * @tparam T The CommandBuffer type. Must satisfy IsCommandBufferLike.
394 *
395 * @pre A CommandBuffer of type `T` must already be registered.
396 * Use tryCommandBuffer<T>() when the buffer is optional.
397 *
398 * @return Reference to the CommandBuffer.
399 */
400 template<typename T>
401 requires IsCommandBufferLike<T>
402 T& commandBuffer() const noexcept {
403 return resourceRegistry_.get<T>();
404 }
405
406
407 /**
408 * @brief Registers a command handler for one or more command types.
409 *
410 * @details Stores a type-erased function pointer for each CommandType
411 * that routes to `owner.submit(cmd)`. During flush, the
412 * TypedCommandBuffer uses the CommandHandlerRegistry to dispatch
413 * queued commands to the registered handler.
414 *
415 * @tparam CommandType The command types to register handlers for.
416 * @tparam OwningT The handler type. Must satisfy IsCommandHandlerLike.
417 *
418 * @param owner Reference to the handler instance. Must outlive the GameWorld.
419 *
420 * @see CommandHandlerRegistry
421 */
422 template<typename... CommandType, typename OwningT>
423 requires IsCommandHandlerLike<OwningT, CommandType...>
424 void registerCommandHandler(OwningT& owner) {
425 (commandHandlerRegistry_.template registerHandler<CommandType>(owner), ...);
426 }
427
428 /**
429 * @brief Returns a reference to the CommandHandlerRegistry.
430 *
431 * @return Reference to the CommandHandlerRegistry.
432 */
435 }
436
437 /**
438 * @brief Flushes all registered Managers.
439 *
440 * @details Iterates over all Managers in registration order and invokes
441 * `flush(updateContext)` on each. Called by the GameLoop at commit points
442 * after the CommandBuffer has been flushed.
443 *
444 * @param updateContext The current frame's update context.
445 */
446 void flushManagers(UpdateContext& updateContext) {
447 for (auto& mgr : resourceRegistry_.managers()) {
448 mgr->flush(updateContext);
449 }
450 }
451
452 /**
453 * @brief Flushes all registered CommandBuffers.
454 *
455 * @details Iterates over all CommandBuffers in registration order and
456 * invokes `flush(*this, updateContext)` on each. Called by the GameLoop
457 * at commit points before Managers are flushed.
458 *
459 * @param updateContext The current frame's update context.
460 */
461 void flushCommandBuffers(UpdateContext& updateContext) {
462 for (auto& buff : resourceRegistry_.commandBuffers()) {
463 buff->flush(*this, updateContext);
464 }
465 }
466
467 /**
468 * @brief Creates a new GameObject in the world.
469 *
470 * @details Allocates an entity handle via the EntityRegistry and returns
471 * a lightweight GameObject wrapper. The returned GameObject is ~16 bytes
472 * and should be passed by value.
473 *
474 * @return A new GameObject ready for component attachment.
475 *
476 * @see GameObject
477 * @see EntityManager::create
478 */
480 const auto handle = em_.create();
481 return helios::engine::ecs::GameObject(handle, &em_);
482 }
483
484 /**
485 * @brief Creates a View for iterating entities with specific components.
486 *
487 * @details Returns a lightweight view that iterates over all entities
488 * possessing the specified component types. Use with range-based for loops
489 * and structured bindings.
490 *
491 * ```cpp
492 * for (auto [entity, transform, velocity, active] : gameWorld.view<
493 * TransformComponent,
494 * VelocityComponent,
495 * Active
496 * >().whereEnabled()) {
497 * // Process matching entities
498 * }
499 * ```
500 *
501 * @tparam Components The component types to query for.
502 *
503 * @return A View for iterating matching entities.
504 *
505 * @see View
506 */
507 template <typename... Components>
508 [[nodiscard]] auto view() {
509 return helios::engine::ecs::View<Components...>(&em_);
510 }
511
512 /**
513 * @brief Creates a const View for iterating entities with specific components.
514 *
515 * @tparam Components The component types to query for.
516 *
517 * @return A const View for iterating matching entities.
518 */
519 template <typename... Components>
520 [[nodiscard]] auto view() const {
521 return helios::engine::ecs::View<const Components...>(&em_);
522 }
523
524 /**
525 * @brief Finds a GameObject by its EntityHandle.
526 *
527 * @details Validates the handle and returns a GameObject wrapper if valid.
528 * Returns std::nullopt if the handle is stale or invalid.
529 *
530 * @param handle The EntityHandle to look up.
531 *
532 * @return Optional containing the GameObject if found, std::nullopt otherwise.
533 */
534 [[nodiscard]] std::optional<helios::engine::ecs::GameObject> find(const helios::engine::ecs::EntityHandle handle) {
535 if (!em_.isValid(handle)) {
536 return std::nullopt;
537 }
538
539 return helios::engine::ecs::GameObject(handle, &em_);
540 }
541
542 /**
543 * @brief Attempts to remove an entity from the GameWorld.
544 *
545 * @details Delegates to the EntityManager who is then tasked to remove the
546 * entity and all associated components.
547 *
548 * @param handle The EntityHandle to remove.
549 *
550 * @return True if the entity was removed, otherwise false.
551 *
552 * @see EntityManager::destroy()
553 */
554 [[nodiscard]] bool destroy(const helios::engine::ecs::EntityHandle handle) {
555 return em_.destroy(handle);
556 }
557
558 /**
559 * @brief Finds a GameObject by its EntityHandle (const version).
560 *
561 * @param handle The EntityHandle to look up.
562 *
563 * @return Optional containing the GameObject if found, std::nullopt otherwise.
564 */
565 [[nodiscard]] std::optional<helios::engine::ecs::GameObject> find(const helios::engine::ecs::EntityHandle handle) const {
566
567 if (!em_.isValid(handle)) {
568 return std::nullopt;
569 }
570
571 return helios::engine::ecs::GameObject(handle, &em_);
572 }
573
574 /**
575 * @brief Clones a GameObject and all its components.
576 *
577 * @details Creates a new entity and copies all components from the source
578 * to the target. The new GameObject is initially inactive. Components with
579 * `onClone()` hooks will have them invoked after copy construction.
580 *
581 * @param gameObject The source GameObject to clone.
582 *
583 * @return A new inactive GameObject with cloned components.
584 *
585 * @see EntityManager::clone
586 */
588
589 auto newGo = addGameObject();
590
591 newGo.setActive(false);
592
593 em_.clone(gameObject.entityHandle(), newGo.entityHandle());
594
595
596 return newGo;
597 }
598
599 /**
600 * @brief Resets all managers and the session to their initial state.
601 *
602 * @details Called during level transitions or game restarts to clear
603 * accumulated state. Invokes reset() on all managers and the session.
604 */
605 void reset() {
606
607 for (auto& mgr : resourceRegistry_.managers()) {
608 mgr->reset();
609 }
610
611 session_.reset();
612
613 }
614
615 /**
616 * @brief Returns a reference to the ResourceRegistry.
617 *
618 * @details Use for direct resource access. Prefer the convenience methods
619 * `registerManager()`, `registerCommandBuffer()`, `manager()`,
620 * `tryManager()`, and `tryCommandBuffer()` for type-constrained access.
621 *
622 * @return Reference to the ResourceRegistry.
623 */
625 return resourceRegistry_;
626 }
627
628 /**
629 * @copydoc resourceRegistry()
630 */
631 const ResourceRegistry& resourceRegistry() const noexcept {
632 return resourceRegistry_;
633 }
634
635
636 };
637
638}
639

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.