1use std::io::{self, Read};
36use std::path::PathBuf;
37
38use bevy_app::{App, Plugin, PreUpdate};
39use bevy_ecs::entity::EntityHashMap;
40use bevy_ecs::{prelude::*, query::QueryFilter, schedule::SystemConfigs};
41use bevy_hierarchy::DespawnRecursiveExt;
42use bevy_scene::{serde::SceneDeserializer, SceneSpawnError};
43use bevy_utils::tracing::{error, info, warn};
44use moonshine_util::system::*;
45use serde::de::DeserializeSeed;
46
47use crate::{
48 file_from_event, file_from_resource,
49 save::{Save, SaveSystem, Saved},
50 static_file, FileFromEvent, FileFromResource, GetFilePath, MapComponent, Pipeline, SceneMapper,
51 StaticFile,
52};
53use crate::{GetStaticStream, GetStream, StaticStream, StreamFromEvent, StreamFromResource};
54
55pub struct LoadPlugin;
57
58impl Plugin for LoadPlugin {
59 fn build(&self, app: &mut App) {
60 app.configure_sets(
61 PreUpdate,
62 (
63 LoadSystem::Load,
64 LoadSystem::PostLoad.run_if(has_resource::<Loaded>),
65 )
66 .chain()
67 .before(SaveSystem::Save),
68 )
69 .add_systems(
70 PreUpdate,
71 remove_resource::<Loaded>.in_set(LoadSystem::PostLoad),
72 );
73 }
74}
75
76#[derive(Clone, Debug, Hash, PartialEq, Eq, SystemSet)]
78pub enum LoadSystem {
79 Load,
81 PostLoad,
83}
84
85#[derive(Component, Default, Clone)]
127pub struct Unload;
128
129#[derive(Resource)]
131pub struct Loaded {
132 pub entity_map: EntityHashMap<Entity>,
133}
134
135#[derive(Debug)]
136pub enum LoadError {
137 Io(io::Error),
138 De(ron::de::SpannedError),
139 Ron(ron::Error),
140 Scene(SceneSpawnError),
141}
142
143impl From<io::Error> for LoadError {
144 fn from(e: io::Error) -> Self {
145 Self::Io(e)
146 }
147}
148
149impl From<ron::de::SpannedError> for LoadError {
150 fn from(e: ron::de::SpannedError) -> Self {
151 Self::De(e)
152 }
153}
154
155impl From<ron::Error> for LoadError {
156 fn from(e: ron::Error) -> Self {
157 Self::Ron(e)
158 }
159}
160
161impl From<SceneSpawnError> for LoadError {
162 fn from(e: SceneSpawnError) -> Self {
163 Self::Scene(e)
164 }
165}
166
167#[deprecated]
190pub fn load_from_file(path: impl Into<PathBuf>) -> SystemConfigs {
191 load(static_file(path))
192}
193
194#[deprecated]
195pub fn load_from_file_with_mapper(path: impl Into<PathBuf>, mapper: SceneMapper) -> SystemConfigs {
196 load(LoadPipelineBuilder {
197 pipeline: static_file(path),
198 mapper,
199 })
200}
201
202#[deprecated]
226pub fn load_from_file_on_request<R>() -> SystemConfigs
227where
228 R: GetFilePath + Resource,
229{
230 load(file_from_resource::<R>())
231}
232
233#[deprecated]
234pub fn load_from_file_on_request_with_mapper<R>(mapper: SceneMapper) -> SystemConfigs
235where
236 R: GetFilePath + Resource,
237{
238 load(LoadPipelineBuilder {
239 pipeline: file_from_resource::<R>(),
240 mapper,
241 })
242}
243
244#[deprecated]
248pub fn load_from_file_on_event<R>() -> SystemConfigs
249where
250 R: GetFilePath + Event,
251{
252 load(file_from_event::<R>())
253}
254
255#[deprecated]
257pub fn load_from_file_on_event_with_mapper<R>(mapper: SceneMapper) -> SystemConfigs
258where
259 R: GetFilePath + Event,
260{
261 load(LoadPipelineBuilder::<FileFromEvent<R>> {
262 pipeline: file_from_event::<R>(),
263 mapper,
264 })
265}
266
267pub trait LoadPipeline: Pipeline {
268 fn load(&self) -> impl System<In = (), Out = Result<Saved, LoadError>>;
269}
270
271impl LoadPipeline for StaticFile {
272 fn load(&self) -> impl System<In = (), Out = Result<Saved, LoadError>> {
273 IntoSystem::into_system(read_static_file(self.0.clone(), Default::default()))
274 }
275}
276
277impl<S: GetStaticStream> LoadPipeline for StaticStream<S>
278where
279 S::Stream: Read,
280{
281 fn load(&self) -> impl System<In = (), Out = Result<Saved, LoadError>> {
282 IntoSystem::into_system((|| S::stream()).pipe(read_stream))
283 }
284}
285
286impl<R> LoadPipeline for FileFromResource<R>
287where
288 R: Resource + GetFilePath,
289{
290 fn load(&self) -> impl System<In = (), Out = Result<Saved, LoadError>> {
291 IntoSystem::into_system(get_file_from_resource::<R>.pipe(read_file))
292 }
293}
294
295impl<R: GetStream + Resource> LoadPipeline for StreamFromResource<R>
296where
297 R::Stream: Read,
298{
299 fn load(&self) -> impl System<In = (), Out = Result<Saved, LoadError>> {
300 IntoSystem::into_system((|resource: Res<R>| resource.stream()).pipe(read_stream))
301 }
302}
303
304impl<E> LoadPipeline for FileFromEvent<E>
305where
306 E: Event + GetFilePath,
307{
308 fn load(&self) -> impl System<In = (), Out = Result<Saved, LoadError>> {
309 IntoSystem::into_system(get_file_from_event::<E>.pipe(read_file))
310 }
311}
312
313impl<E: GetStream + Event> LoadPipeline for StreamFromEvent<E>
314where
315 E::Stream: Read,
316{
317 fn load(&self) -> impl System<In = (), Out = Result<Saved, LoadError>> {
318 IntoSystem::into_system(get_stream_from_event::<E>.pipe(read_stream))
319 }
320}
321
322pub fn load(p: impl LoadPipeline) -> SystemConfigs {
323 let system = p
324 .load()
325 .pipe(unload::<DefaultUnloadFilter>)
326 .pipe(write_to_world)
327 .pipe(insert_into_loaded(Save))
328 .pipe(insert_loaded);
329 p.finish(IntoSystem::into_system(system))
330 .in_set(LoadSystem::Load)
331}
332
333pub trait LoadMapComponent: Sized {
334 fn map_component<U: Component>(self, m: impl MapComponent<U>) -> LoadPipelineBuilder<Self>;
335}
336
337impl<P: Pipeline> LoadMapComponent for P {
338 fn map_component<U: Component>(self, m: impl MapComponent<U>) -> LoadPipelineBuilder<Self> {
339 LoadPipelineBuilder {
340 pipeline: self,
341 mapper: SceneMapper::default().map(m),
342 }
343 }
344}
345
346pub struct LoadPipelineBuilder<P> {
347 pipeline: P,
348 mapper: SceneMapper,
349}
350
351impl<P> LoadPipelineBuilder<P> {
352 pub fn map_component<U: Component>(self, m: impl MapComponent<U>) -> Self {
353 Self {
354 mapper: self.mapper.map(m),
355 ..self
356 }
357 }
358}
359
360impl<P: Pipeline> Pipeline for LoadPipelineBuilder<P> {
361 fn finish(&self, pipeline: impl System<In = (), Out = ()>) -> SystemConfigs {
362 self.pipeline.finish(pipeline)
363 }
364}
365
366impl<P: LoadPipeline> LoadPipeline for LoadPipelineBuilder<P> {
367 fn load(&self) -> impl System<In = (), Out = Result<Saved, LoadError>> {
368 let mapper = self.mapper.clone();
369 IntoSystem::into_system(self.pipeline.load().pipe(
370 move |In(saved): In<Result<Saved, LoadError>>| {
371 saved.map(|saved| Saved {
372 mapper: mapper.clone(),
373 ..saved
374 })
375 },
376 ))
377 }
378}
379
380pub fn read_static_file(
382 path: impl Into<PathBuf>,
383 mapper: SceneMapper,
384) -> impl Fn(Res<AppTypeRegistry>) -> Result<Saved, LoadError> {
385 let path = path.into();
386 move |type_registry| {
387 let input = std::fs::read(&path)?;
388 let mut deserializer = ron::Deserializer::from_bytes(&input)?;
389 let scene = {
390 let type_registry = &type_registry.read();
391 let scene_deserializer = SceneDeserializer { type_registry };
392 scene_deserializer.deserialize(&mut deserializer)?
393 };
394 info!("loaded from file: {path:?}");
395 Ok(Saved {
396 scene,
397 mapper: mapper.clone(),
398 })
399 }
400}
401
402pub fn read_file(
404 In(path): In<PathBuf>,
405 type_registry: Res<AppTypeRegistry>,
406) -> Result<Saved, LoadError> {
407 let input = std::fs::read(&path)?;
408 let mut deserializer = ron::Deserializer::from_bytes(&input)?;
409 let scene = {
410 let type_registry = &type_registry.read();
411 let scene_deserializer = SceneDeserializer { type_registry };
412 scene_deserializer.deserialize(&mut deserializer)?
413 };
414 info!("loaded from file: {path:?}");
415 Ok(Saved {
416 scene,
417 mapper: Default::default(),
418 })
419}
420
421pub fn read_stream<S: Read>(
422 In(mut stream): In<S>,
423 type_registry: Res<AppTypeRegistry>,
424) -> Result<Saved, LoadError> {
425 let mut input = Vec::new();
426 stream.read_to_end(&mut input)?;
427 let mut deserializer = ron::Deserializer::from_bytes(&input)?;
428 let scene = {
429 let type_registry = &type_registry.read();
430 let scene_deserializer = SceneDeserializer { type_registry };
431 scene_deserializer.deserialize(&mut deserializer)?
432 };
433 info!("loaded from stream");
434 Ok(Saved {
435 scene,
436 mapper: Default::default(),
437 })
438}
439
440pub type DefaultUnloadFilter = Or<(With<Save>, With<Unload>)>;
441
442pub fn unload<Filter: QueryFilter>(
444 In(result): In<Result<Saved, LoadError>>,
445 world: &mut World,
446) -> Result<Saved, LoadError> {
447 let saved = result?;
448 let entities: Vec<Entity> = world
449 .query_filtered::<Entity, Filter>()
450 .iter(world)
451 .collect();
452 for entity in entities {
453 if let Ok(entity) = world.get_entity_mut(entity) {
454 entity.despawn_recursive();
455 }
456 }
457 Ok(saved)
458}
459
460pub fn write_to_world(
462 In(result): In<Result<Saved, LoadError>>,
463 world: &mut World,
464) -> Result<Loaded, LoadError> {
465 let Saved { scene, mut mapper } = result?;
466 let mut entity_map = EntityHashMap::default();
467 scene.write_to_world(world, &mut entity_map)?;
468 if !mapper.is_empty() {
469 for entity in entity_map.values() {
470 if let Ok(entity) = world.get_entity_mut(*entity) {
471 mapper.replace(entity);
472 }
473 }
474 }
475 Ok(Loaded { entity_map })
476}
477
478pub fn insert_into_loaded(
480 bundle: impl Bundle + Clone,
481) -> impl Fn(In<Result<Loaded, LoadError>>, &mut World) -> Result<Loaded, LoadError> {
482 move |In(result), world| {
483 if let Ok(loaded) = &result {
484 for (saved_entity, entity) in loaded.entity_map.iter() {
485 if let Ok(mut entity) = world.get_entity_mut(*entity) {
486 entity.insert(bundle.clone());
487 } else {
488 error!(
489 "entity {saved_entity} is referenced in saved data but was never saved (raw bits = {})",
490 saved_entity.to_bits()
491 );
492 }
493 }
494 }
495 result
496 }
497}
498
499pub fn insert_loaded(In(result): In<Result<Loaded, LoadError>>, world: &mut World) {
505 match result {
506 Ok(loaded) => world.insert_resource(loaded),
507 Err(why) => error!("load failed: {why:?}"),
508 }
509}
510
511pub fn get_file_from_resource<R>(request: Res<R>) -> PathBuf
513where
514 R: GetFilePath + Resource,
515{
516 request.path().to_owned()
517}
518
519pub fn get_file_from_event<E>(mut events: EventReader<E>) -> PathBuf
527where
528 E: GetFilePath + Event,
529{
530 let mut iter = events.read();
531 let event = iter.next().unwrap();
532 if iter.next().is_some() {
533 warn!("multiple load request events received; only the first one is processed.");
534 }
535 event.path().to_owned()
536}
537
538pub fn get_stream_from_event<E>(mut events: EventReader<E>) -> E::Stream
539where
540 E: GetStream + Event,
541{
542 let mut iter = events.read();
543 let event = iter.next().unwrap();
544 if iter.next().is_some() {
545 warn!("multiple load request events received; only the first one is processed.");
546 }
547 event.stream()
548}
549
550#[cfg(test)]
551mod tests {
552 use std::{fs::*, path::Path};
553
554 use bevy::prelude::*;
555
556 use super::*;
557 use crate::*;
558
559 pub const DATA: &str = "(
560 resources: {},
561 entities: {
562 4294967296: (
563 components: {
564 \"moonshine_save::load::tests::Dummy\": (),
565 },
566 ),
567 },
568 )";
569
570 #[derive(Component, Default, Reflect)]
571 #[reflect(Component)]
572 struct Dummy;
573
574 fn app() -> App {
575 let mut app = App::new();
576 app.add_plugins((MinimalPlugins, LoadPlugin))
577 .register_type::<Dummy>();
578 app
579 }
580
581 #[test]
582 fn test_load_file() {
583 pub const PATH: &str = "test_load_file.ron";
584
585 write(PATH, DATA).unwrap();
586
587 let mut app = app();
588 app.add_systems(PreUpdate, load(static_file(PATH)));
589
590 app.update();
591
592 let world = app.world_mut();
593 assert!(!world.contains_resource::<Loaded>());
594 assert!(world
595 .query_filtered::<(), With<Dummy>>()
596 .get_single(world)
597 .is_ok());
598
599 remove_file(PATH).unwrap();
600 }
601
602 #[test]
603 fn test_load_stream() {
604 pub const PATH: &str = "test_load_stream.ron";
605
606 struct LoadStream;
607
608 impl GetStaticStream for LoadStream {
609 type Stream = File;
610
611 fn stream() -> Self::Stream {
612 File::open(PATH).unwrap()
613 }
614 }
615
616 write(PATH, DATA).unwrap();
617
618 let mut app = app();
619 app.add_systems(PreUpdate, load(static_stream(LoadStream)));
620
621 app.update();
622
623 let world = app.world_mut();
624 assert!(!world.contains_resource::<Loaded>());
625 assert!(world
626 .query_filtered::<(), With<Dummy>>()
627 .get_single(world)
628 .is_ok());
629
630 remove_file(PATH).unwrap();
631 }
632
633 #[test]
634 fn test_load_file_from_resource() {
635 pub const PATH: &str = "test_load_file_from_resource.ron";
636
637 write(PATH, DATA).unwrap();
638
639 #[derive(Resource)]
640 struct LoadRequest;
641
642 impl GetFilePath for LoadRequest {
643 fn path(&self) -> &Path {
644 Path::new(PATH)
645 }
646 }
647
648 let mut app = app();
649 app.add_systems(PreUpdate, load(file_from_resource::<LoadRequest>()));
650
651 app.world_mut().insert_resource(LoadRequest);
652 app.update();
653
654 let world = app.world_mut();
655 assert!(world
656 .query_filtered::<(), With<Dummy>>()
657 .get_single(world)
658 .is_ok());
659
660 remove_file(PATH).unwrap();
661 }
662
663 #[test]
664 fn test_load_stream_from_resource() {
665 pub const PATH: &str = "test_load_stream_from_resource.ron";
666
667 write(PATH, DATA).unwrap();
668
669 #[derive(Resource)]
670 struct LoadRequest(&'static str);
671
672 impl GetStream for LoadRequest {
673 type Stream = File;
674
675 fn stream(&self) -> Self::Stream {
676 File::open(self.0).unwrap()
677 }
678 }
679
680 let mut app = app();
681 app.add_systems(PreUpdate, load(stream_from_resource::<LoadRequest>()));
682
683 app.world_mut().insert_resource(LoadRequest(PATH));
684 app.update();
685
686 let world = app.world_mut();
687 assert!(world
688 .query_filtered::<(), With<Dummy>>()
689 .get_single(world)
690 .is_ok());
691
692 remove_file(PATH).unwrap();
693 }
694
695 #[test]
696 fn test_load_file_from_event() {
697 pub const PATH: &str = "test_load_file_from_event.ron";
698
699 write(PATH, DATA).unwrap();
700
701 #[derive(Event)]
702 struct LoadRequest;
703
704 impl GetFilePath for LoadRequest {
705 fn path(&self) -> &Path {
706 Path::new(PATH)
707 }
708 }
709
710 let mut app = app();
711 app.add_event::<LoadRequest>()
712 .add_systems(PreUpdate, load(file_from_event::<LoadRequest>()));
713
714 app.world_mut().send_event(LoadRequest);
715 app.update();
716
717 let world = app.world_mut();
718 assert!(world
719 .query_filtered::<(), With<Dummy>>()
720 .get_single(world)
721 .is_ok());
722
723 remove_file(PATH).unwrap();
724 }
725
726 #[test]
727 fn test_load_stream_from_event() {
728 pub const PATH: &str = "test_load_stream_from_event.ron";
729
730 write(PATH, DATA).unwrap();
731
732 #[derive(Event)]
733 struct LoadRequest(&'static str);
734
735 impl GetStream for LoadRequest {
736 type Stream = File;
737
738 fn stream(&self) -> Self::Stream {
739 File::open(self.0).unwrap()
740 }
741 }
742
743 let mut app = app();
744 app.add_event::<LoadRequest>()
745 .add_systems(PreUpdate, load(stream_from_event::<LoadRequest>()));
746
747 app.world_mut().send_event(LoadRequest(PATH));
748 app.update();
749
750 let world = app.world_mut();
751 assert!(world
752 .query_filtered::<(), With<Dummy>>()
753 .get_single(world)
754 .is_ok());
755
756 remove_file(PATH).unwrap();
757 }
758
759 #[test]
760 fn test_load_map_component() {
761 pub const PATH: &str = "test_load_map_component.ron";
762
763 write(PATH, DATA).unwrap();
764
765 let mut app = app();
766
767 #[derive(Component)]
768 struct Foo; app.add_systems(
771 PreUpdate,
772 load(static_file(PATH).map_component(|_: &Dummy| Foo)),
773 );
774
775 app.update();
776
777 let world = app.world_mut();
778 assert!(world
779 .query_filtered::<(), With<Foo>>()
780 .get_single(world)
781 .is_ok());
782 assert!(world
783 .query_filtered::<(), With<Dummy>>()
784 .get_single(world)
785 .is_err());
786
787 remove_file(PATH).unwrap();
788 }
789}