Skip to content

Scene serialization doesn't work without field types being registered #8415

@InnocentusLime

Description

@InnocentusLime

Bevy version

Bevy 0.10.1 and c488b70 (latest commit on main so far)

What you did

Consider the following code. Here I am trying to serialize just one entity from the scene -- a text entity I spawned during startup.

For bevy 0.10.1

// For bevy 0.10.1
use bevy::prelude::*;

fn startup(mut commands: Commands, server: Res<AssetServer>) {
    commands.spawn(TextBundle {
        text: Text::from_section(
            "Hello, world!",
            TextStyle {
                font: server.load("my_font.ttf"),
                font_size: 12.0f32,
                color: Color::BLACK,
            },
        ),
        ..default()
    });
}

fn tick(
    text_q: Query<Entity, With<Text>>,
    type_registry: Res<AppTypeRegistry>,
    world: &World,
) {
    let e = text_q.single();
    let mut dyn_scene = DynamicScene::from_world(world, &type_registry);

    dyn_scene.entities.retain(|ent| ent.entity == e.index());
    assert!(dyn_scene.entities.len() == 1); // We do have some entities left

    dyn_scene.serialize_ron(&type_registry).unwrap(); // Try to save
    info!("yes");
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_startup_system(startup)
        .add_system(tick)
        .run();
}

For bevy 0.11.0-dev (c488b70)

// For bevy 0.11-dev (c488b7089c58c99371af25b7e27551e892df3dcf)
use bevy::prelude::*;

fn startup(mut commands: Commands, server: Res<AssetServer>) {
    commands.spawn(TextBundle {
        text: Text::from_section(
            "Hello, world!",
            TextStyle {
                font: server.load("my_font.ttf"),
                font_size: 12.0f32,
                color: Color::BLACK,
            },
        ),
        ..default()
    });
}

fn tick(
    text_q: Query<Entity, With<Text>>,
    type_registry: Res<AppTypeRegistry>,
    world: &World,
) {
    let e = text_q.single();
    let mut dyn_scene = DynamicScene::from_world(world, &type_registry);

    dyn_scene.resources.clear();
    dyn_scene.entities.retain(|ent| ent.entity == e.index());
    assert!(dyn_scene.entities.len() == 1); // We do have some entities left

    dyn_scene.serialize_ron(&type_registry).unwrap(); // Try to save
    info!("yes");
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, startup)
        .add_systems(Update, tick)
        .run();
}

What were you expecting

The app should have been working and spamming "yes" without any problems, since bevy_text::BreakLineOn implements all the needed traits. The struct containing it (bevy_text::Text) is registered

What actually happened

Both respective versions crash with a message saying that bevy_text::BreakLineOn isn't a registered type.

thread 'Compute Task Pool (4)' panicked at 'called `Result::unwrap()` on an `Err` value: Message("no registration found for dynamic type with name bevy_text::text::BreakLineOn")', src\main.rs:29:45

Additional information

My guess is that the serialization implementation can't somehow poke the the serialize implementations of the fields without looking at the type registry.

At first I thought that this is a design choice. But it seems that it's not, since bevy_text is an official crate and BreakLineOn isn't explicitly registered. Vec3 seemingly isn't explicitly registered either. Yet, when I do the same test with a lone TransformBundle, everything just works.

// For bevy 0.10.1
// Spams "yes" without any problem
use bevy::prelude::*;

fn startup(mut commands: Commands) {
    commands.spawn(TransformBundle::default());
}

fn tick(
    text_q: Query<Entity, With<Transform>>,
    type_registry: Res<AppTypeRegistry>,
    world: &World,
) {
    let e = text_q.single();
    let mut dyn_scene = DynamicScene::from_world(world, &type_registry);

    dyn_scene.entities.retain(|ent| ent.entity == e.index());
    assert!(dyn_scene.entities.len() == 1); // We do have some entities left

    dyn_scene.serialize_ron(&type_registry).unwrap(); // Try to save
    info!("Yes");
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_startup_system(startup)
        .add_system(tick)
        .run();
}

Force-registering bevy_text::BreakLineOn fixes the issue, but that is going to blow the amount of needed to plug things into bevy's reflection.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ReflectionRuntime information about typesA-ScenesSerialized ECS data stored on the diskC-BugAn unexpected or incorrect behaviorD-TrivialNice and easy! A great choice to get started with Bevy

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions