moonshine_save/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3
4use std::marker::PhantomData;
5
6use bevy_ecs::prelude::*;
7use moonshine_util::Static;
8
9/// Types, traits, and functions related to loading.
10pub mod load;
11
12/// Types, traits, and functions related to saving.
13pub mod save;
14
15/// Common elements for saving/loading world state.
16pub mod prelude {
17    pub use crate::load::{
18        load_on, load_on_default_event, LoadError, LoadEvent, LoadInput, LoadWorld, Loaded,
19        TriggerLoad, Unload,
20    };
21
22    pub use crate::save::{
23        save_on, save_on_default_event, Save, SaveError, SaveEvent, SaveOutput, SaveWorld, Saved,
24        TriggerSave,
25    };
26
27    pub use bevy_ecs::{
28        entity::{EntityMapper, MapEntities},
29        reflect::ReflectMapEntities,
30    };
31}
32
33/// A trait used for mapping components during a save operation.
34///
35/// # Usage
36///
37/// Component mapping is useful when you wish to serialize an unserializable component.
38///
39/// All component mappers are executed **BEFORE** the serialization step of the Save Pipeline.
40/// When invoked, the given component `T` will be replaced with the output of the mapper for all saved entities.
41/// When the save operation is complete, the original component will be restored.
42///
43/// Keep in mind that this will trigger [change detection](DetectChanges) for the mapped component.
44pub trait MapComponent<T: Component>: 'static + Clone + Send + Sync {
45    /// The mapped output type.
46    type Output: Component;
47
48    /// Called during the Save/Load process to map components.
49    fn map_component(&self, component: &T) -> Self::Output;
50}
51
52impl<F: Fn(&T) -> U, T: Component, U: Component> MapComponent<T> for F
53where
54    F: 'static + Clone + Send + Sync,
55{
56    type Output = U;
57
58    fn map_component(&self, component: &T) -> Self::Output {
59        self(component)
60    }
61}
62
63/// A collection of component mappers. See [`MapComponent`] for more information.
64#[derive(Default)]
65pub struct SceneMapper(Vec<ComponentMapperDyn>);
66
67impl SceneMapper {
68    /// Adds a component mapper to the scene mapper.
69    pub fn map<T: Component>(mut self, m: impl MapComponent<T>) -> Self {
70        self.0.push(Box::new(ComponentMapperImpl::new(m)));
71        self
72    }
73
74    pub(crate) fn apply(&mut self, mut entity: EntityWorldMut) {
75        for mapper in &mut self.0 {
76            mapper.apply(&mut entity);
77        }
78    }
79
80    pub(crate) fn replace(&mut self, mut entity: EntityWorldMut) {
81        for mapper in &mut self.0 {
82            mapper.replace(&mut entity);
83        }
84    }
85
86    pub(crate) fn undo(&mut self, mut entity: EntityWorldMut) {
87        for mapper in &mut self.0 {
88            mapper.undo(&mut entity);
89        }
90    }
91}
92
93trait ComponentMapper: Static {
94    fn apply(&mut self, entity: &mut EntityWorldMut);
95
96    fn replace(&mut self, entity: &mut EntityWorldMut);
97
98    fn undo(&mut self, entity: &mut EntityWorldMut);
99}
100
101struct ComponentMapperImpl<T: Component, M: MapComponent<T>>(M, PhantomData<T>);
102
103impl<T: Component, M: MapComponent<T>> ComponentMapperImpl<T, M> {
104    fn new(m: M) -> Self {
105        Self(m, PhantomData)
106    }
107}
108
109impl<T: Component, M: MapComponent<T>> ComponentMapper for ComponentMapperImpl<T, M> {
110    fn apply(&mut self, entity: &mut EntityWorldMut) {
111        if let Some(component) = entity.get::<T>() {
112            entity.insert(self.0.map_component(component));
113        }
114    }
115
116    fn replace(&mut self, entity: &mut EntityWorldMut) {
117        if let Some(component) = entity.take::<T>() {
118            entity.insert(self.0.map_component(&component));
119        }
120    }
121
122    fn undo(&mut self, entity: &mut EntityWorldMut) {
123        entity.remove::<M::Output>();
124    }
125}
126
127type ComponentMapperDyn = Box<dyn ComponentMapper>;