#Error when saving DynamicScenes

32 messages · Page 1 of 1 (latest)

hasty bough
#

I have a save game system that uses Queries to grab all the relevant entities I need to save (units, items, etc) and then creates a dynamic scene builder that extracts all those entities, as well as any children.

Once I try to serialize the scene, it errors out and says that “Type ‘bevy_ecs::entity::Entity’ did not register ReflectSerialize”. These type of errors are the reason why I moved away from the idea of saving the entire world into a scene in one go, but now it’s still happening even when I am only extracting very specific entities. Any idea why this might be happening?

Entity is not a component or resource, so I can’t just .deny(Entity) on the DynamicSceneBuilder.

Thanks

gloomy dune
#

Do you have a snippet of the how you're using the DynamicSceneBuilder?

#

Also, are you using the same World as the one from App?

#

Because Entity, along with its ReflectSerialize type data, should be registered

hasty bough
#

function was too long to copy/paste, so here is a screenshot

#

basically I am saving all these different things (items, units, spells) as different scn files, originally because i thought i might want to be able to load things in individually instead of all at once

#

Yes, I am using the same World as the one from App

#

this is an exclusive system that runs at a particular state change

#

Oh I just noticed the scenebuilder line is cut off, here it is:
let mut builder = DynamicSceneBuilder::from_world(world).extract_entity(ent);

#

I am also absolutely open to suggestions if there are easier ways to do what I'm doing here. I'm not really sure if having a bunch of different .scn files is really that advantageous in the long run, so maybe just collecting all these things as before but then loading them all into a single dynamic scene?

hasty bough
#

Refactored and simplified the save_game system to use a single DynamicSceneBuilder and output a single .scn file:

fn save_game(world: &mut World, params: &mut SystemState<ResMut<NextState<AppState>>>) {
    let registry = TypeRegistry::from_world(world);
    let mut units: QueryState<
        (Entity, Option<&Children>),
        Or<(With<Unit>, With<Item>, With<Spell>)>,
    > = QueryState::new(world);
    let parents = units.iter(world).map(|x| x.0);
    let mut builder = DynamicSceneBuilder::from_world(world).extract_entities(parents);
    let children = units.iter(world).filter_map(|x| x.1);
    builder = builder.extract_entities(children.flatten().map(|x| *x));
    let scene = builder.build();
    let serialized_scene = scene.serialize(&registry).unwrap();
    IoTaskPool::get()
        .spawn(async move {
            // Write the scene RON data to file
            File::create(format!("assets/saved-game.scn.ron"))
                .and_then(|mut file| file.write_all(serialized_scene.as_bytes()))
                .expect("Error while writing scene to file");
        })
        .detach();
    {
        let mut state = params.get_mut(world);
        state.set(AppState::InGame);
    }
    params.apply(world);
}
#

The “Type ‘bevy_ecs::entity::Entity’ did not register ReflectSerialize” error is still happening, unfortunately.

whole sundial
#

How are you creating/setting up the App?

#

And is this bevy 0.13 or 0.14?

#

IIRC you might need to add MapEntities to your reflection components

#

Though my simple MVCE on 0.14 seems to not require it

hasty bough
#

Hi @whole sundial , thanks for commenting!
App setup:

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(EguiPlugin)
        .add_plugins(StatePlugins)
        .add_plugins(bevy_inspector_egui::DefaultInspectorConfigPlugin)
        .add_systems(Update, inspector_ui)
        .add_event::<SaveToDisk>()
        .register_type::<ComponentRegistry>()
        .insert_state(AppState::MainMenu)
        .add_systems(Startup, setup)
        .add_systems(OnEnter(AppState::SaveCharacter), save_game)
        .run();
}

#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
enum AppState {
    Startup,
    MainMenu,
    LoadCharacter,
    NewCharacter,
    SaveCharacter,
    InGame,
    CharacterSheet(Entity),
}

#[derive(Component, Default, Reflect)]
#[reflect(Component)]
struct ComponentRegistry {
    player: PlayerBundle,
    weapon: WeaponBundle,
    spell: SpellBundle,
}
#

Bevy version is 0.14.1

#

Let me look into the MapEntities thing and see if that helps. TBH with you, I've never encountered that before so not even sure what it does. Time to read some more docs!

hasty bough
#

okay @whole sundial @gloomy dune , I have found the source of the ticking noise!

Test results based on what was in the world when I tried to save:
Unit1, Unit2 = Success
Unit1, Unit2, Item1 as child of unit2 = Failure
Unit1, Unit2, Item1 standalone = Success

So there is something going on where the Item having a Parent(Entity) component causes the error. Maybe something about that entity ID being meaningless outside the context of the original world, like what @whole sundial mentioned about MapEntities?

whole sundial
#

Interesting, can you post a minimal example?

#

Here is my MVCE: ```rs
use bevy::ecs::system::SystemState;
use bevy::prelude::*;

fn main() {
App::new()
.add_plugins(HierarchyPlugin)
.register_type::<Foo>()
.register_type::<Bar>()
.add_systems(Startup, |world: &mut World| {
world.spawn(Bar { x: 1.0, y: 2.0 }).with_children(|p| {
p.spawn(Bar { x: 3.0, y: 4.0 });
});
})
.add_systems(Update, |world: &mut World, state: &mut SystemState<Query<Entity>>| {
let query = state.get(world);

        let builder = DynamicSceneBuilder::from_world(world).extract_entities(query.iter());
        let scene = builder.build();

        let type_registry = world.resource::<AppTypeRegistry>();
        let serialized_scene = scene.serialize(&type_registry.read()).unwrap();

        println!("{}", serialized_scene);
    })
    .run();

}

#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
struct Foo {
pub target: Entity,
}

#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
struct Bar {
pub x: f32,
pub y: f32,
}

hasty bough
#

yeah will do, one sec

hasty bough
#

Sorry super hectic day at work, will post example as soon as I can

gloomy dune
#

TypeRegistry doesn't implement FromWorld, it implements Default. And since FromWorld has a blanket impl over T: Default, that code is effectively just calling TypeRegistry::default()

#

Which means you don't have the same registrations as the one from your World

#

So you probably want to do what s33n suggested and grab the registry directly from the World:

let type_registry = world.resource::<AppTypeRegistry>();
let serialized_scene = scene.serialize(&type_registry.read()).unwrap();
hasty bough
#

Oh my gosh you are right!!!! I just tested it and it worked with world.resource::<AppTypeRegistry>(); instead of TypeRegistry::from_world(world);

#

I didn't realize there was a substantial difference between TypeRegistry and AppTypeRegistry. Thank you both for spotting the problem

gloomy dune
#

Yeah honestly I don't blame you at all haha, that's a really confusing footgun