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}