diff options
author | 2024-10-16 15:12:15 -0400 | |
---|---|---|
committer | 2024-10-16 20:19:07 -0400 | |
commit | 99c398cc127dbc83480f98fea8c76f7c19d4dce8 (patch) | |
tree | c4c311300ad4194217eb55b5f4c278694f6bcdc2 | |
parent | 4fc097045b0baf8a3a5626681149f4540f8305d7 (diff) |
Navigation Rewrite
-rw-r--r-- | src/commands.rs | 288 | ||||
-rw-r--r-- | src/components.rs | 3 | ||||
-rw-r--r-- | src/events.rs | 11 | ||||
-rw-r--r-- | src/lib.rs | 24 | ||||
-rw-r--r-- | src/observers.rs | 142 | ||||
-rw-r--r-- | src/resources.rs | 8 | ||||
-rw-r--r-- | src/systems.rs | 24 | ||||
-rw-r--r-- | src/utils.rs | 124 | ||||
-rw-r--r-- | src/watcher.rs | 95 |
9 files changed, 361 insertions, 358 deletions
diff --git a/src/commands.rs b/src/commands.rs index 69cc83a..6066ab7 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,282 +1,28 @@ use std::{ - fs, iter, - path::{Path, PathBuf}, + fs, path::PathBuf, }; use bevy::{ - ecs::{ - system::SystemState, - world::{Command, CommandQueue}, - }, + ecs:: + world::{Command, CommandQueue} + , prelude::*, tasks::AsyncComputeTaskPool, }; use crypto::{ aes::KeySize, - blockmodes::{EcbEncryptor, PkcsPadding}, + blockmodes::PkcsPadding, buffer::{BufferResult, ReadBuffer, RefReadBuffer, RefWriteBuffer, WriteBuffer}, }; use occule::Error; use xz2::read::{XzDecoder, XzEncoder}; use crate::{ - components::DirworldEntity, - events::{DirworldNavigationEvent, DirworldSpawn}, - payload::{DirworldComponent, DirworldComponentDiscriminants, DirworldEntityPayload}, - resources::{ - DirworldCodecs, DirworldCurrentDir, DirworldObservers, DirworldRootDir, DirworldTasks, - EntryType, - }, - Extensions, + payload::{DirworldComponent, DirworldComponentDiscriminants, DirworldEntityPayload}, resources::{ + DirworldCodecs, DirworldObservers, DirworldTasks, + }, utils::extract_entity_payload, Extensions }; -struct DirworldNavigateCommand { - pub path: PathBuf, -} - -impl Command for DirworldNavigateCommand { - fn apply(self, world: &mut World) { - let root_dir = world.remove_resource::<DirworldRootDir>().unwrap(); - let mut current_dir = world.remove_resource::<DirworldCurrentDir>().unwrap(); - - let current_path; - let old_dir; - if let Some(old_path) = ¤t_dir.0 { - world.send_event(DirworldNavigationEvent::LeftRoom { - path: old_path.clone(), - }); - - current_path = old_path.join(self.path); - old_dir = Some(old_path.clone()); - } else { - current_path = self.path; - old_dir = None; - } - current_dir.0 = Some(current_path.clone()); - - let mut system_state: SystemState<( - Commands, - Query<(Entity, &DirworldEntity)>, - Res<DirworldObservers>, - Res<DirworldCodecs>, - )> = SystemState::new(world); - let (mut commands, dirworld_entities, observers, codecs) = system_state.get_mut(world); - update_entries( - &mut commands, - &dirworld_entities, - old_dir, - ¤t_path, - &root_dir.0.clone().unwrap(), - &observers, - &codecs, - ); - system_state.apply(world); - - world.send_event(DirworldNavigationEvent::EnteredRoom { path: current_path }); - world.insert_resource(current_dir); - world.insert_resource(root_dir); - } -} - -pub(crate) fn update_entries( - commands: &mut Commands, - dirworld_entities: &Query<(Entity, &DirworldEntity)>, - old_dir: Option<PathBuf>, - current_dir: &PathBuf, - project_dir: &PathBuf, - observers: &DirworldObservers, - codecs: &DirworldCodecs, -) { - let directory = current_dir.read_dir().unwrap(); - - if let Some(old_dir) = old_dir { - let mut entities_to_despawn = vec![]; - for (entity, dirworld_entity) in dirworld_entities.iter() { - if dirworld_entity.path.parent().unwrap() == old_dir { - entities_to_despawn.push(entity); - } - } - for entity in entities_to_despawn { - commands.entity(entity).despawn_recursive(); - } - } - - let mut entry_paths: Vec<PathBuf> = directory - .flatten() - .map(|entry| entry.path().canonicalize().unwrap()) - .collect::<Vec<_>>(); - entry_paths.retain(|entry| { - !entry - .file_name() - .is_some_and(|entry| entry.to_string_lossy().starts_with(".")) - }); - if current_dir != project_dir { - entry_paths = iter::once(current_dir.join("..")) - .chain(entry_paths) - .collect(); - } - - for entry_path in entry_paths { - process_entry(commands, &entry_path, &observers, &codecs); - } -} - -pub(crate) fn process_entry( - commands: &mut Commands, - entry_path: &PathBuf, - observers: &DirworldObservers, - codecs: &DirworldCodecs, -) { - let (payload, data) = extract_payload(entry_path, codecs); - let transform = if let Some(component) = payload - .as_ref() - .and_then(|payload| payload.component("Transform")) - { - if let DirworldComponent::Transform(component) = component { - component.clone() - } else { - panic!("Failed to decompose component") - } - } else { - Transform::default() - }; - - let entity = commands.spawn(( - SpatialBundle { - transform, - ..Default::default() - }, - DirworldEntity { - path: entry_path.clone(), - payload: payload.clone(), - }, - )); - - let entity = entity.id(); - let entry_type = if entry_path.is_dir() { - EntryType::Folder - } else { - let extensions = entry_path.extensions(); - EntryType::File(extensions) - }; - if let Some(observer) = observers.get(&entry_type) { - commands.trigger_targets(DirworldSpawn { entity, data }, observer.clone()); - } -} - -fn extract_payload( - entry_path: &PathBuf, - codecs: &DirworldCodecs, -) -> (Option<DirworldEntityPayload>, Option<Vec<u8>>) { - let entry_type = if entry_path.is_dir() { - EntryType::Folder - } else { - let extensions = entry_path.extensions(); - EntryType::File(extensions) - }; - - let mut data: Option<Vec<u8>> = None; - let mut payload: Option<DirworldEntityPayload> = None; - match &entry_type { - EntryType::File(Some(extension)) => { - if let Ok(file_data) = fs::read(entry_path.clone()) { - match codecs.get(extension) { - Some(codec) => match codec.decode(&file_data.as_slice()) { - Ok((carrier, extracted_payload)) => { - match rmp_serde::from_slice::<DirworldEntityPayload>( - extracted_payload.as_slice(), - ) { - Ok(deserialized_payload) => { - data = Some(carrier); - payload = Some(deserialized_payload); - } - Err(e) => { - warn!("{:?}", e); - data = Some(file_data); - } - } - } - Err(e) => match e { - Error::DataNotEncoded => { - data = Some(file_data); - } - _ => error!("{:?}", e), - }, - }, - None => { - data = Some(file_data); - } - } - } else { - warn!("Failed to read data from {entry_path:?}"); - } - } - EntryType::Folder => { - let door_path = entry_path.join(".door"); - if door_path.exists() { - let door_file_data = fs::read(door_path).unwrap(); - match rmp_serde::from_slice::<DirworldEntityPayload>(&door_file_data.as_slice()) { - Ok(deserialized_payload) => { - payload = Some(deserialized_payload); - } - Err(e) => { - warn!("{:?}", e); - } - } - } - } - _ => {} - } - (payload, data) -} - -struct DirworldChangeRootCommand { - pub path: PathBuf, -} - -impl Command for DirworldChangeRootCommand { - fn apply(self, world: &mut World) { - let mut root_dir = world.remove_resource::<DirworldRootDir>().unwrap(); - let mut current_dir = world.remove_resource::<DirworldCurrentDir>().unwrap(); - - let old_root; - if let DirworldRootDir(Some(old_dir)) = root_dir { - world.send_event(DirworldNavigationEvent::LeftRoom { - path: self.path.clone(), - }); - old_root = Some(old_dir); - } else { - old_root = None; - } - - root_dir.0 = Some(self.path.canonicalize().unwrap()); - current_dir.0 = Some(self.path.canonicalize().unwrap()); - - let mut system_state: SystemState<( - Commands, - Query<(Entity, &DirworldEntity)>, - Res<DirworldObservers>, - Res<DirworldCodecs>, - )> = SystemState::new(world); - let (mut commands, dirworld_entities, observers, codecs) = system_state.get_mut(world); - update_entries( - &mut commands, - &dirworld_entities, - old_root, - ¤t_dir.0.clone().unwrap(), - &root_dir.0.clone().unwrap(), - &observers, - &codecs, - ); - system_state.apply(world); - - world.send_event(DirworldNavigationEvent::EnteredRoom { path: self.path }); - - world.insert_resource(root_dir); - world.insert_resource(current_dir); - } -} - struct DirworldLockDoorCommand { path: PathBuf, key: Vec<u8>, @@ -287,7 +33,7 @@ impl Command for DirworldLockDoorCommand { let path = self.path.clone(); // Get existing payload let codecs = world.remove_resource::<DirworldCodecs>().unwrap(); - let (payload, _) = extract_payload(&path, &codecs); + let (payload, _) = extract_entity_payload(&path, &codecs); world.insert_resource(codecs); let task = AsyncComputeTaskPool::get().spawn(async move { // Tar directory @@ -357,7 +103,7 @@ impl Command for DirworldUnlockDoorCommand { let path = self.path.clone(); // Get existing payload let codecs = world.remove_resource::<DirworldCodecs>().unwrap(); - let (payload, carrier) = extract_payload(&path, &codecs); + let (payload, carrier) = extract_entity_payload(&path, &codecs); world.insert_resource(codecs); let task = AsyncComputeTaskPool::get().spawn(async move { // Decrypt archive @@ -509,12 +255,6 @@ impl Command for DirworldSaveEntityCommand { /// Commands for dirworld navigation pub trait DirworldCommands { - /// Change the root of the world. This will also set the current directory. This is not really meant to be used in-game but is useful for editor applications. - fn dirworld_change_root(&mut self, path: PathBuf); - - /// Move to given directory - fn dirworld_navigate(&mut self, path: PathBuf); - /// Lock Door fn dirworld_lock_door(&mut self, path: PathBuf, key: Vec<u8>); @@ -525,14 +265,6 @@ pub trait DirworldCommands { } impl<'w, 's> DirworldCommands for Commands<'w, 's> { - fn dirworld_change_root(&mut self, path: PathBuf) { - self.add(DirworldChangeRootCommand { path }); - } - - fn dirworld_navigate(&mut self, path: PathBuf) { - self.add(DirworldNavigateCommand { path }); - } - fn dirworld_lock_door(&mut self, path: PathBuf, key: Vec<u8>) { self.add(DirworldLockDoorCommand { key, path }); } diff --git a/src/components.rs b/src/components.rs index ea76185..8bb2bff 100644 --- a/src/components.rs +++ b/src/components.rs @@ -14,3 +14,6 @@ pub struct DirworldEntity { pub path: PathBuf, pub payload: Option<DirworldEntityPayload>, } + +#[derive(Debug, Component)] +pub struct Persist; diff --git a/src/events.rs b/src/events.rs index 41a0db3..4d12f5b 100644 --- a/src/events.rs +++ b/src/events.rs @@ -17,8 +17,17 @@ pub enum DirworldNavigationEvent { }, } +#[derive(Debug, Event, Deref, DerefMut)] +pub struct DirworldLeaveRoom(pub PathBuf); + +#[derive(Debug, Event, Deref, DerefMut)] +pub struct DirworldEnterRoom(pub PathBuf); + +#[derive(Debug, Event, Deref, DerefMut)] +pub struct DirworldChangeRoot(pub PathBuf); + #[derive(Event)] pub struct DirworldSpawn { pub entity: Entity, pub data: Option<Vec<u8>>, -}
\ No newline at end of file +} @@ -6,8 +6,9 @@ use std::{ffi::OsStr, path::PathBuf}; use bevy::{ecs::system::IntoObserverSystem, prelude::*}; use bevy_scriptum::{runtimes::lua::LuaRuntime, BuildScriptingRuntime, ScriptingRuntimeBuilder}; -use events::{DirworldNavigationEvent, DirworldSpawn}; +use events::{DirworldChangeRoot, DirworldEnterRoom, DirworldLeaveRoom, DirworldNavigationEvent, DirworldSpawn}; use occule::Codec; +use resources::DirworldCache; use resources::{ DirworldCodecs, DirworldCurrentDir, DirworldObservers, DirworldRootDir, DirworldTasks, EntryType, @@ -31,6 +32,10 @@ pub mod commands; mod systems; +mod observers; + +mod utils; + /// Payload for dirworld entities pub mod payload; @@ -54,24 +59,27 @@ impl Plugin for DirworldPlugin { Update, (systems::remove_completed_tasks, lua_api::trigger_update), ) - .add_systems(PostUpdate, watcher::update) - .add_systems( - PreUpdate, - watcher::handle_changes, - ) + .add_systems(PostUpdate, (watcher::update, systems::sync_entity_transforms)) .add_scripting::<LuaRuntime>(|runtime| { let runtime = lua_api::register(runtime); if let Some(register_custom) = &self.register_custom_lua_api { (register_custom)(runtime); } }) - .add_event::<DirworldNavigationEvent>() + .init_resource::<DirworldCache>() .init_resource::<DirworldRootDir>() .init_resource::<DirworldCurrentDir>() .init_resource::<DirworldTasks>() .init_resource::<DirworldObservers>() .init_resource::<DirworldCodecs>() - .add_event::<DirworldWatcherEvent>(); + .add_event::<DirworldEnterRoom>() + .add_event::<DirworldLeaveRoom>() + .add_event::<DirworldChangeRoot>() + .add_event::<DirworldWatcherEvent>() + .observe(observers::navigate_to_room) + .observe(observers::handle_changes) + .observe(observers::change_root) + .observe(observers::navigate_from_room); } } diff --git a/src/observers.rs b/src/observers.rs new file mode 100644 index 0000000..b321c53 --- /dev/null +++ b/src/observers.rs @@ -0,0 +1,142 @@ +use std::ops::Deref; + +use bevy::prelude::*; +use notify::{ + event::{MetadataKind, ModifyKind, RenameMode}, + EventKind, +}; + +use crate::{ + components::{DirworldEntity, Persist}, + events::{DirworldChangeRoot, DirworldEnterRoom, DirworldLeaveRoom}, + resources::{ + DirworldCache, DirworldCodecs, DirworldCurrentDir, DirworldObservers, DirworldRootDir, + }, + utils::{despawn_entity_by_path, spawn_entity}, + DirworldWatcherEvent, +}; + +/// On navigation from a room, insert modified payloads into the cache +pub fn navigate_from_room( + _: Trigger<DirworldLeaveRoom>, + entities: Query<(Entity, Ref<DirworldEntity>), Without<Persist>>, + mut cache: ResMut<DirworldCache>, + mut commands: Commands, +) { + for (entity, dirworld_entity) in entities.iter() { + if let Some(payload) = &dirworld_entity.payload { + info!("Caching {entity:?}"); + cache.insert(dirworld_entity.path.clone(), payload.clone()); + } + commands.entity(entity).despawn_recursive(); + } +} + +pub fn navigate_to_room( + trigger: Trigger<DirworldEnterRoom>, + root_dir: Res<DirworldRootDir>, + mut cache: ResMut<DirworldCache>, + observers: Res<DirworldObservers>, + codecs: Res<DirworldCodecs>, + mut commands: Commands, +) { + let path = &trigger.event().0; + + let entries = match path.read_dir() { + Ok(entries) => entries + .flatten() + .map(|entry| entry.path().canonicalize()) + .flatten() + .filter(|entry| { + !entry + .file_name() + .is_some_and(|file_name| file_name.to_string_lossy().starts_with(".")) + }) + .chain( + root_dir + .clone() + .map(|root_dir| { + if root_dir == *path { + None + } else { + Some(path.join("..")) + } + }) + .into_iter() + .flatten(), + ), + Err(e) => { + error!("Failed to read directory \"{}\", ({:?})", path.display(), e); + return; + } + }; + + for entry in entries { + spawn_entity(&entry, &mut cache, &codecs, &observers, &mut commands); + } +} + +pub fn handle_changes( + trigger: Trigger<DirworldWatcherEvent>, + mut commands: Commands, + dirworld_entities: Query<(Entity, &DirworldEntity)>, + observers: Res<DirworldObservers>, + codecs: Res<DirworldCodecs>, + mut cache: ResMut<DirworldCache>, +) { + let event = &trigger.event().0; + info!("Watcher Event: {event:?}"); + match event.kind { + EventKind::Remove(_) | EventKind::Modify(ModifyKind::Name(RenameMode::From)) => { + for path in &event.paths { + despawn_entity_by_path(&mut commands, &dirworld_entities, path); + } + } + EventKind::Create(_) | EventKind::Modify(ModifyKind::Name(RenameMode::To)) => { + for path in &event.paths { + spawn_entity(path, &mut cache, &codecs, &observers, &mut commands); + } + } + EventKind::Modify(ModifyKind::Name(RenameMode::Both)) => { + despawn_entity_by_path(&mut commands, &dirworld_entities, &event.paths[0]); + spawn_entity( + &event.paths[1], + &mut cache, + &codecs, + &observers, + &mut commands, + ); + } + EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any)) => { + despawn_entity_by_path(&mut commands, &dirworld_entities, &event.paths[1]); + spawn_entity( + &event.paths[0], + &mut cache, + &codecs, + &observers, + &mut commands, + ); + } + _ => { + // warn!("Not Processed.") + } + } +} + +pub fn change_root( + trigger: Trigger<DirworldChangeRoot>, + mut root_dir: ResMut<DirworldRootDir>, + mut current_dir: ResMut<DirworldCurrentDir>, + mut commands: Commands, +) { + if let DirworldRootDir(Some(old_dir)) = root_dir.deref() { + commands.trigger(DirworldLeaveRoom(old_dir.to_path_buf())); + }; + + let new_root = &trigger.event().0; + info!("Changing Root to {}", new_root.display()); + **root_dir = Some(new_root.to_path_buf()); + **current_dir = Some(new_root.to_path_buf()); + + commands.trigger(DirworldEnterRoom(new_root.to_path_buf())); +} diff --git a/src/resources.rs b/src/resources.rs index c9f7b40..152a149 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -1,9 +1,11 @@ -use std::{collections::BTreeMap, path::PathBuf}; +use std::{collections::{BTreeMap, HashMap}, path::PathBuf}; use bevy::{ecs::world::CommandQueue, prelude::*, tasks::Task}; use multi_key_map::MultiKeyMap; use occule::Codec; +use crate::payload::DirworldEntityPayload; + /// Root directory of the world #[derive(Resource, Deref, DerefMut, Default)] pub struct DirworldRootDir(pub Option<PathBuf>); @@ -27,3 +29,7 @@ pub enum EntryType { File(Option<String>), Folder, } + +/// Structure containing payload data for cached (non-current) rooms +#[derive(Resource, Default, Debug, Deref, DerefMut)] +pub struct DirworldCache(pub HashMap<PathBuf, DirworldEntityPayload>); diff --git a/src/systems.rs b/src/systems.rs index ac5f667..6c3bc71 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -1,6 +1,9 @@ -use bevy::{prelude::{Commands, ResMut}, tasks::{block_on, futures_lite::future}}; +use bevy::{ + prelude::*, + tasks::{block_on, futures_lite::future}, +}; -use crate::resources::DirworldTasks; +use crate::{components::DirworldEntity, payload::DirworldComponent, resources::DirworldTasks}; pub fn remove_completed_tasks(mut commands: Commands, mut tasks: ResMut<DirworldTasks>) { tasks.retain(|_, task| { @@ -12,3 +15,20 @@ pub fn remove_completed_tasks(mut commands: Commands, mut tasks: ResMut<Dirworld !task.is_finished() }); } + +pub fn sync_entity_transforms( + mut dirworld_entity_query: Query<(&mut DirworldEntity, Ref<Transform>, &GlobalTransform)>, +) { + for (mut dirworld_entity, transform, global_transform) in dirworld_entity_query.iter_mut() { + if transform.is_changed() && !transform.is_added() { + if let Some(payload) = &mut dirworld_entity.payload { + if let Some(DirworldComponent::Transform(payload_transform)) = + payload.component_mut("Transform") + { + let transform = global_transform.compute_transform(); + *payload_transform = transform; + } + } + } + } +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..df0a8b6 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,124 @@ +use std::{fs, path::PathBuf}; + +use bevy::prelude::*; + +use crate::{ + components::DirworldEntity, events::DirworldSpawn, payload::{DirworldComponent, DirworldEntityPayload}, resources::{DirworldCache, DirworldCodecs, DirworldObservers, EntryType}, Extensions +}; + +pub fn extract_entity_payload( + path: &PathBuf, + codecs: &DirworldCodecs, +) -> (Option<DirworldEntityPayload>, Option<Vec<u8>>) { + let mut data = None; + let mut payload = None; + + if path.is_dir() { + let payload_file_path = path.join(".door"); + if payload_file_path.exists() { + if let Ok(payload_file_data) = fs::read(&payload_file_path) { + match rmp_serde::from_slice::<DirworldEntityPayload>(&payload_file_data) { + Ok(deserialized_payload) => { + payload = Some(deserialized_payload); + } + Err(e) => { + warn!("Could not deserialize extracted payload: {e:?}"); + } + } + } + } + } else { + if let Some(extensions) = path.extensions() { + if let Ok(file_data) = fs::read(&path) { + if let Some(codec) = codecs.get(&extensions) { + match codec.decode(&file_data) { + Ok((carrier, extracted_payload)) => { + match rmp_serde::from_slice::<DirworldEntityPayload>(&extracted_payload) + { + Ok(deserialized_payload) => { + data = Some(carrier); + payload = Some(deserialized_payload); + } + Err(e) => { + warn!("Could not deserialize extracted payload: {e:?}"); + data = Some(file_data); + } + } + } + Err(e) => match e { + occule::Error::DataNotEncoded => { + data = Some(file_data); + } + _ => error!("Could not decode payload: {e:?}"), + }, + } + } else { + data = Some(file_data); + } + } + } + } + + (payload, data) +} + +pub fn spawn_entity( + entry: &PathBuf, + cache: &mut DirworldCache, + codecs: &DirworldCodecs, + observers: &DirworldObservers, + commands: &mut Commands, +) { + let (mut payload, data) = extract_entity_payload(&entry, &codecs); + if let Some(cached_payload) = cache.remove(entry) { + payload = Some(cached_payload); + } + + let transform = if let Some(component) = payload + .as_ref() + .and_then(|payload| payload.component("Transform")) + { + if let DirworldComponent::Transform(transform) = component { + transform.clone() + } else { + panic!("BAD DECOMPOSE: TRANSFORM ({component:?})"); + } + } else { + Transform::default() + }; + let entry_type = if entry.is_dir() { + EntryType::Folder + } else { + EntryType::File(entry.extensions()) + }; + let entity = commands + .spawn(( + SpatialBundle { + transform, + ..Default::default() + }, + DirworldEntity { + path: entry.clone(), + payload, + }, + )) + .id(); + if let Some(observer) = observers.get(&entry_type) { + commands.trigger_targets(DirworldSpawn { entity, data }, observer.clone()); + } +} + +pub fn despawn_entity_by_path( + commands: &mut Commands, + dirworld_entities: &Query<(Entity, &DirworldEntity)>, + path: &PathBuf, +) { + if let Some((entity, _)) = dirworld_entities + .iter() + .find(|(_, dirworld_entity)| dirworld_entity.path == *path) + { + commands.entity(entity).despawn_recursive(); + } else { + warn!("Failed to find entity corresponding to path for despawning: {path:?}"); + } +} diff --git a/src/watcher.rs b/src/watcher.rs index b94c0d4..78d74f2 100644 --- a/src/watcher.rs +++ b/src/watcher.rs @@ -1,4 +1,7 @@ -use std::{path::{Path, PathBuf}, time::Duration}; +use std::{ + path::{Path, PathBuf}, + time::Duration, +}; use async_channel::{Receiver, Sender}; use bevy::{prelude::*, tasks::IoTaskPool}; @@ -9,16 +12,15 @@ use notify::{ use notify_debouncer_full::{new_debouncer, DebounceEventResult}; use crate::{ - commands::process_entry, components::DirworldEntity, - resources::{DirworldCodecs, DirworldObservers, DirworldRootDir}, + resources::{DirworldCache, DirworldCodecs, DirworldObservers, DirworldRootDir}, }; #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] pub struct DirworldWatcherSet; /// Event fired when a file watcher event is caught. -#[derive(Event)] +#[derive(Event, Debug)] pub struct DirworldWatcherEvent(pub notify::Event); #[derive(Resource)] @@ -42,23 +44,33 @@ pub fn setup(mut commands: Commands) { async fn file_watcher(rx: Receiver<PathBuf>, tx: Sender<notify::Event>) { let (watcher_tx, watcher_rx) = std::sync::mpsc::channel(); - let mut debouncer = new_debouncer(Duration::from_millis(500), None, move |result: DebounceEventResult| { - match result { - Ok(events) => for event in events.iter() { - watcher_tx.send(event.clone()).unwrap(); + let mut debouncer = new_debouncer( + Duration::from_millis(500), + None, + move |result: DebounceEventResult| match result { + Ok(events) => { + for event in events.iter() { + watcher_tx.send(event.clone()).unwrap(); + } } - Err(errors) => for error in errors.iter() { - error!("{error:?}"); + Err(errors) => { + for error in errors.iter() { + error!("{error:?}"); + } } - } - }).unwrap(); + }, + ) + .unwrap(); let mut old_path: Option<PathBuf> = None; loop { while let Ok(message) = rx.try_recv() { if let Some(old_path) = &old_path { debouncer.watcher().unwatch(old_path).unwrap(); } - debouncer.watcher().watch(&message, RecursiveMode::NonRecursive).unwrap(); + debouncer + .watcher() + .watch(&message, RecursiveMode::NonRecursive) + .unwrap(); old_path = Some(message); } @@ -70,8 +82,8 @@ async fn file_watcher(rx: Receiver<PathBuf>, tx: Sender<notify::Event>) { pub fn update( watcher_channels: Res<WatcherChannels>, - mut event_writer: EventWriter<DirworldWatcherEvent>, root_dir: Res<DirworldRootDir>, + mut commands: Commands, ) { if root_dir.is_changed() { if let Some(project_dir) = &root_dir.0 { @@ -79,61 +91,8 @@ pub fn update( } } else { while let Ok(event) = watcher_channels.rx_changes.try_recv() { - event_writer.send(DirworldWatcherEvent(event)); + commands.trigger(DirworldWatcherEvent(event)); } } } -pub fn handle_changes( - mut event_reader: EventReader<DirworldWatcherEvent>, - mut commands: Commands, - dirworld_entities: Query<(Entity, &DirworldEntity)>, - observers: Res<DirworldObservers>, - codecs: Res<DirworldCodecs>, -) { - if !event_reader.is_empty() { - for DirworldWatcherEvent(event) in event_reader.read() { - info!("Watcher Event: {event:?}"); - match event.kind { - EventKind::Remove(_) | EventKind::Modify(ModifyKind::Name(RenameMode::From)) => { - for path in &event.paths { - remove_entity(&mut commands, &dirworld_entities, path); - } - } - EventKind::Create(_) | EventKind::Modify(ModifyKind::Name(RenameMode::To)) => { - for path in &event.paths { - process_entry(&mut commands, path, &observers, &codecs); - } - } - EventKind::Modify(ModifyKind::Name(RenameMode::Both)) - => { - remove_entity(&mut commands, &dirworld_entities, &event.paths[0]); - process_entry(&mut commands, &event.paths[1], &observers, &codecs); - } - // EventKind::Modify(ModifyKind::Data(DataChange::Content)) - EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any)) => { - remove_entity(&mut commands, &dirworld_entities, &event.paths[0]); - process_entry(&mut commands, &event.paths[0], &observers, &codecs); - } - _ => { - // warn!("Not Processed.") - } - } - } - } -} - -fn remove_entity( - commands: &mut Commands, - dirworld_entities: &Query<(Entity, &DirworldEntity)>, - path: &Path, -) { - if let Some((entity, _)) = dirworld_entities - .iter() - .find(|(_, dirworld_entity)| dirworld_entity.path == *path) - { - commands.entity(entity).despawn_recursive(); - } else { - warn!("Failed to find entity corresponding to path for despawning: {path:?}"); - } -} |