Skip to main content

goud_engine/ecs/world/
mod.rs

1//! World - the central container for all ECS data.
2//!
3//! The `World` is the heart of the ECS. It manages:
4//!
5//! - **Entities**: Lightweight identifiers created and destroyed via the world
6//! - **Components**: Data attached to entities, stored in type-erased sparse sets
7//! - **Archetypes**: Groups of entities with identical component sets
8//! - **Resources**: Global data shared across systems (future)
9//!
10//! # Architecture
11//!
12//! The World coordinates several subsystems:
13//!
14//! ```text
15//! ┌─────────────────────────────────────────────────────────────┐
16//! │                          World                              │
17//! ├─────────────────────────────────────────────────────────────┤
18//! │  EntityAllocator     - Manages entity ID allocation         │
19//! │  ArchetypeGraph      - Tracks entity->component groupings   │
20//! │  entity_archetypes   - Maps Entity -> ArchetypeId           │
21//! │  storages            - Type-erased component storage        │
22//! └─────────────────────────────────────────────────────────────┘
23//! ```
24//!
25//! # Thread Safety
26//!
27//! The World itself is not thread-safe. For concurrent access:
28//! - Wrap in `Arc<RwLock<World>>` for shared ownership
29//! - Or use the scheduler (future) which manages safe parallel access
30//!
31//! # Example
32//!
33//! ```
34//! use goud_engine::ecs::World;
35//!
36//! let world = World::new();
37//!
38//! assert_eq!(world.entity_count(), 0);
39//! assert_eq!(world.archetype_count(), 1); // Empty archetype always exists
40//! ```
41
42use std::collections::HashMap;
43
44use super::archetype::{ArchetypeGraph, ArchetypeId};
45use super::component::ComponentId;
46use super::entity::{Entity, EntityAllocator};
47use super::resource::{NonSendResources, Resources};
48
49mod component_access;
50mod component_mutation;
51mod entity_ops;
52mod entity_world_mut;
53mod non_send_resources;
54mod resources;
55mod storage_entry;
56
57#[cfg(test)]
58mod tests;
59
60pub use entity_world_mut::EntityWorldMut;
61
62use storage_entry::ComponentStorageEntry;
63
64// =============================================================================
65// World
66// =============================================================================
67
68/// The central container for all ECS data.
69///
70/// `World` is the top-level struct that owns all ECS state. It provides
71/// methods to spawn/despawn entities, add/remove/query components, and
72/// manage archetypes.
73///
74/// # Design Philosophy
75///
76/// The World follows Bevy's architecture pattern:
77///
78/// 1. **Entity allocation** is handled by `EntityAllocator` using generational indices
79/// 2. **Component storage** uses type-erased `SparseSet<T>` instances
80/// 3. **Archetype tracking** groups entities by their component signature
81/// 4. **Entity-archetype mapping** enables O(1) component lookups
82///
83/// # Type Erasure
84///
85/// Component storage is type-erased using `Box<dyn Any + Send + Sync>`.
86/// This allows the World to store components of any type without being
87/// generic. Type safety is maintained through `ComponentId` keys and
88/// careful downcasting.
89///
90/// # Memory Layout
91///
92/// Components are stored in `SparseSet<T>` instances, one per component type.
93/// Entities are tracked in archetypes for efficient querying. The
94/// `entity_archetypes` map provides O(1) lookup of an entity's archetype.
95///
96/// # Example
97///
98/// ```
99/// use goud_engine::ecs::{World, Component};
100///
101/// // Define components
102/// struct Position { x: f32, y: f32 }
103/// impl Component for Position {}
104///
105/// // Create world
106/// let mut world = World::new();
107///
108/// // World starts with one archetype (empty)
109/// assert_eq!(world.archetype_count(), 1);
110/// assert_eq!(world.entity_count(), 0);
111/// ```
112#[derive(Debug)]
113pub struct World {
114    /// Entity ID allocator.
115    ///
116    /// Manages allocation and deallocation of entity IDs with generation
117    /// counting to prevent stale entity access.
118    entities: EntityAllocator,
119
120    /// Archetype graph.
121    ///
122    /// Tracks all archetypes and their relationships (add/remove edges).
123    /// Always contains at least the EMPTY archetype at index 0.
124    archetypes: ArchetypeGraph,
125
126    /// Maps entities to their current archetype.
127    ///
128    /// Every alive entity has an entry here. When an entity's components
129    /// change, it moves to a new archetype and this mapping is updated.
130    entity_archetypes: HashMap<Entity, ArchetypeId>,
131
132    /// Type-erased component storage.
133    ///
134    /// Maps `ComponentId` to a type-erased storage wrapper. The wrapper contains
135    /// both a `Box<dyn AnyComponentStorage>` for type-erased operations (like remove)
136    /// and a `Box<dyn Any + Send + Sync>` for typed downcasting.
137    ///
138    /// Type safety is ensured by:
139    /// 1. Only inserting `SparseSet<T>` for component type T
140    /// 2. Downcasting with the correct type when accessing
141    storages: HashMap<ComponentId, ComponentStorageEntry>,
142
143    /// Resource storage.
144    ///
145    /// Resources are singleton data that exists outside the entity-component model.
146    /// Examples include Time, Input state, Asset manager, etc.
147    resources: Resources,
148
149    /// Non-send resource storage.
150    ///
151    /// Resources that cannot be safely sent between threads. These include
152    /// window handles, OpenGL contexts, and other thread-local data.
153    /// Non-send resources must only be accessed from the main thread.
154    non_send_resources: NonSendResources,
155}
156
157impl World {
158    /// Creates a new, empty world.
159    ///
160    /// The world starts with:
161    /// - No entities
162    /// - No component storage
163    /// - One archetype (the EMPTY archetype)
164    ///
165    /// # Example
166    ///
167    /// ```
168    /// use goud_engine::ecs::World;
169    ///
170    /// let world = World::new();
171    /// assert_eq!(world.entity_count(), 0);
172    /// assert_eq!(world.archetype_count(), 1);
173    /// ```
174    #[inline]
175    pub fn new() -> Self {
176        Self {
177            entities: EntityAllocator::new(),
178            archetypes: ArchetypeGraph::new(),
179            entity_archetypes: HashMap::new(),
180            storages: HashMap::new(),
181            resources: Resources::new(),
182            non_send_resources: NonSendResources::new(),
183        }
184    }
185
186    /// Creates a new world with pre-allocated capacity.
187    ///
188    /// Use this when you know approximately how many entities and component
189    /// types you'll have, to avoid reallocations during gameplay.
190    ///
191    /// # Arguments
192    ///
193    /// * `entity_capacity` - Expected number of entities
194    /// * `component_type_capacity` - Expected number of unique component types
195    ///
196    /// # Example
197    ///
198    /// ```
199    /// use goud_engine::ecs::World;
200    ///
201    /// // Pre-allocate for a game with ~10,000 entities and ~50 component types
202    /// let world = World::with_capacity(10_000, 50);
203    /// assert_eq!(world.entity_count(), 0);
204    /// ```
205    #[inline]
206    pub fn with_capacity(entity_capacity: usize, component_type_capacity: usize) -> Self {
207        Self {
208            entities: EntityAllocator::with_capacity(entity_capacity),
209            archetypes: ArchetypeGraph::new(),
210            entity_archetypes: HashMap::with_capacity(entity_capacity),
211            storages: HashMap::with_capacity(component_type_capacity),
212            resources: Resources::new(),
213            non_send_resources: NonSendResources::new(),
214        }
215    }
216
217    // =========================================================================
218    // Direct Access (For Advanced Use)
219    // =========================================================================
220
221    /// Returns a reference to the entity allocator.
222    ///
223    /// This is primarily for internal use and debugging.
224    #[inline]
225    pub fn entities(&self) -> &EntityAllocator {
226        &self.entities
227    }
228
229    /// Returns a reference to the archetype graph.
230    ///
231    /// This is primarily for internal use and debugging.
232    #[inline]
233    pub fn archetypes(&self) -> &ArchetypeGraph {
234        &self.archetypes
235    }
236
237    /// Clears all entities and components from the world.
238    ///
239    /// After calling this:
240    /// - All entities are invalid (even if you hold references)
241    /// - All component storage is cleared
242    /// - Archetypes remain but are empty
243    ///
244    /// # Use Case
245    ///
246    /// Useful for level transitions or resetting game state without
247    /// recreating the world.
248    ///
249    /// # Example
250    ///
251    /// ```
252    /// use goud_engine::ecs::World;
253    ///
254    /// let mut world = World::new();
255    /// // ... spawn entities, add components ...
256    ///
257    /// world.clear();
258    /// assert_eq!(world.entity_count(), 0);
259    /// ```
260    pub fn clear(&mut self) {
261        // Clear all entities mapping
262        self.entity_archetypes.clear();
263
264        // Clear all component storage
265        // Note: We're dropping all storage and recreating empty.
266        // This properly cleans up all components regardless of type.
267        self.storages.clear();
268
269        // Clear archetype entity lists
270        // Collect IDs first to avoid borrow conflict
271        let archetype_ids: Vec<ArchetypeId> = self.archetypes.archetype_ids().collect();
272        for archetype_id in archetype_ids {
273            if let Some(archetype) = self.archetypes.get_mut(archetype_id) {
274                archetype.clear_entities();
275            }
276        }
277
278        // Reset entity allocator by creating a new one
279        // This is safe because we've cleared all references
280        self.entities = EntityAllocator::new();
281    }
282}
283
284impl Default for World {
285    /// Creates an empty world.
286    ///
287    /// Equivalent to [`World::new()`].
288    #[inline]
289    fn default() -> Self {
290        Self::new()
291    }
292}