ecs: rename ComMut<T> to Track<T> and fix nested change queries

This commit is contained in:
Carter Anderson 2020-07-18 01:05:06 -07:00
parent 23b96a48a6
commit fbcf3f89d0
3 changed files with 77 additions and 27 deletions

View File

@ -203,6 +203,10 @@ impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> {
unsafe fn next(&mut self) -> Option<T::Item> { unsafe fn next(&mut self) -> Option<T::Item> {
Some(self.0.as_mut()?.next()) Some(self.0.as_mut()?.next())
} }
unsafe fn should_skip(&self) -> bool {
self.0.as_ref().map_or(false, |fetch| fetch.should_skip())
}
} }
/// Query transformer skipping entities that have a `T` component /// Query transformer skipping entities that have a `T` component
@ -258,6 +262,10 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout<T, F> {
unsafe fn next(&mut self) -> F::Item { unsafe fn next(&mut self) -> F::Item {
self.0.next() self.0.next()
} }
unsafe fn should_skip(&self) -> bool {
self.0.should_skip()
}
} }
/// Query transformer skipping entities that do not have a `T` component /// Query transformer skipping entities that do not have a `T` component
@ -315,6 +323,10 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith<T, F> {
unsafe fn next(&mut self) -> F::Item { unsafe fn next(&mut self) -> F::Item {
self.0.next() self.0.next()
} }
unsafe fn should_skip(&self) -> bool {
self.0.should_skip()
}
} }
/// A borrow of a `World` sufficient to execute the query `Q` /// A borrow of a `World` sufficient to execute the query `Q`
@ -536,7 +548,7 @@ impl<Q: Query> ChunkIter<Q> {
continue; continue;
} }
break Some(self.fetch.next()) break Some(self.fetch.next());
} }
} }
} }
@ -636,6 +648,10 @@ macro_rules! tuple_impl {
let ($($name,)*) = self; let ($($name,)*) = self;
($($name.next(),)*) ($($name.next(),)*)
} }
unsafe fn should_skip(&self) -> bool {
false
}
} }
impl<$($name: Query),*> Query for ($($name,)*) { impl<$($name: Query),*> Query for ($($name,)*) {

View File

@ -15,7 +15,7 @@ pub mod prelude {
system::{ system::{
Commands, IntoForEachSystem, IntoQuerySystem, IntoThreadLocalSystem, Query, System, Commands, IntoForEachSystem, IntoQuerySystem, IntoThreadLocalSystem, Query, System,
}, },
world::{WorldBuilderSource, ComMut}, world::{WorldBuilderSource, Track},
Bundle, Component, Entity, Ref, RefMut, With, Without, World, Bundle, Component, Entity, Ref, RefMut, With, Without, World,
}; };
} }

View File

@ -7,36 +7,36 @@ use std::{
}; };
/// Unique borrow of an entity's component /// Unique borrow of an entity's component
pub struct ComMut<'a, T: Component> { pub struct Track<'a, T: Component> {
value: &'a mut T, value: &'a mut T,
modified: &'a mut bool, modified: &'a mut bool,
} }
unsafe impl<T: Component> Send for ComMut<'_, T> {} unsafe impl<T: Component> Send for Track<'_, T> {}
unsafe impl<T: Component> Sync for ComMut<'_, T> {} unsafe impl<T: Component> Sync for Track<'_, T> {}
impl<'a, T: Component> Deref for ComMut<'a, T> { impl<'a, T: Component> Deref for Track<'a, T> {
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {
self.value self.value
} }
} }
impl<'a, T: Component> DerefMut for ComMut<'a, T> { impl<'a, T: Component> DerefMut for Track<'a, T> {
fn deref_mut(&mut self) -> &mut T { fn deref_mut(&mut self) -> &mut T {
*self.modified = true; *self.modified = true;
self.value self.value
} }
} }
impl<'a, T: Component> HecsQuery for ComMut<'a, T> { impl<'a, T: Component> HecsQuery for Track<'a, T> {
type Fetch = FetchComMut<T>; type Fetch = FetchTrack<T>;
} }
#[doc(hidden)] #[doc(hidden)]
pub struct FetchComMut<T>(NonNull<T>, NonNull<bool>); pub struct FetchTrack<T>(NonNull<T>, NonNull<bool>);
impl<'a, T: Component> Fetch<'a> for FetchComMut<T> { impl<'a, T: Component> Fetch<'a> for FetchTrack<T> {
type Item = ComMut<'a, T>; type Item = Track<'a, T>;
fn access(archetype: &Archetype) -> Option<Access> { fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() { if archetype.has::<T>() {
@ -63,12 +63,12 @@ impl<'a, T: Component> Fetch<'a> for FetchComMut<T> {
archetype.release_mut::<T>(); archetype.release_mut::<T>();
} }
unsafe fn next(&mut self) -> ComMut<'a, T> { unsafe fn next(&mut self) -> Track<'a, T> {
let component = self.0.as_ptr(); let component = self.0.as_ptr();
let modified = self.1.as_ptr(); let modified = self.1.as_ptr();
self.0 = NonNull::new_unchecked(component.add(1)); self.0 = NonNull::new_unchecked(component.add(1));
self.1 = NonNull::new_unchecked(modified.add(1)); self.1 = NonNull::new_unchecked(modified.add(1));
ComMut { Track {
value: &mut *component, value: &mut *component,
modified: &mut *modified, modified: &mut *modified,
} }
@ -114,7 +114,7 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchChanged<T, F> {
unsafe fn should_skip(&self) -> bool { unsafe fn should_skip(&self) -> bool {
// skip if the current item wasn't changed // skip if the current item wasn't changed
!*self.2.as_ref() !*self.2.as_ref() || self.0.should_skip()
} }
unsafe fn next(&mut self) -> F::Item { unsafe fn next(&mut self) -> F::Item {
@ -125,52 +125,64 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchChanged<T, F> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{Changed, ComMut}; use crate::{Changed, Track};
use hecs::{Entity, World}; use hecs::{Entity, World};
struct A(usize); struct A(usize);
struct B; struct B(usize);
struct C; struct C;
#[test] #[test]
fn modified_trackers() { fn modified_trackers() {
let mut world = World::default(); let mut world = World::default();
let e1 = world.spawn((A(0), B)); let e1 = world.spawn((A(0), B(0)));
let e2 = world.spawn((A(0), B)); let e2 = world.spawn((A(0), B(0)));
let e3 = world.spawn((A(0), B)); let e3 = world.spawn((A(0), B(0)));
world.spawn((A(0), B)); world.spawn((A(0), B));
for (i, mut a) in world.query::<ComMut<A>>().iter().enumerate() { for (i, mut a) in world.query::<Track<A>>().iter().enumerate() {
if i % 2 == 0 { if i % 2 == 0 {
a.0 += 1; a.0 += 1;
} }
} }
fn get_changed(world: &World) -> Vec<Entity> { fn get_changed_a(world: &World) -> Vec<Entity> {
world world
.query::<Changed<A, Entity>>() .query::<Changed<A, Entity>>()
.iter() .iter()
.collect::<Vec<Entity>>() .collect::<Vec<Entity>>()
}; };
assert_eq!(get_changed(&world), vec![e1, e3]); assert_eq!(get_changed_a(&world), vec![e1, e3]);
// ensure changing an entity's archetypes also moves its modified state // ensure changing an entity's archetypes also moves its modified state
world.insert(e1, (C,)).unwrap(); world.insert(e1, (C,)).unwrap();
assert_eq!(get_changed(&world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)"); assert_eq!(get_changed_a(&world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)");
// spawning a new A entity should not change existing modified state // spawning a new A entity should not change existing modified state
world.insert(e1, (A(0), B)).unwrap(); world.insert(e1, (A(0), B)).unwrap();
assert_eq!(get_changed(&world), vec![e3, e1], "changed entities list should not change"); assert_eq!(
get_changed_a(&world),
vec![e3, e1],
"changed entities list should not change"
);
// removing an unchanged entity should not change modified state // removing an unchanged entity should not change modified state
world.despawn(e2).unwrap(); world.despawn(e2).unwrap();
assert_eq!(get_changed(&world), vec![e3, e1], "changed entities list should not change"); assert_eq!(
get_changed_a(&world),
vec![e3, e1],
"changed entities list should not change"
);
// removing a changed entity should remove it from enumeration // removing a changed entity should remove it from enumeration
world.despawn(e1).unwrap(); world.despawn(e1).unwrap();
assert_eq!(get_changed(&world), vec![e3], "e1 should no longer be returned"); assert_eq!(
get_changed_a(&world),
vec![e3],
"e1 should no longer be returned"
);
world.clear_trackers(); world.clear_trackers();
@ -180,4 +192,26 @@ mod tests {
.collect::<Vec<Entity>>() .collect::<Vec<Entity>>()
.is_empty()); .is_empty());
} }
#[test]
fn nested_changed_query() {
let mut world = World::default();
world.spawn((A(0), B(0)));
let e2 = world.spawn((A(0), B(0)));
world.spawn((A(0), B(0)));
for mut a in world.query::<Track<A>>().iter() {
a.0 += 1;
}
for mut b in world.query::<Track<B>>().iter().skip(1).take(1) {
b.0 += 1;
}
let a_b_changed = world
.query::<Changed<A, Changed<B, Entity>>>()
.iter()
.collect::<Vec<Entity>>();
assert_eq!(a_b_changed, vec![e2]);
}
} }