Implement relationships in the ECS #3
|
@ -1,4 +1,4 @@
|
||||||
use std::{ptr::{NonNull, self}, alloc::{self, Layout, alloc, dealloc}, mem, collections::HashMap, cell::{RefCell, Ref, RefMut, BorrowError, BorrowMutError}, ops::{DerefMut, Deref}};
|
use std::{ptr::{NonNull, self}, alloc::{self, Layout, alloc, dealloc}, mem, collections::HashMap, cell::{RefCell, Ref, RefMut, BorrowError, BorrowMutError}, ops::DerefMut};
|
||||||
|
|
||||||
use crate::{world::ArchetypeEntityId, bundle::Bundle, component_info::ComponentInfo, DynTypeId, Tick, Entity};
|
use crate::{world::ArchetypeEntityId, bundle::Bundle, component_info::ComponentInfo, DynTypeId, Tick, Entity};
|
||||||
|
|
||||||
|
@ -81,14 +81,15 @@ impl ComponentColumn {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This column MUST have the entity. If it does not, it WILL NOT panic and will cause UB.
|
/// This column MUST have the entity. If it does not, it WILL NOT panic and will cause UB.
|
||||||
pub unsafe fn get<T>(&self, entity_index: usize) -> &T {
|
pub unsafe fn get<T>(&self, entity_index: usize) -> Ref<T> {
|
||||||
let data = self.data.borrow();
|
let data = self.data.borrow();
|
||||||
let data = data.deref();
|
|
||||||
|
|
||||||
|
Ref::map(data, |data| {
|
||||||
let ptr = NonNull::new_unchecked(data.as_ptr()
|
let ptr = NonNull::new_unchecked(data.as_ptr()
|
||||||
.add(entity_index * self.info.layout.size))
|
.add(entity_index * self.info.layout.size))
|
||||||
.cast();
|
.cast();
|
||||||
&*ptr.as_ptr()
|
&*ptr.as_ptr()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a component at an entities index.
|
/// Get a component at an entities index.
|
||||||
|
@ -411,11 +412,15 @@ impl Archetype {
|
||||||
pub fn entity_of_index(&self, id: ArchetypeEntityId) -> Option<Entity> {
|
pub fn entity_of_index(&self, id: ArchetypeEntityId) -> Option<Entity> {
|
||||||
self.ids_to_entity.get(&id).cloned()
|
self.ids_to_entity.get(&id).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_entity(&self, e: Entity) -> bool {
|
||||||
|
self.entities.contains_key(&e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{alloc::Layout, ptr::NonNull};
|
use std::{alloc::Layout, cell::Ref, ptr::NonNull};
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
|
@ -435,7 +440,7 @@ mod tests {
|
||||||
let entity_arch_id = a.add_entity(entity, bundle, &Tick::default());
|
let entity_arch_id = a.add_entity(entity, bundle, &Tick::default());
|
||||||
|
|
||||||
let col = a.columns.get(0).unwrap();
|
let col = a.columns.get(0).unwrap();
|
||||||
let vec2: &Vec2 = unsafe { col.get(entity_arch_id.0 as usize) };
|
let vec2: Ref<Vec2> = unsafe { col.get(entity_arch_id.0 as usize) };
|
||||||
assert_eq!(vec2.clone(), bundle.0);
|
assert_eq!(vec2.clone(), bundle.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,11 +456,11 @@ mod tests {
|
||||||
let entity_arch_id = a.add_entity(entity, bundle, &Tick::default());
|
let entity_arch_id = a.add_entity(entity, bundle, &Tick::default());
|
||||||
|
|
||||||
let col = a.columns.get(0).unwrap();
|
let col = a.columns.get(0).unwrap();
|
||||||
let vec2: &Vec2 = unsafe { col.get(entity_arch_id.0 as usize) };
|
let vec2: Ref<Vec2> = unsafe { col.get(entity_arch_id.0 as usize) };
|
||||||
assert_eq!(vec2.clone(), bundle.0);
|
assert_eq!(vec2.clone(), bundle.0);
|
||||||
|
|
||||||
let col = a.columns.get(1).unwrap();
|
let col = a.columns.get(1).unwrap();
|
||||||
let vec3: &Vec3 = unsafe { col.get(entity_arch_id.0 as usize) };
|
let vec3: Ref<Vec3> = unsafe { col.get(entity_arch_id.0 as usize) };
|
||||||
assert_eq!(vec3.clone(), bundle.1);
|
assert_eq!(vec3.clone(), bundle.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -477,9 +482,9 @@ mod tests {
|
||||||
let earch2 = a.add_entity(e2, b2, &Tick::default());
|
let earch2 = a.add_entity(e2, b2, &Tick::default());
|
||||||
|
|
||||||
let col = a.columns.get(0).unwrap();
|
let col = a.columns.get(0).unwrap();
|
||||||
let vec2: &Vec2 = unsafe { col.get(earch1.0 as usize) };
|
let vec2: Ref<Vec2> = unsafe { col.get(earch1.0 as usize) };
|
||||||
assert_eq!(vec2.clone(), b1.0);
|
assert_eq!(vec2.clone(), b1.0);
|
||||||
let vec2: &Vec2 = unsafe { col.get(earch2.0 as usize) };
|
let vec2: Ref<Vec2> = unsafe { col.get(earch2.0 as usize) };
|
||||||
assert_eq!(vec2.clone(), b2.0);
|
assert_eq!(vec2.clone(), b2.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,15 +506,15 @@ mod tests {
|
||||||
let earch2 = a.add_entity(e2, b2, &Tick::default());
|
let earch2 = a.add_entity(e2, b2, &Tick::default());
|
||||||
|
|
||||||
let col = a.columns.get(0).unwrap();
|
let col = a.columns.get(0).unwrap();
|
||||||
let vec2: &Vec2 = unsafe { col.get(earch1.0 as usize) };
|
let vec2: Ref<Vec2> = unsafe { col.get(earch1.0 as usize) };
|
||||||
assert_eq!(vec2.clone(), b1.0);
|
assert_eq!(vec2.clone(), b1.0);
|
||||||
let vec2: &Vec2 = unsafe { col.get(earch2.0 as usize) };
|
let vec2: Ref<Vec2> = unsafe { col.get(earch2.0 as usize) };
|
||||||
assert_eq!(vec2.clone(), b2.0);
|
assert_eq!(vec2.clone(), b2.0);
|
||||||
|
|
||||||
let col = a.columns.get(1).unwrap();
|
let col = a.columns.get(1).unwrap();
|
||||||
let vec3: &Vec3 = unsafe { col.get(earch1.0 as usize) };
|
let vec3: Ref<Vec3> = unsafe { col.get(earch1.0 as usize) };
|
||||||
assert_eq!(vec3.clone(), b1.1);
|
assert_eq!(vec3.clone(), b1.1);
|
||||||
let vec3: &Vec3 = unsafe { col.get(earch2.0 as usize) };
|
let vec3: Ref<Vec3> = unsafe { col.get(earch2.0 as usize) };
|
||||||
assert_eq!(vec3.clone(), b2.1);
|
assert_eq!(vec3.clone(), b2.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,7 +557,7 @@ mod tests {
|
||||||
|
|
||||||
let col = a.columns.get(0).unwrap();
|
let col = a.columns.get(0).unwrap();
|
||||||
for i in 0..bundle_count {
|
for i in 0..bundle_count {
|
||||||
let vec2: &Vec2 = unsafe { col.get(i) };
|
let vec2: Ref<Vec2> = unsafe { col.get(i) };
|
||||||
assert_eq!(vec2.clone(), bundles[i].0);
|
assert_eq!(vec2.clone(), bundles[i].0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ pub fn execute_deferred_commands(world: &mut World, mut commands: RefMut<Command
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::ptr::NonNull;
|
use std::{cell::Ref, ptr::NonNull};
|
||||||
|
|
||||||
use crate::{system::{GraphExecutor, IntoSystem}, tests::Vec2, Commands, DynTypeId, World};
|
use crate::{system::{GraphExecutor, IntoSystem}, tests::Vec2, Commands, DynTypeId, World};
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ mod tests {
|
||||||
// there's only one archetype
|
// there's only one archetype
|
||||||
let arch = world.archetypes.values().next().unwrap();
|
let arch = world.archetypes.values().next().unwrap();
|
||||||
let col = arch.get_column(DynTypeId::of::<Vec2>()).unwrap();
|
let col = arch.get_column(DynTypeId::of::<Vec2>()).unwrap();
|
||||||
let vec2: &Vec2 = unsafe { col.get(3) };
|
let vec2: Ref<Vec2> = unsafe { col.get(3) };
|
||||||
assert_eq!(vec2.clone(), spawned_vec);
|
assert_eq!(vec2.clone(), spawned_vec);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,39 +7,42 @@ pub(crate) mod lyra_engine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod archetype;
|
|
||||||
use std::ops::BitOr;
|
use std::ops::BitOr;
|
||||||
|
|
||||||
|
mod archetype;
|
||||||
pub use archetype::*;
|
pub use archetype::*;
|
||||||
|
|
||||||
pub mod entity;
|
mod entity;
|
||||||
pub use entity::*;
|
pub use entity::*;
|
||||||
|
|
||||||
pub mod world;
|
mod world;
|
||||||
pub use world::*;
|
pub use world::*;
|
||||||
|
|
||||||
pub mod command;
|
mod command;
|
||||||
pub use command::*;
|
pub use command::*;
|
||||||
|
|
||||||
pub mod bundle;
|
mod bundle;
|
||||||
pub use bundle::*;
|
pub use bundle::*;
|
||||||
|
|
||||||
pub mod component;
|
mod component;
|
||||||
pub use component::*;
|
pub use component::*;
|
||||||
|
|
||||||
pub mod query;
|
pub mod query;
|
||||||
//pub use query::*;
|
//pub use query::*;
|
||||||
|
|
||||||
pub mod component_info;
|
mod relation;
|
||||||
|
pub use relation::Relation;
|
||||||
|
|
||||||
|
mod component_info;
|
||||||
pub use component_info::*;
|
pub use component_info::*;
|
||||||
|
|
||||||
pub mod resource;
|
mod resource;
|
||||||
pub use resource::*;
|
pub use resource::*;
|
||||||
|
|
||||||
pub mod system;
|
pub mod system;
|
||||||
//pub use system::*;
|
//pub use system::*;
|
||||||
|
|
||||||
pub mod tick;
|
mod tick;
|
||||||
pub use tick::*;
|
pub use tick::*;
|
||||||
|
|
||||||
/// Implements Component for glam math types
|
/// Implements Component for glam math types
|
||||||
|
|
|
@ -221,7 +221,7 @@ impl<T: Component> AsQuery for &mut T {
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{mem::size_of, marker::PhantomData, ptr::NonNull};
|
use std::{mem::size_of, marker::PhantomData, ptr::NonNull};
|
||||||
|
|
||||||
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{Fetch, View}, tests::Vec2, world::World, DynTypeId, Entity, EntityId, Tick};
|
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{Fetch, ViewState}, tests::Vec2, world::World, DynTypeId, Entity, EntityId, Tick};
|
||||||
|
|
||||||
use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut};
|
use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut};
|
||||||
|
|
||||||
|
@ -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::<QueryBorrow<Vec2>>::new(&world, borrow, archetypes);
|
let v = ViewState::<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::<QueryBorrowMut<Vec2>>::new(&world, borrow, archetypes);
|
let v = ViewState::<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::<QueryBorrow<Vec2>>::new(&world, borrow, archetypes);
|
let v = ViewState::<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);
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::ptr::NonNull;
|
||||||
|
|
||||||
use crate::{world::World, ComponentColumn, ComponentInfo};
|
use crate::{world::World, ComponentColumn, ComponentInfo};
|
||||||
|
|
||||||
pub mod view;
|
mod view;
|
||||||
pub use view::*;
|
pub use view::*;
|
||||||
|
|
||||||
use super::Fetch;
|
use super::Fetch;
|
||||||
|
|
|
@ -3,27 +3,27 @@ use crate::{archetype::Archetype, world::{ArchetypeEntityId, World}, Tick};
|
||||||
pub mod view;
|
pub mod view;
|
||||||
pub use view::*;
|
pub use view::*;
|
||||||
|
|
||||||
pub mod entities;
|
mod entities;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use entities::*;
|
pub use entities::*;
|
||||||
|
|
||||||
pub mod borrow;
|
mod borrow;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use borrow::*;
|
pub use borrow::*;
|
||||||
|
|
||||||
pub mod tuple;
|
mod tuple;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use tuple::*;
|
pub use tuple::*;
|
||||||
|
|
||||||
pub mod resource;
|
mod resource;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use resource::*;
|
pub use resource::*;
|
||||||
|
|
||||||
pub mod tick;
|
mod tick;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use tick::*;
|
pub use tick::*;
|
||||||
|
|
||||||
pub mod world;
|
mod world;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use world::*;
|
pub use world::*;
|
||||||
|
|
||||||
|
@ -87,11 +87,45 @@ pub trait IntoQuery {
|
||||||
fn into_query(self) -> Self;
|
fn into_query(self) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> Fetch<'a> for () {
|
||||||
|
type Item = ();
|
||||||
|
|
||||||
|
fn dangling() -> Self {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_item(&mut self, entity: ArchetypeEntityId) -> Self::Item {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Query for () {
|
||||||
|
type Item<'a> = ();
|
||||||
|
|
||||||
|
type Fetch<'a> = ();
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_visit_archetype(&self, archetype: &Archetype) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn fetch<'a>(&self, world: &'a World, archetype: &'a Archetype, tick: Tick) -> Self::Fetch<'a> {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsQuery for () {
|
||||||
|
type Query = ();
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{world::World, archetype::Archetype, tests::Vec2};
|
use crate::{world::World, archetype::Archetype, tests::Vec2};
|
||||||
|
|
||||||
use super::{View, Entities};
|
use super::{ViewState, Entities};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_view() {
|
fn simple_view() {
|
||||||
|
@ -104,7 +138,7 @@ mod tests {
|
||||||
|
|
||||||
let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
|
let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
|
||||||
|
|
||||||
let v = View::<Entities>::new(&world, entities, archetypes);
|
let v = ViewState::<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);
|
||||||
|
|
|
@ -2,6 +2,50 @@ use crate::world::World;
|
||||||
|
|
||||||
use super::{Query, Fetch, AsQuery};
|
use super::{Query, Fetch, AsQuery};
|
||||||
|
|
||||||
|
impl<'a, F1> Fetch<'a> for (F1,)
|
||||||
|
where
|
||||||
|
F1: Fetch<'a>,
|
||||||
|
{
|
||||||
|
type Item = (F1::Item,);
|
||||||
|
|
||||||
|
fn dangling() -> Self {
|
||||||
|
(F1::dangling(),)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_visit_item(&mut self, entity: crate::world::ArchetypeEntityId) -> bool {
|
||||||
|
let (f1,) = self;
|
||||||
|
f1.can_visit_item(entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
||||||
|
let (f1,) = self;
|
||||||
|
( f1.get_item(entity), )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Q1> Query for (Q1,)
|
||||||
|
where
|
||||||
|
Q1: Query,
|
||||||
|
{
|
||||||
|
type Item<'a> = (Q1::Item<'a>,);
|
||||||
|
|
||||||
|
type Fetch<'a> = (Q1::Fetch<'a>,);
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
(Q1::new(),)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
|
||||||
|
let (q1,) = self;
|
||||||
|
q1.can_visit_archetype(archetype)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn fetch<'a>(&self, world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
|
||||||
|
let (q1,) = self;
|
||||||
|
( q1.fetch(world, archetype, tick), )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Technically all of these implementations for a 2-sized tuple
|
// Technically all of these implementations for a 2-sized tuple
|
||||||
// can be implemented by the macro near the end of the file, but
|
// can be implemented by the macro near the end of the file, but
|
||||||
// these are left here for development.
|
// these are left here for development.
|
||||||
|
|
|
@ -4,41 +4,64 @@ use crate::{archetype::Archetype, world::{ArchetypeEntityId, World}, EntityId, T
|
||||||
|
|
||||||
use super::{Query, Fetch, AsQuery};
|
use super::{Query, Fetch, AsQuery};
|
||||||
|
|
||||||
pub struct View<'a, Q: AsQuery> {
|
pub type View<'a, Q, F = ()> = ViewState<'a, <Q as AsQuery>::Query, <F as AsQuery>::Query>;
|
||||||
|
|
||||||
|
pub struct ViewState<'a, Q: Query, F: Query> {
|
||||||
world: &'a World,
|
world: &'a World,
|
||||||
query: Q::Query,
|
query: Q,
|
||||||
|
filter: F,
|
||||||
archetypes: Vec<&'a Archetype>,
|
archetypes: Vec<&'a Archetype>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Q> View<'a, Q>
|
impl<'a, Q, F> ViewState<'a, Q, F>
|
||||||
where
|
where
|
||||||
Q: AsQuery,
|
Q: Query,
|
||||||
|
F: Query,
|
||||||
{
|
{
|
||||||
pub fn new(world: &'a World, query: Q::Query, archetypes: Vec<&'a Archetype>) -> Self {
|
pub fn new(world: &'a World, query: Q, filter: F, archetypes: Vec<&'a Archetype>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
world,
|
world,
|
||||||
query,
|
query,
|
||||||
|
filter,
|
||||||
archetypes,
|
archetypes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts self into an iterator
|
||||||
|
pub fn iter(self) -> ViewIter<'a, Q, F> {
|
||||||
|
self.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes `self`, adding a query to the view.
|
||||||
|
pub fn expand<U: AsQuery>(self, query: U::Query) -> ViewState<'a, (Q, U::Query), F> {
|
||||||
|
ViewState::new(self.world, (self.query, query), self.filter, self.archetypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes `self`, adding a filter to the view.
|
||||||
|
pub fn with<U: AsQuery>(self, filter: U::Query) -> ViewState<'a, Q, (F, U::Query)> {
|
||||||
|
ViewState::new(self.world, self.query, (self.filter, filter), self.archetypes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Q> IntoIterator for View<'a, Q>
|
impl<'a, Q, F> IntoIterator for ViewState<'a, Q, F>
|
||||||
where
|
where
|
||||||
Q: AsQuery,
|
Q: Query,
|
||||||
|
F: Query,
|
||||||
{
|
{
|
||||||
type Item = <Q::Query as Query>::Item<'a>;
|
type Item = Q::Item<'a>;
|
||||||
|
|
||||||
type IntoIter = ViewIter<'a, Q::Query>;
|
type IntoIter = ViewIter<'a, Q, F>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
let tick = self.world.tick_tracker().tick_when(Q::Query::MUTATES);
|
let tick = self.world.tick_tracker().tick_when(Q::MUTATES);
|
||||||
|
|
||||||
ViewIter {
|
ViewIter {
|
||||||
world: self.world,
|
world: self.world,
|
||||||
tick,
|
tick,
|
||||||
query: self.query,
|
query: self.query,
|
||||||
|
filter: self.filter,
|
||||||
fetcher: None,
|
fetcher: None,
|
||||||
|
filter_fetcher: None,
|
||||||
archetypes: self.archetypes,
|
archetypes: self.archetypes,
|
||||||
next_archetype: 0,
|
next_archetype: 0,
|
||||||
component_indices: 0..0,
|
component_indices: 0..0,
|
||||||
|
@ -46,25 +69,28 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ViewIter<'a, Q: Query> {
|
pub struct ViewIter<'a, Q: Query, F: Query> {
|
||||||
world: &'a World,
|
world: &'a World,
|
||||||
tick: Tick,
|
tick: Tick,
|
||||||
query: Q,
|
query: Q,
|
||||||
|
filter: F,
|
||||||
fetcher: Option<Q::Fetch<'a>>,
|
fetcher: Option<Q::Fetch<'a>>,
|
||||||
|
filter_fetcher: Option<F::Fetch<'a>>,
|
||||||
archetypes: Vec<&'a Archetype>,
|
archetypes: Vec<&'a Archetype>,
|
||||||
next_archetype: usize,
|
next_archetype: usize,
|
||||||
component_indices: Range<u64>,
|
component_indices: Range<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Q> Iterator for ViewIter<'a, Q>
|
impl<'a, Q, F> Iterator for ViewIter<'a, Q, F>
|
||||||
where
|
where
|
||||||
Q: Query,
|
Q: Query,
|
||||||
|
F: Query,
|
||||||
{
|
{
|
||||||
type Item = Q::Item<'a>;
|
type Item = Q::Item<'a>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
loop {
|
loop {
|
||||||
if Q::ALWAYS_FETCHES {
|
if Q::ALWAYS_FETCHES && F::ALWAYS_FETCHES {
|
||||||
// only fetch this query once.
|
// only fetch this query once.
|
||||||
// fetcher gets set to Some after this `next` call.
|
// fetcher gets set to Some after this `next` call.
|
||||||
if self.fetcher.is_none() {
|
if self.fetcher.is_none() {
|
||||||
|
@ -80,10 +106,10 @@ where
|
||||||
|
|
||||||
if let Some(entity_index) = self.component_indices.next() {
|
if let Some(entity_index) = self.component_indices.next() {
|
||||||
let fetcher = self.fetcher.as_mut().unwrap();
|
let fetcher = self.fetcher.as_mut().unwrap();
|
||||||
|
let filter_fetcher = self.filter_fetcher.as_mut().unwrap();
|
||||||
let entity_index = ArchetypeEntityId(entity_index);
|
let entity_index = ArchetypeEntityId(entity_index);
|
||||||
if !fetcher.can_visit_item(entity_index) {
|
|
||||||
continue;
|
if fetcher.can_visit_item(entity_index) && filter_fetcher.can_visit_item(entity_index) {
|
||||||
} else {
|
|
||||||
let i = unsafe { fetcher.get_item(entity_index) };
|
let i = unsafe { fetcher.get_item(entity_index) };
|
||||||
return Some(i);
|
return Some(i);
|
||||||
}
|
}
|
||||||
|
@ -100,15 +126,16 @@ where
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.query.can_visit_archetype(arch) {
|
if self.query.can_visit_archetype(arch) && self.filter.can_visit_archetype(arch) {
|
||||||
continue;
|
unsafe {
|
||||||
|
self.fetcher = Some(self.query.fetch(self.world, arch, self.tick));
|
||||||
|
self.filter_fetcher = Some(self.filter.fetch(self.world, arch, self.tick));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.fetcher = unsafe { Some(self.query.fetch(self.world, arch, self.tick)) };
|
|
||||||
self.component_indices = 0..arch.entities.len() as u64;
|
self.component_indices = 0..arch.entities.len() as u64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ViewOne<'a, Q: Query> {
|
pub struct ViewOne<'a, Q: Query> {
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use lyra_ecs_derive::Component;
|
||||||
|
|
||||||
|
use crate::query::Query;
|
||||||
|
use crate::query::ViewState;
|
||||||
|
use crate::Entity;
|
||||||
|
|
||||||
|
use crate::lyra_engine;
|
||||||
|
use crate::World;
|
||||||
|
|
||||||
|
mod relates_to;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use relates_to::*;
|
||||||
|
|
||||||
|
mod relate_pair;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use relate_pair::*;
|
||||||
|
|
||||||
|
pub trait Relation: 'static {
|
||||||
|
/// called when a relation of this type is set on a target
|
||||||
|
fn relation_add(&self, origin: Entity, target: Entity) { }
|
||||||
|
/// called when a relation is removed
|
||||||
|
fn relation_remove(&self, origin: Entity, target: Entity) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A component that stores the target of a relation.
|
||||||
|
///
|
||||||
|
/// This component is on the origin of the relation and can be used to find all
|
||||||
|
/// entities that the relation targets.
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct RelationOriginComponent<R: Relation> {
|
||||||
|
pub(crate) relation: R,
|
||||||
|
target: Entity,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A component that stores the origin of a relation.
|
||||||
|
///
|
||||||
|
/// This component is on the target of the relation and can be used to find the
|
||||||
|
/// origin of the relation -- the entity that targets this entity.
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct RelationTargetComponent<R: Relation> {
|
||||||
|
origin: Entity,
|
||||||
|
_marker: PhantomData<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl World {
|
||||||
|
/// Creates a relation between two entities.
|
||||||
|
///
|
||||||
|
/// ```compile_fail
|
||||||
|
/// struct ChildOf;
|
||||||
|
///
|
||||||
|
/// impl Relation for ChildOf { /* snip */ };
|
||||||
|
///
|
||||||
|
/// let a = world.spawn((Vec2 { x: 10.0, y: 20.0 },));
|
||||||
|
/// let b = world.spawn((Vec2 { x: 158.0, y: 65.0 },));
|
||||||
|
///
|
||||||
|
/// // Entity a is a child of entity b
|
||||||
|
/// world.add_relation(a, ChildOf, b);
|
||||||
|
///
|
||||||
|
/// // Construct a view for querying the entities that have a relation to entity `b`.
|
||||||
|
/// let v = world.view::<Entities>()
|
||||||
|
/// .relates_to::<ChildOf>(b);
|
||||||
|
///
|
||||||
|
/// // Iterate through the entities. ChildOf has no members, so it will be unused.
|
||||||
|
/// for (e, _childof) in v.into_iter() {
|
||||||
|
/// println!("{:?} is a child of {:?}", e, b);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn add_relation<R>(&mut self, origin: Entity, relation: R, target: Entity)
|
||||||
|
where
|
||||||
|
R: Relation
|
||||||
|
{
|
||||||
|
let comp = RelationTargetComponent {
|
||||||
|
origin,
|
||||||
|
_marker: PhantomData::<R>,
|
||||||
|
};
|
||||||
|
self.insert(target, comp);
|
||||||
|
|
||||||
|
let comp = RelationOriginComponent {
|
||||||
|
relation,
|
||||||
|
target,
|
||||||
|
};
|
||||||
|
|
||||||
|
comp.relation.relation_add(origin, target);
|
||||||
|
self.insert(origin, comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, Q, F> ViewState<'a, Q, F>
|
||||||
|
where
|
||||||
|
Q: Query,
|
||||||
|
F: Query,
|
||||||
|
{
|
||||||
|
/// Consumes `self` to return a view that fetches the relation to a specific target entity.
|
||||||
|
pub fn relates_to<R>(self, target: Entity) -> ViewState<'a, (Q, QueryRelatesTo<R>), F>
|
||||||
|
where
|
||||||
|
R: Relation,
|
||||||
|
{
|
||||||
|
let rel = QueryRelatesTo::new(Some(target));
|
||||||
|
self.expand::<RelatesTo<R>>(rel)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes `self` to return a view that fetches the origin, target, and a reference to
|
||||||
|
/// the relation that the entities have together.
|
||||||
|
pub fn relate_pair<R>(self) -> ViewState<'a, (Q, QueryRelatePair<R>), F>
|
||||||
|
where
|
||||||
|
R: Relation,
|
||||||
|
{
|
||||||
|
let rel = QueryRelatePair::<R>::new();
|
||||||
|
self.expand::<RelatePair<R>>(rel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{query::{Entities, ViewState}, relation::QueryRelatesTo, tests::Vec2, World};
|
||||||
|
|
||||||
|
use super::{RelatePair, Relation};
|
||||||
|
|
||||||
|
struct ChildOf;
|
||||||
|
|
||||||
|
impl Relation for ChildOf {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_relates_to() {
|
||||||
|
let mut world = World::new();
|
||||||
|
|
||||||
|
let a = world.spawn((Vec2::new(10.0, 20.0),));
|
||||||
|
let b = world.spawn((Vec2::new(158.0, 65.0),));
|
||||||
|
|
||||||
|
world.add_relation(a, ChildOf, b);
|
||||||
|
|
||||||
|
let archetypes = world.archetypes.values().collect();
|
||||||
|
let v = ViewState::<(Entities, QueryRelatesTo<ChildOf>), ()>::new(&world, (Entities::default(), QueryRelatesTo::new(Some(b))), (), archetypes);
|
||||||
|
|
||||||
|
for (e, _relation) in v.into_iter() {
|
||||||
|
println!("{:?} is a child of {:?}", e, b);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_relates_to_view() {
|
||||||
|
let mut world = World::new();
|
||||||
|
|
||||||
|
let a = world.spawn((Vec2::new(10.0, 20.0),));
|
||||||
|
let b = world.spawn((Vec2::new(158.0, 65.0),));
|
||||||
|
|
||||||
|
world.add_relation(a, ChildOf, b);
|
||||||
|
|
||||||
|
let v = world.view::<Entities>()
|
||||||
|
.relates_to::<ChildOf>(b);
|
||||||
|
|
||||||
|
for (e, _rel) in v.into_iter() {
|
||||||
|
println!("{:?} is a child of {:?}", e, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_relate_pair_view() {
|
||||||
|
let mut world = World::new();
|
||||||
|
|
||||||
|
let a = world.spawn((Vec2::new(10.0, 20.0),));
|
||||||
|
let b = world.spawn((Vec2::new(158.0, 65.0),));
|
||||||
|
|
||||||
|
world.add_relation(a, ChildOf, b);
|
||||||
|
|
||||||
|
let v = world.view::<RelatePair<ChildOf>>();
|
||||||
|
|
||||||
|
for (origin, _childof, target) in v.into_iter() {
|
||||||
|
println!("{:?} is a child of {:?}", origin, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
use std::{any::TypeId, cell::Ref, marker::PhantomData};
|
||||||
|
|
||||||
|
use crate::{query::{AsQuery, Fetch, Query}, Archetype, ComponentColumn, Entity, World};
|
||||||
|
|
||||||
|
use super::{Relation, RelationOriginComponent};
|
||||||
|
|
||||||
|
pub struct FetchRelatePair<'a, T> {
|
||||||
|
col: &'a ComponentColumn,
|
||||||
|
arch: &'a Archetype,
|
||||||
|
_phantom: PhantomData<&'a T>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, R> Fetch<'a> for FetchRelatePair<'a, R>
|
||||||
|
where
|
||||||
|
R: Relation,
|
||||||
|
{
|
||||||
|
type Item = (Entity, Ref<'a, R>, Entity);
|
||||||
|
|
||||||
|
fn dangling() -> Self {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
||||||
|
let comp: Ref<RelationOriginComponent<R>> = self.col.get(entity.0 as usize);
|
||||||
|
let rel_target = comp.target;
|
||||||
|
let rel_origin = self.arch.entity_of_index(entity).unwrap();
|
||||||
|
|
||||||
|
let comp = Ref::map(comp, |r| &r.relation);
|
||||||
|
(rel_origin, comp, rel_target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct QueryRelatePair<R> {
|
||||||
|
_marker: PhantomData<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> Copy for QueryRelatePair<R> {}
|
||||||
|
|
||||||
|
impl<R> Clone for QueryRelatePair<R> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> QueryRelatePair<R> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
_marker: PhantomData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> Query for QueryRelatePair<R>
|
||||||
|
where
|
||||||
|
R: Relation + 'static
|
||||||
|
{
|
||||||
|
type Item<'a> = (Entity, Ref<'a, R>, Entity);
|
||||||
|
|
||||||
|
type Fetch<'a> = FetchRelatePair<'a, R>;
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
QueryRelatePair::<R>::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
|
||||||
|
let tyid = crate::DynTypeId::Rust(TypeId::of::<RelationOriginComponent<R>>());
|
||||||
|
archetype.has_column(tyid)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
|
||||||
|
let _ = tick;
|
||||||
|
let tyid = crate::DynTypeId::Rust(TypeId::of::<RelationOriginComponent<R>>());
|
||||||
|
let col = archetype.columns.iter().find(|c| c.info.type_id == tyid)
|
||||||
|
.expect("You ignored 'can_visit_archetype'!");
|
||||||
|
|
||||||
|
FetchRelatePair {
|
||||||
|
col,
|
||||||
|
arch: archetype,
|
||||||
|
_phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A query that fetches the origin, and target of a relation of type `R`.
|
||||||
|
///
|
||||||
|
/// It provides it as a tuple in the following format: `(origin, relation, target)`.
|
||||||
|
/// Similar to [`RelatesTo`](super::RelatesTo), you can use [`ViewState::relate_pair`] to get a view that fetches the
|
||||||
|
/// pair, or unlike [`RelatesTo`](super::RelatesTo), you can do the common procedure of using [`World::view`].
|
||||||
|
pub struct RelatePair<R: Relation> {
|
||||||
|
_marker: PhantomData<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Relation> AsQuery for RelatePair<R> {
|
||||||
|
type Query = QueryRelatePair<R>;
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
use std::{any::TypeId, cell::Ref, marker::PhantomData};
|
||||||
|
|
||||||
|
use crate::{query::{AsQuery, Fetch, Query}, ComponentColumn, Entity, World};
|
||||||
|
|
||||||
|
use super::{Relation, RelationOriginComponent};
|
||||||
|
|
||||||
|
pub struct FetchRelatesTo<'a, T> {
|
||||||
|
col: &'a ComponentColumn,
|
||||||
|
target: Entity,
|
||||||
|
_phantom: PhantomData<&'a T>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, R> Fetch<'a> for FetchRelatesTo<'a, R>
|
||||||
|
where
|
||||||
|
R: Relation,
|
||||||
|
{
|
||||||
|
type Item = Ref<'a, R>;
|
||||||
|
|
||||||
|
fn dangling() -> Self {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_visit_item(&mut self, entity: crate::ArchetypeEntityId) -> bool {
|
||||||
|
unsafe {
|
||||||
|
let comp: Ref<RelationOriginComponent<R>> = self.col.get(entity.0 as usize);
|
||||||
|
comp.target == self.target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
||||||
|
let comp: Ref<RelationOriginComponent<R>> = self.col.get(entity.0 as usize);
|
||||||
|
let comp = Ref::map(comp, |r| &r.relation);
|
||||||
|
comp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct QueryRelatesTo<R> {
|
||||||
|
target: Option<Entity>,
|
||||||
|
_marker: PhantomData<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> Copy for QueryRelatesTo<R> {}
|
||||||
|
|
||||||
|
impl<R> Clone for QueryRelatesTo<R> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> QueryRelatesTo<R> {
|
||||||
|
pub fn new(target: Option<Entity>) -> Self {
|
||||||
|
Self {
|
||||||
|
target,
|
||||||
|
_marker: PhantomData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> Query for QueryRelatesTo<R>
|
||||||
|
where
|
||||||
|
R: Relation + 'static
|
||||||
|
{
|
||||||
|
type Item<'a> = Ref<'a, R>;
|
||||||
|
|
||||||
|
type Fetch<'a> = FetchRelatesTo<'a, R>;
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
panic!("RelatesTo MUST be made with View::relates_to since it requires State provided by \
|
||||||
|
that function.")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
|
||||||
|
let tyid = crate::DynTypeId::Rust(TypeId::of::<RelationOriginComponent<R>>());
|
||||||
|
archetype.has_column(tyid)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
|
||||||
|
let _ = tick;
|
||||||
|
let tyid = crate::DynTypeId::Rust(TypeId::of::<RelationOriginComponent<R>>());
|
||||||
|
let col = archetype.columns.iter().find(|c| c.info.type_id == tyid)
|
||||||
|
.expect("You ignored 'can_visit_archetype'!");
|
||||||
|
|
||||||
|
FetchRelatesTo {
|
||||||
|
col,
|
||||||
|
target: self.target.expect("Filter not initialized"),
|
||||||
|
_phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A query that fetches the relation to a specific target entity.
|
||||||
|
///
|
||||||
|
/// This can be combined with Entities, to query all entities that have a relation targeting
|
||||||
|
/// the target entity.
|
||||||
|
///
|
||||||
|
/// ```compile_fail
|
||||||
|
/// let v = world.view::<Entities>()
|
||||||
|
/// .relates_to::<ChildOf>(b);
|
||||||
|
///
|
||||||
|
/// // Iterate through the entities. ChildOf has no members, so it will be unused.
|
||||||
|
/// for (e, _childof) in v.into_iter() {
|
||||||
|
/// println!("{:?} is a child of {:?}", e, b);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub struct RelatesTo<R: Relation> {
|
||||||
|
_marker: PhantomData<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Relation> AsQuery for RelatesTo<R> {
|
||||||
|
type Query = QueryRelatesTo<R>;
|
||||||
|
}
|
|
@ -2,14 +2,23 @@ use std::ptr::NonNull;
|
||||||
|
|
||||||
use lyra_ecs::world::World;
|
use lyra_ecs::world::World;
|
||||||
|
|
||||||
|
/// An enum that is used to control if the Criteria was met or not.
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum CriteriaSchedule {
|
pub enum CriteriaSchedule {
|
||||||
|
/// The criteria was completely met and the system can continue to run.
|
||||||
Yes,
|
Yes,
|
||||||
|
/// The criteria was not met and must run next time the system batch is ran.
|
||||||
No,
|
No,
|
||||||
|
/// The criteria was met and the system can run.
|
||||||
|
/// After the system runs, the criteria should be checked again and may cause another
|
||||||
|
/// execution of the system.
|
||||||
YesAndLoop,
|
YesAndLoop,
|
||||||
|
/// The criteria was not met, but it should be checked again during this tick before giving
|
||||||
|
/// up. If the criteria returns `Yes` next check, the systems will run.
|
||||||
NoAndLoop,
|
NoAndLoop,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A Criteria can be used to conditionally execute [`BatchedSystems`](super::BatchedSystems).
|
||||||
pub trait Criteria {
|
pub trait Criteria {
|
||||||
/// Checks if this Criteria can run, and if it should check it again.
|
/// Checks if this Criteria can run, and if it should check it again.
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use std::{any::Any, marker::PhantomData, ptr::NonNull};
|
use std::{any::Any, marker::PhantomData, ptr::NonNull};
|
||||||
|
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery, ResMut, Res}};
|
use crate::{world::World, Access, ResourceObject, query::{Query, ViewState, ResMut, Res}};
|
||||||
|
|
||||||
use super::{System, IntoSystem};
|
use super::{System, IntoSystem};
|
||||||
|
|
||||||
|
/// A trait that is used for fetching an argument for a [`FnSystem`].
|
||||||
pub trait FnArgFetcher {
|
pub trait FnArgFetcher {
|
||||||
/// stores data that persists after an execution of a system
|
/// stores data that persists after an execution of a system
|
||||||
type State: 'static;
|
type State: 'static;
|
||||||
|
@ -30,14 +31,20 @@ pub trait FnArgFetcher {
|
||||||
fn apply_deferred(state: Self::State, world: NonNull<World>);
|
fn apply_deferred(state: Self::State, world: NonNull<World>);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FnArg {
|
/// A system that is implemented as a function.
|
||||||
type Fetcher: FnArgFetcher;
|
///
|
||||||
}
|
/// The arguments of the functions must implement `FnArgFetcher` so that the arguments can be
|
||||||
|
/// fetched on the fly.
|
||||||
|
///
|
||||||
|
/// ```fail_compile
|
||||||
|
/// fn enemy_movement_system(enemies: ViewState<(&Health, &EnemyStats, &Movement)>) -> anyhow::Result<()> {
|
||||||
|
/// for (health, stats, movement) in enemies.iter() {
|
||||||
|
/// // ...
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub struct FnSystem<F, Args> {
|
pub struct FnSystem<F, Args> {
|
||||||
inner: F,
|
inner: F,
|
||||||
//#[allow(dead_code)]
|
|
||||||
//args: Args,
|
|
||||||
arg_state: Option<Vec<Box<dyn Any>>>,
|
arg_state: Option<Vec<Box<dyn Any>>>,
|
||||||
_marker: PhantomData<Args>,
|
_marker: PhantomData<Args>,
|
||||||
}
|
}
|
||||||
|
@ -129,22 +136,14 @@ impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N }
|
||||||
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 }
|
||||||
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 [`ViewState`]s
|
||||||
/* pub struct ViewArgFetcher<Q: AsQuery> {
|
impl<'c, Q, F> FnArgFetcher for ViewState<'c, Q, F>
|
||||||
query: Q::Query
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Q: AsQuery> FnArg for View<'a, Q> {
|
|
||||||
type Fetcher = ViewArgFetcher<Q>;
|
|
||||||
} */
|
|
||||||
|
|
||||||
impl<'c, Q> FnArgFetcher for View<'c, Q>
|
|
||||||
where
|
where
|
||||||
Q: AsQuery,
|
Q: Query + 'static,
|
||||||
<Q as AsQuery>::Query: 'static
|
F: Query + 'static,
|
||||||
{
|
{
|
||||||
type State = Q::Query;
|
type State = (Q, F);
|
||||||
type Arg<'a, 'state> = View<'a, Q>;
|
type Arg<'a, 'state> = ViewState<'a, Q, F>;
|
||||||
|
|
||||||
fn world_access(&self) -> Access {
|
fn world_access(&self) -> Access {
|
||||||
todo!()
|
todo!()
|
||||||
|
@ -153,7 +152,8 @@ where
|
||||||
unsafe fn get<'a, 'state>(state: &'state mut Self::State, world: NonNull<World>) -> Self::Arg<'a, 'state> {
|
unsafe fn get<'a, 'state>(state: &'state mut Self::State, world: NonNull<World>) -> Self::Arg<'a, 'state> {
|
||||||
let world = &*world.as_ptr();
|
let world = &*world.as_ptr();
|
||||||
let arch = world.archetypes.values().collect();
|
let arch = world.archetypes.values().collect();
|
||||||
let v = View::new(world, state.clone(), arch);
|
let (query, filter) = state.clone();
|
||||||
|
let v = ViewState::new(world, query, filter, arch);
|
||||||
|
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
@ -161,17 +161,10 @@ where
|
||||||
fn apply_deferred(_: Self::State, _: NonNull<World>) { }
|
fn apply_deferred(_: Self::State, _: NonNull<World>) { }
|
||||||
|
|
||||||
fn create_state(_: NonNull<World>) -> Self::State {
|
fn create_state(_: NonNull<World>) -> Self::State {
|
||||||
<Q::Query as Query>::new()
|
(Q::new(), F::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An ArgFetcher implementation for borrowing the [`World`].
|
|
||||||
/* pub struct WorldArgFetcher;
|
|
||||||
|
|
||||||
impl<'a> FnArg for &'a World {
|
|
||||||
type Fetcher = WorldArgFetcher;
|
|
||||||
} */
|
|
||||||
|
|
||||||
impl FnArgFetcher for &'_ World {
|
impl FnArgFetcher for &'_ World {
|
||||||
type State = ();
|
type State = ();
|
||||||
type Arg<'a, 'state> = &'a World;
|
type Arg<'a, 'state> = &'a World;
|
||||||
|
@ -246,7 +239,7 @@ impl<R: ResourceObject> FnArgFetcher for ResMut<'_, R> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use crate::{tests::{Vec2, Vec3}, world::World, query::{QueryBorrow, View, ResMut}};
|
use crate::{tests::{Vec2, Vec3}, world::World, query::{QueryBorrow, ViewState, ResMut}};
|
||||||
use super::{System, IntoSystem};
|
use super::{System, IntoSystem};
|
||||||
|
|
||||||
struct SomeCounter(u32);
|
struct SomeCounter(u32);
|
||||||
|
@ -262,7 +255,7 @@ mod tests {
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|
||||||
let test_system = |view: View<QueryBorrow<Vec2>>| -> anyhow::Result<()> {
|
let test_system = |view: ViewState<QueryBorrow<Vec2>, ()>| -> anyhow::Result<()> {
|
||||||
let mut vecs = vecs.to_vec();
|
let mut vecs = vecs.to_vec();
|
||||||
for v in view.into_iter() {
|
for v in view.into_iter() {
|
||||||
let pos = vecs.iter().position(|vec| *vec == *v)
|
let pos = vecs.iter().position(|vec| *vec == *v)
|
||||||
|
@ -291,7 +284,7 @@ mod tests {
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|
||||||
let test_system = |view: View<(QueryBorrow<Vec2>, QueryBorrow<Vec3>)>| -> anyhow::Result<()> {
|
let test_system = |view: ViewState<(QueryBorrow<Vec2>, QueryBorrow<Vec3>), ()>| -> anyhow::Result<()> {
|
||||||
for (v2, v3) in view.into_iter() {
|
for (v2, v3) in view.into_iter() {
|
||||||
println!("Got v2 at '{:?}' and v3 at: '{:?}'", v2, v3);
|
println!("Got v2 at '{:?}' and v3 at: '{:?}'", v2, v3);
|
||||||
count += 1;
|
count += 1;
|
||||||
|
@ -392,7 +385,7 @@ mod tests {
|
||||||
world.spawn((Vec2::rand(), ));
|
world.spawn((Vec2::rand(), ));
|
||||||
world.add_resource(SomeCounter(0));
|
world.add_resource(SomeCounter(0));
|
||||||
|
|
||||||
let test_system = |mut counter: ResMut<SomeCounter>, view: View<QueryBorrow<Vec2>>| -> anyhow::Result<()> {
|
let test_system = |mut counter: ResMut<SomeCounter>, view: ViewState<QueryBorrow<Vec2>, ()>| -> anyhow::Result<()> {
|
||||||
for v2 in view.into_iter() {
|
for v2 in view.into_iter() {
|
||||||
println!("Got v2 at '{:?}'", v2);
|
println!("Got v2 at '{:?}'", v2);
|
||||||
// .0 is twice here since ResMut's tuple field is pub(crate).
|
// .0 is twice here since ResMut's tuple field is pub(crate).
|
||||||
|
|
|
@ -140,7 +140,7 @@ impl GraphExecutor {
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use crate::{world::World, query::{ResMut, View}, system::IntoSystem};
|
use crate::{query::{ResMut, View}, system::IntoSystem, world::World};
|
||||||
|
|
||||||
use super::GraphExecutor;
|
use super::GraphExecutor;
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ mod tests {
|
||||||
|
|
||||||
let mut exec = GraphExecutor::new();
|
let mut exec = GraphExecutor::new();
|
||||||
|
|
||||||
let a_system = |view: View<ResMut<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();
|
||||||
|
@ -161,7 +161,7 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
let b_system = |view: View<ResMut<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();
|
||||||
|
@ -170,7 +170,7 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
let c_system = |view: View<ResMut<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();
|
||||||
|
|
|
@ -2,16 +2,16 @@ use std::ptr::NonNull;
|
||||||
|
|
||||||
use crate::{world::World, Access};
|
use crate::{world::World, Access};
|
||||||
|
|
||||||
pub mod graph;
|
mod graph;
|
||||||
pub use graph::*;
|
pub use graph::*;
|
||||||
|
|
||||||
pub mod criteria;
|
mod criteria;
|
||||||
pub use criteria::*;
|
pub use criteria::*;
|
||||||
|
|
||||||
pub mod batched;
|
mod batched;
|
||||||
pub use batched::*;
|
pub use batched::*;
|
||||||
|
|
||||||
pub mod fn_sys;
|
mod fn_sys;
|
||||||
pub use fn_sys::*;
|
pub use fn_sys::*;
|
||||||
|
|
||||||
/// A system that does not mutate the world
|
/// A system that does not mutate the world
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use std::{any::TypeId, cell::{Ref, RefMut}, collections::HashMap, ptr::NonNull};
|
use std::{any::TypeId, cell::{Ref, RefMut}, collections::HashMap, ptr::NonNull};
|
||||||
|
|
||||||
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsQuery, Query, View, ViewIter, ViewOne}, resource::ResourceData, ComponentInfo, DynTypeId, Entities, Entity, ResourceObject, Tick, TickTracker};
|
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsQuery, Query, ViewState, ViewIter, ViewOne}, resource::ResourceData, ComponentInfo, DynTypeId, Entities, Entity, ResourceObject, Tick, TickTracker};
|
||||||
|
|
||||||
/// The id of the entity for the Archetype.
|
/// The id of the entity for the Archetype.
|
||||||
/// The Archetype struct uses this as the index in the component columns
|
///
|
||||||
|
/// The Archetype uses this as the index in the component columns
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct ArchetypeEntityId(pub u64);
|
pub struct ArchetypeEntityId(pub u64);
|
||||||
|
|
||||||
|
@ -207,9 +208,20 @@ 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<Q: AsQuery>(&self) -> ViewState<Q::Query, ()> {
|
||||||
|
self.filtered_view::<Q, ()>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// View into the world for a set of entities that satisfy the query and the filter.
|
||||||
|
pub fn filtered_view<Q: AsQuery, F: AsQuery>(&self) -> ViewState<Q::Query, F::Query> {
|
||||||
let archetypes = self.archetypes.values().collect();
|
let archetypes = self.archetypes.values().collect();
|
||||||
let v = View::<T>::new(self, T::Query::new(), archetypes);
|
ViewState::<Q::Query, F::Query>::new(self, Q::Query::new(), F::Query::new(), archetypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// View into the world for a set of entities that satisfy the queries.
|
||||||
|
pub fn view_iter<Q: AsQuery>(&self) -> ViewIter<Q::Query, ()> {
|
||||||
|
let archetypes = self.archetypes.values().collect();
|
||||||
|
let v = ViewState::new(self, Q::Query::new(), (), archetypes);
|
||||||
v.into_iter()
|
v.into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +229,7 @@ impl World {
|
||||||
DynamicView::new(self)
|
DynamicView::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view_one<T: 'static + AsQuery>(&self, entity: Entity) -> ViewOne<T::Query> {
|
pub fn view_one<T: AsQuery>(&self, entity: Entity) -> ViewOne<T::Query> {
|
||||||
ViewOne::new(self, entity.id, T::Query::new())
|
ViewOne::new(self, entity.id, T::Query::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue