diff --git a/lyra-ecs/src/archetype.rs b/lyra-ecs/src/archetype.rs index 33e1d51..e6853ec 100644 --- a/lyra-ecs/src/archetype.rs +++ b/lyra-ecs/src/archetype.rs @@ -19,8 +19,8 @@ impl Drop for ComponentColumn { unsafe { // layout of current alloc - let layout = Layout::from_size_align_unchecked(self.info.layout.size * self.capacity, - self.info.layout.alignment); + let layout = Layout::from_size_align_unchecked(self.info.layout.size() * self.capacity, + self.info.layout.align()); dealloc(data, layout); } } @@ -42,7 +42,7 @@ impl ComponentColumn { } pub unsafe fn new(info: ComponentInfo, capacity: usize) -> Self { - let data = ComponentColumn::alloc(info.layout.into_layout().unwrap(), capacity); + let data = ComponentColumn::alloc(info.layout, capacity); Self { data: RefCell::new(data), @@ -64,8 +64,9 @@ impl ComponentColumn { let mut data = self.data.borrow_mut(); let data = data.deref_mut(); - let dest = NonNull::new_unchecked(data.as_ptr().add(entity_index * self.info.layout.size)); - ptr::copy_nonoverlapping(comp_src.as_ptr(), dest.as_ptr(), self.info.layout.size); + let size = self.info.layout.size(); + let dest = NonNull::new_unchecked(data.as_ptr().add(entity_index * size)); + ptr::copy_nonoverlapping(comp_src.as_ptr(), dest.as_ptr(), size); // check if a component spot is being set twice and that the entity's tick is // already stored @@ -86,7 +87,7 @@ impl ComponentColumn { Ref::map(data, |data| { let ptr = NonNull::new_unchecked(data.as_ptr() - .add(entity_index * self.info.layout.size)) + .add(entity_index * self.info.layout.size())) .cast(); &*ptr.as_ptr() }) @@ -105,7 +106,7 @@ impl ComponentColumn { let p = data.as_ptr() .cast::() - .add(entity_index * self.info.layout.size); + .add(entity_index * self.info.layout.size()); &mut *p } @@ -124,17 +125,17 @@ impl ComponentColumn { let mut data = self.data.borrow_mut(); - let mut new_ptr = Self::alloc(self.info.layout.into_layout().unwrap(), new_capacity); + let mut new_ptr = Self::alloc(self.info.layout, new_capacity); if self.len > 0 { - ptr::copy_nonoverlapping(data.as_ptr(), new_ptr.as_ptr(), self.len * self.info.layout.size); + ptr::copy_nonoverlapping(data.as_ptr(), new_ptr.as_ptr(), self.len * self.info.layout.size()); } // dont attempt to free if we weren't able to store anything anyway if self.capacity != 0 { let old_layout = Layout::from_size_align_unchecked( - self.info.layout.size.checked_mul(self.capacity).unwrap(), - self.info.layout.alignment + self.info.layout.size().checked_mul(self.capacity).unwrap(), + self.info.layout.align() ); mem::swap(data.deref_mut(), &mut new_ptr); @@ -153,15 +154,16 @@ impl ComponentColumn { let mut data = self.data.borrow_mut(); let data = data.deref_mut(); + let size = self.info.layout.size(); let mut old_comp_ptr = NonNull::new_unchecked(data.as_ptr() - .add(entity_index * self.info.layout.size)); + .add(entity_index * size)); let moved_index = if entity_index != self.len - 1 { let moved_index = self.len - 1; let mut new_comp_ptr = NonNull::new_unchecked(data.as_ptr() - .add(moved_index * self.info.layout.size)); + .add(moved_index * size)); - ptr::copy_nonoverlapping(new_comp_ptr.as_ptr(), old_comp_ptr.as_ptr(), self.info.layout.size); + ptr::copy_nonoverlapping(new_comp_ptr.as_ptr(), old_comp_ptr.as_ptr(), size); mem::swap(&mut old_comp_ptr, &mut new_comp_ptr); // new_comp_ptr is now the old ptr @@ -194,6 +196,7 @@ impl ComponentColumn { } } +/// An id of an Archetype #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] pub struct ArchetypeId(pub u64); @@ -207,20 +210,41 @@ impl ArchetypeId { } } +/// Stores a group of entities with matching components. +/// +/// An Archetype can be thought of as a table, with entities as the rows and the entity's +/// components as each column. This means you can have tightly packed components of entities and +/// quickly iterate through entities with the same components. #[derive(Clone, Default)] pub struct Archetype { - pub id: ArchetypeId, - pub(crate) entities: HashMap, + id: ArchetypeId, + /// The indexes of the entities in the archetype. + pub(crate) entity_ids: HashMap, /// map an Archetype entity id to an entity - pub(crate) ids_to_entity: HashMap, + //pub(crate) ids_to_entity: HashMap, + /// The entities in the Archetype. + /// + /// Can be used to map `ArchetypeEntityId` to an Entity since `ArchetypeEntityId` has + /// the index that the entity is stored at. + pub(crate) entities: Vec, pub(crate) columns: Vec, - pub capacity: usize, + capacity: usize, } /// The default capacity of the columns const DEFAULT_CAPACITY: usize = 32; impl Archetype { + /// Returns the id of the Archetype + pub fn id(&self) -> ArchetypeId { + self.id + } + + /// Returns the max amount of Entities that the Archetype can store without reallocating. + pub fn capacity(&self) -> usize { + self.capacity + } + pub fn from_bundle_info(new_id: ArchetypeId, bundle_info: Vec) -> Archetype { let columns = bundle_info.into_iter().map(|i| { unsafe { ComponentColumn::new(i, DEFAULT_CAPACITY) } @@ -228,8 +252,8 @@ impl Archetype { Archetype { id: new_id, - entities: HashMap::new(), - ids_to_entity: HashMap::new(), + entity_ids: HashMap::new(), + entities: Vec::new(), columns, capacity: DEFAULT_CAPACITY, } @@ -244,15 +268,16 @@ impl Archetype { where B: Bundle { - if self.capacity == self.entities.len() { + if self.capacity == self.entity_ids.len() { let new_cap = self.capacity * 2; self.grow_columns(new_cap); self.capacity = new_cap; } - let entity_index = ArchetypeEntityId(self.entities.len() as u64); - self.entities.insert(entity, entity_index); - self.ids_to_entity.insert(entity_index, entity); + debug_assert_eq!(self.entity_ids.len(), self.entities.len(), "Somehow the Archetype's entity storage got unsynced"); + let entity_index = ArchetypeEntityId(self.entity_ids.len() as u64); + self.entity_ids.insert(entity, entity_index); + self.entities.push(entity); bundle.take(|data, type_id, _size| { let col = self.get_column_mut(type_id).unwrap(); @@ -263,39 +288,41 @@ impl Archetype { entity_index } - /// Removes an entity from the Archetype and frees its components. Returns the entity record that took its place in the component column. + /// Removes an entity from the Archetype and frees its components. Returns the entity record + /// that took its place in the component column. pub(crate) fn remove_entity(&mut self, entity: Entity, tick: &Tick) -> Option<(Entity, ArchetypeEntityId)> { - let entity_index = *self.entities.get(&entity) + let entity_index = *self.entity_ids.get(&entity) .expect("The entity is not in this Archetype!"); let mut removed_entity: Option<(Entity, ArchetypeEntityId)> = None; for c in self.columns.iter_mut() { let moved_entity = unsafe { c.remove_component(entity_index.0 as usize, tick) }; - // Make sure that the moved entity is the same as what was moved in other columns. - // If this is the first move, find the EntityId that points to the column index. - // If there wasn't a moved entity, make sure no other columns moved something. if let Some(res) = moved_entity { - if let Some((_, aid)) = removed_entity { - assert!(res as u64 == aid.0); // make sure all columns removed the same entity + // Make sure that the moved entity is the same as what was moved in other columns. + assert!(res as u64 == aid.0); } else { - let replaced_entity = self.entities.iter().find(|(_, a)| a.0 == res as u64) - .map(|(e, _a)| *e).expect("Failure to find entity for moved component!"); - removed_entity = Some((replaced_entity, ArchetypeEntityId(res as u64))); + // This is the first move, so find the EntityId that points to the column index. + let just_removed = self.entities[res]; + removed_entity = Some((just_removed, ArchetypeEntityId(res as u64))); } } else { + // If there wasn't a moved entity, make sure no other columns moved something. assert!(removed_entity.is_none()); } } // safe from the .expect at the start of this method. - self.entities.remove(&entity).unwrap(); - self.ids_to_entity.remove(&entity_index).unwrap(); + self.entity_ids.remove(&entity).unwrap(); + if self.entities.len() > 1 { + let len = self.entities.len(); + self.entities.swap(entity_index.0 as _, len - 1); + } + self.entities.pop().unwrap(); // now change the ArchetypeEntityId to be the index that the moved entity was moved into. removed_entity.map(|(e, _a)| (e, entity_index)) - } /// Returns a boolean indicating whether this archetype can store the TypeIds given @@ -312,12 +339,12 @@ impl Archetype { /// Returns a boolean indicating whether this archetype is empty or not. pub fn is_empty(&self) -> bool { - self.entities.is_empty() + self.entity_ids.is_empty() } /// Returns the amount of entities that are stored in the archetype. pub fn len(&self) -> usize { - self.entities.len() + self.entity_ids.len() } /// Grows columns in the archetype @@ -338,28 +365,32 @@ impl Archetype { self.capacity = new_capacity; } - pub fn get_column(&self, type_id: DynTypeId) -> Option<&ComponentColumn> { + /// Attempts to find the column storing components of `type_id` + pub fn get_column>(&self, type_id: I) -> Option<&ComponentColumn> { + let type_id = type_id.into(); self.columns.iter().find(|c| c.info.type_id == type_id) } /// Returns a mutable borrow to a component column for `type_id`. /// /// Note: This does not modify the tick for the column! - pub fn get_column_mut(&mut self, type_id: DynTypeId) -> Option<&mut ComponentColumn> { + pub fn get_column_mut>(&mut self, type_id: I) -> Option<&mut ComponentColumn> { + let type_id = type_id.into(); self.columns.iter_mut().find(|c| c.info.type_id == type_id) } /// Reserves a slot in the columns for an entity and returns the index of that reserved spot pub fn reserve_one(&mut self, entity: Entity) -> ArchetypeEntityId { - if self.capacity == self.entities.len() { + if self.capacity == self.entity_ids.len() { let new_cap = self.capacity * 2; self.grow_columns(new_cap); self.capacity = new_cap; } - let entity_index = ArchetypeEntityId(self.entities.len() as u64); - self.entities.insert(entity, entity_index); - self.ids_to_entity.insert(entity_index, entity); + debug_assert_eq!(self.entity_ids.len(), self.entities.len(), "Somehow the Archetype's entity storage got unsynced"); + let entity_index = ArchetypeEntityId(self.entity_ids.len() as u64); + self.entity_ids.insert(entity, entity_index); + self.entities.push(entity); for col in self.columns.iter_mut() { col.len += 1; @@ -371,8 +402,8 @@ impl Archetype { /// Moves the entity from this archetype into another one. /// /// # Safety - /// The entity IS NOT removed from the old archetype. You must manually call [`Archetype::remove_entity`]. - /// It was done this way because I had some borrow check issues when writing [`World::insert`] + /// The entity IS NOT removed from the old archetype. You must manually call [`Archetype::remove_entity`](crate::Archetype). + /// It was done this way because I had some borrow check issues when writing [`World::insert`](crate::World) /// related to borrowing mutably from self more than once. /* pub fn move_into(&self, into_arch: &mut Archetype, entity: Entity, new_components: B) where @@ -405,16 +436,19 @@ impl Archetype { //self.remove_entity(entity); } */ - pub fn entities(&self) -> &HashMap { - &self.entities + /// Returns a borrow to the map used to find the column indices of the entity. + pub fn entity_indexes(&self) -> &HashMap { + &self.entity_ids } - pub fn entity_of_index(&self, id: ArchetypeEntityId) -> Option { - self.ids_to_entity.get(&id).cloned() + /// Returns the Entity that is stored at the column matching `id`. + pub fn entity_at_index(&self, id: ArchetypeEntityId) -> Option { + self.entities.get(id.0 as usize).cloned() } + /// Returns a boolean indicating if the Archetype is storing the entity. pub fn has_entity(&self, e: Entity) -> bool { - self.entities.contains_key(&e) + self.entity_ids.contains_key(&e) } } @@ -424,7 +458,7 @@ mod tests { use rand::Rng; - use crate::{bundle::Bundle, tests::{Vec2, Vec3}, ComponentInfo, DynTypeId, DynamicBundle, Entity, EntityId, MemoryLayout, Tick}; + use crate::{bundle::Bundle, tests::{Vec2, Vec3}, ComponentInfo, DynTypeId, DynamicBundle, Entity, EntityId, Tick}; use super::Archetype; @@ -604,7 +638,7 @@ mod tests { /// This test simulates an archetype that stores types that rust does not know about. #[test] fn dynamic_archetype() { - let layout = MemoryLayout::from(Layout::new::()); + let layout = Layout::new::(); let info = ComponentInfo::new_unknown(DynTypeId::Unknown(100), "u32", layout); let infos = vec![info.clone()]; diff --git a/lyra-ecs/src/bundle.rs b/lyra-ecs/src/bundle.rs index 98be308..85bc4ae 100644 --- a/lyra-ecs/src/bundle.rs +++ b/lyra-ecs/src/bundle.rs @@ -152,7 +152,7 @@ impl Bundle for DynamicBundle { fn take(self, mut f: impl FnMut(NonNull, DynTypeId, usize)) { for (data, info) in self.bundle.iter() { - f(*data, info.type_id, info.layout.size); + f(*data, info.type_id, info.layout.size()); } } diff --git a/lyra-ecs/src/command.rs b/lyra-ecs/src/command.rs index 56d7aca..b7f6f91 100644 --- a/lyra-ecs/src/command.rs +++ b/lyra-ecs/src/command.rs @@ -2,30 +2,55 @@ use std::{any::Any, cell::RefMut, collections::VecDeque, ptr::{self, NonNull}}; use crate::{system::FnArgFetcher, Access, Bundle, Entities, Entity, World}; +/// A Command be used to delay mutation of the world until after this system is ran. pub trait Command: Any { fn as_any_boxed(self: Box) -> Box; - fn run(self, world: &mut World) -> anyhow::Result<()>; + /// Executes the command + fn run(self, world: &mut World); } impl Command for F where - F: FnOnce(&mut World) -> anyhow::Result<()> + 'static + F: FnOnce(&mut World) + 'static { fn as_any_boxed(self: Box) -> Box { self } - fn run(self, world: &mut World) -> anyhow::Result<()> { + fn run(self, world: &mut World) { self(world) } } -type RunCommand = unsafe fn(cmd: Box, world: &mut World) -> anyhow::Result<()>; +type RunCommand = unsafe fn(cmd: Box, world: &mut World); +/// Stores a queue of commands that will get executed after the system is ran. +/// +/// This struct can be inserted as a resource into the world, and the commands will be +/// executed by the [`GraphExecutor`](crate::system::GraphExecutor) after the system is executed. #[derive(Default)] pub struct CommandQueue(VecDeque<(RunCommand, Box)>); +/// Used in a system to queue up commands that will run right after this system. +/// +/// This can be used to delay the mutation of the world until after the system is ran. These +/// must be used if you're mutating the world inside a [`ViewState`](crate::query::ViewState). +/// +/// ```nobuild +/// fn particle_spawner_system( +/// commands: Commands, +/// view: ViewState<(&Campfire, &Transform)> +/// ) -> anyhow::Result<()> { +/// for (campfire, pos) in view.iter() { +/// // If you do not use commands to spawn this, the next iteration +/// // of the view will cause a segfault. +/// commands.spawn((pos, Particle::new(/* ... */))); +/// } +/// +/// Ok(()) +/// } +/// ``` pub struct Commands<'a, 'b> { queue: &'b mut CommandQueue, entities: &'a mut Entities, @@ -48,20 +73,18 @@ impl<'a, 'b> Commands<'a, 'b> { .downcast::() .unwrap(); - cmd.run(world)?; - - Ok(()) + cmd.run(world); }; self.queue.0.push_back((run_fn, cmd)); } + /// Spawn an entity into the World. See [`World::spawn`] pub fn spawn(&mut self, bundle: B) -> Entity { let e = self.entities.reserve(); self.add(move |world: &mut World| { world.spawn_into(e, bundle); - Ok(()) }); e @@ -71,7 +94,7 @@ impl<'a, 'b> Commands<'a, 'b> { pub fn execute(&mut self, world: &mut World) -> anyhow::Result<()> { while let Some((cmd_fn, cmd_ptr)) = self.queue.0.pop_front() { unsafe { - cmd_fn(cmd_ptr, world)?; + cmd_fn(cmd_ptr, world); } } diff --git a/lyra-ecs/src/component_info.rs b/lyra-ecs/src/component_info.rs index 7437e16..a819f72 100644 --- a/lyra-ecs/src/component_info.rs +++ b/lyra-ecs/src/component_info.rs @@ -1,41 +1,4 @@ -use std::{any::TypeId, alloc::{Layout, LayoutError}}; - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct MemoryLayout { - pub size: usize, - pub alignment: usize -} - -impl TryInto for MemoryLayout { - type Error = LayoutError; - - fn try_into(self) -> Result { - Layout::from_size_align(self.size, self.alignment) - } -} - -impl From for MemoryLayout { - fn from(value: Layout) -> Self { - Self { - size: value.size(), - alignment: value.align(), - } - } -} - -impl MemoryLayout { - pub fn new(size: usize, alignment: usize) -> Self { - MemoryLayout { - size, - alignment - } - } - - /// Converts self into a Layout. - pub fn into_layout(self) -> Result { - Layout::from_size_align(self.size, self.alignment) - } -} +use std::{any::TypeId, alloc::Layout}; /// A dynamic type id. Supports types that are not known to Rust. #[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)] @@ -88,31 +51,29 @@ impl DynTypeId { } } +/// Some information about a component. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ComponentInfo { pub type_id: DynTypeId, - //pub name: String, - pub layout: MemoryLayout, + pub(crate) layout: Layout, } impl ComponentInfo { pub fn new() -> Self { Self { type_id: TypeId::of::().into(), - //name: type_name::().to_string(), - layout: MemoryLayout::from(Layout::new::()), + layout: Layout::new::(), } } /// Create ComponentInfo from a type that is not known to rust - pub fn new_unknown(type_id: D, name: &str, layout: MemoryLayout) -> Self + pub fn new_unknown(type_id: D, name: &str, layout: Layout) -> Self where D: Into, { let _ = name; // would be used at some point Self { type_id: type_id.into(), - //name: name.to_string(), layout, } } diff --git a/lyra-ecs/src/query/borrow.rs b/lyra-ecs/src/query/borrow.rs index a3e4ec6..f1cc5de 100644 --- a/lyra-ecs/src/query/borrow.rs +++ b/lyra-ecs/src/query/borrow.rs @@ -91,7 +91,7 @@ where FetchBorrow { col, - size: col.info.layout.size, + size: col.info.layout.size(), _phantom: PhantomData, } } @@ -194,7 +194,7 @@ where unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> { let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id) .expect("You ignored 'can_visit_archetype'!"); - let layout_size = col.info.layout.size; + let layout_size = col.info.layout.size(); let col = NonNull::from(col); // TODO: find a way to get the component column mutable with a borrowed archetype so its tick can be updated. diff --git a/lyra-ecs/src/query/dynamic/mod.rs b/lyra-ecs/src/query/dynamic/mod.rs index 221870c..9ef43fc 100644 --- a/lyra-ecs/src/query/dynamic/mod.rs +++ b/lyra-ecs/src/query/dynamic/mod.rs @@ -38,7 +38,7 @@ impl<'a> Fetch<'a> for FetchDynamicType<'a> { unsafe fn get_item(&mut self, entity: crate::ArchetypeEntityId) -> Self::Item { let ptr = self.col.borrow_ptr(); let ptr = NonNull::new_unchecked(ptr.as_ptr() - .add(entity.0 as usize * self.info.layout.size)); + .add(entity.0 as usize * self.info.layout.size())); DynamicType { info: self.info, diff --git a/lyra-ecs/src/query/dynamic/view.rs b/lyra-ecs/src/query/dynamic/view.rs index fb88b85..e2cbd8c 100644 --- a/lyra-ecs/src/query/dynamic/view.rs +++ b/lyra-ecs/src/query/dynamic/view.rs @@ -83,7 +83,7 @@ impl<'a> Iterator for DynamicViewIter<'a> { self.next_archetype += 1; let arch = unsafe { self.archetypes.get_unchecked(arch_id) }; - if arch.entities.is_empty() { + if arch.entity_ids.is_empty() { continue; } @@ -94,7 +94,7 @@ impl<'a> Iterator for DynamicViewIter<'a> { self.fetchers = self.queries.iter() .map(|q| unsafe { q.fetch(self.world, ArchetypeId(arch_id as u64), arch) } ) .collect(); - self.component_indices = 0..arch.entities.len() as u64; + self.component_indices = 0..arch.entity_ids.len() as u64; } } } @@ -104,13 +104,13 @@ impl<'a> Iterator for DynamicViewIter<'a> { mod tests { use std::{alloc::Layout, ptr::NonNull}; - use crate::{world::World, MemoryLayout, ComponentInfo, DynTypeId, DynamicBundle, query::dynamic::QueryDynamicType}; + use crate::{world::World, ComponentInfo, DynTypeId, DynamicBundle, query::dynamic::QueryDynamicType}; use super::DynamicView; #[test] fn single_dynamic_view() { - let comp_layout = MemoryLayout::from(Layout::new::()); + let comp_layout = Layout::new::(); let comp_info = ComponentInfo::new_unknown(DynTypeId::Unknown(100), "u32", comp_layout); let mut dynamic_bundle = DynamicBundle::default(); diff --git a/lyra-ecs/src/query/entities.rs b/lyra-ecs/src/query/entities.rs index a75c33c..95f2137 100644 --- a/lyra-ecs/src/query/entities.rs +++ b/lyra-ecs/src/query/entities.rs @@ -41,7 +41,7 @@ impl Query for Entities { unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a Archetype, tick: crate::Tick) -> Self::Fetch<'a> { let _ = tick; // ignore unused warnings EntitiesFetch { - entities: archetype.entities.keys().cloned().collect::>(), + entities: archetype.entity_ids.keys().cloned().collect::>(), } } } diff --git a/lyra-ecs/src/query/mod.rs b/lyra-ecs/src/query/mod.rs index cc7d699..02c5e62 100644 --- a/lyra-ecs/src/query/mod.rs +++ b/lyra-ecs/src/query/mod.rs @@ -94,7 +94,7 @@ impl<'a> Fetch<'a> for () { unreachable!() } - unsafe fn get_item(&mut self, entity: ArchetypeEntityId) -> Self::Item { + unsafe fn get_item(&mut self, _: ArchetypeEntityId) -> Self::Item { () } } @@ -104,15 +104,17 @@ impl Query for () { type Fetch<'a> = (); + const ALWAYS_FETCHES: bool = true; + fn new() -> Self { () } - fn can_visit_archetype(&self, archetype: &Archetype) -> bool { + fn can_visit_archetype(&self, _: &Archetype) -> bool { true } - unsafe fn fetch<'a>(&self, world: &'a World, archetype: &'a Archetype, tick: Tick) -> Self::Fetch<'a> { + unsafe fn fetch<'a>(&self, _: &'a World, _: &'a Archetype, _: Tick) -> Self::Fetch<'a> { () } } diff --git a/lyra-ecs/src/query/resource.rs b/lyra-ecs/src/query/resource.rs index 377f5de..1f20667 100644 --- a/lyra-ecs/src/query/resource.rs +++ b/lyra-ecs/src/query/resource.rs @@ -192,9 +192,7 @@ impl<'a, T: ResourceObject> AsQuery for ResMut<'a, T> { mod tests { use std::ops::{Deref, DerefMut}; - use crate::{world::World, tests::{Vec2, Vec3}, query::QueryResourceMut}; - - use super::QueryResource; + use crate::{query::{Res, ResMut}, tests::{Vec2, Vec3}, world::World}; struct SomeCounter(u32); @@ -207,7 +205,7 @@ mod tests { println!("Added resource"); } - let mut res_iter = world.view_iter::>(); + let mut res_iter = world.view_iter::>(); let res = res_iter.next().unwrap(); let res = res.deref(); assert_eq!(res.0, 0); @@ -228,16 +226,16 @@ mod tests { println!("Added resource"); } - let i = world.view_iter::<(QueryResource, &Vec2)>(); + let i = world.view_iter::<(Res, &Vec2)>(); assert_eq!(i.count(), 3); - let i = world.view_iter::<(&Vec2, QueryResource)>(); + let i = world.view_iter::<(&Vec2, Res)>(); assert_eq!(i.count(), 3); - for (res, e) in world.view_iter::<(QueryResource, &Vec2)>() { + for (res, e) in world.view_iter::<(Res, &Vec2)>() { println!("Got res {}! and entity at {:?}", res.deref().0, e); } - let i = world.view_iter::>(); + let i = world.view_iter::>(); assert_eq!(i.count(), 1); } @@ -251,14 +249,14 @@ mod tests { } { - let mut resmut_iter = world.view_iter::>(); + let mut resmut_iter = world.view_iter::>(); let mut resmut = resmut_iter.next().unwrap(); let resmut = resmut.deref_mut(); assert_eq!(resmut.0, 0); resmut.0 += 20; } - let mut res_iter = world.view_iter::>(); + let mut res_iter = world.view_iter::>(); let res = res_iter.next().unwrap(); let res = res.deref(); assert_eq!(res.0, 20); diff --git a/lyra-ecs/src/query/view.rs b/lyra-ecs/src/query/view.rs index 4dc3a3c..93c67a0 100644 --- a/lyra-ecs/src/query/view.rs +++ b/lyra-ecs/src/query/view.rs @@ -122,7 +122,7 @@ where self.next_archetype += 1; let arch = unsafe { self.archetypes.get_unchecked(arch_id) }; - if arch.entities.is_empty() { + if arch.entity_ids.is_empty() { continue; } @@ -131,7 +131,7 @@ where self.fetcher = Some(self.query.fetch(self.world, arch, self.tick)); self.filter_fetcher = Some(self.filter.fetch(self.world, arch, self.tick)); } - self.component_indices = 0..arch.entities.len() as u64; + self.component_indices = 0..arch.entity_ids.len() as u64; } } } diff --git a/lyra-ecs/src/relation/mod.rs b/lyra-ecs/src/relation/mod.rs index 1b693b0..1acb0ce 100644 --- a/lyra-ecs/src/relation/mod.rs +++ b/lyra-ecs/src/relation/mod.rs @@ -17,6 +17,7 @@ mod relate_pair; #[doc(hidden)] pub use relate_pair::*; +#[allow(unused_variables)] pub trait Relation: 'static { /// called when a relation of this type is set on a target fn relation_add(&self, origin: Entity, target: Entity) { } @@ -40,6 +41,7 @@ pub struct RelationOriginComponent { /// origin of the relation -- the entity that targets this entity. #[derive(Component)] pub struct RelationTargetComponent { + #[allow(dead_code)] origin: Entity, _marker: PhantomData, } @@ -47,7 +49,7 @@ pub struct RelationTargetComponent { impl World { /// Creates a relation between two entities. /// - /// ```compile_fail + /// ```nobuild /// struct ChildOf; /// /// impl Relation for ChildOf { /* snip */ }; diff --git a/lyra-ecs/src/relation/relate_pair.rs b/lyra-ecs/src/relation/relate_pair.rs index ab58b3c..1ab8f96 100644 --- a/lyra-ecs/src/relation/relate_pair.rs +++ b/lyra-ecs/src/relation/relate_pair.rs @@ -23,7 +23,7 @@ where unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item { let comp: Ref> = self.col.get(entity.0 as usize); let rel_target = comp.target; - let rel_origin = self.arch.entity_of_index(entity).unwrap(); + let rel_origin = self.arch.entity_at_index(entity).unwrap(); let comp = Ref::map(comp, |r| &r.relation); (rel_origin, comp, rel_target) diff --git a/lyra-ecs/src/relation/relates_to.rs b/lyra-ecs/src/relation/relates_to.rs index 7cd6657..e6588f6 100644 --- a/lyra-ecs/src/relation/relates_to.rs +++ b/lyra-ecs/src/relation/relates_to.rs @@ -93,7 +93,7 @@ where /// This can be combined with Entities, to query all entities that have a relation targeting /// the target entity. /// -/// ```compile_fail +/// ```nobuild /// let v = world.view::() /// .relates_to::(b); /// diff --git a/lyra-ecs/src/system/criteria.rs b/lyra-ecs/src/system/criteria.rs index e33e5ae..e7dcf07 100644 --- a/lyra-ecs/src/system/criteria.rs +++ b/lyra-ecs/src/system/criteria.rs @@ -18,7 +18,7 @@ pub enum CriteriaSchedule { NoAndLoop, } -/// A Criteria can be used to conditionally execute [`BatchedSystems`](super::BatchedSystems). +/// A Criteria can be used to conditionally execute [`BatchedSystem`](super::BatchedSystem). pub trait Criteria { /// Checks if this Criteria can run, and if it should check it again. /// diff --git a/lyra-ecs/src/tick.rs b/lyra-ecs/src/tick.rs index 02dd4c7..bf3c916 100644 --- a/lyra-ecs/src/tick.rs +++ b/lyra-ecs/src/tick.rs @@ -1,9 +1,9 @@ use std::sync::atomic::{AtomicU64, Ordering}; -/// TickTracker is used for tracking changes of [`Component`]s and entities. +/// TickTracker is used for tracking changes of [`Component`](crate::Component)s and entities. /// /// TickTracker stores an [`AtomicU64`], making all operations on `TickTracker`, atomic as well. -/// Note that [`Tick::Clone`] only clones the inner value of atomic, and not the atomic itself. +/// Note that [`Tick::clone`] only clones the inner value of atomic, and not the atomic itself. #[derive(Debug, Default)] pub struct TickTracker { tick: AtomicU64, diff --git a/lyra-ecs/src/world.rs b/lyra-ecs/src/world.rs index 4472bb8..bc38646 100644 --- a/lyra-ecs/src/world.rs +++ b/lyra-ecs/src/world.rs @@ -54,7 +54,7 @@ impl World { } /// Spawn the components into a reserved entity. Only do this with entities that - /// were 'reserved' with [`World::reserve`] + /// were reserved with [`World::reserve_entity`]. /// /// # Safety /// Do not use this method with an entity that is currently alive, it WILL cause undefined behavior. @@ -73,14 +73,14 @@ impl World { if let Some(archetype) = archetype { // make at just one check to ensure you're not spawning twice - debug_assert!(!archetype.entities.contains_key(&entity), + debug_assert!(!archetype.entity_ids.contains_key(&entity), "You attempted to spawn components into an entity that already exists!"); let arche_idx = archetype.add_entity(entity, bundle, &tick); // Create entity record and store it let record = Record { - id: archetype.id, + id: archetype.id(), index: arche_idx, }; @@ -158,7 +158,7 @@ impl World { for (col_type, (col_ptr, col_info)) in orig_col.into_iter().zip(col_ptrs.into_iter()) { unsafe { let ptr = NonNull::new_unchecked(col_ptr.as_ptr() - .add(res_index.0 as usize * col_info.layout.size)); + .add(res_index.0 as usize * col_info.layout.size())); let col = arch.get_column_mut(col_type).unwrap(); col.set_at(res_index.0 as _, ptr, tick); } @@ -170,10 +170,10 @@ impl World { col.len += 1; }); - arch.entities.insert(entity, res_index); + arch.entity_ids.insert(entity, res_index); let new_record = Record { - id: arch.id, + id: arch.id(), index: res_index, }; self.entities.insert_entity_record(entity, new_record); @@ -264,7 +264,7 @@ impl World { /// Gets a resource from the World. /// - /// Will panic if the resource is not in the world. See [`try_get_resource`] for + /// Will panic if the resource is not in the world. See [`World::try_get_resource`] for /// a function that returns an option. pub fn get_resource(&self) -> Ref { self.resources.get(&TypeId::of::()) @@ -287,7 +287,7 @@ impl World { /// Gets a mutable borrow of a resource from the World. /// - /// Will panic if the resource is not in the world. See [`try_get_resource_mut`] for + /// Will panic if the resource is not in the world. See [`World::try_get_resource_mut`] for /// a function that returns an option. pub fn get_resource_mut(&self) -> RefMut { self.resources.get(&TypeId::of::())