Zeenobit / moonshine_save

A save/load framework for Bevy game engine.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

No longer works on SceneBundles

AngeloMateus opened this issue · comments

Sorry if this is a simple mistake but this crate used to work in bevy 10 for SceneBundles, I can't seem to get it to load a scene in 11.2

I have a simple SceneBundle, I've registered the type TestCarScene, the save.ron file does save the component correctly however when calling load_from_file("./data/save.ron") it no longer renders the scene. Am I missing something simple? I have regeneration systems for other visual components such as meshes however I swear the scenebundle use to just work out of the box.

#[derive(Component, Default, Reflect, Debug)]
#[reflect(Component)]
pub struct TestCarScene;

pub fn add_test_car(mut commands: Commands, object_assets: Res<ObjectAssets>) {
    commands.spawn((
        SceneBundle {
            scene: object_assets.free_car.clone(),
            transform: Transform::from_xyz(2.0, 0.03, 3.0)
                .with_rotation(Quat::from_euler(
                    EulerRot::XYZ,
                    (0.0_f32).to_radians(),
                    (180.0_f32).to_radians(),
                    (0.0_f32).to_radians(),
                ))
                .with_scale(Vec3 {
                    x: 0.10,
                    y: 0.10,
                    z: 0.10,
                }),
            ..Default::default()
        },
        Save,
        TestCarScene,
    ));
}

Can you show me a minimal test case, or a minimal output save file?

I'm seeing these components in SceneBundle:

#[derive(Default, Bundle)]
pub struct SceneBundle {
    /// Handle to the scene to spawn
    pub scene: Handle<Scene>,
    pub transform: Transform,
    pub global_transform: GlobalTransform,

    #[cfg(feature = "bevy_render")]
    pub visibility: Visibility,
    #[cfg(feature = "bevy_render")]
    pub computed_visibility: ComputedVisibility,
}

Are all of these components present in your save output? Is Handle<Scene> a registered type?

Edit: I haven't been using SceneBundle myself. Has its components changed between Bevy 0.10 and Bevy 0.11?

So I had forgotten that I forked this repo and added .pipe(remove_component::<ComputedVisibility>) to the save_into_file function. Trying it again I get the error:
Type 'bevy_render::view::visibility::ComputedVisibilityFlags' did not register ReflectSerialize

