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
@ -86,23 +86,6 @@ impl std::fmt::Debug for AssetInfos {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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(
|
pub(crate) fn create_loading_handle_untyped(
|
||||||
&mut self,
|
&mut self,
|
||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
|
|||||||
@ -23,6 +23,7 @@ use crossbeam_channel::{Receiver, Sender};
|
|||||||
use futures_lite::StreamExt;
|
use futures_lite::StreamExt;
|
||||||
use info::*;
|
use info::*;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::{any::TypeId, path::Path, sync::Arc};
|
use std::{any::TypeId, path::Path, sync::Arc};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
@ -525,15 +526,33 @@ impl AssetServer {
|
|||||||
/// Loads all assets from the specified folder recursively. The [`LoadedFolder`] asset (when it loads) will
|
/// 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
|
/// contain handles to all assets in the folder. You can wait for all assets to load by checking the [`LoadedFolder`]'s
|
||||||
/// [`RecursiveDependencyLoadState`].
|
/// [`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"]
|
#[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> {
|
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 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>(
|
fn load_folder<'a>(
|
||||||
source: AssetSourceId<'static>,
|
source: AssetSourceId<'static>,
|
||||||
path: &'a Path,
|
path: &'a Path,
|
||||||
@ -568,6 +587,7 @@ impl AssetServer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let path = path.into_owned();
|
||||||
let server = self.clone();
|
let server = self.clone();
|
||||||
IoTaskPool::get()
|
IoTaskPool::get()
|
||||||
.spawn(async move {
|
.spawn(async move {
|
||||||
@ -607,8 +627,6 @@ impl AssetServer {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
handle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_asset_event(&self, event: InternalAssetEvent) {
|
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 paths_to_reload = HashSet::new();
|
||||||
let mut handle_event = |source: AssetSourceId<'static>, event: AssetSourceEvent| {
|
let mut handle_event = |source: AssetSourceId<'static>, event: AssetSourceEvent| {
|
||||||
match event {
|
match event {
|
||||||
@ -870,6 +901,16 @@ pub fn handle_internal_asset_events(world: &mut World) {
|
|||||||
queue_ancestors(&path, &infos, &mut paths_to_reload);
|
queue_ancestors(&path, &infos, &mut paths_to_reload);
|
||||||
paths_to_reload.insert(path);
|
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 New Issue
Block a user