mod type_info; pub use type_info::*; use crate::storage::SparseSetIndex; use std::{ alloc::Layout, any::{Any, TypeId}, collections::hash_map::Entry, }; use thiserror::Error; /// A component is data associated with an `Entity`. Each entity can have multiple different types /// of components, but only one of them per type. /// /// Any type that is `Send + Sync + 'static` automatically implements `Component`. /// /// Components are added with new entities using [Commands::spawn](crate::system::Commands::spawn), /// or to existing entities with [Commands::insert](crate::system::Commands::insert), /// or their [World](crate::world::World) equivalents. /// /// Components can be accessed in systems by using a [Query](crate::system::Query) /// as one of the arguments. /// /// Components can be grouped together into a [Bundle](crate::bundle::Bundle). pub trait Component: Send + Sync + 'static {} impl Component for T {} #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum StorageType { Table, SparseSet, } impl Default for StorageType { fn default() -> Self { StorageType::Table } } #[derive(Debug)] pub struct ComponentInfo { name: String, id: ComponentId, type_id: Option, // SAFETY: This must remain private. It must only be set to "true" if this component is // actually Send + Sync is_send_and_sync: bool, layout: Layout, drop: unsafe fn(*mut u8), storage_type: StorageType, } impl ComponentInfo { #[inline] pub fn id(&self) -> ComponentId { self.id } #[inline] pub fn name(&self) -> &str { &self.name } #[inline] pub fn type_id(&self) -> Option { self.type_id } #[inline] pub fn layout(&self) -> Layout { self.layout } #[inline] pub fn drop(&self) -> unsafe fn(*mut u8) { self.drop } #[inline] pub fn storage_type(&self) -> StorageType { self.storage_type } #[inline] pub fn is_send_and_sync(&self) -> bool { self.is_send_and_sync } fn new(id: ComponentId, descriptor: ComponentDescriptor) -> Self { ComponentInfo { id, name: descriptor.name, storage_type: descriptor.storage_type, type_id: descriptor.type_id, is_send_and_sync: descriptor.is_send_and_sync, drop: descriptor.drop, layout: descriptor.layout, } } } #[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] pub struct ComponentId(usize); impl ComponentId { #[inline] pub const fn new(index: usize) -> ComponentId { ComponentId(index) } #[inline] pub fn index(self) -> usize { self.0 } } impl SparseSetIndex for ComponentId { #[inline] fn sparse_set_index(&self) -> usize { self.index() } fn get_sparse_set_index(value: usize) -> Self { Self(value) } } pub struct ComponentDescriptor { name: String, storage_type: StorageType, // SAFETY: This must remain private. It must only be set to "true" if this component is // actually Send + Sync is_send_and_sync: bool, type_id: Option, layout: Layout, drop: unsafe fn(*mut u8), } impl ComponentDescriptor { pub fn new(storage_type: StorageType) -> Self { Self { name: std::any::type_name::().to_string(), storage_type, is_send_and_sync: true, type_id: Some(TypeId::of::()), layout: Layout::new::(), drop: TypeInfo::drop_ptr::, } } #[inline] pub fn storage_type(&self) -> StorageType { self.storage_type } #[inline] pub fn type_id(&self) -> Option { self.type_id } #[inline] pub fn name(&self) -> &str { &self.name } } impl From for ComponentDescriptor { fn from(type_info: TypeInfo) -> Self { Self { name: type_info.type_name().to_string(), storage_type: StorageType::default(), is_send_and_sync: type_info.is_send_and_sync(), type_id: Some(type_info.type_id()), drop: type_info.drop(), layout: type_info.layout(), } } } #[derive(Debug, Default)] pub struct Components { components: Vec, indices: std::collections::HashMap, resource_indices: std::collections::HashMap, } #[derive(Debug, Error)] pub enum ComponentsError { #[error("A component of type {name:?} ({type_id:?}) already exists")] ComponentAlreadyExists { type_id: TypeId, name: String }, } impl Components { pub(crate) fn add( &mut self, descriptor: ComponentDescriptor, ) -> Result { let index = self.components.len(); if let Some(type_id) = descriptor.type_id { let index_entry = self.indices.entry(type_id); if let Entry::Occupied(_) = index_entry { return Err(ComponentsError::ComponentAlreadyExists { type_id, name: descriptor.name, }); } self.indices.insert(type_id, index); } self.components .push(ComponentInfo::new(ComponentId(index), descriptor)); Ok(ComponentId(index)) } #[inline] pub fn get_or_insert_id(&mut self) -> ComponentId { self.get_or_insert_with(TypeId::of::(), TypeInfo::of::) } #[inline] pub fn get_or_insert_info(&mut self) -> &ComponentInfo { let id = self.get_or_insert_id::(); // SAFE: component_info with the given `id` initialized above unsafe { self.get_info_unchecked(id) } } #[inline] pub fn len(&self) -> usize { self.components.len() } #[inline] pub fn is_empty(&self) -> bool { self.components.len() == 0 } #[inline] pub fn get_info(&self, id: ComponentId) -> Option<&ComponentInfo> { self.components.get(id.0) } /// # Safety /// `id` must be a valid [ComponentId] #[inline] pub unsafe fn get_info_unchecked(&self, id: ComponentId) -> &ComponentInfo { debug_assert!(id.index() < self.components.len()); self.components.get_unchecked(id.0) } #[inline] pub fn get_id(&self, type_id: TypeId) -> Option { self.indices.get(&type_id).map(|index| ComponentId(*index)) } #[inline] pub fn get_resource_id(&self, type_id: TypeId) -> Option { self.resource_indices .get(&type_id) .map(|index| ComponentId(*index)) } #[inline] pub fn get_or_insert_resource_id(&mut self) -> ComponentId { self.get_or_insert_resource_with(TypeId::of::(), TypeInfo::of::) } #[inline] pub fn get_or_insert_non_send_resource_id(&mut self) -> ComponentId { self.get_or_insert_resource_with(TypeId::of::(), TypeInfo::of_non_send_and_sync::) } #[inline] fn get_or_insert_resource_with( &mut self, type_id: TypeId, func: impl FnOnce() -> TypeInfo, ) -> ComponentId { let components = &mut self.components; let index = self.resource_indices.entry(type_id).or_insert_with(|| { let type_info = func(); let index = components.len(); components.push(ComponentInfo::new(ComponentId(index), type_info.into())); index }); ComponentId(*index) } #[inline] pub(crate) fn get_or_insert_with( &mut self, type_id: TypeId, func: impl FnOnce() -> TypeInfo, ) -> ComponentId { let components = &mut self.components; let index = self.indices.entry(type_id).or_insert_with(|| { let type_info = func(); let index = components.len(); components.push(ComponentInfo::new(ComponentId(index), type_info.into())); index }); ComponentId(*index) } } #[derive(Copy, Clone, Debug)] pub struct ComponentTicks { pub(crate) added: u32, pub(crate) changed: u32, } impl ComponentTicks { #[inline] pub fn is_added(&self, last_change_tick: u32, change_tick: u32) -> bool { // The comparison is relative to `change_tick` so that we can detect changes over the whole // `u32` range. Comparing directly the ticks would limit to half that due to overflow // handling. let component_delta = change_tick.wrapping_sub(self.added); let system_delta = change_tick.wrapping_sub(last_change_tick); component_delta < system_delta } #[inline] pub fn is_changed(&self, last_change_tick: u32, change_tick: u32) -> bool { let component_delta = change_tick.wrapping_sub(self.changed); let system_delta = change_tick.wrapping_sub(last_change_tick); component_delta < system_delta } pub(crate) fn new(change_tick: u32) -> Self { Self { added: change_tick, changed: change_tick, } } pub(crate) fn check_ticks(&mut self, change_tick: u32) { check_tick(&mut self.added, change_tick); check_tick(&mut self.changed, change_tick); } /// Manually sets the change tick. /// Usually, this is done automatically via the [`DerefMut`](std::ops::DerefMut) implementation /// on [`Mut`](crate::world::Mut) or [`ResMut`](crate::system::ResMut) etc. /// /// # Example /// ```rust,no_run /// # use bevy_ecs::{world::World, component::ComponentTicks}; /// let world: World = unimplemented!(); /// let component_ticks: ComponentTicks = unimplemented!(); /// /// component_ticks.set_changed(world.read_change_tick()); /// ``` #[inline] pub fn set_changed(&mut self, change_tick: u32) { self.changed = change_tick; } } fn check_tick(last_change_tick: &mut u32, change_tick: u32) { let tick_delta = change_tick.wrapping_sub(*last_change_tick); const MAX_DELTA: u32 = (u32::MAX / 4) * 3; // Clamp to max delta if tick_delta > MAX_DELTA { *last_change_tick = change_tick.wrapping_sub(MAX_DELTA); } }