2020-11-08 20:34:05 +00:00
|
|
|
use crate::{
|
2021-03-03 01:59:40 +00:00
|
|
|
ArchetypeComponent, Commands, Fetch, FromResources, Local, NonSend, Or, Query, QueryAccess,
|
|
|
|
QueryFilter, QuerySet, QueryTuple, Res, ResMut, Resource, ResourceIndex, Resources,
|
|
|
|
SystemState, TypeAccess, World, WorldQuery,
|
2020-11-08 20:34:05 +00:00
|
|
|
};
|
|
|
|
use parking_lot::Mutex;
|
2020-12-16 05:57:16 +00:00
|
|
|
use std::{any::TypeId, marker::PhantomData, sync::Arc};
|
2021-03-03 03:11:11 +00:00
|
|
|
|
|
|
|
/// A parameter that can be used in a system function
|
|
|
|
///
|
|
|
|
/// # Derive
|
|
|
|
/// This trait can be derived.
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use bevy_ecs::prelude::*;
|
|
|
|
/// use bevy_ecs::SystemParam;
|
|
|
|
///
|
|
|
|
/// #[derive(SystemParam)]
|
|
|
|
/// pub struct MyParam<'a> {
|
|
|
|
/// foo: Res<'a, usize>,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// fn my_system(param: MyParam) {
|
|
|
|
/// // Access the resource through `param.foo`
|
|
|
|
/// }
|
|
|
|
/// ```
|
2020-12-16 05:57:16 +00:00
|
|
|
pub trait SystemParam: Sized {
|
|
|
|
type Fetch: for<'a> FetchSystemParam<'a>;
|
2020-11-17 02:18:00 +00:00
|
|
|
}
|
|
|
|
|
2020-12-16 05:57:16 +00:00
|
|
|
pub trait FetchSystemParam<'a> {
|
|
|
|
type Item;
|
2020-11-08 20:34:05 +00:00
|
|
|
fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources);
|
|
|
|
/// # Safety
|
|
|
|
/// This call might access any of the input parameters in an unsafe way. Make sure the data access is safe in
|
|
|
|
/// the context of the system scheduler
|
|
|
|
unsafe fn get_param(
|
2020-12-16 05:57:16 +00:00
|
|
|
system_state: &'a SystemState,
|
|
|
|
world: &'a World,
|
|
|
|
resources: &'a Resources,
|
|
|
|
) -> Option<Self::Item>;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct FetchQuery<Q, F>(PhantomData<(Q, F)>);
|
|
|
|
|
|
|
|
impl<'a, Q: WorldQuery, F: QueryFilter> SystemParam for Query<'a, Q, F> {
|
|
|
|
type Fetch = FetchQuery<Q, F>;
|
2020-11-08 20:34:05 +00:00
|
|
|
}
|
|
|
|
|
2020-12-16 05:57:16 +00:00
|
|
|
impl<'a, Q: WorldQuery, F: QueryFilter> FetchSystemParam<'a> for FetchQuery<Q, F> {
|
|
|
|
type Item = Query<'a, Q, F>;
|
|
|
|
|
2020-11-08 20:34:05 +00:00
|
|
|
#[inline]
|
|
|
|
unsafe fn get_param(
|
2020-12-16 05:57:16 +00:00
|
|
|
system_state: &'a SystemState,
|
|
|
|
world: &'a World,
|
|
|
|
_resources: &'a Resources,
|
|
|
|
) -> Option<Self::Item> {
|
|
|
|
let query_index = *system_state.current_query_index.get();
|
2020-11-08 20:34:05 +00:00
|
|
|
let archetype_component_access: &'a TypeAccess<ArchetypeComponent> =
|
2020-12-16 05:57:16 +00:00
|
|
|
&system_state.query_archetype_component_accesses[query_index];
|
|
|
|
*system_state.current_query_index.get() += 1;
|
2020-11-08 20:34:05 +00:00
|
|
|
Some(Query::new(world, archetype_component_access))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) {
|
|
|
|
system_state
|
|
|
|
.query_archetype_component_accesses
|
|
|
|
.push(TypeAccess::default());
|
2020-11-11 04:48:34 +00:00
|
|
|
let access = QueryAccess::union(vec![Q::Fetch::access(), F::access()]);
|
2021-02-09 20:14:10 +00:00
|
|
|
access.get_component_access(&mut system_state.component_access);
|
2020-11-11 04:48:34 +00:00
|
|
|
system_state.query_accesses.push(vec![access]);
|
2020-11-08 20:34:05 +00:00
|
|
|
system_state
|
|
|
|
.query_type_names
|
|
|
|
.push(std::any::type_name::<Q>());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 05:57:16 +00:00
|
|
|
pub struct FetchQuerySet<T>(PhantomData<T>);
|
|
|
|
|
|
|
|
impl<T: QueryTuple> SystemParam for QuerySet<T> {
|
|
|
|
type Fetch = FetchQuerySet<T>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, T: QueryTuple> FetchSystemParam<'a> for FetchQuerySet<T> {
|
|
|
|
type Item = QuerySet<T>;
|
|
|
|
|
2020-11-08 20:34:05 +00:00
|
|
|
#[inline]
|
|
|
|
unsafe fn get_param(
|
2020-12-16 05:57:16 +00:00
|
|
|
system_state: &'a SystemState,
|
|
|
|
world: &'a World,
|
|
|
|
_resources: &'a Resources,
|
|
|
|
) -> Option<Self::Item> {
|
|
|
|
let query_index = *system_state.current_query_index.get();
|
|
|
|
*system_state.current_query_index.get() += 1;
|
2020-11-08 20:34:05 +00:00
|
|
|
Some(QuerySet::new(
|
|
|
|
world,
|
|
|
|
&system_state.query_archetype_component_accesses[query_index],
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) {
|
|
|
|
system_state
|
|
|
|
.query_archetype_component_accesses
|
|
|
|
.push(TypeAccess::default());
|
2021-02-09 20:14:10 +00:00
|
|
|
let accesses = T::get_accesses();
|
|
|
|
for access in &accesses {
|
|
|
|
access.get_component_access(&mut system_state.component_access);
|
|
|
|
}
|
|
|
|
system_state.query_accesses.push(accesses);
|
2020-11-08 20:34:05 +00:00
|
|
|
system_state
|
|
|
|
.query_type_names
|
|
|
|
.push(std::any::type_name::<T>());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 05:57:16 +00:00
|
|
|
pub struct FetchCommands;
|
|
|
|
|
|
|
|
impl<'a> SystemParam for &'a mut Commands {
|
|
|
|
type Fetch = FetchCommands;
|
|
|
|
}
|
|
|
|
impl<'a> FetchSystemParam<'a> for FetchCommands {
|
|
|
|
type Item = &'a mut Commands;
|
|
|
|
|
2020-11-08 20:34:05 +00:00
|
|
|
fn init(system_state: &mut SystemState, world: &World, _resources: &mut Resources) {
|
2020-12-16 05:57:16 +00:00
|
|
|
// SAFE: this is called with unique access to SystemState
|
|
|
|
unsafe {
|
|
|
|
(&mut *system_state.commands.get()).set_entity_reserver(world.get_entity_reserver())
|
|
|
|
}
|
2020-11-08 20:34:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
unsafe fn get_param(
|
2020-12-16 05:57:16 +00:00
|
|
|
system_state: &'a SystemState,
|
|
|
|
_world: &'a World,
|
|
|
|
_resources: &'a Resources,
|
|
|
|
) -> Option<Self::Item> {
|
|
|
|
Some(&mut *system_state.commands.get())
|
2020-11-08 20:34:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 05:57:16 +00:00
|
|
|
pub struct FetchArcCommands;
|
|
|
|
impl SystemParam for Arc<Mutex<Commands>> {
|
|
|
|
type Fetch = FetchArcCommands;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FetchSystemParam<'a> for FetchArcCommands {
|
|
|
|
type Item = Arc<Mutex<Commands>>;
|
|
|
|
|
2020-11-08 20:34:05 +00:00
|
|
|
fn init(system_state: &mut SystemState, world: &World, _resources: &mut Resources) {
|
|
|
|
system_state.arc_commands.get_or_insert_with(|| {
|
|
|
|
let mut commands = Commands::default();
|
|
|
|
commands.set_entity_reserver(world.get_entity_reserver());
|
|
|
|
Arc::new(Mutex::new(commands))
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
unsafe fn get_param(
|
2020-12-16 05:57:16 +00:00
|
|
|
system_state: &SystemState,
|
2020-11-08 20:34:05 +00:00
|
|
|
_world: &World,
|
|
|
|
_resources: &Resources,
|
2020-12-16 05:57:16 +00:00
|
|
|
) -> Option<Self::Item> {
|
2020-11-08 20:34:05 +00:00
|
|
|
Some(system_state.arc_commands.as_ref().unwrap().clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 05:57:16 +00:00
|
|
|
pub struct FetchRes<T>(PhantomData<T>);
|
|
|
|
|
|
|
|
impl<'a, T: Resource> SystemParam for Res<'a, T> {
|
|
|
|
type Fetch = FetchRes<T>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, T: Resource> FetchSystemParam<'a> for FetchRes<T> {
|
|
|
|
type Item = Res<'a, T>;
|
|
|
|
|
2020-11-08 20:34:05 +00:00
|
|
|
fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) {
|
2020-11-15 20:42:43 +00:00
|
|
|
if system_state.resource_access.is_write(&TypeId::of::<T>()) {
|
|
|
|
panic!(
|
|
|
|
"System `{}` has a `Res<{res}>` parameter that conflicts with \
|
|
|
|
another parameter with mutable access to the same `{res}` resource.",
|
|
|
|
system_state.name,
|
|
|
|
res = std::any::type_name::<T>()
|
|
|
|
);
|
|
|
|
}
|
2020-11-08 20:34:05 +00:00
|
|
|
system_state.resource_access.add_read(TypeId::of::<T>());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
unsafe fn get_param(
|
2020-12-16 05:57:16 +00:00
|
|
|
_system_state: &'a SystemState,
|
|
|
|
_world: &'a World,
|
|
|
|
resources: &'a Resources,
|
|
|
|
) -> Option<Self::Item> {
|
2021-03-03 01:59:40 +00:00
|
|
|
let result = resources.get_unsafe_ref_with_added_and_mutated::<T>(ResourceIndex::Global);
|
|
|
|
Some(Res::new(result.0, *result.1.as_ptr(), *result.2.as_ptr()))
|
2020-11-08 20:34:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 05:57:16 +00:00
|
|
|
pub struct FetchResMut<T>(PhantomData<T>);
|
|
|
|
|
|
|
|
impl<'a, T: Resource> SystemParam for ResMut<'a, T> {
|
|
|
|
type Fetch = FetchResMut<T>;
|
|
|
|
}
|
|
|
|
impl<'a, T: Resource> FetchSystemParam<'a> for FetchResMut<T> {
|
|
|
|
type Item = ResMut<'a, T>;
|
|
|
|
|
2020-11-08 20:34:05 +00:00
|
|
|
fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) {
|
2020-11-15 20:42:43 +00:00
|
|
|
// If a system already has access to the resource in another parameter, then we fail early.
|
|
|
|
// e.g. `fn(Res<Foo>, ResMut<Foo>)` or `fn(ResMut<Foo>, ResMut<Foo>)` must not be allowed.
|
|
|
|
if system_state
|
|
|
|
.resource_access
|
|
|
|
.is_read_or_write(&TypeId::of::<T>())
|
|
|
|
{
|
|
|
|
panic!(
|
|
|
|
"System `{}` has a `ResMut<{res}>` parameter that conflicts with \
|
|
|
|
another parameter to the same `{res}` resource. `ResMut` must have unique access.",
|
|
|
|
system_state.name,
|
|
|
|
res = std::any::type_name::<T>()
|
|
|
|
);
|
|
|
|
}
|
2020-11-08 20:34:05 +00:00
|
|
|
system_state.resource_access.add_write(TypeId::of::<T>());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
unsafe fn get_param(
|
2020-12-16 05:57:16 +00:00
|
|
|
_system_state: &'a SystemState,
|
|
|
|
_world: &'a World,
|
|
|
|
resources: &'a Resources,
|
|
|
|
) -> Option<Self::Item> {
|
2020-11-17 00:55:59 +00:00
|
|
|
let (value, added, mutated) =
|
|
|
|
resources.get_unsafe_ref_with_added_and_mutated::<T>(ResourceIndex::Global);
|
2021-03-03 01:59:40 +00:00
|
|
|
Some(ResMut::new(value, *added.as_ptr(), mutated))
|
2020-11-08 20:34:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 05:57:16 +00:00
|
|
|
pub struct FetchLocal<T>(PhantomData<T>);
|
|
|
|
|
|
|
|
impl<'a, T: Resource + FromResources> SystemParam for Local<'a, T> {
|
|
|
|
type Fetch = FetchLocal<T>;
|
|
|
|
}
|
|
|
|
impl<'a, T: Resource + FromResources> FetchSystemParam<'a> for FetchLocal<T> {
|
|
|
|
type Item = Local<'a, T>;
|
|
|
|
|
2020-11-08 20:34:05 +00:00
|
|
|
fn init(system_state: &mut SystemState, _world: &World, resources: &mut Resources) {
|
2020-11-15 20:42:43 +00:00
|
|
|
if system_state
|
|
|
|
.local_resource_access
|
|
|
|
.is_read_or_write(&TypeId::of::<T>())
|
|
|
|
{
|
|
|
|
panic!(
|
|
|
|
"System `{}` has multiple parameters requesting access to a local resource of type `{}`. \
|
|
|
|
There may be at most one `Local` parameter per resource type.",
|
|
|
|
system_state.name,
|
|
|
|
std::any::type_name::<T>()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// A resource could have been already initialized by another system with
|
|
|
|
// `Commands::insert_local_resource` or `Resources::insert_local`
|
2020-11-08 20:34:05 +00:00
|
|
|
if resources.get_local::<T>(system_state.id).is_none() {
|
|
|
|
let value = T::from_resources(resources);
|
|
|
|
resources.insert_local(system_state.id, value);
|
|
|
|
}
|
2020-11-15 20:42:43 +00:00
|
|
|
|
|
|
|
system_state
|
|
|
|
.local_resource_access
|
|
|
|
.add_write(TypeId::of::<T>());
|
2020-11-08 20:34:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
unsafe fn get_param(
|
2020-12-16 05:57:16 +00:00
|
|
|
system_state: &'a SystemState,
|
|
|
|
_world: &'a World,
|
|
|
|
resources: &'a Resources,
|
|
|
|
) -> Option<Self::Item> {
|
2020-11-08 20:34:05 +00:00
|
|
|
Some(Local::new(resources, system_state.id))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-09 20:14:10 +00:00
|
|
|
pub struct FetchNonSend<T>(PhantomData<T>);
|
|
|
|
|
|
|
|
impl<'a, T: Resource> SystemParam for NonSend<'a, T> {
|
|
|
|
type Fetch = FetchNonSend<T>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, T: Resource> FetchSystemParam<'a> for FetchNonSend<T> {
|
|
|
|
type Item = NonSend<'a, T>;
|
|
|
|
|
|
|
|
fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) {
|
|
|
|
// !Send systems run only on the main thread, so only one system
|
|
|
|
// at a time will ever access any thread-local resource.
|
|
|
|
system_state.is_non_send = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
unsafe fn get_param(
|
|
|
|
_system_state: &'a SystemState,
|
|
|
|
_world: &'a World,
|
|
|
|
resources: &'a Resources,
|
|
|
|
) -> Option<Self::Item> {
|
|
|
|
Some(NonSend::new(resources))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 05:57:16 +00:00
|
|
|
pub struct FetchParamTuple<T>(PhantomData<T>);
|
|
|
|
pub struct FetchOr<T>(PhantomData<T>);
|
|
|
|
|
2020-11-08 20:34:05 +00:00
|
|
|
macro_rules! impl_system_param_tuple {
|
|
|
|
($($param: ident),*) => {
|
2020-12-16 05:57:16 +00:00
|
|
|
impl<$($param: SystemParam),*> SystemParam for ($($param,)*) {
|
|
|
|
type Fetch = FetchParamTuple<($($param::Fetch,)*)>;
|
|
|
|
}
|
2020-11-08 20:34:05 +00:00
|
|
|
#[allow(unused_variables)]
|
2020-12-16 05:57:16 +00:00
|
|
|
impl<'a, $($param: FetchSystemParam<'a>),*> FetchSystemParam<'a> for FetchParamTuple<($($param,)*)> {
|
|
|
|
type Item = ($($param::Item,)*);
|
2020-11-08 20:34:05 +00:00
|
|
|
fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources) {
|
|
|
|
$($param::init(system_state, world, resources);)*
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
unsafe fn get_param(
|
2020-12-16 05:57:16 +00:00
|
|
|
system_state: &'a SystemState,
|
|
|
|
world: &'a World,
|
|
|
|
resources: &'a Resources,
|
|
|
|
) -> Option<Self::Item> {
|
|
|
|
Some(($($param::get_param(system_state, world, resources)?,)*))
|
2020-11-08 20:34:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 05:57:16 +00:00
|
|
|
impl<$($param: SystemParam),*> SystemParam for Or<($(Option<$param>,)*)> {
|
|
|
|
type Fetch = FetchOr<($($param::Fetch,)*)>;
|
|
|
|
}
|
|
|
|
|
2020-11-08 20:34:05 +00:00
|
|
|
#[allow(unused_variables)]
|
|
|
|
#[allow(unused_mut)]
|
|
|
|
#[allow(non_snake_case)]
|
2020-12-16 05:57:16 +00:00
|
|
|
impl<'a, $($param: FetchSystemParam<'a>),*> FetchSystemParam<'a> for FetchOr<($($param,)*)> {
|
|
|
|
type Item = Or<($(Option<$param::Item>,)*)>;
|
2020-11-08 20:34:05 +00:00
|
|
|
fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources) {
|
|
|
|
$($param::init(system_state, world, resources);)*
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
unsafe fn get_param(
|
2020-12-16 05:57:16 +00:00
|
|
|
system_state: &'a SystemState,
|
|
|
|
world: &'a World,
|
|
|
|
resources: &'a Resources,
|
|
|
|
) -> Option<Self::Item> {
|
2020-11-08 20:34:05 +00:00
|
|
|
let mut has_some = false;
|
|
|
|
$(
|
2020-12-16 05:57:16 +00:00
|
|
|
let $param = $param::get_param(system_state, world, resources);
|
2020-11-08 20:34:05 +00:00
|
|
|
if $param.is_some() {
|
|
|
|
has_some = true;
|
|
|
|
}
|
|
|
|
)*
|
|
|
|
|
|
|
|
if has_some {
|
|
|
|
Some(Or(($($param,)*)))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_system_param_tuple!();
|
|
|
|
impl_system_param_tuple!(A);
|
|
|
|
impl_system_param_tuple!(A, B);
|
|
|
|
impl_system_param_tuple!(A, B, C);
|
|
|
|
impl_system_param_tuple!(A, B, C, D);
|
|
|
|
impl_system_param_tuple!(A, B, C, D, E);
|
|
|
|
impl_system_param_tuple!(A, B, C, D, E, F);
|
|
|
|
impl_system_param_tuple!(A, B, C, D, E, F, G);
|
|
|
|
impl_system_param_tuple!(A, B, C, D, E, F, G, H);
|
|
|
|
impl_system_param_tuple!(A, B, C, D, E, F, G, H, I);
|
|
|
|
impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J);
|
|
|
|
impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K);
|
|
|
|
impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
|
|
|
|
impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
|
|
|
|
impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
|
|
|
impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
|
|
|
impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|