use crate::io::{ file::{get_asset_path, get_base_path, new_asset_event_debouncer, FilesystemEventHandler}, memory::Dir, AssetSourceEvent, AssetWatcher, }; use alloc::{boxed::Box, sync::Arc, vec::Vec}; use bevy_platform::collections::HashMap; use core::time::Duration; use notify_debouncer_full::{notify::RecommendedWatcher, Debouncer, RecommendedCache}; use parking_lot::RwLock; use std::{ fs::File, io::{BufReader, Read}, path::{Path, PathBuf}, }; use tracing::warn; /// A watcher for assets stored in the `embedded` asset source. Embedded assets are assets whose /// bytes have been embedded into the Rust binary using the [`embedded_asset`](crate::embedded_asset) macro. /// This watcher will watch for changes to the "source files", read the contents of changed files from the file system /// and overwrite the initial static bytes of the file embedded in the binary with the new dynamically loaded bytes. pub struct EmbeddedWatcher { _watcher: Debouncer, } impl EmbeddedWatcher { /// Creates a new `EmbeddedWatcher` that watches for changes to the embedded assets in the given `dir`. pub fn new( dir: Dir, root_paths: Arc, PathBuf>>>, sender: crossbeam_channel::Sender, debounce_wait_time: Duration, ) -> Self { let root = get_base_path(); let handler = EmbeddedEventHandler { dir, root: root.clone(), sender, root_paths, last_event: None, }; let watcher = new_asset_event_debouncer(root, debounce_wait_time, handler).unwrap(); Self { _watcher: watcher } } } impl AssetWatcher for EmbeddedWatcher {} /// A [`FilesystemEventHandler`] that uses [`EmbeddedAssetRegistry`](crate::io::embedded::EmbeddedAssetRegistry) to hot-reload /// binary-embedded Rust source files. This will read the contents of changed files from the file system and overwrite /// the initial static bytes from the file embedded in the binary. pub(crate) struct EmbeddedEventHandler { sender: crossbeam_channel::Sender, root_paths: Arc, PathBuf>>>, root: PathBuf, dir: Dir, last_event: Option, } impl FilesystemEventHandler for EmbeddedEventHandler { fn begin(&mut self) { self.last_event = None; } fn get_path(&self, absolute_path: &Path) -> Option<(PathBuf, bool)> { let (local_path, is_meta) = get_asset_path(&self.root, absolute_path); let final_path = self.root_paths.read().get(local_path.as_path())?.clone(); if is_meta { warn!("Meta file asset hot-reloading is not supported yet: {final_path:?}"); } Some((final_path, false)) } fn handle(&mut self, absolute_paths: &[PathBuf], event: AssetSourceEvent) { if self.last_event.as_ref() != Some(&event) { if let AssetSourceEvent::ModifiedAsset(path) = &event { if let Ok(file) = File::open(&absolute_paths[0]) { let mut reader = BufReader::new(file); let mut buffer = Vec::new(); // Read file into vector. if reader.read_to_end(&mut buffer).is_ok() { self.dir.insert_asset(path, buffer); } } } self.last_event = Some(event.clone()); self.sender.send(event).unwrap(); } } }