1use std::io::{self, Read};
2use std::marker::PhantomData;
3use std::path::PathBuf;
4
5use bevy_scene::DynamicScene;
6use moonshine_util::expect::{expect_deferred, ExpectDeferred};
7use moonshine_util::Static;
8use serde::de::DeserializeSeed;
9
10use bevy_ecs::entity::EntityHashMap;
11use bevy_ecs::prelude::*;
12use bevy_ecs::query::QueryFilter;
13use bevy_log::prelude::*;
14use bevy_scene::{serde::SceneDeserializer, SceneSpawnError};
15
16use moonshine_util::event::{OnSingle, SingleEvent, TriggerSingle};
17use thiserror::Error;
18
19use crate::save::Save;
20use crate::{MapComponent, SceneMapper};
21
22#[derive(Component, Default, Clone)]
64pub struct Unload;
65
66pub trait TriggerLoad {
68 #[doc(alias = "trigger_single")]
70 fn trigger_load(self, event: impl LoadEvent);
71}
72
73impl TriggerLoad for &mut Commands<'_, '_> {
74 fn trigger_load(self, event: impl LoadEvent) {
75 self.trigger_single(event);
76 }
77}
78
79impl TriggerLoad for &mut World {
80 fn trigger_load(self, event: impl LoadEvent) {
81 self.trigger_single(event);
82 }
83}
84
85pub type DefaultUnloadFilter = Or<(With<Save>, With<Unload>)>;
87
88pub trait LoadEvent: SingleEvent {
95 type UnloadFilter: QueryFilter;
97
98 fn input(&mut self) -> LoadInput;
100
101 fn before_load(&mut self, _world: &mut World) {}
105
106 fn before_unload(&mut self, _world: &mut World, _entities: &[Entity]) {}
111
112 fn after_load(&mut self, _world: &mut World, _result: &LoadResult) {}
117}
118
119pub struct LoadWorld<U: QueryFilter = DefaultUnloadFilter> {
121 pub input: LoadInput,
123 pub mapper: SceneMapper,
125 #[doc(hidden)]
126 pub unload: PhantomData<U>,
127}
128
129impl<U: QueryFilter> LoadWorld<U> {
130 pub fn new(input: LoadInput, mapper: SceneMapper) -> Self {
132 LoadWorld {
133 input,
134 mapper,
135 unload: PhantomData,
136 }
137 }
138
139 pub fn from_file(path: impl Into<PathBuf>) -> Self {
142 LoadWorld {
143 input: LoadInput::File(path.into()),
144 mapper: SceneMapper::default(),
145 unload: PhantomData,
146 }
147 }
148
149 pub fn from_stream(stream: impl LoadStream) -> Self {
152 LoadWorld {
153 input: LoadInput::Stream(Box::new(stream)),
154 mapper: SceneMapper::default(),
155 unload: PhantomData,
156 }
157 }
158
159 pub fn map_component<T: Component>(self, m: impl MapComponent<T>) -> Self {
161 LoadWorld {
162 mapper: self.mapper.map(m),
163 ..self
164 }
165 }
166}
167
168impl LoadWorld {
169 pub fn default_from_file(path: impl Into<PathBuf>) -> Self {
172 Self::from_file(path)
173 }
174
175 pub fn default_from_stream(stream: impl LoadStream) -> Self {
178 Self::from_stream(stream)
179 }
180}
181
182impl<U: QueryFilter> SingleEvent for LoadWorld<U> where U: Static {}
183
184impl<U: QueryFilter> LoadEvent for LoadWorld<U>
185where
186 U: Static,
187{
188 type UnloadFilter = U;
189
190 fn input(&mut self) -> LoadInput {
191 self.input.consume().unwrap()
192 }
193
194 fn before_load(&mut self, world: &mut World) {
195 world.insert_resource(ExpectDeferred);
196 }
197
198 fn after_load(&mut self, world: &mut World, result: &LoadResult) {
199 if let Ok(loaded) = result {
200 for entity in loaded.entities() {
201 let Ok(entity) = world.get_entity_mut(entity) else {
202 continue;
204 };
205 self.mapper.replace(entity);
206 }
207 }
208
209 expect_deferred(world);
210 }
211}
212
213pub enum LoadInput {
215 File(PathBuf),
217 Stream(Box<dyn LoadStream>),
219 Scene(DynamicScene),
223 #[doc(hidden)]
224 Invalid,
225}
226
227impl LoadInput {
228 pub fn file(path: impl Into<PathBuf>) -> Self {
230 Self::File(path.into())
231 }
232
233 pub fn stream<S: LoadStream + 'static>(stream: S) -> Self {
235 Self::Stream(Box::new(stream))
236 }
237
238 pub fn consume(&mut self) -> Option<LoadInput> {
240 let input = std::mem::replace(self, LoadInput::Invalid);
241 if let LoadInput::Invalid = input {
242 return None;
243 }
244 Some(input)
245 }
246}
247
248pub trait LoadStream: Read
250where
251 Self: Static,
252{
253}
254
255impl<S: Read> LoadStream for S where S: Static {}
256
257#[derive(Event)]
261pub struct Loaded {
262 pub entity_map: EntityHashMap<Entity>,
264}
265
266impl Loaded {
267 pub fn entities(&self) -> impl Iterator<Item = Entity> + '_ {
272 self.entity_map.values().copied()
273 }
274}
275
276#[doc(hidden)]
277#[deprecated(since = "0.5.2", note = "use `Loaded` instead")]
278pub type OnLoad = Loaded;
279
280#[derive(Error, Debug)]
282pub enum LoadError {
283 #[error("Failed to read world: {0}")]
285 Io(io::Error),
286 #[error("Failed to deserialize world: {0}")]
288 Ron(ron::Error),
289 #[error("Failed to spawn scene: {0}")]
291 Scene(SceneSpawnError),
292}
293
294impl From<io::Error> for LoadError {
295 fn from(e: io::Error) -> Self {
296 Self::Io(e)
297 }
298}
299
300impl From<ron::de::SpannedError> for LoadError {
301 fn from(e: ron::de::SpannedError) -> Self {
302 Self::Ron(e.into())
303 }
304}
305
306impl From<ron::Error> for LoadError {
307 fn from(e: ron::Error) -> Self {
308 Self::Ron(e)
309 }
310}
311
312impl From<SceneSpawnError> for LoadError {
313 fn from(e: SceneSpawnError) -> Self {
314 Self::Scene(e)
315 }
316}
317
318pub type LoadResult = Result<Loaded, LoadError>;
320
321pub fn load_on_default_event(event: OnSingle<LoadWorld>, commands: Commands) {
323 load_on(event, commands);
324}
325
326pub fn load_on<E: LoadEvent>(event: OnSingle<E>, mut commands: Commands) {
328 commands.queue_handled(LoadCommand(event.consume().unwrap()), |err, ctx| {
329 error!("load failed: {err:?} ({ctx})");
330 });
331}
332
333fn load_world<E: LoadEvent>(mut event: E, world: &mut World) -> LoadResult {
334 event.before_load(world);
336
337 let scene = match event.input() {
339 LoadInput::File(path) => {
340 let bytes = std::fs::read(&path)?;
341 let mut deserializer = ron::Deserializer::from_bytes(&bytes)?;
342 let type_registry = &world.resource::<AppTypeRegistry>().read();
343 let scene_deserializer = SceneDeserializer { type_registry };
344 scene_deserializer.deserialize(&mut deserializer).unwrap()
345 }
346 LoadInput::Stream(mut stream) => {
347 let mut bytes = Vec::new();
348 stream.read_to_end(&mut bytes)?;
349 let mut deserializer = ron::Deserializer::from_bytes(&bytes)?;
350 let type_registry = &world.resource::<AppTypeRegistry>().read();
351 let scene_deserializer = SceneDeserializer { type_registry };
352 scene_deserializer.deserialize(&mut deserializer)?
353 }
354 LoadInput::Scene(scene) => scene,
355 LoadInput::Invalid => {
356 panic!("LoadInput is invalid");
357 }
358 };
359
360 let entities: Vec<_> = world
362 .query_filtered::<Entity, E::UnloadFilter>()
363 .iter(world)
364 .collect();
365 event.before_unload(world, &entities);
366 for entity in entities {
367 if let Ok(entity) = world.get_entity_mut(entity) {
368 entity.despawn();
369 }
370 }
371
372 let mut entity_map = EntityHashMap::default();
374 scene.write_to_world(world, &mut entity_map)?;
375 debug!("loaded {} entities", entity_map.len());
376
377 let result = Ok(Loaded { entity_map });
378 event.after_load(world, &result);
379 result
380}
381
382struct LoadCommand<E>(E);
383
384impl<E: LoadEvent> Command<Result<(), LoadError>> for LoadCommand<E> {
385 fn apply(self, world: &mut World) -> Result<(), LoadError> {
386 let loaded = load_world(self.0, world)?;
387 world.trigger(loaded);
388 Ok(())
389 }
390}
391
392#[cfg(test)]
393mod tests {
394 use std::fs::*;
395
396 use bevy::prelude::*;
397 use bevy_ecs::system::RunSystemOnce;
398
399 use super::*;
400
401 pub const DATA: &str = "(
402 resources: {},
403 entities: {
404 4294967293: (
405 components: {
406 \"moonshine_save::load::tests::Foo\": (),
407 },
408 ),
409 },
410 )";
411
412 #[derive(Component, Default, Reflect)]
413 #[reflect(Component)]
414 #[require(Save)]
415 struct Foo;
416
417 fn app() -> App {
418 let mut app = App::new();
419 app.add_plugins(MinimalPlugins).register_type::<Foo>();
420 app
421 }
422
423 #[test]
424 fn test_load_file() {
425 #[derive(Resource)]
426 struct EventTriggered;
427
428 pub const PATH: &str = "test_load_file.ron";
429
430 write(PATH, DATA).unwrap();
431
432 let mut app = app();
433 app.add_observer(load_on_default_event);
434
435 app.add_observer(|_: On<Loaded>, mut commands: Commands| {
436 commands.insert_resource(EventTriggered);
437 });
438
439 let _ = app.world_mut().run_system_once(|mut commands: Commands| {
440 commands.trigger_load(LoadWorld::default_from_file(PATH));
441 });
442
443 let world = app.world_mut();
444 assert!(world.contains_resource::<EventTriggered>());
445 assert!(world
446 .query_filtered::<(), With<Foo>>()
447 .single(world)
448 .is_ok());
449
450 remove_file(PATH).unwrap();
451 }
452
453 #[test]
454 fn test_load_stream() {
455 pub const PATH: &str = "test_load_stream.ron";
456
457 write(PATH, DATA).unwrap();
458
459 let mut app = app();
460 app.add_observer(load_on_default_event);
461
462 let _ = app.world_mut().run_system_once(|mut commands: Commands| {
463 commands.spawn((Foo, Save));
464 commands.trigger_load(LoadWorld::default_from_stream(File::open(PATH).unwrap()));
465 });
466
467 let data = read_to_string(PATH).unwrap();
468 assert!(data.contains("Foo"));
469
470 remove_file(PATH).unwrap();
471 }
472
473 #[test]
474 fn test_load_map_component() {
475 pub const PATH: &str = "test_load_map_component.ron";
476
477 write(PATH, DATA).unwrap();
478
479 #[derive(Component)]
480 struct Bar; let mut app = app();
483 app.add_observer(load_on_default_event);
484
485 let _ = app.world_mut().run_system_once(|mut commands: Commands| {
486 commands.trigger_load(LoadWorld::default_from_file(PATH).map_component(|_: &Foo| Bar));
487 });
488
489 let world = app.world_mut();
490 assert!(world
491 .query_filtered::<(), With<Bar>>()
492 .single(world)
493 .is_ok());
494 assert!(world.query_filtered::<(), With<Foo>>().iter(world).count() == 0);
495
496 remove_file(PATH).unwrap();
497 }
498}