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/// Immutable borrow of a singleton resource `T`.
7///
8/// Obtained as a system parameter; derefs to `T`.
9pub struct Res<'a, T: hecs::Component> {
10    pub(crate) data: hecs::Ref<'a, T>,
11}
12
13impl<'a, T: hecs::Component> Deref for Res<'a, T> {
14    type Target = T;
15    fn deref(&self) -> &Self::Target {
16        &self.data
17    }
18}
19
20/// Mutable borrow of a singleton resource `T`.
21///
22/// Obtained as a system parameter; derefs to `T`.
23pub struct ResMut<'a, T: hecs::Component> {
24    data: hecs::RefMut<'a, T>,
25}
26
27impl<'a, T: hecs::Component> Deref for ResMut<'a, T> {
28    type Target = T;
29    fn deref(&self) -> &Self::Target {
30        &self.data
31    }
32}
33
34impl<'a, T: hecs::Component> DerefMut for ResMut<'a, T> {
35    fn deref_mut(&mut self) -> &mut Self::Target {
36        &mut self.data
37    }
38}
39
40/// Borrow of an ECS query result.
41///
42/// Obtained as a system parameter; derefs to [`hecs::QueryBorrow`].
43///
44/// # Iterating
45///
46/// `Query` implements `IntoIterator` for `&mut Query`, so you can iterate it
47/// directly without going through `Deref`:
48///
49/// ```ignore
50/// fn move_system(mut q: Query<(&mut Position, &Velocity)>) {
51///     for (entity, (pos, vel)) in &mut q {
52///         pos.x += vel.x;
53///         pos.y += vel.y;
54///     }
55/// }
56/// ```
57///
58/// # Single-entity lookups
59///
60/// Use [`Query::get`] to fetch components for one known `Entity` without
61/// scanning the whole result set, and [`Query::single`] /
62/// [`Query::get_single`] when you expect exactly one match (e.g. "the
63/// player", "the active camera").
64pub struct Query<'a, Q: hecs::Query> {
65    world: &'a hecs::World,
66    borrow: hecs::QueryBorrow<'a, Q>,
67}
68
69impl<'a, Q: hecs::Query> Deref for Query<'a, Q> {
70    type Target = hecs::QueryBorrow<'a, Q>;
71    fn deref(&self) -> &Self::Target {
72        &self.borrow
73    }
74}
75
76impl<'a, Q: hecs::Query> DerefMut for Query<'a, Q> {
77    fn deref_mut(&mut self) -> &mut Self::Target {
78        &mut self.borrow
79    }
80}
81
82impl<'q, Q: hecs::Query> IntoIterator for &'q mut Query<'_, Q> {
83    type Item = Q::Item<'q>;
84    type IntoIter = hecs::QueryIter<'q, Q>;
85
86    fn into_iter(self) -> Self::IntoIter {
87        (&mut self.borrow).into_iter()
88    }
89}
90
91impl<'a, Q: hecs::Query> Query<'a, Q> {
92    /// Fetch components for a single known entity, without iterating the
93    /// rest of the query.
94    ///
95    /// Returns `None` if the entity doesn't exist or doesn't match `Q`.
96    /// The result is handed to `f` rather than returned directly, since the
97    /// borrow can only live as long as the lookup itself.
98    ///
99    /// Include `hecs::Entity` in `Q` if you need the id back out, e.g.
100    /// `Query<(hecs::Entity, &Health)>`.
101    ///
102    /// ```ignore
103    /// let hp = q.get(player, |health| health.current);
104    /// ```
105    pub fn get<T>(
106        &self,
107        entity: hecs::Entity,
108        f: impl for<'r> FnOnce(Q::Item<'r>) -> T,
109    ) -> Option<T> {
110        self.world.query_one::<Q>(entity).get().ok().map(f)
111    }
112
113    /// Filter this query to only entities that ALSO have component `R`,
114    /// without `R` itself being part of the yielded items. Consumes
115    /// `self` — matches hecs's own `QueryBorrow::with` signature.
116    pub fn with<R: hecs::Query>(self) -> hecs::QueryBorrow<'a, hecs::With<Q, R>> {
117        self.borrow.with::<R>()
118    }
119
120    /// Filter this query to only entities that do NOT have component `R`.
121    /// Consumes `self`, same reasoning as `with`.
122    pub fn without<R: hecs::Query>(self) -> hecs::QueryBorrow<'a, hecs::Without<Q, R>> {
123        self.borrow.without::<R>()
124    }
125
126    /// Return the single entity's components for this query.
127    ///
128    /// Panics if there isn't exactly one match. Intended for singleton-style
129    /// queries (the player, the active camera, ...) where zero or multiple
130    /// matches indicate a bug. See [`Query::get_single`] for a
131    /// non-panicking version. Include `hecs::Entity` in `Q` if you need the
132    /// id alongside the components.
133    pub fn single(&mut self) -> Q::Item<'_> {
134        self.get_single()
135            .expect("Query::single: expected exactly one matching entity")
136    }
137
138    /// Like [`Query::single`], but returns `None` instead of panicking when
139    /// there isn't exactly one match.
140    pub fn get_single(&mut self) -> Option<Q::Item<'_>> {
141        let mut iter = self.borrow.iter();
142        let first = iter.next()?;
143        if iter.next().is_some() {
144            return None;
145        }
146        Some(first)
147    }
148}
149
150/// Deferred world-mutation commands available as a system parameter.
151///
152/// Mutations are buffered and applied to the world after all systems in the
153/// current stage have finished running.
154pub struct Commands<'a> {
155    buffer: RefMut<'a, hecs::CommandBuffer>,
156    resource_entity: hecs::Entity,
157}
158
159impl<'a> Commands<'a> {
160    /// Queue a resource insertion. Applied after the current stage finishes.
161    pub fn insert_resource<T: hecs::Component>(&mut self, res: T) {
162        self.buffer.insert_one(self.resource_entity, res);
163    }
164
165    /// Queue a resource removal. Applied after the current stage finishes.
166    pub fn remove_resource<T: hecs::Component>(&mut self) {
167        self.buffer.remove_one::<T>(self.resource_entity);
168    }
169}
170
171impl<'a> Deref for Commands<'a> {
172    type Target = hecs::CommandBuffer;
173    fn deref(&self) -> &Self::Target {
174        &self.buffer
175    }
176}
177
178impl<'a> DerefMut for Commands<'a> {
179    fn deref_mut(&mut self) -> &mut Self::Target {
180        &mut self.buffer
181    }
182}
183
184/// Per-system persistent local state.
185///
186/// Unlike [`Res`]/[`ResMut`], a `Local<T>` is *not* shared through
187/// [`Resources`] — each system gets its own private `T`, initialized with
188/// [`Default::default`] the first time the system is registered, and
189/// preserved across every subsequent run of that system.
190///
191/// Useful for counters, caches, or any state a single system needs to
192/// remember without polluting the global resource set.
193pub struct Local<'a, T: Default + Send + Sync + 'static> {
194    data: &'a mut T,
195}
196
197impl<'a, T: Default + Send + Sync + 'static> Deref for Local<'a, T> {
198    type Target = T;
199    fn deref(&self) -> &Self::Target {
200        self.data
201    }
202}
203
204impl<'a, T: Default + Send + Sync + 'static> DerefMut for Local<'a, T> {
205    fn deref_mut(&mut self) -> &mut Self::Target {
206        self.data
207    }
208}
209
210/// Trait implemented for each valid system parameter type.
211///
212/// The macro-generated [`impl_system!`] blanket implementations use this to
213/// fetch each parameter from the world and resources before calling the system
214/// function. `State` is per-system storage owned by the [`FunctionSystem`]
215/// itself (as opposed to `Item`, which only lives for the duration of one
216/// call) — this is what lets [`Local`] persist between runs.
217pub trait SystemParam {
218    type Item<'a>;
219    type State: Default + 'static;
220    fn fetch<'a>(
221        state: &'a mut Self::State,
222        world: &'a hecs::World,
223        resources: &'a Resources,
224    ) -> Self::Item<'a>;
225}
226
227impl<T> SystemParam for Res<'static, T>
228where
229    T: 'static + Sync + Send,
230{
231    type Item<'a> = Res<'a, T>;
232    type State = ();
233
234    fn fetch<'a>(
235        _state: &'a mut Self::State,
236        world: &'a hecs::World,
237        resource: &'a Resources,
238    ) -> Self::Item<'a> {
239        Res {
240            data: resource.get_resource(world),
241        }
242    }
243}
244
245impl<T> SystemParam for Option<Res<'static, T>>
246where
247    T: 'static + Sync + Send,
248{
249    type Item<'a> = Option<Res<'a, T>>;
250    type State = ();
251
252    fn fetch<'a>(
253        _state: &'a mut Self::State,
254        world: &'a hecs::World,
255        resource: &'a Resources,
256    ) -> Self::Item<'a> {
257        if resource.has_resource::<T>(world) {
258            return Some(Res {
259                data: resource.get_resource(world),
260            });
261        }
262
263        None
264    }
265}
266
267impl<T> SystemParam for ResMut<'static, T>
268where
269    T: 'static + Sync + Send,
270{
271    type Item<'a> = ResMut<'a, T>;
272    type State = ();
273
274    fn fetch<'a>(
275        _state: &'a mut Self::State,
276        world: &'a hecs::World,
277        resource: &'a Resources,
278    ) -> Self::Item<'a> {
279        ResMut {
280            data: resource.get_resource_mut(world),
281        }
282    }
283}
284
285impl<T> SystemParam for Option<ResMut<'static, T>>
286where
287    T: 'static + Sync + Send,
288{
289    type Item<'a> = Option<ResMut<'a, T>>;
290    type State = ();
291
292    fn fetch<'a>(
293        _state: &'a mut Self::State,
294        world: &'a hecs::World,
295        resource: &'a Resources,
296    ) -> Self::Item<'a> {
297        if resource.has_resource::<T>(world) {
298            return Some(ResMut {
299                data: resource.get_resource_mut(world),
300            });
301        }
302
303        None
304    }
305}
306
307impl<Q> SystemParam for Query<'static, Q>
308where
309    Q: hecs::Query + 'static,
310{
311    type Item<'a> = Query<'a, Q>;
312    type State = ();
313
314    fn fetch<'a>(
315        _state: &'a mut Self::State,
316        world: &'a hecs::World,
317        _resources: &'a Resources,
318    ) -> Self::Item<'a> {
319        Query {
320            world: world,
321            borrow: world.query::<Q>(),
322        }
323    }
324}
325
326impl SystemParam for Commands<'static> {
327    type Item<'a> = Commands<'a>;
328    type State = ();
329
330    fn fetch<'a>(
331        _state: &'a mut Self::State,
332        _world: &'a hecs::World,
333        resources: &'a Resources,
334    ) -> Self::Item<'a> {
335        Commands {
336            buffer: resources.get_command_buffer(),
337            resource_entity: resources.resource_entity,
338        }
339    }
340}
341
342impl SystemParam for &'static hecs::World {
343    type Item<'a> = &'a hecs::World;
344    type State = ();
345
346    fn fetch<'a>(
347        _state: &'a mut Self::State,
348        world: &'a hecs::World,
349        _resources: &'a Resources,
350    ) -> Self::Item<'a> {
351        world
352    }
353}
354
355impl SystemParam for &'static Resources {
356    type Item<'a> = &'a Resources;
357    type State = ();
358
359    fn fetch<'a>(
360        _state: &'a mut Self::State,
361        _world: &'a hecs::World,
362        resources: &'a Resources,
363    ) -> Self::Item<'a> {
364        resources
365    }
366}
367
368impl<T> SystemParam for Local<'static, T>
369where
370    T: Default + Send + Sync + 'static,
371{
372    type Item<'a> = Local<'a, T>;
373    type State = T;
374
375    fn fetch<'a>(
376        state: &'a mut Self::State,
377        _world: &'a hecs::World,
378        _resources: &'a Resources,
379    ) -> Self::Item<'a> {
380        Local { data: state }
381    }
382}
383
384/// A type-erased, executable system.
385pub trait System: 'static {
386    fn run(&mut self, world: &hecs::World, resources: &Resources);
387}
388
389/// Type-erased wrapper around a system function, created by [`IntoSystem`].
390///
391/// Holds `State`, the tuple of each parameter's [`SystemParam::State`] — this
392/// is where [`Local`] values actually live between calls to `run`.
393pub struct FunctionSystem<F, Marker, State = ()> {
394    pub func: F,
395    state: State,
396    _marker: std::marker::PhantomData<Marker>,
397}
398
399/// Converts a function (or closure) with valid system parameters into a
400/// [`System`] that can be registered with [`App::add_system`](crate::app::App::add_system).
401///
402/// Implemented via the [`impl_system!`] macro for function arities 0–8.
403pub trait IntoSystem<Marker> {
404    type System: System;
405
406    fn into_system(self) -> Self::System;
407}
408
409macro_rules! impl_system {
410    ($($param:ident),*) => {
411        impl<T, $($param),*> IntoSystem<($($param,)*)> for T
412        where
413            T: for<'a> FnMut($($param::Item<'a>),*) + 'static,
414            for<'a> &'a mut T: FnMut($($param),*),
415            $($param: SystemParam + 'static),*
416        {
417            type System = FunctionSystem<T, ($($param,)*), ($($param::State,)*)>;
418
419            fn into_system(self) -> Self::System {
420                FunctionSystem {
421                    func: self,
422                    state: Default::default(),
423                    _marker: std::marker::PhantomData,
424                }
425            }
426        }
427
428        impl<T, $($param),*> System for FunctionSystem<T, ($($param,)*), ($($param::State,)*)>
429        where
430            T: for<'a> FnMut($($param::Item<'a>),*) + 'static,
431            $($param: SystemParam + 'static),*
432        {
433            fn run(&mut self, _world: &hecs::World, _resources: &Resources) {
434                #[allow(non_snake_case)]
435                let ($($param,)*) = &mut self.state;
436                (self.func)($($param::fetch($param, _world, _resources)),*);
437            }
438        }
439    };
440}
441
442impl_system!();
443impl_system!(A);
444impl_system!(A, B);
445impl_system!(A, B, C);
446impl_system!(A, B, C, D);
447impl_system!(A, B, C, D, E);
448impl_system!(A, B, C, D, E, F);
449impl_system!(A, B, C, D, E, F, G);
450impl_system!(A, B, C, D, E, F, G, H);