diff --git a/.github/workflows/bevy_mod_scripting.yml b/.github/workflows/bevy_mod_scripting.yml index e47b23c42d..f4f74b82d4 100644 --- a/.github/workflows/bevy_mod_scripting.yml +++ b/.github/workflows/bevy_mod_scripting.yml @@ -1,5 +1,12 @@ on: push: + branches: + - main + - staging + paths-ignore: + - '.github/workflows/release-plz.yml' + - 'docs/**' + pull_request: branches: - "**" paths-ignore: diff --git a/.github/workflows/mdbook.yml b/.github/workflows/mdbook.yml index 2a9dac9579..d81310efbe 100644 --- a/.github/workflows/mdbook.yml +++ b/.github/workflows/mdbook.yml @@ -2,6 +2,13 @@ name: Deploy mdBook to GitHub Pages on: push: + branches: + - master + - staging + paths: + - 'docs/**' + - '.github/workflows/mdbook.yml' + pull_request: branches: - "**" paths: diff --git a/Cargo.toml b/Cargo.toml index 76e5e34041..f272838a65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,14 +10,14 @@ homepage = "https://github.com/makspll/bevy_mod_scripting" keywords = ["bevy", "gamedev", "scripting", "lua"] categories = ["game-development"] readme = "readme.md" -include = ["readme.md", "/src", "/examples", "/assets", "LICENSE"] +include = ["readme.md", "/src", "/examples", "/assets", "LICENSE", "/badges"] [lib] name = "bevy_mod_scripting" path = "src/lib.rs" [package.metadata."docs.rs"] -features = ["lua54"] +features = ["lua54", "rhai"] [features] default = ["core_functions", "bevy_bindings"] @@ -58,7 +58,7 @@ bevy_mod_scripting_rhai = { path = "crates/languages/bevy_mod_scripting_rhai", v # bevy_mod_scripting_rune = { path = "crates/languages/bevy_mod_scripting_rune", version = "0.9.0-alpha.2", optional = true } bevy_mod_scripting_functions = { workspace = true } [workspace.dependencies] -profiling = {version = "1.0" } +profiling = { version = "1.0" } bevy = { version = "0.15.0", default-features = false } bevy_mod_scripting_core = { path = "crates/bevy_mod_scripting_core", version = "0.9.0-alpha.9" } bevy_mod_scripting_functions = { path = "crates/bevy_mod_scripting_functions", version = "0.9.0-alpha.9", default-features = false } @@ -127,3 +127,6 @@ panic = "deny" unwrap_used = "deny" expect_used = "deny" todo = "deny" + +[workspace.lints.rust] +missing_docs = "deny" diff --git a/crates/bevy_mod_scripting_core/src/asset.rs b/crates/bevy_mod_scripting_core/src/asset.rs index 19ad3c8822..990539c4fa 100644 --- a/crates/bevy_mod_scripting_core/src/asset.rs +++ b/crates/bevy_mod_scripting_core/src/asset.rs @@ -1,3 +1,5 @@ +//! Systems and resources for handling script assets and events + use crate::{ commands::{CreateOrUpdateScript, DeleteScript}, error::ScriptError, @@ -6,7 +8,7 @@ use crate::{ }; use bevy::{ app::{App, PreUpdate}, - asset::{Asset, AssetEvent, AssetId, AssetLoader, Assets}, + asset::{Asset, AssetEvent, AssetId, AssetLoader, AssetPath, Assets}, ecs::system::Resource, log::{debug, error, info, trace}, prelude::{ @@ -16,17 +18,18 @@ use bevy::{ reflect::TypePath, utils::HashMap, }; -use std::{ - borrow::Cow, - path::{Path, PathBuf}, -}; +use std::borrow::Cow; /// Represents a scripting language. Languages which compile into another language should use the target language as their language. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum Language { + /// The Rhai scripting language Rhai, + /// The Lua scripting language Lua, + /// The Rune scripting language Rune, + /// An external scripting language External(Cow<'static, str>), /// Set if none of the asset path to language mappers match Unknown, @@ -47,9 +50,10 @@ impl std::fmt::Display for Language { /// Represents a script loaded into memory as an asset #[derive(Asset, TypePath, Clone)] pub struct ScriptAsset { + /// The body of the script pub content: Box<[u8]>, /// The virtual filesystem path of the asset, used to map to the script Id for asset backed scripts - pub asset_path: PathBuf, + pub asset_path: AssetPath<'static>, } #[derive(Event, Debug, Clone)] @@ -60,6 +64,7 @@ pub(crate) enum ScriptAssetEvent { } #[derive(Default)] +/// A loader for script assets pub struct ScriptAssetLoader { /// The file extensions this loader should handle pub extensions: &'static [&'static str], @@ -90,7 +95,7 @@ impl AssetLoader for ScriptAssetLoader { } let asset = ScriptAsset { content: content.into_boxed_slice(), - asset_path: load_context.path().to_owned(), + asset_path: load_context.asset_path().to_owned(), }; Ok(asset) } @@ -101,13 +106,17 @@ impl AssetLoader for ScriptAssetLoader { } #[derive(Clone, Resource)] +/// Settings to do with script assets and how they are handled pub struct ScriptAssetSettings { + /// Strategy for mapping asset paths to script ids, by default this is the identity function pub script_id_mapper: AssetPathToScriptIdMapper, + /// Strategies for mapping asset paths to languages pub script_language_mappers: Vec, } impl ScriptAssetSettings { - pub fn select_script_language(&self, path: &Path) -> Language { + /// Selects the language for a given asset path + pub fn select_script_language(&self, path: &AssetPath) -> Language { for mapper in &self.script_language_mappers { let language = (mapper.map)(path); match language { @@ -124,7 +133,7 @@ impl Default for ScriptAssetSettings { fn default() -> Self { Self { script_id_mapper: AssetPathToScriptIdMapper { - map: (|path: &Path| path.to_string_lossy().into_owned().into()), + map: (|path: &AssetPath| path.path().to_string_lossy().into_owned().into()), }, script_language_mappers: vec![], } @@ -134,41 +143,53 @@ impl Default for ScriptAssetSettings { /// Strategy for mapping asset paths to script ids, by default this is the identity function #[derive(Clone, Copy)] pub struct AssetPathToScriptIdMapper { - pub map: fn(&Path) -> ScriptId, + /// The mapping function + pub map: fn(&AssetPath) -> ScriptId, } #[derive(Clone, Copy)] +/// Strategy for mapping asset paths to languages pub struct AssetPathToLanguageMapper { - pub map: fn(&Path) -> Language, + /// The mapping function + pub map: fn(&AssetPath) -> Language, } /// A cache of asset id's to their script id's. Necessary since when we drop an asset we won't have the ability to get the path from the asset. #[derive(Default, Debug, Resource)] pub struct ScriptMetadataStore { + /// The map of asset id's to their metadata pub map: HashMap, ScriptMetadata>, } #[derive(Debug, Clone, PartialEq, Eq)] +/// Metadata for a script asset pub struct ScriptMetadata { + /// The asset id of the script pub asset_id: AssetId, + /// The script id of the script pub script_id: ScriptId, + /// The language of the script pub language: Language, } impl ScriptMetadataStore { + /// Inserts a new metadata entry pub fn insert(&mut self, id: AssetId, meta: ScriptMetadata) { // TODO: new generations of assets are not going to have the same ID as the old one self.map.insert(id, meta); } + /// Gets a metadata entry pub fn get(&self, id: AssetId) -> Option<&ScriptMetadata> { self.map.get(&id) } + /// Removes a metadata entry pub fn remove(&mut self, id: AssetId) -> Option { self.map.remove(&id) } + /// Checks if the store contains a metadata entry pub fn contains(&self, id: AssetId) -> bool { self.map.contains_key(&id) } @@ -333,6 +354,8 @@ pub(crate) fn configure_asset_systems_for_plugin( #[cfg(test)] mod tests { + use std::path::{Path, PathBuf}; + use bevy::{ app::{App, Update}, asset::{AssetApp, AssetPlugin, AssetServer, Assets, Handle, LoadState}, @@ -352,12 +375,12 @@ mod tests { fn make_test_settings() -> ScriptAssetSettings { ScriptAssetSettings { script_id_mapper: AssetPathToScriptIdMapper { - map: |path| path.to_string_lossy().into_owned().into(), + map: |path| path.path().to_string_lossy().into_owned().into(), }, script_language_mappers: vec![ AssetPathToLanguageMapper { map: |path| { - if path.extension().unwrap() == "lua" { + if path.path().extension().unwrap() == "lua" { Language::Lua } else { Language::Unknown @@ -366,7 +389,7 @@ mod tests { }, AssetPathToLanguageMapper { map: |path| { - if path.extension().unwrap() == "rhai" { + if path.path().extension().unwrap() == "rhai" { Language::Rhai } else { Language::Unknown @@ -427,7 +450,7 @@ mod tests { assert_eq!( asset.asset_path, - PathBuf::from("test_assets/test_script.script") + AssetPath::from_path(&PathBuf::from("test_assets/test_script.script")) ); assert_eq!( @@ -457,7 +480,7 @@ mod tests { assert_eq!( asset.asset_path, - PathBuf::from("test_assets/test_script.script") + AssetPath::from(PathBuf::from("test_assets/test_script.script")) ); assert_eq!( String::from_utf8(asset.content.clone().to_vec()).unwrap(), @@ -485,14 +508,14 @@ mod tests { fn test_script_asset_settings_select_language() { let settings = make_test_settings(); - let path = Path::new("test.lua"); - assert_eq!(settings.select_script_language(path), Language::Lua); + let path = AssetPath::from(Path::new("test.lua")); + assert_eq!(settings.select_script_language(&path), Language::Lua); assert_eq!( - settings.select_script_language(Path::new("test.rhai")), + settings.select_script_language(&AssetPath::from(Path::new("test.rhai"))), Language::Rhai ); assert_eq!( - settings.select_script_language(Path::new("test.blob")), + settings.select_script_language(&AssetPath::from(Path::new("test.blob"))), Language::Unknown ); } diff --git a/crates/bevy_mod_scripting_core/src/bindings/access_map.rs b/crates/bevy_mod_scripting_core/src/bindings/access_map.rs index 18c81da88e..5a2e149966 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/access_map.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/access_map.rs @@ -1,3 +1,4 @@ +//! A map of access claims used to safely and dynamically access the world. use std::thread::ThreadId; use bevy::{ @@ -13,12 +14,14 @@ use crate::error::InteropError; use super::{ReflectAllocationId, ReflectBase}; #[derive(Debug, Clone, PartialEq, Eq)] +/// An owner of an access claim and the code location of the claim. pub struct ClaimOwner { id: ThreadId, location: std::panic::Location<'static>, } #[derive(Debug, Clone, PartialEq, Eq)] +/// A count of the number of readers and writers of an access claim. pub struct AccessCount { /// The number of readers including thread information read_by: SmallVec<[ClaimOwner; 1]>, @@ -81,8 +84,11 @@ impl AccessMapKey for u64 { /// Describes kinds of base value we are accessing via reflection #[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)] pub enum ReflectAccessKind { + /// Accessing a component or resource ComponentOrResource, + /// Accessing an owned value Allocation, + /// Accessing the world Global, } @@ -129,6 +135,7 @@ impl AccessMapKey for ReflectAccessId { } impl ReflectAccessId { + /// Creates a new access id for the global world pub fn for_global() -> Self { Self { kind: ReflectAccessKind::Global, @@ -136,6 +143,7 @@ impl ReflectAccessId { } } + /// Creates a new access id for a resource pub fn for_resource(cell: &UnsafeWorldCell) -> Result { let resource_id = cell.components().resource_id::().ok_or_else(|| { InteropError::unregistered_component_or_resource_type(std::any::type_name::()) @@ -147,6 +155,7 @@ impl ReflectAccessId { }) } + /// Creates a new access id for a component pub fn for_component( cell: &UnsafeWorldCell, ) -> Result { @@ -157,6 +166,7 @@ impl ReflectAccessId { Ok(Self::for_component_id(component_id)) } + /// Creates a new access id for a component id pub fn for_allocation(id: ReflectAllocationId) -> Self { Self { kind: ReflectAccessKind::Allocation, @@ -164,6 +174,7 @@ impl ReflectAccessId { } } + /// Creates a new access id for a component id pub fn for_component_id(id: ComponentId) -> Self { Self { kind: ReflectAccessKind::ComponentOrResource, @@ -171,6 +182,7 @@ impl ReflectAccessId { } } + /// Creates a new access id for a reference pub fn for_reference(base: ReflectBase) -> Self { match base { ReflectBase::Resource(id) => Self::for_component_id(id), @@ -211,17 +223,21 @@ impl From for ReflectAllocationId { } #[derive(Debug, Default)] +/// A map of access claims pub struct AccessMap { individual_accesses: DashMap, global_lock: RwLock, } + #[profiling::all_functions] impl AccessMap { + /// Checks if the map is locked exclusively pub fn is_locked_exclusively(&self) -> bool { let global_lock = self.global_lock.read(); !global_lock.can_write() } + /// retrieves the location of the global lock if any pub fn global_access_location(&self) -> Option> { let global_lock = self.global_lock.read(); global_lock.as_location() @@ -325,6 +341,7 @@ impl AccessMap { } } + /// Lists all accesses pub fn list_accesses(&self) -> Vec<(K, AccessCount)> { self.individual_accesses .iter() @@ -332,15 +349,18 @@ impl AccessMap { .collect() } + /// Counts the number of accesses pub fn count_accesses(&self) -> usize { self.individual_accesses.len() } + /// Releases all accesses pub fn release_all_accesses(&self) { self.individual_accesses.clear(); self.release_global_access(); } + /// Accesses the location of a key pub fn access_location( &self, key: K, @@ -355,6 +375,7 @@ impl AccessMap { .and_then(|access| access.as_location()) } + /// Accesses the location of the first access pub fn access_first_location(&self) -> Option> { self.individual_accesses .iter() @@ -362,7 +383,9 @@ impl AccessMap { } } +/// A trait for displaying a code location nicely pub trait DisplayCodeLocation { + /// Displays the location fn display_location(self) -> String; } @@ -380,6 +403,7 @@ impl DisplayCodeLocation for Option> { } #[macro_export] +/// A macro for claiming access to a value for reading macro_rules! with_access_read { ($access_map:expr, $id:expr, $msg:expr, $body:block) => {{ if !$access_map.claim_read_access($id) { @@ -397,6 +421,7 @@ macro_rules! with_access_read { } #[macro_export] +/// A macro for claiming access to a value for writing macro_rules! with_access_write { ($access_map:expr, $id:expr, $msg:expr, $body:block) => { if !$access_map.claim_write_access($id) { @@ -414,6 +439,7 @@ macro_rules! with_access_write { } #[macro_export] +/// A macro for claiming global access macro_rules! with_global_access { ($access_map:expr, $msg:expr, $body:block) => { if !$access_map.claim_global_access() { diff --git a/crates/bevy_mod_scripting_core/src/bindings/allocator.rs b/crates/bevy_mod_scripting_core/src/bindings/allocator.rs index 269ccf26e4..0f0943381f 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/allocator.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/allocator.rs @@ -1,7 +1,8 @@ +//! An allocator used to control the lifetime of allocations + use bevy::{ecs::system::Resource, prelude::ResMut, reflect::PartialReflect}; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use std::{ - any::TypeId, cell::UnsafeCell, cmp::Ordering, collections::HashMap, @@ -11,8 +12,10 @@ use std::{ }; #[derive(Clone, Debug)] +/// Unique identifier for an allocation pub struct ReflectAllocationId(pub(crate) Arc); impl ReflectAllocationId { + /// Returns the id of the allocation pub fn id(&self) -> u64 { *self.0 } @@ -22,6 +25,7 @@ impl ReflectAllocationId { Self(Arc::new(id)) } + /// Returns the number of strong references to this id pub fn strong_count(&self) -> usize { Arc::strong_count(&self.0) } @@ -79,16 +83,19 @@ impl Drop for OwningPtr { // yikes, the indirection. I need this to store boxed values too though #[derive(Debug)] +/// A boxed [`PartialReflect`] value pub struct ReflectAllocation(Box>); // unsafe impl Send for ReflectAllocation {} unsafe impl Sync for ReflectAllocation {} impl ReflectAllocation { + /// Returns a pointer to the [`PartialReflect`] value pub fn get_ptr(&self) -> *mut dyn PartialReflect { self.0.as_ref().get() } + /// Creates a new [`ReflectAllocation`] from a boxed [`PartialReflect`] value pub fn new(value: Box) -> Self { let value: Box> = unsafe { std::mem::transmute(value) }; Self(value) @@ -124,10 +131,12 @@ impl Default for AppReflectAllocator { } impl AppReflectAllocator { + /// claim a read lock on the allocator pub fn read(&self) -> RwLockReadGuard { self.allocator.read() } + /// claim a write lock on the allocator pub fn write(&self) -> RwLockWriteGuard { self.allocator.write() } @@ -139,8 +148,8 @@ impl AppReflectAllocator { pub struct ReflectAllocator { // TODO: experiment with object pools, sparse set etc. allocations: HashMap, - types: HashMap, } + #[profiling::all_functions] impl ReflectAllocator { /// Allocates a new [`Reflect`] value and returns an [`AllocationId`] which can be used to access it later. @@ -149,20 +158,18 @@ impl ReflectAllocator { self.allocate_boxed(Box::new(value)) } + /// Allocates a new boxed [`PartialReflect`] value and returns an [`AllocationId`] which can be used to access it later. pub fn allocate_boxed(&mut self, value: Box) -> ReflectAllocationId { static COUNTER: AtomicU64 = AtomicU64::new(0); - let type_id = value.get_represented_type_info().map(|i| i.type_id()); let id = ReflectAllocationId::new(COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)); - let index = id.id(); let value = ReflectAllocation::new(value); self.allocations.insert(id.clone(), value); - if let Some(type_id) = type_id { - self.types.insert(index, type_id); - } id } + + /// Insert a value into the allocator with a given id pub fn insert( &mut self, id: ReflectAllocationId, @@ -171,18 +178,17 @@ impl ReflectAllocator { self.allocations.insert(id, value) } + /// Remove a value from the allocator with a given id pub fn remove(&mut self, id: &ReflectAllocationId) -> Option { self.allocations.remove(id) } - pub fn get_type_id(&self, id: &ReflectAllocationId) -> Option { - self.types.get(&id.id()).cloned() - } - + /// Get the type id of a value with a given id pub fn get_mut(&mut self, id: &ReflectAllocationId) -> Option<&mut ReflectAllocation> { self.allocations.get_mut(id) } + /// Get the reflect value with a given id pub fn get(&self, id: &ReflectAllocationId) -> Option<&ReflectAllocation> { self.allocations.get(id) } @@ -198,6 +204,7 @@ impl ReflectAllocator { self.allocations.retain(|k, _| Arc::strong_count(&k.0) > 1); } + /// Returns an iterator over all allocations pub fn iter_allocations( &self, ) -> impl Iterator { diff --git a/crates/bevy_mod_scripting_core/src/bindings/function/arg_meta.rs b/crates/bevy_mod_scripting_core/src/bindings/function/arg_meta.rs index a5cee5abe8..a8ca08cee4 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/function/arg_meta.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/function/arg_meta.rs @@ -21,6 +21,7 @@ impl ScriptReturn for T {} /// Describes an argument to a script function. Provides necessary information for the function to handle dispatch. pub trait ArgMeta { + /// The default value for the argument. Used when the argument is not provided. fn default_value() -> Option { None } diff --git a/crates/bevy_mod_scripting_core/src/bindings/function/from.rs b/crates/bevy_mod_scripting_core/src/bindings/function/from.rs index efd28d2dc9..ebb9ec5237 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/function/from.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/function/from.rs @@ -1,3 +1,5 @@ +//! This module contains the [`FromScript`] trait and its implemenations. + use crate::{ bindings::{access_map::ReflectAccessId, ReflectReference, WorldGuard}, error::InteropError, @@ -18,7 +20,10 @@ use super::script_function::{DynamicScriptFunction, DynamicScriptFunctionMut}; /// The [`FromScript::This`] associated type is used to allow for the implementation of this trait to return /// a type with the lifetime of the world guard. In 99% cases you can just use `Self` as the associated type. pub trait FromScript { + /// The type that is constructed from the script value. type This<'w>; + + /// Construct a value of type `T` from a [`ScriptValue`]. fn from_script( value: ScriptValue, world: WorldGuard<'_>, @@ -150,10 +155,12 @@ impl FromScript for ReflectReference { pub struct Val(pub T); impl Val { + /// Create a new `Val` with the given value. pub fn new(value: T) -> Self { Val(value) } + /// Unwrap the value from the `Val`. pub fn into_inner(self) -> T { self.0 } diff --git a/crates/bevy_mod_scripting_core/src/bindings/function/from_ref.rs b/crates/bevy_mod_scripting_core/src/bindings/function/from_ref.rs index af23175fa3..61c7130fd0 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/function/from_ref.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/function/from_ref.rs @@ -1,3 +1,5 @@ +//! Contains the [`FromScriptRef`] trait and its implementations. + use std::{any::TypeId, ffi::OsString, path::PathBuf}; use bevy::reflect::{DynamicEnum, DynamicList, DynamicTuple, DynamicVariant, PartialReflect}; @@ -14,6 +16,7 @@ use crate::{ /// /// Type Erased version of [`super::from::FromScript`]. pub trait FromScriptRef { + /// Converts a [`ScriptValue`] to a value equivalent to the given [`TypeId`]. fn from_script_ref( target: TypeId, value: ScriptValue, diff --git a/crates/bevy_mod_scripting_core/src/bindings/function/into.rs b/crates/bevy_mod_scripting_core/src/bindings/function/into.rs index fac522c515..4f50fd492f 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/function/into.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/function/into.rs @@ -1,3 +1,5 @@ +//! Implementations of the [`IntoScript`] trait for various types. + use std::{borrow::Cow, collections::HashMap, ffi::OsString, path::PathBuf}; use bevy::reflect::Reflect; @@ -13,7 +15,9 @@ use super::{ script_function::{DynamicScriptFunction, DynamicScriptFunctionMut}, }; +/// Converts a value into a [`ScriptValue`]. pub trait IntoScript { + /// Convert this value into a [`ScriptValue`]. fn into_script(self, world: WorldGuard) -> Result; } diff --git a/crates/bevy_mod_scripting_core/src/bindings/function/into_ref.rs b/crates/bevy_mod_scripting_core/src/bindings/function/into_ref.rs index eb2496460e..cda4eb4640 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/function/into_ref.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/function/into_ref.rs @@ -1,3 +1,5 @@ +//! Contains the [`IntoScriptRef`] trait and its implementations. + use std::{borrow::Cow, ffi::OsString, path::PathBuf}; use bevy::reflect::{Access, PartialReflect}; @@ -17,6 +19,7 @@ use crate::{ /// - Primitives are converted to simple values /// - Container types are converted to references (so the references persist after accesses inside them) pub trait IntoScriptRef { + /// Converts a value represented by a reference into a [`crate::bindings::function::ScriptValue`]. fn into_script_ref( self_: ReflectReference, world: WorldGuard, @@ -24,6 +27,7 @@ pub trait IntoScriptRef { } #[macro_export] +/// a utility for matching types by their [`std::any::TypeId`] macro_rules! match_by_type { (match $on:ident {$($id:ident : $ty:ty => $conv:expr),*}) => { $( @@ -41,6 +45,7 @@ macro_rules! match_by_type { } #[macro_export] +/// Downcasts a reference into a value of a given type or returns an error if the downcast fails. macro_rules! downcast_into_value { ($r:ident, $ty:ty) => { *$r.try_downcast_ref::<$ty>().ok_or_else(|| { diff --git a/crates/bevy_mod_scripting_core/src/bindings/function/mod.rs b/crates/bevy_mod_scripting_core/src/bindings/function/mod.rs index 5046157a0f..f7d3b4ff7d 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/function/mod.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/function/mod.rs @@ -1,3 +1,5 @@ +//! Abstractions to do with dynamic script functions + pub mod arg_meta; pub mod from; pub mod from_ref; diff --git a/crates/bevy_mod_scripting_core/src/bindings/function/namespace.rs b/crates/bevy_mod_scripting_core/src/bindings/function/namespace.rs index 44eaf649f9..e255798688 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/function/namespace.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/function/namespace.rs @@ -1,7 +1,7 @@ +//! A module for managing namespaces for functions + use crate::{ - bindings::function::script_function::{ - AppScriptFunctionRegistry, DynamicScriptFunction, ScriptFunction, - }, + bindings::function::script_function::{AppScriptFunctionRegistry, ScriptFunction}, docgen::info::GetFunctionInfo, }; use bevy::{ @@ -12,52 +12,8 @@ use std::{any::TypeId, borrow::Cow, marker::PhantomData}; use super::type_dependencies::GetFunctionTypeDependencies; -pub trait RegisterNamespacedFunction { - fn register_namespaced_function(&mut self, name: N, function: F) - where - N: Into>, - S: IntoNamespace, - F: ScriptFunction<'static, M>; -} - -pub trait GetNamespacedFunction { - fn iter_overloads_namespaced( - &self, - name: N, - namespace: Namespace, - ) -> impl Iterator - where - N: Into>; - fn get_namespaced_function( - &self, - name: N, - namespace: Namespace, - ) -> Option<&DynamicScriptFunction> - where - N: Into>; - - fn get_namespaced_function_typed(&self, name: N) -> Option<&DynamicScriptFunction> - where - N: Into>, - NS: IntoNamespace, - { - Self::get_namespaced_function(self, name, NS::into_namespace()) - } - - fn has_namespaced_function(&self, name: N, namespace: Namespace) -> bool - where - N: Into>; - - fn has_namespaced_function_typed(&self, name: N) -> bool - where - N: Into>, - NS: IntoNamespace, - { - Self::has_namespaced_function(self, name, NS::into_namespace()) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Reflect)] +/// A namespace for functions pub enum Namespace { /// The function is registered in the global namespace, i.e. with no namespace. /// In practice functions in this namespace should be callable directly by their name, i.e. `my_function()` @@ -71,7 +27,9 @@ pub enum Namespace { /// A type which implements [`IntoNamespace`] by always converting to the global namespace pub struct GlobalNamespace; +/// A type convertible to a [`Namespace`] pub trait IntoNamespace { + /// Converts this type into a [`Namespace`] fn into_namespace() -> Namespace; } @@ -85,7 +43,9 @@ impl IntoNamespace for T { } } +/// A type which implements [`IntoNamespace`] by always converting to the global namespace impl Namespace { + /// Returns the prefix for this namespace pub fn prefix(self) -> Cow<'static, str> { match self { Namespace::Global => Cow::Borrowed(""), @@ -102,8 +62,11 @@ impl Namespace { } } +/// A convenience builder for registering multiple functions in a namespace pub struct NamespaceBuilder<'a, N> { + /// phantom data to reference the namespace type namespace: PhantomData, + /// a cached reference to the world pub world: &'a mut World, } @@ -133,6 +96,7 @@ impl<'a, S: IntoNamespace> NamespaceBuilder<'a, S> { } } + /// Registers a function in the namespace pub fn register<'env, N, F, M>(&mut self, name: N, function: F) -> &mut Self where N: Into>, diff --git a/crates/bevy_mod_scripting_core/src/bindings/function/script_function.rs b/crates/bevy_mod_scripting_core/src/bindings/function/script_function.rs index 890f340d41..635c2c08f8 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/function/script_function.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/function/script_function.rs @@ -1,3 +1,5 @@ +//! Implementations of the [`ScriptFunction`] and [`ScriptFunctionMut`] traits for functions with up to 13 arguments. + use super::{from::FromScript, into::IntoScript, namespace::Namespace}; use crate::bindings::function::arg_meta::ArgMeta; use crate::docgen::info::{FunctionInfo, GetFunctionInfo}; @@ -21,7 +23,9 @@ use std::sync::Arc; message = "This function does not fulfil the requirements to be a script callable function. All arguments must implement the ScriptArgument trait and all return values must implement the ScriptReturn trait", note = "If you're trying to return a non-primitive type, you might need to use Val Ref or Mut wrappers" )] +/// A trait implemented by functions which can act as dynamic script functions, which can then be registered against a [`ScriptFunctionRegistry`]. pub trait ScriptFunction<'env, Marker> { + /// Convert this function into a [`DynamicScriptFunction`] fn into_dynamic_script_function(self) -> DynamicScriptFunction; } @@ -29,7 +33,9 @@ pub trait ScriptFunction<'env, Marker> { message = "Only functions with all arguments impplementing FromScript and return values supporting IntoScript are supported. Registering functions also requires they implement GetTypeDependencies", note = "If you're trying to return a non-primitive type, you might need to use Val Ref or Mut wrappers" )] +/// A trait implemented by functions which can act as mutable dynamic script functions. pub trait ScriptFunctionMut<'env, Marker> { + /// Convert this function into a [`DynamicScriptFunctionMut`] fn into_dynamic_script_function_mut(self) -> DynamicScriptFunctionMut; } @@ -38,9 +44,11 @@ pub trait ScriptFunctionMut<'env, Marker> { #[derive(Clone, Copy, Debug, Reflect, Default)] #[reflect(opaque)] pub struct FunctionCallContext { + /// Whether the caller uses 1-indexing on all indexes and expects 0-indexing conversions to be performed. pub convert_to_0_indexed: bool, } impl FunctionCallContext { + /// Create a new FunctionCallContext with the given 1-indexing conversion preference pub fn new(convert_to_0_indexed: bool) -> Self { Self { convert_to_0_indexed, @@ -53,10 +61,11 @@ impl FunctionCallContext { } } -/// The Script Function equivalent for dynamic functions. Currently unused #[derive(Clone, Reflect)] #[reflect(opaque)] +/// A dynamic script function. pub struct DynamicScriptFunction { + /// The meta information about the function pub info: FunctionInfo, // TODO: info about the function, this is hard right now because of non 'static lifetimes in wrappers, we can't use TypePath etc func: Arc< @@ -72,7 +81,9 @@ impl PartialEq for DynamicScriptFunction { #[derive(Clone, Reflect)] #[reflect(opaque)] +/// A dynamic mutable script function. pub struct DynamicScriptFunctionMut { + /// The meta information about the function pub info: FunctionInfo, func: Arc< RwLock< @@ -114,14 +125,17 @@ impl DynamicScriptFunction { } } + /// Get the name of the function pub fn name(&self) -> &Cow<'static, str> { &self.info.name } + /// Set the meta information about the function pub fn with_info(self, info: FunctionInfo) -> Self { Self { info, ..self } } + /// Set the name of the function pub fn with_name>>(self, name: N) -> Self { Self { info: FunctionInfo { @@ -132,6 +146,7 @@ impl DynamicScriptFunction { } } + /// Set the namespace of the function pub fn with_namespace(self, namespace: Namespace) -> Self { Self { info: FunctionInfo { @@ -166,14 +181,18 @@ impl DynamicScriptFunctionMut { v => Ok(v), } } + + /// Get the name of the function pub fn name(&self) -> &Cow<'static, str> { &self.info.name } + /// Set the meta information about the function pub fn with_info(self, info: FunctionInfo) -> Self { Self { info, ..self } } + /// Set the name of the function pub fn with_name>>(self, name: N) -> Self { Self { info: FunctionInfo { @@ -184,6 +203,7 @@ impl DynamicScriptFunctionMut { } } + /// Set the namespace of the function pub fn with_namespace(self, namespace: Namespace) -> Self { Self { info: FunctionInfo { @@ -256,28 +276,36 @@ impl DerefMut for AppScriptFunctionRegistry { } #[derive(Clone, Debug, Default)] +/// A thread-safe reference counted wrapper around a [`ScriptFunctionRegistry`] pub struct ScriptFunctionRegistryArc(pub Arc>); impl ScriptFunctionRegistryArc { + /// claim a read lock on the registry pub fn read(&self) -> RwLockReadGuard { self.0.read() } + /// claim a write lock on the registry pub fn write(&mut self) -> RwLockWriteGuard { self.0.write() } } #[derive(Clone, Debug, PartialEq, Eq, Hash)] +/// A key used to identify a function in the registry pub struct FunctionKey { + /// The name of the function pub name: Cow<'static, str>, + /// The namespace of the function pub namespace: Namespace, } #[derive(Debug, Default)] +/// A registry of dynamic script functions pub struct ScriptFunctionRegistry { functions: HashMap, } + #[profiling::all_functions] impl ScriptFunctionRegistry { /// Register a script function with the given name. If the name already exists, @@ -322,6 +350,7 @@ impl ScriptFunctionRegistry { self.register_overload(namespace, name, func, true, None::<&'static str>); } + /// Equivalent to [`ScriptFunctionRegistry::overwrite`] but with the ability to provide documentation for the function. pub fn overwrite_documented( &mut self, namespace: Namespace, @@ -347,6 +376,7 @@ impl ScriptFunctionRegistry { self.functions.remove(&FunctionKey { name, namespace }) } + /// Remove all overloads of a function with the given name. Returns a vector of the removed functions. pub fn remove_all_overloads( &mut self, namespace: Namespace, @@ -362,6 +392,8 @@ impl ScriptFunctionRegistry { Ok(overloads) } + /// Register a script function with the given name. If the name already exists, + /// the new function will be registered as an overload of the function. fn register_overload<'env, F, M>( &mut self, namespace: Namespace, @@ -395,6 +427,7 @@ impl ScriptFunctionRegistry { } } + /// Check if a function with the given name and namespace exists pub fn contains(&self, namespace: Namespace, name: impl Into>) -> bool { self.functions.contains_key(&FunctionKey { name: name.into(), @@ -450,6 +483,7 @@ impl ScriptFunctionRegistry { self.functions.iter() } + /// Iterates over all functions in the given namespace pub fn iter_namespace( &self, namespace: Namespace, @@ -636,7 +670,7 @@ mod test { assert!(out.is_err()); assert_eq!( - out.unwrap_err().into_inner().unwrap(), + out.unwrap_err(), InteropError::function_interop_error( "my_fn", Namespace::Global, @@ -645,8 +679,6 @@ mod test { received: 1 }) ) - .into_inner() - .unwrap() ); }); } diff --git a/crates/bevy_mod_scripting_core/src/bindings/function/type_dependencies.rs b/crates/bevy_mod_scripting_core/src/bindings/function/type_dependencies.rs index 2d6596d864..866702643b 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/function/type_dependencies.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/function/type_dependencies.rs @@ -1,3 +1,5 @@ +//! This module contains the [`GetTypeDependencies`] trait and its implementations for various types. + use super::{ from::{Mut, Ref, Val}, script_function::FunctionCallContext, @@ -12,10 +14,12 @@ use std::hash::Hash; /// Functionally identical to [`GetTypeRegistration`] but without the 'static bound pub trait GetTypeDependencies { + /// Registers the type dependencies of the implementing type with the given [`TypeRegistry`]. fn register_type_dependencies(registry: &mut TypeRegistry); } #[macro_export] +/// A macro for implementing [`GetTypeDependencies`] for types with no type dependencies. macro_rules! no_type_dependencies { ($($path:path),*) => { $( @@ -27,6 +31,7 @@ macro_rules! no_type_dependencies { } #[macro_export] +/// A macro for implementing [`GetTypeDependencies`] for types that only depend on themselves. macro_rules! self_type_dependency_only { ($($path:ty),*) => { $( @@ -84,7 +89,10 @@ recursive_type_dependencies!( ); bevy::utils::all_tuples!(register_tuple_dependencies, 1, 14, T); + +/// A trait collecting type dependency information for a whole function. Used to register everything used by a function with the type registry pub trait GetFunctionTypeDependencies { + /// Registers the type dependencies of the implementing type with the given [`TypeRegistry`]. fn register_type_dependencies(registry: &mut TypeRegistry); } diff --git a/crates/bevy_mod_scripting_core/src/bindings/mod.rs b/crates/bevy_mod_scripting_core/src/bindings/mod.rs index 4ce76e7276..e7076abbbc 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/mod.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/mod.rs @@ -1,12 +1,12 @@ +//! Abstractions to help with creating bindings between bevy and scripting languages. + pub mod access_map; pub mod allocator; pub mod function; pub mod pretty_print; -// pub mod proxy; pub mod query; pub mod reference; pub mod script_value; pub mod world; pub use {allocator::*, query::*, reference::*, world::*}; -// pub use {proxy::*}; diff --git a/crates/bevy_mod_scripting_core/src/bindings/pretty_print.rs b/crates/bevy_mod_scripting_core/src/bindings/pretty_print.rs index 5f5a20d7f6..7f207f3fb5 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/pretty_print.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/pretty_print.rs @@ -1,3 +1,5 @@ +//! Pretty printing for reflect references and other types. + use crate::reflection_extensions::{FakeType, TypeIdExtensions}; use super::{ @@ -12,6 +14,7 @@ use bevy::{ use itertools::Itertools; use std::{any::TypeId, borrow::Cow}; +/// A utility for printing reflect references in a human readable format. pub struct ReflectReferencePrinter { pub(crate) reference: ReflectReference, } @@ -64,6 +67,7 @@ impl ReflectReferencePrinter { const UNREGISTERED_TYPE: &'static str = "Unregistered"; const UNKNOWN_FIELD: &'static str = ""; + /// Creates a new reflect reference printer pub fn new(reference: ReflectReference) -> Self { Self { reference } } @@ -131,6 +135,8 @@ impl ReflectReferencePrinter { out.push_str(&format!("{}({})", base_kind, type_path)); } + + /// Pretty prints a value of an opaque type. pub fn pretty_print_value_opaque(&self, v: &dyn PartialReflect, output: &mut String) { let type_id = v .get_represented_type_info() @@ -418,8 +424,21 @@ impl DisplayWithWorld for ReflectAccessId { let allocation_id = ReflectAllocationId::from(*self); let allocator = world.allocator(); let allocator = allocator.read(); - let type_id = allocator.get_type_id(&allocation_id).or_fake_id(); - format!("Allocation({})", type_id.display_with_world(world)) + let raid = ReflectAccessId::for_allocation(allocation_id.clone()); + + if world.claim_read_access(raid) { + if let Some(allocation) = allocator.get(&allocation_id) { + let ptr = allocation.get_ptr(); + let val = unsafe { &*ptr }; + let o = format!("Allocation({:?})", val); + unsafe { world.release_access(raid) }; + o + } else { + format!("Allocation({})", allocation_id) + } + } else { + format!("Allocation({})", allocation_id) + } } super::access_map::ReflectAccessKind::Global => "Global".to_owned(), } diff --git a/crates/bevy_mod_scripting_core/src/bindings/query.rs b/crates/bevy_mod_scripting_core/src/bindings/query.rs index 642d49b857..6a877f83a2 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/query.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/query.rs @@ -1,3 +1,5 @@ +//! Utilities for querying the world. + use super::{ReflectReference, WorldAccessGuard}; use crate::{error::InteropError, with_global_access}; use bevy::{ @@ -17,42 +19,50 @@ pub struct ScriptTypeRegistration { } #[derive(Clone, Reflect, Debug)] +/// A registration for a component type. pub struct ScriptComponentRegistration { pub(crate) registration: ScriptTypeRegistration, pub(crate) component_id: ComponentId, } #[derive(Clone, Reflect, Debug)] +/// A registration for a resource type. pub struct ScriptResourceRegistration { pub(crate) registration: ScriptTypeRegistration, pub(crate) resource_id: ComponentId, } impl ScriptTypeRegistration { + /// Creates a new [`ScriptTypeRegistration`] from a [`TypeRegistration`]. pub fn new(registration: Arc) -> Self { Self { registration } } #[inline(always)] + /// Returns the short name of the type. pub fn short_name(&self) -> &'static str { self.registration.type_info().type_path_table().short_path() } #[inline(always)] + /// Returns the full name of the type. pub fn type_name(&self) -> &'static str { self.registration.type_info().type_path_table().path() } #[inline(always)] + /// Returns the [`TypeId`] of the type. pub fn type_id(&self) -> TypeId { self.registration.type_info().type_id() } + /// Returns the [`TypeRegistration`] for this type. pub fn type_registration(&self) -> &TypeRegistration { &self.registration } } impl ScriptResourceRegistration { + /// Creates a new [`ScriptResourceRegistration`] from a [`ScriptTypeRegistration`] and a [`ComponentId`]. pub fn new(registration: ScriptTypeRegistration, resource_id: ComponentId) -> Self { Self { registration, @@ -73,6 +83,7 @@ impl ScriptResourceRegistration { } impl ScriptComponentRegistration { + /// Creates a new [`ScriptComponentRegistration`] from a [`ScriptTypeRegistration`] and a [`ComponentId`]. pub fn new(registration: ScriptTypeRegistration, component_id: ComponentId) -> Self { Self { registration, @@ -108,6 +119,7 @@ impl std::fmt::Display for ScriptTypeRegistration { #[derive(Clone, Default, Reflect)] #[reflect(opaque)] +/// A builder for a query. pub struct ScriptQueryBuilder { components: Vec, with: Vec, @@ -115,30 +127,36 @@ pub struct ScriptQueryBuilder { } impl ScriptQueryBuilder { + /// Adds components to the query. pub fn components(&mut self, components: Vec) -> &mut Self { self.components.extend(components); self } + /// Adds a component to the query. pub fn component(&mut self, component: ScriptComponentRegistration) -> &mut Self { self.components.push(component); self } + /// Adds components to the query that must be present. pub fn with_components(&mut self, with: Vec) -> &mut Self { self.with.extend(with); self } + /// Adds a component to the query that must be present. pub fn with_component(&mut self, with: ScriptComponentRegistration) -> &mut Self { self.with.push(with); self } + /// Adds components to the query that must not be present. pub fn without_components(&mut self, without: Vec) -> &mut Self { self.without.extend(without); self } + /// Adds a component to the query that must not be present. pub fn without_component(&mut self, without: ScriptComponentRegistration) -> &mut Self { self.without.push(without); self @@ -147,22 +165,17 @@ impl ScriptQueryBuilder { #[derive(Clone, Reflect)] #[reflect(opaque)] +/// A result from a query. pub struct ScriptQueryResult { + /// The entity that matched the query. pub entity: Entity, + /// The components that matched the query. pub components: Vec, } -// impl WorldCallbackAccess { -// pub fn query( -// &self, -// query: ScriptQueryBuilder, -// ) -> Result, InteropError> { -// // find the set of components -// self.try_read().and_then(|world| world.query(query)) -// } -// } #[profiling::all_functions] impl WorldAccessGuard<'_> { + /// Queries the world for entities that match the given query. pub fn query( &self, query: ScriptQueryBuilder, diff --git a/crates/bevy_mod_scripting_core/src/bindings/reference.rs b/crates/bevy_mod_scripting_core/src/bindings/reference.rs index 83e1dd1e44..7cc9fc3803 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/reference.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/reference.rs @@ -28,6 +28,7 @@ use std::{any::TypeId, fmt::Debug}; #[reflect(Default)] pub struct ReflectReference { #[reflect(ignore)] + /// The base type and id of the value we want to access pub base: ReflectBaseType, // TODO: experiment with Fixed capacity vec, boxed array etc, compromise between heap allocation and runtime cost // needs benchmarks first though @@ -123,6 +124,7 @@ impl ReflectReference { } } + /// Create a new reference to a value by allocating it. pub fn new_allocated_boxed( value: Box, allocator: &mut ReflectAllocator, @@ -138,6 +140,7 @@ impl ReflectReference { } } + /// Create a new reference to resource pub fn new_resource_ref(world: WorldGuard) -> Result { let reflect_id = ReflectAccessId::for_resource::(&world.as_unsafe_world_cell()?)?; Ok(Self { @@ -149,6 +152,7 @@ impl ReflectReference { }) } + /// Create a new reference to component pub fn new_component_ref( entity: Entity, world: WorldGuard, @@ -253,6 +257,7 @@ impl ReflectReference { ) } + /// Retrieves the type id of the value the reference points to. pub fn tail_type_id(&self, world: WorldGuard) -> Result, InteropError> { if self.reflect_path.is_empty() { return Ok(Some(self.base.type_id)); @@ -262,14 +267,17 @@ impl ReflectReference { }) } + /// Retrieves the type id of the elements in the value the reference points to. pub fn element_type_id(&self, world: WorldGuard) -> Result, InteropError> { self.with_reflect(world, |r| r.element_type_id()) } + /// Retrieves the type id of the keys in the value the reference points to. pub fn key_type_id(&self, world: WorldGuard) -> Result, InteropError> { self.with_reflect(world, |r| r.key_type_id()) } + /// Retrieves the type id of the value the reference points to based on the given source. pub fn type_id_of( &self, source: TypeIdSource, @@ -409,18 +417,25 @@ impl ReflectReference { } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd)] +/// The type id and base id of the value we want to access pub struct ReflectBaseType { + /// The type id of the value we want to access pub type_id: TypeId, + /// The base kind of the value we want to access pub base_id: ReflectBase, } /// The Id of the kind of reflection base being pointed to #[derive(Clone, Debug, PartialEq, Eq, PartialOrd)] pub enum ReflectBase { + /// A component of an entity Component(Entity, ComponentId), + /// A resource Resource(ComponentId), + /// an allocation Owned(ReflectAllocationId), } + #[profiling::all_functions] impl ReflectBase { /// Retrieves the pointer to the underlying `dyn PartialReflect` object valid for the 'w lifteime of the world cell @@ -462,11 +477,13 @@ impl ReflectBase { } } +/// An iterator over a reflect reference that will keep returning the next element forever. pub trait ReflectionPathExt { + /// Assumes the accesses are 1 indexed and converts them to 0 indexed fn convert_to_0_indexed(&mut self); - + /// Returns true if the path is empty fn is_empty(&self) -> bool; - + /// Returns an iterator over the accesses fn iter(&self) -> impl Iterator; } #[profiling::all_functions] @@ -502,11 +519,15 @@ pub struct ReflectRefIter { } #[derive(Clone, Debug, PartialEq, Eq)] +/// The key of the current iteration pub enum IterationKey { + /// The current index Index(usize), } + #[profiling::all_functions] impl ReflectRefIter { + /// Creates a new iterator that will keep returning the next element forever. pub fn new_indexed(base: ReflectReference) -> Self { Self { base, @@ -514,6 +535,7 @@ impl ReflectRefIter { } } + /// Returns the current index of the iterator pub fn index(&self) -> IterationKey { self.index.clone() } diff --git a/crates/bevy_mod_scripting_core/src/bindings/script_value.rs b/crates/bevy_mod_scripting_core/src/bindings/script_value.rs index b1b67de1cf..306d318a32 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/script_value.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/script_value.rs @@ -1,3 +1,5 @@ +//! This module contains the `ScriptValue` enum which is used to pass values between scripting languages and Rust. + use std::{borrow::Cow, collections::HashMap}; use bevy::reflect::{OffsetAccess, ParsedPath, Reflect}; @@ -47,6 +49,7 @@ impl ScriptValue { } } + /// Returns the variant of the value as a string. pub fn type_name(&self) -> String { match self { ScriptValue::Unit => "Unit".to_owned(), diff --git a/crates/bevy_mod_scripting_core/src/bindings/world.rs b/crates/bevy_mod_scripting_core/src/bindings/world.rs index 43eb7ca89d..62120c407e 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/world.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/world.rs @@ -36,7 +36,6 @@ use std::{ fmt::Debug, rc::Rc, sync::Arc, - time::Duration, }; /// Prefer to directly using [`WorldAccessGuard`]. If the underlying type changes, this alias will be updated. @@ -44,9 +43,6 @@ pub type WorldGuard<'w> = WorldAccessGuard<'w>; /// Similar to [`WorldGuard`], but without the arc, use for when you don't need the outer Arc. pub type WorldGuardRef<'w> = &'w WorldAccessGuard<'w>; -pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5); -pub const DEFAULT_INTERVAL: Duration = Duration::from_millis(10); - /// Provides safe access to the world via [`WorldAccess`] permissions, which enforce aliasing rules at runtime in multi-thread environments #[derive(Clone)] pub struct WorldAccessGuard<'w>(pub(crate) Rc>); @@ -159,6 +155,7 @@ impl<'w> WorldAccessGuard<'w> { .get_id(id)) } + /// Gets the resource id of the given component or resource pub fn get_resource_id(&self, id: TypeId) -> Result, InteropError> { Ok(self .as_unsafe_world_cell_readonly()? @@ -166,6 +163,7 @@ impl<'w> WorldAccessGuard<'w> { .get_resource_id(id)) } + /// Get the location of the given access pub fn get_access_location( &self, raid: ReflectAccessId, @@ -174,11 +172,13 @@ impl<'w> WorldAccessGuard<'w> { } #[track_caller] + /// Claims read access to the given type. pub fn claim_read_access(&self, raid: ReflectAccessId) -> bool { self.0.accesses.claim_read_access(raid) } #[track_caller] + /// Claims write access to the given type. pub fn claim_write_access(&self, raid: ReflectAccessId) -> bool { self.0.accesses.claim_write_access(raid) } @@ -193,6 +193,7 @@ impl<'w> WorldAccessGuard<'w> { self.0.accesses.release_access(raid) } + /// Claims global access to the world pub fn claim_global_access(&self) -> bool { self.0.accesses.claim_global_access() } @@ -430,6 +431,7 @@ impl<'w> WorldAccessGuard<'w> { /// Impl block for higher level world methods #[profiling::all_functions] impl WorldAccessGuard<'_> { + /// Spawns a new entity in the world pub fn spawn(&self) -> Result { self.with_global_access(|world| { let entity = world.spawn_empty(); @@ -437,6 +439,7 @@ impl WorldAccessGuard<'_> { }) } + /// get a type registration for the type pub fn get_type_by_name(&self, type_name: String) -> Option { let type_registry = self.type_registry(); let type_registry = type_registry.read(); @@ -446,6 +449,7 @@ impl WorldAccessGuard<'_> { .map(|registration| ScriptTypeRegistration::new(Arc::new(registration.clone()))) } + /// get a component type registration for the type pub fn get_component_type( &self, registration: ScriptTypeRegistration, @@ -456,6 +460,7 @@ impl WorldAccessGuard<'_> { }) } + /// get a resource type registration for the type pub fn get_resource_type( &self, registration: ScriptTypeRegistration, @@ -466,6 +471,7 @@ impl WorldAccessGuard<'_> { }) } + /// add a default component to an entity pub fn add_default_component( &self, entity: Entity, @@ -518,6 +524,7 @@ impl WorldAccessGuard<'_> { })? } + /// insert the component into the entity pub fn insert_component( &self, entity: Entity, @@ -551,6 +558,7 @@ impl WorldAccessGuard<'_> { })? } + /// get the component from the entity pub fn get_component( &self, entity: Entity, @@ -588,6 +596,7 @@ impl WorldAccessGuard<'_> { } } + /// check if the entity has the component pub fn has_component( &self, entity: Entity, @@ -601,6 +610,7 @@ impl WorldAccessGuard<'_> { Ok(entity.contains_id(component_id)) } + /// remove the component from the entity pub fn remove_component( &self, entity: Entity, @@ -627,6 +637,7 @@ impl WorldAccessGuard<'_> { })? } + /// get the given resource pub fn get_resource( &self, resource_id: ComponentId, @@ -657,6 +668,7 @@ impl WorldAccessGuard<'_> { })) } + /// remove the given resource pub fn remove_resource( &self, registration: ScriptResourceRegistration, @@ -676,6 +688,7 @@ impl WorldAccessGuard<'_> { self.with_global_access(|world| component_data.remove(world)) } + /// check if the entity has the resource pub fn has_resource(&self, resource_id: ComponentId) -> Result { let cell = self.as_unsafe_world_cell()?; // Safety: we are not reading the value at all @@ -683,10 +696,12 @@ impl WorldAccessGuard<'_> { Ok(res_ptr.is_some()) } + /// check the given entity exists pub fn has_entity(&self, entity: Entity) -> Result { self.is_valid_entity(entity) } + /// get the children of the given entity pub fn get_children(&self, entity: Entity) -> Result, InteropError> { if !self.is_valid_entity(entity)? { return Err(InteropError::missing_entity(entity)); @@ -697,6 +712,7 @@ impl WorldAccessGuard<'_> { }) } + /// get the parent of the given entity pub fn get_parent(&self, entity: Entity) -> Result, InteropError> { if !self.is_valid_entity(entity)? { return Err(InteropError::missing_entity(entity)); @@ -705,6 +721,7 @@ impl WorldAccessGuard<'_> { self.with_component(entity, |c: Option<&Parent>| c.map(|c| c.get())) } + /// insert children into the given entity pub fn push_children(&self, parent: Entity, children: &[Entity]) -> Result<(), InteropError> { // verify entities exist if !self.is_valid_entity(parent)? { @@ -723,6 +740,7 @@ impl WorldAccessGuard<'_> { }) } + /// remove children from the given entity pub fn remove_children(&self, parent: Entity, children: &[Entity]) -> Result<(), InteropError> { if !self.is_valid_entity(parent)? { return Err(InteropError::missing_entity(parent)); @@ -741,6 +759,7 @@ impl WorldAccessGuard<'_> { }) } + /// insert children into the given entity at the given index pub fn insert_children( &self, parent: Entity, @@ -765,6 +784,7 @@ impl WorldAccessGuard<'_> { }) } + /// despawn this and all children of the given entity recursively pub fn despawn_recursive(&self, parent: Entity) -> Result<(), InteropError> { if !self.is_valid_entity(parent)? { return Err(InteropError::missing_entity(parent)); @@ -777,6 +797,7 @@ impl WorldAccessGuard<'_> { }) } + /// despawn the given entity pub fn despawn(&self, entity: Entity) -> Result<(), InteropError> { if !self.is_valid_entity(entity)? { return Err(InteropError::missing_entity(entity)); @@ -790,6 +811,7 @@ impl WorldAccessGuard<'_> { }) } + /// despawn all children of the given entity recursively pub fn despawn_descendants(&self, parent: Entity) -> Result<(), InteropError> { if !self.is_valid_entity(parent)? { return Err(InteropError::missing_entity(parent)); @@ -813,6 +835,7 @@ impl WorldAccessGuard<'_> { /// Utility type for accessing the world in a callback pub trait WorldContainer { + /// The error type for the container type Error: Debug; /// Sets the world to the given value in the container fn set_world(&mut self, world: WorldGuard<'static>) -> Result<(), Self::Error>; diff --git a/crates/bevy_mod_scripting_core/src/commands.rs b/crates/bevy_mod_scripting_core/src/commands.rs index 485cc7cfa2..002f4becc1 100644 --- a/crates/bevy_mod_scripting_core/src/commands.rs +++ b/crates/bevy_mod_scripting_core/src/commands.rs @@ -1,3 +1,5 @@ +//! Commands for creating, updating and deleting scripts + use crate::{ asset::ScriptAsset, context::ContextBuilder, @@ -10,13 +12,16 @@ use crate::{ use bevy::{asset::Handle, log::debug, prelude::Command}; use std::marker::PhantomData; +/// Deletes a script with the given ID pub struct DeleteScript { + /// The ID of the script to delete pub id: ScriptId, - // hack to make this Send, C does not need to be Send since it is not stored in the command + /// hack to make this Send, C does not need to be Send since it is not stored in the command pub _ph: PhantomData, } impl DeleteScript

{ + /// Creates a new DeleteScript command with the given ID pub fn new(id: ScriptId) -> Self { Self { id, @@ -110,6 +115,7 @@ pub struct CreateOrUpdateScript { } impl CreateOrUpdateScript

{ + /// Creates a new CreateOrUpdateScript command with the given ID, content and asset pub fn new(id: ScriptId, content: Box<[u8]>, asset: Option>) -> Self { Self { id, diff --git a/crates/bevy_mod_scripting_core/src/context.rs b/crates/bevy_mod_scripting_core/src/context.rs index bf2c50184e..79a225233f 100644 --- a/crates/bevy_mod_scripting_core/src/context.rs +++ b/crates/bevy_mod_scripting_core/src/context.rs @@ -1,3 +1,5 @@ +//! Traits and types for managing script contexts. + use crate::{ bindings::{ThreadWorldContainer, WorldContainer, WorldGuard}, error::ScriptError, @@ -7,14 +9,17 @@ use crate::{ use bevy::ecs::{entity::Entity, system::Resource, world::World}; use std::{collections::HashMap, sync::atomic::AtomicU32}; +/// A trait that all script contexts must implement. pub trait Context: 'static {} impl Context for T {} +/// The type of a context id pub type ContextId = u32; /// Stores script state for a scripting plugin. Scripts are identified by their `ScriptId`, while contexts are identified by their `ContextId`. #[derive(Resource)] pub struct ScriptContexts { + /// The contexts of the scripts pub contexts: HashMap, } @@ -35,6 +40,7 @@ impl ScriptContexts

{ id } + /// Inserts a context with a specific id pub fn insert_with_id(&mut self, id: ContextId, ctxt: P::C) -> Option { self.contexts.insert(id, ctxt) } @@ -44,18 +50,22 @@ impl ScriptContexts

{ CONTEXT_ID_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed) } + /// Removes a context from the map pub fn remove(&mut self, id: ContextId) -> Option { self.contexts.remove(&id) } + /// Get a reference to a context pub fn get(&self, id: ContextId) -> Option<&P::C> { self.contexts.get(&id) } + /// Get a mutable reference to a context pub fn get_mut(&mut self, id: ContextId) -> Option<&mut P::C> { self.contexts.get_mut(&id) } + /// Check if a context exists pub fn contains(&self, id: ContextId) -> bool { self.contexts.contains_key(&id) } @@ -92,6 +102,7 @@ impl Clone for ContextLoadingSettings { } } } +/// A strategy for loading contexts pub type ContextLoadFn

= fn( script_id: &ScriptId, content: &[u8], @@ -100,6 +111,7 @@ pub type ContextLoadFn

= fn( runtime: &mut

::R, ) -> Result<

::C, ScriptError>; +/// A strategy for reloading contexts pub type ContextReloadFn

= fn( script_id: &ScriptId, content: &[u8], @@ -111,11 +123,14 @@ pub type ContextReloadFn

= fn( /// A strategy for loading and reloading contexts pub struct ContextBuilder { + /// The function to load a context pub load: ContextLoadFn

, + /// The function to reload a context pub reload: ContextReloadFn

, } impl ContextBuilder

{ + /// load a context pub fn load( loader: ContextLoadFn

, script: &ScriptId, @@ -137,6 +152,7 @@ impl ContextBuilder

{ }) } + /// reload a context pub fn reload( reloader: ContextReloadFn

, script: &ScriptId, diff --git a/crates/bevy_mod_scripting_core/src/docgen/info.rs b/crates/bevy_mod_scripting_core/src/docgen/info.rs index 5cafa6f771..8633bb5f37 100644 --- a/crates/bevy_mod_scripting_core/src/docgen/info.rs +++ b/crates/bevy_mod_scripting_core/src/docgen/info.rs @@ -1,3 +1,5 @@ +//! Information about functions and their arguments. + use bevy::reflect::Reflect; use crate::bindings::function::arg_meta::ArgMeta; @@ -6,15 +8,22 @@ use std::{any::TypeId, borrow::Cow}; /// for things you can call and provide some introspection capability. pub trait GetFunctionInfo { + /// Get the function info for the function. fn get_function_info(&self, name: Cow<'static, str>, namespace: Namespace) -> FunctionInfo; } #[derive(Debug, Clone, PartialEq, Reflect)] +/// Information about a function. pub struct FunctionInfo { + /// The name of the function. pub name: Cow<'static, str>, + /// The namespace of the function. pub namespace: Namespace, + /// Information about the arguments of the function. pub arg_info: Vec, + /// Information about the return value of the function. pub return_info: FunctionReturnInfo, + /// Documentation for the function. pub docs: Option>, } @@ -25,6 +34,7 @@ impl Default for FunctionInfo { } impl FunctionInfo { + /// Create a new function info with default values. pub fn new() -> Self { Self { name: Cow::Borrowed(""), @@ -35,6 +45,7 @@ impl FunctionInfo { } } + /// Create a new function info with a name and namespace. pub fn new_for(name: Cow<'static, str>, namespace: Namespace) -> Self { Self { name, @@ -45,6 +56,7 @@ impl FunctionInfo { } } + /// Add an argument to the function info. pub fn add_arg(mut self, name: Option>) -> Self { self.arg_info.push(FunctionArgInfo { name, @@ -55,11 +67,13 @@ impl FunctionInfo { self } + /// Add a return value to the function info. pub fn add_return(mut self, return_info: FunctionReturnInfo) -> Self { self.return_info = return_info; self } + /// Add documentation to the function info. pub fn with_docs(mut self, docs: impl Into>) -> Self { self.docs = Some(docs.into()); self @@ -67,14 +81,20 @@ impl FunctionInfo { } #[derive(Debug, Clone, PartialEq, Reflect)] +/// Information about a function argument. pub struct FunctionArgInfo { + /// The name of the argument. pub name: Option>, + /// The index of the argument. pub arg_index: usize, + /// The type of the argument. pub type_id: TypeId, + /// Documentation for the argument. pub docs: Option>, } impl FunctionArgInfo { + /// Create a new function argument info with default values. pub fn new(arg_index: usize, type_id: TypeId) -> Self { Self { name: None, @@ -84,11 +104,13 @@ impl FunctionArgInfo { } } + /// Create a new function argument info with a name. pub fn with_name(mut self, name: Cow<'static, str>) -> Self { self.name = Some(name); self } + /// Add documentation to the function argument info. pub fn with_docs(mut self, docs: Cow<'static, str>) -> Self { self.docs = Some(docs); self @@ -96,7 +118,9 @@ impl FunctionArgInfo { } #[derive(Debug, Clone, PartialEq, Reflect)] +/// Information about a function return value. pub struct FunctionReturnInfo { + /// The type of the return value. pub type_id: TypeId, } @@ -107,12 +131,14 @@ impl Default for FunctionReturnInfo { } impl FunctionReturnInfo { + /// Create a new function return info with default values. pub fn new() -> Self { Self { type_id: TypeId::of::<()>(), } } + /// Create a new function return info for a specific type. pub fn new_for() -> Self { Self { type_id: TypeId::of::(), diff --git a/crates/bevy_mod_scripting_core/src/docgen/mod.rs b/crates/bevy_mod_scripting_core/src/docgen/mod.rs index 4b8757fcc7..38cc7be64f 100644 --- a/crates/bevy_mod_scripting_core/src/docgen/mod.rs +++ b/crates/bevy_mod_scripting_core/src/docgen/mod.rs @@ -1 +1,2 @@ +//! Documentation generation for scripting languages. pub mod info; diff --git a/crates/bevy_mod_scripting_core/src/error.rs b/crates/bevy_mod_scripting_core/src/error.rs index 0449ba5d91..f077dc94d5 100644 --- a/crates/bevy_mod_scripting_core/src/error.rs +++ b/crates/bevy_mod_scripting_core/src/error.rs @@ -1,3 +1,5 @@ +//! Errors that can occur when interacting with the scripting system + use crate::bindings::{ access_map::{DisplayCodeLocation, ReflectAccessId}, function::namespace::Namespace, @@ -19,8 +21,6 @@ use std::{ sync::Arc, }; -pub type ScriptResult = Result; - /// An error with an optional script Context #[derive(Debug, Clone, PartialEq, Reflect)] #[reflect(opaque)] @@ -39,14 +39,20 @@ impl Deref for ScriptError { /// The innards are separated to reduce the size of this error #[derive(Debug, Clone)] pub struct ScriptErrorInner { + /// The script that caused the error pub script: Option, + /// The context in which the error occurred pub context: String, + /// The error that occurred pub reason: Arc, } #[derive(Debug)] +/// The kind of error that occurred pub enum ErrorKind { + /// An error that can be displayed Display(Box), + /// An error that can be displayed with a world WithWorld(Box), } @@ -102,6 +108,7 @@ impl ScriptError { } #[cfg(feature = "rhai_impls")] + /// destructures a rhai error into a script error, taking care to preserve as much information as possible pub fn from_rhai_error(error: rhai::EvalAltResult) -> Self { match error { rhai::EvalAltResult::ErrorSystem(message, error) => { @@ -117,10 +124,12 @@ impl ScriptError { } } + /// Creates a new script error with an external error pub fn new_external(reason: impl std::error::Error + Send + Sync + 'static) -> Self { Self::new_external_boxed(Box::new(reason)) } + /// Creates a new script error with an external error pub fn new_external_boxed(reason: Box) -> Self { Self(Arc::new(ScriptErrorInner { script: None, @@ -129,6 +138,7 @@ impl ScriptError { })) } + /// Creates a new script error with a reason pub fn new(reason: impl DisplayWithWorld + Send + Sync + 'static) -> Self { Self(Arc::new(ScriptErrorInner { script: None, @@ -137,6 +147,7 @@ impl ScriptError { })) } + /// Creates a new script error with a reason pub fn with_script(self, script: S) -> Self { Self(Arc::new(ScriptErrorInner { script: Some(script.to_string()), @@ -145,6 +156,7 @@ impl ScriptError { })) } + /// Adds context to the error pub fn with_context(self, context: S) -> Self { Self(Arc::new(ScriptErrorInner { script: self.0.script.clone(), @@ -246,9 +258,11 @@ impl From for Box { } #[derive(Clone, Debug, PartialEq)] +/// An error thrown when a resource is missing pub struct MissingResourceError(&'static str); impl MissingResourceError { + /// Creates a new missing resource error pub fn new() -> Self { Self(std::any::type_name::()) } @@ -267,6 +281,7 @@ impl Display for MissingResourceError { impl std::error::Error for MissingResourceError {} #[derive(Debug, Clone, PartialEq, Reflect)] +/// An error thrown when interoperating with scripting languages. pub struct InteropError(#[reflect(ignore)] Arc); impl std::error::Error for InteropError {} @@ -299,7 +314,9 @@ impl From for ScriptError { } } +/// Utility trait for flattening errors pub trait FlattenError { + /// Flattens the error into a single error type fn flatten_interop_error(self) -> Result; } @@ -314,6 +331,7 @@ impl FlattenError for Result, Intero } impl InteropError { + /// Creates a new invariant error. Thrown when an invariant is violated. pub fn invariant(message: impl Display) -> Self { Self(Arc::new(InteropErrorInner::Invariant { message: message.to_string(), @@ -470,12 +488,15 @@ impl InteropError { Self(Arc::new(InteropErrorInner::FunctionCallError { inner })) } + /// Thrown when an error happens during argument conversion in a function call pub fn function_arg_conversion_error(argument: String, error: InteropError) -> Self { Self(Arc::new(InteropErrorInner::FunctionArgConversionError { argument, error, })) } + + /// Thrown when a length mismatch occurs pub fn length_mismatch(expected: usize, got: usize) -> Self { Self(Arc::new(InteropErrorInner::LengthMismatch { expected, @@ -483,10 +504,12 @@ impl InteropError { })) } + /// Thrown when an error happens that is not covered by the other variants pub fn external_error(error: Box) -> Self { Self(Arc::new(InteropErrorInner::OtherError { error })) } + /// Thrown when a function is missing from the function registry pub fn missing_function(on: TypeId, function_name: impl Display) -> Self { Self(Arc::new(InteropErrorInner::MissingFunctionError { on, @@ -494,6 +517,7 @@ impl InteropError { })) } + /// Thrown when an invalid access count is detected pub fn invalid_access_count(count: usize, expected: usize, context: String) -> Self { Self(Arc::new(InteropErrorInner::InvalidAccessCount { count, @@ -502,6 +526,7 @@ impl InteropError { })) } + /// Thrown when a component or resource type is not registered pub fn unregistered_component_or_resource_type( type_name: impl Into>, ) -> Self { @@ -511,115 +536,176 @@ impl InteropError { }, )) } - - pub fn inner(&self) -> &InteropErrorInner { - &self.0 - } - - /// Unwraps the inner error if there is only one reference to it. - /// Otherwise returns Self. - pub fn into_inner(self) -> Result { - Arc::try_unwrap(self.0).map_err(Self) - } } /// For errors to do with reflection, type conversions or other interop issues #[derive(Debug)] -pub enum InteropErrorInner { +pub(crate) enum InteropErrorInner { + /// Thrown if a callback requires world access, but is unable to do so due StaleWorldAccess, + /// Thrown if a callback requires world access, but is unable to do so due MissingWorld, + /// Thrown if a base type is not registered with the reflection system UnregisteredBase { + /// The base type that was not registered base: ReflectBaseType, }, + /// Thrown if a base type is not registered with the reflection system MissingTypeData { + /// The type that was missing data type_id: TypeId, + /// The type data that was missing type_data: String, }, + /// Thrown if a type cannot be converted from reflect FailedFromReflect { + /// The type that failed to convert type_id: Option, + /// The reason for the failure reason: String, }, + /// Thrown if access to the given reflection base is required but cannot be claimed CannotClaimAccess { + /// The base that could not be claimed base: ReflectAccessId, + /// The context in which the error occurred context: Cow<'static, str>, + /// The location in the code where the blocking access is being held location: Option>, }, + /// thrown when the access count is invalid InvalidAccessCount { + /// The count of accesses count: usize, + /// The expected count expected: usize, + /// The context in which the error occurred context: String, }, + /// Thrown if a conversion into the given type is impossible ImpossibleConversion { + /// The type that the conversion was attempted into into: TypeId, }, + /// Thrown if a conversion was not fully completed, as a better conversion exists BetterConversionExists { + /// The context in which the error occurred context: String, }, + /// Thrown if a value was expected to be of one type but was of another TypeMismatch { + /// The type that was expected expected: TypeId, + /// The type that was received got: Option, }, + /// Thrown if a value was expected to be of one type but was of another StringTypeMismatch { + /// The type that was expected expected: String, + /// The type that was received got: Option, }, + /// Thrown if a [`ScriptValue`] could not be converted to the expected type ValueMismatch { + /// The type that was expected expected: TypeId, + /// The value that was received got: ScriptValue, }, + /// Thrown if a length mismatch occurs LengthMismatch { + /// The length that was expected expected: usize, + /// The length that was received got: usize, }, + /// Thrown if a downcast from a reflect reference to a specific type failed CouldNotDowncast { + /// The reference that was attempted to be downcast from: ReflectReference, + /// The type that the downcast was attempted to to: TypeId, }, + /// Thrown if a garbage collected allocation was attempted to be accessed GarbageCollectedAllocation { + /// The reference that was attempted to be accessed reference: ReflectReference, }, + /// Thrown if a reflection path is invalid ReflectionPathError { + /// The error that occurred error: String, + /// The reference that was attempted to be accessed reference: Option, }, + /// Thrown if an operation is not supported on the given base type UnsupportedOperation { + /// The base that the operation was attempted on base: Option, + /// The value that was used in the operation value: Option>, + /// The operation that was attempted operation: String, }, + /// Thrown if an invalid index operation was attempted on a value InvalidIndex { + /// The value that was attempted to be indexed value: ScriptValue, + /// The reason for the invalid index reason: String, }, + /// Thrown if an entity was missing or invalid MissingEntity { + /// The entity that was missing entity: Entity, }, + /// Thrown if a component was invalid InvalidComponent { + /// The component that was invalid component_id: ComponentId, }, + /// Thrown when an error happens in a function call FunctionCallError { + /// The inner error that occurred inner: FunctionError, }, + /// Thrown when an error happens during argument conversion in a function call MissingFunctionError { + /// The type that the function was attempted to be called on on: TypeId, + /// The function that was attempted to be called function_name: String, }, + /// Thrown when an error happens in the context of a function call FunctionInteropError { + /// The function that the error occurred in function_name: String, + /// The namespace that the function was called on on: Namespace, + /// The error that occurred error: InteropError, }, + /// Thrown when an error happens that is not covered by the other variants FunctionArgConversionError { + /// The argument that was attempted to be converted argument: String, + /// The error that occurred error: InteropError, }, + /// Thrown when an error happens that is not covered by the other variants OtherError { + /// The error that occurred error: Box, }, + /// Thrown when a component or resource type is not registered UnregisteredComponentOrResourceType { + /// The type that was not registered type_name: Cow<'static, str>, }, + /// Thrown when an invariant is violated Invariant { + /// The message that describes the invariant violation message: String, }, } diff --git a/crates/bevy_mod_scripting_core/src/event.rs b/crates/bevy_mod_scripting_core/src/event.rs index c84af073df..6a67fd93ce 100644 --- a/crates/bevy_mod_scripting_core/src/event.rs +++ b/crates/bevy_mod_scripting_core/src/event.rs @@ -1,9 +1,12 @@ +//! Event handlers and event types for scripting. + use crate::{bindings::script_value::ScriptValue, error::ScriptError, script::ScriptId}; use bevy::{ecs::entity::Entity, prelude::Event}; /// An error coming from a script #[derive(Debug, Event)] pub struct ScriptErrorEvent { + /// The script that caused the error pub error: ScriptError, } @@ -35,10 +38,12 @@ impl CallbackLabel { } } + /// Creates a new callback label, filtering out invalid characters pub fn new_lossy(label: &str) -> Self { Self(Self::filter_invalid(label)) } + /// Creates a new callback label, returning None if the label is invalid pub fn new(label: &str) -> Option { let new_lossy = Self::new_lossy(label); if new_lossy.0.len() != label.len() { @@ -50,10 +55,13 @@ impl CallbackLabel { } #[macro_export] +/// Creates a set of callback labels macro_rules! callback_labels { ($($name:ident => $label:expr),*) => { $( + #[doc = "A callback label for the event: "] + #[doc = stringify!($label)] pub struct $name; impl $crate::event::IntoCallbackLabel for $name { fn into_callback_label() -> $crate::event::CallbackLabel { @@ -69,7 +77,9 @@ callback_labels!( OnScriptUnloaded => "on_script_unloaded" ); +/// A trait for types that can be converted into a callback label pub trait IntoCallbackLabel { + /// Converts the type into a callback label fn into_callback_label() -> CallbackLabel; } @@ -117,12 +127,16 @@ pub enum Recipients { /// A callback event meant to trigger a callback in a subset/set of scripts in the world with the given arguments #[derive(Clone, Event, Debug)] pub struct ScriptCallbackEvent { + /// The label of the callback pub label: CallbackLabel, + /// The recipients of the callback pub recipients: Recipients, + /// The arguments to the callback pub args: Vec, } impl ScriptCallbackEvent { + /// Creates a new callback event with the given label, arguments and recipients pub fn new>( label: L, args: Vec, @@ -135,6 +149,7 @@ impl ScriptCallbackEvent { } } + /// Creates a new callback event with the given label, arguments and all scripts as recipients pub fn new_for_all>(label: L, args: Vec) -> Self { Self::new(label, args, Recipients::All) } diff --git a/crates/bevy_mod_scripting_core/src/handler.rs b/crates/bevy_mod_scripting_core/src/handler.rs index ac99e73fb3..2a627c70ca 100644 --- a/crates/bevy_mod_scripting_core/src/handler.rs +++ b/crates/bevy_mod_scripting_core/src/handler.rs @@ -1,3 +1,5 @@ +//! Contains the logic for handling script callback events + use crate::{ bindings::{ pretty_print::DisplayWithWorld, script_value::ScriptValue, ThreadWorldContainer, @@ -20,9 +22,7 @@ use bevy::{ prelude::{EventReader, Events, Query, Ref}, }; -pub trait Args: Clone + Send + Sync + 'static {} -impl Args for T {} - +/// A function that handles a callback event pub type HandlerFn

= fn( args: Vec, entity: Entity, @@ -36,6 +36,7 @@ pub type HandlerFn

= fn( /// A resource that holds the settings for the callback handler for a specific combination of type parameters #[derive(Resource)] pub struct CallbackSettings { + /// The callback handler function pub callback_handler: HandlerFn

, } @@ -46,8 +47,10 @@ impl Clone for CallbackSettings

{ } } } + #[profiling::all_functions] impl CallbackSettings

{ + /// Creates a new callback settings resource with the given handler function pub fn new(callback_handler: HandlerFn

) -> Self { Self { callback_handler } } diff --git a/crates/bevy_mod_scripting_core/src/lib.rs b/crates/bevy_mod_scripting_core/src/lib.rs index da540286bf..2a2f585a52 100644 --- a/crates/bevy_mod_scripting_core/src/lib.rs +++ b/crates/bevy_mod_scripting_core/src/lib.rs @@ -1,3 +1,7 @@ +//! Core functionality for the bevy_mod_scripting framework. +//! +//! Contains language agnostic systems and types for handling scripting in bevy. + use crate::event::ScriptErrorEvent; use asset::{ configure_asset_systems, configure_asset_systems_for_plugin, AssetPathToLanguageMapper, @@ -52,10 +56,14 @@ pub enum ScriptingSystemSet { /// Types which act like scripting plugins, by selecting a context and runtime /// Each individual combination of context and runtime has specific infrastructure built for it and does not interact with other scripting plugins pub trait IntoScriptPluginParams: 'static { + /// The language of the scripts const LANGUAGE: Language; + /// The context type used for the scripts type C: Context; + /// The runtime type used for the scripts type R: Runtime; + /// Build the runtime fn build_runtime() -> Self::R; } @@ -70,6 +78,7 @@ pub struct ScriptingPlugin { /// The context assigner for assigning contexts to scripts. pub context_assigner: ContextAssigner

, + /// The asset path to language mapper for the plugin pub language_mapper: AssetPathToLanguageMapper, /// initializers for the contexts, run when loading the script @@ -139,12 +148,19 @@ impl ScriptingPlugin

{ /// Utility trait for configuring all scripting plugins. pub trait ConfigureScriptPlugin { + /// The type of the plugin to configure type P: IntoScriptPluginParams; + + /// Add a context initializer to the plugin fn add_context_initializer(self, initializer: ContextInitializer) -> Self; + + /// Add a context pre-handling initializer to the plugin fn add_context_pre_handling_initializer( self, initializer: ContextPreHandlingInitializer, ) -> Self; + + /// Add a runtime initializer to the plugin fn add_runtime_initializer(self, initializer: RuntimeInitializer) -> Self; /// Switch the context assigning strategy to a global context assigner. @@ -233,7 +249,9 @@ fn register_types(app: &mut App) { app.register_type::(); } +/// Trait for adding a runtime initializer to an app pub trait AddRuntimeInitializer { + /// Adds a runtime initializer to the app fn add_runtime_initializer( &mut self, initializer: RuntimeInitializer

, diff --git a/crates/bevy_mod_scripting_core/src/reflection_extensions.rs b/crates/bevy_mod_scripting_core/src/reflection_extensions.rs index d009acd333..cc42a26a7b 100644 --- a/crates/bevy_mod_scripting_core/src/reflection_extensions.rs +++ b/crates/bevy_mod_scripting_core/src/reflection_extensions.rs @@ -1,3 +1,5 @@ +//! Various utility functions for working with reflection types. + use crate::{ bindings::{ReflectReference, WorldGuard}, error::InteropError, @@ -11,18 +13,22 @@ use std::{ }; /// Extension trait for [`PartialReflect`] providing additional functionality for working with specific types. pub trait PartialReflectExt { + /// Try to remove the value at the given key, if the type supports removing with the given key. fn try_remove_boxed( &mut self, key: Box, ) -> Result>, InteropError>; + /// Convert index keys to 0-indexed keys if this type is an index key. fn convert_to_0_indexed_key(&mut self); + /// Try to create a new instance of the concrete type from a possibly untyped reference. fn from_reflect_or_clone( reflect: &dyn PartialReflect, world: WorldGuard, ) -> Box; + /// Allocate a new boxed reflect reference from a boxed reflect. fn allocate(boxed: Box, world: WorldGuard) -> ReflectReference; /// Check if the represented type is from the given crate and has the given type identifier, @@ -69,7 +75,7 @@ pub trait PartialReflectExt { /// For maps, there is no natural `end`, so the push will error out fn try_push_boxed(&mut self, value: Box) -> Result<(), InteropError>; - // If the type has a natural last element to pop, pops the last element and returns it as a boxed value. + /// If the type has a natural last element to pop, pops the last element and returns it as a boxed value. fn try_pop_boxed(&mut self) -> Result, InteropError>; /// If the type is a container type, empties the contents @@ -90,7 +96,10 @@ pub trait PartialReflectExt { world: WorldGuard, ) -> Result, InteropError>; } + +/// Extension trait for [`TypeId`] providing additional functionality for working with type ids. pub trait TypeIdExtensions { + /// Returns the type id if it is Some, otherwise returns a fake type id. fn or_fake_id(&self) -> TypeId; } @@ -410,11 +419,17 @@ impl PartialReflectExt for T { } } +/// Extension trait for TypeInfos providing additional functionality for working with type information. pub trait TypeInfoExtensions { + /// Returns the inner type of the list if the type is a list, otherwise None. fn list_inner_type(&self) -> Option; + /// Returns true if the type is a list. fn is_list(&self) -> bool; + /// Returns true if the type is an option. fn is_option(&self) -> bool; + /// Returns the inner type of the option if the type is an option, otherwise None. fn option_inner_type(&self) -> Option; + /// Returns true if the type is the given type from the given crate. fn is_type(&self, crate_name: Option<&str>, type_ident: &str) -> bool; } @@ -445,10 +460,14 @@ impl TypeInfoExtensions for TypeInfo { } } +/// Extension trait for [`Return`] providing additional functionality for working with return values. pub trait ReturnValExt<'a> { + /// Try to convert the return value into the concrete type, or return a boxed partial reflect if the conversion fails. fn try_into_or_boxed( self, ) -> Result>; + + /// Get a reference to the partial reflect value. fn as_ref(&'a self) -> &'a dyn PartialReflect; } diff --git a/crates/bevy_mod_scripting_core/src/runtime.rs b/crates/bevy_mod_scripting_core/src/runtime.rs index 01ac1cdf30..eb3cb666a0 100644 --- a/crates/bevy_mod_scripting_core/src/runtime.rs +++ b/crates/bevy_mod_scripting_core/src/runtime.rs @@ -7,14 +7,18 @@ use bevy::{ prelude::{NonSendMut, Res}, }; +/// A trait that all script runtimes must implement. pub trait Runtime: 'static {} impl Runtime for T {} +/// A function that initializes a runtime. pub type RuntimeInitializer

= fn(&mut

::R) -> Result<(), ScriptError>; #[derive(Resource)] +/// Resource storing settings for a scripting plugin regarding runtime initialization & configuration. pub struct RuntimeSettings { + /// Initializers for the runtime. These are run when the runtime is initialized. pub initializers: Vec>, } @@ -37,10 +41,11 @@ impl Clone for RuntimeSettings

{ /// Stores a particular runtime. #[derive(Resource)] pub struct RuntimeContainer { + /// The runtime contained within. pub runtime: P::R, } -pub fn initialize_runtime( +pub(crate) fn initialize_runtime( mut runtime: NonSendMut>, settings: Res>, ) -> Result<(), ScriptError> { diff --git a/crates/bevy_mod_scripting_core/src/script.rs b/crates/bevy_mod_scripting_core/src/script.rs index 8b17ebfca6..81b1b3bf96 100644 --- a/crates/bevy_mod_scripting_core/src/script.rs +++ b/crates/bevy_mod_scripting_core/src/script.rs @@ -1,10 +1,19 @@ +//! Script related types, functions and components + use crate::{asset::ScriptAsset, context::ContextId}; use bevy::{asset::Handle, ecs::system::Resource, reflect::Reflect}; use std::{borrow::Cow, collections::HashMap, ops::Deref}; +/// A unique identifier for a script, by default corresponds to the path of the asset excluding the asset source. +/// +/// I.e. an asset with the path `path/to/asset.ext` will have the script id `path/to/asset.ext` pub type ScriptId = Cow<'static, str>; #[derive(bevy::ecs::component::Component, Reflect, Clone)] + +/// A component which identifies the scripts existing on an entity. +/// +/// Event handlers search for components with this component to figure out which scripts to run and on which entities. pub struct ScriptComponent(pub Vec); impl Deref for ScriptComponent { @@ -16,6 +25,7 @@ impl Deref for ScriptComponent { } impl ScriptComponent { + /// Creates a new [`ScriptComponent`] with the given ScriptID's pub fn new(components: Vec) -> Self { Self(components) } @@ -30,6 +40,7 @@ pub struct Scripts { /// A script #[derive(Clone)] pub struct Script { + /// The id of the script pub id: ScriptId, /// the asset holding the content of the script if it comes from an asset pub asset: Option>, diff --git a/crates/bevy_mod_scripting_functions/src/lib.rs b/crates/bevy_mod_scripting_functions/src/lib.rs index 3293a82e77..3a05127a05 100644 --- a/crates/bevy_mod_scripting_functions/src/lib.rs +++ b/crates/bevy_mod_scripting_functions/src/lib.rs @@ -1,9 +1,12 @@ +#![allow(missing_docs)] + use ::bevy::prelude::*; #[cfg(feature = "bevy_bindings")] pub mod bevy_bindings; pub mod core; pub use core::*; +/// A plugin that registers the core scripting functions. pub struct ScriptFunctionsPlugin; impl Plugin for ScriptFunctionsPlugin { diff --git a/crates/languages/bevy_mod_scripting_lua/src/bindings/mod.rs b/crates/languages/bevy_mod_scripting_lua/src/bindings/mod.rs index c589a51f46..b95b3a6cd1 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/bindings/mod.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/bindings/mod.rs @@ -1,3 +1,4 @@ +/// ReflectReference implementations and lua wrappers pub mod reference; +/// ScriptValue implementations and lua wrappers pub mod script_value; -pub mod world; diff --git a/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs b/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs index 97b935709d..7d39eb8f70 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs @@ -9,6 +9,7 @@ use std::{ }; #[derive(Debug, Clone)] +/// A wrapper around a [`ScriptValue`] that implements [`FromLua`] and [`IntoLua`] pub struct LuaScriptValue(pub ScriptValue); impl Deref for LuaScriptValue { @@ -102,6 +103,7 @@ impl FromLua for LuaScriptValue { } } +/// The context for calling a function from Lua pub const LUA_CALLER_CONTEXT: FunctionCallContext = FunctionCallContext { convert_to_0_indexed: true, }; diff --git a/crates/languages/bevy_mod_scripting_lua/src/bindings/world.rs b/crates/languages/bevy_mod_scripting_lua/src/bindings/world.rs deleted file mode 100644 index a4185deb74..0000000000 --- a/crates/languages/bevy_mod_scripting_lua/src/bindings/world.rs +++ /dev/null @@ -1,21 +0,0 @@ -// use std::sync::Arc; - -// use bevy_mod_scripting_core::bindings::WorldCallbackAccess; -// use mlua::UserData; - -// #[derive(Clone, Debug, mlua::FromLua)] -// pub struct LuaWorld(pub WorldCallbackAccess); - -// impl LuaWorld { -// pub fn world_callback_access(self) -> WorldCallbackAccess { -// self.0.clone() -// } -// } - -// impl UserData for LuaWorld {} - -// impl From<&LuaWorld> for WorldCallbackAccess { -// fn from(value: &LuaWorld) -> Self { -// value.0.clone() -// } -// } diff --git a/crates/languages/bevy_mod_scripting_lua/src/lib.rs b/crates/languages/bevy_mod_scripting_lua/src/lib.rs index 46679c103e..3b3e89374c 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/lib.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/lib.rs @@ -1,5 +1,7 @@ +//! Lua integration for the bevy_mod_scripting system. use bevy::{ app::Plugin, + asset::AssetPath, ecs::{entity::Entity, world::World}, }; use bevy_mod_scripting_core::{ @@ -22,6 +24,8 @@ use bindings::{ }; pub use mlua; use mlua::{Function, IntoLua, Lua, MultiValue}; + +/// Bindings for lua. pub mod bindings; impl IntoScriptPluginParams for LuaScriptingPlugin { @@ -39,7 +43,9 @@ impl AsMut> for LuaScriptingPlugin { } } +/// The lua scripting plugin. Used to add lua scripting to a bevy app within the context of the BMS framework. pub struct LuaScriptingPlugin { + /// The internal scripting plugin pub scripting_plugin: ScriptingPlugin, } @@ -133,8 +139,8 @@ impl Default for LuaScriptingPlugin { } } #[profiling::function] -fn lua_language_mapper(path: &std::path::Path) -> Language { - match path.extension().and_then(|ext| ext.to_str()) { +fn lua_language_mapper(path: &AssetPath) -> Language { + match path.path().extension().and_then(|ext| ext.to_str()) { Some("lua") => Language::Lua, _ => Language::Unknown, } @@ -146,6 +152,7 @@ impl Plugin for LuaScriptingPlugin { } } #[profiling::function] +/// Load a lua context from a script pub fn lua_context_load( script_id: &ScriptId, content: &[u8], @@ -174,6 +181,7 @@ pub fn lua_context_load( Ok(context) } #[profiling::function] +/// Reload a lua context from a script pub fn lua_context_reload( script: &ScriptId, content: &[u8], @@ -194,6 +202,7 @@ pub fn lua_context_reload( #[allow(clippy::too_many_arguments)] #[profiling::function] +/// The lua handler for events pub fn lua_handler( args: Vec, entity: bevy::ecs::entity::Entity, diff --git a/crates/languages/bevy_mod_scripting_lua/tests/lua_tests.rs b/crates/languages/bevy_mod_scripting_lua/tests/lua_tests.rs index bc20b21230..5ceddb7acd 100644 --- a/crates/languages/bevy_mod_scripting_lua/tests/lua_tests.rs +++ b/crates/languages/bevy_mod_scripting_lua/tests/lua_tests.rs @@ -1,4 +1,4 @@ -#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] +#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic, missing_docs)] use bevy_mod_scripting_core::{ bindings::{pretty_print::DisplayWithWorld, ThreadWorldContainer, WorldContainer}, error::ScriptError, diff --git a/crates/languages/bevy_mod_scripting_rhai/src/bindings/mod.rs b/crates/languages/bevy_mod_scripting_rhai/src/bindings/mod.rs index 7f3ef78fa8..b453b97f0e 100644 --- a/crates/languages/bevy_mod_scripting_rhai/src/bindings/mod.rs +++ b/crates/languages/bevy_mod_scripting_rhai/src/bindings/mod.rs @@ -1,2 +1,4 @@ +/// ReflectReference implementations and rhai wrappers pub mod reference; +/// ScriptValue implementations and rhai wrappers pub mod script_value; diff --git a/crates/languages/bevy_mod_scripting_rhai/src/bindings/reference.rs b/crates/languages/bevy_mod_scripting_rhai/src/bindings/reference.rs index 1618ecb24e..65c2ee6fd6 100644 --- a/crates/languages/bevy_mod_scripting_rhai/src/bindings/reference.rs +++ b/crates/languages/bevy_mod_scripting_rhai/src/bindings/reference.rs @@ -15,6 +15,8 @@ use std::{ use strum::VariantNames; #[derive(Debug, strum::EnumString, strum::VariantNames, Clone)] +/// A list of reserved keywords in Rhai +#[allow(missing_docs)] pub enum ReservedKeyword { // Reserved under certain flags #[strum(serialize = "?.")] @@ -155,12 +157,14 @@ pub enum ReservedKeyword { } impl ReservedKeyword { + /// Returns whether the given string is a reserved keyword in Rhai pub fn is_reserved_keyword(s: impl AsRef) -> bool { ReservedKeyword::VARIANTS.iter().any(|v| v == &s.as_ref()) } } #[derive(Clone, Debug, PartialEq)] +/// A wrapper around a [`ReflectReference`] that implements [`CustomType`] for Rhai pub struct RhaiReflectReference(pub ReflectReference); impl AsRef for RhaiReflectReference { @@ -195,6 +199,8 @@ impl DerefMut for RhaiReflectReference { } } +/// A rhai operator enum +#[allow(missing_docs)] pub enum RhaiOperator { Sub, Add, @@ -209,6 +215,7 @@ pub enum RhaiOperator { } impl RhaiOperator { + /// Returns the function name for the operator pub fn function_name(self) -> &'static str { match self { RhaiOperator::Sub => "-", @@ -225,6 +232,7 @@ impl RhaiOperator { } } +/// An iterator over a [`ReflectReference`] that implements [`IntoIterator`] for Rhai pub struct RhaiReflectRefIter { next_func: DynamicScriptFunctionMut, } @@ -536,6 +544,7 @@ impl CustomType for RhaiReflectReference { } #[derive(Clone, Debug, Copy, PartialEq)] +/// A wrapper around a [`TypeId`] that implements [`CustomType`] for Rhai pub struct RhaiStaticReflectReference(pub TypeId); impl CustomType for RhaiStaticReflectReference { diff --git a/crates/languages/bevy_mod_scripting_rhai/src/bindings/script_value.rs b/crates/languages/bevy_mod_scripting_rhai/src/bindings/script_value.rs index f3d4a8ad12..bb917b6700 100644 --- a/crates/languages/bevy_mod_scripting_rhai/src/bindings/script_value.rs +++ b/crates/languages/bevy_mod_scripting_rhai/src/bindings/script_value.rs @@ -10,17 +10,21 @@ use std::str::FromStr; use super::reference::RhaiReflectReference; +/// The default function call context for rhai pub const RHAI_CALLER_CONTEXT: FunctionCallContext = FunctionCallContext { convert_to_0_indexed: false, }; /// A function curried with one argument, i.e. the receiver pub struct FunctionWithReceiver { + /// The function pub function: DynamicScriptFunction, + /// The receiver pub receiver: ScriptValue, } impl FunctionWithReceiver { + /// Create a new function with receiver pub fn curry(function: DynamicScriptFunction, receiver: ScriptValue) -> Self { Self { function, receiver } } @@ -48,7 +52,9 @@ impl IntoDynamic for FunctionWithReceiver { } } +/// A trait for converting types into a [`Dynamic`] value pub trait IntoDynamic { + /// Convert the type into a [`Dynamic`] value fn into_dynamic(self) -> Result>; } @@ -125,7 +131,9 @@ impl IntoDynamic for ScriptValue { } } +/// A trait for converting a [`Dynamic`] value into a type pub trait FromDynamic: Sized { + /// Convert a [`Dynamic`] value into a type fn from_dynamic(dynamic: Dynamic) -> Result>; } diff --git a/crates/languages/bevy_mod_scripting_rhai/src/lib.rs b/crates/languages/bevy_mod_scripting_rhai/src/lib.rs index 17d84e358e..29caefe910 100644 --- a/crates/languages/bevy_mod_scripting_rhai/src/lib.rs +++ b/crates/languages/bevy_mod_scripting_rhai/src/lib.rs @@ -1,5 +1,8 @@ +//! Rhai scripting language support for Bevy. + use bevy::{ app::Plugin, + asset::AssetPath, ecs::{entity::Entity, world::World}, }; use bevy_mod_scripting_core::{ @@ -23,12 +26,17 @@ use bindings::{ use rhai::{CallFnOptions, Dynamic, Engine, EvalAltResult, Scope, AST}; pub use rhai; +/// Bindings for rhai. pub mod bindings; +/// The rhai runtime type. pub type RhaiRuntime = Engine; +/// The rhai context type. pub struct RhaiScriptContext { + /// The AST of the script pub ast: AST, + /// The scope of the script pub scope: Scope<'static>, } @@ -43,7 +51,9 @@ impl IntoScriptPluginParams for RhaiScriptingPlugin { } } +/// The rhai scripting plugin. Used to add rhai scripting to a bevy app within the context of the BMS framework. pub struct RhaiScriptingPlugin { + /// The internal scripting plugin pub scripting_plugin: ScriptingPlugin, } @@ -150,8 +160,8 @@ impl Default for RhaiScriptingPlugin { } } -fn rhai_language_mapper(path: &std::path::Path) -> Language { - match path.extension().and_then(|ext| ext.to_str()) { +fn rhai_language_mapper(path: &AssetPath) -> Language { + match path.path().extension().and_then(|ext| ext.to_str()) { Some("rhai") => Language::Rhai, _ => Language::Unknown, } @@ -163,6 +173,7 @@ impl Plugin for RhaiScriptingPlugin { } } +/// Load a rhai context from a script. pub fn rhai_context_load( script: &ScriptId, content: &[u8], @@ -192,6 +203,7 @@ pub fn rhai_context_load( Ok(context) } +/// Reload a rhai context from a script. pub fn rhai_context_reload( script: &ScriptId, content: &[u8], @@ -211,6 +223,7 @@ pub fn rhai_context_reload( } #[allow(clippy::too_many_arguments)] +/// The rhai callback handler. pub fn rhai_callback_handler( args: Vec, entity: Entity, diff --git a/crates/languages/bevy_mod_scripting_rhai/tests/rhai_tests.rs b/crates/languages/bevy_mod_scripting_rhai/tests/rhai_tests.rs index afe157b0de..2ab89b04c6 100644 --- a/crates/languages/bevy_mod_scripting_rhai/tests/rhai_tests.rs +++ b/crates/languages/bevy_mod_scripting_rhai/tests/rhai_tests.rs @@ -1,4 +1,10 @@ -#![allow(clippy::unwrap_used, clippy::todo, clippy::expect_used, clippy::panic)] +#![allow( + clippy::unwrap_used, + clippy::todo, + clippy::expect_used, + clippy::panic, + missing_docs +)] use bevy_mod_scripting_core::{ bindings::{pretty_print::DisplayWithWorld, ThreadWorldContainer, WorldContainer}, error::ScriptError, diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 7485aacd46..23f2f54492 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -9,6 +9,7 @@ - [Controlling Script Bindings](./Summary/controlling-script-bindings.md) - [Modifying Script Contexts](./Summary/customizing-script-contexts.md) - [Shared Contexts](./Summary/sharing-contexts-between-scripts.md) +- [Script ID Mapping](./Summary/script-id-mapping.md) # Scripting Reference diff --git a/docs/src/ScriptingReference/reflect-reference.md b/docs/src/ScriptingReference/reflect-reference.md index 052192a4e1..dca9880d0e 100644 --- a/docs/src/ScriptingReference/reflect-reference.md +++ b/docs/src/ScriptingReference/reflect-reference.md @@ -1,9 +1,9 @@ # ReflectReference -ReflectReferences are simply references to date living either: -- In a component -- In a resource -- In the allocator +ReflectReferences are simply references to data living either in: +- A component +- A resource +- The allocator Reflect references contain a standard interface which operates over the reflection layer exposed by `Bevy` and also provides a way to call various dynamic functions registered on the underlying pointed to data. @@ -220,3 +220,20 @@ for val in pairs(ref) do print(val) end ``` + +## functions +Returns a list of functions that can be called on the reference. + +Returns: + +| Return | Description | +| --- | --- | +| `Vec` | The list of functions | + +```lua +local functions = ref:functions() +for _, func in ipairs(functions) do + print(func.name) + +end +``` \ No newline at end of file diff --git a/docs/src/Summary/controlling-script-bindings.md b/docs/src/Summary/controlling-script-bindings.md index 4b143a24e6..94bc38413d 100644 --- a/docs/src/Summary/controlling-script-bindings.md +++ b/docs/src/Summary/controlling-script-bindings.md @@ -4,6 +4,12 @@ In this book we reffer to anything accessible by a script, which allows it to co The "binding" here being used as in: binding `script` code to `rust` code. +## Namespaces + +Namespaces are a way to group functions together, and are used to prevent naming conflicts. You can have multiple namespaces, and each namespace can have multiple functions. + +Language implementations will also look for specific functions registered on your type first before looking at the generic `ReflectReference` namespace. + ## Dynamic Functions Everything callable by scripts must first be registered in the dynamic function registry. Notably we do not make use of the normal bevy function registry to improve performance and usability. This means you cannot call just any function. @@ -34,14 +40,25 @@ Registering functions can be done via the `NamespaceBuilder` like below: println!(s) }, ); + + NamespaceBuilder::::new_unregistered(&mut world) + .register( + "hello_world2", + |s: String| { + println!(s) + }, + ); ``` This will allow you to call this function within lua like so: ```lua -hello_world("hi from lua!") +some_type:hello_world("hi from method!"); +hello_world2("hi from global!"); ``` +Note the `new_unregistered` call instead of `new`, this is because `GlobalNamespace` is not a `Reflect` type, and the `new` call also automatically registers the type in the reflection registry. + ## Context Arguments Each script function call always receives an additional context argument: `FunctionCallContext`. @@ -92,3 +109,25 @@ Your script functions can return errors either by: - Returning `Result` - Returning `ScriptValue` and manually creating the `ScriptValue::Error(into_interop_erorr.into())` variant. +## Reserved Functions + +There are a few reserved functions that you can override by registering them on a specific type: + +| Function Name | Description | Overridable? | Has Default Implementation? | +|---------------|-------------| ------------ | --------------------------- | +| get | a getter function, used for indexing into a type | ✅ | ✅ | +| set | a setter function, used for setting a value on a type | ✅ | ✅ | +| sub | a subtraction function, used for subtracting two values | ✅ | ❌ | +| add | an addition function, used for adding two values | ✅ | ❌ | +| mul | a multiplication function, used for multiplying two values | ✅ | ❌ | +| div | a division function, used for dividing two values | ✅ | ❌ | +| rem | a remainder function, used for getting the remainder of two values | ✅ | ❌ | +| neg | a negation function, used for negating a value | ✅ | ❌ | +| pow | a power function, used for raising a value to a power | ✅ | ❌ | +| eq | an equality function, used for checking if two values are equal | ✅ | ❌ | +| lt | a less than function, used for checking if a value is less than another | ✅ | ❌ | +| iter | an iterator function, used for iterating over a value | ❌ | ✅ | +| display_ref | a display function, used for displaying a reference to a value | ❌ | ✅ | +| display_value | a display function, used for displaying a mutable reference to a value | ❌ | ✅ | + +In this context `overridable` indicates whether language implementations will look for a specific function on your type before looking at the generic `ReflectReference` namespace. You can still remove the existing registration for these functions on the `ReflectReference` namespace if you want to replace them with your own implementation. \ No newline at end of file diff --git a/docs/src/Summary/customizing-script-contexts.md b/docs/src/Summary/customizing-script-contexts.md index 4aefd4bb07..071a7a72bc 100644 --- a/docs/src/Summary/customizing-script-contexts.md +++ b/docs/src/Summary/customizing-script-contexts.md @@ -45,6 +45,8 @@ let plugin = SomeScriptingPlugin::default().add_runtime_initializer(|runtime: &m }); ``` +In the case of Lua, the runtime type is `()` i.e. This is because `mlua` does not have a separate runtime concept. + ## Accessing the World in Initializers You can access the world in these initializers by using the thread local: `ThreadWorldContainer`: @@ -52,7 +54,7 @@ You can access the world in these initializers by using the thread local: `Threa let plugin = LuaScriptingPlugin::default(); plugin.add_context_initializer(|script_id: &str, context: &mut Lua| { - let world = ThreadWorldContainer::get_world(); + let world = ThreadWorldContainer.try_get_world().unwrap(); world.with_resource::(|res| println!("My resource: {:?}", res)); Ok(()) }); diff --git a/docs/src/Summary/managing-scripts.md b/docs/src/Summary/managing-scripts.md index 0559668492..43d4d57abb 100644 --- a/docs/src/Summary/managing-scripts.md +++ b/docs/src/Summary/managing-scripts.md @@ -49,4 +49,4 @@ DeleteScript::::new("my_script.lua".into()) replace `LuaScriptingPlugin` with the scripting plugin you are using. ## Loading/Unloading timeframe -Scripts are processed via commands, so any asset events will be processed at the next command execution point running after BMS internal asset systems. \ No newline at end of file +Scripts asset events are processed within the same frame they are issued. This means the moment an asset is loaded, it should be loaded and ready to use in the `Update` schedule. Similarly, the moment an asset is deleted, it should be unloaded and no longer usable in the `Update` schedule. \ No newline at end of file diff --git a/docs/src/Summary/script-id-mapping.md b/docs/src/Summary/script-id-mapping.md new file mode 100644 index 0000000000..0dccd85de5 --- /dev/null +++ b/docs/src/Summary/script-id-mapping.md @@ -0,0 +1,7 @@ +# Script ID mapping + +Every script is currently identified by a unique ID. + +ID's are derived from the script asset path for scripts loaded via the asset system. + +By default this is an identity mapping, but you can override this by modifying the `AssetPathToScriptIdMapper` inside the `ScriptAssetSettings` resource before loading the script. \ No newline at end of file diff --git a/readme.md b/readme.md index a9edc8e66b..9850c4bcd9 100644 --- a/readme.md +++ b/readme.md @@ -38,14 +38,14 @@ The languages currently supported are as follows: |Luajit| |Luajit52| |Luau| -|~~Rhai~~ temporarilly on hold[^1]| +|Rhai| |~~Rune~~ temporarilly on hold[^1]| ## Documentation For examples, installation and usage instructions see our shiny new [book](https://makspll.github.io/bevy_mod_scripting) -[^1]: Due to the recent re-write of the crate, documentation generation as well as rhai and rune support are temporarilly on hold. They will likely be re-implemented in the future. `Rhai` in particualar is difficult to re-implement due to a lack of support for first-class-functions. +[^1]: Due to the recent re-write of the crate, documentation generation as well as rune support are temporarilly on hold. They will likely be re-implemented in the future. [^2]: the coverage does not include generated bindings.