Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/system/function_system.rs
9328 views
1
use crate::{
2
change_detection::{CheckChangeTicks, Tick},
3
error::{BevyError, Result},
4
never::Never,
5
prelude::FromWorld,
6
query::FilteredAccessSet,
7
schedule::{InternedSystemSet, SystemSet},
8
system::{
9
check_system_change_tick, FromInput, ReadOnlySystemParam, System, SystemIn, SystemInput,
10
SystemParam, SystemParamItem,
11
},
12
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World, WorldId},
13
};
14
15
use alloc::{borrow::Cow, vec, vec::Vec};
16
use bevy_utils::prelude::DebugName;
17
use core::marker::PhantomData;
18
use variadics_please::all_tuples;
19
20
#[cfg(feature = "trace")]
21
use tracing::{info_span, Span};
22
23
#[cfg(feature = "trace")]
24
use alloc::string::ToString as _;
25
26
use super::{
27
IntoSystem, ReadOnlySystem, RunSystemError, SystemParamBuilder, SystemParamValidationError,
28
SystemStateFlags,
29
};
30
31
/// The metadata of a [`System`].
32
#[derive(Clone)]
33
pub struct SystemMeta {
34
pub(crate) name: DebugName,
35
// NOTE: this must be kept private. making a SystemMeta non-send is irreversible to prevent
36
// SystemParams from overriding each other
37
flags: SystemStateFlags,
38
pub(crate) last_run: Tick,
39
#[cfg(feature = "trace")]
40
pub(crate) system_span: Span,
41
#[cfg(feature = "trace")]
42
pub(crate) commands_span: Span,
43
}
44
45
impl SystemMeta {
46
pub(crate) fn new<T>() -> Self {
47
let name = DebugName::type_name::<T>();
48
Self {
49
// These spans are initialized during plugin build, so we set the parent to `None` to prevent
50
// them from being children of the span that is measuring the plugin build time.
51
#[cfg(feature = "trace")]
52
system_span: info_span!(parent: None, "system", name = name.clone().to_string()),
53
#[cfg(feature = "trace")]
54
commands_span: info_span!(parent: None, "system_commands", name = name.clone().to_string()),
55
name,
56
flags: SystemStateFlags::empty(),
57
last_run: Tick::new(0),
58
}
59
}
60
61
/// Returns the system's name
62
#[inline]
63
pub fn name(&self) -> &DebugName {
64
&self.name
65
}
66
67
/// Sets the name of this system.
68
///
69
/// Useful to give closure systems more readable and unique names for debugging and tracing.
70
#[inline]
71
pub fn set_name(&mut self, new_name: impl Into<Cow<'static, str>>) {
72
let new_name: Cow<'static, str> = new_name.into();
73
#[cfg(feature = "trace")]
74
{
75
let name = new_name.as_ref();
76
self.system_span = info_span!(parent: None, "system", name = name);
77
self.commands_span = info_span!(parent: None, "system_commands", name = name);
78
}
79
self.name = new_name.into();
80
}
81
82
/// Returns true if the system is [`Send`].
83
#[inline]
84
pub fn is_send(&self) -> bool {
85
!self.flags.intersects(SystemStateFlags::NON_SEND)
86
}
87
88
/// Sets the system to be not [`Send`].
89
///
90
/// This is irreversible.
91
#[inline]
92
pub fn set_non_send(&mut self) {
93
self.flags |= SystemStateFlags::NON_SEND;
94
}
95
96
/// Returns true if the system has deferred [`SystemParam`]'s
97
#[inline]
98
pub fn has_deferred(&self) -> bool {
99
self.flags.intersects(SystemStateFlags::DEFERRED)
100
}
101
102
/// Marks the system as having deferred buffers like [`Commands`](`super::Commands`)
103
/// This lets the scheduler insert [`ApplyDeferred`](`crate::prelude::ApplyDeferred`) systems automatically.
104
#[inline]
105
pub fn set_has_deferred(&mut self) {
106
self.flags |= SystemStateFlags::DEFERRED;
107
}
108
109
/// Mark the system to run exclusively. i.e. no other systems will run at the same time.
110
pub fn set_exclusive(&mut self) {
111
self.flags |= SystemStateFlags::EXCLUSIVE;
112
}
113
114
/// Expose a read only copy of `last_run`.
115
pub fn get_last_run(&self) -> Tick {
116
self.last_run
117
}
118
}
119
120
// TODO: Actually use this in FunctionSystem. We should probably only do this once Systems are constructed using a World reference
121
// (to avoid the need for unwrapping to retrieve SystemMeta)
122
/// Holds on to persistent state required to drive [`SystemParam`] for a [`System`].
123
///
124
/// This is a powerful and convenient tool for working with exclusive world access,
125
/// allowing you to fetch data from the [`World`] as if you were running a [`System`].
126
/// However, simply calling `world::run_system(my_system)` using a [`World::run_system`](World::run_system)
127
/// can be significantly simpler and ensures that change detection and command flushing work as expected.
128
///
129
/// Borrow-checking is handled for you, allowing you to mutably access multiple compatible system parameters at once,
130
/// and arbitrary system parameters (like [`MessageWriter`](crate::message::MessageWriter)) can be conveniently fetched.
131
///
132
/// For an alternative approach to split mutable access to the world, see [`World::resource_scope`].
133
///
134
/// # Warning
135
///
136
/// [`SystemState`] values created can be cached to improve performance,
137
/// and *must* be cached and reused in order for system parameters that rely on local state to work correctly.
138
/// These include:
139
/// - [`Added`](crate::query::Added), [`Changed`](crate::query::Changed) and [`Spawned`](crate::query::Spawned) query filters
140
/// - [`Local`](crate::system::Local) variables that hold state
141
/// - [`MessageReader`](crate::message::MessageReader) system parameters, which rely on a [`Local`](crate::system::Local) to track which messages have been seen
142
///
143
/// Note that this is automatically handled for you when using a [`World::run_system`](World::run_system).
144
///
145
/// # Example
146
///
147
/// Basic usage:
148
/// ```
149
/// # use bevy_ecs::prelude::*;
150
/// # use bevy_ecs::system::SystemState;
151
/// #
152
/// # #[derive(Message)]
153
/// # struct MyMessage;
154
/// # #[derive(Resource)]
155
/// # struct MyResource(u32);
156
/// #
157
/// # #[derive(Component)]
158
/// # struct MyComponent;
159
/// #
160
/// // Work directly on the `World`
161
/// let mut world = World::new();
162
/// world.init_resource::<Messages<MyMessage>>();
163
///
164
/// // Construct a `SystemState` struct, passing in a tuple of `SystemParam`
165
/// // as if you were writing an ordinary system.
166
/// let mut system_state: SystemState<(
167
/// MessageWriter<MyMessage>,
168
/// Option<ResMut<MyResource>>,
169
/// Query<&MyComponent>,
170
/// )> = SystemState::new(&mut world);
171
///
172
/// // Use system_state.get_mut(&mut world) and unpack your system parameters into variables!
173
/// // system_state.get(&world) provides read-only versions of your system parameters instead.
174
/// let (message_writer, maybe_resource, query) = system_state.get_mut(&mut world);
175
///
176
/// // If you are using `Commands`, you can choose when you want to apply them to the world.
177
/// // You need to manually call `.apply(world)` on the `SystemState` to apply them.
178
/// ```
179
/// Caching:
180
/// ```
181
/// # use bevy_ecs::prelude::*;
182
/// # use bevy_ecs::system::SystemState;
183
/// # use bevy_ecs::message::Messages;
184
/// #
185
/// # #[derive(Message)]
186
/// # struct MyMessage;
187
/// #[derive(Resource)]
188
/// struct CachedSystemState {
189
/// message_state: SystemState<MessageReader<'static, 'static, MyMessage>>,
190
/// }
191
///
192
/// // Create and store a system state once
193
/// let mut world = World::new();
194
/// world.init_resource::<Messages<MyMessage>>();
195
/// let initial_state: SystemState<MessageReader<MyMessage>> = SystemState::new(&mut world);
196
///
197
/// // The system state is cached in a resource
198
/// world.insert_resource(CachedSystemState {
199
/// message_state: initial_state,
200
/// });
201
///
202
/// // Later, fetch the cached system state, saving on overhead
203
/// world.resource_scope(|world, mut cached_state: Mut<CachedSystemState>| {
204
/// let mut message_reader = cached_state.message_state.get_mut(world);
205
///
206
/// for message in message_reader.read() {
207
/// println!("Hello World!");
208
/// }
209
/// });
210
/// ```
211
/// Exclusive System:
212
/// ```
213
/// # use bevy_ecs::prelude::*;
214
/// # use bevy_ecs::system::SystemState;
215
/// #
216
/// # #[derive(Message)]
217
/// # struct MyMessage;
218
/// #
219
/// fn exclusive_system(world: &mut World, system_state: &mut SystemState<MessageReader<MyMessage>>) {
220
/// let mut message_reader = system_state.get_mut(world);
221
///
222
/// for message in message_reader.read() {
223
/// println!("Hello World!");
224
/// }
225
/// }
226
/// ```
227
pub struct SystemState<Param: SystemParam + 'static> {
228
meta: SystemMeta,
229
param_state: Param::State,
230
world_id: WorldId,
231
}
232
233
// Allow closure arguments to be inferred.
234
// For a closure to be used as a `SystemParamFunction`, it needs to be generic in any `'w` or `'s` lifetimes.
235
// Rust will only infer a closure to be generic over lifetimes if it's passed to a function with a Fn constraint.
236
// So, generate a function for each arity with an explicit `FnMut` constraint to enable higher-order lifetimes,
237
// along with a regular `SystemParamFunction` constraint to allow the system to be built.
238
macro_rules! impl_build_system {
239
($(#[$meta:meta])* $($param: ident),*) => {
240
$(#[$meta])*
241
impl<$($param: SystemParam),*> SystemState<($($param,)*)> {
242
/// Create a [`FunctionSystem`] from a [`SystemState`].
243
/// This method signature allows type inference of closure parameters for a system with no input.
244
/// You can use [`SystemState::build_system_with_input()`] if you have input, or [`SystemState::build_any_system()`] if you don't need type inference.
245
#[inline]
246
pub fn build_system<
247
InnerOut: IntoResult<Out>,
248
Out,
249
Marker,
250
F: FnMut($(SystemParamItem<$param>),*) -> InnerOut
251
+ SystemParamFunction<Marker, In = (), Out = InnerOut, Param = ($($param,)*)>
252
>
253
(
254
self,
255
func: F,
256
) -> FunctionSystem<Marker, (), Out, F>
257
{
258
self.build_any_system(func)
259
}
260
261
/// Create a [`FunctionSystem`] from a [`SystemState`].
262
/// This method signature allows type inference of closure parameters for a system with input.
263
/// You can use [`SystemState::build_system()`] if you have no input, or [`SystemState::build_any_system()`] if you don't need type inference.
264
#[inline]
265
pub fn build_system_with_input<
266
InnerIn: SystemInput + FromInput<In>,
267
In: SystemInput,
268
InnerOut: IntoResult<Out>,
269
Out,
270
Marker,
271
F: FnMut(InnerIn, $(SystemParamItem<$param>),*) -> InnerOut
272
+ SystemParamFunction<Marker, In = InnerIn, Out = InnerOut, Param = ($($param,)*)>
273
>
274
(
275
self,
276
func: F,
277
) -> FunctionSystem<Marker, In, Out, F> {
278
self.build_any_system(func)
279
}
280
}
281
}
282
}
283
284
all_tuples!(
285
#[doc(fake_variadic)]
286
impl_build_system,
287
0,
288
16,
289
P
290
);
291
292
impl<Param: SystemParam> SystemState<Param> {
293
/// Creates a new [`SystemState`] with default state.
294
#[track_caller]
295
pub fn new(world: &mut World) -> Self {
296
let mut meta = SystemMeta::new::<Param>();
297
meta.last_run = world.change_tick().relative_to(Tick::MAX);
298
let param_state = Param::init_state(world);
299
let mut component_access_set = FilteredAccessSet::new();
300
// We need to call `init_access` to ensure there are no panics from conflicts within `Param`,
301
// even though we don't use the calculated access.
302
Param::init_access(&param_state, &mut meta, &mut component_access_set, world);
303
Self {
304
meta,
305
param_state,
306
world_id: world.id(),
307
}
308
}
309
310
/// Create a [`SystemState`] from a [`SystemParamBuilder`]
311
pub(crate) fn from_builder(world: &mut World, builder: impl SystemParamBuilder<Param>) -> Self {
312
let mut meta = SystemMeta::new::<Param>();
313
meta.last_run = world.change_tick().relative_to(Tick::MAX);
314
let param_state = builder.build(world);
315
let mut component_access_set = FilteredAccessSet::new();
316
// We need to call `init_access` to ensure there are no panics from conflicts within `Param`,
317
// even though we don't use the calculated access.
318
Param::init_access(&param_state, &mut meta, &mut component_access_set, world);
319
Self {
320
meta,
321
param_state,
322
world_id: world.id(),
323
}
324
}
325
326
/// Create a [`FunctionSystem`] from a [`SystemState`].
327
/// This method signature allows any system function, but the compiler will not perform type inference on closure parameters.
328
/// You can use [`SystemState::build_system()`] or [`SystemState::build_system_with_input()`] to get type inference on parameters.
329
#[inline]
330
pub fn build_any_system<Marker, In, Out, F>(self, func: F) -> FunctionSystem<Marker, In, Out, F>
331
where
332
In: SystemInput,
333
F: SystemParamFunction<Marker, In: FromInput<In>, Out: IntoResult<Out>, Param = Param>,
334
{
335
FunctionSystem::new(
336
func,
337
self.meta,
338
Some(FunctionSystemState {
339
param: self.param_state,
340
world_id: self.world_id,
341
}),
342
)
343
}
344
345
/// Gets the metadata for this instance.
346
#[inline]
347
pub fn meta(&self) -> &SystemMeta {
348
&self.meta
349
}
350
351
/// Gets the metadata for this instance.
352
#[inline]
353
pub fn meta_mut(&mut self) -> &mut SystemMeta {
354
&mut self.meta
355
}
356
357
/// Retrieve the [`SystemParam`] values. This can only be called when all parameters are read-only.
358
#[inline]
359
pub fn get<'w, 's>(&'s mut self, world: &'w World) -> SystemParamItem<'w, 's, Param>
360
where
361
Param: ReadOnlySystemParam,
362
{
363
self.validate_world(world.id());
364
// SAFETY: Param is read-only and doesn't allow mutable access to World.
365
// It also matches the World this SystemState was created with.
366
unsafe { self.get_unchecked(world.as_unsafe_world_cell_readonly()) }
367
}
368
369
/// Retrieve the mutable [`SystemParam`] values.
370
#[inline]
371
#[track_caller]
372
pub fn get_mut<'w, 's>(&'s mut self, world: &'w mut World) -> SystemParamItem<'w, 's, Param> {
373
self.validate_world(world.id());
374
// SAFETY: World is uniquely borrowed and matches the World this SystemState was created with.
375
unsafe { self.get_unchecked(world.as_unsafe_world_cell()) }
376
}
377
378
/// Applies all state queued up for [`SystemParam`] values. For example, this will apply commands queued up
379
/// by a [`Commands`](`super::Commands`) parameter to the given [`World`].
380
/// This function should be called manually after the values returned by [`SystemState::get`] and [`SystemState::get_mut`]
381
/// are finished being used.
382
pub fn apply(&mut self, world: &mut World) {
383
Param::apply(&mut self.param_state, &self.meta, world);
384
}
385
386
/// Wrapper over [`SystemParam::validate_param`].
387
///
388
/// # Safety
389
///
390
/// - The passed [`UnsafeWorldCell`] must have read-only access to
391
/// world data in `component_access_set`.
392
/// - `world` must be the same [`World`] that was used to initialize [`state`](SystemParam::init_state).
393
pub unsafe fn validate_param(
394
state: &mut Self,
395
world: UnsafeWorldCell,
396
) -> Result<(), SystemParamValidationError> {
397
// SAFETY: Delegated to existing `SystemParam` implementations.
398
unsafe { Param::validate_param(&mut state.param_state, &state.meta, world) }
399
}
400
401
/// Returns `true` if `world_id` matches the [`World`] that was used to call [`SystemState::new`].
402
/// Otherwise, this returns false.
403
#[inline]
404
pub fn matches_world(&self, world_id: WorldId) -> bool {
405
self.world_id == world_id
406
}
407
408
/// Asserts that the [`SystemState`] matches the provided world.
409
#[inline]
410
#[track_caller]
411
fn validate_world(&self, world_id: WorldId) {
412
#[inline(never)]
413
#[track_caller]
414
#[cold]
415
fn panic_mismatched(this: WorldId, other: WorldId) -> ! {
416
panic!("Encountered a mismatched World. This SystemState was created from {this:?}, but a method was called using {other:?}.");
417
}
418
419
if !self.matches_world(world_id) {
420
panic_mismatched(self.world_id, world_id);
421
}
422
}
423
424
/// Retrieve the [`SystemParam`] values.
425
///
426
/// # Safety
427
/// This call might access any of the input parameters in a way that violates Rust's mutability rules. Make sure the data
428
/// access is safe in the context of global [`World`] access. The passed-in [`World`] _must_ be the [`World`] the [`SystemState`] was
429
/// created with.
430
#[inline]
431
#[track_caller]
432
pub unsafe fn get_unchecked<'w, 's>(
433
&'s mut self,
434
world: UnsafeWorldCell<'w>,
435
) -> SystemParamItem<'w, 's, Param> {
436
let change_tick = world.increment_change_tick();
437
// SAFETY: The invariants are upheld by the caller.
438
unsafe { self.fetch(world, change_tick) }
439
}
440
441
/// # Safety
442
/// This call might access any of the input parameters in a way that violates Rust's mutability rules. Make sure the data
443
/// access is safe in the context of global [`World`] access. The passed-in [`World`] _must_ be the [`World`] the [`SystemState`] was
444
/// created with.
445
#[inline]
446
#[track_caller]
447
unsafe fn fetch<'w, 's>(
448
&'s mut self,
449
world: UnsafeWorldCell<'w>,
450
change_tick: Tick,
451
) -> SystemParamItem<'w, 's, Param> {
452
// SAFETY: The invariants are upheld by the caller.
453
let param =
454
unsafe { Param::get_param(&mut self.param_state, &self.meta, world, change_tick) };
455
self.meta.last_run = change_tick;
456
param
457
}
458
459
/// Returns a reference to the current system param states.
460
pub fn param_state(&self) -> &Param::State {
461
&self.param_state
462
}
463
464
/// Returns a mutable reference to the current system param states.
465
/// Marked as unsafe because modifying the system states may result in violation to certain
466
/// assumptions made by the [`SystemParam`]. Use with care.
467
///
468
/// # Safety
469
/// Modifying the system param states may have unintended consequences.
470
/// The param state is generally considered to be owned by the [`SystemParam`]. Modifications
471
/// should respect any invariants as required by the [`SystemParam`].
472
/// For example, modifying the system state of [`ResMut`](crate::system::ResMut) will obviously create issues.
473
pub unsafe fn param_state_mut(&mut self) -> &mut Param::State {
474
&mut self.param_state
475
}
476
}
477
478
impl<Param: SystemParam> FromWorld for SystemState<Param> {
479
fn from_world(world: &mut World) -> Self {
480
Self::new(world)
481
}
482
}
483
484
/// The [`System`] counter part of an ordinary function.
485
///
486
/// You get this by calling [`IntoSystem::into_system`] on a function that only accepts
487
/// [`SystemParam`]s. The output of the system becomes the functions return type, while the input
488
/// becomes the functions first parameter or `()` if no such parameter exists.
489
///
490
/// [`FunctionSystem`] must be `.initialized` before they can be run.
491
///
492
/// The [`Clone`] implementation for [`FunctionSystem`] returns a new instance which
493
/// is NOT initialized. The cloned system must also be `.initialized` before it can be run.
494
pub struct FunctionSystem<Marker, In, Out, F>
495
where
496
F: SystemParamFunction<Marker>,
497
{
498
func: F,
499
#[cfg(feature = "hotpatching")]
500
current_ptr: subsecond::HotFnPtr,
501
state: Option<FunctionSystemState<F::Param>>,
502
system_meta: SystemMeta,
503
// NOTE: PhantomData<fn()-> T> gives this safe Send/Sync impls
504
marker: PhantomData<fn(In) -> (Marker, Out)>,
505
}
506
507
/// The state of a [`FunctionSystem`], which must be initialized with
508
/// [`System::initialize`] before the system can be run. A panic will occur if
509
/// the system is run without being initialized.
510
struct FunctionSystemState<P: SystemParam> {
511
/// The cached state of the system's [`SystemParam`]s.
512
param: P::State,
513
/// The id of the [`World`] this system was initialized with. If the world
514
/// passed to [`System::run_unsafe`] or [`System::validate_param_unsafe`] does not match
515
/// this id, a panic will occur.
516
world_id: WorldId,
517
}
518
519
impl<Marker, In, Out, F> FunctionSystem<Marker, In, Out, F>
520
where
521
F: SystemParamFunction<Marker>,
522
{
523
#[inline]
524
fn new(func: F, system_meta: SystemMeta, state: Option<FunctionSystemState<F::Param>>) -> Self {
525
Self {
526
func,
527
#[cfg(feature = "hotpatching")]
528
current_ptr: subsecond::HotFn::current(<F as SystemParamFunction<Marker>>::run)
529
.ptr_address(),
530
state,
531
system_meta,
532
marker: PhantomData,
533
}
534
}
535
536
/// Return this system with a new name.
537
///
538
/// Useful to give closure systems more readable and unique names for debugging and tracing.
539
pub fn with_name(mut self, new_name: impl Into<Cow<'static, str>>) -> Self {
540
self.system_meta.set_name(new_name.into());
541
self
542
}
543
}
544
545
// De-initializes the cloned system.
546
impl<Marker, In, Out, F> Clone for FunctionSystem<Marker, In, Out, F>
547
where
548
F: SystemParamFunction<Marker> + Clone,
549
{
550
fn clone(&self) -> Self {
551
Self {
552
func: self.func.clone(),
553
#[cfg(feature = "hotpatching")]
554
current_ptr: subsecond::HotFn::current(<F as SystemParamFunction<Marker>>::run)
555
.ptr_address(),
556
state: None,
557
system_meta: SystemMeta::new::<F>(),
558
marker: PhantomData,
559
}
560
}
561
}
562
563
/// A marker type used to distinguish regular function systems from exclusive function systems.
564
#[doc(hidden)]
565
pub struct IsFunctionSystem;
566
567
impl<Marker, In, Out, F> IntoSystem<In, Out, (IsFunctionSystem, Marker)> for F
568
where
569
Marker: 'static,
570
In: SystemInput + 'static,
571
Out: 'static,
572
F: SystemParamFunction<Marker, In: FromInput<In>, Out: IntoResult<Out>>,
573
{
574
type System = FunctionSystem<Marker, In, Out, F>;
575
fn into_system(func: Self) -> Self::System {
576
FunctionSystem::new(func, SystemMeta::new::<F>(), None)
577
}
578
}
579
580
/// A type that may be converted to the output of a [`System`].
581
/// This is used to allow systems to return either a plain value or a [`Result`].
582
pub trait IntoResult<Out>: Sized {
583
/// Converts this type into the system output type.
584
fn into_result(self) -> Result<Out, RunSystemError>;
585
}
586
587
impl<T> IntoResult<T> for T {
588
fn into_result(self) -> Result<T, RunSystemError> {
589
Ok(self)
590
}
591
}
592
593
impl<T> IntoResult<T> for Result<T, RunSystemError> {
594
fn into_result(self) -> Result<T, RunSystemError> {
595
self
596
}
597
}
598
599
impl<T> IntoResult<T> for Result<T, BevyError> {
600
fn into_result(self) -> Result<T, RunSystemError> {
601
Ok(self?)
602
}
603
}
604
605
// The `!` impl can't be generic in `Out`, since that would overlap with
606
// `impl<T> IntoResult<T> for T` when `T` = `!`.
607
// Use explicit impls for `()` and `bool` so diverging functions
608
// can be used for systems and conditions.
609
impl IntoResult<()> for Never {
610
fn into_result(self) -> Result<(), RunSystemError> {
611
self
612
}
613
}
614
615
impl IntoResult<bool> for Never {
616
fn into_result(self) -> Result<bool, RunSystemError> {
617
self
618
}
619
}
620
621
impl<Marker, In, Out, F> FunctionSystem<Marker, In, Out, F>
622
where
623
F: SystemParamFunction<Marker>,
624
{
625
/// Message shown when a system isn't initialized
626
// When lines get too long, rustfmt can sometimes refuse to format them.
627
// Work around this by storing the message separately.
628
const ERROR_UNINITIALIZED: &'static str =
629
"System's state was not found. Did you forget to initialize this system before running it?";
630
}
631
632
impl<Marker, In, Out, F> System for FunctionSystem<Marker, In, Out, F>
633
where
634
Marker: 'static,
635
In: SystemInput + 'static,
636
Out: 'static,
637
F: SystemParamFunction<Marker, In: FromInput<In>, Out: IntoResult<Out>>,
638
{
639
type In = In;
640
type Out = Out;
641
642
#[inline]
643
fn name(&self) -> DebugName {
644
self.system_meta.name.clone()
645
}
646
647
#[inline]
648
fn flags(&self) -> SystemStateFlags {
649
self.system_meta.flags
650
}
651
652
#[inline]
653
unsafe fn run_unsafe(
654
&mut self,
655
input: SystemIn<'_, Self>,
656
world: UnsafeWorldCell,
657
) -> Result<Self::Out, RunSystemError> {
658
#[cfg(feature = "trace")]
659
let _span_guard = self.system_meta.system_span.enter();
660
661
let change_tick = world.increment_change_tick();
662
663
let input = F::In::from_inner(input);
664
665
let state = self.state.as_mut().expect(Self::ERROR_UNINITIALIZED);
666
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.");
667
// SAFETY:
668
// - The above assert ensures the world matches.
669
// - All world accesses used by `F::Param` have been registered, so the caller
670
// will ensure that there are no data access conflicts.
671
let params =
672
unsafe { F::Param::get_param(&mut state.param, &self.system_meta, world, change_tick) };
673
674
#[cfg(feature = "hotpatching")]
675
let out = {
676
let mut hot_fn = subsecond::HotFn::current(<F as SystemParamFunction<Marker>>::run);
677
// SAFETY:
678
// - pointer used to call is from the current jump table
679
unsafe {
680
hot_fn
681
.try_call_with_ptr(self.current_ptr, (&mut self.func, input, params))
682
.expect("Error calling hotpatched system. Run a full rebuild")
683
}
684
};
685
#[cfg(not(feature = "hotpatching"))]
686
let out = self.func.run(input, params);
687
688
self.system_meta.last_run = change_tick;
689
IntoResult::into_result(out)
690
}
691
692
#[cfg(feature = "hotpatching")]
693
#[inline]
694
fn refresh_hotpatch(&mut self) {
695
let new = subsecond::HotFn::current(<F as SystemParamFunction<Marker>>::run).ptr_address();
696
if new != self.current_ptr {
697
log::debug!("system {} hotpatched", self.name());
698
}
699
self.current_ptr = new;
700
}
701
702
#[inline]
703
fn apply_deferred(&mut self, world: &mut World) {
704
let param_state = &mut self.state.as_mut().expect(Self::ERROR_UNINITIALIZED).param;
705
F::Param::apply(param_state, &self.system_meta, world);
706
}
707
708
#[inline]
709
fn queue_deferred(&mut self, world: DeferredWorld) {
710
let param_state = &mut self.state.as_mut().expect(Self::ERROR_UNINITIALIZED).param;
711
F::Param::queue(param_state, &self.system_meta, world);
712
}
713
714
#[inline]
715
unsafe fn validate_param_unsafe(
716
&mut self,
717
world: UnsafeWorldCell,
718
) -> Result<(), SystemParamValidationError> {
719
let state = self.state.as_mut().expect(Self::ERROR_UNINITIALIZED);
720
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.");
721
// SAFETY:
722
// - The above assert ensures the world matches.
723
// - All world accesses used by `F::Param` have been registered, so the caller
724
// will ensure that there are no data access conflicts.
725
unsafe { F::Param::validate_param(&mut state.param, &self.system_meta, world) }
726
}
727
728
#[inline]
729
fn initialize(&mut self, world: &mut World) -> FilteredAccessSet {
730
if let Some(state) = &self.state {
731
assert_eq!(
732
state.world_id,
733
world.id(),
734
"System built with a different world than the one it was added to.",
735
);
736
}
737
let state = self.state.get_or_insert_with(|| FunctionSystemState {
738
param: F::Param::init_state(world),
739
world_id: world.id(),
740
});
741
self.system_meta.last_run = world.change_tick().relative_to(Tick::MAX);
742
let mut component_access_set = FilteredAccessSet::new();
743
F::Param::init_access(
744
&state.param,
745
&mut self.system_meta,
746
&mut component_access_set,
747
world,
748
);
749
component_access_set
750
}
751
752
#[inline]
753
fn check_change_tick(&mut self, check: CheckChangeTicks) {
754
check_system_change_tick(
755
&mut self.system_meta.last_run,
756
check,
757
self.system_meta.name.clone(),
758
);
759
}
760
761
fn default_system_sets(&self) -> Vec<InternedSystemSet> {
762
let set = crate::schedule::SystemTypeSet::<F>::new();
763
vec![set.intern()]
764
}
765
766
fn get_last_run(&self) -> Tick {
767
self.system_meta.last_run
768
}
769
770
fn set_last_run(&mut self, last_run: Tick) {
771
self.system_meta.last_run = last_run;
772
}
773
}
774
775
// SAFETY: `F`'s param is [`ReadOnlySystemParam`], so this system will only read from the world.
776
unsafe impl<Marker, In, Out, F> ReadOnlySystem for FunctionSystem<Marker, In, Out, F>
777
where
778
Marker: 'static,
779
In: SystemInput + 'static,
780
Out: 'static,
781
F: SystemParamFunction<
782
Marker,
783
In: FromInput<In>,
784
Out: IntoResult<Out>,
785
Param: ReadOnlySystemParam,
786
>,
787
{
788
}
789
790
/// A trait implemented for all functions that can be used as [`System`]s.
791
///
792
/// This trait can be useful for making your own systems which accept other systems,
793
/// sometimes called higher order systems.
794
///
795
/// This should be used in combination with [`ParamSet`] when calling other systems
796
/// within your system.
797
/// Using [`ParamSet`] in this case avoids [`SystemParam`] collisions.
798
///
799
/// # Example
800
///
801
/// To create something like [`PipeSystem`], but in entirely safe code.
802
///
803
/// ```
804
/// use std::num::ParseIntError;
805
///
806
/// use bevy_ecs::prelude::*;
807
/// use bevy_ecs::system::StaticSystemInput;
808
///
809
/// /// Pipe creates a new system which calls `a`, then calls `b` with the output of `a`
810
/// pub fn pipe<A, B, AMarker, BMarker>(
811
/// mut a: A,
812
/// mut b: B,
813
/// ) -> impl FnMut(StaticSystemInput<A::In>, ParamSet<(A::Param, B::Param)>) -> B::Out
814
/// where
815
/// // We need A and B to be systems, add those bounds
816
/// A: SystemParamFunction<AMarker>,
817
/// B: SystemParamFunction<BMarker>,
818
/// for<'a> B::In: SystemInput<Inner<'a> = A::Out>,
819
/// {
820
/// // The type of `params` is inferred based on the return of this function above
821
/// move |StaticSystemInput(a_in), mut params| {
822
/// let shared = a.run(a_in, params.p0());
823
/// b.run(shared, params.p1())
824
/// }
825
/// }
826
///
827
/// // Usage example for `pipe`:
828
/// fn main() {
829
/// let mut world = World::default();
830
/// world.insert_resource(Message("42".to_string()));
831
///
832
/// // pipe the `parse_message_system`'s output into the `filter_system`s input.
833
/// // Type annotations should only needed when using `StaticSystemInput` as input
834
/// // AND the input type isn't constrained by nearby code.
835
/// let mut piped_system = IntoSystem::<(), Option<usize>, _>::into_system(pipe(parse_message, filter));
836
/// piped_system.initialize(&mut world);
837
/// assert_eq!(piped_system.run((), &mut world).unwrap(), Some(42));
838
/// }
839
///
840
/// #[derive(Resource)]
841
/// struct Message(String);
842
///
843
/// fn parse_message(message: Res<Message>) -> Result<usize, ParseIntError> {
844
/// message.0.parse::<usize>()
845
/// }
846
///
847
/// fn filter(In(result): In<Result<usize, ParseIntError>>) -> Option<usize> {
848
/// result.ok().filter(|&n| n < 100)
849
/// }
850
/// ```
851
/// [`PipeSystem`]: crate::system::PipeSystem
852
/// [`ParamSet`]: crate::system::ParamSet
853
#[diagnostic::on_unimplemented(
854
message = "`{Self}` is not a valid system",
855
label = "invalid system"
856
)]
857
pub trait SystemParamFunction<Marker>: Send + Sync + 'static {
858
/// The input type of this system. See [`System::In`].
859
type In: SystemInput;
860
/// The return type of this system. See [`System::Out`].
861
type Out;
862
863
/// The [`SystemParam`]/s used by this system to access the [`World`].
864
type Param: SystemParam;
865
866
/// Executes this system once. See [`System::run`] or [`System::run_unsafe`].
867
fn run(
868
&mut self,
869
input: <Self::In as SystemInput>::Inner<'_>,
870
param_value: SystemParamItem<Self::Param>,
871
) -> Self::Out;
872
}
873
874
/// A marker type used to distinguish function systems with and without input.
875
#[doc(hidden)]
876
pub struct HasSystemInput;
877
878
macro_rules! impl_system_function {
879
($($param: ident),*) => {
880
#[expect(
881
clippy::allow_attributes,
882
reason = "This is within a macro, and as such, the below lints may not always apply."
883
)]
884
#[allow(
885
non_snake_case,
886
reason = "Certain variable names are provided by the caller, not by us."
887
)]
888
impl<Out, Func, $($param: SystemParam),*> SystemParamFunction<fn($($param,)*) -> Out> for Func
889
where
890
Func: Send + Sync + 'static,
891
for <'a> &'a mut Func:
892
FnMut($($param),*) -> Out +
893
FnMut($(SystemParamItem<$param>),*) -> Out,
894
Out: 'static
895
{
896
type In = ();
897
type Out = Out;
898
type Param = ($($param,)*);
899
#[inline]
900
fn run(&mut self, _input: (), param_value: SystemParamItem< ($($param,)*)>) -> Out {
901
// Yes, this is strange, but `rustc` fails to compile this impl
902
// without using this function. It fails to recognize that `func`
903
// is a function, potentially because of the multiple impls of `FnMut`
904
fn call_inner<Out, $($param,)*>(
905
mut f: impl FnMut($($param,)*)->Out,
906
$($param: $param,)*
907
)->Out{
908
f($($param,)*)
909
}
910
let ($($param,)*) = param_value;
911
call_inner(self, $($param),*)
912
}
913
}
914
915
#[expect(
916
clippy::allow_attributes,
917
reason = "This is within a macro, and as such, the below lints may not always apply."
918
)]
919
#[allow(
920
non_snake_case,
921
reason = "Certain variable names are provided by the caller, not by us."
922
)]
923
impl<In, Out, Func, $($param: SystemParam),*> SystemParamFunction<(HasSystemInput, fn(In, $($param,)*) -> Out)> for Func
924
where
925
Func: Send + Sync + 'static,
926
for <'a> &'a mut Func:
927
FnMut(In, $($param),*) -> Out +
928
FnMut(In::Param<'_>, $(SystemParamItem<$param>),*) -> Out,
929
In: SystemInput + 'static,
930
Out: 'static
931
{
932
type In = In;
933
type Out = Out;
934
type Param = ($($param,)*);
935
#[inline]
936
fn run(&mut self, input: In::Inner<'_>, param_value: SystemParamItem< ($($param,)*)>) -> Out {
937
fn call_inner<In: SystemInput, Out, $($param,)*>(
938
_: PhantomData<In>,
939
mut f: impl FnMut(In::Param<'_>, $($param,)*)->Out,
940
input: In::Inner<'_>,
941
$($param: $param,)*
942
)->Out{
943
f(In::wrap(input), $($param,)*)
944
}
945
let ($($param,)*) = param_value;
946
call_inner(PhantomData::<In>, self, input, $($param),*)
947
}
948
}
949
};
950
}
951
952
// Note that we rely on the highest impl to be <= the highest order of the tuple impls
953
// of `SystemParam` created.
954
all_tuples!(impl_system_function, 0, 16, F);
955
956
#[cfg(test)]
957
mod tests {
958
use super::*;
959
960
#[test]
961
fn into_system_type_id_consistency() {
962
fn test<T, In: SystemInput, Out, Marker>(function: T)
963
where
964
T: IntoSystem<In, Out, Marker> + Copy,
965
{
966
fn reference_system() {}
967
968
use core::any::TypeId;
969
970
let system = IntoSystem::into_system(function);
971
972
assert_eq!(
973
system.type_id(),
974
function.system_type_id(),
975
"System::type_id should be consistent with IntoSystem::system_type_id"
976
);
977
978
assert_eq!(
979
system.type_id(),
980
TypeId::of::<T::System>(),
981
"System::type_id should be consistent with TypeId::of::<T::System>()"
982
);
983
984
assert_ne!(
985
system.type_id(),
986
IntoSystem::into_system(reference_system).type_id(),
987
"Different systems should have different TypeIds"
988
);
989
}
990
991
fn function_system() {}
992
993
test(function_system);
994
}
995
}
996
997