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 crate::{world::World, ComponentColumn, DynTypeId, Tick};
use crate::{world::World, ComponentColumn, DynTypeId, Tick, Component};
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;
}
impl<T: 'static> AsQuery for &T {
impl<T: Component> AsQuery for &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;
}
impl<T: 'static> AsQuery for &mut T {
impl<T: Component> AsQuery for &mut T {
type Query = QueryBorrowMut<T>;
}
@ -244,7 +244,7 @@ mod tests {
_phantom: std::marker::PhantomData,
};
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() {
println!("Found entity at {:?}", e);
@ -260,7 +260,7 @@ mod tests {
_phantom: std::marker::PhantomData,
};
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![];
@ -277,7 +277,7 @@ mod tests {
_phantom: std::marker::PhantomData,
};
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()) {
assert!(new.x - orig.x == 10.0);

View File

@ -23,6 +23,10 @@ pub mod tick;
#[allow(unused_imports)]
pub use tick::*;
pub mod world;
#[allow(unused_imports)]
pub use world::*;
pub mod dynamic;
/// 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 v = View::new(&world, entities, archetypes);
let v = View::<Entities>::new(&world, entities, archetypes);
for e in v.into_iter() {
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> {
type Item = Ref<'a, T>;
type Item = Res<'a, T>;
fn dangling() -> 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 {
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> {
_phantom: PhantomData<T>
}
pub type Resource<T> = QueryResource<T>;
//pub type Resource<T> = 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> {
type Item<'a> = Ref<'a, T>;
type Item<'a> = Res<'a, T>;
type Fetch<'a> = FetchResource<'a, T>;
@ -78,13 +78,28 @@ impl<R: ResourceObject> AsQuery for 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> {
world: Option<&'a World>,
_phantom: PhantomData<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 {
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 {
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>
}
pub type ResourceMut<T> = QueryResourceMut<T>;
impl<T: ResourceObject> Copy 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> {
type Item<'a> = RefMut<'a, T>;
type Item<'a> = ResMut<'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> {
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)]
mod tests {
use std::ops::{Deref, DerefMut};
use crate::{world::World, tests::{Vec2, Vec3}, query::QueryResourceMut};
use super::QueryResource;
@ -171,6 +209,7 @@ mod tests {
let mut res_iter = world.view_iter::<QueryResource<SomeCounter>>();
let res = res_iter.next().unwrap();
let res = res.deref();
assert_eq!(res.0, 0);
}
@ -195,7 +234,7 @@ mod tests {
assert_eq!(i.count(), 3);
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>>();
@ -214,12 +253,14 @@ mod tests {
{
let mut resmut_iter = world.view_iter::<QueryResourceMut<SomeCounter>>();
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::<QueryResource<SomeCounter>>();
let res = res_iter.next().unwrap();
let res = res.deref();
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 super::{Query, Fetch};
use super::{Query, Fetch, AsQuery};
pub struct View<'a, Q: Query> {
pub struct View<'a, Q: AsQuery> {
world: &'a World,
query: Q,
query: Q::Query,
archetypes: Vec<&'a Archetype>,
}
impl<'a, Q> View<'a, Q>
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 {
world,
query,
@ -25,14 +25,14 @@ where
impl<'a, Q> IntoIterator for View<'a, Q>
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 {
let tick = self.world.tick_tracker().tick_when(Q::MUTATES);
let tick = self.world.tick_tracker().tick_when(Q::Query::MUTATES);
ViewIter {
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}};
/// Shorthand for `Send + Sync + 'static`, so it never needs to be implemented manually.
pub trait ResourceObject: Send + Sync + 'static {}
impl<T: Send + Sync + 'static> ResourceObject for T {}
pub trait ResourceObject: 'static {}
impl<T: 'static> ResourceObject for T {}
/// A type erased storage for a Resource.
pub struct ResourceData {

View File

@ -108,7 +108,7 @@ impl GraphExecutor {
mod tests {
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;
@ -120,7 +120,7 @@ mod tests {
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!");
let mut order = view.into_iter().next().unwrap();
@ -129,7 +129,7 @@ mod tests {
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!");
let mut order = view.into_iter().next().unwrap();
@ -138,7 +138,7 @@ mod tests {
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!");
let mut order = view.into_iter().next().unwrap();

View File

@ -1,6 +1,6 @@
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 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 }
/// An ArgFetcher implementation for query [`View`]s
pub struct ViewArgFetcher<Q> {
query: Q
pub struct ViewArgFetcher<Q: AsQuery> {
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>;
}
impl<Q: Query> FnArgFetcher for ViewArgFetcher<Q> {
impl<Q: AsQuery> FnArgFetcher for ViewArgFetcher<Q> {
type Arg<'a> = View<'a, Q>;
fn new() -> Self {
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.
pub fn view_iter<T: 'static + AsQuery>(&self) -> ViewIter<T::Query> {
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()
}