With .pipe(remove_component::, and manually adding "bevy_render::view::visibility::ComputedVisibility" to the ron the error goes away however the component isn't added and doesn't show up in bevy-inspector-egui. Don't know if ComputedVisibilityFlags is the culprit or if it's silently failing somewhere else?

Doesn't seem anything has changed in SceneBundle between 0.10 and 0.11 but I am keen on getting this to work, most of what I'm saving with this plugin are SceneBundle objects, any pointer in the right direction would be greatly appreciated!

Minimal ron:

(
  resources: {},
  entities: {
    8589934640: (
      components: {
        "bevy_transform::components::transform::Transform": (
          translation: (
            x: 2.0,
            y: 0.03,
            z: 3.0,
          ),
          rotation: (0.0, 1.0, 0.0, -0.00000004371139),
          scale: (
            x: 0.1,
            y: 0.1,
            z: 0.1,
          ),
        ),
        "bevy_transform::components::global_transform::GlobalTransform": ((
          matrix3: (
            x_axis: (
              x: -0.1,
              y: 0.0,
              z: 0.000000008742278,
            ),
            y_axis: (
              x: 0.0,
              y: 0.1,
              z: 0.0,
            ),
            z_axis: (
              x: -0.000000008742278,
              y: 0.0,
              z: -0.1,
            ),
          ),
          translation: (
            x: 2.0,
            y: 0.03,
            z: 3.0,
          ),
        )),
        "bevy_hierarchy::components::children::Children": ([
          1612,
        ]),
        "bevy_asset::handle::Handle<bevy_scene::scene::Scene>": (
          id: AssetPathId(((15150937905447474773), (3199274184615654509))),
        ),
        "bevy_render::view::visibility::Visibility": Inherited,
        "bevy_core::name::Name": (
          hash: 8068448145236990257,
          name: "TestCarScene",
        ),
        "proto_game::systems::gameplay::gameplay_setup::TestCarScene": (),
      },
    ),
  },
)

Similar issue occuring with DynamicScenes here

There is a lot of components in Bevy that derive Reflect but aren't necessarily serializable or registered types.

From what I'm seeing in Bevy 11.2 code (Visibility logic seems to have changed entirely on latest), both ComputedVisibility and ComputedVisibilityFlags are registered types (see bevy_render::view::ViewPlugin). But ComputedVisibilityFlags is a custom bit flag struct which I imagine requires custom de/serialization code, which it doesn't seem to have. This would explain the issue you're experiencing.

With the latest version of this crate, you can pass a SceneFilter (inside a SaveFilter) which is then passed to the DynamicSceneBuilder directly:

pub fn save_into_file_on_event<R: SaveIntoFileRequest + Event>() -> SavePipeline {
    // Note: This is a single system, but still returned as `SystemConfigs` for easier refactoring.
    filter::<With<Save>>
        .pipe(save)
        .pipe(file_from_event::<R>)
        .pipe(into_file_dyn)
        .pipe(finish)
        .run_if(has_event::<R>)
        .in_set(SaveSet::Save)
}

For example:

pub fn filter_without_visibility<Filter: ReadOnlyWorldQuery>(entities: Query<Entity, Filter>) -> SaveFilter {
    let mut scene = SceneFilter::default();
    scene.deny::<ComputedVisibilityFlags>();
    SaveFilter {
        entities: EntityFilter::allow(&entities),
        scene,
    }
}

You can use this to avoid ComputedVisibilityFlags BEFORE scene serialization (you couldn't do this with the old remove_compnent method as it serialized, and then dumped the data -- it was awful 😛), which I think would bypass the error about ComputedVisibilityFlags not being serializable.

Then on load side, to be 100% safe, I'd recommend just checking all entities with a Visibility component, and then inserting a full VisibilityBundle into them with the initial value based on the loaded visibility. You could do this in PostLoad.

Theoretically, this would guarantee that the loaded visibility data is identical to runtime visibility data, and so everything else should work.

Thanks for this, I've tried your suggestion scene.deny::<ComputedVisibility>(); as ComputedVisibilityFlags seems to be a private struct, however on load I get the following error with a panic

Entity {some entity} does not exist

Encountered a panic in exclusive system Pipe(Pipe(Pipe(Pipe(moonshine_save::load::from_file<std::path::PathBuf>::{{closure}}, moonshine_save::load::unload<bevy_ecs::query::filter::Or<(bevy_ecs::query::filter::With<moonshine_save::save::Save>, bevy_ecs::query::filter::With<moonshine_save::load::Unload>)>>), moonshine_save::load::load), moonshine_save::load::insert_into_loaded<moonshine_save::save::Save>::{{closure}}), moonshine_save::load::finish)!
Encountered a panic in system bevy_app::main_schedule::Main::run_main!

Panic occurs in insert_into_loaded()
I'm not really sure how to debug this.

Edit: Not sure exactly why this is happening, but within insert_into_loaded() swapping world.entity_mut(entity) with

if let Some(mut e) = world.get_entity_mut(entity) {
    e.insert(bundle.clone());
};

seems to have done the trick! Not sure what happens with missing entities though.

If you're getting missing entity panics there, it suggests the EntityMap isn't being populated correctly. I can't debug this on my end since I don't have a repro case.

I'm concerned your workaround of using get_entity_mut is just hiding a bigger problem. In a default load pipeline, insert_into_loaded just inserts a Save component into all loaded entities (anything that is loaded is implicitly marked for save).
So if it's causing a panic, it implies you're trying to process entities that the system thinks are loaded, but not spawned. How those entities got inside your EntityMap... I can't know.

I'd suggest debugging to see what exactly is inside the loaded EntityMap at the end of load:

pub fn load(
    In(result): In<Result<Saved, LoadError>>,
    world: &mut World,
) -> Result<Loaded, LoadError> {
    let Saved { scene } = result?;
    let mut entity_map = EntityMap::default();
    scene.write_to_world(world, &mut entity_map)?;
    Ok(Loaded { entity_map }) // <-------------
}

You can compare the entities inside the EntityMap with the entities in your saved data to see which entities are not spawning despite being flagged as loaded, which might give us some clue as to why it's happening.

You're right it's not a solution by any means I'm closing this issue and debugging seperately as it's probably beyond the scope of this crate, I'll let you know if I find anything interesting though. Thanks for the help!