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`].
43pub struct Query<'a, Q: hecs::Query> {
44    borrow: hecs::QueryBorrow<'a, Q>,
45}
46
47impl<'a, Q: hecs::Query> Deref for Query<'a, Q> {
48    type Target = hecs::QueryBorrow<'a, Q>;
49    fn deref(&self) -> &Self::Target {
50        &self.borrow
51    }
52}
53
54impl<'a, Q: hecs::Query> DerefMut for Query<'a, Q> {
55    fn deref_mut(&mut self) -> &mut Self::Target {
56        &mut self.borrow
57    }
58}
59
60/// Deferred world-mutation commands available as a system parameter.
61///
62/// Mutations are buffered and applied to the world after all systems in the
63/// current stage have finished running.
64pub struct Commands<'a> {
65    buffer: RefMut<'a, hecs::CommandBuffer>,
66    resource_entity: hecs::Entity,
67}
68
69impl<'a> Commands<'a> {
70    /// Queue a resource insertion. Applied after the current stage finishes.
71    pub fn insert_resource<T: hecs::Component>(&mut self, res: T) {
72        self.buffer.insert_one(self.resource_entity, res);
73    }
74
75    /// Queue a resource removal. Applied after the current stage finishes.
76    pub fn remove_resource<T: hecs::Component>(&mut self) {
77        self.buffer.remove_one::<T>(self.resource_entity);
78    }
79}
80
81impl<'a> Deref for Commands<'a> {
82    type Target = hecs::CommandBuffer;
83    fn deref(&self) -> &Self::Target {
84        &self.buffer
85    }
86}
87
88impl<'a> DerefMut for Commands<'a> {
89    fn deref_mut(&mut self) -> &mut Self::Target {
90        &mut self.buffer
91    }
92}
93
94/// Per-system persistent local state.
95///
96/// Unlike [`Res`]/[`ResMut`], a `Local<T>` is *not* shared through
97/// [`Resources`] — each system gets its own private `T`, initialized with
98/// [`Default::default`] the first time the system is registered, and
99/// preserved across every subsequent run of that system.
100///
101/// Useful for counters, caches, or any state a single system needs to
102/// remember without polluting the global resource set.
103pub struct Local<'a, T: Default + Send + Sync + 'static> {
104    data: &'a mut T,
105}
106
107impl<'a, T: Default + Send + Sync + 'static> Deref for Local<'a, T> {
108    type Target = T;
109    fn deref(&self) -> &Self::Target {
110        self.data
111    }
112}
113
114impl<'a, T: Default + Send + Sync + 'static> DerefMut for Local<'a, T> {
115    fn deref_mut(&mut self) -> &mut Self::Target {
116        self.data
117    }
118}
119
120/// Trait implemented for each valid system parameter type.
121///
122/// The macro-generated [`impl_system!`] blanket implementations use this to
123/// fetch each parameter from the world and resources before calling the system
124/// function. `State` is per-system storage owned by the [`FunctionSystem`]
125/// itself (as opposed to `Item`, which only lives for the duration of one
126/// call) — this is what lets [`Local`] persist between runs.
127pub trait SystemParam {
128    type Item<'a>;
129    type State: Default + 'static;
130    fn fetch<'a>(
131        state: &'a mut Self::State,
132        world: &'a hecs::World,
133        resources: &'a Resources,
134    ) -> Self::Item<'a>;
135}
136
137impl<T> SystemParam for Res<'static, T>
138where
139    T: 'static + Sync + Send,
140{
141    type Item<'a> = Res<'a, T>;
142    type State = ();
143
144    fn fetch<'a>(
145        _state: &'a mut Self::State,
146        world: &'a hecs::World,
147        resource: &'a Resources,
148    ) -> Self::Item<'a> {
149        Res {
150            data: resource.get_resource(world),
151        }
152    }
153}
154
155impl<T> SystemParam for Option<Res<'static, T>>
156where
157    T: 'static + Sync + Send,
158{
159    type Item<'a> = Option<Res<'a, T>>;
160    type State = ();
161
162    fn fetch<'a>(
163        _state: &'a mut Self::State,
164        world: &'a hecs::World,
165        resource: &'a Resources,
166    ) -> Self::Item<'a> {
167        if resource.has_resource::<T>(world) {
168            return Some(Res {
169                data: resource.get_resource(world),
170            });
171        }
172
173        None
174    }
175}
176
177impl<T> SystemParam for ResMut<'static, T>
178where
179    T: 'static + Sync + Send,
180{
181    type Item<'a> = ResMut<'a, T>;
182    type State = ();
183
184    fn fetch<'a>(
185        _state: &'a mut Self::State,
186        world: &'a hecs::World,
187        resource: &'a Resources,
188    ) -> Self::Item<'a> {
189        ResMut {
190            data: resource.get_resource_mut(world),
191        }
192    }
193}
194
195impl<T> SystemParam for Option<ResMut<'static, T>>
196where
197    T: 'static + Sync + Send,
198{
199    type Item<'a> = Option<ResMut<'a, T>>;
200    type State = ();
201
202    fn fetch<'a>(
203        _state: &'a mut Self::State,
204        world: &'a hecs::World,
205        resource: &'a Resources,
206    ) -> Self::Item<'a> {
207        if resource.has_resource::<T>(world) {
208            return Some(ResMut {
209                data: resource.get_resource_mut(world),
210            });
211        }
212
213        None
214    }
215}
216
217impl<Q> SystemParam for Query<'static, Q>
218where
219    Q: hecs::Query + 'static,
220{
221    type Item<'a> = Query<'a, Q>;
222    type State = ();
223
224    fn fetch<'a>(
225        _state: &'a mut Self::State,
226        world: &'a hecs::World,
227        _resources: &'a Resources,
228    ) -> Self::Item<'a> {
229        Query {
230            borrow: world.query::<Q>(),
231        }
232    }
233}
234
235impl SystemParam for Commands<'static> {
236    type Item<'a> = Commands<'a>;
237    type State = ();
238
239    fn fetch<'a>(
240        _state: &'a mut Self::State,
241        _world: &'a hecs::World,
242        resources: &'a Resources,
243    ) -> Self::Item<'a> {
244        Commands {
245            buffer: resources.get_command_buffer(),
246            resource_entity: resources.resource_entity,
247        }
248    }
249}
250
251impl SystemParam for &'static hecs::World {
252    type Item<'a> = &'a hecs::World;
253    type State = ();
254
255    fn fetch<'a>(
256        _state: &'a mut Self::State,
257        world: &'a hecs::World,
258        _resources: &'a Resources,
259    ) -> Self::Item<'a> {
260        world
261    }
262}
263
264impl SystemParam for &'static Resources {
265    type Item<'a> = &'a Resources;
266    type State = ();
267
268    fn fetch<'a>(
269        _state: &'a mut Self::State,
270        _world: &'a hecs::World,
271        resources: &'a Resources,
272    ) -> Self::Item<'a> {
273        resources
274    }
275}
276
277impl<T> SystemParam for Local<'static, T>
278where
279    T: Default + Send + Sync + 'static,
280{
281    type Item<'a> = Local<'a, T>;
282    type State = T;
283
284    fn fetch<'a>(
285        state: &'a mut Self::State,
286        _world: &'a hecs::World,
287        _resources: &'a Resources,
288    ) -> Self::Item<'a> {
289        Local { data: state }
290    }
291}
292
293/// A type-erased, executable system.
294pub trait System: 'static {
295    fn run(&mut self, world: &hecs::World, resources: &Resources);
296}
297
298/// Type-erased wrapper around a system function, created by [`IntoSystem`].
299///
300/// Holds `State`, the tuple of each parameter's [`SystemParam::State`] — this
301/// is where [`Local`] values actually live between calls to `run`.
302pub struct FunctionSystem<F, Marker, State = ()> {
303    pub func: F,
304    state: State,
305    _marker: std::marker::PhantomData<Marker>,
306}
307
308/// Converts a function (or closure) with valid system parameters into a
309/// [`System`] that can be registered with [`App::add_system`](crate::app::App::add_system).
310///
311/// Implemented via the [`impl_system!`] macro for function arities 0–8.
312pub trait IntoSystem<Marker> {
313    type System: System;
314
315    fn into_system(self) -> Self::System;
316}
317
318macro_rules! impl_system {
319    ($($param:ident),*) => {
320        impl<T, $($param),*> IntoSystem<($($param,)*)> for T
321        where
322            T: for<'a> FnMut($($param::Item<'a>),*) + 'static,
323            for<'a> &'a mut T: FnMut($($param),*),
324            $($param: SystemParam + 'static),*
325        {
326            type System = FunctionSystem<T, ($($param,)*), ($($param::State,)*)>;
327
328            fn into_system(self) -> Self::System {
329                FunctionSystem {
330                    func: self,
331                    state: Default::default(),
332                    _marker: std::marker::PhantomData,
333                }
334            }
335        }
336
337        impl<T, $($param),*> System for FunctionSystem<T, ($($param,)*), ($($param::State,)*)>
338        where
339            T: for<'a> FnMut($($param::Item<'a>),*) + 'static,
340            $($param: SystemParam + 'static),*
341        {
342            fn run(&mut self, _world: &hecs::World, _resources: &Resources) {
343                #[allow(non_snake_case)]
344                let ($($param,)*) = &mut self.state;
345                (self.func)($($param::fetch($param, _world, _resources)),*);
346            }
347        }
348    };
349}
350
351impl_system!();
352impl_system!(A);
353impl_system!(A, B);
354impl_system!(A, B, C);
355impl_system!(A, B, C, D);
356impl_system!(A, B, C, D, E);
357impl_system!(A, B, C, D, E, F);
358impl_system!(A, B, C, D, E, F, G);
359impl_system!(A, B, C, D, E, F, G, H);