ecs: Add world query, make it possible to request resources from function systems

This commit is contained in:
SeanOMik 2024-01-04 20:49:27 -05:00
parent 74f43e9ffe
commit 0f062217ca
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
9 changed files with 226 additions and 41 deletions

View File

@ -1,6 +1,6 @@
use std::{marker::PhantomData, ptr::NonNull, cell::{Ref, RefMut}}; use std::{marker::PhantomData, ptr::NonNull, cell::{Ref, RefMut}};
use crate::{world::World, ComponentColumn, DynTypeId, Tick}; use crate::{world::World, ComponentColumn, DynTypeId, Tick, Component};
use super::{Fetch, Query, AsQuery}; use super::{Fetch, Query, AsQuery};
@ -97,11 +97,11 @@ where
} }
} }
impl<T: 'static> AsQuery for QueryBorrow<T> { impl<T: Component> AsQuery for QueryBorrow<T> {
type Query = Self; type Query = Self;
} }
impl<T: 'static> AsQuery for &T { impl<T: Component> AsQuery for &T {
type Query = QueryBorrow<T>; type Query = QueryBorrow<T>;
} }
@ -209,11 +209,11 @@ where
} }
} }
impl<T: 'static> AsQuery for QueryBorrowMut<T> { impl<T: Component> AsQuery for QueryBorrowMut<T> {
type Query = Self; type Query = Self;
} }
impl<T: 'static> AsQuery for &mut T { impl<T: Component> AsQuery for &mut T {
type Query = QueryBorrowMut<T>; type Query = QueryBorrowMut<T>;
} }
@ -244,7 +244,7 @@ mod tests {
_phantom: std::marker::PhantomData, _phantom: std::marker::PhantomData,
}; };
let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
let v = View::new(&world, borrow, archetypes); let v = View::<QueryBorrow<Vec2>>::new(&world, borrow, archetypes);
for e in v.into_iter() { for e in v.into_iter() {
println!("Found entity at {:?}", e); println!("Found entity at {:?}", e);
@ -260,7 +260,7 @@ mod tests {
_phantom: std::marker::PhantomData, _phantom: std::marker::PhantomData,
}; };
let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
let v = View::new(&world, borrow, archetypes); let v = View::<QueryBorrowMut<Vec2>>::new(&world, borrow, archetypes);
let mut orig = vec![]; let mut orig = vec![];
@ -277,7 +277,7 @@ mod tests {
_phantom: std::marker::PhantomData, _phantom: std::marker::PhantomData,
}; };
let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
let v = View::new(&world, borrow, archetypes); let v = View::<QueryBorrow<Vec2>>::new(&world, borrow, archetypes);
for (new, orig) in v.into_iter().zip(orig.iter()) { for (new, orig) in v.into_iter().zip(orig.iter()) {
assert!(new.x - orig.x == 10.0); assert!(new.x - orig.x == 10.0);

View File

@ -23,6 +23,10 @@ pub mod tick;
#[allow(unused_imports)] #[allow(unused_imports)]
pub use tick::*; pub use tick::*;
pub mod world;
#[allow(unused_imports)]
pub use world::*;
pub mod dynamic; pub mod dynamic;
/// A [`Fetch`]er implementation gets data out of an archetype. /// A [`Fetch`]er implementation gets data out of an archetype.
@ -100,7 +104,7 @@ mod tests {
let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
let v = View::new(&world, entities, archetypes); let v = View::<Entities>::new(&world, entities, archetypes);
for e in v.into_iter() { for e in v.into_iter() {
println!("Got entity! {:?}", e); println!("Got entity! {:?}", e);

View File

@ -10,7 +10,7 @@ pub struct FetchResource<'a, T> {
} }
impl<'a, T: 'a + 'static> Fetch<'a> for FetchResource<'a, T> { impl<'a, T: 'a + 'static> Fetch<'a> for FetchResource<'a, T> {
type Item = Ref<'a, T>; type Item = Res<'a, T>;
fn dangling() -> Self { fn dangling() -> Self {
Self { Self {
@ -25,7 +25,7 @@ impl<'a, T: 'a + 'static> Fetch<'a> for FetchResource<'a, T> {
unsafe fn get_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> Self::Item { unsafe fn get_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> Self::Item {
let w = self.world.unwrap(); let w = self.world.unwrap();
w.get_resource::<T>() Res(w.get_resource::<T>())
} }
} }
@ -34,7 +34,7 @@ impl<'a, T: 'a + 'static> Fetch<'a> for FetchResource<'a, T> {
pub struct QueryResource<T: ResourceObject> { pub struct QueryResource<T: ResourceObject> {
_phantom: PhantomData<T> _phantom: PhantomData<T>
} }
pub type Resource<T> = QueryResource<T>; //pub type Resource<T> = QueryResource<T>;
impl<T: ResourceObject> Copy for QueryResource<T> {} impl<T: ResourceObject> Copy for QueryResource<T> {}
@ -45,7 +45,7 @@ impl<T: ResourceObject> Clone for QueryResource<T> {
} }
impl<T: ResourceObject + 'static> Query for QueryResource<T> { impl<T: ResourceObject + 'static> Query for QueryResource<T> {
type Item<'a> = Ref<'a, T>; type Item<'a> = Res<'a, T>;
type Fetch<'a> = FetchResource<'a, T>; type Fetch<'a> = FetchResource<'a, T>;
@ -78,13 +78,28 @@ impl<R: ResourceObject> AsQuery for QueryResource<R> {
type Query = QueryResource<R>; type Query = QueryResource<R>;
} }
/// A struct used for querying resources from the World.
pub struct Res<'a, T>(Ref<'a, T>);
impl<'a, T: ResourceObject> std::ops::Deref for Res<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl<'a, T: ResourceObject> AsQuery for Res<'a, T> {
type Query = QueryResource<T>;
}
pub struct FetchResourceMut<'a, T> { pub struct FetchResourceMut<'a, T> {
world: Option<&'a World>, world: Option<&'a World>,
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<'a, T: 'a + 'static> Fetch<'a> for FetchResourceMut<'a, T> { impl<'a, T: 'a + 'static> Fetch<'a> for FetchResourceMut<'a, T> {
type Item = RefMut<'a, T>; type Item = ResMut<'a, T>;
fn dangling() -> Self { fn dangling() -> Self {
Self { Self {
@ -99,7 +114,7 @@ impl<'a, T: 'a + 'static> Fetch<'a> for FetchResourceMut<'a, T> {
unsafe fn get_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> Self::Item { unsafe fn get_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> Self::Item {
let w = self.world.unwrap(); let w = self.world.unwrap();
w.get_resource_mut::<T>() ResMut(w.get_resource_mut::<T>())
} }
} }
@ -109,8 +124,6 @@ pub struct QueryResourceMut<T: ResourceObject> {
_phantom: PhantomData<T> _phantom: PhantomData<T>
} }
pub type ResourceMut<T> = QueryResourceMut<T>;
impl<T: ResourceObject> Copy for QueryResourceMut<T> {} impl<T: ResourceObject> Copy for QueryResourceMut<T> {}
impl<T: ResourceObject> Clone for QueryResourceMut<T> { impl<T: ResourceObject> Clone for QueryResourceMut<T> {
@ -120,7 +133,7 @@ impl<T: ResourceObject> Clone for QueryResourceMut<T> {
} }
impl<T: ResourceObject + 'static> Query for QueryResourceMut<T> { impl<T: ResourceObject + 'static> Query for QueryResourceMut<T> {
type Item<'a> = RefMut<'a, T>; type Item<'a> = ResMut<'a, T>;
type Fetch<'a> = FetchResourceMut<'a, T>; type Fetch<'a> = FetchResourceMut<'a, T>;
@ -152,8 +165,33 @@ impl<T: ResourceObject + 'static> Query for QueryResourceMut<T> {
impl<R: ResourceObject> AsQuery for QueryResourceMut<R> { impl<R: ResourceObject> AsQuery for QueryResourceMut<R> {
type Query = QueryResourceMut<R>; type Query = QueryResourceMut<R>;
} }
/// A struct used for querying resources from the World.
pub struct ResMut<'a, T>(RefMut<'a, T>);
impl<'a, T: ResourceObject> std::ops::Deref for ResMut<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl<'a, T: ResourceObject> std::ops::DerefMut for ResMut<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.deref_mut()
}
}
impl<'a, T: ResourceObject> AsQuery for ResMut<'a, T> {
type Query = QueryResourceMut<T>;
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::ops::{Deref, DerefMut};
use crate::{world::World, tests::{Vec2, Vec3}, query::QueryResourceMut}; use crate::{world::World, tests::{Vec2, Vec3}, query::QueryResourceMut};
use super::QueryResource; use super::QueryResource;
@ -171,6 +209,7 @@ mod tests {
let mut res_iter = world.view_iter::<QueryResource<SomeCounter>>(); let mut res_iter = world.view_iter::<QueryResource<SomeCounter>>();
let res = res_iter.next().unwrap(); let res = res_iter.next().unwrap();
let res = res.deref();
assert_eq!(res.0, 0); assert_eq!(res.0, 0);
} }
@ -195,7 +234,7 @@ mod tests {
assert_eq!(i.count(), 3); assert_eq!(i.count(), 3);
for (res, e) in world.view_iter::<(QueryResource<SomeCounter>, &Vec2)>() { for (res, e) in world.view_iter::<(QueryResource<SomeCounter>, &Vec2)>() {
println!("Got res {}! and entity at {:?}", res.0, e); println!("Got res {}! and entity at {:?}", res.deref().0, e);
} }
let i = world.view_iter::<QueryResource<SomeCounter>>(); let i = world.view_iter::<QueryResource<SomeCounter>>();
@ -214,12 +253,14 @@ mod tests {
{ {
let mut resmut_iter = world.view_iter::<QueryResourceMut<SomeCounter>>(); let mut resmut_iter = world.view_iter::<QueryResourceMut<SomeCounter>>();
let mut resmut = resmut_iter.next().unwrap(); let mut resmut = resmut_iter.next().unwrap();
let resmut = resmut.deref_mut();
assert_eq!(resmut.0, 0); assert_eq!(resmut.0, 0);
resmut.0 += 20; resmut.0 += 20;
} }
let mut res_iter = world.view_iter::<QueryResource<SomeCounter>>(); let mut res_iter = world.view_iter::<QueryResource<SomeCounter>>();
let res = res_iter.next().unwrap(); let res = res_iter.next().unwrap();
let res = res.deref();
assert_eq!(res.0, 20); assert_eq!(res.0, 20);
} }
} }

View File

@ -2,19 +2,19 @@ use std::ops::Range;
use crate::{archetype::Archetype, world::{ArchetypeEntityId, World}, EntityId, Tick}; use crate::{archetype::Archetype, world::{ArchetypeEntityId, World}, EntityId, Tick};
use super::{Query, Fetch}; use super::{Query, Fetch, AsQuery};
pub struct View<'a, Q: Query> { pub struct View<'a, Q: AsQuery> {
world: &'a World, world: &'a World,
query: Q, query: Q::Query,
archetypes: Vec<&'a Archetype>, archetypes: Vec<&'a Archetype>,
} }
impl<'a, Q> View<'a, Q> impl<'a, Q> View<'a, Q>
where where
Q: Query, Q: AsQuery,
{ {
pub fn new(world: &'a World, query: Q, archetypes: Vec<&'a Archetype>) -> Self { pub fn new(world: &'a World, query: Q::Query, archetypes: Vec<&'a Archetype>) -> Self {
Self { Self {
world, world,
query, query,
@ -25,14 +25,14 @@ where
impl<'a, Q> IntoIterator for View<'a, Q> impl<'a, Q> IntoIterator for View<'a, Q>
where where
Q: Query, Q: AsQuery,
{ {
type Item = Q::Item<'a>; type Item = <Q::Query as Query>::Item<'a>;
type IntoIter = ViewIter<'a, Q>; type IntoIter = ViewIter<'a, Q::Query>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
let tick = self.world.tick_tracker().tick_when(Q::MUTATES); let tick = self.world.tick_tracker().tick_when(Q::Query::MUTATES);
ViewIter { ViewIter {
world: self.world, world: self.world,

140
lyra-ecs/src/query/world.rs Normal file
View File

@ -0,0 +1,140 @@
use std::ptr::NonNull;
use crate::World;
use super::{Fetch, Query, AsQuery};
pub struct FetchWorldRef<'a> {
world: &'a World
}
impl<'a> Fetch<'a> for FetchWorldRef<'a> {
type Item = &'a World;
fn dangling() -> Self {
unreachable!()
}
fn can_visit_item(&mut self, _entity: crate::ArchetypeEntityId) -> bool {
true
}
unsafe fn get_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> Self::Item {
self.world
}
}
#[derive(Clone, Copy)]
pub struct QueryWorldRef;
impl Default for QueryWorldRef {
fn default() -> Self {
Self
}
}
impl Query for QueryWorldRef {
type Item<'a> = &'a World;
type Fetch<'a> = FetchWorldRef<'a>;
const ALWAYS_FETCHES: bool = true;
fn new() -> Self {
QueryWorldRef
}
fn can_visit_archetype(&self, _archetype: &crate::archetype::Archetype) -> bool {
true
}
unsafe fn fetch<'a>(&self, world: &'a World, _archetype: &'a crate::archetype::Archetype, _tick: crate::Tick) -> Self::Fetch<'a> {
FetchWorldRef {
world
}
}
unsafe fn fetch_world<'a>(&self, world: &'a World) -> Option<Self::Fetch<'a>> {
Some(FetchWorldRef {
world,
})
}
}
impl AsQuery for QueryWorldRef {
type Query = Self;
}
impl AsQuery for &World {
type Query = QueryWorldRef;
}
pub struct FetchWorldRefMut<'a> {
world: &'a mut World
}
impl<'a> Fetch<'a> for FetchWorldRefMut<'a> {
type Item = &'a mut World;
fn dangling() -> Self {
unreachable!()
}
fn can_visit_item(&mut self, _entity: crate::ArchetypeEntityId) -> bool {
true
}
unsafe fn get_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> Self::Item {
// safety: world outlives the fetcher
let mut world_ptr = NonNull::from(&self.world);
unsafe { world_ptr.as_mut() }
}
}
#[derive(Clone, Copy)]
pub struct QueryWorldRefMut;
impl Default for QueryWorldRefMut {
fn default() -> Self {
Self
}
}
impl Query for QueryWorldRefMut {
type Item<'a> = &'a mut World;
type Fetch<'a> = FetchWorldRefMut<'a>;
const ALWAYS_FETCHES: bool = true;
fn new() -> Self {
QueryWorldRefMut
}
fn can_visit_archetype(&self, _archetype: &crate::archetype::Archetype) -> bool {
true
}
unsafe fn fetch<'a>(&self, world: &'a World, _archetype: &'a crate::archetype::Archetype, _tick: crate::Tick) -> Self::Fetch<'a> {
self.fetch_world(world).unwrap()
}
unsafe fn fetch_world<'a>(&self, world: &'a World) -> Option<Self::Fetch<'a>> {
// safety: the queries are expected to be executed by an executor that would
// not cause multiple mutable borrows
let mut world_ptr = NonNull::from(world);
let world = unsafe { world_ptr.as_mut() };
Some(FetchWorldRefMut {
world,
})
}
}
impl AsQuery for QueryWorldRefMut {
type Query = Self;
}
impl AsQuery for &mut World {
type Query = QueryWorldRefMut;
}

View File

@ -1,8 +1,8 @@
use std::{any::{TypeId, Any}, cell::{RefCell, Ref, RefMut}}; use std::{any::{TypeId, Any}, cell::{RefCell, Ref, RefMut}};
/// Shorthand for `Send + Sync + 'static`, so it never needs to be implemented manually. /// Shorthand for `Send + Sync + 'static`, so it never needs to be implemented manually.
pub trait ResourceObject: Send + Sync + 'static {} pub trait ResourceObject: 'static {}
impl<T: Send + Sync + 'static> ResourceObject for T {} impl<T: 'static> ResourceObject for T {}
/// A type erased storage for a Resource. /// A type erased storage for a Resource.
pub struct ResourceData { pub struct ResourceData {

View File

@ -108,7 +108,7 @@ impl GraphExecutor {
mod tests { mod tests {
use std::ptr::NonNull; use std::ptr::NonNull;
use crate::{world::World, query::{ResourceMut, View}, system::IntoSystem}; use crate::{world::World, query::{ResMut, View}, system::IntoSystem};
use super::GraphExecutor; use super::GraphExecutor;
@ -120,7 +120,7 @@ mod tests {
let mut exec = GraphExecutor::new(); let mut exec = GraphExecutor::new();
let a_system = |view: View<ResourceMut<Vec<String>>>| -> anyhow::Result<()> { let a_system = |view: View<ResMut<Vec<String>>>| -> anyhow::Result<()> {
println!("System 'a' ran!"); println!("System 'a' ran!");
let mut order = view.into_iter().next().unwrap(); let mut order = view.into_iter().next().unwrap();
@ -129,7 +129,7 @@ mod tests {
Ok(()) Ok(())
}; };
let b_system = |view: View<ResourceMut<Vec<String>>>| -> anyhow::Result<()> { let b_system = |view: View<ResMut<Vec<String>>>| -> anyhow::Result<()> {
println!("System 'b' ran!"); println!("System 'b' ran!");
let mut order = view.into_iter().next().unwrap(); let mut order = view.into_iter().next().unwrap();
@ -138,7 +138,7 @@ mod tests {
Ok(()) Ok(())
}; };
let c_system = |view: View<ResourceMut<Vec<String>>>| -> anyhow::Result<()> { let c_system = |view: View<ResMut<Vec<String>>>| -> anyhow::Result<()> {
println!("System 'c' ran!"); println!("System 'c' ran!");
let mut order = view.into_iter().next().unwrap(); let mut order = view.into_iter().next().unwrap();

View File

@ -1,6 +1,6 @@
use std::{ptr::NonNull, marker::PhantomData, cell::{Ref, RefMut}}; use std::{ptr::NonNull, marker::PhantomData, cell::{Ref, RefMut}};
use crate::{world::World, Access, ResourceObject, query::{Query, View}}; use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery}};
pub mod graph; pub mod graph;
pub use graph::*; pub use graph::*;
@ -131,20 +131,20 @@ impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O }
impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O, P } impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O, P }
/// An ArgFetcher implementation for query [`View`]s /// An ArgFetcher implementation for query [`View`]s
pub struct ViewArgFetcher<Q> { pub struct ViewArgFetcher<Q: AsQuery> {
query: Q query: Q::Query
} }
impl<'a, Q: Query> FnArg for View<'a, Q> { impl<'a, Q: AsQuery> FnArg for View<'a, Q> {
type Fetcher = ViewArgFetcher<Q>; type Fetcher = ViewArgFetcher<Q>;
} }
impl<Q: Query> FnArgFetcher for ViewArgFetcher<Q> { impl<Q: AsQuery> FnArgFetcher for ViewArgFetcher<Q> {
type Arg<'a> = View<'a, Q>; type Arg<'a> = View<'a, Q>;
fn new() -> Self { fn new() -> Self {
ViewArgFetcher { ViewArgFetcher {
query: Q::new(), query: <Q::Query as Query>::new(),
} }
} }

View File

@ -213,7 +213,7 @@ impl World {
/// View into the world for a set of entities that satisfy the queries. /// View into the world for a set of entities that satisfy the queries.
pub fn view_iter<T: 'static + AsQuery>(&self) -> ViewIter<T::Query> { pub fn view_iter<T: 'static + AsQuery>(&self) -> ViewIter<T::Query> {
let archetypes = self.archetypes.values().collect(); let archetypes = self.archetypes.values().collect();
let v = View::new(self, T::Query::new(), archetypes); let v = View::<T>::new(self, T::Query::new(), archetypes);
v.into_iter() v.into_iter()
} }