Skip to main content

moonshine_save/
load.rs

1use std::io::{self, Read};
2use std::marker::PhantomData;
3use std::path::PathBuf;
4
5use bevy_asset::AssetServer;
6use bevy_world_serialization::DynamicWorld;
7use moonshine_util::expect::{expect_deferred, ExpectDeferredWorld};
8use moonshine_util::Static;
9use serde::de::DeserializeSeed;
10
11use bevy_ecs::entity::EntityHashMap;
12use bevy_ecs::prelude::*;
13use bevy_ecs::query::QueryFilter;
14use bevy_log::prelude::*;
15use bevy_world_serialization::{serde::WorldDeserializer, WorldInstanceSpawnError};
16
17use moonshine_util::event::{OnSingle, SingleEvent, TriggerSingle};
18use thiserror::Error;
19
20use crate::save::Save;
21use crate::{MapComponent, SceneMapper};
22
23/// A [`Component`] which marks its [`Entity`] to be despawned prior to load.
24///
25/// # Usage
26/// When saving game state, it is often undesirable to save visual and aesthetic elements of the game.
27/// Elements such as transforms, camera settings, scene hierarchy, or UI elements are typically either
28/// spawned at game start, or added during initialization of the game data they represent.
29///
30/// This component may be used on such entities to despawn them prior to loading.
31///
32/// # Example
33/// ```
34/// use bevy::prelude::*;
35/// use moonshine_save::prelude::*;
36///
37/// #[derive(Bundle)]
38/// struct PlayerBundle {
39///     player: Player,
40///     /* Saved Player Data */
41///     save: Save,
42/// }
43///
44/// #[derive(Component, Default, Reflect)]
45/// #[reflect(Component)]
46/// struct Player;
47///
48/// #[derive(Component)] // <-- Not serialized!
49/// struct PlayerSprite(Entity);
50///
51/// #[derive(Bundle, Default)]
52/// struct PlayerSpriteBundle {
53///     /* Player Visuals/Aesthetics */
54///     unload: Unload,
55/// }
56///
57/// fn spawn_player_sprite(query: Query<Entity, Added<Player>>, mut commands: Commands) {
58///     for entity in &query {
59///         let sprite = PlayerSprite(commands.spawn(PlayerSpriteBundle::default()).id());
60///         commands.entity(entity).insert(sprite);
61///     }
62/// }
63/// ```
64#[derive(Component, Default, Clone)]
65pub struct Unload;
66
67/// A trait used to trigger a [`LoadEvent`] via [`Commands`] or [`World`].
68pub trait TriggerLoad {
69    /// Triggers the given [`LoadEvent`].
70    #[doc(alias = "trigger_single")]
71    fn trigger_load(self, event: impl LoadEvent);
72}
73
74impl TriggerLoad for &mut Commands<'_, '_> {
75    fn trigger_load(self, event: impl LoadEvent) {
76        self.trigger_single(event);
77    }
78}
79
80impl TriggerLoad for &mut World {
81    fn trigger_load(self, event: impl LoadEvent) {
82        self.trigger_single(event);
83    }
84}
85
86/// A [`QueryFilter`] which determines which entities should be unloaded before the load process begins.
87pub type DefaultUnloadFilter = Or<(With<Save>, With<Unload>)>;
88
89/// A [`SingleEvent`] which starts the load process with the given parameters.
90///
91/// See also:
92/// - [`trigger_load`](TriggerLoad::trigger_load)
93/// - [`trigger_single`](TriggerSingle::trigger_single)
94/// - [`LoadWorld`]
95pub trait LoadEvent: SingleEvent {
96    /// A [`QueryFilter`] used as the initial filter for selecting entities to unload.
97    type UnloadFilter: QueryFilter;
98
99    /// Returns the [`LoadInput`] of the load process.
100    fn input(&mut self) -> LoadInput;
101
102    /// Called once before the load process starts.
103    ///
104    /// This is useful if you want to modify the world just before loading.
105    fn before_load(&mut self, _world: &mut World) {}
106
107    /// Called once before unloading entities.
108    ///
109    /// All given entities will be despawned after this call.
110    /// This is useful if you want to update the world state as a result of unloading these entities.
111    fn before_unload(&mut self, _world: &mut World, _entities: &[Entity]) {}
112
113    /// Called for all entities after they have been loaded.
114    ///
115    /// This is useful to undo any modifications done before loading.
116    /// You also have access to [`Loaded`] here for any additional post-processing before [`OnLoad`] is triggered.
117    fn after_load(&mut self, _world: &mut World, _result: &LoadResult) {}
118}
119
120/// A generic [`LoadEvent`] which loads the world from a file or stream.
121pub struct LoadWorld<U: QueryFilter = DefaultUnloadFilter> {
122    /// The input data used to load the world.
123    pub input: LoadInput,
124    /// A [`SceneMapper`] used to map components after the load process.
125    pub mapper: SceneMapper,
126    #[doc(hidden)]
127    pub unload: PhantomData<U>,
128}
129
130impl<U: QueryFilter> LoadWorld<U> {
131    /// Creates a new [`LoadWorld`] with the given input and mapper.
132    pub fn new(input: LoadInput, mapper: SceneMapper) -> Self {
133        LoadWorld {
134            input,
135            mapper,
136            unload: PhantomData,
137        }
138    }
139
140    /// Creates a new [`LoadWorld`] which unloads entities matching the given
141    /// [`QueryFilter`] before the file at given path.
142    pub fn from_file(path: impl Into<PathBuf>) -> Self {
143        LoadWorld {
144            input: LoadInput::file(path),
145            mapper: SceneMapper::default(),
146            unload: PhantomData,
147        }
148    }
149
150    /// Creates a new [`LoadWorld`] which unloads entities matching the given
151    /// [`QueryFilter`] before loading from the given [`Read`] stream.
152    pub fn from_stream(stream: impl LoadStream) -> Self {
153        LoadWorld {
154            input: LoadInput::stream(stream),
155            mapper: SceneMapper::default(),
156            unload: PhantomData,
157        }
158    }
159
160    /// Maps the given [`Component`] into another using a [component mapper](MapComponent) after loading.
161    pub fn map_component<T: Component>(self, m: impl MapComponent<T>) -> Self {
162        LoadWorld {
163            mapper: self.mapper.map(m),
164            ..self
165        }
166    }
167}
168
169impl LoadWorld {
170    /// Creates a new [`LoadWorld`] event which unloads default entities (with [`Unload`] or [`Save`])
171    /// before loading the file at the given path.
172    pub fn default_from_file(path: impl Into<PathBuf>) -> Self {
173        Self::from_file(path)
174    }
175
176    /// Creates a new [`LoadWorld`] event which unloads default entities (with [`Unload`] or [`Save`])
177    /// before loading from the given [`Read`] stream.
178    pub fn default_from_stream(stream: impl LoadStream) -> Self {
179        Self::from_stream(stream)
180    }
181}
182
183impl<U: QueryFilter> SingleEvent for LoadWorld<U> where U: Static {}
184
185impl<U: QueryFilter> LoadEvent for LoadWorld<U>
186where
187    U: Static,
188{
189    type UnloadFilter = U;
190
191    fn input(&mut self) -> LoadInput {
192        self.input.consume().unwrap()
193    }
194
195    fn before_load(&mut self, world: &mut World) {
196        world.insert_resource(ExpectDeferredWorld);
197    }
198
199    fn after_load(&mut self, world: &mut World, result: &LoadResult) {
200        if let Ok(loaded) = result {
201            for entity in loaded.entities() {
202                let Ok(entity) = world.get_entity_mut(entity) else {
203                    // Some entities may be invalid during load. See `unsaved.rs` test.
204                    continue;
205                };
206                self.mapper.replace(entity);
207            }
208        }
209
210        expect_deferred(world);
211    }
212}
213
214/// Input of the load process.
215pub enum LoadInput {
216    /// Load from a file at the given path.
217    File(PathBuf),
218    /// Load from a [`Read`] stream.
219    Stream(Box<dyn LoadStream>),
220    /// Load from a [`DynamicWorld`].
221    ///
222    /// This is useful if you would like to deserialize the scene manually from any data source.
223    World(DynamicWorld),
224    #[deprecated(note = "use `LoadInput::World` instead")]
225    /// Deprecated — use [`LoadInput::World`] instead.
226    Scene(DynamicWorld),
227    #[doc(hidden)]
228    Invalid,
229}
230
231impl LoadInput {
232    /// Creates a new [`LoadInput`] which loads from a file at the given path.
233    pub fn file(path: impl Into<PathBuf>) -> Self {
234        Self::File(path.into())
235    }
236
237    /// Creates a new [`LoadInput`] which loads from a [`Read`] stream.
238    pub fn stream<S: LoadStream + 'static>(stream: S) -> Self {
239        Self::Stream(Box::new(stream))
240    }
241
242    /// Invalidates this [`LoadInput`] and returns it if it was valid.
243    pub fn consume(&mut self) -> Option<LoadInput> {
244        let input = std::mem::replace(self, LoadInput::Invalid);
245        if let LoadInput::Invalid = input {
246            return None;
247        }
248        Some(input)
249    }
250}
251
252/// Alias for a `'static` [`Read`] stream.
253pub trait LoadStream: Read
254where
255    Self: Static,
256{
257}
258
259impl<S: Read> LoadStream for S where S: Static {}
260
261/// An [`Event`] triggered at the end of a successful load process.
262///
263/// This event contains the loaded entity map.
264#[derive(Event)]
265pub struct Loaded {
266    /// The map of all loaded entities and their new entity IDs.
267    pub entity_map: EntityHashMap<Entity>,
268}
269
270impl Loaded {
271    /// Iterates over all loaded entities.
272    ///
273    /// Note that not all of these entities may be valid. This would indicate an error with save data.
274    /// See `unsaved.rs` test for an example of how this may happen.
275    pub fn entities(&self) -> impl Iterator<Item = Entity> + '_ {
276        self.entity_map.values().copied()
277    }
278}
279
280#[doc(hidden)]
281#[deprecated(since = "0.5.2", note = "use `Loaded` instead")]
282pub type OnLoad = Loaded;
283
284/// An error which indicates a failure during the load process.
285#[derive(Error, Debug)]
286pub enum LoadError {
287    /// Indicates a failure to access the saved data.
288    #[error("Failed to read world: {0}")]
289    Io(io::Error),
290    /// Indicates a deserialization error.
291    #[error("Failed to deserialize world: {0}")]
292    Ron(ron::Error),
293    /// Indicates a failure to reconstruct the world from the loaded data.
294    #[error("Failed to spawn scene: {0}")]
295    Scene(WorldInstanceSpawnError),
296}
297
298impl From<io::Error> for LoadError {
299    fn from(e: io::Error) -> Self {
300        Self::Io(e)
301    }
302}
303
304impl From<ron::de::SpannedError> for LoadError {
305    fn from(e: ron::de::SpannedError) -> Self {
306        Self::Ron(e.into())
307    }
308}
309
310impl From<ron::Error> for LoadError {
311    fn from(e: ron::Error) -> Self {
312        Self::Ron(e)
313    }
314}
315
316impl From<WorldInstanceSpawnError> for LoadError {
317    fn from(e: WorldInstanceSpawnError) -> Self {
318        Self::Scene(e)
319    }
320}
321
322/// [`Result`] of a [`LoadEvent`].
323pub type LoadResult = Result<Loaded, LoadError>;
324
325/// An [`Observer`] which loads the world when a [`LoadWorld`] event is triggered.
326pub fn load_on_default_event(event: OnSingle<LoadWorld>, commands: Commands) {
327    load_on(event, commands);
328}
329
330/// An [`Observer`] which loads the world when the given [`LoadEvent`] is triggered.
331pub fn load_on<E: LoadEvent>(event: OnSingle<E>, mut commands: Commands) {
332    commands.queue_handled(LoadCommand(event.consume().unwrap()), |err, ctx| {
333        error!("load failed: {err:?} ({ctx})");
334    });
335}
336
337fn load_world<E: LoadEvent>(mut event: E, world: &mut World) -> LoadResult {
338    // Notify
339    event.before_load(world);
340
341    let mut asset_server = world.resource::<AssetServer>().clone();
342
343    // Deserialize
344    let loaded_world = match event.input() {
345        LoadInput::File(path) => {
346            let bytes = std::fs::read(&path)?;
347            let mut deserializer = ron::Deserializer::from_bytes(&bytes)?;
348            let type_registry = &world.resource::<AppTypeRegistry>().read();
349            let world_deserializer = WorldDeserializer {
350                type_registry,
351                load_from_path: &mut asset_server,
352            };
353            world_deserializer.deserialize(&mut deserializer)?
354        }
355        LoadInput::Stream(mut data) => {
356            let mut bytes = Vec::new();
357            data.read_to_end(&mut bytes)?;
358            let mut deserializer = ron::Deserializer::from_bytes(&bytes)?;
359            let type_registry = &world.resource::<AppTypeRegistry>().read();
360            let world_deserializer = WorldDeserializer {
361                type_registry,
362                load_from_path: &mut asset_server,
363            };
364            world_deserializer.deserialize(&mut deserializer)?
365        }
366        LoadInput::World(input_world) => input_world,
367        #[allow(deprecated)] // TODO: Remove
368        LoadInput::Scene(scene) => scene,
369        LoadInput::Invalid => {
370            panic!("LoadInput is invalid");
371        }
372    };
373
374    // Unload
375    let entities: Vec<_> = world
376        .query_filtered::<Entity, E::UnloadFilter>()
377        .iter(world)
378        .collect();
379    event.before_unload(world, &entities);
380    for entity in entities {
381        if let Ok(entity) = world.get_entity_mut(entity) {
382            entity.despawn();
383        }
384    }
385
386    // Load
387    let mut entity_map = EntityHashMap::default();
388    loaded_world.write_to_world(world, &mut entity_map)?;
389    debug!("loaded {} entities", entity_map.len());
390
391    let result = Ok(Loaded { entity_map });
392    event.after_load(world, &result);
393    result
394}
395
396// TODO: Documentation
397#[doc(hidden)]
398pub struct LoadCommand<E>(pub E);
399
400impl<E: LoadEvent> Command for LoadCommand<E> {
401    type Out = Result<(), LoadError>;
402
403    fn apply(self, world: &mut World) -> Result<(), LoadError> {
404        let loaded = load_world(self.0, world)?;
405        world.trigger(loaded);
406        Ok(())
407    }
408}
409
410#[cfg(test)]
411mod tests {
412    use std::fs::*;
413
414    use bevy::prelude::*;
415    use bevy_ecs::system::RunSystemOnce;
416
417    use super::*;
418
419    pub const DATA: &str = "(
420        resources: {},
421        entities: {
422            4294967293: (
423                components: {
424                    \"moonshine_save::load::tests::Foo\": (),
425                },
426            ),
427        },
428    )";
429
430    #[derive(Component, Default, Reflect)]
431    #[reflect(Component)]
432    #[require(Save)]
433    struct Foo;
434
435    fn app() -> App {
436        let mut app = App::new();
437        app.add_plugins(MinimalPlugins)
438            .add_plugins(AssetPlugin::default())
439            .register_type::<Foo>();
440        app
441    }
442
443    #[test]
444    fn test_load_file() {
445        #[derive(Resource)]
446        struct EventTriggered;
447
448        pub const PATH: &str = "test_load_file.ron";
449
450        write(PATH, DATA).unwrap();
451
452        let mut app = app();
453        app.add_observer(load_on_default_event);
454
455        app.add_observer(|_: On<Loaded>, mut commands: Commands| {
456            commands.insert_resource(EventTriggered);
457        });
458
459        let _ = app.world_mut().run_system_once(|mut commands: Commands| {
460            commands.trigger_load(LoadWorld::default_from_file(PATH));
461        });
462
463        let world = app.world_mut();
464        assert!(world.contains_resource::<EventTriggered>());
465        assert!(world
466            .query_filtered::<(), With<Foo>>()
467            .single(world)
468            .is_ok());
469
470        remove_file(PATH).unwrap();
471    }
472
473    #[test]
474    fn test_load_stream() {
475        pub const PATH: &str = "test_load_stream.ron";
476
477        write(PATH, DATA).unwrap();
478
479        let mut app = app();
480        app.add_observer(load_on_default_event);
481
482        let _ = app.world_mut().run_system_once(|mut commands: Commands| {
483            commands.spawn((Foo, Save));
484            commands.trigger_load(LoadWorld::default_from_stream(File::open(PATH).unwrap()));
485        });
486
487        let data = read_to_string(PATH).unwrap();
488        assert!(data.contains("Foo"));
489
490        remove_file(PATH).unwrap();
491    }
492
493    #[test]
494    fn test_load_map_component() {
495        pub const PATH: &str = "test_load_map_component.ron";
496
497        write(PATH, DATA).unwrap();
498
499        #[derive(Component)]
500        struct Bar; // Not serializable
501
502        let mut app = app();
503        app.add_observer(load_on_default_event);
504
505        let _ = app.world_mut().run_system_once(|mut commands: Commands| {
506            commands.trigger_load(LoadWorld::default_from_file(PATH).map_component(|_: &Foo| Bar));
507        });
508
509        let world = app.world_mut();
510        assert!(world
511            .query_filtered::<(), With<Bar>>()
512            .single(world)
513            .is_ok());
514        assert!(world.query_filtered::<(), With<Foo>>().iter(world).count() == 0);
515
516        remove_file(PATH).unwrap();
517    }
518}