bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
36.7k stars 3.61k forks source link

`load_folder()` only on some `Asset` types/extensions #2291

Open TheLeonsver1 opened 3 years ago

TheLeonsver1 commented 3 years ago

What problem does this solve or what need does it fill?

I would like to be able to load_folder() on files with only particular extensions,

My game's assets folder would be something like this:

assets
    mods
        [mod_name]
            data
                events
            localization
            scripts
        [mod_name]
            data
                events
            localization
            scripts

and such.

As far as I understand it, it would be hard to traverse the assets folder with std::fs on certain platforms, so i was suggested to just load_folder("/mods") but that would raise the issues: 1) My game might do work that I don't actually want to do at that point(loading assets this system/plugin doesn't care about) 2) I would need to iterate on all assets of some type, clone their handles, and then remove the resource of the Vec<HandleUntyped> i got from load_folder which is not super nice API-wise. (on this part I'm not sure, if there's a way to turn a HandleUntyped to a type of Handle without this, i'm sorry). 3) (I'm not sure it's possible) but I might somehow break other plugins I depend on, for example, bevy_fluent's files would be inside every mod's localization folder

What solution would you like?

It will probably be hard to associate an asset type(for example Texture) with the extensions it might consist of) so it won't be easy to get Vec<Handle<Texture>> out of load_folder::<Texture>("path") although it would be preferable,

So I'd be fine if we would keep returning a Vec<HandleUntyped> but load_folder (or a different function) would take an extensions array and only load files with those extensions.

What alternative(s) have you considered?

only the 2 above for now.

Additional context

none for now.

NathanSWard commented 3 years ago

It will probably be hard to associate an asset type(for example Texture) with the extensions

Yea, sadly this is not possible since a specific asset type might have many possible different extensions. Abd while we could associate an asset type with a set of extensions, I don't think that the correct path forward.

A possible solution I see it:

fn load_folder_if<F>(root: impl AsRef<Path>, f: F) -> Vec<UntypedHandle> 
    where F: Fn(&Path) -> bool;

Where it takes some predicate and then passes you each file it's trying to load and not loadit if the predicate returns false

So for your specific use case it would look like such:

fn system(server: Res<AssetServer>) {
    let handles = server.load_folder_if("mods/", |path| path.ends_with("txt"));
}

this would only load files that have the txt extension

RyeToastyO commented 1 year ago

I was hoping to find the API suggested in this thread, eg load_folder<AssetType>("path"). Wanted to bump this issue since it appears that assets do have an associated array of extensions (at least I had to define those while setting up a custom asset type). I'm pretty new to Bevy, but hoping maybe this solution is more practical now =)

inodentry commented 10 months ago

I would like to bump this. I also have a similar use case where this would be useful. I am making a skinnable game, where each skin has a TOML file (custom asset type) and a GLTF file. I would like to load all the *.ass3d.toml files in a directory to scan for available skins, but only load the associated GLTF files if they are actually going to be used.

inodentry commented 10 months ago

I see a few possible solutions, in order of my personal preference:

Filesystem Scanning / Traversal API in AssetServer

Have an alternative to load_folder and the returned LoadedFolder, to only detect what files are available without loading them. For example, imagine a method scan_folder which returns a ScannedFolder or something like that, and ScannedFolder has methods to iterate over the paths and trigger loading of whatever you want, or could be "upgraded" to a LoadedFolder if you decide you want to load everything.

Predicate

As was already suggested above, have an alternative to load_folder which takes a generic Fn parameter that it will call to decide whether it should load a specific path or not.

Sparse LoadedFolder

This might also require changing the semantics of LoadedFolder so that it can represent partially-loaded folders, instead of guaranteeing that all assets under a folder are loaded and tracked.

mosure commented 5 months ago

+1 for scan_folder or a similar API for listing paths available to the asset server.

hammypants commented 1 month ago

+1 for scan_folder (or similar to the above) as one could basically craft any number of ad-hoc extended load_folder functionalities with it.

Lege19 commented 3 weeks ago

I'm looking into working on this, here's the API I propose:

ScannedFolder would just contain a Vec<AssetPath> My choice to use AssetPath in the scanned directories but Path for the predicates may seem odd, but I think it makes sense: The paths in a ScannedFolder should be easy to then manually load, so they have to be AssetPath's. However the paths passed to predicate should be easy to work with, and since the root directory will be the same for all calls to the predicate, I think it would be unhelpful to pass it to the predicate every time, especially as it would be (I think) non-trivial to remove it.

One thing I'm not sure about though is how to manage the lifetimes for the AssetPaths in a ScannedFolder, because it doesn't own most of it's own data. I'd appreciate suggestions here.