use crate::{
bundle::BundleId,
component::{ComponentId, Components, RequiredComponentConstructor, StorageType},
entity::{Entity, EntityLocation},
event::Event,
observer::Observers,
query::DebugCheckedUnwrap,
storage::{ImmutableSparseSet, SparseArray, SparseSet, TableId, TableRow},
};
use alloc::{boxed::Box, vec::Vec};
use bevy_platform::collections::{hash_map::Entry, HashMap};
use core::{
hash::Hash,
ops::{Index, IndexMut, RangeFrom},
};
use nonmax::NonMaxU32;
#[derive(Event)]
#[expect(dead_code, reason = "Prepare for the upcoming Query as Entities")]
pub(crate) struct ArchetypeCreated(pub ArchetypeId);
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(transparent)]
pub struct ArchetypeRow(NonMaxU32);
impl ArchetypeRow {
#[inline]
pub const fn new(index: NonMaxU32) -> Self {
Self(index)
}
#[inline]
pub const fn index(self) -> usize {
self.0.get() as usize
}
#[inline]
pub const fn index_u32(self) -> u32 {
self.0.get()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct ArchetypeId(u32);
impl ArchetypeId {
pub const EMPTY: ArchetypeId = ArchetypeId(0);
#[inline]
pub const fn new(index: usize) -> Self {
ArchetypeId(index as u32)
}
#[inline]
pub fn index(self) -> usize {
self.0 as usize
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub(crate) enum ComponentStatus {
Added,
Existing,
}
pub(crate) struct ArchetypeAfterBundleInsert {
pub archetype_id: ArchetypeId,
bundle_status: Box<[ComponentStatus]>,
pub required_components: Box<[RequiredComponentConstructor]>,
inserted: Box<[ComponentId]>,
added_len: usize,
}
impl ArchetypeAfterBundleInsert {
pub(crate) fn inserted(&self) -> &[ComponentId] {
&self.inserted
}
pub(crate) fn added(&self) -> &[ComponentId] {
unsafe { self.inserted.get(..self.added_len).debug_checked_unwrap() }
}
pub(crate) fn existing(&self) -> &[ComponentId] {
unsafe { self.inserted.get(self.added_len..).debug_checked_unwrap() }
}
}
pub(crate) trait BundleComponentStatus {
unsafe fn get_status(&self, index: usize) -> ComponentStatus;
}
impl BundleComponentStatus for ArchetypeAfterBundleInsert {
#[inline]
unsafe fn get_status(&self, index: usize) -> ComponentStatus {
unsafe { *self.bundle_status.get_unchecked(index) }
}
}
pub(crate) struct SpawnBundleStatus;
impl BundleComponentStatus for SpawnBundleStatus {
#[inline]
unsafe fn get_status(&self, _index: usize) -> ComponentStatus {
ComponentStatus::Added
}
}
#[derive(Default)]
pub struct Edges {
insert_bundle: SparseArray<BundleId, ArchetypeAfterBundleInsert>,
remove_bundle: SparseArray<BundleId, Option<ArchetypeId>>,
take_bundle: SparseArray<BundleId, Option<ArchetypeId>>,
}
impl Edges {
#[inline]
pub fn get_archetype_after_bundle_insert(&self, bundle_id: BundleId) -> Option<ArchetypeId> {
self.get_archetype_after_bundle_insert_internal(bundle_id)
.map(|bundle| bundle.archetype_id)
}
#[inline]
pub(crate) fn get_archetype_after_bundle_insert_internal(
&self,
bundle_id: BundleId,
) -> Option<&ArchetypeAfterBundleInsert> {
self.insert_bundle.get(bundle_id)
}
#[inline]
pub(crate) fn cache_archetype_after_bundle_insert(
&mut self,
bundle_id: BundleId,
archetype_id: ArchetypeId,
bundle_status: impl Into<Box<[ComponentStatus]>>,
required_components: impl Into<Box<[RequiredComponentConstructor]>>,
mut added: Vec<ComponentId>,
existing: Vec<ComponentId>,
) {
let added_len = added.len();
added.reserve_exact(existing.len());
added.extend(existing);
self.insert_bundle.insert(
bundle_id,
ArchetypeAfterBundleInsert {
archetype_id,
bundle_status: bundle_status.into(),
required_components: required_components.into(),
added_len,
inserted: added.into(),
},
);
}
#[inline]
pub fn get_archetype_after_bundle_remove(
&self,
bundle_id: BundleId,
) -> Option<Option<ArchetypeId>> {
self.remove_bundle.get(bundle_id).cloned()
}
#[inline]
pub(crate) fn cache_archetype_after_bundle_remove(
&mut self,
bundle_id: BundleId,
archetype_id: Option<ArchetypeId>,
) {
self.remove_bundle.insert(bundle_id, archetype_id);
}
#[inline]
pub fn get_archetype_after_bundle_take(
&self,
bundle_id: BundleId,
) -> Option<Option<ArchetypeId>> {
self.take_bundle.get(bundle_id).cloned()
}
#[inline]
pub(crate) fn cache_archetype_after_bundle_take(
&mut self,
bundle_id: BundleId,
archetype_id: Option<ArchetypeId>,
) {
self.take_bundle.insert(bundle_id, archetype_id);
}
}
pub struct ArchetypeEntity {
entity: Entity,
table_row: TableRow,
}
impl ArchetypeEntity {
#[inline]
pub const fn id(&self) -> Entity {
self.entity
}
#[inline]
pub const fn table_row(&self) -> TableRow {
self.table_row
}
}
pub(crate) struct ArchetypeSwapRemoveResult {
pub(crate) swapped_entity: Option<Entity>,
pub(crate) table_row: TableRow,
}
struct ArchetypeComponentInfo {
storage_type: StorageType,
}
bitflags::bitflags! {
#[derive(Clone, Copy)]
pub(crate) struct ArchetypeFlags: u32 {
const ON_ADD_HOOK = (1 << 0);
const ON_INSERT_HOOK = (1 << 1);
const ON_DISCARD_HOOK = (1 << 2);
const ON_REMOVE_HOOK = (1 << 3);
const ON_DESPAWN_HOOK = (1 << 4);
const ON_ADD_OBSERVER = (1 << 5);
const ON_INSERT_OBSERVER = (1 << 6);
const ON_DISCARD_OBSERVER = (1 << 7);
const ON_REMOVE_OBSERVER = (1 << 8);
const ON_DESPAWN_OBSERVER = (1 << 9);
}
}
pub struct Archetype {
id: ArchetypeId,
table_id: TableId,
edges: Edges,
entities: Vec<ArchetypeEntity>,
components: ImmutableSparseSet<ComponentId, ArchetypeComponentInfo>,
pub(crate) flags: ArchetypeFlags,
}
impl Archetype {
pub(crate) fn new(
components: &Components,
component_index: &mut ComponentIndex,
observers: &Observers,
id: ArchetypeId,
table_id: TableId,
table_components: impl Iterator<Item = ComponentId>,
sparse_set_components: impl Iterator<Item = ComponentId>,
) -> Self {
let (min_table, _) = table_components.size_hint();
let (min_sparse, _) = sparse_set_components.size_hint();
let mut flags = ArchetypeFlags::empty();
let mut archetype_components = SparseSet::with_capacity(min_table + min_sparse);
for (idx, component_id) in table_components.enumerate() {
let info = unsafe { components.get_info_unchecked(component_id) };
info.update_archetype_flags(&mut flags);
observers.update_archetype_flags(component_id, &mut flags);
archetype_components.insert(
component_id,
ArchetypeComponentInfo {
storage_type: StorageType::Table,
},
);
component_index
.entry(component_id)
.or_default()
.insert(id, ArchetypeRecord { column: Some(idx) });
}
for component_id in sparse_set_components {
let info = unsafe { components.get_info_unchecked(component_id) };
info.update_archetype_flags(&mut flags);
observers.update_archetype_flags(component_id, &mut flags);
archetype_components.insert(
component_id,
ArchetypeComponentInfo {
storage_type: StorageType::SparseSet,
},
);
component_index
.entry(component_id)
.or_default()
.insert(id, ArchetypeRecord { column: None });
}
Self {
id,
table_id,
entities: Vec::new(),
components: archetype_components.into_immutable(),
edges: Default::default(),
flags,
}
}
#[inline]
pub fn id(&self) -> ArchetypeId {
self.id
}
#[inline]
pub(crate) fn flags(&self) -> ArchetypeFlags {
self.flags
}
#[inline]
pub fn table_id(&self) -> TableId {
self.table_id
}
#[inline]
pub fn entities(&self) -> &[ArchetypeEntity] {
&self.entities
}
#[inline]
pub fn entities_with_location(&self) -> impl Iterator<Item = (Entity, EntityLocation)> {
self.entities.iter().enumerate().map(
|(archetype_row, &ArchetypeEntity { entity, table_row })| {
(
entity,
EntityLocation {
archetype_id: self.id,
archetype_row: unsafe {
ArchetypeRow::new(NonMaxU32::new_unchecked(archetype_row as u32))
},
table_id: self.table_id,
table_row,
},
)
},
)
}
#[inline]
pub fn table_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.components
.iter()
.filter(|(_, component)| component.storage_type == StorageType::Table)
.map(|(id, _)| *id)
}
#[inline]
pub fn sparse_set_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.components
.iter()
.filter(|(_, component)| component.storage_type == StorageType::SparseSet)
.map(|(id, _)| *id)
}
#[inline]
pub fn components(&self) -> &[ComponentId] {
self.components.indices()
}
#[inline]
pub fn iter_components(&self) -> impl Iterator<Item = ComponentId> + Clone {
self.components.indices().iter().copied()
}
#[inline]
pub fn component_count(&self) -> usize {
self.components.len()
}
#[inline]
pub fn edges(&self) -> &Edges {
&self.edges
}
#[inline]
pub(crate) fn edges_mut(&mut self) -> &mut Edges {
&mut self.edges
}
#[inline]
pub fn entity_table_row(&self, row: ArchetypeRow) -> TableRow {
self.entities[row.index()].table_row
}
#[inline]
pub(crate) fn set_entity_table_row(&mut self, row: ArchetypeRow, table_row: TableRow) {
self.entities[row.index()].table_row = table_row;
}
#[inline]
pub(crate) unsafe fn allocate(
&mut self,
entity: Entity,
table_row: TableRow,
) -> EntityLocation {
let archetype_row = unsafe { ArchetypeRow::new(NonMaxU32::new_unchecked(self.len())) };
self.entities.push(ArchetypeEntity { entity, table_row });
EntityLocation {
archetype_id: self.id,
archetype_row,
table_id: self.table_id,
table_row,
}
}
#[inline]
pub(crate) fn reserve(&mut self, additional: usize) {
self.entities.reserve(additional);
}
#[inline]
pub(crate) fn swap_remove(&mut self, row: ArchetypeRow) -> ArchetypeSwapRemoveResult {
let is_last = row.index() == self.entities.len() - 1;
let entity = self.entities.swap_remove(row.index());
ArchetypeSwapRemoveResult {
swapped_entity: if is_last {
None
} else {
Some(self.entities[row.index()].entity)
},
table_row: entity.table_row,
}
}
#[inline]
pub fn len(&self) -> u32 {
self.entities.len() as u32
}
#[inline]
pub fn is_empty(&self) -> bool {
self.entities.is_empty()
}
#[inline]
pub fn contains(&self, component_id: ComponentId) -> bool {
self.components.contains(component_id)
}
#[inline]
pub fn get_storage_type(&self, component_id: ComponentId) -> Option<StorageType> {
self.components
.get(component_id)
.map(|info| info.storage_type)
}
pub(crate) fn clear_entities(&mut self) {
self.entities.clear();
}
#[inline]
pub fn has_add_hook(&self) -> bool {
self.flags().contains(ArchetypeFlags::ON_ADD_HOOK)
}
#[inline]
pub fn has_insert_hook(&self) -> bool {
self.flags().contains(ArchetypeFlags::ON_INSERT_HOOK)
}
#[inline]
pub fn has_discard_hook(&self) -> bool {
self.flags().contains(ArchetypeFlags::ON_DISCARD_HOOK)
}
#[inline]
pub fn has_remove_hook(&self) -> bool {
self.flags().contains(ArchetypeFlags::ON_REMOVE_HOOK)
}
#[inline]
pub fn has_despawn_hook(&self) -> bool {
self.flags().contains(ArchetypeFlags::ON_DESPAWN_HOOK)
}
#[inline]
pub fn has_add_observer(&self) -> bool {
self.flags().contains(ArchetypeFlags::ON_ADD_OBSERVER)
}
#[inline]
pub fn has_insert_observer(&self) -> bool {
self.flags().contains(ArchetypeFlags::ON_INSERT_OBSERVER)
}
#[inline]
pub fn has_discard_observer(&self) -> bool {
self.flags().contains(ArchetypeFlags::ON_DISCARD_OBSERVER)
}
#[inline]
pub fn has_remove_observer(&self) -> bool {
self.flags().contains(ArchetypeFlags::ON_REMOVE_OBSERVER)
}
#[inline]
pub fn has_despawn_observer(&self) -> bool {
self.flags().contains(ArchetypeFlags::ON_DESPAWN_OBSERVER)
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ArchetypeGeneration(pub(crate) ArchetypeId);
impl ArchetypeGeneration {
#[inline]
pub const fn initial() -> Self {
ArchetypeGeneration(ArchetypeId::EMPTY)
}
}
#[derive(Hash, PartialEq, Eq)]
struct ArchetypeComponents {
table_components: Box<[ComponentId]>,
sparse_set_components: Box<[ComponentId]>,
}
pub type ComponentIndex = HashMap<ComponentId, HashMap<ArchetypeId, ArchetypeRecord>>;
pub struct Archetypes {
pub(crate) archetypes: Vec<Archetype>,
by_components: HashMap<ArchetypeComponents, ArchetypeId>,
pub(crate) by_component: ComponentIndex,
}
pub struct ArchetypeRecord {
#[expect(
dead_code,
reason = "Currently unused, but planned to be used to implement a component index to improve performance of fragmenting relations."
)]
pub(crate) column: Option<usize>,
}
impl Archetypes {
pub(crate) fn new() -> Self {
let mut archetypes = Archetypes {
archetypes: Vec::new(),
by_components: Default::default(),
by_component: Default::default(),
};
unsafe {
archetypes.get_id_or_insert(
&Components::default(),
&Observers::default(),
TableId::empty(),
Vec::new(),
Vec::new(),
);
}
archetypes
}
#[inline]
pub fn generation(&self) -> ArchetypeGeneration {
let id = ArchetypeId::new(self.archetypes.len());
ArchetypeGeneration(id)
}
#[inline]
#[expect(
clippy::len_without_is_empty,
reason = "The internal vec is never empty"
)]
pub fn len(&self) -> usize {
self.archetypes.len()
}
#[inline]
pub fn empty(&self) -> &Archetype {
unsafe { self.archetypes.get_unchecked(ArchetypeId::EMPTY.index()) }
}
#[inline]
pub(crate) fn empty_mut(&mut self) -> &mut Archetype {
unsafe {
self.archetypes
.get_unchecked_mut(ArchetypeId::EMPTY.index())
}
}
#[inline]
pub fn get(&self, id: ArchetypeId) -> Option<&Archetype> {
self.archetypes.get(id.index())
}
#[inline]
pub(crate) unsafe fn get_maybe_disjoint_mut(
&mut self,
id_a: ArchetypeId,
id_b: ArchetypeId,
) -> (&mut Archetype, Option<&mut Archetype>) {
if id_a == id_b {
let archetype_a = unsafe { self.archetypes.get_unchecked_mut(id_a.index()) };
(archetype_a, None)
} else {
let [archetype_a, archetype_b] = unsafe {
self.archetypes
.get_disjoint_unchecked_mut([id_a.index(), id_b.index()])
};
(archetype_a, Some(archetype_b))
}
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = &Archetype> {
self.archetypes.iter()
}
pub(crate) unsafe fn get_id_or_insert(
&mut self,
components: &Components,
observers: &Observers,
table_id: TableId,
table_components: Vec<ComponentId>,
sparse_set_components: Vec<ComponentId>,
) -> (ArchetypeId, bool) {
let archetype_identity = ArchetypeComponents {
sparse_set_components: sparse_set_components.into_boxed_slice(),
table_components: table_components.into_boxed_slice(),
};
let archetypes = &mut self.archetypes;
let component_index = &mut self.by_component;
match self.by_components.entry(archetype_identity) {
Entry::Occupied(occupied) => (*occupied.get(), false),
Entry::Vacant(vacant) => {
let ArchetypeComponents {
table_components,
sparse_set_components,
} = vacant.key();
let id = ArchetypeId::new(archetypes.len());
archetypes.push(Archetype::new(
components,
component_index,
observers,
id,
table_id,
table_components.iter().copied(),
sparse_set_components.iter().copied(),
));
vacant.insert(id);
(id, true)
}
}
}
pub(crate) fn clear_entities(&mut self) {
for archetype in &mut self.archetypes {
archetype.clear_entities();
}
}
pub fn component_index(&self) -> &ComponentIndex {
&self.by_component
}
pub(crate) fn update_flags(
&mut self,
component_id: ComponentId,
flags: ArchetypeFlags,
set: bool,
) {
if let Some(archetypes) = self.by_component.get(&component_id) {
for archetype_id in archetypes.keys() {
self.archetypes
.get_mut(archetype_id.index())
.unwrap()
.flags
.set(flags, set);
}
}
}
}
impl Index<RangeFrom<ArchetypeGeneration>> for Archetypes {
type Output = [Archetype];
#[inline]
fn index(&self, index: RangeFrom<ArchetypeGeneration>) -> &Self::Output {
&self.archetypes[index.start.0.index()..]
}
}
impl Index<ArchetypeId> for Archetypes {
type Output = Archetype;
#[inline]
fn index(&self, index: ArchetypeId) -> &Self::Output {
&self.archetypes[index.index()]
}
}
impl IndexMut<ArchetypeId> for Archetypes {
#[inline]
fn index_mut(&mut self, index: ArchetypeId) -> &mut Self::Output {
&mut self.archetypes[index.index()]
}
}