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