-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cannot Serialize Simple Bevy World Into a Scene #9060
Comments
When I migrated the code to v0.11, a different error came up:
|
I apologize, but I'm having the same issue with ComputedVisibilityFlags. |
This reply is going to be a ramble, apologies for that, but... Experiencing all the same stuff with Bevy 0.11.0 right now. Moreso, I tried excluding all components and resources that aren't truly serializable (the only way I found to do this was to actually just serialize each one, which is wildly inefficient, but it works and that's what I wanted lol), and there is still an issue. Sample system for serializing and deserializing only serializable components and resourcesuse bevy::{
ecs::entity::EntityMap,
prelude::*,
reflect::{serde::TypedReflectSerializer, TypeRegistryInternal},
scene::serde::SceneDeserializer,
};
use serde::de::DeserializeSeed;
fn serializable(reflect: &dyn Reflect, registry: &TypeRegistryInternal) -> bool {
let serializer = TypedReflectSerializer::new(reflect, registry);
ron::to_string(&serializer).is_ok()
}
pub fn rescene(world: &mut World) {
let input = world.get_resource::<Input<KeyCode>>().unwrap();
if input.just_pressed(KeyCode::C) {
let mut scene = DynamicScene::from_world(world);
let registry = world.get_resource::<AppTypeRegistry>().unwrap().read();
scene
.resources
.retain(|res| serializable(res.as_ref(), ®istry));
scene.entities.retain_mut(|ent| {
ent.components
.retain(|x| serializable(x.as_ref(), ®istry));
!ent.components.is_empty()
});
drop(registry);
let registry = world.get_resource_mut::<AppTypeRegistry>().unwrap();
let serialized = scene.serialize_ron(®istry.0).unwrap();
//std::fs::write("/tmp/test", &serialized).ok();
let registry = world.get_resource::<AppTypeRegistry>().unwrap().read();
let deserializer = SceneDeserializer {
type_registry: ®istry,
};
let mut ronde = ron::de::Deserializer::from_bytes(serialized.as_bytes()).unwrap();
// panics here
let deserialized = deserializer.deserialize(&mut ronde).unwrap();
//drop(registry);
//deserialized
// .write_to_world(world, &mut EntityMap::default())
// .unwrap();
}
} This panics on actually deserializing the scene I got from serializing everything that could be. In particular:
This seems to be the Funnily enough, excluding that via string comparisons in ...It doesn't seem like serializing the entire world and every single thing is very practical. It could probably be a cool feature to cause deserializing spawn a new window on the user's desktop, but usually that's not what you'd want. This does raise a question of how does one de/serialize "meaningful" parts of a world. Serializing Thinking about it, this sounds like a very subjective thing, as different parts of different components can be meaningless to some users but very important to other. Someone might actually care about the exact computed matrix in a You could make a marker component, slap it on every meaningful entity and then before exporting make a query and use it as a filter in For unserializable components themselves, like Camera2d, it does sound like you'd want to do some more work: store just the meaningful information itself, maybe as a separate component, and then on/after deserializing convert that component into a real new Camera2d. Considering that scenes are assets, this sounds like something related to asset processing, which would get a revamp in Bevy 0.12. Maybe better ways around this stuff would appear at that time? Though, the caveat of "this is likely meaningless to serialize" should probably be mentioned in docs of |
Completely agreed! I'm new to Bevy (and Rust for that matter) so I can't be much help beyond this small contribution: I can reproduce in Bevy 0.11.2 with the following use bevy::prelude::*;
fn main() {
App::new()
.add_systems(Startup, start_up)
.add_systems(Update, serialize_scene_system)
.add_plugins(DefaultPlugins)
.run();
}
fn start_up(mut commands: Commands) {
// comment out this line to fix issue
commands.spawn(Camera2dBundle::default());
}
fn serialize_scene_system(world: &mut World) {
let type_registry = world.resource::<AppTypeRegistry>();
let scene = DynamicScene::from_world(world);
let serialized_scene = scene.serialize_ron(type_registry).unwrap();
info!("{}", serialized_scene);
} The error:
|
Related to this issue, there I noticed that PlaybackSettings do not serialize/deserialize. There are no errors, but that component is not included in DynamicScene exports. I think the root of the issue is that a number of components and resources do not reflect out of the box, resulting in inconsistent and surprising behavior. Reflection is still fairly new (mentioned a lot in the latest release notes) and stabilizing -- so rough edges like this are to be expected. My workaround (for those blocked by this issue) is to use the DynamicSceneBuilder For example, here is a system that exports a scene with none of the resources and a bunch of entities, sans the fn export(
root: Query<Entity, With<LevelRoot>>,
children:Query<&Children>,
world: &World,
) {
let app_type_registry = world.resource::<AppTypeRegistry>().clone();
let mut builder = DynamicSceneBuilder::from_world(world.clone());
builder.deny_all_resources();
builder.allow_all();
builder.deny::<ComputedVisibility>();
// Here we are extracting a `LevelRoot` node and it's children, for example sake.
// Extract the level root
builder.extract_entity(root.single());
// Extract all level root descendants
builder.extract_entities(
children.iter_descendants(root.single())
);
// Build the scene
let scene = builder.build();
let scene_ron = scene
.serialize_ron(&app_type_registry)
.expect("Serialize scene");
} And here is a system that re-hydrates entities missing the fn rehydrate(
events: Query<Entity, (Added<Visibility>, Without<ComputedVisibility>)>,
mut commands: Commands,
) {
for entity in events.iter() {
commands
.entity(entity)
.insert(ComputedVisibility::default());
}
} This is not an ideal UX and I would much rather have all out-of-the-box resources and components reflect by default. |
Just to chime in, I've made Moonshine Save as a generic solution to exactly the problems @pop described. |
Bevy version
0.10.1
What you did
What went wrong
I was expecting it to just serialize the world, as I have not really added any new code (i.e components/resources).
Instead, I got this error: thread 'main' panicked at 'called
Result::unwrap()
on anErr
value: Message("no registration found for dynamic type with name bevy_math::rect::Rect")', src\main.rs:19:63Additional information
It feels like something wasn't registered by bevy. If you comment out the line of code inside of start_up, which spawns the Camera bundle, the error disappears and program runs as expected.
My thoughts moving forward
I understand that in the examples, you usually create an empty world and throw in the things you want to save in the scene, or use the DynamicSceneBuilder. This may or may not be a common practice in the bevy community. But I think if you want to serialize the entire world, then you should be able to without bevy holding you back.
If we're in agreement, then we should make a unit test that ensures that a basic bevy setup always registers what is needed so that the entire World can be serialized. I would make a PR of this myself, and I would actually like to in order to have a contribution under my belt (perhaps the first of many), but I'm not familiar enough with Bevy to make a comprehensive unit test. So if someone could help me out with that, or if you just wanna leave suggestions here on what I should include in the unit test, I'd appreciate it!
The text was updated successfully, but these errors were encountered: