use crate::{
archetype::{ArchetypeComponentId, ArchetypeGeneration, ArchetypeId},
change_detection::MAX_CHANGE_AGE,
component::ComponentId,
prelude::FromWorld,
query::{Access, FilteredAccessSet},
schedule::{SystemLabel, SystemLabelId},
system::{
check_system_change_tick, ReadOnlySystemParamFetch, System, SystemParam, SystemParamFetch,
SystemParamItem, SystemParamState,
},
world::{World, WorldId},
};
use bevy_ecs_macros::all_tuples;
use std::{borrow::Cow, fmt::Debug, marker::PhantomData};
#[derive(Clone)]
pub struct SystemMeta {
pub(crate) name: Cow<'static, str>,
pub(crate) component_access_set: FilteredAccessSet<ComponentId>,
pub(crate) archetype_component_access: Access<ArchetypeComponentId>,
is_send: bool,
pub(crate) last_change_tick: u32,
}
impl SystemMeta {
pub(crate) 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,
last_change_tick: 0,
}
}
#[inline]
pub fn is_send(&self) -> bool {
self.is_send
}
#[inline]
pub fn set_non_send(&mut self) {
self.is_send = false;
}
}
pub struct SystemState<Param: SystemParam + 'static> {
meta: SystemMeta,
param_state: <Param as SystemParam>::Fetch,
world_id: WorldId,
archetype_generation: ArchetypeGeneration,
}
impl<Param: SystemParam> SystemState<Param> {
pub fn new(world: &mut World) -> Self {
let mut meta = SystemMeta::new::<Param>();
meta.last_change_tick = world.change_tick().wrapping_sub(MAX_CHANGE_AGE);
let param_state = <Param::Fetch as SystemParamState>::init(world, &mut meta);
Self {
meta,
param_state,
world_id: world.id(),
archetype_generation: ArchetypeGeneration::initial(),
}
}
#[inline]
pub fn meta(&self) -> &SystemMeta {
&self.meta
}
#[inline]
pub fn get<'w, 's>(
&'s mut self,
world: &'w World,
) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item
where
Param::Fetch: ReadOnlySystemParamFetch,
{
self.validate_world_and_update_archetypes(world);
unsafe { self.get_unchecked_manual(world) }
}
#[inline]
pub fn get_mut<'w, 's>(
&'s mut self,
world: &'w mut World,
) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item {
self.validate_world_and_update_archetypes(world);
unsafe { self.get_unchecked_manual(world) }
}
pub fn apply(&mut self, world: &mut World) {
self.param_state.apply(world);
}
#[inline]
pub fn matches_world(&self, world: &World) -> bool {
self.world_id == world.id()
}
fn validate_world_and_update_archetypes(&mut self, world: &World) {
assert!(self.matches_world(world), "Encountered a mismatched World. A SystemState cannot be used with Worlds other than the one it was created with.");
let archetypes = world.archetypes();
let new_generation = archetypes.generation();
let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation);
let archetype_index_range = old_generation.value()..new_generation.value();
for archetype_index in archetype_index_range {
self.param_state.new_archetype(
&archetypes[ArchetypeId::new(archetype_index)],
&mut self.meta,
);
}
}
#[inline]
pub unsafe fn get_unchecked_manual<'w, 's>(
&'s mut self,
world: &'w World,
) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item {
let change_tick = world.increment_change_tick();
let param = <Param::Fetch as SystemParamFetch>::get_param(
&mut self.param_state,
&self.meta,
world,
change_tick,
);
self.meta.last_change_tick = change_tick;
param
}
}
impl<Param: SystemParam> FromWorld for SystemState<Param> {
fn from_world(world: &mut World) -> Self {
Self::new(world)
}
}
pub trait IntoSystem<In, Out, Params>: Sized {
type System: System<In = In, Out = Out>;
fn into_system(this: Self) -> Self::System;
}
pub struct AlreadyWasSystem;
impl<In, Out, Sys: System<In = In, Out = Out>> IntoSystem<In, Out, AlreadyWasSystem> for Sys {
type System = Sys;
fn into_system(this: Self) -> Sys {
this
}
}
pub struct In<In>(pub In);
#[doc(hidden)]
pub struct InputMarker;
pub struct FunctionSystem<In, Out, Param, Marker, F>
where
Param: SystemParam,
{
func: F,
param_state: Option<Param::Fetch>,
system_meta: SystemMeta,
world_id: Option<WorldId>,
archetype_generation: ArchetypeGeneration,
marker: PhantomData<fn() -> (In, Out, Marker)>,
}
pub struct IsFunctionSystem;
impl<In, Out, Param, Marker, F> IntoSystem<In, Out, (IsFunctionSystem, Param, Marker)> for F
where
In: 'static,
Out: 'static,
Param: SystemParam + 'static,
Marker: 'static,
F: SystemParamFunction<In, Out, Param, Marker> + Send + Sync + 'static,
{
type System = FunctionSystem<In, Out, Param, Marker, F>;
fn into_system(func: Self) -> Self::System {
FunctionSystem {
func,
param_state: None,
system_meta: SystemMeta::new::<F>(),
world_id: None,
archetype_generation: ArchetypeGeneration::initial(),
marker: PhantomData,
}
}
}
impl<In, Out, Param, Marker, F> FunctionSystem<In, Out, Param, Marker, F>
where
Param: SystemParam,
{
const PARAM_MESSAGE: &'static str = "System's param_state was not found. Did you forget to initialize this system before running it?";
}
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_meta.name.clone()
}
#[inline]
fn component_access(&self) -> &Access<ComponentId> {
self.system_meta.component_access_set.combined_access()
}
#[inline]
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
&self.system_meta.archetype_component_access
}
#[inline]
fn is_send(&self) -> bool {
self.system_meta.is_send
}
#[inline]
fn is_exclusive(&self) -> bool {
false
}
#[inline]
unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out {
let change_tick = world.increment_change_tick();
let params = <Param as SystemParam>::Fetch::get_param(
self.param_state.as_mut().expect(Self::PARAM_MESSAGE),
&self.system_meta,
world,
change_tick,
);
let out = self.func.run(input, params);
self.system_meta.last_change_tick = change_tick;
out
}
fn get_last_change_tick(&self) -> u32 {
self.system_meta.last_change_tick
}
fn set_last_change_tick(&mut self, last_change_tick: u32) {
self.system_meta.last_change_tick = last_change_tick;
}
#[inline]
fn apply_buffers(&mut self, world: &mut World) {
let param_state = self.param_state.as_mut().expect(Self::PARAM_MESSAGE);
param_state.apply(world);
}
#[inline]
fn initialize(&mut self, world: &mut World) {
self.world_id = Some(world.id());
self.system_meta.last_change_tick = world.change_tick().wrapping_sub(MAX_CHANGE_AGE);
self.param_state = Some(<Param::Fetch as SystemParamState>::init(
world,
&mut self.system_meta,
));
}
fn update_archetype_component_access(&mut self, world: &World) {
assert!(self.world_id == Some(world.id()), "Encountered a mismatched World. A System cannot be used with Worlds other than the one it was initialized with.");
let archetypes = world.archetypes();
let new_generation = archetypes.generation();
let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation);
let archetype_index_range = old_generation.value()..new_generation.value();
for archetype_index in archetype_index_range {
self.param_state.as_mut().unwrap().new_archetype(
&archetypes[ArchetypeId::new(archetype_index)],
&mut self.system_meta,
);
}
}
#[inline]
fn check_change_tick(&mut self, change_tick: u32) {
check_system_change_tick(
&mut self.system_meta.last_change_tick,
change_tick,
self.system_meta.name.as_ref(),
);
}
fn default_labels(&self) -> Vec<SystemLabelId> {
vec![self.func.as_system_label().as_label()]
}
}
pub struct SystemTypeIdLabel<T: 'static>(pub(crate) PhantomData<fn() -> T>);
impl<T: 'static> SystemLabel for SystemTypeIdLabel<T> {
#[inline]
fn as_str(&self) -> &'static str {
std::any::type_name::<T>()
}
}
impl<T> Debug for SystemTypeIdLabel<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("SystemTypeIdLabel")
.field(&std::any::type_name::<T>())
.finish()
}
}
impl<T> Clone for SystemTypeIdLabel<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for SystemTypeIdLabel<T> {}
pub trait SystemParamFunction<In, Out, Param: SystemParam, Marker>: Send + Sync + 'static {
fn run(&mut self, input: In, param_value: SystemParamItem<Param>) -> Out;
}
macro_rules! impl_system_function {
($($param: ident),*) => {
#[allow(non_snake_case)]
impl<Out, Func: Send + Sync + 'static, $($param: SystemParam),*> SystemParamFunction<(), Out, ($($param,)*), ()> for Func
where
for <'a> &'a mut Func:
FnMut($($param),*) -> Out +
FnMut($(SystemParamItem<$param>),*) -> Out, Out: 'static
{
#[inline]
fn run(&mut self, _input: (), param_value: SystemParamItem< ($($param,)*)>) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<Out, $($param,)*>(
mut f: impl FnMut($($param,)*)->Out,
$($param: $param,)*
)->Out{
f($($param,)*)
}
let ($($param,)*) = param_value;
call_inner(self, $($param),*)
}
}
#[allow(non_snake_case)]
impl<Input, Out, Func: Send + Sync + 'static, $($param: SystemParam),*> SystemParamFunction<Input, Out, ($($param,)*), InputMarker> for Func
where
for <'a> &'a mut Func:
FnMut(In<Input>, $($param),*) -> Out +
FnMut(In<Input>, $(<<$param as SystemParam>::Fetch as SystemParamFetch>::Item),*) -> Out, Out: 'static
{
#[inline]
fn run(&mut self, input: Input, param_value: SystemParamItem< ($($param,)*)>) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<Input, Out, $($param,)*>(
mut f: impl FnMut(In<Input>, $($param,)*)->Out,
input: In<Input>,
$($param: $param,)*
)->Out{
f(input, $($param,)*)
}
let ($($param,)*) = param_value;
call_inner(self, In(input), $($param),*)
}
}
};
}
all_tuples!(impl_system_function, 0, 16, F);
pub trait AsSystemLabel<Marker> {
fn as_system_label(&self) -> SystemLabelId;
}
impl<In, Out, Param: SystemParam, Marker, T: SystemParamFunction<In, Out, Param, Marker>>
AsSystemLabel<(In, Out, Param, Marker)> for T
{
#[inline]
fn as_system_label(&self) -> SystemLabelId {
SystemTypeIdLabel::<T>(PhantomData).as_label()
}
}
impl<T: SystemLabel> AsSystemLabel<()> for T {
#[inline]
fn as_system_label(&self) -> SystemLabelId {
self.as_label()
}
}