Helios Engine 0.1.0
A modular ECS based data-oriented C++23 game engine
 
Loading...
Searching...
No Matches
world.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <helios/core_pch.hpp>
4
23
24#include <array>
25#include <concepts>
26#include <cstddef>
27#include <memory>
28#include <ranges>
29#include <span>
30#include <type_traits>
31#include <utility>
32#include <vector>
33
34namespace helios::ecs {
35
36// Forward declaration of World class for concept definition
37class World;
38
39/**
40 * @brief Concept that constrains a type to be the World type (with any cv-qualifiers).
41 * @details Used to ensure template parameters are World types for Query and QueryBuilder.
42 * @tparam T Type to check
43 */
44template <typename T>
45concept WorldType = std::same_as<std::remove_cvref_t<T>, World>;
46
47/**
48 * @brief The World class manages entities with their components and systems.
49 * @note Partially thread safe.
50 * All modifications to the world (adding/removing entities or components) should be done
51 * via command buffers to defer changes until the next update.
52 */
53class World {
54public:
55 World() = default;
56 World(const World&) = delete;
59
62
63 /**
64 * @brief Flushes buffer of pending operations.
65 * @note This method should be called once per frame after all systems have been updated.
66 * Not thread-safe.
67 */
68 void Update();
69
70 /**
71 * @brief Clears the world, removing all data.
72 * @note Not thread-safe.
73 */
74 void Clear();
75
76 /**
77 * @brief Clears all entities and components from the world.
78 * @note Not thread-safe.
79 */
81
82 /**
83 * @brief Clears all event queues without removing event registration.
84 * @details Events can still be written/read after calling this method.
85 * To completely reset the event system including registration, use Clear().
86 * @note Not thread-safe.
87 */
88 void ClearAllEventQueues() noexcept { event_manager_.ClearAllQueues(); }
89
90 /**
91 * @brief Merges events from another event queue into the main queue.
92 * @note Not thread-safe.
93 * @param other Event queue to merge from
94 */
95 void MergeEventQueue(details::EventQueue& other) { event_manager_.Merge(other); }
96
97 /**
98 * @brief Merges commands from a command range into the main queue.
99 * @note Not thread-safe.
100 * @tparam R Range type containing unique_ptr<Command> elements
101 * @param commands Range of commands to merge from
102 */
103 template <std::ranges::range R>
104 requires std::same_as<std::ranges::range_value_t<R>, std::unique_ptr<Command>>
105 void MergeCommands(R&& commands);
106
107 /**
108 * @brief Creates new entity.
109 * @details If EntitySpawnedEvent is registered, emits the event automatically.
110 * @note Not thread-safe.
111 * @return Newly created entity.
112 */
114
115 /**
116 * @brief Reserves an entity ID for deferred creation.
117 * @details The actual entity creation is deferred until `Update()` is called.
118 * @note Thread-safe.
119 * @return Reserved entity ID
120 */
121 [[nodiscard]] Entity ReserveEntity() { return entities_.ReserveEntity(); }
122
123 /**
124 * @brief Destroys entity and removes it from the world.
125 * @note Not thread-safe.
126 * @warning Triggers assertion in next cases:
127 * - Entity is invalid.
128 * - World does not own entity.
129 * @param entity Entity to destroy
130 */
131 void DestroyEntity(Entity entity);
132
133 /**
134 * @brief Tries to destroy entity if it exists in the world.
135 * @note Not thread-safe.
136 * @warning Triggers assertion only if entity is invalid (does not assert when entity does not exist in the world).
137 * @param entity Entity to destroy
138 */
139 void TryDestroyEntity(Entity entity);
140
141 /**
142 * @brief Destroys entities and removes them from the world.
143 * @note Not thread-safe.
144 * @warning Triggers assertion in next cases:
145 * - Any entity is invalid.
146 * - Any entity does not exist in the world.
147 * @tparam R Range type containing Entity elements
148 * @param entities Entities to destroy
149 */
150 template <std::ranges::range R>
151 requires std::same_as<std::ranges::range_value_t<R>, Entity>
152 void DestroyEntities(const R& entities);
153
154 /**
155 * @brief Tries to destroy entities if they exist in the world.
156 * @note Not thread-safe.
157 * @warning Triggers assertion only if any entity is invalid (non-existing entities are skipped).
158 * @tparam R Range type containing Entity elements
159 * @param entities Entities to destroy
160 */
161 template <std::ranges::range R>
162 requires std::same_as<std::ranges::range_value_t<R>, Entity>
163 void TryDestroyEntities(const R& entities);
164
165 /**
166 * @brief Adds component to the entity.
167 * @details If entity already has component of provided type then it will be replaced.
168 * @note Not thread-safe.
169 * @warning Triggers assertion in next cases:
170 * - Entity is invalid.
171 * - World does not own entity.
172 * @tparam T Component type to add
173 * @param entity Entity to add component to
174 * @param component Component to add
175 */
176 template <ComponentTrait T>
177 void AddComponent(Entity entity, T&& component);
178
179 /**
180 * @brief Adds components to the entity.
181 * @details If entity already has component of provided type then it will be replaced.
182 * @note Not thread-safe.
183 * @warning Triggers assertion in next cases:
184 * - Entity is invalid.
185 * - World does not own entity.
186 * @tparam Ts Components types to add
187 * @param entity Entity to add components to
188 * @param components Components to add
189 */
190 template <ComponentTrait... Ts>
191 requires utils::UniqueTypes<Ts...>
192 void AddComponents(Entity entity, Ts&&... components);
193
194 /**
195 * @brief Tries to remove component from the entity.
196 * @details Returns false if component is not on the entity.
197 * @note Not thread-safe.
198 * @warning Triggers assertion in next cases:
199 * - Entity is invalid.
200 * - World does not own entity.
201 * @tparam T Component type to remove
202 * @param entity Entity to remove component from
203 * @return True if component was removed, false otherwise
204 */
205 template <ComponentTrait T>
206 bool TryAddComponent(Entity entity, T&& component);
207
208 /**
209 * @brief Tries to add components to the entity if they don't exist.
210 * @note Not thread-safe.
211 * @warning Triggers assertion in next cases:
212 * - Entity is invalid.
213 * - World does not own entity.
214 * @tparam Ts Components types to add
215 * @param entity Entity to add components to
216 * @param components Components to add
217 * @return Array of bools indicating whether each component was added (true if added, false otherwise)
218 */
219 template <ComponentTrait... Ts>
220 requires utils::UniqueTypes<Ts...>
221 auto TryAddComponents(Entity entity, Ts&&... components) -> std::array<bool, sizeof...(Ts)>;
222
223 /**
224 * @brief Emplaces component for the entity.
225 * @details Constructs component in-place. If entity already has component of provided type then it will be replaced.
226 * @note Not thread-safe.
227 * @warning Triggers assertion in next cases:
228 * - Entity is invalid.
229 * - World does not own entity.
230 * @tparam T Component type to emplace
231 * @tparam Args Argument types to forward to T's constructor
232 * @param entity Entity to emplace component to
233 * @param args Arguments to forward to component constructor
234 */
235 template <ComponentTrait T, typename... Args>
236 requires std::constructible_from<T, Args...>
237 void EmplaceComponent(Entity entity, Args&&... args);
238
239 /**
240 * @brief Tries to emplace component for the entity.
241 * @details Constructs component in-place. Returns false if component is already on the entity.
242 * @note Not thread-safe.
243 * @warning Triggers assertion in next cases:
244 * - Entity is invalid.
245 * - World does not own entity.
246 * @tparam T Component type to emplace
247 * @tparam Args Argument types to forward to T's constructor
248 * @param entity Entity to emplace component to
249 * @param args Arguments to forward to component constructor
250 * @return True if component was emplaced, false otherwise
251 */
252 template <ComponentTrait T, typename... Args>
253 requires std::constructible_from<T, Args...>
254 bool TryEmplaceComponent(Entity entity, Args&&... args);
255
256 /**
257 * @brief Removes component from the entity.
258 * @note Not thread-safe.
259 * @warning Triggers assertion in next cases:
260 * - Entity is invalid.
261 * - World does not own entity.
262 * @tparam T Component type to remove
263 * @param entity Entity to remove component from
264 */
265 template <ComponentTrait T>
266 void RemoveComponent(Entity entity);
267
268 /**
269 * @brief Removes components from the entity.
270 * @note Not thread-safe.
271 * @warning Triggers assertion in next cases:
272 * - Entity is invalid.
273 * - World does not own entity.
274 * @tparam Ts Components types to remove
275 * @param entity Entity to remove components from
276 */
277 template <ComponentTrait... Ts>
278 requires utils::UniqueTypes<Ts...>
279 void RemoveComponents(Entity entity);
280
281 /**
282 * @brief Tries to removes component from the entity if it exists.
283 * @note Not thread-safe.
284 * @warning Triggers assertion in next cases:
285 * - Entity is invalid.
286 * - World does not own entity.
287 * @tparam T Component type to remove
288 * @param entity Entity to remove component from
289 * @return True if component will be removed, false otherwise
290 */
291 template <ComponentTrait T>
292 bool TryRemoveComponent(Entity entity);
293
294 /**
295 * @brief Gets component from the entity (const version).
296 * @note Not thread-safe.
297 * @warning Triggers assertion in next cases:
298 * - Entity is invalid.
299 * - World does not own entity.
300 * - Entity does not have component.
301 * @tparam T Component type to get
302 * @param entity Entity to get component from
303 * @return Const reference to component
304 */
305 template <ComponentTrait... Ts>
306 requires utils::UniqueTypes<Ts...>
307 auto TryRemoveComponents(Entity entity) -> std::array<bool, sizeof...(Ts)>;
308
309 /**
310 * @brief Removes all components from the entity.
311 * @note Not thread-safe.
312 * @warning Triggers assertion in next cases:
313 * - Entity is invalid.
314 * - World does not own entity.
315 * @param entity Entity to remove all components from
316 */
317 void ClearComponents(Entity entity);
318
319 /**
320 * @brief Inserts a resource into the world.
321 * @details Replaces existing resource if present.
322 * @note Not thread-safe.
323 * @tparam T Resource type
324 * @param resource Resource to insert
325 */
326 template <ResourceTrait T>
328 resources_.Insert(std::forward<T>(resource));
329 }
330
331 /**
332 * @brief Tries to insert a resource if not present.
333 * @note Not thread-safe.
334 * @tparam T Resource type
335 * @param resource Resource to insert
336 * @return True if inserted, false if resource already exists
337 */
338 template <ResourceTrait T>
340 return resources_.TryInsert(std::forward<T>(resource));
341 }
342
343 /**
344 * @brief Emplaces a resource in-place.
345 * @note Not thread-safe.
346 * @tparam T Resource type
347 * @tparam Args Constructor argument types
348 * @param args Arguments to forward to resource constructor
349 */
350 template <ResourceTrait T, typename... Args>
351 requires std::constructible_from<T, Args...>
352 void EmplaceResource(Args&&... args) {
353 resources_.Emplace<T>(std::forward<Args>(args)...);
354 }
355
356 /**
357 * @brief Tries to emplace a resource if not present.
358 * @note Not thread-safe.
359 * @tparam T Resource type
360 * @tparam Args Constructor argument types
361 * @param args Arguments to forward to resource constructor
362 * @return True if emplaced, false if resource already exists
363 */
364 template <ResourceTrait T, typename... Args>
365 requires std::constructible_from<T, Args...>
366 bool TryEmplaceResource(Args&&... args) {
367 return resources_.TryEmplace<T>(std::forward<Args>(args)...);
368 }
369
370 /**
371 * @brief Removes a resource from the world.
372 * @note Not thread-safe.
373 * @warning Triggers assertion if resource does not exist.
374 * @tparam T Resource type
375 */
376 template <ResourceTrait T>
377 void RemoveResource();
378
379 /**
380 * @brief Tries to remove a resource.
381 * @note Not thread-safe.
382 * @tparam T Resource type
383 * @return True if removed, false if resource didn't exist
384 */
385 template <ResourceTrait T>
387 return resources_.TryRemove<T>();
388 }
389
390 /**
391 * @brief Gets reference to a resource.
392 * @note Not thread-safe.
393 * @warning Triggers assertion if resource does not exist.
394 * @tparam T Resource type
395 * @return Reference to resource
396 */
397 template <ResourceTrait T>
398 [[nodiscard]] T& WriteResource();
399
400 /**
401 * @brief Gets const reference to a resource.
402 * @note Thread-safe for read operations.
403 * @warning Triggers assertion if resource doesn't exist.
404 * @tparam T Resource type
405 * @return Const reference to resource
406 */
407 template <ResourceTrait T>
408 [[nodiscard]] const T& ReadResource() const;
409
410 /**
411 * @brief Tries to get mutable pointer to a resource.
412 * @note Thread-safe for read operations.
413 * @tparam T Resource type
414 * @return Pointer to resource, or nullptr if not found
415 */
416 template <ResourceTrait T>
418 return resources_.TryGet<T>();
419 }
420
421 /**
422 * @brief Tries to get const pointer to a resource.
423 * @note Thread-safe for read operations.
424 * @tparam T Resource type
425 * @return Const pointer to resource, or nullptr if not found
426 */
427 template <ResourceTrait T>
428 [[nodiscard]] const T* TryReadResource() const {
429 return resources_.TryGet<T>();
430 }
431
432 /**
433 * @brief Registers an event type for use.
434 * @details Events must be registered before they can be written or read.
435 * Built-in events (EntitySpawnedEvent, EntityDestroyedEvent) are registered with is_builtin flag.
436 * @note Not thread-safe.
437 * Should be called during initialization.
438 * @tparam T Event type
439 */
440 template <EventTrait T>
441 void AddEvent();
442
443 /**
444 * @brief Registers multiple event types for use.
445 * @note Not thread-safe.
446 * Should be called during initialization.
447 * @tparam Events Event types to register
448 */
449 template <EventTrait... Events>
450 requires(sizeof...(Events) > 1)
452 (AddEvent<Events>(), ...);
453 }
454
455 /**
456 * @brief Manually clears events of a specific type from both queues.
457 * @details Should only be used for manually-managed events (auto_clear = false).
458 * Built-in events cannot be manually cleared.
459 * @note Not thread-safe.
460 * @warning Triggers assertion if event type is not registered.
461 * @tparam T Event type
462 */
463 template <EventTrait T>
464 void ClearEvents();
465
466 /**
467 * @brief Gets an event reader for type T.
468 * @details Provides a type-safe, ergonomic API for reading events with
469 * support for iteration, filtering, and searching.
470 * Event must be registered via AddEvent<T>() first.
471 * @note Thread-safe for read operations.
472 * @warning Triggers assertion if event type is not registered.
473 * @tparam T Event type
474 * @return EventReader for type T
475 */
476 template <EventTrait T>
478
479 /**
480 * @brief Gets an event writer for type T.
481 * @details Provides a type-safe, ergonomic API for writing events with
482 * support for bulk operations and in-place construction.
483 * Event must be registered via AddEvent<T>() first.
484 * @note Not thread-safe.
485 * @warning Triggers assertion if event type is not registered.
486 * @tparam T Event type
487 * @return EventWriter for type T
488 */
491
492 /**
493 * @brief Checks if entity exists in the world.
494 * @note Thread-safe for read operations.
495 * @warning Triggers assertion if entity is invalid.
496 * @param entity Entity to check
497 * @return True if entity exists, false otherwise
498 */
499 [[nodiscard]] bool Exists(Entity entity) const;
500
501 /**
502 * @brief Checks if entity has component.
503 * @note Thread-safe for read operations.
504 * @warning Triggers assertion in next cases:
505 * - Entity is invalid.
506 * - World does not own entity.
507 * @tparam T Component type to check
508 * @param entity Entity to check
509 * @return Array of bools indicating whether entity has each component
510 */
512 [[nodiscard]] bool HasComponent(Entity entity) const;
513
514 /**
515 * @brief Checks if entity has components.
516 * @note Thread-safe for read operations.
517 * @warning Triggers assertion if entity is invalid.
518 * @tparam Ts Components types
519 * @param entity Entity to check
520 * @return Array of bools indicating whether entity has each component (true if entity has component,
521 * false otherwise)
522 */
524 requires utils::UniqueTypes<Ts...>
525 [[nodiscard]] auto HasComponents(Entity entity) const -> std::array<bool, sizeof...(Ts)>;
526
527 /**
528 * @brief Checks if a resource exists.
529 * @note Thread-safe for read operations.
530 * @tparam T Resource type
531 * @return True if resource exists, false otherwise
532 */
534 [[nodiscard]] bool HasResource() const {
535 return resources_.Has<T>();
536 }
537
538 /**
539 * @brief Checks if a event registered.
540 * @note Thread-safe for read operations.
541 * @tparam T Event type
542 * @return True if event exists, false otherwise
543 */
544 template <ResourceTrait T>
545 [[nodiscard]] bool HasEvent() const {
546 return event_manager_.IsRegistered<T>();
547 }
548
549 /**
550 * @brief Checks if events of a specific type exist in event queue.
551 * @note Thread-safe for read operations.
552 * @tparam T Event type
553 * @return True if event exists, false otherwise
554 */
555 template <ResourceTrait T>
556 [[nodiscard]] bool HasEvents() const {
557 return event_manager_.HasEvents<T>();
558 }
559
560 /**
561 * @brief Gets the number of entities in the world.
562 * @note Thread-safe for read operations.
563 * @return Number of entities in the world
564 */
565 [[nodiscard]] size_t EntityCount() const noexcept { return entities_.Count(); }
566
567private:
568 /**
569 * @brief Updates entity's archetype based on current components (full rebuild).
570 * @details Used when multiple components are added/removed at once.
571 * @param entity Entity to update
572 */
573 void UpdateEntityArchetype(Entity entity);
574
575 /**
576 * @brief Updates entity's archetype after adding a single component.
577 * @details Uses edge graph for O(1) amortized lookup.
578 * @tparam T Component type that was added
579 * @param entity Entity to update
580 */
581 template <ComponentTrait T>
582 void UpdateEntityArchetypeOnAdd(Entity entity);
583
584 /**
585 * @brief Updates entity's archetype after removing a single component.
586 * @details Uses edge graph for O(1) amortized lookup.
587 * @tparam T Component type that was removed
588 * @param entity Entity to update
589 */
590 template <ComponentTrait T>
591 void UpdateEntityArchetypeOnRemove(Entity entity);
592
593 [[nodiscard]] details::Entities& GetEntities() noexcept { return entities_; }
594 [[nodiscard]] const details::Entities& GetEntities() const noexcept { return entities_; }
595
596 [[nodiscard]] details::Components& GetComponents() noexcept { return components_; }
597 [[nodiscard]] const details::Components& GetComponents() const noexcept { return components_; }
598
599 [[nodiscard]] details::Archetypes& GetArchetypes() noexcept { return archetypes_; }
600 [[nodiscard]] const details::Archetypes& GetArchetypes() const noexcept { return archetypes_; }
601
602 [[nodiscard]] details::Resources& GetResources() noexcept { return resources_; }
603 [[nodiscard]] const details::Resources& GetResources() const noexcept { return resources_; }
604
605 [[nodiscard]] details::CmdQueue& GetCmdQueue() noexcept { return command_queue_; }
606 [[nodiscard]] const details::CmdQueue& GetCmdQueue() const noexcept { return command_queue_; }
607
608 [[nodiscard]] details::EventManager& GetEventManager() noexcept { return event_manager_; }
609 [[nodiscard]] const details::EventManager& GetEventManager() const noexcept { return event_manager_; }
610
611 details::Entities entities_;
612 details::Components components_;
613 details::Archetypes archetypes_;
614 details::Resources resources_;
615 details::CmdQueue command_queue_;
616 details::EventManager event_manager_;
617
618 template <WorldType WorldT, typename Allocator, ComponentTrait... Components>
619 requires utils::UniqueTypes<Components...>
620 friend class BasicQuery;
621
622 template <WorldType WorldT, typename Allocator, ComponentTrait... Components>
623 requires utils::UniqueTypes<Components...>
625};
626
627inline void World::Update() {
628 // First, flush reserved entities to ensure all reserved IDs are created.
629 entities_.FlushReservedEntities();
630
631 // Then, execute all commands in the command queue.
632 if (!command_queue_.Empty()) [[likely]] {
633 auto commands = command_queue_.DequeueAll();
634 for (auto& command : commands) {
635 if (command) [[likely]] {
636 command->Execute(*this);
637 }
638 }
639 }
640
641 // Update event lifecycle - swap buffers and clear old events
642 event_manager_.Update();
643}
644
645inline void World::Clear() {
646 entities_.Clear();
647 components_.Clear();
648 archetypes_.Clear();
649 resources_.Clear();
650 command_queue_.Clear();
651 event_manager_.Clear();
652}
653
655 entities_.Clear();
656 components_.Clear();
657 archetypes_.Clear();
658}
659
661 Entity entity = entities_.CreateEntity();
662
663 // Emit EntitySpawnedEvent if registered
664 if (event_manager_.IsRegistered<EntitySpawnedEvent>()) {
665 event_manager_.Write(EntitySpawnedEvent{entity});
666 }
667
668 return entity;
669}
670
671template <std::ranges::range R>
672 requires std::same_as<std::ranges::range_value_t<R>, std::unique_ptr<Command>>
674 if (commands.empty()) [[unlikely]] {
675 return;
676 }
677
678 // Enqueue all commands from the range to the command queue
679 command_queue_.EnqueueBulk(std::forward<R>(commands));
680 commands.clear();
681}
682
683inline void World::DestroyEntity(Entity entity) {
684 HELIOS_ASSERT(entity.Valid(), "Failed to destroy entity: Entity is invalid!");
685 HELIOS_ASSERT(Exists(entity), "Failed to destroy entity: World does not own entity with index '{}'!", entity.Index());
686
687 // Emit EntityDestroyedEvent if registered
688 if (event_manager_.IsRegistered<EntityDestroyedEvent>()) {
689 event_manager_.Write(EntityDestroyedEvent{entity});
690 }
691
692 components_.RemoveAllComponents(entity);
693 archetypes_.RemoveEntity(entity);
694 entities_.Destroy(entity);
695}
696
697inline void World::TryDestroyEntity(Entity entity) {
698 HELIOS_ASSERT(entity.Valid(), "Failed to try destroy entity: Entity is invalid!");
699 if (!Exists(entity)) {
700 return;
701 }
702
703 // Emit EntityDestroyedEvent if registered
704 if (event_manager_.IsRegistered<EntityDestroyedEvent>()) {
705 event_manager_.Write(EntityDestroyedEvent{entity});
706 }
707
708 components_.RemoveAllComponents(entity);
709 archetypes_.RemoveEntity(entity);
710 entities_.Destroy(entity);
711}
712
713template <std::ranges::range R>
714 requires std::same_as<std::ranges::range_value_t<R>, Entity>
716 const bool emit_events = event_manager_.IsRegistered<EntityDestroyedEvent>();
717
718 for (const Entity& entity : entities_range) {
719 HELIOS_ASSERT(entity.Valid(), "Failed to destroy entities: Entity is invalid!");
720 HELIOS_ASSERT(Exists(entity), "Failed to destroy entities: World does not own entity with index '{}'!",
721 entity.Index());
722
723 // Emit EntityDestroyedEvent if registered
724 if (emit_events) {
725 event_manager_.Write(EntityDestroyedEvent{entity});
726 }
727
728 components_.RemoveAllComponents(entity);
729 archetypes_.RemoveEntity(entity);
730 }
731 entities_.Destroy(entities_range);
732}
733
734template <std::ranges::range R>
735 requires std::same_as<std::ranges::range_value_t<R>, Entity>
737 const bool emit_events = event_manager_.IsRegistered<EntityDestroyedEvent>();
738
739 for (const Entity& entity : entities_range) {
740 HELIOS_ASSERT(entity.Valid(), "Failed to try destroy entities: Entity is invalid!");
741 if (!Exists(entity)) [[unlikely]] {
742 continue;
743 }
744
745 // Emit EntityDestroyedEvent if registered
746 if (emit_events) {
747 event_manager_.Write(EntityDestroyedEvent{entity});
748 }
749
750 components_.RemoveAllComponents(entity);
751 archetypes_.RemoveEntity(entity);
752 }
753 entities_.Destroy(entities_range);
754}
755
756template <ComponentTrait T>
757inline void World::AddComponent(Entity entity, T&& component) {
758 HELIOS_ASSERT(entity.Valid(), "Failed to add component: Entity is invalid!");
759 HELIOS_ASSERT(Exists(entity), "Failed to add component: World does not own entity with index '{}'!", entity.Index());
760
761 components_.AddComponent(entity, std::forward<T>(component));
763}
764
765template <ComponentTrait... Ts>
766 requires utils::UniqueTypes<Ts...>
767inline void World::AddComponents(Entity entity, Ts&&... components) {
768 HELIOS_ASSERT(entity.Valid(), "Failed to add components: Entity is invalid!");
769 HELIOS_ASSERT(Exists(entity), "Failed to add components: World does not own entity with index '{}'!", entity.Index());
770
771 (components_.AddComponent(entity, std::forward<Ts>(components)), ...);
772 UpdateEntityArchetype(entity);
773}
774
775template <ComponentTrait T>
776inline bool World::TryAddComponent(Entity entity, T&& component) {
777 HELIOS_ASSERT(entity.Valid(), "Failed to try add component: Entity is invalid!");
778 HELIOS_ASSERT(Exists(entity), "Failed to try add component: World does not own entity with index '{}'!",
779 entity.Index());
780
781 using ComponentType = std::decay_t<T>;
782 if (components_.HasComponent<ComponentType>(entity)) {
783 return false;
784 }
785
786 components_.AddComponent(entity, std::forward<T>(component));
788 return true;
789}
790
791template <ComponentTrait... Ts>
792 requires utils::UniqueTypes<Ts...>
793inline auto World::TryAddComponents(Entity entity, Ts&&... components) -> std::array<bool, sizeof...(Ts)> {
794 HELIOS_ASSERT(entity.Valid(), "Failed to try add components: Entity is invalid!");
795 HELIOS_ASSERT(Exists(entity), "Failed to try add components: World does not own entity with index '{}'!",
796 entity.Index());
797
798 std::array<bool, sizeof...(Ts)> result = {!components_.HasComponent<std::decay_t<Ts>>(entity)...};
799 bool any_added = false;
800
801 size_t idx = 0;
802 auto add_if_missing = [this, &result, &any_added, &idx, entity](auto&& component) {
803 using ComponentType = std::decay_t<decltype(component)>;
804 if (!components_.HasComponent<ComponentType>(entity)) {
805 components_.AddComponent(entity, std::forward<decltype(component)>(component));
806 result[idx] = true;
807 any_added = true;
808 } else {
809 result[idx] = false;
810 }
811 ++idx;
812 };
813
814 (add_if_missing(std::forward<Ts>(components)), ...);
815
816 if (any_added) {
817 UpdateEntityArchetype(entity);
818 }
819
820 return result;
821}
822
823template <ComponentTrait T, typename... Args>
824 requires std::constructible_from<T, Args...>
825inline void World::EmplaceComponent(Entity entity, Args&&... args) {
826 HELIOS_ASSERT(entity.Valid(), "Failed to emplace component: Entity is invalid!");
827 HELIOS_ASSERT(Exists(entity), "Failed to emplace component: World does not own entity with index '{}'!",
828 entity.Index());
829
830 components_.EmplaceComponent<T>(entity, std::forward<Args>(args)...);
832}
833
834template <ComponentTrait T, typename... Args>
835 requires std::constructible_from<T, Args...>
836inline bool World::TryEmplaceComponent(Entity entity, Args&&... args) {
837 HELIOS_ASSERT(entity.Valid(), "Failed to try emplace component: Entity is invalid!");
838 HELIOS_ASSERT(Exists(entity), "Failed to try emplace component: World does not own entity with index '{}'!",
839 entity.Index());
840
841 if (components_.HasComponent<T>(entity)) {
842 return false;
843 }
844
845 components_.EmplaceComponent<T>(entity, std::forward<Args>(args)...);
847 return true;
848}
849
850template <ComponentTrait T>
851inline void World::RemoveComponent(Entity entity) {
852 HELIOS_ASSERT(entity.Valid(), "Failed to remove component: Entity is invalid!");
853 HELIOS_ASSERT(Exists(entity), "Failed to remove component: World does not own entity with index '{}'!",
854 entity.Index());
855
856 components_.RemoveComponent<T>(entity);
858}
859
860template <ComponentTrait... Ts>
861 requires utils::UniqueTypes<Ts...>
862inline void World::RemoveComponents(Entity entity) {
863 HELIOS_ASSERT(entity.Valid(), "Failed to remove components: Entity is invalid!");
864 HELIOS_ASSERT(Exists(entity), "Failed to remove components: World does not own entity with index '{}'!",
865 entity.Index());
866
867 (components_.RemoveComponent<Ts>(entity), ...);
868 UpdateEntityArchetype(entity);
869}
870
871template <ComponentTrait T>
873 HELIOS_ASSERT(entity.Valid(), "Failed to try remove component: Entity is invalid!");
874 HELIOS_ASSERT(Exists(entity), "Failed to try remove component: World does not own entity with index '{}'!",
875 entity.Index());
876
877 const bool had_component = components_.HasComponent<T>(entity);
878 if (had_component) {
879 components_.RemoveComponent<T>(entity);
881 }
882 return had_component;
883}
884
885template <ComponentTrait... Ts>
886 requires utils::UniqueTypes<Ts...>
887inline auto World::TryRemoveComponents(Entity entity) -> std::array<bool, sizeof...(Ts)> {
888 HELIOS_ASSERT(entity.Valid(), "Failed to try remove components: Entity is invalid!");
889 HELIOS_ASSERT(Exists(entity), "Failed to try remove components: World does not own entity with index '{}'!",
890 entity.Index());
891
892 std::array<bool, sizeof...(Ts)> result = {};
893 bool any_removed = false;
894
895 size_t index = 0;
896 auto try_remove_single = [this, &index, &result, &any_removed, entity](auto type_identity_tag) {
897 using ComponentType = typename decltype(type_identity_tag)::type;
898 if (components_.HasComponent<ComponentType>(entity)) {
899 components_.RemoveComponent<ComponentType>(entity);
900 result[index] = true;
901 any_removed = true;
902 } else {
903 result[index] = false;
904 }
905 ++index;
906 };
907
908 (try_remove_single(std::type_identity<Ts>{}), ...);
909
910 if (any_removed) {
911 UpdateEntityArchetype(entity);
912 }
913
914 return result;
915}
916
917inline void World::ClearComponents(Entity entity) {
918 HELIOS_ASSERT(entity.Valid(), "Failed to clear components: Entity is invalid!");
919 HELIOS_ASSERT(Exists(entity), "Failed to clear components: World does not own entity with index '{}'!",
920 entity.Index());
921
922 components_.RemoveAllComponents(entity);
923 archetypes_.RemoveEntity(entity);
924}
925
926template <ResourceTrait T>
928 HELIOS_ASSERT(HasResource<T>(), "Failed to remove resource '{}': Resource does not exist!", ResourceNameOf<T>());
929 resources_.Remove<T>();
930}
931
932template <ResourceTrait T>
934 HELIOS_ASSERT(HasResource<T>(), "Failed to write resource '{}': Resource does not exist!", ResourceNameOf<T>());
935 return resources_.Get<T>();
936}
937
938template <ResourceTrait T>
939inline const T& World::ReadResource() const {
940 HELIOS_ASSERT(HasResource<T>(), "Failed to read resource '{}': Resource does not exist!", ResourceNameOf<T>());
941 return resources_.Get<T>();
942}
943
944inline bool World::Exists(Entity entity) const {
945 HELIOS_ASSERT(entity.Valid(), "Failed to check if entity exists: Entity is invalid!");
946 return entities_.IsValid(entity);
947}
948
949template <ComponentTrait T>
950inline bool World::HasComponent(Entity entity) const {
951 HELIOS_ASSERT(entity.Valid(), "Failed to check if entity has component: Entity is invalid!");
952 HELIOS_ASSERT(Exists(entity), "Failed to check if entity has component: World does not own entity with index '{}'!",
953 entity.Index());
954 return components_.HasComponent<T>(entity);
955}
956
957template <ComponentTrait... Ts>
958 requires utils::UniqueTypes<Ts...>
959inline auto World::HasComponents(Entity entity) const -> std::array<bool, sizeof...(Ts)> {
960 HELIOS_ASSERT(entity.Valid(), "Failed to check if entity has components: Entity is invalid!");
961 HELIOS_ASSERT(Exists(entity), "Failed to check if entity has components: World does not own entity with index '{}'!",
962 entity.Index());
963 return {components_.HasComponent<Ts>(entity)...};
964}
965
966template <EventTrait T>
967inline void World::AddEvent() {
968 if (event_manager_.IsRegistered<T>()) [[unlikely]] {
969 HELIOS_WARN("Event '{}' is already registered in the world!", EventNameOf<T>());
970 return;
971 }
972
973 event_manager_.RegisterEvent<T>();
974}
975
976template <EventTrait T>
977inline void World::ClearEvents() {
978 HELIOS_ASSERT(event_manager_.IsRegistered<T>(), "Failed to clear events of type '{}': Event type is not registered!",
980
981 event_manager_.ManualClear<T>();
982}
983
984template <EventTrait T>
986 HELIOS_ASSERT(event_manager_.IsRegistered<T>(),
987 "Failed to get event reader for type '{}': Event type is not registered!", EventNameOf<T>());
988
989 return EventReader<T>(event_manager_);
990}
991
992template <EventTrait T>
994 HELIOS_ASSERT(event_manager_.IsRegistered<T>(),
995 "Failed to get event writer for type '{}': Event type is not registered!", EventNameOf<T>());
996
997 return EventWriter<T>(event_manager_);
998}
999
1000template <ComponentTrait T>
1001inline void World::UpdateEntityArchetypeOnAdd(Entity entity) {
1002 const auto component_infos = components_.GetComponentTypes(entity);
1003
1004 // Use a stack buffer for small counts
1005 constexpr size_t kStackSize = 16;
1006 if (component_infos.size() <= kStackSize) {
1007 std::array<ComponentTypeId, kStackSize> type_ids = {};
1008 for (size_t i = 0; i < component_infos.size(); ++i) {
1009 type_ids[i] = component_infos[i].TypeId();
1010 }
1011 archetypes_.MoveEntityOnComponentAdd(entity, ComponentTypeIdOf<T>(),
1012 std::span(type_ids.data(), component_infos.size()));
1013 } else {
1014 // Fallback for large counts
1015 std::vector<ComponentTypeId> type_ids;
1016 type_ids.reserve(component_infos.size());
1017 for (const auto& info : component_infos) {
1018 type_ids.push_back(info.TypeId());
1019 }
1021 }
1022}
1023
1024template <ComponentTrait T>
1025inline void World::UpdateEntityArchetypeOnRemove(Entity entity) {
1026 const auto component_infos = components_.GetComponentTypes(entity);
1027
1028 // Use a stack buffer for small counts
1029 constexpr size_t kStackSize = 16;
1030 if (component_infos.size() <= kStackSize) {
1031 std::array<ComponentTypeId, kStackSize> type_ids = {};
1032 for (size_t i = 0; i < component_infos.size(); ++i) {
1033 type_ids[i] = component_infos[i].TypeId();
1034 }
1036 std::span(type_ids.data(), component_infos.size()));
1037 } else {
1038 // Fallback for large counts
1039 std::vector<ComponentTypeId> type_ids;
1040 type_ids.reserve(component_infos.size());
1041 for (const auto& info : component_infos) {
1042 type_ids.push_back(info.TypeId());
1043 }
1045 }
1046}
1047
1048} // namespace helios::ecs
#define HELIOS_ASSERT(condition,...)
Assertion macro that aborts execution in debug builds.
Definition assert.hpp:140
Wrapper for queries that include entity in iteration.
Definition query.hpp:568
Unique identifier for entities with generation counter to handle recycling.
Definition entity.hpp:21
constexpr bool Valid() const noexcept
Checks if the entity is valid.
Definition entity.hpp:58
constexpr IndexType Index() const noexcept
Gets the index component of the entity.
Definition entity.hpp:75
Type-safe writer for events.
The World class manages entities with their components and systems.
Definition world.hpp:53
void DestroyEntity(Entity entity)
Destroys entity and removes it from the world.
Definition world.hpp:683
auto TryRemoveComponents(Entity entity) -> std::array< bool, sizeof...(Ts)>
Gets component from the entity (const version).
Definition world.hpp:887
void AddEvents()
Registers multiple event types for use.
Definition world.hpp:451
auto WriteEvents() -> EventWriter< T >
Gets an event writer for type T.
Definition world.hpp:993
bool HasEvent() const
Checks if a event registered.
Definition world.hpp:545
void RemoveComponents(Entity entity)
Removes components from the entity.
Definition world.hpp:862
void TryDestroyEntity(Entity entity)
Tries to destroy entity if it exists in the world.
Definition world.hpp:697
void AddComponents(Entity entity, Ts &&... components)
Adds components to the entity.
Definition world.hpp:767
bool TryEmplaceResource(Args &&... args)
Tries to emplace a resource if not present.
Definition world.hpp:366
const T & ReadResource() const
Gets const reference to a resource.
Definition world.hpp:939
void EmplaceResource(Args &&... args)
Emplaces a resource in-place.
Definition world.hpp:352
World(const World &)=delete
bool HasResource() const
Checks if a resource exists.
Definition world.hpp:534
bool TryInsertResource(T &&resource)
Tries to insert a resource if not present.
Definition world.hpp:339
auto HasComponents(Entity entity) const -> std::array< bool, sizeof...(Ts)>
Checks if entity has components.
Definition world.hpp:959
Entity ReserveEntity()
Reserves an entity ID for deferred creation.
Definition world.hpp:121
World(World &&) noexcept=default
T & WriteResource()
Gets reference to a resource.
Definition world.hpp:933
void InsertResource(T &&resource)
Inserts a resource into the world.
Definition world.hpp:327
auto ReadEvents() const noexcept -> EventReader< T >
Gets an event reader for type T.
Definition world.hpp:985
auto TryAddComponents(Entity entity, Ts &&... components) -> std::array< bool, sizeof...(Ts)>
Tries to add components to the entity if they don't exist.
Definition world.hpp:793
void ClearComponents(Entity entity)
Removes all components from the entity.
Definition world.hpp:917
bool Exists(Entity entity) const
Checks if entity exists in the world.
Definition world.hpp:944
void DestroyEntities(const R &entities)
Destroys entities and removes them from the world.
Definition world.hpp:715
void Update()
Flushes buffer of pending operations.
Definition world.hpp:627
void RemoveComponent(Entity entity)
Removes component from the entity.
Definition world.hpp:851
bool HasEvents() const
Checks if events of a specific type exist in event queue.
Definition world.hpp:556
T * TryWriteResource()
Tries to get mutable pointer to a resource.
Definition world.hpp:417
void TryDestroyEntities(const R &entities)
Tries to destroy entities if they exist in the world.
Definition world.hpp:736
Entity CreateEntity()
Creates new entity.
Definition world.hpp:660
bool TryEmplaceComponent(Entity entity, Args &&... args)
Tries to emplace component for the entity.
Definition world.hpp:836
void EmplaceComponent(Entity entity, Args &&... args)
Emplaces component for the entity.
Definition world.hpp:825
bool HasComponent(Entity entity) const
Checks if entity has component.
Definition world.hpp:950
const T * TryReadResource() const
Tries to get const pointer to a resource.
Definition world.hpp:428
bool TryRemoveResource()
Tries to remove a resource.
Definition world.hpp:386
bool TryRemoveComponent(Entity entity)
Tries to removes component from the entity if it exists.
Definition world.hpp:872
size_t EntityCount() const noexcept
Gets the number of entities in the world.
Definition world.hpp:565
void ClearEvents()
Manually clears events of a specific type from both queues.
Definition world.hpp:977
void ClearAllEventQueues() noexcept
Clears all event queues without removing event registration.
Definition world.hpp:88
void MergeEventQueue(details::EventQueue &other)
Merges events from another event queue into the main queue.
Definition world.hpp:95
void AddComponent(Entity entity, T &&component)
Adds component to the entity.
Definition world.hpp:757
bool TryAddComponent(Entity entity, T &&component)
Tries to remove component from the entity.
Definition world.hpp:776
void Clear()
Clears the world, removing all data.
Definition world.hpp:645
void ClearEntities() noexcept
Clears all entities and components from the world.
Definition world.hpp:654
void AddEvent()
Registers an event type for use.
Definition world.hpp:967
void MergeCommands(R &&commands)
Merges commands from a command range into the main queue.
Definition world.hpp:673
void RemoveResource()
Removes a resource from the world.
Definition world.hpp:927
void MoveEntityOnComponentRemove(Entity entity, ComponentTypeId removed_component, std::span< const ComponentTypeId > new_component_types)
Moves entity to a new archetype after removing a component.
void RemoveEntity(Entity entity)
Removes entity from its current archetype.
void MoveEntityOnComponentAdd(Entity entity, ComponentTypeId added_component, std::span< const ComponentTypeId > new_component_types)
Moves entity to a new archetype after adding a component.
Definition archetype.cpp:48
void Clear() noexcept
Clears all archetypes and entity mappings.
void EnqueueBulk(R &&commands)
Enqueues multiple commands in bulk.
bool Empty() const noexcept
Checks if the queue is empty.
auto DequeueAll() noexcept -> std::vector< std::unique_ptr< Command > >
Gets all commands and clears the queue.
void Clear() noexcept
Clears all pending commands from the queue.
void RemoveAllComponents(Entity entity)
Removes all components from the specified entity.
void EmplaceComponent(Entity entity, Args &&... args)
Constructs component in-place for entity.
void Clear() noexcept
Clears all component storages.
std::vector< ComponentTypeInfo > GetComponentTypes(Entity entity) const
Gets all component types for the specified entity.
bool HasComponent(Entity entity) const
Checks if entity has the specified component type.
void AddComponent(Entity entity, T &&component)
Adds component to entity (move version).
void RemoveComponent(Entity entity)
Removes component from entity.
Entity manager responsible for entity creation, destruction, and validation.
void Clear() noexcept
Clears all entities.
Entity ReserveEntity()
Reserves an entity ID that can be used immediately.
Entity CreateEntity()
Creates a new entity.
void FlushReservedEntities()
Creates reserved entities in the metadata.
void Destroy(Entity entity)
Destroys an entity by incrementing its generation.
bool IsValid(Entity entity) const noexcept
Checks if an entity exists and is valid.
size_t Count() const noexcept
Gets the current number of living entities.
void ManualClear()
Manually clears events of a specific type from current and previous queues.
void Clear() noexcept
Clears all events and registration data.
void ClearAllQueues() noexcept
Clears all event queues without removing registration data.
void Update()
Updates event lifecycle - swaps buffers and clears old events.
bool HasEvents() const
Checks if events of a specific type exist in either queue.
bool IsRegistered() const noexcept
Checks if an event type is registered.
void Write(const T &event)
Writes a single event to the current queue.
void RegisterEvent()
Registers an event type for use.
void Merge(EventQueue &other)
Merges events from another EventQueue into the current queue.
Queue for managing multiple event types.
bool Has() const
Checks if a resource exists.
void Insert(T &&resource)
Inserts a new resource.
bool TryRemove()
Tries to remove a resource.
bool TryInsert(T &&resource)
Tries to insert a resource if not present.
void Remove()
Removes a resource.
T * TryGet()
Tries to get mutable pointer to a resource.
void Clear()
Clears all resources.
bool TryEmplace(Args &&... args)
Tries to emplace a resource if not present.
T & Get()
Gets mutable reference to a resource. Triggers assertion if resource doesn't exist.
void Emplace(Args &&... args)
Emplaces a new resource in-place.
Concept to check if a type can be used as a component.
Definition component.hpp:39
Concept for valid event types.
Definition event.hpp:44
Concept for valid resource types.
Definition resource.hpp:21
Concept that constrains a type to be the World type (with any cv-qualifiers).
Definition world.hpp:45
#define HELIOS_WARN(...)
Definition logger.hpp:687
BasicQuery< World, Allocator, Components... > Query
Type alias for query with mutable world access.
Definition query.hpp:2481
STL namespace.
Event emitted when an entity is destroyed.
Event emitted when an entity is spawned/created.