From 9e5f782eb228a6b050b01d5c4c82dba1399f7172 Mon Sep 17 00:00:00 2001 From: Silas Bartha Date: Sat, 15 Feb 2025 20:53:19 -0500 Subject: directory-aware navigation --- src/commands.rs | 4 ++-- src/events.rs | 28 +++++++++++----------------- src/lib.rs | 6 ++++-- src/observers.rs | 31 +++++++++++++++++++++++++------ src/payload/components/mod.rs | 5 +++++ src/payload/mod.rs | 2 ++ src/preload/events.rs | 7 +++++++ src/preload/mod.rs | 38 ++++++++++++++++++++++++++++++-------- src/preload/resources.rs | 6 ++++++ src/preload/systems.rs | 30 +++++++++++++++++++++++++++--- src/resources.rs | 8 +++++--- src/systems.rs | 13 ++++++++----- src/utils.rs | 10 ++++++++-- 13 files changed, 140 insertions(+), 48 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index 616f326..f203adc 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -89,7 +89,7 @@ impl Command for DirworldLockDoorCommand { }); world.resource_mut::().insert( format!("Locking {:?}", self.path.file_name().unwrap()), - task, + Some(task), ); } } @@ -163,7 +163,7 @@ impl Command for DirworldUnlockDoorCommand { }); world.resource_mut::().insert( format!("Unlocking {:?}", self.path.file_name().unwrap()), - task, + Some(task), ); } } diff --git a/src/events.rs b/src/events.rs index 7932a67..042c5e6 100644 --- a/src/events.rs +++ b/src/events.rs @@ -2,33 +2,27 @@ use std::path::PathBuf; use bevy::prelude::*; -/// Events related to activities in the dirworld. -#[derive(Event)] -pub enum DirworldNavigationEvent { - /// Triggered when a room is left. - LeftRoom { - /// Path of room just left. - path: PathBuf, - }, - /// Triggered when a room is entered. - EnteredRoom { - /// Path of room just entered. - path: PathBuf, - }, -} - /// Event called when leaving a room #[derive(Debug, Event, Deref, DerefMut, Clone)] pub struct DirworldLeaveRoom(pub PathBuf); /// Event called when entering a room -#[derive(Debug, Event, Deref, DerefMut, Clone)] -pub struct DirworldEnterRoom(pub PathBuf); +#[derive(Debug, Event, Clone)] +pub struct DirworldEnterRoom { + pub exited: PathBuf, + pub entered: PathBuf, +} /// Event called when changing the world root #[derive(Debug, Event, Deref, DerefMut, Clone)] pub struct DirworldChangeRoot(pub PathBuf); +#[derive(Debug, Event, Clone)] +pub struct DirworldNavigationComplete { + pub from: PathBuf, + pub to: PathBuf, +} + /// Event called to spawn a dirworld entities #[derive(Event, Debug, Deref, DerefMut, Clone, Copy)] pub struct DirworldSpawn(pub Entity); diff --git a/src/lib.rs b/src/lib.rs index 7d0749a..5097f2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,10 +9,10 @@ use bevy::{ecs::system::IntoObserverSystem, prelude::*}; use bevy_mod_scripting::core::{AddScriptApiProvider, AddScriptHost, AddScriptHostHandler, ScriptingPlugin}; use bevy_mod_scripting::lua::LuaScriptHost; use cache::DirworldCache; -use events::{DirworldChangeRoot, DirworldEnterRoom, DirworldLeaveRoom, DirworldSpawn}; +use events::{DirworldChangeRoot, DirworldEnterRoom, DirworldLeaveRoom, DirworldNavigationComplete, DirworldSpawn}; use occule::Codec; use preload::{DirworldPreload, DirworldPreloadPlugin}; -use resources::EntryType; +use resources::{DirworldLastDir, EntryType}; use resources::{ DirworldCodecs, DirworldCurrentDir, DirworldObservers, DirworldRootDir, DirworldTasks, }; @@ -87,6 +87,7 @@ impl Plugin for DirworldPlugin { .init_resource::() .init_resource::() .init_resource::() + .init_resource::() .init_resource::() .init_resource::() .init_resource::() @@ -94,6 +95,7 @@ impl Plugin for DirworldPlugin { .add_event::() .add_event::() .add_event::() + .add_event::() .add_observer(observers::navigate_to_room) .add_observer(observers::handle_changes) .add_observer(observers::change_root) diff --git a/src/observers.rs b/src/observers.rs index bd1d1a6..faa4749 100644 --- a/src/observers.rs +++ b/src/observers.rs @@ -7,9 +7,17 @@ use notify::{ }; use crate::{ - cache::DirworldCache, components::{DirworldEntity, Persist}, events::{DirworldChangeRoot, DirworldEnterRoom, DirworldLeaveRoom}, preload::{load_entity, PreloadState, RoomAssets}, resources::{ - DirworldCodecs, DirworldCurrentDir, DirworldObservers, DirworldRootDir, - }, utils::{despawn_entity_by_path, extract_entity_payload}, DirworldWatcherEvent + cache::DirworldCache, + components::{DirworldEntity, Persist}, + events::{ + DirworldChangeRoot, DirworldEnterRoom, DirworldLeaveRoom, DirworldNavigationComplete, + }, + preload::{load_entity, DirworldPreloadBegin, PreloadState, RoomAssets}, + resources::{ + DirworldCodecs, DirworldCurrentDir, DirworldObservers, DirworldRootDir, DirworldTasks, + }, + utils::{despawn_entity_by_path, extract_entity_payload}, + DirworldWatcherEvent, }; /// On navigation from a room, insert modified payloads into the cache @@ -35,11 +43,16 @@ pub fn navigate_to_room( codecs: Res, mut commands: Commands, mut event_writer: EventWriter, + mut preload_event_writer: EventWriter, mut current_dir: ResMut, mut next_preload_state: ResMut>, mut room_assets: ResMut, + mut dirworld_tasks: ResMut, ) { - let path = &trigger.event().0; + let DirworldEnterRoom { + exited: old_path, + entered: path, + } = &trigger.event(); let room_payload = extract_entity_payload(&path.join(".door"), &codecs).0; *current_dir = DirworldCurrentDir { @@ -60,6 +73,7 @@ pub fn navigate_to_room( root_dir .clone() .map(|root_dir| { + info!("Root: {root_dir:?}, Path: {path:?}"); if root_dir == *path { None } else { @@ -75,6 +89,7 @@ pub fn navigate_to_room( } }; + dirworld_tasks.insert("Loading Room".into(), None); for entry in entries { load_entity( &entry, @@ -86,6 +101,7 @@ pub fn navigate_to_room( &mut room_assets, ); } + preload_event_writer.send(DirworldPreloadBegin { old_path: old_path.clone(), path: path.clone() }); event_writer.send(trigger.event().clone()); } @@ -161,9 +177,12 @@ pub fn change_root( commands.trigger(DirworldLeaveRoom(old_dir.to_path_buf())); }; - let new_root = &trigger.event().0; + let new_root = &trigger.event().0.canonicalize().unwrap(); info!("Changing Root to {}", new_root.display()); **root_dir = Some(new_root.to_path_buf()); - commands.trigger(DirworldEnterRoom(new_root.to_path_buf())); + commands.trigger(DirworldEnterRoom { + exited: new_root.to_path_buf(), + entered: new_root.to_path_buf(), + }); } diff --git a/src/payload/components/mod.rs b/src/payload/components/mod.rs index fc24ac1..2812d73 100644 --- a/src/payload/components/mod.rs +++ b/src/payload/components/mod.rs @@ -9,6 +9,11 @@ use yarnspinner::core::YarnValue; #[derive(Serialize, Deserialize, Clone, Default, Deref, DerefMut, Debug)] pub struct Transform(pub bevy::prelude::Transform); +/// Transform for the destination of a door +/// Used to determine where to spawn ".." door and player when entering a room +#[derive(Serialize, Deserialize, Clone, Default, Deref, DerefMut, Debug)] +pub struct DoorDestination(pub bevy::prelude::Transform); + /// Payload component that represent's an entity's name #[derive(Serialize, Deserialize, Clone, Default, Deref, DerefMut, Debug)] pub struct Name(pub String); diff --git a/src/payload/mod.rs b/src/payload/mod.rs index 68e23a0..3639997 100644 --- a/src/payload/mod.rs +++ b/src/payload/mod.rs @@ -27,6 +27,8 @@ pub struct DirworldEntityPayload { pub relationships: Option, /// Pickup information for this entity pub pickup: Option, + /// Door destination + pub door_destination: Option, } impl DirworldEntityPayload { diff --git a/src/preload/events.rs b/src/preload/events.rs index 7167d73..a6f9d16 100644 --- a/src/preload/events.rs +++ b/src/preload/events.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use bevy::prelude::*; /// Event used to trigger preload callbacks after the asset file has been pre-processed to extract @@ -10,3 +12,8 @@ pub struct DirworldPreload { pub data: Option>, } +#[derive(Debug, Event, Clone)] +pub struct DirworldPreloadBegin { + pub old_path: PathBuf, + pub path: PathBuf, +} diff --git a/src/preload/mod.rs b/src/preload/mod.rs index b90db38..266cb37 100644 --- a/src/preload/mod.rs +++ b/src/preload/mod.rs @@ -13,8 +13,8 @@ mod systems; mod resources; pub use resources::*; -mod events; -pub use events::DirworldPreload; +pub mod events; +pub use events::*; pub(crate) struct DirworldPreloadPlugin; @@ -22,9 +22,17 @@ impl Plugin for DirworldPreloadPlugin { fn build(&self, app: &mut App) { app.add_systems( PostUpdate, - systems::handle_preload.run_if(in_state(PreloadState::Loading)), + ( + systems::cache_preload_paths, + systems::handle_preload.run_if(in_state(PreloadState::Loading)), + ), ) .add_systems(OnEnter(PreloadState::Done), systems::handle_spawn) + .add_event::() + .insert_resource(PreloadPaths { + src: PathBuf::new(), + dst: PathBuf::new(), + }) .init_resource::() .init_state::(); } @@ -52,6 +60,7 @@ pub fn load_entity( preload_state: &mut NextState, room_assets: &mut RoomAssets, ) { + info!("Entity: {entry:?}"); let (mut payload, data) = extract_entity_payload(&entry, &codecs); payload = payload.map(|p| cache.get_entity_cache(&entry).unwrap_or(p)); let entry_type = if entry.is_dir() { @@ -59,13 +68,26 @@ pub fn load_entity( } else { EntryType::File(entry.extensions()) }; - let transform = payload - .as_ref() - .map(|payload| payload.transform.clone()) - .unwrap_or_default(); + let transform = if entry.file_name().is_none() { + payload + .as_ref() + .map(|payload| { + payload + .door_destination + .as_ref() + .expect("Door destination missing from payload!") + .0 + }) + .unwrap_or_default() + } else { + payload + .as_ref() + .map(|payload| payload.transform.0) + .unwrap_or_default() + }; let entity = commands .spawn(( - *transform, + transform, Visibility::Inherited, DirworldEntity { path: entry.clone(), diff --git a/src/preload/resources.rs b/src/preload/resources.rs index 4060c10..9cfec70 100644 --- a/src/preload/resources.rs +++ b/src/preload/resources.rs @@ -5,3 +5,9 @@ use bevy::prelude::*; /// A map of asset handles required by each entry in a room, indexed by their paths #[derive(Resource, Default, Debug, Deref, DerefMut)] pub struct RoomAssets(pub HashMap>); + +#[derive(Resource, Clone)] +pub struct PreloadPaths { + pub src: PathBuf, + pub dst: PathBuf, +} diff --git a/src/preload/systems.rs b/src/preload/systems.rs index ec867ae..330c897 100644 --- a/src/preload/systems.rs +++ b/src/preload/systems.rs @@ -1,13 +1,31 @@ use bevy::prelude::*; -use crate::{components::DirworldEntity, events::DirworldSpawn, resources::{DirworldObservers, EntryType}, Extensions}; +use crate::{ + components::DirworldEntity, + events::{DirworldNavigationComplete, DirworldSpawn}, + resources::{DirworldObservers, DirworldTasks, EntryType}, + Extensions, +}; -use super::{PreloadState, RoomAssets}; +use super::{ + DirworldPreloadBegin, PreloadPaths, PreloadState, RoomAssets, +}; + +pub fn cache_preload_paths( + mut event_reader: EventReader, + mut paths: ResMut, +) { + for DirworldPreloadBegin { old_path, path } in event_reader.read() { + paths.src = old_path.canonicalize().unwrap(); + paths.dst = path.canonicalize().unwrap(); + } +} pub fn handle_preload( asset_server: Res, room_assets: Res, mut next_state: ResMut>, + mut dirworld_tasks: ResMut, ) { if room_assets.is_empty() || room_assets @@ -17,13 +35,16 @@ pub fn handle_preload( { info!("Preload Done."); next_state.set(PreloadState::Done); + dirworld_tasks.remove("Loading Room"); } } pub fn handle_spawn( + preload_paths: Res, dirworld_entity_query: Query<(Entity, &DirworldEntity)>, mut commands: Commands, observers: Res, + mut event_writer: EventWriter, ) { info!("Spawning"); for (entity, DirworldEntity { path, .. }) in dirworld_entity_query.iter() { @@ -37,5 +58,8 @@ pub fn handle_spawn( commands.trigger_targets(DirworldSpawn(entity), observer.clone()); } } + event_writer.send(DirworldNavigationComplete { + from: preload_paths.src.clone(), + to: preload_paths.dst.clone(), + }); } - diff --git a/src/resources.rs b/src/resources.rs index bf0c072..f916deb 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -12,16 +12,19 @@ pub struct DirworldRootDir(pub Option); /// Current directory of the world #[derive(Resource, Default)] -pub struct DirworldCurrentDir{ +pub struct DirworldCurrentDir { /// Path of current directory pub path: PathBuf, /// Payload (contents of .door file) in current directory, if present pub payload: Option, } +#[derive(Resource, Deref, DerefMut, Default, Debug)] +pub struct DirworldLastDir(pub PathBuf); + /// Running background tasks #[derive(Default, Resource, Deref, DerefMut)] -pub struct DirworldTasks(pub BTreeMap>>); +pub struct DirworldTasks(pub BTreeMap>>>); /// A map between file types and their corresponding preload/spawn callback observers #[derive(Debug, Default, Resource, Deref, DerefMut)] @@ -39,4 +42,3 @@ pub enum EntryType { /// A folder Folder, } - diff --git a/src/systems.rs b/src/systems.rs index d6840ee..b95b14f 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -6,13 +6,16 @@ use bevy::{ use crate::resources::DirworldTasks; pub fn remove_completed_tasks(mut commands: Commands, mut tasks: ResMut) { - tasks.retain(|_, task| { - if task.is_finished() { - if let Some(Some(mut command_queue)) = block_on(future::poll_once(&mut *task)) { - commands.append(&mut command_queue); + tasks.retain(|_, task| match task { + Some(task) => { + if task.is_finished() { + if let Some(Some(mut command_queue)) = block_on(future::poll_once(&mut *task)) { + commands.append(&mut command_queue); + } } + !task.is_finished() } - !task.is_finished() + None => true, }); } diff --git a/src/utils.rs b/src/utils.rs index 74451ae..a8ee2b8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,7 +3,8 @@ use std::{fs, path::PathBuf}; use bevy::prelude::*; use crate::{ - components::DirworldEntity, payload::DirworldEntityPayload, resources::DirworldCodecs, Extensions + components::DirworldEntity, payload::DirworldEntityPayload, resources::DirworldCodecs, + Extensions, }; /// Extracts the binary payload from a file @@ -15,7 +16,12 @@ pub fn extract_entity_payload( let mut payload = None; if path.is_dir() { - let payload_file_path = path.join(".door"); + let payload_file_path = if path.file_name().is_none() { + path.parent().expect(".. missing parent somehow").join(".door") + } else { + path.join(".door") + }; + info!("{payload_file_path:?}"); if payload_file_path.exists() { if let Ok(payload_file_data) = fs::read(&payload_file_path) { match rmp_serde::from_slice::(&payload_file_data) { -- cgit v1.2.3