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/// Trait implemented for each valid system parameter type.
95///
96/// The macro-generated [`impl_system!`] blanket implementations use this to
97/// fetch each parameter from the world and resources before calling the system
98/// function.
99trait SystemParam {
100    type Item<'a>;
101    fn fetch<'a>(world: &'a hecs::World, resources: &'a Resources) -> Self::Item<'a>;
102}
103
104impl<T> SystemParam for Res<'static, T>
105where
106    T: 'static + Sync + Send,
107{
108    type Item<'a> = Res<'a, T>;
109
110    fn fetch<'a>(world: &'a hecs::World, resource: &'a Resources) -> Self::Item<'a> {
111        Res {
112            data: resource.get_resource(world),
113        }
114    }
115}
116
117impl<T> SystemParam for Option<Res<'static, T>>
118where
119    T: 'static + Sync + Send,
120{
121    type Item<'a> = Option<Res<'a, T>>;
122
123    fn fetch<'a>(world: &'a hecs::World, resource: &'a Resources) -> Self::Item<'a> {
124        if resource.has_resource::<T>(world) {
125            return Some(Res {
126                data: resource.get_resource(world),
127            });
128        }
129
130        None
131    }
132}
133
134impl<T> SystemParam for ResMut<'static, T>
135where
136    T: 'static + Sync + Send,
137{
138    type Item<'a> = ResMut<'a, T>;
139
140    fn fetch<'a>(world: &'a hecs::World, resource: &'a Resources) -> Self::Item<'a> {
141        ResMut {
142            data: resource.get_resource_mut(world),
143        }
144    }
145}
146
147impl<T> SystemParam for Option<ResMut<'static, T>>
148where
149    T: 'static + Sync + Send,
150{
151    type Item<'a> = Option<ResMut<'a, T>>;
152
153    fn fetch<'a>(world: &'a hecs::World, resource: &'a Resources) -> Self::Item<'a> {
154        if resource.has_resource::<T>(world) {
155            return Some(ResMut {
156                data: resource.get_resource_mut(world),
157            });
158        }
159
160        None
161    }
162}
163
164impl<Q> SystemParam for Query<'static, Q>
165where
166    Q: hecs::Query + 'static,
167{
168    type Item<'a> = Query<'a, Q>;
169
170    fn fetch<'a>(world: &'a hecs::World, _resources: &'a Resources) -> Self::Item<'a> {
171        Query {
172            borrow: world.query::<Q>(),
173        }
174    }
175}
176
177impl SystemParam for Commands<'static> {
178    type Item<'a> = Commands<'a>;
179
180    fn fetch<'a>(_world: &'a hecs::World, resources: &'a Resources) -> Self::Item<'a> {
181        Commands {
182            buffer: resources.get_command_buffer(),
183            resource_entity: resources.resource_entity,
184        }
185    }
186}
187
188impl SystemParam for &'static hecs::World {
189    type Item<'a> = &'a hecs::World;
190
191    fn fetch<'a>(world: &'a hecs::World, _resources: &'a Resources) -> Self::Item<'a> {
192        world
193    }
194}
195
196impl SystemParam for &'static Resources {
197    type Item<'a> = &'a Resources;
198
199    fn fetch<'a>(_world: &'a hecs::World, resources: &'a Resources) -> Self::Item<'a> {
200        resources
201    }
202}
203
204/// A type-erased, executable system.
205pub trait System: 'static {
206    fn run(&mut self, world: &hecs::World, resources: &Resources);
207}
208
209/// Type-erased wrapper around a system function, created by [`IntoSystem`].
210pub struct FunctionSystem<F, Marker> {
211    pub func: F,
212    _marker: std::marker::PhantomData<Marker>,
213}
214
215/// Converts a function (or closure) with valid system parameters into a
216/// [`System`] that can be registered with [`App::add_system`](crate::app::App::add_system).
217///
218/// Implemented via the [`impl_system!`] macro for function arities 0–8.
219pub trait IntoSystem<Marker> {
220    type System: System;
221
222    fn into_system(self) -> Self::System;
223}
224
225macro_rules! impl_system {
226    ($($param:ident),*) => {
227        impl<T, $($param),*> IntoSystem<($($param,)*)> for T
228        where
229            T: for<'a> FnMut($($param::Item<'a>),*) + 'static,
230            for<'a> &'a mut T: FnMut($($param),*),
231            $($param: SystemParam + 'static),*
232        {
233            type System = FunctionSystem<T, ($($param,)*)>;
234
235            fn into_system(self) -> Self::System {
236                FunctionSystem {
237                    func: self,
238                    _marker: std::marker::PhantomData,
239                }
240            }
241        }
242
243        impl<T, $($param),*> System for FunctionSystem<T, ($($param,)*)>
244        where
245            T: for<'a> FnMut($($param::Item<'a>),*) + 'static,
246            $($param: SystemParam + 'static),*
247        {
248            fn run(&mut self, _world: &hecs::World, _resources: &Resources) {
249                (self.func)($($param::fetch(_world, _resources)),*);
250            }
251        }
252    };
253}
254
255impl_system!();
256impl_system!(A);
257impl_system!(A, B);
258impl_system!(A, B, C);
259impl_system!(A, B, C, D);
260impl_system!(A, B, C, D, E);
261impl_system!(A, B, C, D, E, F);
262impl_system!(A, B, C, D, E, F, G);
263impl_system!(A, B, C, D, E, F, G, H);