bones_framework/
params.rs

1//! Bones ECS system parameters.
2
3use std::{cell::RefCell, pin::Pin};
4
5use crate::prelude::*;
6
7type DashmapRef<'a, T> = dashmap::mapref::one::MappedRef<'a, Cid, LoadedAsset, T>;
8
9type DashmapIter<'a, K, V> = dashmap::iter::Iter<'a, K, V>;
10
11/// Get the root asset of the core asset pack and cast it to type `T`.
12pub struct Root<'a, T: HasSchema>(DashmapRef<'a, T>);
13
14impl<'a, T: HasSchema> std::ops::Deref for Root<'a, T> {
15    type Target = T;
16    fn deref(&self) -> &Self::Target {
17        &self.0
18    }
19}
20
21impl<'a, T: HasSchema> SystemParam for Root<'a, T> {
22    type State = AssetServer;
23    type Param<'s> = Root<'s, T>;
24
25    fn get_state(world: &World) -> Self::State {
26        (*world.resources.get::<AssetServer>().unwrap()).clone()
27    }
28    fn borrow<'s>(_world: &'s World, asset_server: &'s mut Self::State) -> Self::Param<'s> {
29        Root(asset_server.root())
30    }
31}
32
33/// A helper system param for iterating over the root assets of the (non-core) asset packs, each
34/// casted to type `T`.
35///
36/// Asset packs contain a root asset in the form of an untyped asset handle. Use the
37/// [`iter`][Self::iter] method to get an iterator over all asset packs.
38///
39/// ## Example
40///
41/// ```rust
42/// use bones_framework::prelude::*;
43/// use tracing::info;
44///
45/// #[derive(Clone, Default, HasSchema)]
46/// #[type_data(metadata_asset("root"))]
47/// #[repr(C)]
48/// struct PackMeta {
49///     name: String,
50/// }
51///
52/// // Log the names of all non-core asset packs.
53/// fn test(packs: Packs<PackMeta>) -> Vec<String> {
54///     let mut names = Vec::new();
55///     for pack in packs.iter() {
56///         names.push(pack.name.clone());
57///     }
58///     names
59/// }
60///
61/// // Make sure that `Packs` is a valid system param.
62/// IntoSystem::system(test);
63/// ```
64///
65pub struct Packs<'a, T> {
66    asset_server: AssetServer,
67    _pack_t: std::marker::PhantomData<&'a T>,
68}
69
70impl<'a, T: HasSchema> SystemParam for Packs<'a, T> {
71    type State = AssetServer;
72    type Param<'s> = Packs<'s, T>;
73
74    fn get_state(world: &World) -> Self::State {
75        (*world.resource::<AssetServer>()).clone()
76    }
77
78    fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
79        Packs {
80            asset_server: state.clone(),
81            _pack_t: std::marker::PhantomData,
82        }
83    }
84}
85
86impl<T> Packs<'_, T> {
87    /// Get the typed asset pack roots iterator.
88    pub fn iter(&self) -> PacksIter<'_, T> {
89        PacksIter {
90            asset_server: &self.asset_server,
91            asset_packs_iter: self.asset_server.packs().iter(),
92            _pack_t: std::marker::PhantomData,
93        }
94    }
95}
96
97/// A typed iterator over asset pack roots.
98pub struct PacksIter<'a, T> {
99    asset_server: &'a AssetServer,
100    asset_packs_iter: DashmapIter<'a, AssetPackSpec, AssetPack>,
101    _pack_t: std::marker::PhantomData<&'a T>,
102}
103
104impl<'a, T: HasSchema> Iterator for PacksIter<'a, T> {
105    type Item = DashmapRef<'a, T>;
106
107    fn next(&mut self) -> Option<Self::Item> {
108        let pack = self.asset_packs_iter.next()?;
109        let pack_root = self.asset_server.get(pack.root.typed::<T>());
110        Some(pack_root)
111    }
112}
113
114/// A helper system param that allows for iteration over data contained in the
115/// core game asset pack (the one in the `assets/` directory) and the supplementary asset
116/// packs (the sub-directories of the `packs/` directory).
117///
118/// This is intended for use with lists contained in the asset packs. For example, the core and
119/// supplementary asset packs may contain lists of characters that users can choose to play as.
120/// This system param may be used to iterate all of the characters available in all of the asset
121/// packs.
122///
123/// ## Example
124///
125/// ```rust
126/// use bones_framework::prelude::*;
127/// use tracing::info;
128///
129/// #[derive(Clone, Default, HasSchema)]
130/// #[type_data(metadata_asset("root"))]
131/// #[repr(C)]
132/// struct GameMeta {
133///     maps: SVec<Handle<MapMeta>>,
134/// }
135///
136/// #[derive(Clone, Default, HasSchema)]
137/// #[type_data(metadata_asset("pack"))]
138/// #[repr(C)]
139/// struct PackMeta {
140///     maps: SVec<Handle<MapMeta>>,
141/// }
142///
143/// #[derive(Clone, Default, HasSchema)]
144/// #[type_data(metadata_asset("root"))]
145/// struct MapMeta {
146///     name: String,
147/// }
148///
149/// // Log the names of all maps in the core and other asset packs.
150/// fn test(asset_server: Res<AssetServer>, packs: AllPacksData<GameMeta, PackMeta>) {
151///     for handle in packs.iter_with(
152///         |game: &GameMeta| game.maps.iter().copied(),
153///         |pack: &PackMeta| pack.maps.iter().copied()
154///     ) {
155///         let map_meta = asset_server.get(handle);
156///         info!(name = map_meta.name, "map");
157///     }
158/// }
159///
160/// // Make sure that `AllPacksData` is a valid system param.
161/// IntoSystem::system(test);
162/// ```
163///
164pub struct AllPacksData<'a, Core, Pack>
165where
166    Core: HasSchema,
167    Pack: HasSchema,
168{
169    core_root: Root<'a, Core>,
170    pack_roots: Packs<'a, Pack>,
171}
172
173impl<'a, Core, Pack> SystemParam for AllPacksData<'a, Core, Pack>
174where
175    Core: HasSchema,
176    Pack: HasSchema,
177{
178    type State = (
179        <Root<'a, Core> as SystemParam>::State,
180        <Packs<'a, Pack> as SystemParam>::State,
181    );
182    type Param<'s> = AllPacksData<'s, Core, Pack>;
183
184    fn get_state(world: &World) -> Self::State {
185        (
186            Root::<'a, Core>::get_state(world),
187            Packs::<'a, Pack>::get_state(world),
188        )
189    }
190
191    fn borrow<'s>(world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
192        AllPacksData {
193            core_root: Root::<'s, Core>::borrow(world, &mut state.0),
194            pack_roots: Packs::<'s, Pack>::borrow(world, &mut state.1),
195        }
196    }
197}
198
199impl<'a, Core, Pack> AllPacksData<'a, Core, Pack>
200where
201    Core: HasSchema,
202    Pack: HasSchema,
203{
204    /// Get the iterator over the core and supplementary asset packs.
205    ///
206    /// The first argument `core_accessor` is a function that must produce an iterator of `T` from
207    /// the core asset metadata. This is only called once, prior to iteration.
208    ///
209    /// Similarly, the second argument `pack_accessor` is a function that must produce an iterator
210    /// of `T` from a pack asset metadata. This is called once per pack, during iteration.
211    pub fn iter_with<T, CoreItemIt: Iterator<Item = T>, PackItemIt: Iterator<Item = T>>(
212        &'a self,
213        mut core_accessor: impl FnMut(&'a Core) -> CoreItemIt,
214        pack_accessor: impl 'static + FnMut(&'a Pack) -> PackItemIt,
215    ) -> AllPacksDataIter<'a, T, CoreItemIt, Pack, PacksIter<'a, Pack>, PackItemIt> {
216        AllPacksDataIter {
217            core_item_iter: core_accessor(&*self.core_root),
218            pack_iter: self.pack_roots.iter(),
219            pack_accessor: Box::new(pack_accessor),
220            current_pack: AllPacksDataCurrentPack::new(),
221        }
222    }
223}
224
225/// A flattened iterator of items of type `T` from data within the core and supplementary asset
226/// packs. Items are first yielded from the core asset pack until exhausted, then items are yielded
227/// from the supplementary asset packs, one at a time, and in no particular order.
228///
229/// Can be acquired from [`AllPacksData::iter_with`] which takes two functions that produce the
230/// inner iterator of items from the game meta and the inner iterators of items from the asset
231/// packs, respectively.
232///
233/// See [`AllPacksData`] for more info.
234pub struct AllPacksDataIter<'a, T, CoreItemIt, Pack, PackIt, PackItemIt> {
235    core_item_iter: CoreItemIt,
236    pack_iter: PackIt,
237    pack_accessor: Box<dyn FnMut(&'a Pack) -> PackItemIt>,
238    current_pack: Pin<Box<RefCell<AllPacksDataCurrentPack<'a, Pack, T, PackItemIt>>>>,
239}
240
241struct AllPacksDataCurrentPack<'a, Pack, T, PackItemIt> {
242    pack: Option<DashmapRef<'a, Pack>>,
243    item_iter: Option<PackItemIt>,
244    _marker: std::marker::PhantomData<fn() -> T>,
245}
246
247impl<'a, Pack, T, PackItemIt> AllPacksDataCurrentPack<'a, Pack, T, PackItemIt> {
248    fn new() -> Pin<Box<RefCell<Self>>> {
249        Box::pin(RefCell::new(AllPacksDataCurrentPack {
250            pack: None,
251            item_iter: None,
252            _marker: std::marker::PhantomData,
253        }))
254    }
255
256    fn set_pack(
257        &mut self,
258        next_pack: DashmapRef<'a, Pack>,
259        pack_accessor: &mut dyn FnMut(&'a Pack) -> PackItemIt,
260    ) -> &mut PackItemIt {
261        // Drop the item iterator
262        _ = self.item_iter.take();
263
264        // Set the pack
265        let pack = self.pack.insert(next_pack);
266
267        // Setup the item iterator
268        let pack = unsafe { std::mem::transmute::<&Pack, &'a Pack>(pack) };
269        let pack_item_iter = (pack_accessor)(pack);
270        self.item_iter.insert(pack_item_iter)
271    }
272}
273
274impl<'a, Pack, T, CoreItemIt, PackIt, PackItemIt> Iterator
275    for AllPacksDataIter<'a, T, CoreItemIt, Pack, PackIt, PackItemIt>
276where
277    CoreItemIt: Iterator<Item = T>,
278    PackIt: Iterator<Item = DashmapRef<'a, Pack>>,
279    PackItemIt: Iterator<Item = T>,
280{
281    type Item = T;
282
283    fn next(&mut self) -> Option<Self::Item> {
284        let next_core_item = self.core_item_iter.next();
285        if next_core_item.is_some() {
286            return next_core_item;
287        }
288
289        if let Some(iter) = self.current_pack.borrow_mut().item_iter.as_mut() {
290            let next_pack_item = iter.next();
291            if next_pack_item.is_some() {
292                return next_pack_item;
293            }
294        }
295
296        let next_pack = self.pack_iter.next()?;
297
298        let mut current_pack = self.current_pack.borrow_mut();
299        let current_pack_item_iter = current_pack.set_pack(next_pack, &mut self.pack_accessor);
300
301        current_pack_item_iter.next()
302    }
303}