khora_data/ecs/
world.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The heart of the CRPECS: the `World` struct.
16
17use std::{
18    any::TypeId,
19    collections::{HashMap, HashSet},
20};
21
22use bincode::config;
23use khora_core::{
24    ecs::entity::EntityId,
25    renderer::api::scene::{GpuMesh, Mesh},
26};
27
28use crate::ecs::{
29    components::HandleComponent,
30    entity_store::EntityStore,
31    page::{ComponentPage, PageIndex},
32    planner::QueryPlanner,
33    query::{Query, WorldQuery},
34    registry::ComponentRegistry,
35    serialization::SceneMemoryLayout,
36    storage::StorageManager,
37    AudioListener, AudioSource, Camera, Children, Collider, Component, ComponentBundle,
38    DomainBitset, GlobalTransform, MaterialComponent, Parent, QueryMut, QueryPlan, RigidBody,
39    SemanticDomain, SerializedPage, Transform, TypeRegistry,
40};
41
42/// Errors that can occur when adding a component to an entity.
43#[derive(Debug, PartialEq, Eq)]
44pub enum AddComponentError {
45    /// The specified entity does not exist or is not alive.
46    EntityNotFound,
47    /// The specified component type is not registered in the ECS.
48    ComponentNotRegistered,
49    /// The entity already has a component of the specified type.
50    ComponentAlreadyExists,
51}
52
53/// Simple statistics for a semantic domain.
54#[derive(Debug, Default, Clone, Copy)]
55pub struct DomainStats {
56    /// Total number of entities in this domain.
57    pub entity_count: u32,
58    /// Total number of pages allocated for this domain.
59    pub page_count: u32,
60}
61
62/// A trait providing low-level access to the World for maintenance tasks.
63pub trait WorldMaintenance {
64    /// Cleans up an orphaned data slot in a page.
65    fn cleanup_orphan_at(&mut self, location: PageIndex, domain: SemanticDomain);
66
67    /// Vacuums a hole in a page by moving the last entity into it.
68    ///
69    /// # Arguments
70    /// * `page_index` - The index of the page containing the hole.
71    /// * `hole_row_index` - The row index of the hole to be filled.
72    fn vacuum_hole_at(&mut self, page_index: u32, hole_row_index: u32);
73}
74
75/// The central container for the entire ECS, holding all entities, components, and metadata.
76pub struct World {
77    /// Manages entity IDs and metadata.
78    pub(crate) entities: EntityStore,
79    /// Manages component storage and pages.
80    pub(crate) storage: StorageManager,
81    /// Manages query planning and caching.
82    pub(crate) planner: QueryPlanner,
83    /// The type registry for serialization purposes.
84    type_registry: TypeRegistry,
85}
86
87impl World {
88    /// (Internal) Allocates a new or recycled `EntityId` and reserves its metadata slot.
89    fn create_entity(&mut self) -> EntityId {
90        self.entities.create_entity()
91    }
92
93    /// (Internal) Finds a page suitable for the given `ComponentBundle`, or creates one if none exists.
94    fn find_or_create_page_for_bundle<B: ComponentBundle>(&mut self) -> u32 {
95        self.storage.find_or_create_page_for_bundle::<B>()
96    }
97
98    /// (Internal) A helper function to handle the `swap_remove` logic for a single component group.
99    fn remove_from_page(
100        &mut self,
101        entity_to_despawn: EntityId,
102        location: PageIndex,
103        domain: SemanticDomain,
104    ) {
105        let page = &mut self.storage.pages[location.page_id as usize];
106        if page.entities.is_empty() {
107            return;
108        }
109
110        let last_entity_in_page = *page.entities.last().unwrap();
111        page.swap_remove_row(location.row_index);
112
113        if last_entity_in_page != entity_to_despawn {
114            let metadata = self.entities.get_metadata_mut(last_entity_in_page).unwrap();
115            metadata.locations.insert(domain, location);
116        }
117    }
118
119    /// Finds or creates a page for the given signature of component `TypeId`s.
120    fn find_or_create_page_for_signature(&mut self, signature: &[TypeId]) -> u32 {
121        self.storage.find_or_create_page_for_signature(signature)
122    }
123
124    /// Creates a new, empty `World` with pre-registered internal component types.
125    pub fn new() -> Self {
126        let mut world = Self {
127            entities: EntityStore::new(),
128            storage: StorageManager::new(ComponentRegistry::default()),
129            planner: QueryPlanner::new(),
130            type_registry: TypeRegistry::default(),
131        };
132        // Registration of built-in components
133        world.register_component::<Transform>(SemanticDomain::Spatial);
134        world.register_component::<GlobalTransform>(SemanticDomain::Spatial);
135        world.register_component::<Parent>(SemanticDomain::Spatial);
136        world.register_component::<Children>(SemanticDomain::Spatial);
137
138        // Registration of render components
139        world.register_component::<HandleComponent<Mesh>>(SemanticDomain::Render);
140        world.register_component::<HandleComponent<GpuMesh>>(SemanticDomain::Render);
141        world.register_component::<MaterialComponent>(SemanticDomain::Render);
142        world.register_component::<Camera>(SemanticDomain::Render);
143
144        // Registration of audio components
145        world.register_component::<AudioSource>(SemanticDomain::Audio);
146        world.register_component::<AudioListener>(SemanticDomain::Audio);
147
148        // Registration of physics components
149        world.register_component::<RigidBody>(SemanticDomain::Physics);
150        world.register_component::<Collider>(SemanticDomain::Physics);
151        world.register_component::<crate::ecs::KinematicCharacterController>(
152            SemanticDomain::Physics,
153        );
154
155        world
156    }
157
158    /// Spawns a new entity with the given bundle of components.
159    ///
160    /// This is the primary method for creating entities. It orchestrates the entire process:
161    /// 1. Allocates a new `EntityId`.
162    /// 2. Finds or creates a suitable `ComponentPage` for the component bundle.
163    /// 3. Pushes the component data into the page's columns.
164    /// 4. Updates the entity's metadata to point to the new data's location.
165    /// 5. Updates domain bitsets and stats for the newly created entity components.
166    ///
167    /// Returns the `EntityId` of the newly created entity.
168    pub fn spawn<B: ComponentBundle>(&mut self, bundle: B) -> EntityId {
169        // Step 1: Allocate a new EntityId.
170        let entity_id = self.create_entity();
171
172        // Step 2: Find or create a page for this bundle.
173        let page_id = self.find_or_create_page_for_bundle::<B>();
174
175        // --- Step 3: Push component data into the page. ---
176        let row_index;
177        {
178            let page = &mut self.storage.pages[page_id as usize];
179            row_index = page.entities.len() as u32;
180            unsafe {
181                bundle.add_to_page(page);
182            }
183            page.add_entity(entity_id);
184        }
185
186        // --- Step 4: Update the entity's metadata. ---
187        let location = PageIndex { page_id, row_index };
188        let metadata = self.entities.get_metadata_mut(entity_id).unwrap();
189        B::update_metadata(metadata, location, &self.storage.registry);
190
191        // --- Step 5: Update domain bitsets and stats for the newly created entity components. ---
192        for domain in metadata.locations.keys() {
193            self.storage
194                .domain_bitsets
195                .entry(*domain)
196                .or_default()
197                .set(entity_id.index);
198
199            self.storage
200                .domain_stats
201                .entry(*domain)
202                .or_default()
203                .entity_count += 1;
204        }
205
206        entity_id
207    }
208
209    /// Despawns an entity, removing all its components and freeing its ID for recycling.
210    ///
211    /// This method performs the following steps:
212    /// 1. Verifies that the `EntityId` is valid by checking its index and generation.
213    /// 2. Removes the entity's component data from all pages where it is stored.
214    /// 3. Marks the entity's metadata slot as vacant and adds its index to the free list.
215    ///
216    /// Returns `true` if the entity was valid and despawned, `false` otherwise.
217    pub fn despawn(&mut self, entity_id: EntityId) -> bool {
218        // Step 1: Validate the EntityId.
219        // First, check if the index is even valid for our entities Vec.
220        if entity_id.index as usize >= self.entities.len() {
221            return false;
222        }
223
224        // Get the data at the slot.
225        let (id_in_world, metadata_slot) = self.entities.get(entity_id.index as usize).unwrap();
226
227        // An ID is valid if its generation matches the one in the world,
228        // AND if the metadata slot is currently occupied (`is_some`).
229        if id_in_world.generation != entity_id.generation || metadata_slot.is_none() {
230            return false;
231        }
232
233        // --- At this point, the ID is valid. ---
234
235        // Step 2: Take the metadata out of the slot, leaving it `None`.
236        // This is what officially "kills" the entity.
237        let metadata = self
238            .entities
239            .get_mut(entity_id.index as usize)
240            .unwrap()
241            .1
242            .take()
243            .unwrap();
244        self.entities.freed_entities.push(entity_id.index);
245
246        // --- Step 3: Iterate over the entity's component locations and remove them ---
247        for (domain, location) in metadata.locations {
248            self.remove_from_page(entity_id, location, domain);
249
250            // Clear the entity's bit in the domain bitset and update stats.
251            if let Some(bitset) = self.storage.domain_bitsets.get_mut(&domain) {
252                bitset.clear(entity_id.index);
253            }
254            if let Some(stats) = self.storage.domain_stats.get_mut(&domain) {
255                stats.entity_count = stats.entity_count.saturating_sub(1);
256            }
257        }
258        true
259    }
260
261    /// Creates an iterator that queries the world for entities matching a set of components and filters.
262    ///
263    /// This is the primary method for reading and writing data in the ECS. The query `Q`
264    /// is specified as a tuple via turbofish syntax. It can include component references
265    /// (e.g., `&Position`, `&mut Velocity`) and filters (e.g., `Without<Parent>`).
266    ///
267    /// This method is very cheap to call. It performs an efficient search to identify all
268    /// `ComponentPage`s that satisfy the query's criteria. The returned iterator then
269    /// efficiently iterates over the data in only those pages.
270    ///
271    /// # Examples
272    ///
273    /// ```rust,ignore
274    /// // Find all entities with a `Transform` and `GlobalTransform`.
275    /// for (transform, global) in world.query::<(&Transform, &GlobalTransform)>() {
276    ///     // ...
277    /// }
278    ///
279    /// // Find all root entities (those with a `Transform` but without a `Parent`).
280    /// for (transform,) in world.query::<(&Transform, Without<Parent>)>() {
281    ///     // ...
282    /// }
283    /// ```
284    pub fn query<'a, Q: WorldQuery>(&'a self) -> Query<'a, Q> {
285        let type_ids = Q::type_ids();
286
287        // 1. Try to fetch the strategy plan from the cache.
288        // We cache the execution logic (Native vs Transversal), not the page indices.
289        let plan = {
290            let cache = self.planner.query_cache.read().unwrap();
291            if let Some(plan) = cache.get(&type_ids) {
292                plan.clone()
293            } else {
294                drop(cache);
295                let new_plan = self.analyze_query(&type_ids);
296                let mut cache = self.planner.query_cache.write().unwrap();
297                cache.insert(type_ids, new_plan.clone());
298                new_plan
299            }
300        };
301
302        // 2. Dynamically find matching pages for this call.
303        // This ensures the query is correct even if new archetypes were created
304        // in a different domain since the last call.
305        let matching_page_indices =
306            self.find_matching_pages(&plan.driver_signature, &Q::without_type_ids());
307
308        // 3. Return the query with the plan and the current matching pages.
309        Query::new(self, plan, matching_page_indices)
310    }
311
312    /// Creates a mutable iterator that queries the world for entities matching a set of components and filters.
313    ///
314    /// This method is similar to `query`, but it allows mutable access to the components.
315    /// It uses the same dynamic plan re-finding to ensure thread-safe consistency.
316    pub fn query_mut<'a, Q: WorldQuery>(&'a mut self) -> QueryMut<'a, Q> {
317        let type_ids = Q::type_ids();
318
319        // 1. Get strategy from cache
320        let plan = {
321            let cache = self.planner.query_cache.read().unwrap();
322            if let Some(plan) = cache.get(&type_ids) {
323                plan.clone()
324            } else {
325                drop(cache);
326                let new_plan = self.analyze_query(&type_ids);
327                let mut cache = self.planner.query_cache.write().unwrap();
328                cache.insert(type_ids, new_plan.clone());
329                new_plan
330            }
331        };
332
333        // 2. Dynamically find pages
334        let matching_page_indices =
335            self.find_matching_pages(&plan.driver_signature, &Q::without_type_ids());
336
337        // 3. Construct the iterator
338        QueryMut::new(self, plan, matching_page_indices)
339    }
340
341    /// Registers a component type with a specific semantic domain.
342    ///
343    /// This is a crucial setup step. Before a component of type `T` can be used
344    /// in a bundle, it must be registered with the world to define which semantic
345    /// page group its data will be stored in.
346    pub fn register_component<T: Component>(&mut self, domain: SemanticDomain) {
347        self.storage.registry.register::<T>(domain);
348        self.type_registry.register::<T>();
349    }
350
351    /// Analyzes a query's component signature to create an optimized execution plan.
352    ///
353    /// This method identifies if a query is transversal (spanning multiple domains)
354    /// and selects the most efficient "Driver Domain" based on entity density.
355    pub(crate) fn analyze_query(&self, type_ids: &[TypeId]) -> QueryPlan {
356        let mut domains = HashSet::new();
357        for type_id in type_ids {
358            if let Some(domain) = self.storage.registry.get_domain(*type_id) {
359                domains.insert(domain);
360            }
361        }
362
363        if domains.len() <= 1 {
364            // NATIVE MODE: All components belong to the same semantic domain (or none).
365            // This is the fastest execution path as it avoids any cross-domain joins.
366            let first_domain = domains.into_iter().next();
367            let plan = QueryPlan::new(false, first_domain, HashSet::new(), type_ids.to_vec());
368            return plan;
369        }
370
371        // TRANSVERSAL MODE
372        // Select the domain with the highest density of entities as the driver domain.
373        // Highest density = most entities per page => page_A_count * entity_B_count < page_B_count * entity_A_count
374        let driver_domain = domains
375            .iter()
376            .min_by(|&&a, &&b| {
377                let stats_a = self
378                    .storage
379                    .domain_stats
380                    .get(&a)
381                    .copied()
382                    .unwrap_or_default();
383                let stats_b = self
384                    .storage
385                    .domain_stats
386                    .get(&b)
387                    .copied()
388                    .unwrap_or_default();
389                let score_a = (stats_a.page_count as u64) * (stats_b.entity_count as u64);
390                let score_b = (stats_b.page_count as u64) * (stats_a.entity_count as u64);
391                score_a.cmp(&score_b)
392            })
393            .copied()
394            .unwrap(); // Should always be Some if domains.len() > 1
395
396        let mut peer_domains = domains;
397        peer_domains.remove(&driver_domain);
398
399        // Calculate driver signature (subset of type_ids in the driver domain)
400        let mut driver_signature = Vec::new();
401        for type_id in type_ids {
402            if self.storage.registry.get_domain(*type_id) == Some(driver_domain) {
403                driver_signature.push(*type_id);
404            }
405        }
406        driver_signature.sort();
407
408        // Initialize the final plan.
409        // In transversal mode, the driver signature is the subset of components
410        // that belong to the driver domain.
411        QueryPlan::new(true, Some(driver_domain), peer_domains, driver_signature)
412    }
413
414    /// Internal helper to find pages matching a signature and filter.
415    fn find_matching_pages(&self, type_ids: &[TypeId], without_type_ids: &[TypeId]) -> Vec<u32> {
416        let mut matching_page_indices = Vec::new();
417        'page_loop: for (page_id, page) in self.storage.pages.iter().enumerate() {
418            for required_type in type_ids {
419                if page.type_ids.binary_search(required_type).is_err() {
420                    continue 'page_loop;
421                }
422            }
423            for excluded_type in without_type_ids {
424                if page.type_ids.binary_search(excluded_type).is_ok() {
425                    continue 'page_loop;
426                }
427            }
428            matching_page_indices.push(page_id as u32);
429        }
430        matching_page_indices
431    }
432
433    /// (Internal) Computes a bitset that represents the intersection of all domains
434    /// involved in a transversal query. This is used to speed up joins by skipping
435    /// metadata lookups for entities that are guaranteed to not satisfy the query.
436    pub(crate) fn compute_query_bitset(&self, plan: &QueryPlan) -> Option<DomainBitset> {
437        if plan.mode == crate::ecs::QueryMode::Native {
438            return None;
439        }
440
441        let driver_domain = plan.driver_domain?;
442
443        // Start with the driver domain's bitset.
444        let mut bitset = self.storage.domain_bitsets.get(&driver_domain)?.clone();
445
446        // Intersect with all peer domains.
447        for peer_domain in &plan.peer_domains {
448            if let Some(peer_bitset) = self.storage.domain_bitsets.get(peer_domain) {
449                bitset.intersect(peer_bitset);
450            } else {
451                // If a required peer domain has NO components at all, the intersection is empty.
452                return Some(DomainBitset::new());
453            }
454        }
455
456        Some(bitset)
457    }
458
459    /// This operation is designed to be fast. It performs the necessary data
460    /// migration to move the entity's components for the given `SemanticDomain`
461    /// to a new `ComponentPage` that matches the new layout.
462    ///
463    /// Crucially, it does NOT clean up the "hole" left in the old page. Instead,
464    /// it returns the location of the orphaned data, delegating the cleanup task
465    /// to an asynchronous garbage collection system.
466    ///
467    /// # Returns
468    ///
469    /// - `Ok(Option<PageIndex>)`: On success. The `Option` contains the location of
470    ///   orphaned data if a migration occurred, which should be sent to a garbage collector.
471    ///   It is `None` if no migration was needed (e.g., adding to a new domain).
472    /// - `Err(AddComponentError)`: If the operation failed (e.g., entity not alive,
473    ///   component not registered, or component already present).
474    pub fn add_component<C: Component>(
475        &mut self,
476        entity_id: EntityId,
477        component: C,
478    ) -> Result<Option<PageIndex>, AddComponentError> {
479        // 1. Validate EntityId and get metadata
480        let Some((id_in_world, Some(_))) = self.entities.get(entity_id.index as usize) else {
481            return Err(AddComponentError::EntityNotFound);
482        };
483
484        if id_in_world.generation != entity_id.generation {
485            return Err(AddComponentError::EntityNotFound);
486        }
487
488        let Some(domain) = self.storage.registry.get_domain(TypeId::of::<C>()) else {
489            return Err(AddComponentError::ComponentNotRegistered);
490        };
491
492        let mut metadata = self
493            .entities
494            .get_mut(entity_id.index as usize)
495            .unwrap()
496            .1
497            .take()
498            .unwrap();
499        let old_location_opt = metadata.locations.get(&domain).copied();
500
501        // 2. Determine old and new page signatures
502        let old_type_ids = old_location_opt.map_or(Vec::new(), |loc| {
503            self.storage.pages[loc.page_id as usize].type_ids.clone()
504        });
505        let mut new_type_ids = old_type_ids.clone();
506        new_type_ids.push(TypeId::of::<C>());
507        new_type_ids.sort();
508        new_type_ids.dedup();
509
510        if new_type_ids == old_type_ids {
511            self.entities.get_mut(entity_id.index as usize).unwrap().1 = Some(metadata); // Put it back
512            return Err(AddComponentError::ComponentAlreadyExists);
513        }
514
515        // 3. Find or create the destination page
516        let dest_page_id = self.find_or_create_page_for_signature(&new_type_ids);
517
518        // 4. Perform the migration
519        let dest_row_index;
520        unsafe {
521            let (src_page_opt, dest_page) = if let Some(loc) = old_location_opt {
522                if loc.page_id == dest_page_id {
523                    unreachable!(); // Should be caught by signature check above
524                } else {
525                    // This unsafe block is needed to get mutable access to two different pages
526                    let all_pages_ptr = self.storage.pages.as_mut_ptr();
527                    let dest_page = &mut *all_pages_ptr.add(dest_page_id as usize);
528                    let src_page = &*all_pages_ptr.add(loc.page_id as usize);
529                    (Some(src_page), dest_page)
530                }
531            } else {
532                (None, &mut self.storage.pages[dest_page_id as usize])
533            };
534
535            dest_row_index = dest_page.entities.len() as u32;
536
537            if let Some(src_page) = src_page_opt {
538                let src_row = old_location_opt.unwrap().row_index as usize;
539                for type_id in &old_type_ids {
540                    let copier = self.storage.registry.get_row_copier(type_id).unwrap();
541                    let src_col = src_page.columns.get(type_id).unwrap();
542                    let dest_col = dest_page.columns.get_mut(type_id).unwrap();
543                    copier(src_col.as_ref(), src_row, dest_col.as_mut());
544                }
545            }
546
547            dest_page
548                .columns
549                .get_mut(&TypeId::of::<C>())
550                .unwrap()
551                .as_any_mut()
552                .downcast_mut::<Vec<C>>()
553                .unwrap()
554                .push(component);
555
556            dest_page.add_entity(entity_id);
557        }
558
559        // 5. Update metadata and put it back
560        metadata.locations.insert(
561            domain,
562            PageIndex {
563                page_id: dest_page_id,
564                row_index: dest_row_index,
565            },
566        );
567
568        // Update the domain bitset for the entity.
569        self.storage
570            .domain_bitsets
571            .entry(domain)
572            .or_default()
573            .set(entity_id.index);
574
575        self.entities.get_mut(entity_id.index as usize).unwrap().1 = Some(metadata);
576
577        // 6. Return the old location for cleanup, without performing swap_remove
578        Ok(old_location_opt)
579    }
580
581    /// Logically removes all components belonging to a specific `SemanticDomain` from an entity.
582    ///
583    /// This is an extremely fast, O(1) operation that only modifies the entity's
584    /// metadata. It does not immediately deallocate or move any component data.
585    /// The component data is "orphaned" and will be cleaned up later by a
586    /// garbage collection process.
587    ///
588    /// This method is generic over a component `C` to determine which domain to remove.
589    ///
590    /// # Returns
591    ///
592    /// - `Some(PageIndex)`: Contains the location of the orphaned data if the
593    ///   components were successfully removed. This can be sent to a garbage collector.
594    /// - `None`: If the entity is not alive or did not have any components in the
595    ///   specified `SemanticDomain`.
596    pub fn remove_component_domain<C: Component>(
597        &mut self,
598        entity_id: EntityId,
599    ) -> Option<PageIndex> {
600        // 1. Validate the entity ID to ensure we're acting on a live entity.
601        let (id_in_world, metadata_slot) = self.entities.get_mut(entity_id.index as usize)?;
602        if id_in_world.generation != entity_id.generation || metadata_slot.is_none() {
603            return None;
604        }
605
606        // 2. Use the registry to find the component's domain.
607        let domain = self.storage.registry.get_domain(TypeId::of::<C>())?;
608
609        // 3. Remove the location entry from the entity's metadata.
610        //    `HashMap::remove` returns the value that was at that key, which is exactly what we need.
611        let metadata = metadata_slot.as_mut().unwrap();
612        let location = metadata.locations.remove(&domain);
613
614        // Clear the domain bitset if a component was removed.
615        if location.is_some() {
616            if let Some(bitset) = self.storage.domain_bitsets.get_mut(&domain) {
617                bitset.clear(entity_id.index);
618            }
619        }
620
621        location
622    }
623
624    /// Gets a mutable reference to a single component `T` for a given entity.
625    ///
626    /// This provides direct, "random" access to a component, which can be less
627    /// performant than querying but is useful for targeted modifications.
628    ///
629    /// # Returns
630    ///
631    /// `None` if the entity is not alive or does not have the requested component.
632    pub fn get_mut<T: Component>(&mut self, entity_id: EntityId) -> Option<&mut T> {
633        // 1. Validate the entity ID.
634        let (id_in_world, metadata_opt) = self.entities.get(entity_id.index as usize).unwrap();
635        if id_in_world.generation != entity_id.generation || metadata_opt.is_none() {
636            return None;
637        }
638        let metadata = metadata_opt.as_ref().unwrap();
639
640        // 2. Use the registry to find the component's domain and its location.
641        let domain = self.storage.registry.get_domain(TypeId::of::<T>())?;
642        let location = metadata.locations.get(&domain)?;
643
644        // 3. Get the component data from the page.
645        let type_id = TypeId::of::<T>();
646        let page = self.storage.pages.get_mut(location.page_id as usize)?;
647        let column = page.columns.get_mut(&type_id)?;
648        let vec = column.as_any_mut().downcast_mut::<Vec<T>>()?;
649
650        vec.get_mut(location.row_index as usize)
651    }
652
653    /// Gets mutable references to components of type `T` for multiple entities simultaneously.
654    ///
655    /// This is safer than `get_mut` in a loop because it allows retrieving multiple
656    /// disjoint mutable references to components of the same type.
657    ///
658    /// # Returns
659    ///
660    /// An array of `Option<&mut T>`. If any entity is not found, does not have the component,
661    /// or if there are duplicate requests for the same component instance, that entry will be `None`.
662    pub fn get_many_mut<T: Component, const N: usize>(
663        &mut self,
664        ids: [EntityId; N],
665    ) -> [Option<&mut T>; N] {
666        let mut results: [Option<&mut T>; N] = std::array::from_fn(|_| None);
667
668        let type_id = TypeId::of::<T>();
669        let domain = match self.storage.registry.get_domain(type_id) {
670            Some(d) => d,
671            None => return results,
672        };
673
674        // 1. Collect locations and check for duplicates
675        let mut locations = [(0u32, 0u32); N];
676        let mut found_mask = [false; N];
677
678        for i in 0..N {
679            if let Some((stored_id, Some(metadata))) = self.entities.get(ids[i].index as usize) {
680                // Ensure the EntityId matches (including generation)
681                if *stored_id == ids[i] {
682                    if let Some(loc) = metadata.locations.get(&domain) {
683                        locations[i] = (loc.page_id, loc.row_index);
684                        found_mask[i] = true;
685                    }
686                }
687            }
688        }
689
690        // 2. Check for collisions in (page, row) to prevent aliasing
691        for i in 0..N {
692            if !found_mask[i] {
693                continue;
694            }
695            for j in (i + 1)..N {
696                if found_mask[j] && locations[i] == locations[j] {
697                    // Collision detected: return all None for safety
698                    return std::array::from_fn(|_| None);
699                }
700            }
701        }
702
703        // 3. Retrieve references using unsafe to bypass split_at_mut complexity.
704        // SAFETY: We have verified that all (page, row) pairs are unique,
705        // so we are not creating multiple mutable references to the same data.
706        for i in 0..N {
707            if found_mask[i] {
708                let (page_id, row_index) = locations[i];
709                unsafe {
710                    // We can't borrow self.pages multiple times mutably in the loop,
711                    // but we know the indices are disjoint or the data is disjoint.
712                    let world_ptr = self as *mut Self;
713                    if let Some(page) = (&mut *world_ptr).storage.pages.get_mut(page_id as usize) {
714                        if let Some(column) = page.columns.get_mut(&type_id) {
715                            if let Some(vec) = column.as_any_mut().downcast_mut::<Vec<T>>() {
716                                results[i] = Some(vec.get_unchecked_mut(row_index as usize));
717                            }
718                        }
719                    }
720                }
721            }
722        }
723
724        results
725    }
726
727    /// Gets an immutable reference to a single component `T` for a given entity.
728    ///
729    /// This provides direct, "random" access to a component.
730    ///
731    /// # Returns
732    ///
733    /// `None` if the entity is not alive or does not have the requested component.
734    pub fn get<T: Component>(&self, entity_id: EntityId) -> Option<&T> {
735        // 1. Validate the entity ID.
736        let (id_in_world, metadata_opt) = self.entities.get(entity_id.index as usize).unwrap();
737        if id_in_world.generation != entity_id.generation || metadata_opt.is_none() {
738            return None;
739        }
740        let metadata = metadata_opt.as_ref().unwrap();
741
742        // 2. Use the registry to find the component's domain and its location.
743        let domain = self.storage.registry.get_domain(TypeId::of::<T>())?;
744        let location = metadata.locations.get(&domain)?;
745
746        // 3. Get the component data from the page.
747        let type_id = TypeId::of::<T>();
748        let page = self.storage.pages.get(location.page_id as usize)?;
749
750        // 4. Return the immutable reference.
751        let vec = page
752            .columns
753            .get(&type_id)?
754            .as_any()
755            .downcast_ref::<Vec<T>>()?;
756        vec.get(location.row_index as usize)
757    }
758
759    /// Returns an iterator over all currently living `EntityId`s in the world.
760    pub fn iter_entities(&self) -> impl Iterator<Item = EntityId> + '_ {
761        self.entities
762            .iter()
763            .filter_map(|(id, metadata_opt)| metadata_opt.as_ref().map(|_| *id))
764    }
765
766    /// Serializes the entire World state using a direct memory layout strategy.
767    ///
768    /// This method is highly unsafe as it reads raw component memory.
769    pub fn serialize_archetype(&self) -> Result<Vec<u8>, bincode::error::EncodeError> {
770        let mut serialized_pages = Vec::with_capacity(self.storage.pages.len());
771        for page in &self.storage.pages {
772            let mut serialized_columns = HashMap::new();
773
774            // Use the TypeRegistry to get the stable string name for each TypeId.
775            let type_names: Vec<String> = page
776                .type_ids
777                .iter()
778                .map(|id| self.type_registry.get_name_of(id).unwrap().to_string())
779                .collect();
780
781            for type_id in &page.type_ids {
782                let type_name = self.type_registry.get_name_of(type_id).unwrap();
783                let column = &page.columns[type_id];
784                // UNSAFE: Copying raw bytes from the component vector.
785                let bytes = unsafe { column.as_bytes() };
786                serialized_columns.insert(type_name.to_string(), bytes.to_vec());
787            }
788
789            serialized_pages.push(SerializedPage {
790                type_names,
791                entities: page.entities.clone(),
792                columns: serialized_columns,
793            });
794        }
795        let layout = SceneMemoryLayout {
796            entities: self.entities.entities.clone(),
797            freed_entities: self.entities.freed_entities.clone(),
798            pages: serialized_pages,
799        };
800        bincode::encode_to_vec(layout, config::standard())
801    }
802
803    /// Deserializes and completely replaces the World state from a memory layout.
804    ///
805    /// This method is highly unsafe as it writes raw bytes into component vectors.
806    pub fn deserialize_archetype(
807        &mut self,
808        data: &[u8],
809    ) -> Result<(), bincode::error::DecodeError> {
810        let (layout, _): (SceneMemoryLayout, _) =
811            bincode::decode_from_slice(data, config::standard())?;
812
813        self.entities.entities = layout.entities;
814        self.entities.freed_entities = layout.freed_entities;
815        self.storage.pages.clear();
816
817        for serialized_page in layout.pages {
818            // Use the TypeRegistry to convert string names back to TypeIds.
819            let type_ids: Vec<TypeId> = serialized_page
820                .type_names
821                .iter()
822                .map(|name| {
823                    self.type_registry
824                        .get_id_of(name)
825                        .expect("Serialized component type not registered")
826                })
827                .collect();
828
829            let mut new_page = ComponentPage {
830                type_ids,
831                entities: serialized_page.entities,
832                columns: HashMap::new(),
833            };
834
835            for (type_name, bytes) in &serialized_page.columns {
836                let type_id = self.type_registry.get_id_of(type_name).unwrap();
837                let constructor = self
838                    .storage
839                    .registry
840                    .get_column_constructor(&type_id)
841                    .unwrap();
842                let mut column = constructor();
843                // UNSAFE: Writing raw bytes into the newly created component vector.
844                unsafe {
845                    column.set_from_bytes(bytes);
846                }
847                new_page.columns.insert(type_id, column);
848            }
849            self.storage.pages.push(new_page);
850        }
851
852        Ok(())
853    }
854}
855
856impl WorldMaintenance for World {
857    fn cleanup_orphan_at(&mut self, location: PageIndex, domain: SemanticDomain) {
858        let page = &mut self.storage.pages[location.page_id as usize];
859        if page.entities.is_empty() || location.row_index as usize >= page.entities.len() {
860            return;
861        }
862
863        let last_entity_in_page = *page.entities.last().unwrap();
864        page.swap_remove_row(location.row_index);
865
866        if let Some((_id, metadata_opt)) = self.entities.get_mut(last_entity_in_page.index as usize)
867        {
868            if let Some(metadata) = metadata_opt.as_mut() {
869                if let Some(loc) = metadata.locations.get_mut(&domain) {
870                    *loc = location;
871                }
872            }
873        }
874    }
875
876    fn vacuum_hole_at(&mut self, page_index: u32, hole_row_index: u32) {
877        // Find the domain for this page.
878        // We can infer the domain from the first component type in the page.
879        let domain = {
880            let page = &self.storage.pages[page_index as usize];
881            if let Some(first_type) = page.type_ids.first() {
882                self.storage.registry.get_domain(*first_type)
883            } else {
884                None
885            }
886        };
887
888        if let Some(domain) = domain {
889            // Reuse cleanup logic, constructing a transient PageIndex.
890            let location = PageIndex {
891                page_id: page_index,
892                row_index: hole_row_index,
893            };
894            self.cleanup_orphan_at(location, domain);
895        }
896    }
897}
898
899impl Default for World {
900    /// Creates a new, empty `World` via `World::new()`.
901    fn default() -> Self {
902        Self::new()
903    }
904}