mirror of
https://github.com/bevyengine/bevy
synced 2024-12-23 03:23:20 +00:00
e4e32598a9
Fresh version of #1670 off the latest main. Mostly fixing documentation wrapping.
298 lines
9.3 KiB
Rust
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);
|