diff --git a/lyra-ecs/README.md b/lyra-ecs/README.md index 1b853e6..7d84054 100644 --- a/lyra-ecs/README.md +++ b/lyra-ecs/README.md @@ -12,12 +12,14 @@ I couldn't find anything that fulfilled my needs, specifically an ECS that can s - [x] Find some way to fill in the gaps in component columns after entities are deleted * This was done by moving the last entity in the column to the gap that was just removed. - [x] Grow archetype as it fills up - - [x] Borrow safety of components inside Archetypes + - [ ] Borrow safety of components inside Archetypes + * Return `Ref` and `RefMut` from borrow queries - [ ] Querying components from archetypes - [x] Views - [ ] Mutable views - [ ] Make it possible so that ONLY `ViewMut` can borrow mutably -- [ ] Resources +- [x] Resources +- [ ] Get resources in views somehow - [ ] Relationships (maybe this can be done through queries, idk) - [ ] Dynamic queries * Needed for scripting engines that can create their own components that Rust does not know the types of. diff --git a/lyra-ecs/src/query/borrow.rs b/lyra-ecs/src/query/borrow.rs index 37946c4..23dcf9c 100644 --- a/lyra-ecs/src/query/borrow.rs +++ b/lyra-ecs/src/query/borrow.rs @@ -1,5 +1,7 @@ use std::{marker::PhantomData, any::TypeId, ptr::NonNull, cell::{Ref, RefCell, RefMut}}; +use crate::world::World; + use super::{Fetch, Query, AsQuery, DefaultQuery}; /// Fetcher for borrowing components from archetypes. @@ -66,7 +68,7 @@ where archetype.columns.iter().any(|c| c.info.type_id == self.type_id) } - unsafe fn fetch<'a>(&self, _arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> { + unsafe fn fetch<'a>(&self, _world: &'a World, _arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> { let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id) .expect("You ignored 'can_visit_archetype'!"); let col_data = col.borrow_ptr(); @@ -163,7 +165,7 @@ where archetype.columns.iter().any(|c| c.info.type_id == self.type_id) } - unsafe fn fetch<'a>(&self, _arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> { + unsafe fn fetch<'a>(&self, _world: &'a World, _arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> { let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id) .expect("You ignored 'can_visit_archetype'!"); let col_data = col.borrow_mut_ptr(); @@ -223,7 +225,7 @@ mod tests { _phantom: std::marker::PhantomData, }; let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); - let v = View::new(borrow, archetypes); + let v = View::new(&world, borrow, archetypes); for e in v.into_iter() { println!("Found entity at {:?}", e); @@ -239,7 +241,7 @@ mod tests { _phantom: std::marker::PhantomData, }; let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); - let v = View::new(borrow, archetypes); + let v = View::new(&world, borrow, archetypes); let mut orig = vec![]; @@ -256,7 +258,7 @@ mod tests { _phantom: std::marker::PhantomData, }; let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); - let v = View::new(borrow, archetypes); + let v = View::new(&world, borrow, archetypes); for (new, orig) in v.into_iter().zip(orig.iter()) { assert!(new.x - orig.x == 10.0); diff --git a/lyra-ecs/src/query/entities.rs b/lyra-ecs/src/query/entities.rs index c85e773..122d052 100644 --- a/lyra-ecs/src/query/entities.rs +++ b/lyra-ecs/src/query/entities.rs @@ -1,4 +1,4 @@ -use crate::{world::Entity, archetype::{Archetype, ArchetypeId}}; +use crate::{world::{Entity, World}, archetype::{Archetype, ArchetypeId}}; use super::{Fetch, Query}; @@ -33,7 +33,7 @@ impl Query for Entities { true } - unsafe fn fetch<'a>(&self, arch_id: ArchetypeId, archetype: &'a Archetype) -> Self::Fetch<'a> { + unsafe fn fetch<'a>(&self, _world: &'a World, arch_id: ArchetypeId, archetype: &'a Archetype) -> Self::Fetch<'a> { let _ = arch_id; // ignore unused warnings EntitiesFetch { entities: archetype.entities.keys().cloned().collect::>(), diff --git a/lyra-ecs/src/query/mod.rs b/lyra-ecs/src/query/mod.rs index 03e98fe..f2a92e3 100644 --- a/lyra-ecs/src/query/mod.rs +++ b/lyra-ecs/src/query/mod.rs @@ -1,4 +1,4 @@ -use crate::{archetype::{Archetype, ArchetypeId}, world::ArchetypeEntityId}; +use crate::{archetype::{Archetype, ArchetypeId}, world::{ArchetypeEntityId, World}}; pub mod view; pub use view::*; @@ -42,7 +42,13 @@ pub trait Query { /// Returns true if the archetype should be visited or skipped. fn can_visit_archetype(&self, archetype: &Archetype) -> bool; - unsafe fn fetch<'a>(&self, arch_id: ArchetypeId, archetype: &'a Archetype) -> Self::Fetch<'a>; + /// Returns true if the Query can visit the world + /* fn can_visit_world(&self, world: &'a World) -> bool { + let _ = world; // compiler warnings + false + } */ + + unsafe fn fetch<'a>(&self, world: &'a World, arch_id: ArchetypeId, archetype: &'a Archetype) -> Self::Fetch<'a>; } /// A trait for getting the query of a type. @@ -76,7 +82,7 @@ mod tests { let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); - let v = View::new(entities, archetypes); + let v = View::new(&world, entities, archetypes); for e in v.into_iter() { println!("Got entity! {:?}", e); diff --git a/lyra-ecs/src/query/tuple.rs b/lyra-ecs/src/query/tuple.rs index 7e3a5b7..d3beb47 100644 --- a/lyra-ecs/src/query/tuple.rs +++ b/lyra-ecs/src/query/tuple.rs @@ -1,3 +1,5 @@ +use crate::world::World; + use super::{Query, Fetch, AsQuery, DefaultQuery}; // Technically all of these implementations for a 2-sized tuple @@ -40,9 +42,9 @@ where q1.can_visit_archetype(archetype) && q2.can_visit_archetype(archetype) } - unsafe fn fetch<'a>(&self, arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> { + unsafe fn fetch<'a>(&self, world: &'a World, arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> { let (q1, q2) = self; - ( q1.fetch(arch_id, archetype), q2.fetch(arch_id, archetype) ) + ( q1.fetch(world, arch_id, archetype), q2.fetch(world, arch_id, archetype) ) } } @@ -101,9 +103,9 @@ macro_rules! impl_bundle_tuple { bools.iter().all(|b| *b) } - unsafe fn fetch<'a>(&self, arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> { + unsafe fn fetch<'a>(&self, world: &'a World, arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> { let ( $($name,)+ ) = self; - ( $($name.fetch(arch_id, archetype),)+ ) + ( $($name.fetch(world, arch_id, archetype),)+ ) } } diff --git a/lyra-ecs/src/query/view.rs b/lyra-ecs/src/query/view.rs index a50e763..79b6fd6 100644 --- a/lyra-ecs/src/query/view.rs +++ b/lyra-ecs/src/query/view.rs @@ -1,10 +1,11 @@ use std::ops::Range; -use crate::{archetype::{Archetype, ArchetypeId}, world::ArchetypeEntityId}; +use crate::{archetype::{Archetype, ArchetypeId}, world::{ArchetypeEntityId, World}}; use super::{Query, Fetch}; pub struct View<'a, Q: Query> { + world: &'a World, query: Q, archetypes: Vec<&'a Archetype>, } @@ -13,8 +14,9 @@ impl<'a, Q> View<'a, Q> where Q: Query, { - pub fn new(query: Q, archetypes: Vec<&'a Archetype>) -> Self { + pub fn new(world: &'a World, query: Q, archetypes: Vec<&'a Archetype>) -> Self { Self { + world, query, archetypes, } @@ -31,6 +33,7 @@ where fn into_iter(self) -> Self::IntoIter { ViewIter { + world: self.world, query: self.query, fetcher: Q::Fetch::dangling(), archetypes: self.archetypes, @@ -41,6 +44,7 @@ where } pub struct ViewIter<'a, Q: Query> { + world: &'a World, query: Q, fetcher: Q::Fetch<'a>, archetypes: Vec<&'a Archetype>, @@ -81,7 +85,7 @@ where continue; } - self.fetcher = unsafe { self.query.fetch(ArchetypeId(arch_id as u64), arch) }; + self.fetcher = unsafe { self.query.fetch(self.world, ArchetypeId(arch_id as u64), arch) }; self.component_indices = 0..arch.entities.len() as u64; } } diff --git a/lyra-ecs/src/resource.rs b/lyra-ecs/src/resource.rs index a8e7a32..20130c0 100644 --- a/lyra-ecs/src/resource.rs +++ b/lyra-ecs/src/resource.rs @@ -14,13 +14,13 @@ pub struct ResourceData { } impl ResourceData { - pub fn new(mut data: T) -> Self { + pub fn new(data: T) -> Self { let layout = Layout::new::(); - + let data = NonNull::from(&data).cast(); + let ptr = unsafe { if let Some(ptr) = NonNull::new(alloc::alloc(layout)) { - let data = (&mut data as *mut T) as *mut u8; // thx i hate it - std::ptr::copy_nonoverlapping(data, ptr.as_ptr(), 1); + std::ptr::copy_nonoverlapping(data.as_ptr(), ptr.as_ptr(), layout.size()); ptr } else { alloc::handle_alloc_error(layout) @@ -53,13 +53,41 @@ impl ResourceData { /// Mutably borrow the data inside of the resource. /// - /// # Safety + /// # Panics /// - /// If the type of T is not the same as the expected type, it will panic. + /// * If the type of T is not the same as the expected type, it will panic. + /// * If the data is already borrowed mutably, this will panic. pub fn get_mut<'a, T: 'static>(&'a self) -> RefMut<'a, T> { assert!(TypeId::of::() == self.type_id); let data = self.data.borrow_mut(); RefMut::map(data, |ptr| unsafe { &mut *ptr.cast().as_ptr() }) } + + /// Borrow the data inside of the resource. + /// + /// # Safety + /// + /// If the type of T is not the same as the expected type, it will panic. + pub fn try_get<'a, T: 'static>(&'a self) -> Option> { + assert!(TypeId::of::() == self.type_id); + + self.data.try_borrow() + .map(|r| Ref::map(r, |ptr| unsafe { &*ptr.cast().as_ptr() })) + .ok() + } + + /// Mutably borrow the data inside of the resource. + /// + /// # Panics + /// + /// * If the type of T is not the same as the expected type, it will panic. + /// * If the data is already borrowed mutably, this will panic. + pub fn try_get_mut<'a, T: 'static>(&'a self) -> Option> { + assert!(TypeId::of::() == self.type_id); + + self.data.try_borrow_mut() + .map(|r| RefMut::map(r, |ptr| unsafe { &mut *ptr.cast().as_ptr() })) + .ok() + } } \ No newline at end of file diff --git a/lyra-ecs/src/world.rs b/lyra-ecs/src/world.rs index a9f5852..1cd1109 100644 --- a/lyra-ecs/src/world.rs +++ b/lyra-ecs/src/world.rs @@ -117,7 +117,7 @@ impl World { pub fn view<'a, T: 'static + Component + DefaultQuery>(&'a self) -> ViewIter<'a, T::Query> { let archetypes = self.archetypes.values().collect(); - let v = View::new(T::default_query(), archetypes); + let v = View::new(self, T::default_query(), archetypes); v.into_iter() } @@ -139,7 +139,8 @@ impl World { /// Returns `None` if the resource was not found. pub fn try_get_resource<'a, T: 'static>(&'a self) -> Option> { self.resources.get(&TypeId::of::()) - .map(|r| r.get()) + .map(|r| r.try_get()) + .flatten() } /// Gets a mutable borrow of a resource from the World. @@ -156,7 +157,8 @@ impl World { /// Returns `None` if the resource was not found. pub fn try_get_resource_mut<'a, T: 'static>(&'a self) -> Option> { self.resources.get(&TypeId::of::()) - .map(|r| r.get_mut()) + .map(|r| r.try_get_mut()) + .flatten() } } @@ -166,6 +168,8 @@ mod tests { use super::World; + struct SimpleCounter(i32); + #[test] fn spawning_entity() { let mut world = World::new(); @@ -214,8 +218,6 @@ mod tests { #[test] fn simple_resource() { - struct SimpleCounter(i32); - let mut world = World::new(); { let counter = SimpleCounter(0); @@ -223,16 +225,44 @@ mod tests { } let counter = world.get_resource::(); - assert!(counter.0 == 0); + assert_eq!(counter.0, 0); drop(counter); let mut counter = world.get_resource_mut::(); counter.0 += 4582; drop(counter); - let counter = world.get_resource::(); - assert!(counter.0 == 4582); - assert!(world.try_get_resource::().is_none()); } + + #[test] + fn resource_multi_borrow() { + let mut world = World::new(); + { + let counter = SimpleCounter(4582); + world.add_resource(counter); + } + + // test multiple borrows at the same time + let counter = world.get_resource::(); + assert_eq!(counter.0, 4582); + let counter2 = world.get_resource::(); + assert_eq!(counter2.0, 4582); + assert_eq!(counter2.0, 4582); + } + + #[test] + fn resource_one_mutable_borrow() { + let mut world = World::new(); + { + let counter = SimpleCounter(4582); + world.add_resource(counter); + } + + // test multiple borrows at the same time + let counter = world.get_resource_mut::(); + assert_eq!(counter.0, 4582); + assert!(world.try_get_resource_mut::().is_none()); + assert_eq!(counter.0, 4582); + } } \ No newline at end of file