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 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}