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 clone_entity;
50mod component_access;
51mod component_mutation;
52mod entity_ops;
53mod entity_world_mut;
54mod non_send_resources;
55mod resources;
56mod serialize_entity;
57mod storage_entry;
58
59#[cfg(test)]
60mod tests;
61
62pub use entity_world_mut::EntityWorldMut;
63
64use storage_entry::ComponentStorageEntry;
65
66// =============================================================================
67// World
68// =============================================================================
69
70/// The central container for all ECS data.
71///
72/// `World` is the top-level struct that owns all ECS state. It provides
73/// methods to spawn/despawn entities, add/remove/query components, and
74/// manage archetypes.
75///
76/// # Design Philosophy
77///
78/// The World follows Bevy's architecture pattern:
79///
80/// 1. **Entity allocation** is handled by `EntityAllocator` using generational indices
81/// 2. **Component storage** uses type-erased `SparseSet<T>` instances
82/// 3. **Archetype tracking** groups entities by their component signature
83/// 4. **Entity-archetype mapping** enables O(1) component lookups
84///
85/// # Type Erasure
86///
87/// Component storage is type-erased using `Box<dyn Any + Send + Sync>`.
88/// This allows the World to store components of any type without being
89/// generic. Type safety is maintained through `ComponentId` keys and
90/// careful downcasting.
91///
92/// # Memory Layout
93///
94/// Components are stored in `SparseSet<T>` instances, one per component type.
95/// Entities are tracked in archetypes for efficient querying. The
96/// `entity_archetypes` map provides O(1) lookup of an entity's archetype.
97///
98/// # Example
99///
100/// ```
101/// use goud_engine::ecs::{World, Component};
102///
103/// // Define components
104/// struct Position { x: f32, y: f32 }
105/// impl Component for Position {}
106///
107/// // Create world
108/// let mut world = World::new();
109///
110/// // World starts with one archetype (empty)
111/// assert_eq!(world.archetype_count(), 1);
112/// assert_eq!(world.entity_count(), 0);
113/// ```
114#[derive(Debug)]
115pub struct World {
116 /// Entity ID allocator.
117 ///
118 /// Manages allocation and deallocation of entity IDs with generation
119 /// counting to prevent stale entity access.
120 entities: EntityAllocator,
121
122 /// Archetype graph.
123 ///
124 /// Tracks all archetypes and their relationships (add/remove edges).
125 /// Always contains at least the EMPTY archetype at index 0.
126 archetypes: ArchetypeGraph,
127
128 /// Maps entities to their current archetype.
129 ///
130 /// Every alive entity has an entry here. When an entity's components
131 /// change, it moves to a new archetype and this mapping is updated.
132 entity_archetypes: HashMap<Entity, ArchetypeId>,
133
134 /// Type-erased component storage.
135 ///
136 /// Maps `ComponentId` to a type-erased storage wrapper. The wrapper contains
137 /// both a `Box<dyn AnyComponentStorage>` for type-erased operations (like remove)
138 /// and a `Box<dyn Any + Send + Sync>` for typed downcasting.
139 ///
140 /// Type safety is ensured by:
141 /// 1. Only inserting `SparseSet<T>` for component type T
142 /// 2. Downcasting with the correct type when accessing
143 storages: HashMap<ComponentId, ComponentStorageEntry>,
144
145 /// Resource storage.
146 ///
147 /// Resources are singleton data that exists outside the entity-component model.
148 /// Examples include Time, Input state, Asset manager, etc.
149 resources: Resources,
150
151 /// Non-send resource storage.
152 ///
153 /// Resources that cannot be safely sent between threads. These include
154 /// window handles, OpenGL contexts, and other thread-local data.
155 /// Non-send resources must only be accessed from the main thread.
156 non_send_resources: NonSendResources,
157
158 /// Whether built-in components have been registered as cloneable.
159 builtins_registered: bool,
160
161 /// Whether built-in components have been registered as serializable.
162 builtins_serializable_registered: bool,
163
164 /// Reverse lookup: type name -> ComponentId for deserialization.
165 serializable_names: HashMap<String, ComponentId>,
166
167 /// Current change tick, incremented at system boundaries.
168 change_tick: u32,
169
170 /// The change tick value from the end of the previous system run.
171 /// Used by `Changed<T>` and `Added<T>` filters to detect new changes.
172 last_change_tick: u32,
173}
174
175impl World {
176 /// Creates a new, empty world.
177 ///
178 /// The world starts with:
179 /// - No entities
180 /// - No component storage
181 /// - One archetype (the EMPTY archetype)
182 ///
183 /// # Example
184 ///
185 /// ```
186 /// use goud_engine::ecs::World;
187 ///
188 /// let world = World::new();
189 /// assert_eq!(world.entity_count(), 0);
190 /// assert_eq!(world.archetype_count(), 1);
191 /// ```
192 #[inline]
193 pub fn new() -> Self {
194 Self {
195 entities: EntityAllocator::new(),
196 archetypes: ArchetypeGraph::new(),
197 entity_archetypes: HashMap::new(),
198 storages: HashMap::new(),
199 resources: Resources::new(),
200 non_send_resources: NonSendResources::new(),
201 builtins_registered: false,
202 builtins_serializable_registered: false,
203 serializable_names: HashMap::new(),
204 change_tick: 0,
205 last_change_tick: 0,
206 }
207 }
208
209 /// Creates a new world with pre-allocated capacity.
210 ///
211 /// Use this when you know approximately how many entities and component
212 /// types you'll have, to avoid reallocations during gameplay.
213 ///
214 /// # Arguments
215 ///
216 /// * `entity_capacity` - Expected number of entities
217 /// * `component_type_capacity` - Expected number of unique component types
218 ///
219 /// # Example
220 ///
221 /// ```
222 /// use goud_engine::ecs::World;
223 ///
224 /// // Pre-allocate for a game with ~10,000 entities and ~50 component types
225 /// let world = World::with_capacity(10_000, 50);
226 /// assert_eq!(world.entity_count(), 0);
227 /// ```
228 #[inline]
229 pub fn with_capacity(entity_capacity: usize, component_type_capacity: usize) -> Self {
230 Self {
231 entities: EntityAllocator::with_capacity(entity_capacity),
232 archetypes: ArchetypeGraph::new(),
233 entity_archetypes: HashMap::with_capacity(entity_capacity),
234 storages: HashMap::with_capacity(component_type_capacity),
235 resources: Resources::new(),
236 non_send_resources: NonSendResources::new(),
237 builtins_registered: false,
238 builtins_serializable_registered: false,
239 serializable_names: HashMap::new(),
240 change_tick: 0,
241 last_change_tick: 0,
242 }
243 }
244
245 // =========================================================================
246 // Direct Access (For Advanced Use)
247 // =========================================================================
248
249 /// Returns a reference to the entity allocator.
250 ///
251 /// This is primarily for internal use and debugging.
252 #[inline]
253 pub fn entities(&self) -> &EntityAllocator {
254 &self.entities
255 }
256
257 /// Returns a reference to the archetype graph.
258 ///
259 /// This is primarily for internal use and debugging.
260 #[inline]
261 pub fn archetypes(&self) -> &ArchetypeGraph {
262 &self.archetypes
263 }
264
265 /// Clears all entities and components from the world.
266 ///
267 /// After calling this:
268 /// - All entities are invalid (even if you hold references)
269 /// - All component storage is cleared
270 /// - Archetypes remain but are empty
271 ///
272 /// # Use Case
273 ///
274 /// Useful for level transitions or resetting game state without
275 /// recreating the world.
276 ///
277 /// # Example
278 ///
279 /// ```
280 /// use goud_engine::ecs::World;
281 ///
282 /// let mut world = World::new();
283 /// // ... spawn entities, add components ...
284 ///
285 /// world.clear();
286 /// assert_eq!(world.entity_count(), 0);
287 /// ```
288 pub fn clear(&mut self) {
289 // Clear all entities mapping
290 self.entity_archetypes.clear();
291
292 // Clear all component storage
293 // Note: We're dropping all storage and recreating empty.
294 // This properly cleans up all components regardless of type.
295 self.storages.clear();
296
297 // Clear archetype entity lists
298 // Collect IDs first to avoid borrow conflict
299 let archetype_ids: Vec<ArchetypeId> = self.archetypes.archetype_ids().collect();
300 for archetype_id in archetype_ids {
301 if let Some(archetype) = self.archetypes.get_mut(archetype_id) {
302 archetype.clear_entities();
303 }
304 }
305
306 // Reset entity allocator by creating a new one
307 // This is safe because we've cleared all references
308 self.entities = EntityAllocator::new();
309
310 // Reset change detection ticks
311 self.change_tick = 0;
312 self.last_change_tick = 0;
313 }
314
315 // =========================================================================
316 // Change Detection Ticks
317 // =========================================================================
318
319 /// Returns the current change tick.
320 #[inline]
321 pub fn change_tick(&self) -> u32 {
322 self.change_tick
323 }
324
325 /// Returns the change tick from the end of the previous system run.
326 #[inline]
327 pub fn last_change_tick(&self) -> u32 {
328 self.last_change_tick
329 }
330
331 /// Increments the change tick and returns the new value.
332 #[inline]
333 pub fn increment_change_tick(&mut self) -> u32 {
334 self.change_tick += 1;
335 self.change_tick
336 }
337
338 /// Sets the last change tick (typically called at the end of a system).
339 #[inline]
340 pub fn set_last_change_tick(&mut self, tick: u32) {
341 self.last_change_tick = tick;
342 }
343}
344
345impl Default for World {
346 /// Creates an empty world.
347 ///
348 /// Equivalent to [`World::new()`].
349 #[inline]
350 fn default() -> Self {
351 Self::new()
352 }
353}