MrGVSV/bevy_proto

Unstable/inconsistent bevy_proto_backend::registration::systems: could not register prototype

Closed this issue · 7 comments

It is me again, TT_TT. Currently working alone on this hobby dev, really appreciate the helps.

ERROR bevy_proto_backend::registration::systems: could not register prototype: the prototype with handle HandleUntyped { id: AssetPathId(AssetPathId(SourcePathId(12790493230470399281), LabelId(8823233378563626002))), handle_type: Weak } either doesn't exist or isn't fully loaded

Hi, I have encountered the above error quite a few time, especially after a code change and rebuild (when running cargo run after code change). When I run the second (or sometime need 3rd) time without any code changes, this error went away until I made another code change and rebuild. The error seems to indicate relation to the way the prototype is loaded, but I don't know which asset is not set properly.

Question 1
This may happen again at a different stage of my game, but again, it is not consistent enough for me to know which prototype is causing this. Is there a way for us to identify what the PathID and SourceID refers to?

Not sure if relevant, but I often try to make the loading happen during AppState::Init then spawn during AppState::Game or similar steps. Use prototype.is_ready as auto state change trigger from Init to Game.

Question 2
Is there a built in prototype.is_all_ready or similar or do I need to manually code in for each prototype ya? I am afraid that I may at time forget to include the loading check when adding new type of assets.

MrGVSV commented

Hm, I wonder if this is related to an issue I ran into before. Bevy doesn't make guarantees that dependency assets will be fully loaded by the time they're considered "ready". Because of this, there's an off chance that you run into this kind of issue. The Prototypes::is_ready helps prevent running into this issue super often, but I guess there's still a chance it happens...

Unfortunately, I'm not sure a way of enforcing that (efficiently anyways). I believe it should be fixed in Bevy v0.12 with the new assets rework.

I see, thanks for the info, really appreciate it.

Was just thinking about this again, is there a way for us to "catch" the error, and automatically re-attempt to load the asset again? Tried checking the source code, this is under prototype.get_load_state(handle_id), and I can get handle id from prototype.get("path/to/file.prototype.ron"), right?

Will be attempting the above and get back here. Thanks for any insight if you have the time.

MrGVSV commented

The thing is the asset shouldn't have failed to load. It's just that Bevy doesn't guarantee that all dependencies are fully loaded by the time a dependent is loaded. In other words, it can send out the AssetEvent::Created too early.

Maybe one way of solving this is to queue the final load for the next frame. This is assuming the dependency assets are marked as loaded later in that same frame (which I believe might be the case).

I guess, ultimately, I would have to wait for Bevy asset 2.0 rework to solve this.

Anyhow, just some update on the code. prototype.get_load_state(handle_id) apparently doesn't gave an error state when the failed loading/could not register prototype, but return Loading state.

If I try to have load when !is_ready, I think it simply force the loading to restart all over again, rather than start another new request properly. Attempted to add timer to compensate but still no help there. Will try to find another way later on a fresh mind later. Wondering if I need to limit the hierarchy of the ron files. I can see that for my case, the Hud prototype is the main culprit (but not always), which has 3 level of children.

Example code as follows:

fn autostart_game_once_res_ready(
    // use Option to avoid panic when resource is not ready yet
    is_spawned: Option<ResMut<IsHudRootSpawned>>,
    mut fight_state_next_state: ResMut<NextState<FightState>>,
    mut game_state_next_state: ResMut<NextState<GameState>>,
    fight_state: Res<State<FightState>>,
    mut prototypes: PrototypesMut,
    mut countdown: ResMut<CountdownPreload>,
    time: Res<Time>,
) {
    countdown.main_timer.tick(time.delta());

    let list_proto = [
        ["hud".to_string(), "HudRoot".to_string()],
        ["card".to_string(), "CardRoot".to_string()],
        ["enemy".to_string(), "EnemyRoot".to_string()],
        ["player".to_string(), "PlayerRoot".to_string()],
        ["player".to_string(), "PlayerSprite".to_string()],
        ["effect".to_string(), "EffectRoot".to_string()],
    ];

    fn list_path(i: usize, l: [[String; 2]; 6]) -> String {
        let p0 = &l[i][0];
        let p1 = &l[i][1];
        let mut sentence = "schematics/".to_string();
        sentence = sentence + &p0 + "/" + &p1 + ".prototype.ron";
        sentence
    }

    let mut list_bool = [false, false, false, false, false, false];

    if let Some(is_spawned) = is_spawned {
        // check if prototype & hp resources is ready
        // TODO to add other resources and RON loading as well later

        for (idx, proto) in list_proto.iter().enumerate() {
            list_bool[idx] = prototypes.is_ready(&proto[1]);
        }

        println!("{:?}", list_bool);

        if list_bool.into_iter().all(|x| x) {
            println!("IsHudSpawned = {}", is_spawned.value);
            game_state_next_state.set(GameState::Fight);

            if *fight_state.get() != FightState::Reset {
                fight_state_next_state.set(FightState::Reset);
            }

            dbg!("Entered AppState::Game");
        } else {
            if countdown.main_timer.finished() {
                for (idx, is_ready) in list_bool.iter().enumerate() {
                    if !is_ready {
                        let path = list_path(idx, list_proto.clone());
                        let handle = prototypes.get(&path).unwrap();
                        let status = prototypes.get_load_state(handle);
                        dbg!(handle);

                        if status != LoadState::Loaded {
                            prototypes.remove(&path);
                            prototypes.load(&path);
                            countdown.main_timer.reset();
                        }
                    }
                }
            }
        }
    } else {
        dbg!("Deck is not ready");
    };
}

Closing this issue for now since it might have require update on main bevy.

Just adding some additional info here for others who might encountered the same thing. I have just performed a move to consolidate my schematics into one large file for each category (i.e. avoiding any separate/dedicated child files). Now, the error doesn't seems to be there anymore (or at least not there yet). Might do another simple status update here in a week to confirm.

Example: previously I have HudRoot.ron, HudTop, HudMiddle, HudBottom, HudBottomLeft, etc.... until maybe 10 files for Hud, but now I am merging them into only one Hud files.

Not sure if this is indicative of issues with too many files, either due to read IO or handles memory management.