lyra-engine/lyra-ecs/src/query/borrow.rs

296 lines
7.5 KiB
Rust

use std::{marker::PhantomData, ptr::NonNull, cell::{Ref, RefMut}};
use crate::{World, ComponentColumn, DynTypeId, Tick, Component};
use super::{Fetch, Query, AsQuery};
/// Fetcher for borrowing components from archetypes.
pub struct FetchBorrow<'a, T> {
col: &'a ComponentColumn,
_phantom: PhantomData<&'a T>
}
impl<'a, T> Fetch<'a> for FetchBorrow<'a, T>
where
T: 'a,
{
type Item = Ref<'a, T>;
fn dangling() -> Self {
unreachable!()
}
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
self.col.get(entity.0 as _)
}
}
/// A Query for borrowing components from archetypes.
///
/// Since [`AsQuery`] is implemented for `&T`, you can use this query like this:
/// ```nobuild
/// for ts in world.view::<&T>() {
/// println!("Got a &T!");
/// }
/// ```
pub struct QueryBorrow<T> {
type_id: DynTypeId,
_phantom: PhantomData<T>
}
impl<T: 'static> Default for QueryBorrow<T> {
fn default() -> Self {
Self {
type_id: DynTypeId::of::<T>(),
_phantom: PhantomData,
}
}
}
impl<T> Copy for QueryBorrow<T> {}
impl<T> Clone for QueryBorrow<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> QueryBorrow<T> {
pub fn new() -> Self {
Self::default()
}
}
impl<T> Query for QueryBorrow<T>
where
T: 'static
{
type Item<'a> = Ref<'a, T>;
type Fetch<'a> = FetchBorrow<'a, T>;
fn new() -> Self {
QueryBorrow::<T>::new()
}
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
archetype.has_column(self.type_id)
}
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
let _ = tick;
let col = archetype.get_column(self.type_id)
.expect("You ignored 'can_visit_archetype'!");
FetchBorrow {
col,
_phantom: PhantomData,
}
}
}
impl<T: Component> AsQuery for QueryBorrow<T> {
type Query = Self;
}
impl<T: Component> AsQuery for &T {
type Query = QueryBorrow<T>;
}
/// A fetcher for mutably borrowing components from archetypes.
pub struct FetchBorrowMut<'a, T> {
col: NonNull<ComponentColumn>,
tick: Tick,
_phantom: PhantomData<&'a T>
}
impl<'a, T> Fetch<'a> for FetchBorrowMut<'a, T>
where
T: 'a,
{
type Item = RefMut<'a, T>;
fn dangling() -> Self {
unreachable!()
}
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
let col = unsafe { self.col.as_mut() };
col.get_mut(entity.0 as _, &self.tick)
}
}
/// A Query for mutably borrowing components from archetypes.
///
/// Since [`AsQuery`] is implemented for `&mut T`, you can use this query like this:
/// ```nobuild
/// for ts in world.view::<&mut T>() {
/// println!("Got an &mut T!");
/// }
/// ```
pub struct QueryBorrowMut<T> {
type_id: DynTypeId,
_phantom: PhantomData<T>
}
impl<T: 'static> Default for QueryBorrowMut<T> {
fn default() -> Self {
Self {
type_id: DynTypeId::of::<T>(),
_phantom: PhantomData,
}
}
}
impl<T> Copy for QueryBorrowMut<T> {}
impl<T> Clone for QueryBorrowMut<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> QueryBorrowMut<T> {
pub fn new() -> Self {
Self::default()
}
}
impl<T> Query for QueryBorrowMut<T>
where
T: 'static
{
type Item<'a> = RefMut<'a, T>;
type Fetch<'a> = FetchBorrowMut<'a, T>;
const MUTATES: bool = true;
fn new() -> Self {
QueryBorrowMut::<T>::new()
}
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
archetype.has_column(self.type_id)
}
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
let col = archetype.get_column(self.type_id)
.expect("You ignored 'can_visit_archetype'!");
// TODO: find a way to get the component column mutable with a borrowed archetype so its tick can be updated.
// the fetcher needs to tick the entities tick in the archetype
let col = NonNull::from(col);
FetchBorrowMut {
col,
tick,
_phantom: PhantomData,
}
}
}
impl<T: Component> AsQuery for QueryBorrowMut<T> {
type Query = Self;
}
impl<T: Component> AsQuery for &mut T {
type Query = QueryBorrowMut<T>;
}
#[cfg(test)]
mod tests {
use std::{marker::PhantomData, ptr::NonNull};
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{Fetch, ViewState}, tests::Vec2, World, DynTypeId, Entity, EntityId, Tick};
use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut};
/// Creates a world with two entities, one at Vec(10, 50) and
/// the other at Vec2(25, 30).
fn prepare_world() -> World {
let mut world = World::new();
world.spawn((Vec2::new(10.0, 50.0),));
world.spawn((Vec2::new(25.0, 30.0),));
world
}
#[test]
fn borrow_query() {
let world = prepare_world();
let borrow = QueryBorrow::<Vec2> {
type_id: DynTypeId::of::<Vec2>(),
_phantom: std::marker::PhantomData,
};
let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
let v = ViewState::<QueryBorrow<Vec2>, ()>::new(&world, borrow, (), archetypes);
for e in v.into_iter() {
println!("Found entity at {:?}", e);
}
}
#[test]
fn borrow_mut_query() {
let world = prepare_world();
let borrow = QueryBorrowMut::<Vec2> {
type_id: DynTypeId::of::<Vec2>(),
_phantom: std::marker::PhantomData,
};
let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
let v = ViewState::<QueryBorrowMut<Vec2>, ()>::new(&world, borrow, (), archetypes);
let mut orig = vec![];
for mut v in v.into_iter() {
orig.push(v.clone());
v.x += 10.0;
v.y += 10.0;
}
// Now make sure the changes were actually made
let borrow = QueryBorrow::<Vec2> {
type_id: DynTypeId::of::<Vec2>(),
_phantom: std::marker::PhantomData,
};
let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
let v = ViewState::<QueryBorrow<Vec2>, ()>::new(&world, borrow, (), archetypes);
for (new, orig) in v.into_iter().zip(orig.iter()) {
assert!(new.x - orig.x == 10.0);
assert!(new.y - orig.y == 10.0);
}
println!("They were modified!");
}
#[test]
fn only_one_mutable_borrow() {
let info = (Vec2::new(0.0, 0.0),).info();
let mut a = Archetype::from_bundle_info(ArchetypeId(0), info);
for i in 0..10 {
a.add_entity(Entity {
id: EntityId(i),
generation: 0,
}, (Vec2::rand(),), &Tick::default());
}
let col = a.get_column(DynTypeId::of::<Vec2>()).unwrap();
let mut bmut = FetchBorrowMut::<Vec2> {
col: NonNull::from(col),
tick: Tick::default(),
_phantom: PhantomData,
};
let item = unsafe { bmut.get_item(crate::ArchetypeEntityId(0)) };
// ensure only one mutable borrow can be done at a time
assert!(col.try_borrow_mut_ptr().is_err());
drop(item);
assert!(col.try_borrow_mut_ptr().is_ok());
}
}