mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Reuse and hot reload folder handles (#10210)
# Objective - Folder handles are not shared. Loading the same folder multiple times will result in different handles. - Once folder handles are shared, they can no longer be manually reloaded, so we should add support for hot-reloading them ## Solution - Reuse folder handles based on their path - Trigger a reload of a folder if a file contained in it (or a sub folder) is added or removed - This also covers adding/removing/moving sub folders containing files --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
77309ba5d8
commit
bfca4384cc
2 changed files with 48 additions and 24 deletions
|
@ -86,23 +86,6 @@ impl std::fmt::Debug for AssetInfos {
|
|||
}
|
||||
|
||||
impl AssetInfos {
|
||||
pub(crate) fn create_loading_handle<A: Asset>(&mut self) -> Handle<A> {
|
||||
unwrap_with_context(
|
||||
Self::create_handle_internal(
|
||||
&mut self.infos,
|
||||
&self.handle_providers,
|
||||
&mut self.living_labeled_assets,
|
||||
self.watching_for_changes,
|
||||
TypeId::of::<A>(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
),
|
||||
std::any::type_name::<A>(),
|
||||
)
|
||||
.typed_debug_checked()
|
||||
}
|
||||
|
||||
pub(crate) fn create_loading_handle_untyped(
|
||||
&mut self,
|
||||
type_id: TypeId,
|
||||
|
|
|
@ -23,6 +23,7 @@ use crossbeam_channel::{Receiver, Sender};
|
|||
use futures_lite::StreamExt;
|
||||
use info::*;
|
||||
use parking_lot::RwLock;
|
||||
use std::path::PathBuf;
|
||||
use std::{any::TypeId, path::Path, sync::Arc};
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -525,15 +526,33 @@ impl AssetServer {
|
|||
/// Loads all assets from the specified folder recursively. The [`LoadedFolder`] asset (when it loads) will
|
||||
/// contain handles to all assets in the folder. You can wait for all assets to load by checking the [`LoadedFolder`]'s
|
||||
/// [`RecursiveDependencyLoadState`].
|
||||
///
|
||||
/// Loading the same folder multiple times will return the same handle. If the `file_watcher`
|
||||
/// feature is enabled, [`LoadedFolder`] handles will reload when a file in the folder is
|
||||
/// removed, added or moved. This includes files in subdirectories and moving, adding,
|
||||
/// or removing complete subdirectories.
|
||||
#[must_use = "not using the returned strong handle may result in the unexpected release of the assets"]
|
||||
pub fn load_folder<'a>(&self, path: impl Into<AssetPath<'a>>) -> Handle<LoadedFolder> {
|
||||
let handle = {
|
||||
let mut infos = self.data.infos.write();
|
||||
infos.create_loading_handle::<LoadedFolder>()
|
||||
};
|
||||
let id = handle.id().untyped();
|
||||
let path = path.into().into_owned();
|
||||
let (handle, should_load) = self
|
||||
.data
|
||||
.infos
|
||||
.write()
|
||||
.get_or_create_path_handle::<LoadedFolder>(
|
||||
path.clone(),
|
||||
HandleLoadingMode::Request,
|
||||
None,
|
||||
);
|
||||
if !should_load {
|
||||
return handle;
|
||||
}
|
||||
let id = handle.id().untyped();
|
||||
self.load_folder_internal(id, path);
|
||||
|
||||
handle
|
||||
}
|
||||
|
||||
pub(crate) fn load_folder_internal(&self, id: UntypedAssetId, path: AssetPath) {
|
||||
fn load_folder<'a>(
|
||||
source: AssetSourceId<'static>,
|
||||
path: &'a Path,
|
||||
|
@ -568,6 +587,7 @@ impl AssetServer {
|
|||
})
|
||||
}
|
||||
|
||||
let path = path.into_owned();
|
||||
let server = self.clone();
|
||||
IoTaskPool::get()
|
||||
.spawn(async move {
|
||||
|
@ -607,8 +627,6 @@ impl AssetServer {
|
|||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
handle
|
||||
}
|
||||
|
||||
fn send_asset_event(&self, event: InternalAssetEvent) {
|
||||
|
@ -860,6 +878,19 @@ pub fn handle_internal_asset_events(world: &mut World) {
|
|||
}
|
||||
}
|
||||
|
||||
let reload_parent_folders = |path: PathBuf, source: &AssetSourceId<'static>| {
|
||||
let mut current_folder = path;
|
||||
while let Some(parent) = current_folder.parent() {
|
||||
current_folder = parent.to_path_buf();
|
||||
let parent_asset_path =
|
||||
AssetPath::from(current_folder.clone()).with_source(source.clone());
|
||||
if let Some(folder_handle) = infos.get_path_handle(parent_asset_path.clone()) {
|
||||
info!("Reloading folder {parent_asset_path} because the content has changed");
|
||||
server.load_folder_internal(folder_handle.id(), parent_asset_path);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut paths_to_reload = HashSet::new();
|
||||
let mut handle_event = |source: AssetSourceId<'static>, event: AssetSourceEvent| {
|
||||
match event {
|
||||
|
@ -870,6 +901,16 @@ pub fn handle_internal_asset_events(world: &mut World) {
|
|||
queue_ancestors(&path, &infos, &mut paths_to_reload);
|
||||
paths_to_reload.insert(path);
|
||||
}
|
||||
AssetSourceEvent::RenamedFolder { old, new } => {
|
||||
reload_parent_folders(old, &source);
|
||||
reload_parent_folders(new, &source);
|
||||
}
|
||||
AssetSourceEvent::AddedAsset(path)
|
||||
| AssetSourceEvent::RemovedAsset(path)
|
||||
| AssetSourceEvent::RemovedFolder(path)
|
||||
| AssetSourceEvent::AddedFolder(path) => {
|
||||
reload_parent_folders(path, &source);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue