aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar Silas Bartha <silas@exvacuum.dev>2024-10-16 15:12:15 -0400
committerLibravatar Silas Bartha <silas@exvacuum.dev>2024-10-16 20:19:07 -0400
commit99c398cc127dbc83480f98fea8c76f7c19d4dce8 (patch)
treec4c311300ad4194217eb55b5f4c278694f6bcdc2
parent4fc097045b0baf8a3a5626681149f4540f8305d7 (diff)
Navigation Rewrite
-rw-r--r--src/commands.rs288
-rw-r--r--src/components.rs3
-rw-r--r--src/events.rs11
-rw-r--r--src/lib.rs24
-rw-r--r--src/observers.rs142
-rw-r--r--src/resources.rs8
-rw-r--r--src/systems.rs24
-rw-r--r--src/utils.rs124
-rw-r--r--src/watcher.rs95
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) = &current_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,
- &current_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,
- &current_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
+}
diff --git a/src/lib.rs b/src/lib.rs
index 2bfbc57..cf56ccf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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:?}");
- }
-}