Skip to main content

pebble/ecs/
system.rs

1use std::cell::RefMut;
2use std::ops::{Deref, DerefMut};
3
4use crate::ecs::resources::Resources;
5
6/// A resource requirement declared by a [`SystemParam`]/[`System`], carrying
7/// a human-readable name, the resource's [`TypeId`](std::any::TypeId) (so
8/// [`App`](crate::app::App) can check it against
9/// [`RequiredResources`](crate::assets::required::RequiredResources) —
10/// resources some plugin has declared it eventually provides, e.g. an async
11/// GPU backend or a [`LazyResource`](crate::assets::singleton_asset::LazyResource) —
12/// and a way to check presence dynamically (needed because
13/// [`System::requires`] is type-erased — the concrete `T` is only known
14/// where the check is constructed, inside each `SystemParam` impl).
15#[derive(Clone, Copy)]
16pub struct RequiredResource {
17    pub name: &'static str,
18    pub type_id: std::any::TypeId,
19    pub present: fn(&hecs::World, &Resources) -> bool,
20}
21
22/// Immutable borrow of a singleton resource `T`.
23///
24/// Obtained as a system parameter; derefs to `T`.
25pub struct Res<'a, T: hecs::Component> {
26    pub(crate) data: hecs::Ref<'a, T>,
27}
28
29impl<'a, T: hecs::Component> Deref for Res<'a, T> {
30    type Target = T;
31    fn deref(&self) -> &Self::Target {
32        &self.data
33    }
34}
35
36/// Mutable borrow of a singleton resource `T`.
37///
38/// Obtained as a system parameter; derefs to `T`.
39pub struct ResMut<'a, T: hecs::Component> {
40    data: hecs::RefMut<'a, T>,
41}
42
43impl<'a, T: hecs::Component> Deref for ResMut<'a, T> {
44    type Target = T;
45    fn deref(&self) -> &Self::Target {
46        &self.data
47    }
48}
49
50impl<'a, T: hecs::Component> DerefMut for ResMut<'a, T> {
51    fn deref_mut(&mut self) -> &mut Self::Target {
52        &mut self.data
53    }
54}
55
56/// Borrow of an ECS query result.
57///
58/// Obtained as a system parameter; derefs to [`hecs::QueryBorrow`].
59///
60/// # Iterating
61///
62/// `Query` implements `IntoIterator` for `&mut Query`, so you can iterate it
63/// directly without going through `Deref`:
64///
65/// ```ignore
66/// fn move_system(mut q: Query<(&mut Position, &Velocity)>) {
67///     for (entity, (pos, vel)) in &mut q {
68///         pos.x += vel.x;
69///         pos.y += vel.y;
70///     }
71/// }
72/// ```
73///
74/// # Single-entity lookups
75///
76/// Use [`Query::get`] to fetch components for one known `Entity` without
77/// scanning the whole result set, and [`Query::single`] /
78/// [`Query::get_single`] when you expect exactly one match (e.g. "the
79/// player", "the active camera").
80pub struct Query<'a, Q: hecs::Query> {
81    world: &'a hecs::World,
82    borrow: hecs::QueryBorrow<'a, Q>,
83}
84
85impl<'a, Q: hecs::Query> Deref for Query<'a, Q> {
86    type Target = hecs::QueryBorrow<'a, Q>;
87    fn deref(&self) -> &Self::Target {
88        &self.borrow
89    }
90}
91
92impl<'a, Q: hecs::Query> DerefMut for Query<'a, Q> {
93    fn deref_mut(&mut self) -> &mut Self::Target {
94        &mut self.borrow
95    }
96}
97
98impl<'q, Q: hecs::Query> IntoIterator for &'q mut Query<'_, Q> {
99    type Item = Q::Item<'q>;
100    type IntoIter = hecs::QueryIter<'q, Q>;
101
102    fn into_iter(self) -> Self::IntoIter {
103        (&mut self.borrow).into_iter()
104    }
105}
106
107impl<'a, Q: hecs::Query> Query<'a, Q> {
108    /// Look up a single entity's components for this query. Returns
109    /// `None` if the entity doesn't exist or doesn't match `Q`.
110    pub fn get(&self, entity: hecs::Entity) -> hecs::QueryOne<'_, Q> {
111        self.world.query_one::<Q>(entity)
112    }
113
114    /// Filter this query to only entities that ALSO have component `R`,
115    /// without `R` itself being part of the yielded items. Consumes
116    /// `self` — matches hecs's own `QueryBorrow::with` signature.
117    pub fn with<R: hecs::Query>(self) -> hecs::QueryBorrow<'a, hecs::With<Q, R>> {
118        self.borrow.with::<R>()
119    }
120
121    /// Filter this query to only entities that do NOT have component `R`.
122    /// Consumes `self`, same reasoning as `with`.
123    pub fn without<R: hecs::Query>(self) -> hecs::QueryBorrow<'a, hecs::Without<Q, R>> {
124        self.borrow.without::<R>()
125    }
126
127    /// Return the single entity's components for this query.
128    ///
129    /// Panics if there isn't exactly one match. Intended for singleton-style
130    /// queries (the player, the active camera, ...) where zero or multiple
131    /// matches indicate a bug. See [`Query::get_single`] for a
132    /// non-panicking version. Include `hecs::Entity` in `Q` if you need the
133    /// id alongside the components.
134    pub fn single(&mut self) -> Q::Item<'_> {
135        self.get_single()
136            .expect("Query::single: expected exactly one matching entity")
137    }
138
139    /// Like [`Query::single`], but returns `None` instead of panicking when
140    /// there isn't exactly one match.
141    pub fn get_single(&mut self) -> Option<Q::Item<'_>> {
142        let mut iter = self.borrow.iter();
143        let first = iter.next()?;
144        if iter.next().is_some() {
145            return None;
146        }
147        Some(first)
148    }
149}
150
151/// Deferred world-mutation commands available as a system parameter.
152///
153/// Mutations are buffered and applied to the world after all systems in the
154/// current stage have finished running.
155///
156/// Resource insertions immediately bump the [`Resources`] generation counter so
157/// that the convergence loop in [`App`](crate::app::App) can detect them without
158/// needing to inspect the world after every flush.
159pub struct Commands<'a> {
160    buffer: RefMut<'a, hecs::CommandBuffer>,
161    resource_entity: hecs::Entity,
162    /// Held so `insert_resource` can bump the generation counter at queue time.
163    resources: &'a Resources,
164}
165
166impl<'a> Commands<'a> {
167    /// Queue a resource insertion. Applied after the current stage finishes.
168    ///
169    /// Bumps the [`Resources`] generation counter immediately so the
170    /// convergence loop knows another pass is needed even before the command
171    /// buffer is flushed.
172    pub fn insert_resource<T: hecs::Component>(&mut self, res: T) {
173        self.buffer.insert_one(self.resource_entity, res);
174        self.resources.bump_generation();
175    }
176
177    /// Queue a resource removal. Applied after the current stage finishes.
178    pub fn remove_resource<T: hecs::Component>(&mut self) {
179        self.buffer.remove_one::<T>(self.resource_entity);
180    }
181}
182
183impl<'a> Deref for Commands<'a> {
184    type Target = hecs::CommandBuffer;
185    fn deref(&self) -> &Self::Target {
186        &self.buffer
187    }
188}
189
190impl<'a> DerefMut for Commands<'a> {
191    fn deref_mut(&mut self) -> &mut Self::Target {
192        &mut self.buffer
193    }
194}
195
196/// Per-system persistent local state.
197///
198/// Unlike [`Res`]/[`ResMut`], a `Local<T>` is *not* shared through
199/// [`Resources`] — each system gets its own private `T`, initialized with
200/// [`Default::default`] the first time the system is registered, and
201/// preserved across every subsequent run of that system.
202///
203/// Useful for counters, caches, or any state a single system needs to
204/// remember without polluting the global resource set.
205pub struct Local<'a, T: Default + Send + Sync + 'static> {
206    data: &'a mut T,
207}
208
209impl<'a, T: Default + Send + Sync + 'static> Deref for Local<'a, T> {
210    type Target = T;
211    fn deref(&self) -> &Self::Target {
212        self.data
213    }
214}
215
216impl<'a, T: Default + Send + Sync + 'static> DerefMut for Local<'a, T> {
217    fn deref_mut(&mut self) -> &mut Self::Target {
218        self.data
219    }
220}
221
222/// Trait implemented for each valid system parameter type.
223///
224/// The macro-generated [`impl_system!`] blanket implementations use this to
225/// fetch each parameter from the world and resources before calling the system
226/// function. `State` is per-system storage owned by the [`FunctionSystem`]
227/// itself (as opposed to `Item`, which only lives for the duration of one
228/// call) — this is what lets [`Local`] persist between runs.
229pub trait SystemParam {
230    type Item<'a>;
231    type State: Default + 'static;
232    fn fetch<'a>(
233        state: &'a mut Self::State,
234        world: &'a hecs::World,
235        resources: &'a Resources,
236    ) -> Self::Item<'a>;
237
238    /// Resource types this parameter unconditionally needs present to avoid
239    /// panicking. Used by [`App`](crate::app::App) to validate — before
240    /// running a non-convergent stage's systems — that every hard
241    /// requirement is already satisfied, failing fast with a clear message
242    /// instead of panicking deep inside whichever system happens to run
243    /// first.
244    ///
245    /// Empty by default; only hard requirements (bare [`Res`]/[`ResMut`])
246    /// contribute an entry. `Option<Res<T>>`/`Option<ResMut<T>>` tolerate
247    /// absence and deliberately opt out of this check.
248    fn requires() -> Vec<RequiredResource> {
249        Vec::new()
250    }
251}
252
253impl<T> SystemParam for Res<'static, T>
254where
255    T: 'static + Sync + Send,
256{
257    type Item<'a> = Res<'a, T>;
258    type State = ();
259
260    fn fetch<'a>(
261        _state: &'a mut Self::State,
262        world: &'a hecs::World,
263        resource: &'a Resources,
264    ) -> Self::Item<'a> {
265        Res {
266            data: resource.get_resource(world),
267        }
268    }
269
270    fn requires() -> Vec<RequiredResource> {
271        vec![RequiredResource {
272            name: std::any::type_name::<T>(),
273            type_id: std::any::TypeId::of::<T>(),
274            present: |world, resources| resources.has_resource::<T>(world),
275        }]
276    }
277}
278
279impl<T> SystemParam for Option<Res<'static, T>>
280where
281    T: 'static + Sync + Send,
282{
283    type Item<'a> = Option<Res<'a, T>>;
284    type State = ();
285
286    fn fetch<'a>(
287        _state: &'a mut Self::State,
288        world: &'a hecs::World,
289        resource: &'a Resources,
290    ) -> Self::Item<'a> {
291        if resource.has_resource::<T>(world) {
292            return Some(Res {
293                data: resource.get_resource(world),
294            });
295        }
296
297        None
298    }
299}
300
301impl<T> SystemParam for ResMut<'static, T>
302where
303    T: 'static + Sync + Send,
304{
305    type Item<'a> = ResMut<'a, T>;
306    type State = ();
307
308    fn fetch<'a>(
309        _state: &'a mut Self::State,
310        world: &'a hecs::World,
311        resource: &'a Resources,
312    ) -> Self::Item<'a> {
313        ResMut {
314            data: resource.get_resource_mut(world),
315        }
316    }
317
318    fn requires() -> Vec<RequiredResource> {
319        vec![RequiredResource {
320            name: std::any::type_name::<T>(),
321            type_id: std::any::TypeId::of::<T>(),
322            present: |world, resources| resources.has_resource::<T>(world),
323        }]
324    }
325}
326
327impl<T> SystemParam for Option<ResMut<'static, T>>
328where
329    T: 'static + Sync + Send,
330{
331    type Item<'a> = Option<ResMut<'a, T>>;
332    type State = ();
333
334    fn fetch<'a>(
335        _state: &'a mut Self::State,
336        world: &'a hecs::World,
337        resource: &'a Resources,
338    ) -> Self::Item<'a> {
339        if resource.has_resource::<T>(world) {
340            return Some(ResMut {
341                data: resource.get_resource_mut(world),
342            });
343        }
344
345        None
346    }
347}
348
349impl<Q> SystemParam for Query<'static, Q>
350where
351    Q: hecs::Query + 'static,
352{
353    type Item<'a> = Query<'a, Q>;
354    type State = ();
355
356    fn fetch<'a>(
357        _state: &'a mut Self::State,
358        world: &'a hecs::World,
359        _resources: &'a Resources,
360    ) -> Self::Item<'a> {
361        Query {
362            world: world,
363            borrow: world.query::<Q>(),
364        }
365    }
366}
367
368impl SystemParam for Commands<'static> {
369    type Item<'a> = Commands<'a>;
370    type State = ();
371
372    fn fetch<'a>(
373        _state: &'a mut Self::State,
374        _world: &'a hecs::World,
375        resources: &'a Resources,
376    ) -> Self::Item<'a> {
377        Commands {
378            buffer: resources.get_command_buffer(),
379            resource_entity: resources.resource_entity,
380            resources,
381        }
382    }
383}
384
385impl SystemParam for &'static hecs::World {
386    type Item<'a> = &'a hecs::World;
387    type State = ();
388
389    fn fetch<'a>(
390        _state: &'a mut Self::State,
391        world: &'a hecs::World,
392        _resources: &'a Resources,
393    ) -> Self::Item<'a> {
394        world
395    }
396}
397
398impl SystemParam for &'static Resources {
399    type Item<'a> = &'a Resources;
400    type State = ();
401
402    fn fetch<'a>(
403        _state: &'a mut Self::State,
404        _world: &'a hecs::World,
405        resources: &'a Resources,
406    ) -> Self::Item<'a> {
407        resources
408    }
409}
410
411impl<T> SystemParam for Local<'static, T>
412where
413    T: Default + Send + Sync + 'static,
414{
415    type Item<'a> = Local<'a, T>;
416    type State = T;
417
418    fn fetch<'a>(
419        state: &'a mut Self::State,
420        _world: &'a hecs::World,
421        _resources: &'a Resources,
422    ) -> Self::Item<'a> {
423        Local { data: state }
424    }
425}
426
427/// A type-erased, executable system.
428pub trait System: 'static {
429    fn run(&mut self, world: &hecs::World, resources: &Resources);
430
431    /// Resource types this system needs present, derived automatically from
432    /// its bare [`Res`]/[`ResMut`] parameters. [`App`](crate::app::App)
433    /// checks these before running a non-convergent stage's systems and
434    /// panics with a clear message naming the missing resource(s) rather
435    /// than letting a param fetch panic deep inside whichever system happens
436    /// to run first.
437    fn requires(&self) -> Vec<RequiredResource> {
438        Vec::new()
439    }
440
441    /// Human-readable identifier for this system, used in error/trace output
442    /// so a missing-resource failure can be pinned to the system that needs
443    /// it instead of just the resource name. Defaults to the type name of
444    /// the [`System`] impl; [`FunctionSystem`] overrides this with the name
445    /// of the wrapped function/closure, which is far more legible.
446    fn name(&self) -> &'static str {
447        std::any::type_name::<Self>()
448    }
449
450    /// This system's identity for ordering purposes — the [`TypeId`](std::any::TypeId)
451    /// of the function/closure it wraps. Automatic: every distinct function
452    /// or closure has a distinct type, so no manual labeling is needed to
453    /// make a system a valid target for another system's
454    /// [`after`](SystemOrderingExt::after)/[`before`](SystemOrderingExt::before).
455    /// Defaults to `Self`'s own `TypeId`; [`FunctionSystem`]/[`OnceFunctionSystem`]
456    /// override it with the wrapped function's `TypeId` instead of the
457    /// wrapper's, so ordering constraints referencing the bare function match.
458    fn ordering_id(&self) -> std::any::TypeId {
459        std::any::TypeId::of::<Self>()
460    }
461
462    /// Other systems in the same stage that must run before this one. Set
463    /// via [`SystemOrderingExt::after`]. A referenced system that isn't
464    /// registered in the same stage is silently ignored.
465    fn after_ids(&self) -> &[std::any::TypeId] {
466        &[]
467    }
468
469    /// Other systems in the same stage that must run after this one. Set
470    /// via [`SystemOrderingExt::before`]. A referenced system that isn't
471    /// registered in the same stage is silently ignored.
472    fn before_ids(&self) -> &[std::any::TypeId] {
473        &[]
474    }
475}
476
477/// Wraps a [`System`] with ordering constraints relative to other systems in
478/// the same stage, added via [`SystemOrderingExt`].
479///
480/// Constraints only take effect within the stage the system is registered
481/// to — there's no cross-stage ordering, since stage order is already fixed
482/// by [`SystemStage`](crate::app::SystemStage). [`App::build`](crate::app::App::build)
483/// topologically sorts each stage's systems by these constraints, breaking
484/// ties by registration order, and panics if constraints form a cycle.
485pub struct Labeled<S: System> {
486    inner: S,
487    after: Vec<std::any::TypeId>,
488    before: Vec<std::any::TypeId>,
489}
490
491impl<S: System> Labeled<S> {
492    /// Require that `system` runs before this one, within the same stage.
493    /// Chainable — call multiple times to depend on multiple systems.
494    pub fn after<F: 'static, Marker>(mut self, system: F) -> Self
495    where
496        F: IntoSystem<Marker>,
497    {
498        let _ = system;
499        self.after.push(std::any::TypeId::of::<F>());
500        self
501    }
502
503    /// Require that `system` runs after this one, within the same stage.
504    /// Chainable — call multiple times to constrain multiple systems.
505    pub fn before<F: 'static, Marker>(mut self, system: F) -> Self
506    where
507        F: IntoSystem<Marker>,
508    {
509        let _ = system;
510        self.before.push(std::any::TypeId::of::<F>());
511        self
512    }
513}
514
515impl<S: System> System for Labeled<S> {
516    fn run(&mut self, world: &hecs::World, resources: &Resources) {
517        self.inner.run(world, resources)
518    }
519
520    fn requires(&self) -> Vec<RequiredResource> {
521        self.inner.requires()
522    }
523
524    fn name(&self) -> &'static str {
525        self.inner.name()
526    }
527
528    fn ordering_id(&self) -> std::any::TypeId {
529        self.inner.ordering_id()
530    }
531
532    fn after_ids(&self) -> &[std::any::TypeId] {
533        &self.after
534    }
535
536    fn before_ids(&self) -> &[std::any::TypeId] {
537        &self.before
538    }
539}
540
541impl<S: System> IntoSystem<()> for Labeled<S> {
542    type System = Self;
543
544    fn into_system(self) -> Self::System {
545        self
546    }
547}
548
549/// Adds [`.after()`](SystemOrderingExt::after)/[`.before()`](SystemOrderingExt::before)
550/// to any system, for declaring run-order constraints relative to other
551/// systems in the same stage — referenced directly by their function/closure,
552/// no string labels needed.
553///
554/// ```ignore
555/// app.add_system(SystemStage::Update, physics_step);
556/// app.add_system(SystemStage::Update, apply_damage.after(physics_step));
557/// ```
558pub trait SystemOrderingExt<Marker>: IntoSystem<Marker> + Sized {
559    /// Require that `system` runs before this one, within the same stage.
560    fn after<F: 'static, Marker2>(self, system: F) -> Labeled<Self::System>
561    where
562        F: IntoSystem<Marker2>,
563    {
564        let _ = system;
565        Labeled {
566            inner: self.into_system(),
567            after: vec![std::any::TypeId::of::<F>()],
568            before: Vec::new(),
569        }
570    }
571
572    /// Require that `system` runs after this one, within the same stage.
573    fn before<F: 'static, Marker2>(self, system: F) -> Labeled<Self::System>
574    where
575        F: IntoSystem<Marker2>,
576    {
577        let _ = system;
578        Labeled {
579            inner: self.into_system(),
580            after: Vec::new(),
581            before: vec![std::any::TypeId::of::<F>()],
582        }
583    }
584}
585
586impl<T, Marker> SystemOrderingExt<Marker> for T where T: IntoSystem<Marker> {}
587
588/// Type-erased wrapper around a system function, created by [`IntoSystem`].
589///
590/// Holds `State`, the tuple of each parameter's [`SystemParam::State`] — this
591/// is where [`Local`] values actually live between calls to `run`.
592pub struct FunctionSystem<F, Marker, State = ()> {
593    pub func: F,
594    state: State,
595    _marker: std::marker::PhantomData<Marker>,
596}
597
598/// Converts a function (or closure) with valid system parameters into a
599/// [`System`] that can be registered with [`App::add_system`](crate::app::App::add_system).
600///
601/// Implemented via the [`impl_system!`] macro for function arities 0–8.
602pub trait IntoSystem<Marker> {
603    type System: System;
604
605    fn into_system(self) -> Self::System;
606}
607
608macro_rules! impl_system {
609    ($($param:ident),*) => {
610        impl<T, $($param),*> IntoSystem<($($param,)*)> for T
611        where
612            T: for<'a> FnMut($($param::Item<'a>),*) + 'static,
613            for<'a> &'a mut T: FnMut($($param),*),
614            $($param: SystemParam + 'static),*
615        {
616            type System = FunctionSystem<T, ($($param,)*), ($($param::State,)*)>;
617
618            fn into_system(self) -> Self::System {
619                FunctionSystem {
620                    func: self,
621                    state: Default::default(),
622                    _marker: std::marker::PhantomData,
623                }
624            }
625        }
626
627        impl<T, $($param),*> System for FunctionSystem<T, ($($param,)*), ($($param::State,)*)>
628        where
629            T: for<'a> FnMut($($param::Item<'a>),*) + 'static,
630            $($param: SystemParam + 'static),*
631        {
632            fn run(&mut self, _world: &hecs::World, _resources: &Resources) {
633                #[allow(non_snake_case)]
634                let ($($param,)*) = &mut self.state;
635                (self.func)($($param::fetch($param, _world, _resources)),*);
636            }
637
638            fn requires(&self) -> Vec<RequiredResource> {
639                let mut _v = Vec::new();
640                $(_v.extend($param::requires());)*
641                _v
642            }
643
644            fn name(&self) -> &'static str {
645                std::any::type_name::<T>()
646            }
647
648            fn ordering_id(&self) -> std::any::TypeId {
649                std::any::TypeId::of::<T>()
650            }
651        }
652    };
653}
654
655impl_system!();
656impl_system!(A);
657impl_system!(A, B);
658impl_system!(A, B, C);
659impl_system!(A, B, C, D);
660impl_system!(A, B, C, D, E);
661impl_system!(A, B, C, D, E, F);
662impl_system!(A, B, C, D, E, F, G);
663impl_system!(A, B, C, D, E, F, G, H);
664impl_system!(A, B, C, D, E, F, G, H, I);
665impl_system!(A, B, C, D, E, F, G, H, I, J);
666impl_system!(A, B, C, D, E, F, G, H, I, J, K);
667impl_system!(A, B, C, D, E, F, G, H, I, J, K, L);
668
669/// Type-erased wrapper produced by [`OnceExt::once`]. Runs `func` every time
670/// it's invoked until `func` returns `Some(())`, at which point it's
671/// permanently retired — every subsequent invocation (and requirement check)
672/// is a no-op. The "have I already succeeded" bookkeeping lives entirely in
673/// `done`, hidden inside this wrapper; the wrapped function itself just
674/// returns `None` ("not ready, call me again") or `Some(())` ("done").
675pub struct OnceFunctionSystem<F, Marker, State = ()> {
676    func: F,
677    state: State,
678    done: bool,
679    _marker: std::marker::PhantomData<Marker>,
680}
681
682/// Adds [`.once()`](OnceExt::once) to a function/closure whose parameters
683/// are valid [`SystemParam`]s and whose return type is `Option<()>`,
684/// registering it as a system that runs on every tick of whichever stage
685/// it's added to until it returns `Some(())`, then never runs again.
686///
687/// This replaces manually tracking a "have I already done this" flag with
688/// a `Local<bool>`: return `None` from the function to mean "not ready,
689/// try again next tick" and `Some(())` to mean "done, retire me".
690///
691/// ```ignore
692/// fn setup(mut commands: Commands, pbr: Option<Res<PBR>>) -> Option<()> {
693///     let pbr = pbr?;
694///     if pbr.cubemap_material_inst == RawAssetHandle::default() {
695///         return None; // not ready yet — try again next tick
696///     }
697///     commands.spawn(/* ... */);
698///     Some(()) // done — never runs again
699/// }
700///
701/// app.add_system(SystemStage::PreUpdate, setup.once());
702/// ```
703pub trait OnceExt<Marker> {
704    type System: System;
705    fn once(self) -> Self::System;
706}
707
708macro_rules! impl_once_system {
709    ($($param:ident),*) => {
710        impl<T, $($param),*> OnceExt<($($param,)*)> for T
711        where
712            T: for<'a> FnMut($($param::Item<'a>),*) -> Option<()> + 'static,
713            for<'a> &'a mut T: FnMut($($param),*) -> Option<()>,
714            $($param: SystemParam + 'static),*
715        {
716            type System = OnceFunctionSystem<T, ($($param,)*), ($($param::State,)*)>;
717
718            fn once(self) -> Self::System {
719                OnceFunctionSystem {
720                    func: self,
721                    state: Default::default(),
722                    done: false,
723                    _marker: std::marker::PhantomData,
724                }
725            }
726        }
727
728        impl<T, $($param),*> IntoSystem<($($param,)*)> for OnceFunctionSystem<T, ($($param,)*), ($($param::State,)*)>
729        where
730            T: for<'a> FnMut($($param::Item<'a>),*) -> Option<()> + 'static,
731            $($param: SystemParam + 'static),*
732        {
733            type System = Self;
734
735            fn into_system(self) -> Self::System {
736                self
737            }
738        }
739
740        impl<T, $($param),*> System for OnceFunctionSystem<T, ($($param,)*), ($($param::State,)*)>
741        where
742            T: for<'a> FnMut($($param::Item<'a>),*) -> Option<()> + 'static,
743            $($param: SystemParam + 'static),*
744        {
745            fn run(&mut self, _world: &hecs::World, _resources: &Resources) {
746                if self.done {
747                    return;
748                }
749                #[allow(non_snake_case)]
750                let ($($param,)*) = &mut self.state;
751                let result = (self.func)($($param::fetch($param, _world, _resources)),*);
752                if result.is_some() {
753                    self.done = true;
754                }
755            }
756
757            fn requires(&self) -> Vec<RequiredResource> {
758                if self.done {
759                    return Vec::new();
760                }
761                let mut _v = Vec::new();
762                $(_v.extend($param::requires());)*
763                _v
764            }
765
766            fn name(&self) -> &'static str {
767                std::any::type_name::<T>()
768            }
769
770            fn ordering_id(&self) -> std::any::TypeId {
771                std::any::TypeId::of::<T>()
772            }
773        }
774    };
775}
776
777impl_once_system!();
778impl_once_system!(A);
779impl_once_system!(A, B);
780impl_once_system!(A, B, C);
781impl_once_system!(A, B, C, D);
782impl_once_system!(A, B, C, D, E);
783impl_once_system!(A, B, C, D, E, F);
784impl_once_system!(A, B, C, D, E, F, G);
785impl_once_system!(A, B, C, D, E, F, G, H);
786impl_once_system!(A, B, C, D, E, F, G, H, I);
787impl_once_system!(A, B, C, D, E, F, G, H, I, J);
788impl_once_system!(A, B, C, D, E, F, G, H, I, J, K);
789impl_once_system!(A, B, C, D, E, F, G, H, I, J, K, L);