Skip to content
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

Hot-reloading is watching only one file from folder #3719

Closed
SmallNibbler opened this issue Jan 18, 2022 · 2 comments
Closed

Hot-reloading is watching only one file from folder #3719

SmallNibbler opened this issue Jan 18, 2022 · 2 comments
Labels
A-Assets Load files from disk to use for things like images, models, and sounds C-Bug An unexpected or incorrect behavior

Comments

@SmallNibbler
Copy link

SmallNibbler commented Jan 18, 2022

Bevy version

[package]
name = "experimental"
version = "0.1.0"
edition = "2021"

[dependencies]
anyhow = "1.0.52"

[dependencies.bevy]
version = "0.6.0"
default-features = false
features = [
"filesystem_watcher",
"bevy_dynamic_plugin",
"render",
"bevy_winit",
"png",
"x11",
]

Operating system & version

Operating System: Manjaro Linux
KDE Plasma Version: 5.23.4
KDE Frameworks Version: 5.89.0
Qt Version: 5.15.2
Kernel Version: 5.15.12-1-MANJARO (64-bit)
Graphics Platform: X11
Processors: 4 × Intel® Pentium® CPU 3825U @ 1.90GHz
Memory: 7.7 Gb of RAM
Graphics Processor: Mesa Intel® HD Graphics

What you did

code_loader.rs

use bevy::prelude::*;
use bevy::asset::{AssetLoader, LoadContext, LoadedAsset};
use bevy::utils::BoxedFuture;
use bevy::reflect::TypeUuid;

#[derive(Debug, TypeUuid, Clone)]
#[uuid = "39cadc56-aa9c-4543-8640-a018b74b5052"]
pub (crate) struct Code {
    pub (crate) content: String
}

#[derive(Default)]
struct CodeAssetLoader;

impl AssetLoader for CodeAssetLoader {
    fn extensions(&self) -> &[&str] {
        &["rs"]
    }

    fn load<'a>(&'a self, bytes: &'a [u8], load_context: &'a mut LoadContext) -> BoxedFuture<'a, Result<(), anyhow::Error>> {
        Box::pin(async move {
            use std::str;

            let loaded_content = str::from_utf8(bytes)?;
            load_context.set_default_asset(LoadedAsset::new(Code{ content: loaded_content.to_string() }));
            Ok(())
        })
    }
}

pub struct CodeAssetPlugin;

impl Plugin for CodeAssetPlugin {
    fn build(&self, app: &mut App) {
        app.add_asset::<Code>()
            .init_asset_loader::<CodeAssetLoader>();
    }
}

main.rs

use bevy::prelude::*;

mod code_loader;
use crate::code_loader::*;

fn main() {
    App::new()
    .add_plugins(DefaultPlugins)
    .add_plugin(CodeAssetPlugin)
    .init_resource::<CodeHandles>()
    .add_startup_system(assets_load)
    .add_system(assets_print)
    .run();
}

#[derive(Default, Debug)]
pub (crate) struct CodeHandles {
    pub (crate) storage: Vec<Handle<Code>>
}

// Systems
fn assets_load (
    asset_server:       Res<AssetServer>,
    mut handles_storage:ResMut<CodeHandles>
) {
    handles_storage.storage = asset_server
        .load_folder("slides").unwrap()
        .into_iter().map(|h| h.typed::<Code>())
        .collect();
    asset_server.watch_for_changes().unwrap();

    println!("Handles {:#?}", handles_storage.storage);
}

fn assets_print (
    code_assets:            Res<Assets<Code>>,
    mut asset_events:       EventReader<AssetEvent<Code>>,
) {
    for event in asset_events.iter() {
        match event {
            AssetEvent::Created { handle } => {
                println!("Created {:#?}", handle);
                println!("Content {:#?}", &code_assets.get(handle).unwrap().content);
            }
            AssetEvent::Modified { handle } => {
                println!("Modified {:#?}", handle);
                println!("Content {:#?}", &code_assets.get(handle).unwrap().content);
            }
            AssetEvent::Removed { handle } => {println!("Removed {:#?}", handle);}
        }
    }
}

