use crate::{
archetype::{Archetype, ArchetypeId, Archetypes, ComponentStatus},
bundle::{Bundle, BundleInfo},
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
entity::{Entity, EntityLocation},
storage::{SparseSet, Storages},
world::{Mut, World},
};
use std::any::TypeId;
pub struct EntityRef<'w> {
world: &'w World,
entity: Entity,
location: EntityLocation,
}
impl<'w> EntityRef<'w> {
#[inline]
pub(crate) fn new(world: &'w World, entity: Entity, location: EntityLocation) -> Self {
Self {
world,
entity,
location,
}
}
#[inline]
pub fn id(&self) -> Entity {
self.entity
}
#[inline]
pub fn location(&self) -> EntityLocation {
self.location
}
#[inline]
pub fn archetype(&self) -> &Archetype {
&self.world.archetypes[self.location.archetype_id]
}
#[inline]
pub fn world(&mut self) -> &World {
self.world
}
#[inline]
pub fn contains<T: Component>(&self) -> bool {
self.contains_type_id(TypeId::of::<T>())
}
#[inline]
pub fn contains_id(&self, component_id: ComponentId) -> bool {
contains_component_with_id(self.world, component_id, self.location)
}
#[inline]
pub fn contains_type_id(&self, type_id: TypeId) -> bool {
contains_component_with_type(self.world, type_id, self.location)
}
#[inline]
pub fn get<T: Component>(&self) -> Option<&'w T> {
unsafe {
get_component_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
.map(|value| &*value.cast::<T>())
}
}
#[inline]
pub unsafe fn get_unchecked_mut<T: Component>(
&self,
last_change_tick: u32,
change_tick: u32,
) -> Option<Mut<'w, T>> {
get_component_and_ticks_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
.map(|(value, ticks)| Mut {
value: &mut *value.cast::<T>(),
component_ticks: &mut *ticks,
last_change_tick,
change_tick,
})
}
}
pub struct EntityMut<'w> {
world: &'w mut World,
entity: Entity,
location: EntityLocation,
}
impl<'w> EntityMut<'w> {
#[inline]
pub(crate) unsafe fn new(
world: &'w mut World,
entity: Entity,
location: EntityLocation,
) -> Self {
EntityMut {
world,
entity,
location,
}
}
#[inline]
pub fn id(&self) -> Entity {
self.entity
}
#[inline]
pub fn location(&self) -> EntityLocation {
self.location
}
#[inline]
pub fn archetype(&self) -> &Archetype {
&self.world.archetypes[self.location.archetype_id]
}
#[inline]
pub fn contains<T: Component>(&self) -> bool {
self.contains_type_id(TypeId::of::<T>())
}
#[inline]
pub fn contains_id(&self, component_id: ComponentId) -> bool {
contains_component_with_id(self.world, component_id, self.location)
}
#[inline]
pub fn contains_type_id(&self, type_id: TypeId) -> bool {
contains_component_with_type(self.world, type_id, self.location)
}
#[inline]
pub fn get<T: Component>(&self) -> Option<&'w T> {
unsafe {
get_component_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
.map(|value| &*value.cast::<T>())
}
}
#[inline]
pub fn get_mut<T: Component>(&mut self) -> Option<Mut<'w, T>> {
unsafe {
get_component_and_ticks_with_type(
self.world,
TypeId::of::<T>(),
self.entity,
self.location,
)
.map(|(value, ticks)| Mut {
value: &mut *value.cast::<T>(),
component_ticks: &mut *ticks,
last_change_tick: self.world.last_change_tick(),
change_tick: self.world.change_tick(),
})
}
}
#[inline]
pub unsafe fn get_unchecked_mut<T: Component>(&self) -> Option<Mut<'w, T>> {
get_component_and_ticks_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
.map(|(value, ticks)| Mut {
value: &mut *value.cast::<T>(),
component_ticks: &mut *ticks,
last_change_tick: self.world.last_change_tick(),
change_tick: self.world.read_change_tick(),
})
}
pub fn insert_bundle<T: Bundle>(&mut self, bundle: T) -> &mut Self {
let entity = self.entity;
let change_tick = self.world.change_tick();
let entities = &mut self.world.entities;
let archetypes = &mut self.world.archetypes;
let components = &mut self.world.components;
let storages = &mut self.world.storages;
let bundle_info = self.world.bundles.init_info::<T>(components);
let current_location = self.location;
let (archetype, bundle_status, archetype_index) = unsafe {
let new_archetype_id = add_bundle_to_archetype(
archetypes,
storages,
components,
self.location.archetype_id,
bundle_info,
);
if new_archetype_id == current_location.archetype_id {
let archetype = &archetypes[current_location.archetype_id];
let edge = archetype.edges().get_add_bundle(bundle_info.id).unwrap();
(archetype, &edge.bundle_status, current_location.index)
} else {
let (old_table_row, old_table_id) = {
let old_archetype = &mut archetypes[current_location.archetype_id];
let result = old_archetype.swap_remove(current_location.index);
if let Some(swapped_entity) = result.swapped_entity {
entities.meta[swapped_entity.id as usize].location = current_location;
}
(result.table_row, old_archetype.table_id())
};
let new_table_id = archetypes[new_archetype_id].table_id();
let new_location = if old_table_id == new_table_id {
archetypes[new_archetype_id].allocate(entity, old_table_row)
} else {
let (old_table, new_table) =
storages.tables.get_2_mut(old_table_id, new_table_id);
let move_result =
old_table.move_to_superset_unchecked(old_table_row, new_table);
let new_location =
archetypes[new_archetype_id].allocate(entity, move_result.new_row);
if let Some(swapped_entity) = move_result.swapped_entity {
let swapped_location = entities.get(swapped_entity).unwrap();
archetypes[swapped_location.archetype_id]
.set_entity_table_row(swapped_location.index, old_table_row);
}
new_location
};
self.location = new_location;
entities.meta[self.entity.id as usize].location = new_location;
let (old_archetype, new_archetype) =
archetypes.get_2_mut(current_location.archetype_id, new_archetype_id);
let edge = old_archetype
.edges()
.get_add_bundle(bundle_info.id)
.unwrap();
(&*new_archetype, &edge.bundle_status, new_location.index)
}
};
let table = &storages.tables[archetype.table_id()];
let table_row = archetype.entity_table_row(archetype_index);
unsafe {
bundle_info.write_components(
&mut storages.sparse_sets,
entity,
table,
table_row,
bundle_status,
bundle,
change_tick,
)
};
self
}
pub fn remove_bundle<T: Bundle>(&mut self) -> Option<T> {
let archetypes = &mut self.world.archetypes;
let storages = &mut self.world.storages;
let components = &mut self.world.components;
let entities = &mut self.world.entities;
let removed_components = &mut self.world.removed_components;
let bundle_info = self.world.bundles.init_info::<T>(components);
let old_location = self.location;
let new_archetype_id = unsafe {
remove_bundle_from_archetype(
archetypes,
storages,
components,
old_location.archetype_id,
bundle_info,
false,
)?
};
if new_archetype_id == old_location.archetype_id {
return None;
}
let old_archetype = &mut archetypes[old_location.archetype_id];
let mut bundle_components = bundle_info.component_ids.iter().cloned();
let entity = self.entity;
let result = unsafe {
T::from_components(|| {
let component_id = bundle_components.next().unwrap();
remove_component(
components,
storages,
old_archetype,
removed_components,
component_id,
entity,
old_location,
)
})
};
let remove_result = old_archetype.swap_remove(old_location.index);
if let Some(swapped_entity) = remove_result.swapped_entity {
entities.meta[swapped_entity.id as usize].location = old_location;
}
let old_table_row = remove_result.table_row;
let old_table_id = old_archetype.table_id();
let new_archetype = &mut archetypes[new_archetype_id];
let new_location = if old_table_id == new_archetype.table_id() {
unsafe { new_archetype.allocate(entity, old_table_row) }
} else {
let (old_table, new_table) = storages
.tables
.get_2_mut(old_table_id, new_archetype.table_id());
let move_result =
unsafe { old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table) };
let new_location = unsafe { new_archetype.allocate(entity, move_result.new_row) };
if let Some(swapped_entity) = move_result.swapped_entity {
let swapped_location = entities.get(swapped_entity).unwrap();
let archetype = &mut archetypes[swapped_location.archetype_id];
archetype.set_entity_table_row(swapped_location.index, old_table_row);
}
new_location
};
self.location = new_location;
entities.meta[self.entity.id as usize].location = new_location;
Some(result)
}
pub fn remove_bundle_intersection<T: Bundle>(&mut self) {
let archetypes = &mut self.world.archetypes;
let storages = &mut self.world.storages;
let components = &mut self.world.components;
let entities = &mut self.world.entities;
let removed_components = &mut self.world.removed_components;
let bundle_info = self.world.bundles.init_info::<T>(components);
let old_location = self.location;
let new_archetype_id = unsafe {
remove_bundle_from_archetype(
archetypes,
storages,
components,
old_location.archetype_id,
bundle_info,
true,
)
.expect("intersections should always return a result")
};
if new_archetype_id == old_location.archetype_id {
return;
}
let old_archetype = &mut archetypes[old_location.archetype_id];
let entity = self.entity;
for component_id in bundle_info.component_ids.iter().cloned() {
if old_archetype.contains(component_id) {
unsafe {
remove_component(
components,
storages,
old_archetype,
removed_components,
component_id,
entity,
old_location,
);
}
}
}
let remove_result = old_archetype.swap_remove(old_location.index);
if let Some(swapped_entity) = remove_result.swapped_entity {
entities.meta[swapped_entity.id as usize].location = old_location;
}
let old_table_row = remove_result.table_row;
let old_table_id = old_archetype.table_id();
let new_archetype = &mut archetypes[new_archetype_id];
let new_location = if old_table_id == new_archetype.table_id() {
unsafe { new_archetype.allocate(entity, old_table_row) }
} else {
let (old_table, new_table) = storages
.tables
.get_2_mut(old_table_id, new_archetype.table_id());
let move_result =
unsafe { old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table) };
let new_location = unsafe { new_archetype.allocate(entity, move_result.new_row) };
if let Some(swapped_entity) = move_result.swapped_entity {
let swapped_location = entities.get(swapped_entity).unwrap();
archetypes[swapped_location.archetype_id]
.set_entity_table_row(swapped_location.index, old_table_row);
}
new_location
};
self.location = new_location;
entities.meta[self.entity.id as usize].location = new_location;
}
pub fn insert<T: Component>(&mut self, value: T) -> &mut Self {
self.insert_bundle((value,))
}
pub fn remove<T: Component>(&mut self) -> Option<T> {
self.remove_bundle::<(T,)>().map(|v| v.0)
}
pub fn despawn(self) {
let world = self.world;
world.flush();
let location = world
.entities
.free(self.entity)
.expect("entity should exist at this point.");
let table_row;
let moved_entity;
{
let archetype = &mut world.archetypes[location.archetype_id];
for component_id in archetype.components() {
let removed_components = world
.removed_components
.get_or_insert_with(component_id, Vec::new);
removed_components.push(self.entity);
}
let remove_result = archetype.swap_remove(location.index);
if let Some(swapped_entity) = remove_result.swapped_entity {
world.entities.meta[swapped_entity.id as usize].location = location;
}
table_row = remove_result.table_row;
for component_id in archetype.sparse_set_components() {
let sparse_set = world.storages.sparse_sets.get_mut(*component_id).unwrap();
sparse_set.remove(self.entity);
}
moved_entity = unsafe {
world.storages.tables[archetype.table_id()].swap_remove_unchecked(table_row)
};
};
if let Some(moved_entity) = moved_entity {
let moved_location = world.entities.get(moved_entity).unwrap();
world.archetypes[moved_location.archetype_id]
.set_entity_table_row(moved_location.index, table_row);
}
}
#[inline]
pub fn world(&mut self) -> &World {
self.world
}
#[inline]
pub unsafe fn world_mut(&mut self) -> &mut World {
self.world
}
pub fn update_location(&mut self) {
self.location = self.world.entities().get(self.entity).unwrap();
}
}
#[inline]
unsafe fn get_component(
world: &World,
component_id: ComponentId,
entity: Entity,
location: EntityLocation,
) -> Option<*mut u8> {
let archetype = &world.archetypes[location.archetype_id];
let component_info = world.components.get_info_unchecked(component_id);
match component_info.storage_type() {
StorageType::Table => {
let table = &world.storages.tables[archetype.table_id()];
let components = table.get_column(component_id)?;
let table_row = archetype.entity_table_row(location.index);
Some(components.get_unchecked(table_row))
}
StorageType::SparseSet => world
.storages
.sparse_sets
.get(component_id)
.and_then(|sparse_set| sparse_set.get(entity)),
}
}
#[inline]
unsafe fn get_component_and_ticks(
world: &World,
component_id: ComponentId,
entity: Entity,
location: EntityLocation,
) -> Option<(*mut u8, *mut ComponentTicks)> {
let archetype = &world.archetypes[location.archetype_id];
let component_info = world.components.get_info_unchecked(component_id);
match component_info.storage_type() {
StorageType::Table => {
let table = &world.storages.tables[archetype.table_id()];
let components = table.get_column(component_id)?;
let table_row = archetype.entity_table_row(location.index);
Some((
components.get_unchecked(table_row),
components.get_ticks_unchecked(table_row),
))
}
StorageType::SparseSet => world
.storages
.sparse_sets
.get(component_id)
.and_then(|sparse_set| sparse_set.get_with_ticks(entity)),
}
}
#[inline]
unsafe fn remove_component(
components: &Components,
storages: &mut Storages,
archetype: &Archetype,
removed_components: &mut SparseSet<ComponentId, Vec<Entity>>,
component_id: ComponentId,
entity: Entity,
location: EntityLocation,
) -> *mut u8 {
let component_info = components.get_info_unchecked(component_id);
let removed_components = removed_components.get_or_insert_with(component_id, Vec::new);
removed_components.push(entity);
match component_info.storage_type() {
StorageType::Table => {
let table = &storages.tables[archetype.table_id()];
let components = table.get_column(component_id).unwrap();
let table_row = archetype.entity_table_row(location.index);
components.get_unchecked(table_row)
}
StorageType::SparseSet => storages
.sparse_sets
.get_mut(component_id)
.unwrap()
.remove_and_forget(entity)
.unwrap(),
}
}
unsafe fn get_component_with_type(
world: &World,
type_id: TypeId,
entity: Entity,
location: EntityLocation,
) -> Option<*mut u8> {
let component_id = world.components.get_id(type_id)?;
get_component(world, component_id, entity, location)
}
pub(crate) unsafe fn get_component_and_ticks_with_type(
world: &World,
type_id: TypeId,
entity: Entity,
location: EntityLocation,
) -> Option<(*mut u8, *mut ComponentTicks)> {
let component_id = world.components.get_id(type_id)?;
get_component_and_ticks(world, component_id, entity, location)
}
fn contains_component_with_type(world: &World, type_id: TypeId, location: EntityLocation) -> bool {
if let Some(component_id) = world.components.get_id(type_id) {
contains_component_with_id(world, component_id, location)
} else {
false
}
}
fn contains_component_with_id(
world: &World,
component_id: ComponentId,
location: EntityLocation,
) -> bool {
world.archetypes[location.archetype_id].contains(component_id)
}
pub(crate) unsafe fn add_bundle_to_archetype(
archetypes: &mut Archetypes,
storages: &mut Storages,
components: &mut Components,
archetype_id: ArchetypeId,
bundle_info: &BundleInfo,
) -> ArchetypeId {
if let Some(add_bundle) = archetypes[archetype_id]
.edges()
.get_add_bundle(bundle_info.id)
{
return add_bundle.archetype_id;
}
let mut new_table_components = Vec::new();
let mut new_sparse_set_components = Vec::new();
let mut bundle_status = Vec::with_capacity(bundle_info.component_ids.len());
let current_archetype = &mut archetypes[archetype_id];
for component_id in bundle_info.component_ids.iter().cloned() {
if current_archetype.contains(component_id) {
bundle_status.push(ComponentStatus::Mutated);
} else {
bundle_status.push(ComponentStatus::Added);
let component_info = components.get_info_unchecked(component_id);
match component_info.storage_type() {
StorageType::Table => new_table_components.push(component_id),
StorageType::SparseSet => {
storages.sparse_sets.get_or_insert(component_info);
new_sparse_set_components.push(component_id)
}
}
}
}
if new_table_components.is_empty() && new_sparse_set_components.is_empty() {
let edges = current_archetype.edges_mut();
edges.set_add_bundle(bundle_info.id, archetype_id, bundle_status);
archetype_id
} else {
let table_id;
let table_components;
let sparse_set_components;
{
let current_archetype = &archetypes[archetype_id];
table_components = if new_table_components.is_empty() {
table_id = current_archetype.table_id();
current_archetype.table_components().to_vec()
} else {
new_table_components.extend(current_archetype.table_components());
new_table_components.sort();
table_id = storages
.tables
.get_id_or_insert(&new_table_components, components);
new_table_components
};
sparse_set_components = if new_sparse_set_components.is_empty() {
current_archetype.sparse_set_components().to_vec()
} else {
new_sparse_set_components.extend(current_archetype.sparse_set_components());
new_sparse_set_components.sort();
new_sparse_set_components
};
};
let new_archetype_id =
archetypes.get_id_or_insert(table_id, table_components, sparse_set_components);
archetypes[archetype_id].edges_mut().set_add_bundle(
bundle_info.id,
new_archetype_id,
bundle_status,
);
new_archetype_id
}
}
unsafe fn remove_bundle_from_archetype(
archetypes: &mut Archetypes,
storages: &mut Storages,
components: &mut Components,
archetype_id: ArchetypeId,
bundle_info: &BundleInfo,
intersection: bool,
) -> Option<ArchetypeId> {
let remove_bundle_result = {
let current_archetype = &mut archetypes[archetype_id];
if intersection {
current_archetype
.edges()
.get_remove_bundle_intersection(bundle_info.id)
} else {
current_archetype.edges().get_remove_bundle(bundle_info.id)
}
};
let result = if let Some(result) = remove_bundle_result {
result
} else {
let mut next_table_components;
let mut next_sparse_set_components;
let next_table_id;
{
let current_archetype = &mut archetypes[archetype_id];
let mut removed_table_components = Vec::new();
let mut removed_sparse_set_components = Vec::new();
for component_id in bundle_info.component_ids.iter().cloned() {
if current_archetype.contains(component_id) {
let component_info = components.get_info_unchecked(component_id);
match component_info.storage_type() {
StorageType::Table => removed_table_components.push(component_id),
StorageType::SparseSet => removed_sparse_set_components.push(component_id),
}
} else if !intersection {
current_archetype
.edges_mut()
.set_remove_bundle(bundle_info.id, None);
return None;
}
}
removed_table_components.sort();
removed_sparse_set_components.sort();
next_table_components = current_archetype.table_components().to_vec();
next_sparse_set_components = current_archetype.sparse_set_components().to_vec();
sorted_remove(&mut next_table_components, &removed_table_components);
sorted_remove(
&mut next_sparse_set_components,
&removed_sparse_set_components,
);
next_table_id = if removed_table_components.is_empty() {
current_archetype.table_id()
} else {
storages
.tables
.get_id_or_insert(&next_table_components, components)
};
}
let new_archetype_id = archetypes.get_id_or_insert(
next_table_id,
next_table_components,
next_sparse_set_components,
);
Some(new_archetype_id)
};
let current_archetype = &mut archetypes[archetype_id];
if intersection {
current_archetype
.edges_mut()
.set_remove_bundle_intersection(bundle_info.id, result);
} else {
current_archetype
.edges_mut()
.set_remove_bundle(bundle_info.id, result);
}
result
}
fn sorted_remove<T: Eq + Ord + Copy>(source: &mut Vec<T>, remove: &[T]) {
let mut remove_index = 0;
source.retain(|value| {
while remove_index < remove.len() && *value > remove[remove_index] {
remove_index += 1;
}
if remove_index < remove.len() {
*value != remove[remove_index]
} else {
true
}
})
}
#[cfg(test)]
mod tests {
#[test]
fn sorted_remove() {
let mut a = vec![1, 2, 3, 4, 5, 6, 7];
let b = vec![1, 2, 3, 5, 7];
super::sorted_remove(&mut a, &b);
assert_eq!(a, vec![4, 6]);
let mut a = vec![1];
let b = vec![1];
super::sorted_remove(&mut a, &b);
assert_eq!(a, vec![]);
let mut a = vec![1];
let b = vec![2];
super::sorted_remove(&mut a, &b);
assert_eq!(a, vec![1]);
}
}