use std::{
collections::HashMap,
ops::Range,
sync::atomic::{AtomicU64, Ordering},
};
use bit_set::BitSet;
use itertools::Itertools;
use super::{
entity::{Allocate, Entity, EntityHasher, EntityLocation, LocationMap, ID_CLONE_MAPPINGS},
entry::{Entry, EntryMut, EntryRef},
event::{EventSender, Subscriber, Subscribers},
insert::{ArchetypeSource, ArchetypeWriter, ComponentSource, IntoComponentSource},
query::{
filter::{EntityFilter, LayoutFilter},
view::{IntoView, View},
Query,
},
storage::{
archetype::{Archetype, ArchetypeIndex, EntityLayout},
component::{Component, ComponentTypeId},
group::{Group, GroupDef},
index::SearchIndex,
ComponentIndex, Components, PackOptions, UnknownComponentStorage,
},
subworld::{ComponentAccess, SubWorld},
};
type MapEntry<'a, K, V> = std::collections::hash_map::Entry<'a, K, V>;
#[derive(thiserror::Error, Debug, Eq, PartialEq, Hash)]
pub enum EntityAccessError {
#[error("this world does not have permission to access the entity")]
AccessDenied,
#[error("the entity does not exist")]
EntityNotFound,
}
pub trait EntityStore {
fn id(&self) -> WorldId;
fn entry_ref(&self, entity: Entity) -> Result<EntryRef, EntityAccessError>;
fn entry_mut(&mut self, entity: Entity) -> Result<EntryMut, EntityAccessError>;
fn get_component_storage<V: for<'b> View<'b>>(
&self,
) -> Result<StorageAccessor, EntityAccessError>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WorldId(u64);
static WORLD_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
impl WorldId {
fn next() -> Self {
WorldId(WORLD_ID_COUNTER.fetch_add(1, Ordering::Relaxed))
}
}
impl Default for WorldId {
fn default() -> Self {
Self::next()
}
}
#[derive(Default)]
pub struct WorldOptions {
pub groups: Vec<GroupDef>,
}
#[derive(Debug)]
pub struct World {
id: WorldId,
index: SearchIndex,
components: Components,
groups: Vec<Group>,
group_members: HashMap<ComponentTypeId, usize>,
archetypes: Vec<Archetype>,
entities: LocationMap,
allocation_buffer: Vec<Entity>,
subscribers: Subscribers,
}
impl Default for World {
fn default() -> Self {
Self::new(WorldOptions::default())
}
}
impl World {
pub fn new(options: WorldOptions) -> Self {
let groups: Vec<Group> = options.groups.into_iter().map(|def| def.into()).collect();
let mut group_members = HashMap::default();
for (i, group) in groups.iter().enumerate() {
for comp in group.components() {
match group_members.entry(comp) {
MapEntry::Vacant(entry) => {
entry.insert(i);
}
MapEntry::Occupied(_) => {
panic!("components can only belong to a single group");
}
}
}
}
Self {
id: WorldId::next(),
index: SearchIndex::default(),
components: Components::default(),
groups,
group_members,
archetypes: Vec::default(),
entities: LocationMap::default(),
allocation_buffer: Vec::default(),
subscribers: Subscribers::default(),
}
}
pub fn id(&self) -> WorldId {
self.id
}
pub fn len(&self) -> usize {
self.entities.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn contains(&self, entity: Entity) -> bool {
self.entities.contains(entity)
}
pub fn push_with_id<T>(&mut self, entity_id: Entity, components: T)
where
Option<T>: IntoComponentSource,
{
self.remove(entity_id);
let mut components = <Option<T> as IntoComponentSource>::into(Some(components));
let arch_index = self.get_archetype_for_components(&mut components);
let archetype = &mut self.archetypes[arch_index.0 as usize];
let mut writer =
ArchetypeWriter::new(arch_index, archetype, self.components.get_multi_mut());
components.push_components(&mut writer, std::iter::once(entity_id));
let (base, entities) = writer.inserted();
self.entities.insert(entities, arch_index, base);
}
pub fn push<T>(&mut self, components: T) -> Entity
where
Option<T>: IntoComponentSource,
{
self.extend(Some(components))[0]
}
pub fn extend(&mut self, components: impl IntoComponentSource) -> &[Entity] {
let replaced = {
let mut components = components.into();
let arch_index = self.get_archetype_for_components(&mut components);
let archetype = &mut self.archetypes[arch_index.0 as usize];
let mut writer =
ArchetypeWriter::new(arch_index, archetype, self.components.get_multi_mut());
components.push_components(&mut writer, Allocate::new());
let (base, entities) = writer.inserted();
self.allocation_buffer.clear();
self.allocation_buffer.extend_from_slice(entities);
self.entities.insert(entities, arch_index, base)
};
for location in replaced {
self.remove_at_location(location);
}
&self.allocation_buffer
}
pub fn remove(&mut self, entity: Entity) -> bool {
let location = self.entities.remove(entity);
if let Some(location) = location {
self.remove_at_location(location);
true
} else {
false
}
}
fn remove_at_location(&mut self, location: EntityLocation) {
let EntityLocation(arch_index, component_index) = location;
let archetype = &mut self.archetypes[arch_index];
archetype.swap_remove(component_index.0);
for type_id in archetype.layout().component_types() {
let storage = self.components.get_mut(*type_id).unwrap();
storage.swap_remove(arch_index, component_index);
}
if component_index.0 < archetype.entities().len() {
let swapped = archetype.entities()[component_index.0];
self.entities.set(swapped, location);
}
}
pub fn clear(&mut self) {
use crate::internals::query::IntoQuery;
let mut all = Entity::query();
let entities = all.iter(self).copied().collect::<Vec<_>>();
for entity in entities {
self.remove(entity);
}
}
pub fn entry(&mut self, entity: Entity) -> Option<Entry> {
self.entities
.get(entity)
.map(move |location| Entry::new(location, self))
}
pub(crate) unsafe fn entry_unchecked(
&self,
entity: Entity,
) -> Result<EntryMut, EntityAccessError> {
self.entities
.get(entity)
.map(|location| {
EntryMut::new(
location,
&self.components,
&self.archetypes,
ComponentAccess::All,
)
})
.ok_or(EntityAccessError::EntityNotFound)
}
pub fn subscribe<T, S>(&mut self, sender: S, filter: T)
where
T: LayoutFilter + Send + Sync + 'static,
S: EventSender + 'static,
{
let subscriber = Subscriber::new(filter, sender);
for arch in &mut self.archetypes {
if subscriber.is_interested(arch) {
arch.subscribe(subscriber.clone());
}
}
self.subscribers.push(subscriber);
}
pub fn pack(&mut self, options: PackOptions) {
self.components.pack(&options);
}
pub fn components(&self) -> &Components {
&self.components
}
pub(crate) fn components_mut(&mut self) -> &mut Components {
&mut self.components
}
pub(crate) fn archetypes(&self) -> &[Archetype] {
&self.archetypes
}
pub(crate) unsafe fn transfer_archetype(
&mut self,
ArchetypeIndex(from): ArchetypeIndex,
ArchetypeIndex(to): ArchetypeIndex,
ComponentIndex(idx): ComponentIndex,
) -> ComponentIndex {
if from == to {
return ComponentIndex(idx);
}
let (from_arch, to_arch) = if from < to {
let (a, b) = self.archetypes.split_at_mut(to as usize);
(&mut a[from as usize], &mut b[0])
} else {
let (a, b) = self.archetypes.split_at_mut(from as usize);
(&mut b[0], &mut a[to as usize])
};
let entity = from_arch.swap_remove(idx);
to_arch.push(entity);
self.entities.set(
entity,
EntityLocation::new(
ArchetypeIndex(to),
ComponentIndex(to_arch.entities().len() - 1),
),
);
if from_arch.entities().len() > idx {
let moved = from_arch.entities()[idx];
self.entities.set(
moved,
EntityLocation::new(ArchetypeIndex(from), ComponentIndex(idx)),
);
}
let from_layout = from_arch.layout();
let to_layout = to_arch.layout();
for type_id in from_layout.component_types() {
let storage = self.components.get_mut(*type_id).unwrap();
if to_layout.component_types().contains(type_id) {
storage.move_component(
ArchetypeIndex(from),
ComponentIndex(idx),
ArchetypeIndex(to),
);
} else {
storage.swap_remove(ArchetypeIndex(from), ComponentIndex(idx));
}
}
ComponentIndex(to_arch.entities().len() - 1)
}
pub(crate) fn get_archetype_for_components<T: ArchetypeSource>(
&mut self,
components: &mut T,
) -> ArchetypeIndex {
let index = self.index.search(&components.filter()).next();
if let Some(index) = index {
index
} else {
self.insert_archetype(components.layout())
}
}
fn insert_archetype(&mut self, layout: EntityLayout) -> ArchetypeIndex {
self.index.push(&layout);
let arch_index = ArchetypeIndex(self.archetypes.len() as u32);
let subscribers = self.subscribers.matches_layout(layout.component_types());
self.archetypes
.push(Archetype::new(arch_index, layout, subscribers));
let archetype = &self.archetypes[self.archetypes.len() - 1];
let groups = &mut self.groups;
let group_members = &mut self.group_members;
let types_by_group = archetype
.layout()
.component_types()
.iter()
.map(|type_id| {
(
match group_members.entry(*type_id) {
MapEntry::Occupied(entry) => *entry.get(),
MapEntry::Vacant(entry) => {
let mut group = GroupDef::new();
group.add(*type_id);
groups.push(group.into());
*entry.insert(groups.len() - 1)
}
},
*type_id,
)
})
.into_group_map();
for (group_index, component_types) in types_by_group.iter() {
let group = &mut self.groups[*group_index];
let index = group.try_insert(arch_index, archetype);
for type_id in component_types {
let storage = self.components.get_or_insert_with(*type_id, || {
let index = archetype
.layout()
.component_types()
.iter()
.position(|t| t == type_id)
.unwrap();
archetype.layout().component_constructors()[index]()
});
storage.insert_archetype(arch_index, index);
}
}
arch_index
}
pub fn split<T: IntoView>(&mut self) -> (SubWorld, SubWorld) {
let permissions = T::View::requires_permissions();
let (left, right) = ComponentAccess::All.split(permissions);
unsafe {
(
SubWorld::new_unchecked(self, left, None),
SubWorld::new_unchecked(self, right, None),
)
}
}
pub fn split_for_query<'q, V: IntoView, F: EntityFilter>(
&mut self,
_: &'q Query<V, F>,
) -> (SubWorld, SubWorld) {
self.split::<V>()
}
pub fn move_from<F: LayoutFilter>(&mut self, source: &mut World, filter: &F) {
for src_arch in source.archetypes.iter_mut().filter(|arch| {
filter
.matches_layout(arch.layout().component_types())
.is_pass()
}) {
for src_entity in src_arch.entities() {
self.remove(*src_entity);
}
let layout = &**src_arch.layout();
let dst_arch_index = if src_arch.entities().len() < 32 {
self.index.search(layout).next()
} else {
None
};
let dst_arch_index =
dst_arch_index.unwrap_or_else(|| self.insert_archetype(layout.clone()));
let dst_arch = &mut self.archetypes[dst_arch_index.0 as usize];
let mut writer =
ArchetypeWriter::new(dst_arch_index, dst_arch, self.components.get_multi_mut());
for entity in src_arch.entities() {
writer.push(*entity);
}
for component in src_arch.layout().component_types() {
let src_storage = source.components.get_mut(*component).unwrap();
let mut dst_storage = writer.claim_components_unknown(*component);
dst_storage.move_archetype_from(src_arch.index(), src_storage);
}
for entity in src_arch.drain() {
source.entities.remove(entity);
}
let (base, entities) = writer.inserted();
self.entities.insert(entities, dst_arch_index, base);
}
}
pub fn clone_from<F: LayoutFilter, M: Merger>(
&mut self,
source: &World,
filter: &F,
merger: &mut M,
) -> HashMap<Entity, Entity, EntityHasher> {
let mut allocator = Allocate::new();
let mut reallocated = HashMap::default();
for src_arch in source.archetypes.iter().filter(|arch| {
filter
.matches_layout(arch.layout().component_types())
.is_pass()
}) {
for src_entity in src_arch.entities() {
let dst_entity = merger.assign_id(*src_entity, &mut allocator);
self.remove(dst_entity);
reallocated.insert(*src_entity, dst_entity);
}
}
let mut reallocated = Some(reallocated);
let mut mappings = match merger.entity_map() {
EntityRewrite::Auto(Some(mut overrides)) => {
for (a, b) in reallocated.as_ref().unwrap().iter() {
overrides.entry(*a).or_insert(*b);
}
overrides
}
EntityRewrite::Auto(None) => reallocated.take().unwrap(),
EntityRewrite::Explicit(overrides) => overrides,
};
ID_CLONE_MAPPINGS.with(|cell| {
std::mem::swap(&mut *cell.borrow_mut(), &mut mappings);
});
for src_arch in source.archetypes.iter().filter(|arch| {
filter
.matches_layout(arch.layout().component_types())
.is_pass()
}) {
let layout = merger.convert_layout((**src_arch.layout()).clone());
let dst_arch_index = if !M::prefers_new_archetype() || src_arch.entities().len() < 32 {
self.index.search(&layout).next()
} else {
None
};
let dst_arch_index = dst_arch_index.unwrap_or_else(|| self.insert_archetype(layout));
let dst_arch = &mut self.archetypes[dst_arch_index.0 as usize];
let mut writer =
ArchetypeWriter::new(dst_arch_index, dst_arch, self.components.get_multi_mut());
ID_CLONE_MAPPINGS.with(|cell| {
let map = cell.borrow();
for entity in src_arch.entities() {
let entity = map.get(entity).unwrap_or(entity);
writer.push(*entity);
}
});
merger.merge_archetype(
0..src_arch.entities().len(),
src_arch,
&source.components,
&mut writer,
);
let (base, entities) = writer.inserted();
self.entities.insert(entities, dst_arch_index, base);
}
reallocated.unwrap_or_else(|| {
ID_CLONE_MAPPINGS.with(|cell| {
std::mem::swap(&mut *cell.borrow_mut(), &mut mappings);
});
mappings
})
}
pub fn clone_from_single<M: Merger>(
&mut self,
source: &World,
entity: Entity,
merger: &mut M,
) -> Entity {
let mut allocator = Allocate::new();
let dst_entity = merger.assign_id(entity, &mut allocator);
self.remove(dst_entity);
let src_location = source
.entities
.get(entity)
.expect("entity not found in source world");
let src_arch = &source.archetypes[src_location.archetype()];
let layout = merger.convert_layout((**src_arch.layout()).clone());
let dst_arch_index = self.insert_archetype(layout);
let dst_arch = &mut self.archetypes[dst_arch_index.0 as usize];
let mut writer =
ArchetypeWriter::new(dst_arch_index, dst_arch, self.components.get_multi_mut());
writer.push(dst_entity);
let mut mappings = match merger.entity_map() {
EntityRewrite::Auto(Some(mut overrides)) => {
overrides.entry(entity).or_insert(dst_entity);
overrides
}
EntityRewrite::Auto(None) => {
let mut map = HashMap::default();
map.insert(entity, dst_entity);
map
}
EntityRewrite::Explicit(overrides) => overrides,
};
ID_CLONE_MAPPINGS.with(|cell| {
std::mem::swap(&mut *cell.borrow_mut(), &mut mappings);
});
let index = src_location.component().0;
merger.merge_archetype(
index..(index + 1),
src_arch,
&source.components,
&mut writer,
);
let (base, entities) = writer.inserted();
self.entities.insert(entities, dst_arch_index, base);
ID_CLONE_MAPPINGS.with(|cell| {
cell.borrow_mut().clear();
});
dst_entity
}
#[cfg(feature = "serialize")]
pub fn as_serializable<
'a,
F: LayoutFilter,
W: crate::internals::serialize::ser::WorldSerializer,
E: crate::internals::serialize::id::EntitySerializer,
>(
&'a self,
filter: F,
world_serializer: &'a W,
entity_serializer: &'a E,
) -> crate::internals::serialize::ser::SerializableWorld<'a, F, W, E> {
crate::internals::serialize::ser::SerializableWorld::new(
&self,
filter,
world_serializer,
entity_serializer,
)
}
}
impl EntityStore for World {
fn entry_ref(&self, entity: Entity) -> Result<EntryRef, EntityAccessError> {
self.entities
.get(entity)
.map(|location| {
EntryRef::new(
location,
&self.components,
&self.archetypes,
ComponentAccess::All,
)
})
.ok_or(EntityAccessError::EntityNotFound)
}
fn entry_mut(&mut self, entity: Entity) -> Result<EntryMut, EntityAccessError> {
unsafe { self.entry_unchecked(entity) }
}
fn get_component_storage<V: for<'b> View<'b>>(
&self,
) -> Result<StorageAccessor, EntityAccessError> {
Ok(StorageAccessor::new(
self.id,
&self.index,
&self.components,
&self.archetypes,
&self.groups,
&self.group_members,
None,
))
}
fn id(&self) -> WorldId {
self.id
}
}
#[derive(Clone, Copy)]
pub struct StorageAccessor<'a> {
id: WorldId,
index: &'a SearchIndex,
components: &'a Components,
archetypes: &'a [Archetype],
groups: &'a [Group],
group_members: &'a HashMap<ComponentTypeId, usize>,
allowed_archetypes: Option<&'a BitSet>,
}
impl<'a> StorageAccessor<'a> {
pub(crate) fn new(
id: WorldId,
index: &'a SearchIndex,
components: &'a Components,
archetypes: &'a [Archetype],
groups: &'a [Group],
group_members: &'a HashMap<ComponentTypeId, usize>,
allowed_archetypes: Option<&'a BitSet>,
) -> Self {
Self {
id,
index,
components,
archetypes,
groups,
group_members,
allowed_archetypes,
}
}
pub(crate) fn with_allowed_archetypes(
mut self,
allowed_archetypes: Option<&'a BitSet>,
) -> Self {
self.allowed_archetypes = allowed_archetypes;
self
}
pub fn id(&self) -> WorldId {
self.id
}
pub fn can_access_archetype(&self, ArchetypeIndex(archetype): ArchetypeIndex) -> bool {
match self.allowed_archetypes {
None => true,
Some(archetypes) => archetypes.contains(archetype as usize),
}
}
pub fn layout_index(&self) -> &'a SearchIndex {
self.index
}
pub fn components(&self) -> &'a Components {
self.components
}
pub fn archetypes(&self) -> &'a [Archetype] {
self.archetypes
}
pub fn groups(&self) -> &'a [Group] {
self.groups
}
pub fn group(&self, type_id: ComponentTypeId) -> Option<(usize, &'a Group)> {
self.group_members
.get(&type_id)
.map(|i| (*i, self.groups.get(*i).unwrap()))
}
}
pub trait Merger {
#[inline]
fn prefers_new_archetype() -> bool {
false
}
fn entity_map(&mut self) -> EntityRewrite {
EntityRewrite::default()
}
#[inline]
#[allow(unused_variables)]
fn assign_id(&mut self, existing: Entity, allocator: &mut Allocate) -> Entity {
allocator.next().unwrap()
}
#[inline]
fn convert_layout(&mut self, source_layout: EntityLayout) -> EntityLayout {
source_layout
}
fn merge_archetype(
&mut self,
src_entity_range: Range<usize>,
src_arch: &Archetype,
src_components: &Components,
dst: &mut ArchetypeWriter,
);
}
pub enum EntityRewrite {
Auto(Option<HashMap<Entity, Entity, EntityHasher>>),
Explicit(HashMap<Entity, Entity, EntityHasher>),
}
impl Default for EntityRewrite {
fn default() -> Self {
Self::Auto(None)
}
}
#[derive(Default)]
#[allow(clippy::type_complexity)]
pub struct Duplicate {
duplicate_fns: HashMap<
ComponentTypeId,
(
ComponentTypeId,
fn() -> Box<dyn UnknownComponentStorage>,
Box<
dyn FnMut(
Range<usize>,
&Archetype,
&dyn UnknownComponentStorage,
&mut ArchetypeWriter,
),
>,
),
>,
}
impl Duplicate {
pub fn new() -> Self {
Self::default()
}
pub fn register_copy<T: Component + Copy>(&mut self) {
use crate::internals::storage::ComponentStorage;
let type_id = ComponentTypeId::of::<T>();
let constructor = || Box::new(T::Storage::default()) as Box<dyn UnknownComponentStorage>;
let convert = Box::new(
move |src_entities: Range<usize>,
src_arch: &Archetype,
src: &dyn UnknownComponentStorage,
dst: &mut ArchetypeWriter| {
let src = src.downcast_ref::<T::Storage>().unwrap();
let mut dst = dst.claim_components::<T>();
let src_slice = &src.get(src_arch.index()).unwrap().into_slice()[src_entities];
unsafe { dst.extend_memcopy(src_slice.as_ptr(), src_slice.len()) };
},
);
self.duplicate_fns
.insert(type_id, (type_id, constructor, convert));
}
pub fn register_clone<T: Component + Clone>(&mut self) {
self.register_convert(|source: &T| source.clone());
}
pub fn register_convert<
Source: Component,
Target: Component,
F: FnMut(&Source) -> Target + 'static,
>(
&mut self,
mut convert: F,
) {
use crate::internals::storage::ComponentStorage;
let source_type = ComponentTypeId::of::<Source>();
let dest_type = ComponentTypeId::of::<Target>();
let constructor =
|| Box::new(Target::Storage::default()) as Box<dyn UnknownComponentStorage>;
let convert = Box::new(
move |src_entities: Range<usize>,
src_arch: &Archetype,
src: &dyn UnknownComponentStorage,
dst: &mut ArchetypeWriter| {
let src = src.downcast_ref::<Source::Storage>().unwrap();
let mut dst = dst.claim_components::<Target>();
let src_slice = &src.get(src_arch.index()).unwrap().into_slice()[src_entities];
dst.ensure_capacity(src_slice.len());
for component in src_slice {
let component = convert(component);
unsafe {
dst.extend_memcopy(&component as *const Target, 1);
std::mem::forget(component);
}
}
},
);
self.duplicate_fns
.insert(source_type, (dest_type, constructor, convert));
}
pub fn register_convert_raw(
&mut self,
src_type: ComponentTypeId,
dst_type: ComponentTypeId,
constructor: fn() -> Box<dyn UnknownComponentStorage>,
duplicate_fn: Box<
dyn FnMut(Range<usize>, &Archetype, &dyn UnknownComponentStorage, &mut ArchetypeWriter),
>,
) {
self.duplicate_fns
.insert(src_type, (dst_type, constructor, duplicate_fn));
}
}
impl Merger for Duplicate {
fn convert_layout(&mut self, source_layout: EntityLayout) -> EntityLayout {
let mut layout = EntityLayout::new();
for src_type in source_layout.component_types() {
if let Some((dst_type, constructor, _)) = self.duplicate_fns.get(src_type) {
unsafe { layout.register_component_raw(*dst_type, *constructor) };
}
}
layout
}
fn merge_archetype(
&mut self,
src_entity_range: Range<usize>,
src_arch: &Archetype,
src_components: &Components,
dst: &mut ArchetypeWriter,
) {
for src_type in src_arch.layout().component_types() {
if let Some((_, _, convert)) = self.duplicate_fns.get_mut(src_type) {
let src_storage = src_components.get(*src_type).unwrap();
convert(src_entity_range.clone(), src_arch, src_storage, dst);
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::internals::{insert::IntoSoa, query::filter::filter_fns::any};
#[derive(Clone, Copy, Debug, PartialEq)]
struct Pos(f32, f32, f32);
#[derive(Clone, Copy, Debug, PartialEq)]
struct Rot(f32, f32, f32);
#[test]
fn create() {
let _ = World::default();
}
#[test]
fn extend_soa() {
let mut world = World::default();
let entities =
world.extend((vec![1usize, 2usize, 3usize], vec![true, true, false]).into_soa());
assert_eq!(entities.len(), 3);
}
#[test]
fn extend_soa_zerosize_component() {
let mut world = World::default();
let entities = world.extend((vec![(), (), ()], vec![true, true, false]).into_soa());
assert_eq!(entities.len(), 3);
}
#[test]
#[should_panic(expected = "all component vecs must have equal length")]
fn extend_soa_unbalanced_components() {
let mut world = World::default();
let _ = world.extend((vec![1usize, 2usize], vec![true, true, false]).into_soa());
}
#[test]
#[should_panic(
expected = "only one component of a given type may be attached to a single entity"
)]
fn extend_soa_duplicate_components() {
let mut world = World::default();
let _ =
world.extend((vec![1usize, 2usize, 3usize], vec![1usize, 2usize, 3usize]).into_soa());
}
#[test]
fn extend_aos() {
let mut world = World::default();
let entities = world.extend(vec![(1usize, true), (2usize, true), (3usize, false)]);
assert_eq!(entities.len(), 3);
}
#[test]
fn extend_aos_zerosize_component() {
let mut world = World::default();
let entities = world.extend(vec![((), true), ((), true), ((), false)]);
assert_eq!(entities.len(), 3);
}
#[test]
#[should_panic(
expected = "only one component of a given type may be attached to a single entity"
)]
fn extend_aos_duplicate_components() {
let mut world = World::default();
let _ = world.extend(vec![(1usize, 1usize), (2usize, 2usize), (3usize, 3usize)]);
}
#[test]
fn remove() {
let mut world = World::default();
let entities: Vec<_> = world
.extend(vec![(1usize, true), (2usize, true), (3usize, false)])
.iter()
.copied()
.collect();
assert_eq!(world.len(), 3);
assert!(world.remove(entities[0]));
assert_eq!(world.len(), 2);
}
#[test]
fn remove_repeat() {
let mut world = World::default();
let entities: Vec<_> = world
.extend(vec![(1usize, true), (2usize, true), (3usize, false)])
.iter()
.copied()
.collect();
assert_eq!(world.len(), 3);
assert!(world.remove(entities[0]));
assert_eq!(world.len(), 2);
assert_eq!(world.remove(entities[0]), false);
assert_eq!(world.len(), 2);
}
#[test]
fn pack() {
use crate::internals::{
query::{
view::{read::Read, write::Write},
IntoQuery,
},
storage::group::GroupSource,
};
#[derive(Copy, Clone, Debug, PartialEq)]
struct A(f32);
#[derive(Copy, Clone, Debug, PartialEq)]
struct B(f32);
#[derive(Copy, Clone, Debug, PartialEq)]
struct C(f32);
#[derive(Copy, Clone, Debug, PartialEq)]
struct D(f32);
let mut world = crate::internals::world::World::new(WorldOptions {
groups: vec![<(A, B, C, D)>::to_group()],
});
world.extend(std::iter::repeat((A(0f32),)).take(10000));
world.extend(std::iter::repeat((A(0f32), B(1f32))).take(10000));
world.extend(std::iter::repeat((A(0f32), B(1f32), C(2f32))).take(10000));
world.extend(std::iter::repeat((A(0f32), B(1f32), C(2f32), D(3f32))).take(10000));
world.pack(PackOptions::force());
let mut query = <(Read<A>, Write<B>)>::query();
assert_eq!(query.iter_mut(&mut world).count(), 30000);
let mut count = 0;
for chunk in query.iter_chunks_mut(&mut world) {
count += chunk.into_iter().count();
}
assert_eq!(count, 30000);
let mut count = 0;
for chunk in query.iter_chunks_mut(&mut world) {
let (x, _) = chunk.into_components();
count += x.iter().count();
}
assert_eq!(count, 30000);
}
#[test]
fn move_from() {
let mut a = World::default();
let mut b = World::default();
let entity_a = a.extend(vec![
(Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)),
(Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)),
])[0];
let entity_b = b.extend(vec![
(Pos(7., 8., 9.), Rot(0.7, 0.8, 0.9)),
(Pos(10., 11., 12.), Rot(0.10, 0.11, 0.12)),
])[0];
b.move_from(&mut a, &any());
assert!(a.entry(entity_a).is_none());
assert_eq!(
*b.entry(entity_b).unwrap().get_component::<Pos>().unwrap(),
Pos(7., 8., 9.)
);
assert_eq!(
*b.entry(entity_a).unwrap().get_component::<Pos>().unwrap(),
Pos(1., 2., 3.)
);
}
#[test]
fn clone_from() {
let mut a = World::default();
let mut b = World::default();
let entity_a = a.extend(vec![
(Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)),
(Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)),
])[0];
let entity_b = b.extend(vec![
(Pos(7., 8., 9.), Rot(0.7, 0.8, 0.9)),
(Pos(10., 11., 12.), Rot(0.10, 0.11, 0.12)),
])[0];
let mut merger = Duplicate::default();
merger.register_copy::<Pos>();
merger.register_clone::<Rot>();
let map = b.clone_from(&a, &any(), &mut merger);
assert_eq!(
*a.entry(entity_a).unwrap().get_component::<Pos>().unwrap(),
Pos(1., 2., 3.)
);
assert_eq!(
*b.entry(map[&entity_a])
.unwrap()
.get_component::<Pos>()
.unwrap(),
Pos(1., 2., 3.)
);
assert_eq!(
*b.entry(entity_b).unwrap().get_component::<Pos>().unwrap(),
Pos(7., 8., 9.)
);
assert_eq!(
*a.entry(entity_a).unwrap().get_component::<Rot>().unwrap(),
Rot(0.1, 0.2, 0.3)
);
assert_eq!(
*b.entry(map[&entity_a])
.unwrap()
.get_component::<Rot>()
.unwrap(),
Rot(0.1, 0.2, 0.3)
);
assert_eq!(
*b.entry(entity_b).unwrap().get_component::<Rot>().unwrap(),
Rot(0.7, 0.8, 0.9)
);
}
#[test]
fn clone_update_entity_refs() {
let mut a = World::default();
let mut b = World::default();
let entity_1 = a.push((Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)));
let entity_2 = a.push((Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6), entity_1));
a.entry(entity_1).unwrap().add_component(entity_2);
b.extend(vec![
(Pos(7., 8., 9.), Rot(0.7, 0.8, 0.9)),
(Pos(10., 11., 12.), Rot(0.10, 0.11, 0.12)),
]);
let mut merger = Duplicate::default();
merger.register_copy::<Pos>();
merger.register_clone::<Rot>();
merger.register_clone::<Entity>();
let map = b.clone_from(&a, &any(), &mut merger);
assert_eq!(
*b.entry(map[&entity_1])
.unwrap()
.get_component::<Entity>()
.unwrap(),
map[&entity_2]
);
assert_eq!(
*b.entry(map[&entity_2])
.unwrap()
.get_component::<Entity>()
.unwrap(),
map[&entity_1]
);
}
#[test]
fn clone_from_single() {
let mut a = World::default();
let mut b = World::default();
let entities = a
.extend(vec![
(Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)),
(Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)),
])
.to_vec();
let entity_b = b.extend(vec![
(Pos(7., 8., 9.), Rot(0.7, 0.8, 0.9)),
(Pos(10., 11., 12.), Rot(0.10, 0.11, 0.12)),
])[0];
let mut merger = Duplicate::default();
merger.register_copy::<Pos>();
merger.register_clone::<Rot>();
let cloned = b.clone_from_single(&a, entities[0], &mut merger);
assert_eq!(
*a.entry(entities[0])
.unwrap()
.get_component::<Pos>()
.unwrap(),
Pos(1., 2., 3.)
);
assert_eq!(
*b.entry(cloned).unwrap().get_component::<Pos>().unwrap(),
Pos(1., 2., 3.)
);
assert_eq!(
*b.entry(entity_b).unwrap().get_component::<Pos>().unwrap(),
Pos(7., 8., 9.)
);
assert_eq!(
*a.entry(entities[0])
.unwrap()
.get_component::<Rot>()
.unwrap(),
Rot(0.1, 0.2, 0.3)
);
assert_eq!(
*b.entry(cloned).unwrap().get_component::<Rot>().unwrap(),
Rot(0.1, 0.2, 0.3)
);
assert_eq!(
*b.entry(entity_b).unwrap().get_component::<Rot>().unwrap(),
Rot(0.7, 0.8, 0.9)
);
assert!(a.entry(entities[1]).is_some());
assert!(b.entry(entities[1]).is_none());
}
#[test]
#[allow(clippy::float_cmp)]
fn clone_from_convert() {
let mut a = World::default();
let mut b = World::default();
let entity_a = a.extend(vec![
(Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)),
(Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)),
])[0];
b.extend(vec![
(Pos(7., 8., 9.), Rot(0.7, 0.8, 0.9)),
(Pos(10., 11., 12.), Rot(0.10, 0.11, 0.12)),
]);
let mut merger = Duplicate::default();
merger.register_convert::<Pos, f32, _>(|comp| comp.0 as f32);
let map = b.clone_from(&a, &any(), &mut merger);
assert_eq!(
*a.entry(entity_a).unwrap().get_component::<Pos>().unwrap(),
Pos(1., 2., 3.)
);
assert_eq!(
*b.entry(map[&entity_a])
.unwrap()
.get_component::<f32>()
.unwrap(),
1f32
);
assert_eq!(
*a.entry(entity_a).unwrap().get_component::<Rot>().unwrap(),
Rot(0.1, 0.2, 0.3)
);
assert!(b
.entry(map[&entity_a])
.unwrap()
.get_component::<Rot>()
.is_err());
}
}