From a1e9304dc31980703446bdb33246e314bafd3b15 Mon Sep 17 00:00:00 2001 From: Silas Bartha Date: Tue, 24 Dec 2024 00:54:31 -0500 Subject: Switched to bevy_mod_scripting, bevy 0.15 update --- Cargo.toml | 25 +++--- src/actor/mod.rs | 3 +- src/cache/mod.rs | 3 + src/cache/resources.rs | 22 +++++ src/commands.rs | 7 +- src/components.rs | 3 + src/conditionals.rs | 22 ++++- src/events.rs | 11 +-- src/lib.rs | 109 ++++++++++++++----------- src/lua_api.rs | 184 ++++++++++++++++++++++++------------------ src/observers.rs | 47 +++++++---- src/payload/components/mod.rs | 20 +++++ src/payload/mod.rs | 13 +++ src/preload/events.rs | 12 +++ src/preload/mod.rs | 82 +++++++++++++++++++ src/preload/resources.rs | 7 ++ src/preload/systems.rs | 41 ++++++++++ src/resources.rs | 13 +-- src/room_generation/mod.rs | 0 src/systems.rs | 26 +++--- src/utils.rs | 39 +-------- src/watcher.rs | 14 +--- 22 files changed, 475 insertions(+), 228 deletions(-) create mode 100644 src/cache/mod.rs create mode 100644 src/cache/resources.rs create mode 100644 src/preload/events.rs create mode 100644 src/preload/mod.rs create mode 100644 src/preload/resources.rs create mode 100644 src/preload/systems.rs delete mode 100644 src/room_generation/mod.rs diff --git a/Cargo.toml b/Cargo.toml index cad6065..b3460b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,9 @@ [package] name = "bevy_dirworld" -version = "0.2.1" +version = "0.4.0" edition = "2021" [dependencies] -anyhow = "1.0" async-channel = "2.3" notify = "7.0" tar = "0.4" @@ -13,10 +12,8 @@ rust-crypto = "0.2" multi_key_map = "0.3" serde = "1.0" rmp-serde = "1.3" -anymap = "0.12" notify-debouncer-full = "0.4" md5 = "0.7" -bevy-async-ecs = "0.6" aes = "0.8" hex = "0.4" hex-literal = "0.4" @@ -24,30 +21,30 @@ uuid = "1.11" lazy_static = "1.5" [dependencies.bevy] -version = "0.14" +version = "0.15" default-features = false -features = ["serialize", "multi_threaded"] +features = ["serialize", "multi_threaded", "bevy_state"] [dependencies.avian3d] -version = "0.1" +version = "0.2" features = ["serialize"] [dependencies.occule] -git = "http://github.com/exvacuum/occule" -branch = "wip" +git = "https://git.exvacuum.dev/occule" +tag = "v0.3.1" [dependencies.yarnspinner] git = "https://github.com/YarnSpinnerTool/YarnSpinner-Rust" optional = true features = ["serde"] -[dependencies.bevy_scriptum] -version = "0.6" -features = ["lua"] +[dependencies.bevy_mod_scripting] +version = "0.8" +features = ["lua54", "lua_script_api"] [dependencies.bevy_basic_interaction] -git = "https://github.com/exvacuum/bevy_basic_interaction.git" -branch = "wip" +git = "https://git.exvacuum.dev/bevy_basic_interaction" +tag = "v0.2.0" [dependencies.strum] version = "0.26" diff --git a/src/actor/mod.rs b/src/actor/mod.rs index 9d0af43..eabf0fe 100644 --- a/src/actor/mod.rs +++ b/src/actor/mod.rs @@ -6,7 +6,7 @@ use std::sync::{Arc, Mutex}; use bevy::{prelude::*, utils::HashMap}; use lazy_static::lazy_static; use resources::FunctionLibrary; -use yarnspinner::{core::{LineId, YarnValue}, runtime::Dialogue}; +use yarnspinner::core::YarnValue; pub mod components; pub mod events; @@ -23,6 +23,7 @@ lazy_static! { /// Plugin which controls the behavior of actors pub struct ActorPlugin { + /// Callback for registering custom yarnspinner functions pub custom_function_registration: Option, } diff --git a/src/cache/mod.rs b/src/cache/mod.rs new file mode 100644 index 0000000..19c3a6f --- /dev/null +++ b/src/cache/mod.rs @@ -0,0 +1,3 @@ +mod resources; +pub use resources::DirworldCache; + diff --git a/src/cache/resources.rs b/src/cache/resources.rs new file mode 100644 index 0000000..6aba61a --- /dev/null +++ b/src/cache/resources.rs @@ -0,0 +1,22 @@ +use std::{collections::HashMap, path::PathBuf}; + +use bevy::prelude::*; + +use crate::{components::DirworldEntity, payload::DirworldEntityPayload}; + +/// Structure containing payload data for cached (non-current) rooms +#[derive(Resource, Default, Debug, Deref, DerefMut)] +pub struct DirworldCache(pub HashMap); + +impl DirworldCache { + /// Stores an entity's payload in the cache, if it exists + pub fn cache_entity(&mut self, dirworld_entity: &DirworldEntity) { + if let Some(payload) = &dirworld_entity.payload { + self.insert(dirworld_entity.path.clone(), payload.clone()); + } + } + + pub fn get_entity_cache(&mut self, path: impl Into) -> Option { + self.remove(&path.into()) + } +} diff --git a/src/commands.rs b/src/commands.rs index 60da108..616f326 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -261,19 +261,20 @@ pub trait DirworldCommands { /// Unlock Door fn dirworld_unlock_door(&mut self, path: PathBuf, key: Vec); + /// Save entity fn dirworld_save_entity(&mut self, path: PathBuf, payload: DirworldEntityPayload); } impl<'w, 's> DirworldCommands for Commands<'w, 's> { fn dirworld_lock_door(&mut self, path: PathBuf, key: Vec) { - self.add(DirworldLockDoorCommand { key, path }); + self.queue(DirworldLockDoorCommand { key, path }); } fn dirworld_unlock_door(&mut self, path: PathBuf, key: Vec) { - self.add(DirworldUnlockDoorCommand { key, path }); + self.queue(DirworldUnlockDoorCommand { key, path }); } fn dirworld_save_entity(&mut self, path: PathBuf, payload: DirworldEntityPayload) { - self.add(DirworldSaveEntityCommand { path, payload }); + self.queue(DirworldSaveEntityCommand { path, payload }); } } diff --git a/src/components.rs b/src/components.rs index 8bb2bff..0a52560 100644 --- a/src/components.rs +++ b/src/components.rs @@ -11,9 +11,12 @@ pub struct Tooltip(pub String); /// A marker component for entities spawned by dirworld handlers, i.e. they should be removed when the room changes. #[derive(Component, Clone, Debug)] pub struct DirworldEntity { + /// Path on filesystem corresponding to this entity pub path: PathBuf, + /// Extracted payload if present pub payload: Option, } +/// Marker component that prevents an entity from despawning on room change #[derive(Debug, Component)] pub struct Persist; diff --git a/src/conditionals.rs b/src/conditionals.rs index 266379a..5362066 100644 --- a/src/conditionals.rs +++ b/src/conditionals.rs @@ -3,44 +3,60 @@ use bevy::{ prelude::{AncestorIter, Entity, Parent, Query, World}, }; use serde::{Deserialize, Serialize}; -use strum::{AsRefStr, EnumString}; +use strum::AsRefStr; use uuid::Uuid; use crate::{components::DirworldEntity, resources::DirworldCurrentDir}; -// I Store Conditions as Enum Data +/// Conditions which can be checked in lua and yarnspinner scripts #[derive(Serialize, Deserialize, AsRefStr, Debug, Default, Clone, PartialEq, Eq)] pub enum Condition { + /// Always true #[default] #[strum(serialize = "Always True")] True, + /// True if `child` is a child of `parent` #[strum(serialize = "Child Of")] ChildOf { + /// Entity that must be child child: Uuid, + /// Entity that must be parent parent: Uuid, }, + /// True if `parent` is the parent of `child` #[strum(serialize = "Parent Of")] ParentOf { + /// Entity that must be parent parent: Uuid, + /// Entity that must be child child: Uuid, }, + /// True if `descendant` is a descendant of `ancestor` #[strum(serialize = "Descendant Of")] DescendantOf { + /// Entity that must be descendant descendant: Uuid, + /// Entity that must be ancestor ancestor: Uuid, }, + /// True if `ancestor` is an ancestor of `descendant` #[strum(serialize = "Ancestor Of")] AncestorOf { + /// Entity that must be ancestor ancestor: Uuid, + /// Entity that must be descendant descendant: Uuid, }, + /// True if current room matches provided id #[strum(serialize = "In Room")] InRoom(Uuid), + /// True if an object with the provided id is in the current room #[strum(serialize = "Object In Room")] ObjectInRoom(Uuid), } impl Condition { + /// Evaluate the condition and return the result pub fn evaluate(&self, world: &mut World) -> bool { match self { Condition::True => true, @@ -59,6 +75,7 @@ impl Condition { } } + /// Get the name of the condition's corresponding function for lua/yarnspinner APIs pub fn get_api_function_name(&self) -> &'static str { match self { Condition::True => "conditional_true", @@ -71,6 +88,7 @@ impl Condition { } } + /// Parses function name and argument strings into the corresponding condition representation pub fn from_api_function_name_and_args(name: &str, args: &[&str]) -> Option { match name { "conditional_true" => Some(Condition::True), diff --git a/src/events.rs b/src/events.rs index 2fa7f81..7932a67 100644 --- a/src/events.rs +++ b/src/events.rs @@ -17,17 +17,18 @@ pub enum DirworldNavigationEvent { }, } +/// 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); +/// Event called when changing the world root #[derive(Debug, Event, Deref, DerefMut, Clone)] pub struct DirworldChangeRoot(pub PathBuf); -#[derive(Event)] -pub struct DirworldSpawn { - pub entity: Entity, - pub data: Option>, -} +/// 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 e6a5f25..7d0749a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,26 +1,23 @@ -// #![warn(missing_docs)] +#![warn(missing_docs)] //! Plugin for bevy engine enabling interaction with and representation of the file system in the world. use std::{ffi::OsStr, path::PathBuf}; use actor::ActorPlugin; -use bevy::render::mesh::ExtrusionBuilder; use bevy::{ecs::system::IntoObserverSystem, prelude::*}; -use bevy_scriptum::{runtimes::lua::LuaRuntime, BuildScriptingRuntime, ScriptingRuntimeBuilder}; -use events::{ - DirworldChangeRoot, DirworldEnterRoom, DirworldLeaveRoom, DirworldNavigationEvent, - DirworldSpawn, -}; +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 occule::Codec; -use resources::DirworldCache; +use preload::{DirworldPreload, DirworldPreloadPlugin}; +use resources::EntryType; use resources::{ DirworldCodecs, DirworldCurrentDir, DirworldObservers, DirworldRootDir, DirworldTasks, - EntryType, }; pub use watcher::DirworldWatcherEvent; pub use watcher::DirworldWatcherSet; -use yarnspinner::core::Library; /// Components used by this plugin pub mod components; @@ -31,15 +28,10 @@ pub mod events; /// Resources used by this plugin pub mod resources; -mod watcher; - /// Commands for this plugin pub mod commands; -mod systems; - -mod observers; - +/// Utility functions pub mod utils; /// Payload for dirworld entities @@ -48,27 +40,37 @@ pub mod payload; /// Actor component pub mod actor; +/// System for dirworld-related condition checking +pub mod conditionals; + +/// Room/asset preloading +pub mod preload; + +mod cache; + +mod yarnspinner_api; + mod lua_api; -pub mod conditionals; +mod systems; -pub mod yarnspinner_api; +mod observers; -pub mod room_generation; +mod watcher; /// Plugin which enables high-level interaction #[derive(Default)] -pub struct DirworldPlugin { - pub register_custom_lua_api: - Option) + Send + Sync>>, -} +pub struct DirworldPlugin; impl Plugin for DirworldPlugin { fn build(&self, app: &mut App) { - info!("building"); - app.add_plugins(ActorPlugin { - custom_function_registration: Some(yarnspinner_api::setup_yarnspinner_functions), - }) + app.add_plugins(( + ActorPlugin { + custom_function_registration: Some(yarnspinner_api::setup_yarnspinner_functions), + }, + DirworldPreloadPlugin, + ScriptingPlugin, + )) .add_systems(Startup, watcher::setup) .add_systems( Update, @@ -78,18 +80,12 @@ impl Plugin for DirworldPlugin { yarnspinner_api::process_commands, ), ) - .add_systems( - PostUpdate, - watcher::update, - ) - .add_scripting::(|runtime| { - let runtime = lua_api::register(runtime); - if let Some(register_custom) = &self.register_custom_lua_api { - (register_custom)(runtime); - } - }) - .init_resource::() + .add_script_host::>(PostUpdate) + .add_script_handler::, 0, 0>(PostUpdate) + .add_api_provider::>(Box::new(lua_api::ConditionalAPI)) + .add_systems(PostUpdate, watcher::update) .init_resource::() + .init_resource::() .init_resource::() .init_resource::() .init_resource::() @@ -98,18 +94,22 @@ impl Plugin for DirworldPlugin { .add_event::() .add_event::() .add_event::() - .observe(observers::navigate_to_room) - .observe(observers::handle_changes) - .observe(observers::change_root) - .observe(observers::navigate_from_room); + .add_observer(observers::navigate_to_room) + .add_observer(observers::handle_changes) + .add_observer(observers::change_root) + .add_observer(observers::navigate_from_room); } } +/// Extension trait for working with multiple file extensions on paths pub trait Extensions { + /// Get all the extensions on this path if applicable fn extensions(&self) -> Option; + /// Gets the file stem (without any extensions) of this path if applicable fn file_stem_no_extensions(&self) -> Option; + /// Gets the path with any extensions removed fn no_extensions(&self) -> PathBuf; } @@ -148,13 +148,20 @@ impl Extensions for PathBuf { } } +/// Extension trait providing functions for registering callbacks and codecs for filesystem entries pub trait DirworldApp { - fn register_dirworld_entry_callback( + /// Register callbacks to be executed when a file with given [`EntryType`]s is loaded. The + /// `preload_callback` parameter controls loading assets and is called before spawning any + /// entities in the room, and the `spawn_callback` handles initializing the spawned entities. + fn register_dirworld_entry_callbacks( &mut self, extensions: Vec, - observer: impl IntoObserverSystem, + preload_callback: Option>, + spawn_callback: impl IntoObserverSystem, ) -> &mut Self; + /// Register a [`Codec`] to be used to extract [`crate::payload::DirworldEntityPayload`]s from + /// files with matching extensions. fn register_dirworld_entry_codec( &mut self, extensions: Vec, @@ -163,10 +170,11 @@ pub trait DirworldApp { } impl DirworldApp for App { - fn register_dirworld_entry_callback( + fn register_dirworld_entry_callbacks( &mut self, extensions: Vec, - observer: impl IntoObserverSystem, + preload_callback: Option>, + spawn_observer: impl IntoObserverSystem, ) -> &mut Self { let world = self.world_mut(); let observer_entity_id; @@ -174,7 +182,14 @@ impl DirworldApp for App { { let mut observer_entity = world.spawn_empty(); observer_entity_id = observer_entity.id(); - observer_entity.insert(Observer::new(observer).with_entity(observer_entity_id)); + if let Some(preload_callback) = preload_callback { + observer_entity.with_children(|parent| { + parent.spawn(Observer::new(preload_callback).with_entity(observer_entity_id)); + }); + } + observer_entity.with_children(|parent| { + parent.spawn(Observer::new(spawn_observer).with_entity(observer_entity_id)); + }); } world.flush(); diff --git a/src/lua_api.rs b/src/lua_api.rs index f9a34d4..53252a5 100644 --- a/src/lua_api.rs +++ b/src/lua_api.rs @@ -1,25 +1,21 @@ -use std::str::FromStr; +use std::{str::FromStr, sync::Mutex}; use bevy::prelude::*; -use bevy_scriptum::{ - runtimes::lua::{BevyEntity, BevyVec3, LuaRuntime, LuaScriptData}, - Runtime, ScriptingRuntimeBuilder, -}; +use bevy_mod_scripting::api::providers::bevy_reflect::LuaVec3; +use bevy_mod_scripting::{api::providers::bevy_ecs::LuaEntity, lua::tealr::mlu::mlua::Error as LuaError}; +use bevy_mod_scripting::lua::LuaEvent; +use bevy_mod_scripting::prelude::*; use uuid::Uuid; use crate::{components::DirworldEntity, conditionals::Condition}; -pub fn trigger_update( - mut scripted_entities: Query<(Entity, &mut LuaScriptData)>, - scripting_runtime: Res, - time: Res