Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/schedule/set.rs
6849 views
1
use alloc::boxed::Box;
2
use bevy_utils::prelude::DebugName;
3
use core::{
4
any::TypeId,
5
fmt::Debug,
6
hash::{Hash, Hasher},
7
marker::PhantomData,
8
};
9
10
pub use crate::label::DynEq;
11
pub use bevy_ecs_macros::{ScheduleLabel, SystemSet};
12
13
use crate::{
14
define_label,
15
intern::Interned,
16
system::{
17
ExclusiveFunctionSystem, ExclusiveSystemParamFunction, FunctionSystem, IntoResult,
18
IsExclusiveFunctionSystem, IsFunctionSystem, SystemParamFunction,
19
},
20
};
21
22
define_label!(
23
/// A strongly-typed class of labels used to identify a [`Schedule`].
24
///
25
/// Each schedule in a [`World`] has a unique schedule label value, and
26
/// schedules can be automatically created from labels via [`Schedules::add_systems()`].
27
///
28
/// # Defining new schedule labels
29
///
30
/// By default, you should use Bevy's premade schedule labels which implement this trait.
31
/// If you are using [`bevy_ecs`] directly or if you need to run a group of systems outside
32
/// the existing schedules, you may define your own schedule labels by using
33
/// `#[derive(ScheduleLabel)]`.
34
///
35
/// ```
36
/// use bevy_ecs::prelude::*;
37
/// use bevy_ecs::schedule::ScheduleLabel;
38
///
39
/// // Declare a new schedule label.
40
/// #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
41
/// struct Update;
42
///
43
/// let mut world = World::new();
44
///
45
/// // Add a system to the schedule with that label (creating it automatically).
46
/// fn a_system_function() {}
47
/// world.get_resource_or_init::<Schedules>().add_systems(Update, a_system_function);
48
///
49
/// // Run the schedule, and therefore run the system.
50
/// world.run_schedule(Update);
51
/// ```
52
///
53
/// [`Schedule`]: crate::schedule::Schedule
54
/// [`Schedules::add_systems()`]: crate::schedule::Schedules::add_systems
55
/// [`World`]: crate::world::World
56
#[diagnostic::on_unimplemented(
57
note = "consider annotating `{Self}` with `#[derive(ScheduleLabel)]`"
58
)]
59
ScheduleLabel,
60
SCHEDULE_LABEL_INTERNER
61
);
62
63
define_label!(
64
/// System sets are tag-like labels that can be used to group systems together.
65
///
66
/// This allows you to share configuration (like run conditions) across multiple systems,
67
/// and order systems or system sets relative to conceptual groups of systems.
68
/// To control the behavior of a system set as a whole, use [`Schedule::configure_sets`](crate::prelude::Schedule::configure_sets),
69
/// or the method of the same name on `App`.
70
///
71
/// Systems can belong to any number of system sets, reflecting multiple roles or facets that they might have.
72
/// For example, you may want to annotate a system as "consumes input" and "applies forces",
73
/// and ensure that your systems are ordered correctly for both of those sets.
74
///
75
/// System sets can belong to any number of other system sets,
76
/// allowing you to create nested hierarchies of system sets to group systems together.
77
/// Configuration applied to system sets will flow down to their members (including other system sets),
78
/// allowing you to set and modify the configuration in a single place.
79
///
80
/// Systems sets are also useful for exposing a consistent public API for dependencies
81
/// to hook into across versions of your crate,
82
/// allowing them to add systems to a specific set, or order relative to that set,
83
/// without leaking implementation details of the exact systems involved.
84
///
85
/// ## Defining new system sets
86
///
87
/// To create a new system set, use the `#[derive(SystemSet)]` macro.
88
/// Unit structs are a good choice for one-off sets.
89
///
90
/// ```rust
91
/// # use bevy_ecs::prelude::*;
92
///
93
/// #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
94
/// struct PhysicsSystems;
95
/// ```
96
///
97
/// When you want to define several related system sets,
98
/// consider creating an enum system set.
99
/// Each variant will be treated as a separate system set.
100
///
101
/// ```rust
102
/// # use bevy_ecs::prelude::*;
103
///
104
/// #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
105
/// enum CombatSystems {
106
/// TargetSelection,
107
/// DamageCalculation,
108
/// Cleanup,
109
/// }
110
/// ```
111
///
112
/// By convention, the listed order of the system set in the enum
113
/// corresponds to the order in which the systems are run.
114
/// Ordering must be explicitly added to ensure that this is the case,
115
/// but following this convention will help avoid confusion.
116
///
117
/// ### Adding systems to system sets
118
///
119
/// To add systems to a system set, call [`in_set`](crate::prelude::IntoScheduleConfigs::in_set) on the system function
120
/// while adding it to your app or schedule.
121
///
122
/// Like usual, these methods can be chained with other configuration methods like [`before`](crate::prelude::IntoScheduleConfigs::before),
123
/// or repeated to add systems to multiple sets.
124
///
125
/// ```rust
126
/// use bevy_ecs::prelude::*;
127
///
128
/// #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
129
/// enum CombatSystems {
130
/// TargetSelection,
131
/// DamageCalculation,
132
/// Cleanup,
133
/// }
134
///
135
/// fn target_selection() {}
136
///
137
/// fn enemy_damage_calculation() {}
138
///
139
/// fn player_damage_calculation() {}
140
///
141
/// let mut schedule = Schedule::default();
142
/// // Configuring the sets to run in order.
143
/// schedule.configure_sets((CombatSystems::TargetSelection, CombatSystems::DamageCalculation, CombatSystems::Cleanup).chain());
144
///
145
/// // Adding a single system to a set.
146
/// schedule.add_systems(target_selection.in_set(CombatSystems::TargetSelection));
147
///
148
/// // Adding multiple systems to a set.
149
/// schedule.add_systems((player_damage_calculation, enemy_damage_calculation).in_set(CombatSystems::DamageCalculation));
150
/// ```
151
#[diagnostic::on_unimplemented(
152
note = "consider annotating `{Self}` with `#[derive(SystemSet)]`"
153
)]
154
SystemSet,
155
SYSTEM_SET_INTERNER,
156
extra_methods: {
157
/// Returns `Some` if this system set is a [`SystemTypeSet`].
158
fn system_type(&self) -> Option<TypeId> {
159
None
160
}
161
162
/// Returns `true` if this system set is an [`AnonymousSet`].
163
fn is_anonymous(&self) -> bool {
164
false
165
}
166
},
167
extra_methods_impl: {
168
fn system_type(&self) -> Option<TypeId> {
169
(**self).system_type()
170
}
171
172
fn is_anonymous(&self) -> bool {
173
(**self).is_anonymous()
174
}
175
}
176
);
177
178
/// A shorthand for `Interned<dyn SystemSet>`.
179
pub type InternedSystemSet = Interned<dyn SystemSet>;
180
/// A shorthand for `Interned<dyn ScheduleLabel>`.
181
pub type InternedScheduleLabel = Interned<dyn ScheduleLabel>;
182
183
/// A [`SystemSet`] grouping instances of the same function.
184
///
185
/// This kind of set is automatically populated and thus has some special rules:
186
/// - You cannot manually add members.
187
/// - You cannot configure them.
188
/// - You cannot order something relative to one if it has more than one member.
189
pub struct SystemTypeSet<T: 'static>(PhantomData<fn() -> T>);
190
191
impl<T: 'static> SystemTypeSet<T> {
192
pub(crate) fn new() -> Self {
193
Self(PhantomData)
194
}
195
}
196
197
impl<T> Debug for SystemTypeSet<T> {
198
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
199
f.debug_tuple("SystemTypeSet")
200
.field(&format_args!("fn {}()", DebugName::type_name::<T>()))
201
.finish()
202
}
203
}
204
205
impl<T> Hash for SystemTypeSet<T> {
206
fn hash<H: Hasher>(&self, _state: &mut H) {
207
// all systems of a given type are the same
208
}
209
}
210
211
impl<T> Clone for SystemTypeSet<T> {
212
fn clone(&self) -> Self {
213
*self
214
}
215
}
216
217
impl<T> Copy for SystemTypeSet<T> {}
218
219
impl<T> PartialEq for SystemTypeSet<T> {
220
#[inline]
221
fn eq(&self, _other: &Self) -> bool {
222
// all systems of a given type are the same
223
true
224
}
225
}
226
227
impl<T> Eq for SystemTypeSet<T> {}
228
229
impl<T> SystemSet for SystemTypeSet<T> {
230
fn system_type(&self) -> Option<TypeId> {
231
Some(TypeId::of::<T>())
232
}
233
234
fn dyn_clone(&self) -> Box<dyn SystemSet> {
235
Box::new(*self)
236
}
237
}
238
239
/// A [`SystemSet`] implicitly created when using
240
/// [`Schedule::add_systems`](super::Schedule::add_systems) or
241
/// [`Schedule::configure_sets`](super::Schedule::configure_sets).
242
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
243
pub struct AnonymousSet(usize);
244
245
impl AnonymousSet {
246
pub(crate) fn new(id: usize) -> Self {
247
Self(id)
248
}
249
}
250
251
impl SystemSet for AnonymousSet {
252
fn is_anonymous(&self) -> bool {
253
true
254
}
255
256
fn dyn_clone(&self) -> Box<dyn SystemSet> {
257
Box::new(*self)
258
}
259
}
260
261
/// Types that can be converted into a [`SystemSet`].
262
///
263
/// # Usage notes
264
///
265
/// This trait should only be used as a bound for trait implementations or as an
266
/// argument to a function. If a system set needs to be returned from a function
267
/// or stored somewhere, use [`SystemSet`] instead of this trait.
268
#[diagnostic::on_unimplemented(
269
message = "`{Self}` is not a system set",
270
label = "invalid system set"
271
)]
272
pub trait IntoSystemSet<Marker>: Sized {
273
/// The type of [`SystemSet`] this instance converts into.
274
type Set: SystemSet;
275
276
/// Converts this instance to its associated [`SystemSet`] type.
277
fn into_system_set(self) -> Self::Set;
278
}
279
280
// systems sets
281
impl<S: SystemSet> IntoSystemSet<()> for S {
282
type Set = Self;
283
284
#[inline]
285
fn into_system_set(self) -> Self::Set {
286
self
287
}
288
}
289
290
// systems
291
impl<Marker, F> IntoSystemSet<(IsFunctionSystem, Marker)> for F
292
where
293
Marker: 'static,
294
F::Out: IntoResult<()>,
295
F: SystemParamFunction<Marker>,
296
{
297
type Set = SystemTypeSet<FunctionSystem<Marker, (), F>>;
298
299
#[inline]
300
fn into_system_set(self) -> Self::Set {
301
SystemTypeSet::<FunctionSystem<Marker, (), F>>::new()
302
}
303
}
304
305
// exclusive systems
306
impl<Marker, F> IntoSystemSet<(IsExclusiveFunctionSystem, Marker)> for F
307
where
308
Marker: 'static,
309
F::Out: IntoResult<()>,
310
F: ExclusiveSystemParamFunction<Marker>,
311
{
312
type Set = SystemTypeSet<ExclusiveFunctionSystem<Marker, (), F>>;
313
314
#[inline]
315
fn into_system_set(self) -> Self::Set {
316
SystemTypeSet::<ExclusiveFunctionSystem<Marker, (), F>>::new()
317
}
318
}
319
320
#[cfg(test)]
321
mod tests {
322
use crate::{
323
resource::Resource,
324
schedule::{tests::ResMut, Schedule},
325
system::{IntoSystem, System},
326
};
327
328
use super::*;
329
330
#[test]
331
fn test_schedule_label() {
332
use crate::world::World;
333
334
#[derive(Resource)]
335
struct Flag(bool);
336
337
#[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
338
struct A;
339
340
#[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
341
struct B;
342
343
let mut world = World::new();
344
345
let mut schedule = Schedule::new(A);
346
schedule.add_systems(|mut flag: ResMut<Flag>| flag.0 = true);
347
world.add_schedule(schedule);
348
349
let interned = A.intern();
350
351
world.insert_resource(Flag(false));
352
world.run_schedule(interned);
353
assert!(world.resource::<Flag>().0);
354
355
world.insert_resource(Flag(false));
356
world.run_schedule(interned);
357
assert!(world.resource::<Flag>().0);
358
359
assert_ne!(A.intern(), B.intern());
360
}
361
362
#[test]
363
fn test_derive_schedule_label() {
364
#[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
365
struct UnitLabel;
366
367
#[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
368
struct TupleLabel(u32, u32);
369
370
#[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
371
struct StructLabel {
372
a: u32,
373
b: u32,
374
}
375
376
#[expect(
377
dead_code,
378
reason = "This is a derive macro compilation test. It won't be constructed."
379
)]
380
#[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
381
struct EmptyTupleLabel();
382
383
#[expect(
384
dead_code,
385
reason = "This is a derive macro compilation test. It won't be constructed."
386
)]
387
#[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
388
struct EmptyStructLabel {}
389
390
#[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
391
enum EnumLabel {
392
#[default]
393
Unit,
394
Tuple(u32, u32),
395
Struct {
396
a: u32,
397
b: u32,
398
},
399
}
400
401
#[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
402
struct GenericLabel<T>(PhantomData<T>);
403
404
assert_eq!(UnitLabel.intern(), UnitLabel.intern());
405
assert_eq!(EnumLabel::Unit.intern(), EnumLabel::Unit.intern());
406
assert_ne!(UnitLabel.intern(), EnumLabel::Unit.intern());
407
assert_ne!(UnitLabel.intern(), TupleLabel(0, 0).intern());
408
assert_ne!(EnumLabel::Unit.intern(), EnumLabel::Tuple(0, 0).intern());
409
410
assert_eq!(TupleLabel(0, 0).intern(), TupleLabel(0, 0).intern());
411
assert_eq!(
412
EnumLabel::Tuple(0, 0).intern(),
413
EnumLabel::Tuple(0, 0).intern()
414
);
415
assert_ne!(TupleLabel(0, 0).intern(), TupleLabel(0, 1).intern());
416
assert_ne!(
417
EnumLabel::Tuple(0, 0).intern(),
418
EnumLabel::Tuple(0, 1).intern()
419
);
420
assert_ne!(TupleLabel(0, 0).intern(), EnumLabel::Tuple(0, 0).intern());
421
assert_ne!(
422
TupleLabel(0, 0).intern(),
423
StructLabel { a: 0, b: 0 }.intern()
424
);
425
assert_ne!(
426
EnumLabel::Tuple(0, 0).intern(),
427
EnumLabel::Struct { a: 0, b: 0 }.intern()
428
);
429
430
assert_eq!(
431
StructLabel { a: 0, b: 0 }.intern(),
432
StructLabel { a: 0, b: 0 }.intern()
433
);
434
assert_eq!(
435
EnumLabel::Struct { a: 0, b: 0 }.intern(),
436
EnumLabel::Struct { a: 0, b: 0 }.intern()
437
);
438
assert_ne!(
439
StructLabel { a: 0, b: 0 }.intern(),
440
StructLabel { a: 0, b: 1 }.intern()
441
);
442
assert_ne!(
443
EnumLabel::Struct { a: 0, b: 0 }.intern(),
444
EnumLabel::Struct { a: 0, b: 1 }.intern()
445
);
446
assert_ne!(
447
StructLabel { a: 0, b: 0 }.intern(),
448
EnumLabel::Struct { a: 0, b: 0 }.intern()
449
);
450
assert_ne!(
451
StructLabel { a: 0, b: 0 }.intern(),
452
EnumLabel::Struct { a: 0, b: 0 }.intern()
453
);
454
assert_ne!(StructLabel { a: 0, b: 0 }.intern(), UnitLabel.intern(),);
455
assert_ne!(
456
EnumLabel::Struct { a: 0, b: 0 }.intern(),
457
EnumLabel::Unit.intern()
458
);
459
460
assert_eq!(
461
GenericLabel::<u32>(PhantomData).intern(),
462
GenericLabel::<u32>(PhantomData).intern()
463
);
464
assert_ne!(
465
GenericLabel::<u32>(PhantomData).intern(),
466
GenericLabel::<u64>(PhantomData).intern()
467
);
468
}
469
470
#[test]
471
fn test_derive_system_set() {
472
#[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
473
struct UnitSet;
474
475
#[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
476
struct TupleSet(u32, u32);
477
478
#[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
479
struct StructSet {
480
a: u32,
481
b: u32,
482
}
483
484
#[expect(
485
dead_code,
486
reason = "This is a derive macro compilation test. It won't be constructed."
487
)]
488
#[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
489
struct EmptyTupleSet();
490
491
#[expect(
492
dead_code,
493
reason = "This is a derive macro compilation test. It won't be constructed."
494
)]
495
#[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
496
struct EmptyStructSet {}
497
498
#[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
499
enum EnumSet {
500
#[default]
501
Unit,
502
Tuple(u32, u32),
503
Struct {
504
a: u32,
505
b: u32,
506
},
507
}
508
509
#[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
510
struct GenericSet<T>(PhantomData<T>);
511
512
assert_eq!(UnitSet.intern(), UnitSet.intern());
513
assert_eq!(EnumSet::Unit.intern(), EnumSet::Unit.intern());
514
assert_ne!(UnitSet.intern(), EnumSet::Unit.intern());
515
assert_ne!(UnitSet.intern(), TupleSet(0, 0).intern());
516
assert_ne!(EnumSet::Unit.intern(), EnumSet::Tuple(0, 0).intern());
517
518
assert_eq!(TupleSet(0, 0).intern(), TupleSet(0, 0).intern());
519
assert_eq!(EnumSet::Tuple(0, 0).intern(), EnumSet::Tuple(0, 0).intern());
520
assert_ne!(TupleSet(0, 0).intern(), TupleSet(0, 1).intern());
521
assert_ne!(EnumSet::Tuple(0, 0).intern(), EnumSet::Tuple(0, 1).intern());
522
assert_ne!(TupleSet(0, 0).intern(), EnumSet::Tuple(0, 0).intern());
523
assert_ne!(TupleSet(0, 0).intern(), StructSet { a: 0, b: 0 }.intern());
524
assert_ne!(
525
EnumSet::Tuple(0, 0).intern(),
526
EnumSet::Struct { a: 0, b: 0 }.intern()
527
);
528
529
assert_eq!(
530
StructSet { a: 0, b: 0 }.intern(),
531
StructSet { a: 0, b: 0 }.intern()
532
);
533
assert_eq!(
534
EnumSet::Struct { a: 0, b: 0 }.intern(),
535
EnumSet::Struct { a: 0, b: 0 }.intern()
536
);
537
assert_ne!(
538
StructSet { a: 0, b: 0 }.intern(),
539
StructSet { a: 0, b: 1 }.intern()
540
);
541
assert_ne!(
542
EnumSet::Struct { a: 0, b: 0 }.intern(),
543
EnumSet::Struct { a: 0, b: 1 }.intern()
544
);
545
assert_ne!(
546
StructSet { a: 0, b: 0 }.intern(),
547
EnumSet::Struct { a: 0, b: 0 }.intern()
548
);
549
assert_ne!(
550
StructSet { a: 0, b: 0 }.intern(),
551
EnumSet::Struct { a: 0, b: 0 }.intern()
552
);
553
assert_ne!(StructSet { a: 0, b: 0 }.intern(), UnitSet.intern(),);
554
assert_ne!(
555
EnumSet::Struct { a: 0, b: 0 }.intern(),
556
EnumSet::Unit.intern()
557
);
558
559
assert_eq!(
560
GenericSet::<u32>(PhantomData).intern(),
561
GenericSet::<u32>(PhantomData).intern()
562
);
563
assert_ne!(
564
GenericSet::<u32>(PhantomData).intern(),
565
GenericSet::<u64>(PhantomData).intern()
566
);
567
}
568
569
#[test]
570
fn system_set_matches_default_system_set() {
571
fn system() {}
572
let set_from_into_system_set = IntoSystemSet::into_system_set(system).intern();
573
let system = IntoSystem::into_system(system);
574
let set_from_system = system.default_system_sets()[0];
575
assert_eq!(set_from_into_system_set, set_from_system);
576
}
577
578
#[test]
579
fn system_set_matches_default_system_set_exclusive() {
580
fn system(_: &mut crate::world::World) {}
581
let set_from_into_system_set = IntoSystemSet::into_system_set(system).intern();
582
let system = IntoSystem::into_system(system);
583
let set_from_system = system.default_system_sets()[0];
584
assert_eq!(set_from_into_system_set, set_from_system);
585
}
586
}
587
588