use crate::{
change_detection::{CheckChangeTicks, Tick},
error::{BevyError, Result},
never::Never,
prelude::FromWorld,
query::FilteredAccessSet,
schedule::{InternedSystemSet, SystemSet},
system::{
check_system_change_tick, FromInput, ReadOnlySystemParam, System, SystemIn, SystemInput,
SystemParam, SystemParamItem,
},
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World, WorldId},
};
use alloc::{borrow::Cow, vec, vec::Vec};
use bevy_utils::prelude::DebugName;
use core::marker::PhantomData;
use variadics_please::all_tuples;
#[cfg(feature = "trace")]
use tracing::{info_span, Span};
#[cfg(feature = "trace")]
use alloc::string::ToString as _;
use super::{
IntoSystem, ReadOnlySystem, RunSystemError, SystemParamBuilder, SystemParamValidationError,
SystemStateFlags,
};
#[derive(Clone)]
pub struct SystemMeta {
pub(crate) name: DebugName,
flags: SystemStateFlags,
pub(crate) last_run: Tick,
#[cfg(feature = "trace")]
pub(crate) system_span: Span,
#[cfg(feature = "trace")]
pub(crate) commands_span: Span,
}
impl SystemMeta {
pub(crate) fn new<T>() -> Self {
let name = DebugName::type_name::<T>();
Self {
#[cfg(feature = "trace")]
system_span: info_span!(parent: None, "system", name = name.clone().to_string()),
#[cfg(feature = "trace")]
commands_span: info_span!(parent: None, "system_commands", name = name.clone().to_string()),
name,
flags: SystemStateFlags::empty(),
last_run: Tick::new(0),
}
}
#[inline]
pub fn name(&self) -> &DebugName {
&self.name
}
#[inline]
pub fn set_name(&mut self, new_name: impl Into<Cow<'static, str>>) {
let new_name: Cow<'static, str> = new_name.into();
#[cfg(feature = "trace")]
{
let name = new_name.as_ref();
self.system_span = info_span!(parent: None, "system", name = name);
self.commands_span = info_span!(parent: None, "system_commands", name = name);
}
self.name = new_name.into();
}
#[inline]
pub fn is_send(&self) -> bool {
!self.flags.intersects(SystemStateFlags::NON_SEND)
}
#[inline]
pub fn set_non_send(&mut self) {
self.flags |= SystemStateFlags::NON_SEND;
}
#[inline]
pub fn has_deferred(&self) -> bool {
self.flags.intersects(SystemStateFlags::DEFERRED)
}
#[inline]
pub fn set_has_deferred(&mut self) {
self.flags |= SystemStateFlags::DEFERRED;
}
pub fn set_exclusive(&mut self) {
self.flags |= SystemStateFlags::EXCLUSIVE;
}
pub fn get_last_run(&self) -> Tick {
self.last_run
}
}
pub struct SystemState<Param: SystemParam + 'static> {
meta: SystemMeta,
param_state: Param::State,
world_id: WorldId,
}
macro_rules! impl_build_system {
($(#[$meta:meta])* $($param: ident),*) => {
$(#[$meta])*
impl<$($param: SystemParam),*> SystemState<($($param,)*)> {
#[inline]
pub fn build_system<
InnerOut: IntoResult<Out>,
Out,
Marker,
F: FnMut($(SystemParamItem<$param>),*) -> InnerOut
+ SystemParamFunction<Marker, In = (), Out = InnerOut, Param = ($($param,)*)>
>
(
self,
func: F,
) -> FunctionSystem<Marker, (), Out, F>
{
self.build_any_system(func)
}
#[inline]
pub fn build_system_with_input<
InnerIn: SystemInput + FromInput<In>,
In: SystemInput,
InnerOut: IntoResult<Out>,
Out,
Marker,
F: FnMut(InnerIn, $(SystemParamItem<$param>),*) -> InnerOut
+ SystemParamFunction<Marker, In = InnerIn, Out = InnerOut, Param = ($($param,)*)>
>
(
self,
func: F,
) -> FunctionSystem<Marker, In, Out, F> {
self.build_any_system(func)
}
}
}
}
all_tuples!(
#[doc(fake_variadic)]
impl_build_system,
0,
16,
P
);
impl<Param: SystemParam> SystemState<Param> {
#[track_caller]
pub fn new(world: &mut World) -> Self {
let mut meta = SystemMeta::new::<Param>();
meta.last_run = world.change_tick().relative_to(Tick::MAX);
let param_state = Param::init_state(world);
let mut component_access_set = FilteredAccessSet::new();
Param::init_access(¶m_state, &mut meta, &mut component_access_set, world);
Self {
meta,
param_state,
world_id: world.id(),
}
}
pub(crate) fn from_builder(world: &mut World, builder: impl SystemParamBuilder<Param>) -> Self {
let mut meta = SystemMeta::new::<Param>();
meta.last_run = world.change_tick().relative_to(Tick::MAX);
let param_state = builder.build(world);
let mut component_access_set = FilteredAccessSet::new();
Param::init_access(¶m_state, &mut meta, &mut component_access_set, world);
Self {
meta,
param_state,
world_id: world.id(),
}
}
#[inline]
pub fn build_any_system<Marker, In, Out, F>(self, func: F) -> FunctionSystem<Marker, In, Out, F>
where
In: SystemInput,
F: SystemParamFunction<Marker, In: FromInput<In>, Out: IntoResult<Out>, Param = Param>,
{
FunctionSystem::new(
func,
self.meta,
Some(FunctionSystemState {
param: self.param_state,
world_id: self.world_id,
}),
)
}
#[inline]
pub fn meta(&self) -> &SystemMeta {
&self.meta
}
#[inline]
pub fn meta_mut(&mut self) -> &mut SystemMeta {
&mut self.meta
}
#[inline]
pub fn get<'w, 's>(&'s mut self, world: &'w World) -> SystemParamItem<'w, 's, Param>
where
Param: ReadOnlySystemParam,
{
self.validate_world(world.id());
unsafe { self.get_unchecked(world.as_unsafe_world_cell_readonly()) }
}
#[inline]
#[track_caller]
pub fn get_mut<'w, 's>(&'s mut self, world: &'w mut World) -> SystemParamItem<'w, 's, Param> {
self.validate_world(world.id());
unsafe { self.get_unchecked(world.as_unsafe_world_cell()) }
}
pub fn apply(&mut self, world: &mut World) {
Param::apply(&mut self.param_state, &self.meta, world);
}
pub unsafe fn validate_param(
state: &mut Self,
world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError> {
unsafe { Param::validate_param(&mut state.param_state, &state.meta, world) }
}
#[inline]
pub fn matches_world(&self, world_id: WorldId) -> bool {
self.world_id == world_id
}
#[inline]
#[track_caller]
fn validate_world(&self, world_id: WorldId) {
#[inline(never)]
#[track_caller]
#[cold]
fn panic_mismatched(this: WorldId, other: WorldId) -> ! {
panic!("Encountered a mismatched World. This SystemState was created from {this:?}, but a method was called using {other:?}.");
}
if !self.matches_world(world_id) {
panic_mismatched(self.world_id, world_id);
}
}
#[inline]
#[track_caller]
pub unsafe fn get_unchecked<'w, 's>(
&'s mut self,
world: UnsafeWorldCell<'w>,
) -> SystemParamItem<'w, 's, Param> {
let change_tick = world.increment_change_tick();
unsafe { self.fetch(world, change_tick) }
}
#[inline]
#[track_caller]
unsafe fn fetch<'w, 's>(
&'s mut self,
world: UnsafeWorldCell<'w>,
change_tick: Tick,
) -> SystemParamItem<'w, 's, Param> {
let param =
unsafe { Param::get_param(&mut self.param_state, &self.meta, world, change_tick) };
self.meta.last_run = change_tick;
param
}
pub fn param_state(&self) -> &Param::State {
&self.param_state
}
pub unsafe fn param_state_mut(&mut self) -> &mut Param::State {
&mut self.param_state
}
}
impl<Param: SystemParam> FromWorld for SystemState<Param> {
fn from_world(world: &mut World) -> Self {
Self::new(world)
}
}
pub struct FunctionSystem<Marker, In, Out, F>
where
F: SystemParamFunction<Marker>,
{
func: F,
#[cfg(feature = "hotpatching")]
current_ptr: subsecond::HotFnPtr,
state: Option<FunctionSystemState<F::Param>>,
system_meta: SystemMeta,
marker: PhantomData<fn(In) -> (Marker, Out)>,
}
struct FunctionSystemState<P: SystemParam> {
param: P::State,
world_id: WorldId,
}
impl<Marker, In, Out, F> FunctionSystem<Marker, In, Out, F>
where
F: SystemParamFunction<Marker>,
{
#[inline]
fn new(func: F, system_meta: SystemMeta, state: Option<FunctionSystemState<F::Param>>) -> Self {
Self {
func,
#[cfg(feature = "hotpatching")]
current_ptr: subsecond::HotFn::current(<F as SystemParamFunction<Marker>>::run)
.ptr_address(),
state,
system_meta,
marker: PhantomData,
}
}
pub fn with_name(mut self, new_name: impl Into<Cow<'static, str>>) -> Self {
self.system_meta.set_name(new_name.into());
self
}
}
impl<Marker, In, Out, F> Clone for FunctionSystem<Marker, In, Out, F>
where
F: SystemParamFunction<Marker> + Clone,
{
fn clone(&self) -> Self {
Self {
func: self.func.clone(),
#[cfg(feature = "hotpatching")]
current_ptr: subsecond::HotFn::current(<F as SystemParamFunction<Marker>>::run)
.ptr_address(),
state: None,
system_meta: SystemMeta::new::<F>(),
marker: PhantomData,
}
}
}
#[doc(hidden)]
pub struct IsFunctionSystem;
impl<Marker, In, Out, F> IntoSystem<In, Out, (IsFunctionSystem, Marker)> for F
where
Marker: 'static,
In: SystemInput + 'static,
Out: 'static,
F: SystemParamFunction<Marker, In: FromInput<In>, Out: IntoResult<Out>>,
{
type System = FunctionSystem<Marker, In, Out, F>;
fn into_system(func: Self) -> Self::System {
FunctionSystem::new(func, SystemMeta::new::<F>(), None)
}
}
pub trait IntoResult<Out>: Sized {
fn into_result(self) -> Result<Out, RunSystemError>;
}
impl<T> IntoResult<T> for T {
fn into_result(self) -> Result<T, RunSystemError> {
Ok(self)
}
}
impl<T> IntoResult<T> for Result<T, RunSystemError> {
fn into_result(self) -> Result<T, RunSystemError> {
self
}
}
impl<T> IntoResult<T> for Result<T, BevyError> {
fn into_result(self) -> Result<T, RunSystemError> {
Ok(self?)
}
}
impl IntoResult<()> for Never {
fn into_result(self) -> Result<(), RunSystemError> {
self
}
}
impl IntoResult<bool> for Never {
fn into_result(self) -> Result<bool, RunSystemError> {
self
}
}
impl<Marker, In, Out, F> FunctionSystem<Marker, In, Out, F>
where
F: SystemParamFunction<Marker>,
{
const ERROR_UNINITIALIZED: &'static str =
"System's state was not found. Did you forget to initialize this system before running it?";
}
impl<Marker, In, Out, F> System for FunctionSystem<Marker, In, Out, F>
where
Marker: 'static,
In: SystemInput + 'static,
Out: 'static,
F: SystemParamFunction<Marker, In: FromInput<In>, Out: IntoResult<Out>>,
{
type In = In;
type Out = Out;
#[inline]
fn name(&self) -> DebugName {
self.system_meta.name.clone()
}
#[inline]
fn flags(&self) -> SystemStateFlags {
self.system_meta.flags
}
#[inline]
unsafe fn run_unsafe(
&mut self,
input: SystemIn<'_, Self>,
world: UnsafeWorldCell,
) -> Result<Self::Out, RunSystemError> {
#[cfg(feature = "trace")]
let _span_guard = self.system_meta.system_span.enter();
let change_tick = world.increment_change_tick();
let input = F::In::from_inner(input);
let state = self.state.as_mut().expect(Self::ERROR_UNINITIALIZED);
assert_eq!(state.world_id, world.id(), "Encountered a mismatched World. A System cannot be used with Worlds other than the one it was initialized with.");
let params =
unsafe { F::Param::get_param(&mut state.param, &self.system_meta, world, change_tick) };
#[cfg(feature = "hotpatching")]
let out = {
let mut hot_fn = subsecond::HotFn::current(<F as SystemParamFunction<Marker>>::run);
unsafe {
hot_fn
.try_call_with_ptr(self.current_ptr, (&mut self.func, input, params))
.expect("Error calling hotpatched system. Run a full rebuild")
}
};
#[cfg(not(feature = "hotpatching"))]
let out = self.func.run(input, params);
self.system_meta.last_run = change_tick;
IntoResult::into_result(out)
}
#[cfg(feature = "hotpatching")]
#[inline]
fn refresh_hotpatch(&mut self) {
let new = subsecond::HotFn::current(<F as SystemParamFunction<Marker>>::run).ptr_address();
if new != self.current_ptr {
log::debug!("system {} hotpatched", self.name());
}
self.current_ptr = new;
}
#[inline]
fn apply_deferred(&mut self, world: &mut World) {
let param_state = &mut self.state.as_mut().expect(Self::ERROR_UNINITIALIZED).param;
F::Param::apply(param_state, &self.system_meta, world);
}
#[inline]
fn queue_deferred(&mut self, world: DeferredWorld) {
let param_state = &mut self.state.as_mut().expect(Self::ERROR_UNINITIALIZED).param;
F::Param::queue(param_state, &self.system_meta, world);
}
#[inline]
unsafe fn validate_param_unsafe(
&mut self,
world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError> {
let state = self.state.as_mut().expect(Self::ERROR_UNINITIALIZED);
assert_eq!(state.world_id, world.id(), "Encountered a mismatched World. A System cannot be used with Worlds other than the one it was initialized with.");
unsafe { F::Param::validate_param(&mut state.param, &self.system_meta, world) }
}
#[inline]
fn initialize(&mut self, world: &mut World) -> FilteredAccessSet {
if let Some(state) = &self.state {
assert_eq!(
state.world_id,
world.id(),
"System built with a different world than the one it was added to.",
);
}
let state = self.state.get_or_insert_with(|| FunctionSystemState {
param: F::Param::init_state(world),
world_id: world.id(),
});
self.system_meta.last_run = world.change_tick().relative_to(Tick::MAX);
let mut component_access_set = FilteredAccessSet::new();
F::Param::init_access(
&state.param,
&mut self.system_meta,
&mut component_access_set,
world,
);
component_access_set
}
#[inline]
fn check_change_tick(&mut self, check: CheckChangeTicks) {
check_system_change_tick(
&mut self.system_meta.last_run,
check,
self.system_meta.name.clone(),
);
}
fn default_system_sets(&self) -> Vec<InternedSystemSet> {
let set = crate::schedule::SystemTypeSet::<Self>::new();
vec![set.intern()]
}
fn get_last_run(&self) -> Tick {
self.system_meta.last_run
}
fn set_last_run(&mut self, last_run: Tick) {
self.system_meta.last_run = last_run;
}
}
unsafe impl<Marker, In, Out, F> ReadOnlySystem for FunctionSystem<Marker, In, Out, F>
where
Marker: 'static,
In: SystemInput + 'static,
Out: 'static,
F: SystemParamFunction<
Marker,
In: FromInput<In>,
Out: IntoResult<Out>,
Param: ReadOnlySystemParam,
>,
{
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a valid system",
label = "invalid system"
)]
pub trait SystemParamFunction<Marker>: Send + Sync + 'static {
type In: SystemInput;
type Out;
type Param: SystemParam;
fn run(
&mut self,
input: <Self::In as SystemInput>::Inner<'_>,
param_value: SystemParamItem<Self::Param>,
) -> Self::Out;
}
#[doc(hidden)]
pub struct HasSystemInput;
macro_rules! impl_system_function {
($($param: ident),*) => {
#[expect(
clippy::allow_attributes,
reason = "This is within a macro, and as such, the below lints may not always apply."
)]
#[allow(
non_snake_case,
reason = "Certain variable names are provided by the caller, not by us."
)]
impl<Out, Func, $($param: SystemParam),*> SystemParamFunction<fn($($param,)*) -> Out> for Func
where
Func: Send + Sync + 'static,
for <'a> &'a mut Func:
FnMut($($param),*) -> Out +
FnMut($(SystemParamItem<$param>),*) -> Out,
Out: 'static
{
type In = ();
type Out = Out;
type Param = ($($param,)*);
#[inline]
fn run(&mut self, _input: (), param_value: SystemParamItem< ($($param,)*)>) -> Out {
fn call_inner<Out, $($param,)*>(
mut f: impl FnMut($($param,)*)->Out,
$($param: $param,)*
)->Out{
f($($param,)*)
}
let ($($param,)*) = param_value;
call_inner(self, $($param),*)
}
}
#[expect(
clippy::allow_attributes,
reason = "This is within a macro, and as such, the below lints may not always apply."
)]
#[allow(
non_snake_case,
reason = "Certain variable names are provided by the caller, not by us."
)]
impl<In, Out, Func, $($param: SystemParam),*> SystemParamFunction<(HasSystemInput, fn(In, $($param,)*) -> Out)> for Func
where
Func: Send + Sync + 'static,
for <'a> &'a mut Func:
FnMut(In, $($param),*) -> Out +
FnMut(In::Param<'_>, $(SystemParamItem<$param>),*) -> Out,
In: SystemInput + 'static,
Out: 'static
{
type In = In;
type Out = Out;
type Param = ($($param,)*);
#[inline]
fn run(&mut self, input: In::Inner<'_>, param_value: SystemParamItem< ($($param,)*)>) -> Out {
fn call_inner<In: SystemInput, Out, $($param,)*>(
_: PhantomData<In>,
mut f: impl FnMut(In::Param<'_>, $($param,)*)->Out,
input: In::Inner<'_>,
$($param: $param,)*
)->Out{
f(In::wrap(input), $($param,)*)
}
let ($($param,)*) = param_value;
call_inner(PhantomData::<In>, self, input, $($param),*)
}
}
};
}
all_tuples!(impl_system_function, 0, 16, F);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn into_system_type_id_consistency() {
fn test<T, In: SystemInput, Out, Marker>(function: T)
where
T: IntoSystem<In, Out, Marker> + Copy,
{
fn reference_system() {}
use core::any::TypeId;
let system = IntoSystem::into_system(function);
assert_eq!(
system.type_id(),
function.system_type_id(),
"System::type_id should be consistent with IntoSystem::system_type_id"
);
assert_eq!(
system.type_id(),
TypeId::of::<T::System>(),
"System::type_id should be consistent with TypeId::of::<T::System>()"
);
assert_ne!(
system.type_id(),
IntoSystem::into_system(reference_system).type_id(),
"Different systems should have different TypeIds"
);
}
fn function_system() {}
test(function_system);
}
}