Skip to content

Adding the same asset label multiple times to LoadContext does not return an error. #19026

Open
@andriyDev

Description

@andriyDev

The problem

Calling LoadContext::add_labeled_asset multiple times with the same label is not considered an error. This case is kinda trivial, but the more tricky case is when using LoadContext::add_loaded_labeled_asset. Calling this multiple times can have subassets of those subassets conflict.

Prior attempts

In #15481, I flattened the subasset hierarchy, and in #18013, I took advantage of this to return an error on duplicate labels. However these were reverted in #18567 due to #18010. The TL;DR is that multiple nested asset loads with subassets can have overlapping subasset names. For example, if you have an asset loader that does two nested asset loads for GLTF files, both are likely to contain "Mesh0/Primitive0" subassets. Since nested asset loads currently are loaded as LoadedAsset, that means that their subassets were getting flattened, resulting in them being detected as duplicates.

Basic Example

The following test fails since no error is returned by the loader. One of the subassets just silently gets dropped (which is bad since they contain different data).

#[test]
fn error_on_duplicate_subasset_names() {
    let mut app = App::new();

    let dir = Dir::default();
    dir.insert_asset_text(Path::new("a.txt"), "");

    app.register_asset_source(
        AssetSourceId::Default,
        AssetSource::build()
            .with_reader(move || Box::new(MemoryAssetReader { root: dir.clone() })),
    )
    .add_plugins((TaskPoolPlugin::default(), AssetPlugin::default()));

    struct DuplicateSubassetLoader;

    impl AssetLoader for DuplicateSubassetLoader {
        type Asset = TestAsset;
        type Error = std::io::Error;
        type Settings = ();

        async fn load(
            &self,
            _: &mut dyn Reader,
            _: &Self::Settings,
            load_context: &mut LoadContext<'_>,
        ) -> Result<Self::Asset, Self::Error> {
            load_context.add_labeled_asset("A".into(), SubText { text: "one".into() });
            load_context.add_labeled_asset("A".into(), SubText { text: "two".into() });
            Ok(TestAsset)
        }
    }

    app.init_asset::<TestAsset>()
        .init_asset::<SubText>()
        .register_asset_loader(DuplicateSubassetLoader);

    let asset_server = app.world().resource::<AssetServer>().clone();
    let handle = asset_server.load::<TestAsset>("a.txt");

    run_app_until(&mut app, |_world| match asset_server.load_state(&handle) {
        LoadState::Loading => None,
        LoadState::Failed(err) => {
            // TODO: Check the error message matches here.
            Some(())
        }
        state => panic!("Unexpected asset state: {state:?}"),
    });
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-AssetsLoad files from disk to use for things like images, models, and soundsC-BugAn unexpected or incorrect behaviorS-Needs-DesignThis issue requires design work to think about how it would best be accomplished

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions