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}