Skip to main content

GameWorld.ixx File

Central registry for managing game entities. More...

Included Headers

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 and per-frame update context. More...

Classes Index

classGameWorld

Central registry for game entities, managers, pools, and the active level. More...

Macro Definitions Index

#defineHELIOS_LOG_SCOPE   "helios::engine::runtime::world::GameWorld"

Description

Central registry for managing game entities.

Macro Definitions

HELIOS_LOG_SCOPE

#define HELIOS_LOG_SCOPE   "helios::engine::runtime::world::GameWorld"

Definition at line 43 of file GameWorld.ixx.

43#define HELIOS_LOG_SCOPE "helios::engine::runtime::world::GameWorld"

File Listing

The file content with the documentation metadata removed is:

1/**
2 * @file GameWorld.ixx
3 * @brief Central registry for managing game entities.
4 */
5module;
6
7
8#include <cassert>
9#include <format>
10#include <memory>
11#include <optional>
12#include <span>
13#include <string>
14#include <unordered_map>
15
16export module helios.engine.runtime.world.GameWorld;
17
18import helios.engine.runtime.world.UpdateContext;
19import helios.engine.ecs.GameObject;
20import helios.engine.runtime.world.Manager;
21import helios.engine.runtime.spawn.SpawnCommandHandler;
22
23
24
25import helios.engine.mechanics.scoring.ScoreCommandHandler;
26
27import helios.util.Guid;
28import helios.util.log.Logger;
29import helios.util.log.LogManager;
30import helios.engine.runtime.world.Level;
31
32import helios.engine.ecs.EntityHandle;
33import helios.engine.ecs.EntityManager;
34import helios.engine.ecs.EntityRegistry;
35import helios.engine.ecs.View;
36
37import helios.engine.core.data;
38
39import helios.engine.runtime.spawn.SpawnCommandHandlerRegistry;
40
41
42
43#define HELIOS_LOG_SCOPE "helios::engine::runtime::world::GameWorld"
44export namespace helios::engine::runtime::world {
45
46
47 /**
48 * @brief Central registry for game entities, managers, pools, and the active level.
49 *
50 * @details
51 * The GameWorld is the root container for the game state. It manages the lifecycle
52 * of entities via EntityRegistry/EntityManager, coordinates Managers, and holds
53 * the current Level.
54 *
55 * ## Key Responsibilities
56 *
57 * - **Entity Management:** Creates entities via `addGameObject()` which returns
58 * lightweight (~16 bytes) `GameObject` wrappers. Entities are identified by
59 * versioned `EntityHandle` for stale reference detection.
60 * - **Component Queries:** Efficient iteration over entities with specific components
61 * via `view<Components...>()` with optional filtering via `.whereEnabled()` and
62 * `.exclude<T>()`.
63 * - **Entity Cloning:** Deep-copy entities with all components via `clone()`.
64 * - **Pool Management:** Registers and provides access to SpawnCommandHandlers for
65 * entity recycling.
66 * - **Manager Coordination:** Holds Managers that handle cross-cutting concerns
67 * (spawning, projectile pooling) and flushes them each frame.
68 * - **Level Management:** Holds the active Level instance with arena bounds.
69 *
70 * ## Usage with GameLoop
71 *
72 * The GameWorld is passed to Systems via UpdateContext. Systems query entities
73 * using views, while mutations are typically performed through Commands
74 * that are flushed by the CommandBuffer.
75 *
76 * ```cpp
77 * // In a System
78 * void update(UpdateContext& ctx) noexcept override {
79 * for (auto [entity, transform, velocity, active] : gameWorld_->view<
80 * TransformComponent,
81 * VelocityComponent,
82 * Active
83 * >().whereEnabled()) {
84 * // Process entities with both components
85 * }
86 * }
87 * ```
88 *
89 * ## Entity Lifecycle
90 *
91 * ```cpp
92 * // Create entity
93 * auto player = gameWorld.addGameObject();
94 *
95 * // Add components
96 * player.add<TransformComponent>(position);
97 * player.add<HealthComponent>(100.0f);
98 *
99 * // Activate
100 * player.setActive(true);
101 *
102 * // Find by handle
103 * if (auto entity = gameWorld.find(handle)) {
104 * entity->get<HealthComponent>()->takeDamage(10.0f);
105 * }
106 *
107 * // Clone
108 * auto clone = gameWorld.clone(player);
109 * ```
110 *
111 * @see GameObject
112 * @see EntityHandle
113 * @see EntityManager
114 * @see View
115 * @see UpdateContext
116 * @see Manager
117 * @see Level
118 */
119 class GameWorld {
120
121
122
123 protected:
124
125
126 /**
127 * @brief Hash map storing all active GameObjects, indexed by their Guid.
128 *
129 * @details Uses `std::unordered_map` for amortized O(1) average-case lookup.
130 * The map owns all GameObjects via `std::unique_ptr`. Worst-case lookup is O(n)
131 * in case of hash collisions, but the sequential Guid generation ensures good
132 * hash distribution in practice.
133 */
134 std::unordered_map<helios::util::Guid, std::unique_ptr<helios::engine::ecs::GameObject>> gameObjects_;
135
136
137 /**
138 * @brief The logger used with this GameWorld instance.
139 *
140 * Defaults to HELIOS_LOG_SCOPE.
141 */
144
145 /**
146 * @brief Collection of registered Manager instances.
147 *
148 * @details Managers handle cross-cutting concerns such as object pooling,
149 * spawn management, and projectile lifecycle. They are initialized via init()
150 * and flushed each frame via flushManagers().
151 */
152 std::vector<std::unique_ptr<helios::engine::runtime::world::Manager>> managers_;
153
154 /**
155 * @brief The current level loaded in the game world.
156 *
157 * @details Can be null if no level is currently active.
158 */
159 std::unique_ptr<helios::engine::runtime::world::Level> level_ = nullptr;
160
161 /**
162 * @brief Registered handler for score commands.
163 *
164 * @details Receives score update commands and processes them
165 * according to the scoring system logic.
166 */
168
169 /**
170 * @brief Registry for mapping spawn profiles to their command handlers.
171 *
172 * @details
173 * Stores the association between SpawnProfileIds and their corresponding
174 * SpawnCommandHandlers. This allows the system to look up the correct handler
175 * (e.g., a specific object pool) when processing spawn commands.
176 */
178
179 /**
180 * @brief Entity registry for handle allocation and validation.
181 *
182 * @details Manages entity lifecycle including creation, destruction,
183 * and stale handle detection via versioning.
184 */
186
187 /**
188 * @brief Entity manager for component storage.
189 *
190 * @details Stores components in type-indexed SparseSets and provides
191 * methods for component manipulation. Marked `mutable` to allow const
192 * methods to use it without const_cast.
193 */
195
196 public:
197 /**
198
199 * @brief Central registry for game entities, managers, pools, and the active level.
200 *
201 * @details
202 * The GameWorld serves as the primary container for managing the game state and its subsystems.
203 * It facilitates the lifecycle of entities, oversees component-based management, coordinates Managers,
204 * and holds a reference to the active Level instance.
205 */
206 explicit GameWorld() : em_(helios::engine::ecs::EntityManager(entityRegistry_)) { };
207
208
209 /**
210 * @brief Initializes all registered managers.
211 *
212 * @details Should be called after all managers have been added and before
213 * the game loop starts. Each manager's init() method is invoked with a
214 * reference to this GameWorld.
215 */
216 void init() {
217 for (auto& mgr : managers_) {
218 mgr->init(*this);
219 }
220 }
221
222 /**
223 * @brief Sets the current level for the game world.
224 *
225 * @param level Unique pointer to the Level instance. Ownership is transferred to the GameWorld.
226 */
227 void setLevel(std::unique_ptr<helios::engine::runtime::world::Level> level) noexcept {
228 level_ = std::move(level);
229 }
230
231 /**
232 * @brief Checks if a level is currently loaded.
233 *
234 * @return True if a level is set, false otherwise.
235 */
236 [[nodiscard]] bool hasLevel() const noexcept{
237 return level_ != nullptr;
238 }
239
240 /**
241 * @brief Retrieves the currently loaded level.
242 *
243 * @return Reference to the active Level.
244 *
245 * @warning Calling this method when hasLevel() returns false results in undefined behavior.
246 */
247 [[nodiscard]] const helios::engine::runtime::world::Level& level() const noexcept{
248 return *level_;
249 }
250
251 /**
252 * @brief Checks if a Manager of the specified type is registered.
253 *
254 * @tparam T The Manager type to check for.
255 *
256 * @return True if a Manager of type T is registered, false otherwise.
257 */
258 template<typename T>
259 requires std::is_base_of_v<helios::engine::runtime::world::Manager, T>
260 [[nodiscard]] bool hasManager() const {
261 return getManager<T>() != nullptr;
262 }
263
264 /**
265 * @brief Adds and registers a Manager of the specified type.
266 *
267 * @details Creates a new Manager instance and adds it to the world.
268 * The manager's onAdd() callback is invoked after registration.
269 *
270 * @tparam T The Manager type to add. Must derive from Manager.
271 * @tparam Args Constructor argument types.
272 *
273 * @param args Arguments forwarded to the Manager constructor.
274 *
275 * @return Reference to the newly added Manager.
276 *
277 * @pre No Manager of type T is already registered.
278 */
279 template<typename T, typename... Args>
280 requires std::is_base_of_v<helios::engine::runtime::world::Manager, T>
281 T& addManager(Args&&... args) {
282
283 assert(!hasManager<T>() && "Manager already registered.");
284
285 auto manager = std::make_unique<T>(std::forward<Args>(args)...);
286 auto* manager_ptr = manager.get();
287
288 managers_.push_back(std::move(manager));
289
290 manager_ptr->onAdd(*this);
291 return *manager_ptr;
292 }
293
294 /**
295 * @brief Registers a SpawnCommandHandler for a specific spawn profile.
296 *
297 * @details Associates a handler with a spawn profile ID. The handler processes
298 * spawn and despawn requests for entities associated with that profile.
299 *
300 * @param spawnProfileId The spawn profile identifier to associate with the handler.
301 * @param poolManager Reference to the handler to register.
302 *
303 * @return True if registration succeeded, false if already registered.
304 */
308 ) {
309 bool added = spawnCommandHandlerRegistry_.add(spawnProfileId, poolManager);
310
311 assert(added && "PoolManager already registered");
312
313 return added;
314 }
315
316 /**
317 * @brief Retrieves a SpawnCommandHandler for a specific spawn profile.
318 *
319 * @details Used to submit spawn/despawn commands to the handler responsible
320 * for a particular spawn profile (e.g., bullet pool, enemy pool).
321 *
322 * @param spawnProfileId The spawn profile identifier to look up.
323 *
324 * @return Pointer to the handler, or nullptr if not registered.
325 *
326 * @see registerSpawnCommandHandler()
327 * @see SpawnCommandHandler
328 */
330 const helios::engine::core::data::SpawnProfileId spawnProfileId) {
331 return spawnCommandHandlerRegistry_.get(spawnProfileId);
332 }
333
334 /**
335 * @brief Registers a handler for score commands.
336 *
337 * @details Associates a ScoreCommandHandler with this GameWorld.
338 * Only one handler can be registered at a time.
339 *
340 * @param scoreCommandHandler Reference to the handler to register.
341 *
342 * @return True if registration succeeded, false if already registered.
343 */
346 ) {
347 assert(!scoreCommandHandler_ && "ScoreCommandHandler already registered");
348
350
351 return true;
352 }
353
354 /**
355 * @brief Retrieves the registered ScoreCommandHandler.
356 *
357 * @return Pointer to the handler, or nullptr if not registered.
358 */
361 }
362
363 /**
364 * @brief Retrieves a registered Manager by type.
365 *
366 * @tparam T The Manager type to retrieve. Must derive from Manager.
367 *
368 * @return Pointer to the Manager if found, nullptr otherwise.
369 */
370 template<typename T>
371 requires std::is_base_of_v<helios::engine::runtime::world::Manager, T>
372 [[nodiscard]] T* getManager() const {
373
374 for (auto& mgr : managers_) {
375 if (auto* c = dynamic_cast<T*>(mgr.get())) {
376 return c;
377 }
378 }
379
380 return nullptr;
381 }
382
383 /**
384 * @brief Flushes all registered managers.
385 *
386 * @details Called during the game loop to allow managers to process
387 * their queued requests (e.g., spawn requests, pool returns).
388 *
389 * @param updateContext The current frame's update context.
390 */
392 for (auto& mgr : managers_) {
393 mgr->flush(*this, updateContext);
394 }
395 }
396
397 /**
398 * @brief Creates a new GameObject in the world.
399 *
400 * @details Allocates an entity handle via the EntityRegistry and returns
401 * a lightweight GameObject wrapper. The returned GameObject is ~16 bytes
402 * and should be passed by value.
403 *
404 * @return A new GameObject ready for component attachment.
405 *
406 * @see GameObject
407 * @see EntityManager::create
408 */
410 const auto handle = em_.create();
411 return helios::engine::ecs::GameObject(handle, &em_);
412 }
413
414 /**
415 * @brief Creates a View for iterating entities with specific components.
416 *
417 * @details Returns a lightweight view that iterates over all entities
418 * possessing the specified component types. Use with range-based for loops
419 * and structured bindings.
420 *
421 * ```cpp
422 * for (auto [entity, transform, velocity, active] : gameWorld.view<
423 * TransformComponent,
424 * VelocityComponent,
425 * Active
426 * >().whereEnabled()) {
427 * // Process matching entities
428 * }
429 * ```
430 *
431 * @tparam Components The component types to query for.
432 *
433 * @return A View for iterating matching entities.
434 *
435 * @see View
436 */
437 template <typename... Components>
438 [[nodiscard]] auto view() {
439 return helios::engine::ecs::View<Components...>(&em_);
440 }
441
442 /**
443 * @brief Creates a const View for iterating entities with specific components.
444 *
445 * @tparam Components The component types to query for.
446 *
447 * @return A const View for iterating matching entities.
448 */
449 template <typename... Components>
450 [[nodiscard]] auto view() const {
451 return helios::engine::ecs::View<Components...>(&em_);
452 }
453
454 /**
455 * @brief Finds a GameObject by its EntityHandle.
456 *
457 * @details Validates the handle and returns a GameObject wrapper if valid.
458 * Returns std::nullopt if the handle is stale or invalid.
459 *
460 * @param handle The EntityHandle to look up.
461 *
462 * @return Optional containing the GameObject if found, std::nullopt otherwise.
463 */
464 [[nodiscard]] std::optional<helios::engine::ecs::GameObject> find(const helios::engine::ecs::EntityHandle handle) {
465 if (!em_.isValid(handle)) {
466 return std::nullopt;
467 }
468
469 return helios::engine::ecs::GameObject(handle, &em_);
470 }
471
472 /**
473 * @brief Finds a GameObject by its EntityHandle (const version).
474 *
475 * @param handle The EntityHandle to look up.
476 *
477 * @return Optional containing the GameObject if found, std::nullopt otherwise.
478 */
479 [[nodiscard]] std::optional<helios::engine::ecs::GameObject> find(const helios::engine::ecs::EntityHandle handle) const {
480
481 if (!em_.isValid(handle)) {
482 return std::nullopt;
483 }
484
485 return helios::engine::ecs::GameObject(handle, &em_);
486 }
487
488 /**
489 * @brief Clones a GameObject and all its components.
490 *
491 * @details Creates a new entity and copies all components from the source
492 * to the target. The new GameObject is initially inactive. Components with
493 * `onClone()` hooks will have them invoked after copy construction.
494 *
495 * @param gameObject The source GameObject to clone.
496 *
497 * @return A new inactive GameObject with cloned components.
498 *
499 * @see EntityManager::clone
500 */
502
503 auto newGo = addGameObject();
504
505 newGo.setActive(false);
506
507 em_.clone(gameObject.entityHandle(), newGo.entityHandle());
508
509
510 return newGo;
511 }
512
513 };
514
515}
516

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.