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
14pub 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
177impl 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>;