What does this code mean:
There is an asset defenition in code_loader.rs. Asset with a plain text from .rs file.
main.rs algorithm:

  1. Loading folder of .rs files.
  2. Convert all HandleUntyped types to Handle<Code>.
  3. Save it to Resource with Vec<Handle<Code>>.
  4. Start watch for changes.
  5. If a file has been loaded or modified, prints its content.

What you expected to happen

In most situations one file of the three is tracked, rarely two of three, sometimes no one.
In the next example i'm loading two files from folder, one is tracked or no one.

Handles [
    StrongHandle<Code>(AssetPathId(AssetPathId(SourcePathId(13027041954229367283), LabelId(6298619649789039366)))),
    StrongHandle<Code>(AssetPathId(AssetPathId(SourcePathId(9312919904264361102), LabelId(6298619649789039366)))),
]
Created WeakHandle<Code>(AssetPathId(AssetPathId(SourcePathId(13027041954229367283), LabelId(6298619649789039366))))
Content "// Example\nfn main() {\n    println!(\"Hello World, from short string!\");\n}\n"
Created WeakHandle<Code>(AssetPathId(AssetPathId(SourcePathId(9312919904264361102), LabelId(6298619649789039366))))
Content "// Example\nfn main() {\n    println!(\"Hello World, from this very long string!\");\n}\n"
Modified WeakHandle<Code>(AssetPathId(AssetPathId(SourcePathId(9312919904264361102), LabelId(6298619649789039366))))
Content "// Example 1\nfn main() {\n    println!(\"Hello World, from this very long string!\");\n}\n"
Modified WeakHandle<Code>(AssetPathId(AssetPathId(SourcePathId(13027041954229367283), LabelId(6298619649789039366))))
Content "// Example 1\nfn main() {\n    println!(\"Hello World, from short string!\");\n}\n"

What actually happened

Handles [
    StrongHandle<Code>(AssetPathId(AssetPathId(SourcePathId(13027041954229367283), LabelId(6298619649789039366)))),
    StrongHandle<Code>(AssetPathId(AssetPathId(SourcePathId(9312919904264361102), LabelId(6298619649789039366)))),
]
Created WeakHandle<Code>(AssetPathId(AssetPathId(SourcePathId(13027041954229367283), LabelId(6298619649789039366))))
Content "// Example\nfn main() {\n    println!(\"Hello World, from short string!\");\n}\n"
Created WeakHandle<Code>(AssetPathId(AssetPathId(SourcePathId(9312919904264361102), LabelId(6298619649789039366))))
Content "// Example\nfn main() {\n    println!(\"Hello World, from this very long string!\");\n}\n"
Modified WeakHandle<Code>(AssetPathId(AssetPathId(SourcePathId(9312919904264361102), LabelId(6298619649789039366))))
Content "// Example 1\nfn main() {\n    println!(\"Hello World, from this very long string!\");\n}\n"
@SmallNibbler SmallNibbler added C-Bug An unexpected or incorrect behavior S-Needs-Triage This issue needs to be labelled labels Jan 18, 2022
@alice-i-cecile alice-i-cecile added A-Assets Load files from disk to use for things like images, models, and sounds and removed S-Needs-Triage This issue needs to be labelled labels Jan 19, 2022
@superdump
Copy link
Contributor

superdump commented Jan 19, 2022

Calling AssetServer::watch_for_changes() initialises the filesystem watcher synchronously. Calling AssetServer::load_folder() eventually spawns a call to the internal AssetServer::load_async() as a task on a TaskPool. That is, it is called asynchronously and after load_folder() returns, the timing of those tasks executing can and will vary. At the end of load_async(), the path to the asset is added to the patches to be watched, but only if the filesystem watcher has been initialised and exists.

Hopefully from this description of how the code works, it is clear that this code is racey. I discovered this while investigating inconsistency in how hot-reloading was working and fixing it here: #3643

You could also try enabling the watcher first, and then calling load_folder.

@SmallNibbler
Copy link
Author

SmallNibbler commented Jan 19, 2022

This helped, thanks.

You could also try enabling the watcher first, and then calling load_folder.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Assets Load files from disk to use for things like images, models, and sounds C-Bug An unexpected or incorrect behavior
Projects
None yet
Development

No branches or pull requests

3 participants