moonshine_save/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::{
4    marker::PhantomData,
5    path::{Path, PathBuf},
6};
7
8use bevy_ecs::{prelude::*, schedule::SystemConfigs};
9use moonshine_util::system::{has_resource, remove_resource};
10
11pub mod load;
12pub mod save;
13
14/// Common elements for saving/loading world state.
15pub mod prelude {
16    pub use crate::load::{
17        load, LoadError, LoadMapComponent, LoadPlugin, LoadSystem, Loaded, Unload,
18    };
19
20    pub use crate::save::{
21        save, save_all, save_all_with, save_default, save_default_with, save_with, Save, SaveError,
22        SaveInput, SavePlugin, SaveSystem, Saved,
23    };
24
25    pub use bevy_ecs::{
26        entity::{EntityMapper, MapEntities},
27        reflect::ReflectMapEntities,
28    };
29
30    pub use crate::{file_from_event, file_from_resource, static_file, GetFilePath};
31}
32
33pub trait GetFilePath {
34    fn path(&self) -> &Path;
35}
36
37pub trait GetStaticStream: 'static + Send + Sync {
38    type Stream: 'static + Send + Sync;
39
40    fn stream() -> Self::Stream;
41}
42
43pub trait GetStream: 'static + Send + Sync {
44    type Stream: 'static + Send + Sync;
45
46    fn stream(&self) -> Self::Stream;
47}
48
49pub trait Pipeline: 'static + Send + Sync {
50    fn finish(&self, pipeline: impl System<In = (), Out = ()>) -> SystemConfigs {
51        pipeline.into_configs()
52    }
53}
54
55pub fn static_file(path: impl Into<PathBuf>) -> StaticFile {
56    StaticFile(path.into())
57}
58
59pub fn static_stream<S>(stream: S) -> StaticStream<S> {
60    StaticStream(stream)
61}
62
63pub fn file_from_resource<R>() -> FileFromResource<R>
64where
65    R: Resource,
66{
67    FileFromResource(PhantomData::<R>)
68}
69
70pub fn stream_from_resource<R>() -> StreamFromResource<R>
71where
72    R: Resource,
73{
74    StreamFromResource(PhantomData::<R>)
75}
76
77pub fn file_from_event<E>() -> FileFromEvent<E>
78where
79    E: Event,
80{
81    FileFromEvent(PhantomData::<E>)
82}
83
84pub fn stream_from_event<E>() -> StreamFromEvent<E>
85where
86    E: Event,
87{
88    StreamFromEvent(PhantomData::<E>)
89}
90
91pub struct StaticFile(PathBuf);
92
93impl Pipeline for StaticFile {}
94
95#[derive(Clone)]
96pub struct StaticStream<S>(S);
97
98impl<S: 'static + Send + Sync> Pipeline for StaticStream<S> {}
99
100pub struct FileFromResource<R>(PhantomData<R>);
101
102impl<R: Resource> Pipeline for FileFromResource<R> {
103    fn finish(&self, pipeline: impl System<In = (), Out = ()>) -> SystemConfigs {
104        pipeline
105            .pipe(remove_resource::<R>)
106            .run_if(has_resource::<R>)
107    }
108}
109
110pub struct StreamFromResource<R>(PhantomData<R>);
111
112impl<R: Resource> Pipeline for StreamFromResource<R> {
113    fn finish(&self, pipeline: impl System<In = (), Out = ()>) -> SystemConfigs {
114        pipeline
115            .pipe(remove_resource::<R>)
116            .run_if(has_resource::<R>)
117    }
118}
119
120pub struct FileFromEvent<E>(PhantomData<E>);
121
122impl<E: Event> Pipeline for FileFromEvent<E> {}
123
124pub struct StreamFromEvent<E>(PhantomData<E>);
125
126impl<E: Event> Pipeline for StreamFromEvent<E> {}
127
128pub trait MapComponent<T: Component>: 'static + Clone + Send + Sync {
129    type Output: Component;
130
131    fn map_component(&self, component: &T) -> Self::Output;
132}
133
134impl<F: Fn(&T) -> U, T: Component, U: Component> MapComponent<T> for F
135where
136    F: 'static + Clone + Send + Sync,
137{
138    type Output = U;
139
140    fn map_component(&self, component: &T) -> Self::Output {
141        self(component)
142    }
143}
144
145#[derive(Default)]
146pub struct SceneMapper(Vec<ComponentMapperDyn>);
147
148impl SceneMapper {
149    pub fn map<T: Component>(mut self, m: impl MapComponent<T>) -> Self {
150        self.0.push(Box::new(ComponentMapperImpl::new(m)));
151        self
152    }
153
154    pub(crate) fn is_empty(&self) -> bool {
155        self.0.is_empty()
156    }
157
158    pub(crate) fn apply(&mut self, mut entity: EntityWorldMut) {
159        for mapper in &mut self.0 {
160            mapper.apply(&mut entity);
161        }
162    }
163
164    pub(crate) fn replace(&mut self, mut entity: EntityWorldMut) {
165        for mapper in &mut self.0 {
166            mapper.replace(&mut entity);
167        }
168    }
169
170    pub(crate) fn undo(&mut self, mut entity: EntityWorldMut) {
171        for mapper in &mut self.0 {
172            mapper.undo(&mut entity);
173        }
174    }
175}
176
177// TODO: Can we avoid this clone?
178impl Clone for SceneMapper {
179    fn clone(&self) -> Self {
180        Self(self.0.iter().map(|mapper| mapper.clone_dyn()).collect())
181    }
182}
183
184trait ComponentMapper: 'static + Send + Sync {
185    fn apply(&mut self, entity: &mut EntityWorldMut);
186
187    fn replace(&mut self, entity: &mut EntityWorldMut);
188
189    fn undo(&mut self, entity: &mut EntityWorldMut);
190
191    fn clone_dyn(&self) -> Box<dyn ComponentMapper>;
192}
193
194struct ComponentMapperImpl<T: Component, M: MapComponent<T>>(M, PhantomData<T>);
195
196impl<T: Component, M: MapComponent<T>> ComponentMapperImpl<T, M> {
197    fn new(m: M) -> Self {
198        Self(m, PhantomData)
199    }
200}
201
202impl<T: Component, M: MapComponent<T>> ComponentMapper for ComponentMapperImpl<T, M> {
203    fn apply(&mut self, entity: &mut EntityWorldMut) {
204        if let Some(component) = entity.get::<T>() {
205            entity.insert(self.0.map_component(component));
206        }
207    }
208
209    fn replace(&mut self, entity: &mut EntityWorldMut) {
210        if let Some(component) = entity.take::<T>() {
211            entity.insert(self.0.map_component(&component));
212        }
213    }
214
215    fn undo(&mut self, entity: &mut EntityWorldMut) {
216        entity.remove::<M::Output>();
217    }
218
219    fn clone_dyn(&self) -> Box<dyn ComponentMapper> {
220        Box::new(Self::new(self.0.clone()))
221    }
222}
223
224type ComponentMapperDyn = Box<dyn ComponentMapper>;