bevy/crates/bevy_asset/src/io/file_asset_io.rs
Daniel McNab f3de12bc5e Add a warning when watch_for_changes has no effect (#3684)
# Objective

- Users can get confused when they ask for watching to be unsupported, then find it isn't supported
- Fixes https://github.com/bevyengine/bevy/issues/3683

## Solution

- Add a warning if the `watch_for_changes` call would do nothing
2022-01-21 00:29:29 +00:00

155 lines
4.8 KiB
Rust

#[cfg(feature = "filesystem_watcher")]
use crate::{filesystem_watcher::FilesystemWatcher, AssetServer};
use crate::{AssetIo, AssetIoError};
use anyhow::Result;
#[cfg(feature = "filesystem_watcher")]
use bevy_ecs::system::Res;
use bevy_utils::BoxedFuture;
#[cfg(feature = "filesystem_watcher")]
use bevy_utils::HashSet;
#[cfg(feature = "filesystem_watcher")]
use crossbeam_channel::TryRecvError;
use fs::File;
#[cfg(feature = "filesystem_watcher")]
use parking_lot::RwLock;
#[cfg(feature = "filesystem_watcher")]
use std::sync::Arc;
use std::{
env, fs,
io::Read,
path::{Path, PathBuf},
};
pub struct FileAssetIo {
root_path: PathBuf,
#[cfg(feature = "filesystem_watcher")]
filesystem_watcher: Arc<RwLock<Option<FilesystemWatcher>>>,
}
impl FileAssetIo {
pub fn new<P: AsRef<Path>>(path: P) -> Self {
FileAssetIo {
#[cfg(feature = "filesystem_watcher")]
filesystem_watcher: Default::default(),
root_path: Self::get_root_path().join(path.as_ref()),
}
}
pub fn get_root_path() -> PathBuf {
if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
PathBuf::from(manifest_dir)
} else {
env::current_exe()
.map(|path| {
path.parent()
.map(|exe_parent_path| exe_parent_path.to_owned())
.unwrap()
})
.unwrap()
}
}
}
impl AssetIo for FileAssetIo {
fn load_path<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<Vec<u8>, AssetIoError>> {
Box::pin(async move {
let mut bytes = Vec::new();
let full_path = self.root_path.join(path);
match File::open(&full_path) {
Ok(mut file) => {
file.read_to_end(&mut bytes)?;
}
Err(e) => {
return if e.kind() == std::io::ErrorKind::NotFound {
Err(AssetIoError::NotFound(full_path))
} else {
Err(e.into())
}
}
}
Ok(bytes)
})
}
fn read_directory(
&self,
path: &Path,
) -> Result<Box<dyn Iterator<Item = PathBuf>>, AssetIoError> {
let root_path = self.root_path.to_owned();
Ok(Box::new(fs::read_dir(root_path.join(path))?.map(
move |entry| {
let path = entry.unwrap().path();
path.strip_prefix(&root_path).unwrap().to_owned()
},
)))
}
fn watch_path_for_changes(&self, _path: &Path) -> Result<(), AssetIoError> {
#[cfg(feature = "filesystem_watcher")]
{
let path = self.root_path.join(_path);
let mut watcher = self.filesystem_watcher.write();
if let Some(ref mut watcher) = *watcher {
watcher
.watch(&path)
.map_err(|_error| AssetIoError::PathWatchError(path))?;
}
}
Ok(())
}
fn watch_for_changes(&self) -> Result<(), AssetIoError> {
#[cfg(feature = "filesystem_watcher")]
{
*self.filesystem_watcher.write() = Some(FilesystemWatcher::default());
}
#[cfg(not(feature = "filesystem_watcher"))]
bevy_log::warn!("Watching for changes is not supported when the `filesystem_watcher` feature is disabled");
Ok(())
}
fn is_directory(&self, path: &Path) -> bool {
self.root_path.join(path).is_dir()
}
}
#[cfg(all(
feature = "filesystem_watcher",
all(not(target_arch = "wasm32"), not(target_os = "android"))
))]
pub fn filesystem_watcher_system(asset_server: Res<AssetServer>) {
let mut changed = HashSet::default();
let asset_io =
if let Some(asset_io) = asset_server.server.asset_io.downcast_ref::<FileAssetIo>() {
asset_io
} else {
return;
};
let watcher = asset_io.filesystem_watcher.read();
if let Some(ref watcher) = *watcher {
loop {
let event = match watcher.receiver.try_recv() {
Ok(result) => result.unwrap(),
Err(TryRecvError::Empty) => break,
Err(TryRecvError::Disconnected) => panic!("FilesystemWatcher disconnected."),
};
if let notify::event::Event {
kind: notify::event::EventKind::Modify(_),
paths,
..
} = event
{
for path in paths.iter() {
if !changed.contains(path) {
let relative_path = path.strip_prefix(&asset_io.root_path).unwrap();
let _ = asset_server.load_untracked(relative_path.into(), true);
}
}
changed.extend(paths);
}
}
}
}