-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
37c15ae
commit bc9a2a3
Showing
6 changed files
with
325 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
use std::ops::{Deref, DerefMut}; | ||
|
||
use bevy::ecs::{ | ||
query::{QueryData, QueryItem, ROQueryItem}, | ||
system::{ParamBuilder, SystemParam}, | ||
}; | ||
pub use bevy::prelude::*; | ||
|
||
use super::*; | ||
|
||
pub struct Given<'w, 's, D: QueryData, Marker = ()> { | ||
given: Entity, | ||
query: Query<'w, 's, D>, | ||
_ph: PhantomData<Marker>, | ||
} | ||
|
||
impl<'w, 's, D: QueryData, Label> Given<'w, 's, D, Label> { | ||
pub fn new(given: Entity, query: Query<'w, 's, D>) -> Self { | ||
Self { | ||
given, | ||
query, | ||
_ph: PhantomData, | ||
} | ||
} | ||
|
||
pub fn get(&self) -> ROQueryItem<D> { self.query.get(self.given).unwrap() } | ||
pub fn get_mut(&mut self) -> QueryItem<D> { self.query.get_mut(self.given).unwrap() } | ||
} | ||
|
||
impl<'w, 's, D: QueryData, Label> Deref for Given<'w, 's, D, Label> { | ||
type Target = D; | ||
|
||
fn deref(&self) -> &Self::Target { todo!() } | ||
} | ||
impl<'w, 's, D: QueryData, Label> DerefMut for Given<'w, 's, D, Label> { | ||
fn deref_mut(&mut self) -> &mut Self::Target { todo!() } | ||
} | ||
|
||
unsafe impl<'w, 's, D: QueryData + 'static, Label> SystemParam for Given<'w, 's, D, Label> { | ||
type State = (Entity, <Query<'w, 's, D> as SystemParam>::State); | ||
type Item<'world, 'state> = Given<'world, 'state, D, Label>; | ||
|
||
fn init_state( | ||
_world: &mut World, | ||
_system_meta: &mut bevy::ecs::system::SystemMeta, | ||
) -> Self::State { | ||
panic!("Given must be initialized by a SystemParamBuilder to provide an Entity"); | ||
} | ||
|
||
unsafe fn get_param<'world, 'state>( | ||
&mut (given, ref mut query_state): &'state mut Self::State, | ||
_system_meta: &bevy::ecs::system::SystemMeta, | ||
_world: UnsafeWorldCell<'world>, | ||
_change_tick: Tick, | ||
) -> Self::Item<'world, 'state> { | ||
// SAFETY: The state was initialized using the GivenParamBuilder, which forwards to Query. | ||
unsafe { | ||
Self::Item::new( | ||
given, | ||
<Query<'w, 's, D> as SystemParam>::get_param( | ||
query_state, | ||
_system_meta, | ||
_world, | ||
_change_tick, | ||
), | ||
) | ||
} | ||
} | ||
} | ||
|
||
#[derive(Copy, Clone, Debug)] | ||
pub struct GivenBuilder { | ||
given: Entity, | ||
} | ||
|
||
impl GivenBuilder { | ||
pub fn new(given: Entity) -> Self { Self { given } } | ||
} | ||
|
||
unsafe impl<'w, 's, D: QueryData + 'static, Label> SystemParamBuilder<Given<'w, 's, D, Label>> | ||
for GivenBuilder | ||
{ | ||
fn build( | ||
self, | ||
world: &mut World, | ||
meta: &mut bevy::ecs::system::SystemMeta, | ||
) -> <Given<'w, 's, D, Label> as SystemParam>::State { | ||
( | ||
self.given, | ||
ParamBuilder::of::<Query<D>>().build(world, meta), | ||
) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
#[cfg(test)] | ||
fn no_val_panic() { | ||
let mut world = World::new(); | ||
|
||
let mut sys = (ParamBuilder,) | ||
.build_state(&mut world) | ||
.build_any_system(|given: Given<Entity>| given.get()); | ||
|
||
let _result: Entity = sys.run((), &mut world); | ||
} | ||
|
||
#[cfg(test)] | ||
fn entity() { | ||
let mut world = World::new(); | ||
|
||
let entity = world.spawn(()).id(); | ||
let mut sys = (GivenBuilder::new(entity),) | ||
.build_state(&mut world) | ||
.build_any_system(|given: Given<Entity>| given.get()); | ||
|
||
let result: Entity = sys.run((), &mut world); | ||
|
||
assert_eq!(result, entity); | ||
} | ||
|
||
#[cfg(test)] | ||
fn component() { | ||
let mut world = World::new(); | ||
|
||
#[derive(Component)] | ||
struct C(u32); | ||
|
||
let entity = world.spawn_batch((1..10).map(C)).nth(7).unwrap(); | ||
let mut sys = (GivenBuilder::new(entity),) | ||
.build_state(&mut world) | ||
.build_any_system(|given: Given<&C>| given.get().0); | ||
|
||
let result: u32 = sys.run((), &mut world); | ||
|
||
assert_eq!(result, 32); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
use std::{any::TypeId, marker::PhantomData}; | ||
|
||
use bevy::{ | ||
ecs::system::{LocalBuilder, SystemMeta, SystemParam}, | ||
prelude::{SystemParamBuilder, *}, | ||
}; | ||
use derive_where::derive_where; | ||
|
||
pub trait ICantBelieveItsNotClone { | ||
type Butter; | ||
fn i_cant_believe_its_not_clone(&self) -> Self::Butter; | ||
} | ||
|
||
impl<C: Clone> ICantBelieveItsNotClone for &C { | ||
type Butter = C; | ||
fn i_cant_believe_its_not_clone(&self) -> Self::Butter { C::clone(self) } | ||
} | ||
|
||
impl<T: Clone> ICantBelieveItsNotClone for LocalBuilder<T> { | ||
type Butter = Self; | ||
fn i_cant_believe_its_not_clone(&self) -> Self::Butter { LocalBuilder(self.0.clone()) } | ||
} | ||
|
||
#[derive_where(Copy, Clone; IfP, ElseQ)] | ||
pub struct OverlayBuilder<P, Q, IfP, ElseQ> { | ||
i: IfP, | ||
e: ElseQ, | ||
_ph: PhantomData<(P, Q)>, | ||
} | ||
|
||
impl<P, Q, IfP, ElseQ> OverlayBuilder<P, Q, IfP, ElseQ> | ||
where | ||
P: SystemParam + 'static, | ||
Q: SystemParam + 'static, | ||
IfP: SystemParamBuilder<P>, | ||
ElseQ: SystemParamBuilder<Q>, | ||
{ | ||
pub fn new(i: IfP, e: ElseQ) -> Self { | ||
Self { | ||
i, | ||
e, | ||
_ph: PhantomData, | ||
} | ||
} | ||
} | ||
|
||
type ParamState<P> = <P as SystemParam>::State; | ||
|
||
unsafe impl<P, Q, IfP, ElseQ> SystemParamBuilder<Q> for OverlayBuilder<P, Q, IfP, ElseQ> | ||
where | ||
P: SystemParam + 'static, | ||
Q: SystemParam + 'static, | ||
IfP: SystemParamBuilder<P>, | ||
ElseQ: SystemParamBuilder<Q>, | ||
{ | ||
fn build(self, world: &mut World, meta: &mut SystemMeta) -> ParamState<Q> { | ||
let p_ty = TypeId::of::<P>(); | ||
let q_ty = TypeId::of::<Q>(); | ||
if p_ty == q_ty { | ||
let mut out: ParamState<P> = self.i.build(world, meta); | ||
// SAFETY: We proved above that P == Q, so this operations are valid by substitution. | ||
// There are also no implicit hidden lifetime parameters in ParamState. | ||
unsafe { (&mut out as *mut ParamState<P> as *mut ParamState<Q>).read() } | ||
} else { | ||
self.e.build(world, meta) | ||
} | ||
} | ||
} | ||
|
||
pub fn overlay_matching<OParam, OBuilder, Param1, Param2, Builder1, Builder2>( | ||
overlay: OBuilder, | ||
(builder1, builder2): (Builder1, Builder2), | ||
) -> ( | ||
OverlayBuilder<OParam, Param1, OBuilder, Builder1>, | ||
OverlayBuilder<OParam, Param2, OBuilder, Builder2>, | ||
) | ||
where | ||
OParam: SystemParam + 'static, | ||
OBuilder: SystemParamBuilder<OParam> + ICantBelieveItsNotClone<Butter = OBuilder>, | ||
Param1: SystemParam + 'static, | ||
Param2: SystemParam + 'static, | ||
Builder1: SystemParamBuilder<Param1>, | ||
Builder2: SystemParamBuilder<Param2>, | ||
{ | ||
( | ||
OverlayBuilder::new(overlay.i_cant_believe_its_not_clone(), builder1), | ||
OverlayBuilder::new(overlay, builder2), | ||
) | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use bevy::ecs::system::{LocalBuilder, ParamBuilder}; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn overlay_eq() { | ||
let mut world = World::new(); | ||
|
||
let overlay = LocalBuilder(true); | ||
let builder = OverlayBuilder::<Local<bool>, Local<bool>, _, _>::new( | ||
overlay, | ||
ParamBuilder::of::<Local<bool>>(), | ||
); | ||
|
||
let mut sys = (builder,) | ||
.build_state(&mut world) | ||
.build_any_system(|b: Local<bool>| *b); | ||
|
||
let result: bool = sys.run((), &mut world); | ||
|
||
assert!(result); | ||
} | ||
|
||
#[test] | ||
fn overlay_ne() { | ||
let mut world = World::new(); | ||
|
||
let overlay = LocalBuilder(1); | ||
let builder = | ||
OverlayBuilder::<Local<u32>, _, _, _>::new(overlay, ParamBuilder::of::<Local<bool>>()); | ||
|
||
let mut sys = (builder,) | ||
.build_state(&mut world) | ||
.build_any_system(|b: Local<bool>| *b); | ||
|
||
let result = sys.run((), &mut world); | ||
|
||
assert!(!result); | ||
} | ||
|
||
#[test] | ||
fn overlay_2() { | ||
let mut world = World::new(); | ||
|
||
let overlay = LocalBuilder(true); | ||
macro_rules! builder { | ||
() => { | ||
( | ||
ParamBuilder::of::<Local<u32>>(), | ||
ParamBuilder::of::<Local<bool>>(), | ||
) | ||
}; | ||
} | ||
let overlay_builder = overlay_matching(overlay, builder!()); | ||
|
||
let sys_fn = |u: Local<u32>, b: Local<bool>| (*u, *b); | ||
let mut sys = builder!().build_state(&mut world).build_any_system(sys_fn); | ||
let mut overlay_sys = overlay_builder | ||
.build_state(&mut world) | ||
.build_any_system(sys_fn); | ||
|
||
let result = sys.run((), &mut world); | ||
let overlay_result = overlay_sys.run((), &mut world); | ||
|
||
// Check that the no-overlay version had a different result first, to test the test. | ||
assert_eq!(result, (0, false)); | ||
assert_eq!(overlay_result, (0, true)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters