bevy/crates/bevy_ecs/src/system/into_system.rs
Alice Cecile e4e32598a9 Cargo fmt with unstable features (#1903)
Fresh version of #1670 off the latest main.

Mostly fixing documentation wrapping.
2021-04-21 23:19:34 +00:00

298 lines
9.3 KiB
Rust

use crate::{
archetype::{Archetype, ArchetypeComponentId},
component::ComponentId,
query::{Access, FilteredAccessSet},
system::{
check_system_change_tick, System, SystemId, SystemParam, SystemParamFetch, SystemParamState,
},
world::World,
};
use bevy_ecs_macros::all_tuples;
use std::{borrow::Cow, marker::PhantomData};
/// The state of a [`System`].
pub struct SystemState {
pub(crate) id: SystemId,
pub(crate) name: Cow<'static, str>,
pub(crate) component_access_set: FilteredAccessSet<ComponentId>,
pub(crate) archetype_component_access: Access<ArchetypeComponentId>,
// NOTE: this must be kept private. making a SystemState non-send is irreversible to prevent
// SystemParams from overriding each other
is_send: bool,
pub(crate) last_change_tick: u32,
}
impl SystemState {
fn new<T>() -> Self {
Self {
name: std::any::type_name::<T>().into(),
archetype_component_access: Access::default(),
component_access_set: FilteredAccessSet::default(),
is_send: true,
id: SystemId::new(),
last_change_tick: 0,
}
}
/// Returns true if the system is [`Send`].
#[inline]
pub fn is_send(&self) -> bool {
self.is_send
}
/// Sets the system to be not [`Send`].
///
/// This is irreversible.
#[inline]
pub fn set_non_send(&mut self) {
self.is_send = false;
}
}
/// Conversion trait to turn something into a [`System`].
///
/// Use this to get a system from a function. Also note that every system implements this trait as
/// well.
///
/// # Examples
///
/// ```
/// use bevy_ecs::system::IntoSystem;
/// use bevy_ecs::system::Res;
///
/// fn my_system_function(an_usize_resource: Res<usize>) {}
///
/// let system = my_system_function.system();
/// ```
pub trait IntoSystem<Params, SystemType: System> {
/// Turns this value into its corresponding [`System`].
fn system(self) -> SystemType;
}
// Systems implicitly implement IntoSystem
impl<Sys: System> IntoSystem<(), Sys> for Sys {
fn system(self) -> Sys {
self
}
}
/// Wrapper type to mark a [`SystemParam`] as an input.
///
/// [`System`]s may take an optional input which they require to be passed to them when they
/// are being [`run`](System::run). For [`FunctionSystems`](FunctionSystem) the input may be marked
/// with this `In` type, but only the first param of a function may be tagged as an input. This also
/// means a system can only have one or zero input paramaters.
///
/// # Examples
///
/// Here is a simple example of a system that takes a [`usize`] returning the square of it.
///
/// ```
/// use bevy_ecs::prelude::*;
///
/// fn main() {
/// let mut square_system = square.system();
///
/// let mut world = World::default();
/// square_system.initialize(&mut world);
/// assert_eq!(square_system.run(12, &mut world), 144);
/// }
///
/// fn square(In(input): In<usize>) -> usize {
/// input * input
/// }
/// ```
pub struct In<In>(pub In);
pub struct InputMarker;
/// The [`System`] counter part of an ordinary function.
///
/// You get this by calling [`IntoSystem::system`] on a function that only accepts
/// [`SystemParam`]s. The output of the system becomes the functions return type, while the input
/// becomes the functions [`In`] tagged parameter or `()` if no such paramater exists.
pub struct FunctionSystem<In, Out, Param, Marker, F>
where
Param: SystemParam,
{
func: F,
param_state: Option<Param::Fetch>,
system_state: SystemState,
config: Option<<Param::Fetch as SystemParamState>::Config>,
// NOTE: PhantomData<fn()-> T> gives this safe Send/Sync impls
marker: PhantomData<fn() -> (In, Out, Marker)>,
}
impl<In, Out, Param: SystemParam, Marker, F> FunctionSystem<In, Out, Param, Marker, F> {
/// Gives mutable access to the systems config via a callback. This is useful to set up system
/// [`Local`](crate::system::Local)s.
///
/// # Examples
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # let world = &mut World::default();
/// fn local_is_42(local: Local<usize>) {
/// assert_eq!(*local, 42);
/// }
/// let mut system = local_is_42.system().config(|config| config.0 = Some(42));
/// system.initialize(world);
/// system.run((), world);
/// ```
pub fn config(
mut self,
f: impl FnOnce(&mut <Param::Fetch as SystemParamState>::Config),
) -> Self {
f(self.config.as_mut().unwrap());
self
}
}
impl<In, Out, Param, Marker, F> IntoSystem<Param, FunctionSystem<In, Out, Param, Marker, F>> for F
where
In: 'static,
Out: 'static,
Param: SystemParam + 'static,
Marker: 'static,
F: SystemParamFunction<In, Out, Param, Marker> + Send + Sync + 'static,
{
fn system(self) -> FunctionSystem<In, Out, Param, Marker, F> {
FunctionSystem {
func: self,
param_state: None,
config: Some(<Param::Fetch as SystemParamState>::default_config()),
system_state: SystemState::new::<F>(),
marker: PhantomData,
}
}
}
impl<In, Out, Param, Marker, F> System for FunctionSystem<In, Out, Param, Marker, F>
where
In: 'static,
Out: 'static,
Param: SystemParam + 'static,
Marker: 'static,
F: SystemParamFunction<In, Out, Param, Marker> + Send + Sync + 'static,
{
type In = In;
type Out = Out;
#[inline]
fn name(&self) -> Cow<'static, str> {
self.system_state.name.clone()
}
#[inline]
fn id(&self) -> SystemId {
self.system_state.id
}
#[inline]
fn new_archetype(&mut self, archetype: &Archetype) {
let param_state = self.param_state.as_mut().unwrap();
param_state.new_archetype(archetype, &mut self.system_state);
}
#[inline]
fn component_access(&self) -> &Access<ComponentId> {
&self.system_state.component_access_set.combined_access()
}
#[inline]
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
&self.system_state.archetype_component_access
}
#[inline]
fn is_send(&self) -> bool {
self.system_state.is_send
}
#[inline]
unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out {
let change_tick = world.increment_change_tick();
let out = self.func.run(
input,
self.param_state.as_mut().unwrap(),
&self.system_state,
world,
change_tick,
);
self.system_state.last_change_tick = change_tick;
out
}
#[inline]
fn apply_buffers(&mut self, world: &mut World) {
let param_state = self.param_state.as_mut().unwrap();
param_state.apply(world);
}
#[inline]
fn initialize(&mut self, world: &mut World) {
self.param_state = Some(<Param::Fetch as SystemParamState>::init(
world,
&mut self.system_state,
self.config.take().unwrap(),
));
}
#[inline]
fn check_change_tick(&mut self, change_tick: u32) {
check_system_change_tick(
&mut self.system_state.last_change_tick,
change_tick,
self.system_state.name.as_ref(),
);
}
}
/// A trait implemented for all functions that can be used as [`System`]s.
pub trait SystemParamFunction<In, Out, Param: SystemParam, Marker>: Send + Sync + 'static {
fn run(
&mut self,
input: In,
state: &mut Param::Fetch,
system_state: &SystemState,
world: &World,
change_tick: u32,
) -> Out;
}
macro_rules! impl_system_function {
($($param: ident),*) => {
#[allow(non_snake_case)]
impl<Out, Func, $($param: SystemParam),*> SystemParamFunction<(), Out, ($($param,)*), ()> for Func
where
Func:
FnMut($($param),*) -> Out +
FnMut($(<<$param as SystemParam>::Fetch as SystemParamFetch>::Item),*) -> Out + Send + Sync + 'static, Out: 'static
{
#[inline]
fn run(&mut self, _input: (), state: &mut <($($param,)*) as SystemParam>::Fetch, system_state: &SystemState, world: &World, change_tick: u32) -> Out {
unsafe {
let ($($param,)*) = <<($($param,)*) as SystemParam>::Fetch as SystemParamFetch>::get_param(state, system_state, world, change_tick);
self($($param),*)
}
}
}
#[allow(non_snake_case)]
impl<Input, Out, Func, $($param: SystemParam),*> SystemParamFunction<Input, Out, ($($param,)*), InputMarker> for Func
where
Func:
FnMut(In<Input>, $($param),*) -> Out +
FnMut(In<Input>, $(<<$param as SystemParam>::Fetch as SystemParamFetch>::Item),*) -> Out + Send + Sync + 'static, Out: 'static
{
#[inline]
fn run(&mut self, input: Input, state: &mut <($($param,)*) as SystemParam>::Fetch, system_state: &SystemState, world: &World, change_tick: u32) -> Out {
unsafe {
let ($($param,)*) = <<($($param,)*) as SystemParam>::Fetch as SystemParamFetch>::get_param(state, system_state, world, change_tick);
self(In(input), $($param),*)
}
}
}
};
}
all_tuples!(impl_system_function, 0, 16, F);