From eb1212d6a07797c46fbf8e61d4764f60badd1e65 Mon Sep 17 00:00:00 2001 From: makspll Date: Mon, 8 Sep 2025 01:22:38 +0100 Subject: [PATCH 1/5] feature: improved dynamic printing --- Cargo.toml | 23 +- crates/bevy_mod_scripting_asset/Cargo.toml | 18 +- crates/bevy_mod_scripting_asset/readme.md | 3 - .../bevy_mod_scripting_asset/src/language.rs | 18 +- crates/bevy_mod_scripting_bindings/Cargo.toml | 19 +- .../src/access_map.rs | 28 +- .../src/allocator.rs | 4 +- .../src/docgen/info.rs | 6 +- .../bevy_mod_scripting_bindings/src/error.rs | 102 +++++-- .../src/function/namespace.rs | 8 +- .../src/reference.rs | 106 +++++-- .../src/script_value.rs | 15 +- .../bevy_mod_scripting_bindings/src/world.rs | 88 +++++- crates/bevy_mod_scripting_core/Cargo.toml | 18 +- crates/bevy_mod_scripting_core/readme.md | 3 - .../bevy_mod_scripting_core/src/commands.rs | 54 ++-- crates/bevy_mod_scripting_core/src/context.rs | 22 +- crates/bevy_mod_scripting_core/src/error.rs | 254 ++++++++++++++-- .../bevy_mod_scripting_core/src/extractors.rs | 10 +- crates/bevy_mod_scripting_core/src/handler.rs | 88 ++++-- crates/bevy_mod_scripting_core/src/lib.rs | 16 +- .../src/script_system.rs | 8 +- crates/bevy_mod_scripting_derive/Cargo.toml | 18 +- crates/bevy_mod_scripting_derive/readme.md | 3 - .../src/derive/debug_with_type_info.rs | 4 +- crates/bevy_mod_scripting_display/Cargo.toml | 20 +- .../src/impls/bevy_ecs.rs | 46 +-- .../src/impls/bevy_platform.rs | 12 +- .../src/impls/parking_lock.rs | 12 +- .../src/impls/std.rs | 73 +++-- crates/bevy_mod_scripting_display/src/lib.rs | 272 ++++++++++++++---- .../src/printer/mod.rs | 140 ++++++--- .../bevy_mod_scripting_functions/Cargo.toml | 18 +- crates/bevy_mod_scripting_functions/readme.md | 3 - .../bevy_a11y_bms_bindings/Cargo.toml | 17 +- .../bevy_animation_bms_bindings/Cargo.toml | 17 +- .../bevy_asset_bms_bindings/Cargo.toml | 17 +- .../bevy_color_bms_bindings/Cargo.toml | 21 +- .../Cargo.toml | 17 +- .../bindings/bevy_ecs_bms_bindings/Cargo.toml | 19 +- .../bevy_gizmos_bms_bindings/Cargo.toml | 17 +- .../bevy_gltf_bms_bindings/Cargo.toml | 17 +- .../bevy_image_bms_bindings/Cargo.toml | 19 +- .../bevy_input_bms_bindings/Cargo.toml | 17 +- .../bevy_input_focus_bms_bindings/Cargo.toml | 17 +- .../bevy_math_bms_bindings/Cargo.toml | 25 +- .../bevy_mesh_bms_bindings/Cargo.toml | 17 +- .../bindings/bevy_pbr_bms_bindings/Cargo.toml | 17 +- .../bevy_picking_bms_bindings/Cargo.toml | 17 +- .../bevy_reflect_bms_bindings/Cargo.toml | 17 +- .../bevy_render_bms_bindings/Cargo.toml | 17 +- .../bevy_scene_bms_bindings/Cargo.toml | 17 +- .../bevy_sprite_bms_bindings/Cargo.toml | 17 +- .../bevy_text_bms_bindings/Cargo.toml | 17 +- .../bevy_time_bms_bindings/Cargo.toml | 17 +- .../bevy_transform_bms_bindings/Cargo.toml | 19 +- .../mdbook_lad_preprocessor/Cargo.toml | 15 +- crates/ladfile/Cargo.toml | 15 +- crates/ladfile_builder/Cargo.toml | 17 +- crates/ladfile_builder/readme.md | 3 - .../bevy_mod_scripting_lua/Cargo.toml | 18 +- .../bevy_mod_scripting_lua/readme.md | 3 - .../bevy_mod_scripting_lua/src/lib.rs | 9 +- .../bevy_mod_scripting_rhai/Cargo.toml | 18 +- .../bevy_mod_scripting_rhai/readme.md | 3 - .../bevy_mod_scripting_rhai/src/lib.rs | 29 +- .../src/lib.rs | 2 +- xtask/templates/bindings_crate.toml.tera | 17 +- 68 files changed, 1383 insertions(+), 690 deletions(-) delete mode 100644 crates/bevy_mod_scripting_asset/readme.md delete mode 100644 crates/bevy_mod_scripting_core/readme.md delete mode 100644 crates/bevy_mod_scripting_derive/readme.md delete mode 100644 crates/bevy_mod_scripting_functions/readme.md delete mode 100644 crates/ladfile_builder/readme.md delete mode 100644 crates/languages/bevy_mod_scripting_lua/readme.md delete mode 100644 crates/languages/bevy_mod_scripting_rhai/readme.md diff --git a/Cargo.toml b/Cargo.toml index cc9902f9c2..7dfd066606 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,27 @@ [package] name = "bevy_mod_scripting" +description = "Multi language scripting in Bevy" +include = ["readme.md", "/src", "/examples", "/assets", "LICENSE", "/badges"] +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +readme.workspace = true + +[workspace.package] version = "0.15.1" -authors = ["Maksymilian Mozolewski "] edition = "2024" +authors = ["Maksymilian Mozolewski "] license = "MIT OR Apache-2.0" -description = "Multi language scripting in Bevy" -repository = "https://github.com/makspll/bevy_mod_scripting" homepage = "https://github.com/makspll/bevy_mod_scripting" +repository = "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", "/badges"] [lib] name = "bevy_mod_scripting" @@ -292,6 +303,10 @@ name = "docgen" path = "examples/docgen.rs" required-features = [] +[[example]] +name = "runscript" +path = "examples/run-script.rs" + [workspace.lints.clippy] panic = "deny" unwrap_used = "deny" diff --git a/crates/bevy_mod_scripting_asset/Cargo.toml b/crates/bevy_mod_scripting_asset/Cargo.toml index 0699b70fa6..f99cb60cd1 100644 --- a/crates/bevy_mod_scripting_asset/Cargo.toml +++ b/crates/bevy_mod_scripting_asset/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "bevy_mod_scripting_asset" -version = "0.15.1" -authors = ["Maksymilian Mozolewski "] -edition = "2024" -license = "MIT OR Apache-2.0" description = "Core traits and structures required for other parts of bevy_mod_scripting" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -categories = ["game-development"] -readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +readme.workspace = true [dependencies] bevy_reflect = { workspace = true } diff --git a/crates/bevy_mod_scripting_asset/readme.md b/crates/bevy_mod_scripting_asset/readme.md deleted file mode 100644 index 8ab67ce5a9..0000000000 --- a/crates/bevy_mod_scripting_asset/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# bevy_mod_scripting_core - -This crate is a part of the ["bevy_mod_scripting" workspace](https://github.com/makspll/bevy_mod_scripting). \ No newline at end of file diff --git a/crates/bevy_mod_scripting_asset/src/language.rs b/crates/bevy_mod_scripting_asset/src/language.rs index cfa66e54e5..fb590cfcaf 100644 --- a/crates/bevy_mod_scripting_asset/src/language.rs +++ b/crates/bevy_mod_scripting_asset/src/language.rs @@ -21,12 +21,18 @@ pub enum Language { impl std::fmt::Display for Language { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Language::Rhai => "Rhai".fmt(f), - Language::Lua => "Lua".fmt(f), - Language::Rune => "Rune".fmt(f), - Language::External(cow) => cow.fmt(f), - Language::Unknown => "Unknown".fmt(f), + Cow::<'static, str>::from(self).fmt(f) + } +} + +impl From<&Language> for Cow<'static, str> { + fn from(val: &Language) -> Self { + match val { + Language::Rhai => Cow::Borrowed("Rhai"), + Language::Lua => Cow::Borrowed("Lua"), + Language::Rune => Cow::Borrowed("Rune"), + Language::External(cow) => cow.clone(), + Language::Unknown => Cow::Borrowed("Unknown"), } } } diff --git a/crates/bevy_mod_scripting_bindings/Cargo.toml b/crates/bevy_mod_scripting_bindings/Cargo.toml index a51a4eeb95..a1c0a3fbcb 100644 --- a/crates/bevy_mod_scripting_bindings/Cargo.toml +++ b/crates/bevy_mod_scripting_bindings/Cargo.toml @@ -1,14 +1,15 @@ [package] name = "bevy_mod_scripting_bindings" -version = "0.15.1" -authors = ["Maksymilian Mozolewski "] -edition = "2024" -license = "MIT OR Apache-2.0" -description = "Core traits and structures required for other parts of bevy_mod_scripting" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -categories = ["game-development"] -readme = "readme.md" +description = "Core traits and structures required for smoothly interfacing with other languages in a generic way" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +readme.workspace = true [dependencies] diff --git a/crates/bevy_mod_scripting_bindings/src/access_map.rs b/crates/bevy_mod_scripting_bindings/src/access_map.rs index f43b88f6a6..efa1524d5e 100644 --- a/crates/bevy_mod_scripting_bindings/src/access_map.rs +++ b/crates/bevy_mod_scripting_bindings/src/access_map.rs @@ -109,20 +109,26 @@ impl DisplayWithTypeInfo for ReflectAccessId { fn display_with_type_info( &self, f: &mut std::fmt::Formatter<'_>, - _type_info_provider: Option<&dyn GetTypeInfo>, + type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { match self.kind { ReflectAccessKind::ComponentOrResource => { write!( f, "Component or resource: {}", - WithTypeInfo(&ComponentId::new(self.id as usize)) + WithTypeInfo::new_with_opt_info( + &ComponentId::new(self.id as usize), + type_info_provider + ) ) } ReflectAccessKind::Allocation => write!( f, "Allocation to: {}", - WithTypeInfo(&ReflectAllocationId::new(self.id)) + WithTypeInfo::new_with_opt_info( + &ReflectAllocationId::new(self.id), + type_info_provider + ) ), ReflectAccessKind::Global => write!(f, "World(Global)"), } @@ -221,23 +227,15 @@ impl ReflectAccessId { } } -#[profiling::all_functions] impl From for ReflectAccessId { - fn from(value: ComponentId) -> Self { - Self { - kind: ReflectAccessKind::ComponentOrResource, - id: value.index() as u64, - } + fn from(id: ComponentId) -> Self { + ReflectAccessId::for_component_id(id) } } -#[profiling::all_functions] impl From for ReflectAccessId { - fn from(value: ReflectAllocationId) -> Self { - Self { - kind: ReflectAccessKind::Allocation, - id: value.id(), - } + fn from(id: ReflectAllocationId) -> Self { + ReflectAccessId::for_allocation(id) } } diff --git a/crates/bevy_mod_scripting_bindings/src/allocator.rs b/crates/bevy_mod_scripting_bindings/src/allocator.rs index 8dcc163b0c..3258911134 100644 --- a/crates/bevy_mod_scripting_bindings/src/allocator.rs +++ b/crates/bevy_mod_scripting_bindings/src/allocator.rs @@ -119,9 +119,9 @@ impl DebugWithTypeInfo for ReflectAllocation { fn to_string_with_type_info( &self, f: &mut std::fmt::Formatter<'_>, - _type_info_provider: Option<&dyn bevy_mod_scripting_display::GetTypeInfo>, + type_info_provider: Option<&dyn bevy_mod_scripting_display::GetTypeInfo>, ) -> std::fmt::Result { - f.debug_tuple_with_type_info("ReflectAllocation") + f.debug_tuple_with_type_info("ReflectAllocation", type_info_provider) .field(&((self.0.get() as *mut ()) as usize)) .finish() } diff --git a/crates/bevy_mod_scripting_bindings/src/docgen/info.rs b/crates/bevy_mod_scripting_bindings/src/docgen/info.rs index fa83a7344f..b9afd5115c 100644 --- a/crates/bevy_mod_scripting_bindings/src/docgen/info.rs +++ b/crates/bevy_mod_scripting_bindings/src/docgen/info.rs @@ -135,7 +135,8 @@ impl DisplayWithTypeInfo for FunctionArgInfo { write!(f, "{name}: ")?; } let type_id = self.type_id; - WithTypeInfo(&type_id).display_with_type_info(f, type_info_provider)?; + WithTypeInfo::new_with_opt_info(&type_id, type_info_provider) + .display_with_type_info(f, type_info_provider)?; Ok(()) } } @@ -186,7 +187,8 @@ impl DisplayWithTypeInfo for FunctionReturnInfo { type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { let type_id = self.type_id; - WithTypeInfo(&type_id).display_with_type_info(f, type_info_provider)?; + WithTypeInfo::new_with_opt_info(&type_id, type_info_provider) + .display_with_type_info(f, type_info_provider)?; Ok(()) } } diff --git a/crates/bevy_mod_scripting_bindings/src/error.rs b/crates/bevy_mod_scripting_bindings/src/error.rs index 99ef6a8361..8eb7b909ca 100644 --- a/crates/bevy_mod_scripting_bindings/src/error.rs +++ b/crates/bevy_mod_scripting_bindings/src/error.rs @@ -22,7 +22,8 @@ impl bevy_mod_scripting_display::DebugWithTypeInfo for ReflectWrapper { f: &mut std::fmt::Formatter, type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { - PrintReflectAsDebug(&*self.0).to_string_with_type_info(f, type_info_provider) + PrintReflectAsDebug::new_with_opt_info(&*self.0, type_info_provider) + .to_string_with_type_info(f, type_info_provider) } } @@ -33,7 +34,8 @@ impl DisplayWithTypeInfo for ReflectWrapper { type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { // TODO: different display? - PrintReflectAsDebug(&*self.0).to_string_with_type_info(f, type_info_provider) + PrintReflectAsDebug::new_with_opt_info(&*self.0, type_info_provider) + .to_string_with_type_info(f, type_info_provider) } } @@ -435,7 +437,7 @@ impl InteropError { impl std::fmt::Display for InteropError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", WithTypeInfo(self)) + std::fmt::Display::fmt(&WithTypeInfo::new(self), f) } } @@ -445,7 +447,7 @@ impl DisplayWithTypeInfo for InteropError { fn display_with_type_info( &self, f: &mut std::fmt::Formatter, - _type_info_provider: Option<&dyn GetTypeInfo>, + type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { match self { InteropError::NotImplemented => { @@ -455,8 +457,8 @@ impl DisplayWithTypeInfo for InteropError { write!( f, "Value type mismatch: expected {}, got {}", - WithTypeInfo(expected), - WithTypeInfo(value) + WithTypeInfo::new_with_opt_info(expected, type_info_provider), + WithTypeInfo::new_with_opt_info(value, type_info_provider) ) } InteropError::LengthMismatch { expected, got } => { @@ -466,7 +468,10 @@ impl DisplayWithTypeInfo for InteropError { write!( f, "Failed to convert from reflect {}: {}", - WithTypeInfo(&type_id.as_ref().or_fake_id()), + WithTypeInfo::new_with_opt_info( + &type_id.as_ref().or_fake_id(), + type_info_provider + ), reason ) } @@ -474,8 +479,8 @@ impl DisplayWithTypeInfo for InteropError { write!( f, "Type mismatch: expected {}, got {}", - WithTypeInfo(expected), - WithTypeInfo(&got.as_ref().or_fake_id()) + WithTypeInfo::new_with_opt_info(expected, type_info_provider), + WithTypeInfo::new_with_opt_info(&got.as_ref().or_fake_id(), type_info_provider) ) } InteropError::StringTypeMismatch { expected, got } => { @@ -483,7 +488,7 @@ impl DisplayWithTypeInfo for InteropError { f, "Type mismatch: expected {}, got {}", expected, - WithTypeInfo(&got.as_ref().or_fake_id()) + WithTypeInfo::new_with_opt_info(&got.as_ref().or_fake_id(), type_info_provider) ) } InteropError::CannotClaimAccess { @@ -495,7 +500,7 @@ impl DisplayWithTypeInfo for InteropError { write!( f, "Cannot claim access to {} at {}:{}:{}: {}", - WithTypeInfo(base), + WithTypeInfo::new_with_opt_info(base, type_info_provider), location.file(), location.line(), location.column(), @@ -505,7 +510,7 @@ impl DisplayWithTypeInfo for InteropError { write!( f, "Cannot claim access to {}: {}", - WithTypeInfo(base), + WithTypeInfo::new_with_opt_info(base, type_info_provider), context ) } @@ -524,9 +529,15 @@ impl DisplayWithTypeInfo for InteropError { write!( f, "Unsupported operation on {}{}: {}", - WithTypeInfo(&base.as_ref().or_fake_id()), + WithTypeInfo::new_with_opt_info( + &base.as_ref().or_fake_id(), + type_info_provider + ), if let Some(value) = value.as_ref() { - format!(" with value {}", WithTypeInfo(value)) + format!( + " with value {}", + WithTypeInfo::new_with_opt_info(value, type_info_provider) + ) } else { "".to_string() }, @@ -538,7 +549,7 @@ impl DisplayWithTypeInfo for InteropError { f, "Missing type data {} for type: {}", type_data, - WithTypeInfo(type_id) + WithTypeInfo::new_with_opt_info(type_id, type_info_provider) ) } InteropError::MissingEntity(entity) => { @@ -560,7 +571,7 @@ impl DisplayWithTypeInfo for InteropError { f, "Error in function {} on {}: {}", function_name, - WithTypeInfo(on), + WithTypeInfo::new_with_opt_info(on, type_info_provider), error ) } @@ -569,7 +580,7 @@ impl DisplayWithTypeInfo for InteropError { f, "Error converting argument {}: {}", argument, - WithTypeInfo(error) + WithTypeInfo::new_with_opt_info(error, type_info_provider) ) } InteropError::ArgumentCountMismatch { expected, got } => { @@ -579,11 +590,15 @@ impl DisplayWithTypeInfo for InteropError { write!( f, "Garbage collected allocation used: {}", - WithTypeInfo(reference) + WithTypeInfo::new_with_opt_info(reference, type_info_provider) ) } InteropError::UnregisteredReflectBase { base } => { - write!(f, "Unregistered reflect base: {}", WithTypeInfo(base)) + write!( + f, + "Unregistered reflect base: {}", + WithTypeInfo::new_with_opt_info(base, type_info_provider) + ) } InteropError::ReflectionPathError { error, reflected } => { write!( @@ -591,7 +606,10 @@ impl DisplayWithTypeInfo for InteropError { "Reflection path error: {}{}", error, if let Some(reflected) = reflected.as_ref() { - format!(", on value {}", WithTypeInfo(reflected)) + format!( + "\nOn value: {}", + WithTypeInfo::new_with_opt_info(reflected, type_info_provider) + ) } else { "".to_string() } @@ -601,25 +619,59 @@ impl DisplayWithTypeInfo for InteropError { write!( f, "Could not downcast reference {} to type {}", - WithTypeInfo(reference), - WithTypeInfo(to) + WithTypeInfo::new_with_opt_info(reference, type_info_provider), + WithTypeInfo::new_with_opt_info(to, type_info_provider) ) } InteropError::MissingSchedule { schedule_name } => { write!(f, "Missing schedule: {schedule_name}") } InteropError::InvalidIndex { index, reason } => { - write!(f, "Invalid index {}: {}", WithTypeInfo(index), reason) + write!( + f, + "Invalid index {}: {}", + WithTypeInfo::new_with_opt_info(index, type_info_provider), + reason + ) } InteropError::MissingWorld => { write!(f, "Missing world") } InteropError::External(external_error) => { - write!(f, "External error: {}", WithTypeInfo(external_error)) + write!( + f, + "External error: {}", + WithTypeInfo::new_with_opt_info(external_error, type_info_provider) + ) } InteropError::WithContext(cow, interop_error) => { - write!(f, "{}: {}", cow, WithTypeInfo(interop_error)) + write!( + f, + "{}: {}", + cow, + WithTypeInfo::new_with_opt_info(interop_error, type_info_provider) + ) } } } } + +#[cfg(test)] +mod test { + use bevy_reflect::TypeRegistry; + + use super::*; + #[test] + fn test_script_value_prints_using_type_data() { + // check script values print fine + let mut registry = TypeRegistry::empty(); + registry.register::(); + pretty_assertions::assert_str_eq!( + format!( + "{:?}", + PrintReflectAsDebug::new_with_opt_info(&ScriptValue::Integer(1), Some(®istry)) + ), + "1", + ); + } +} diff --git a/crates/bevy_mod_scripting_bindings/src/function/namespace.rs b/crates/bevy_mod_scripting_bindings/src/function/namespace.rs index b9108d4809..af8d8ea0b4 100644 --- a/crates/bevy_mod_scripting_bindings/src/function/namespace.rs +++ b/crates/bevy_mod_scripting_bindings/src/function/namespace.rs @@ -168,12 +168,16 @@ impl DisplayWithTypeInfo for Namespace { fn display_with_type_info( &self, f: &mut std::fmt::Formatter<'_>, - _type_info_provider: Option<&dyn GetTypeInfo>, + type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { match self { Namespace::Global => f.write_str("Global Namespace"), Namespace::OnType(type_id) => { - write!(f, "Namespace for type {}", WithTypeInfo(type_id)) + write!( + f, + "Namespace for type {}", + WithTypeInfo::new_with_opt_info(type_id, type_info_provider) + ) } } } diff --git a/crates/bevy_mod_scripting_bindings/src/reference.rs b/crates/bevy_mod_scripting_bindings/src/reference.rs index f470277022..b07cf96375 100644 --- a/crates/bevy_mod_scripting_bindings/src/reference.rs +++ b/crates/bevy_mod_scripting_bindings/src/reference.rs @@ -6,11 +6,21 @@ //! we need wrapper types which have owned and ref variants. use super::{WorldGuard, access_map::ReflectAccessId}; use crate::{ - ReflectAllocationId, ReflectAllocator, ThreadWorldContainer, WorldContainer, - error::InteropError, reflection_extensions::PartialReflectExt, with_access_read, - with_access_write, + ReflectAllocationId, ReflectAllocator, error::InteropError, + reflection_extensions::PartialReflectExt, with_access_read, with_access_write, }; -use ::{ +use bevy_ecs::{component::Component, ptr::Ptr, resource::Resource}; +use bevy_mod_scripting_derive::DebugWithTypeInfo; +use bevy_mod_scripting_display::{ + DebugWithTypeInfo, DisplayWithTypeInfo, OrFakeId, PrintReflectAsDebug, WithTypeInfo, +}; +use bevy_reflect::{Access, OffsetAccess, ReflectRef}; +use core::alloc; +use std::{ + any::{Any, TypeId}, + fmt::Debug, +}; +use { bevy_ecs::{ change_detection::MutUntyped, component::ComponentId, entity::Entity, world::unsafe_world_cell::UnsafeWorldCell, @@ -19,13 +29,6 @@ use ::{ ParsedPath, PartialReflect, Reflect, ReflectFromPtr, ReflectPath, prelude::ReflectDefault, }, }; -use bevy_ecs::{component::Component, ptr::Ptr, resource::Resource}; -use bevy_mod_scripting_derive::DebugWithTypeInfo; -use bevy_mod_scripting_display::{ - DebugWithTypeInfo, DisplayWithTypeInfo, OrFakeId, PrintReflectAsDebug, WithTypeInfo, -}; -use bevy_reflect::{Access, OffsetAccess, ReflectRef}; -use std::{any::TypeId, fmt::Debug}; /// A reference to an arbitrary reflected instance. /// @@ -55,15 +58,23 @@ impl DisplayWithTypeInfo for ReflectReference { f: &mut std::fmt::Formatter<'_>, type_info_provider: Option<&dyn bevy_mod_scripting_display::GetTypeInfo>, ) -> std::fmt::Result { - // try to display the most information we can - if let Ok(world) = ThreadWorldContainer.try_get_world() { - if let Ok(r) = self.with_reflect(world, |s| { - PrintReflectAsDebug(s).to_string_with_type_info(f, type_info_provider) - }) { - return r; + // try to display the most information we can, the type info provider happens to be the world guard, we can + // actually display the reference + if let Some(type_info_provider) = type_info_provider { + // Safety: should be safe as the guard is invalidated when world is released per iteration + let any: &dyn Any = unsafe { type_info_provider.as_any_static() }; + + if let Some(guard) = any.downcast_ref::() { + if let Ok(r) = self.with_reflect(guard.clone(), |s| { + PrintReflectAsDebug::new_with_opt_info(s, Some(type_info_provider)) + .to_string_with_type_info(f, Some(type_info_provider)) + }) { + return r; + } } } + f.write_str("(cannot access value, showing reference) ")?; self.base.display_with_type_info(f, type_info_provider)?; if !self.reflect_path.is_empty() { f.write_str(" at path ")?; @@ -151,6 +162,18 @@ impl ReflectReference { } } + /// Creates a raw reference to an existing allocation without checking the type ID. + /// If the type id does not match you will get runtime errors + pub fn new_allocated_raw(type_id: TypeId, id: ReflectAllocationId) -> ReflectReference { + ReflectReference { + base: ReflectBaseType { + type_id, + base_id: ReflectBase::Owned(id), + }, + reflect_path: ParsedPath(Vec::default()), + } + } + /// Create a new reference to a value by allocating it. /// /// Prefer using [`Self::new_allocated`] if you have a value that implements [`Reflect`]. @@ -493,9 +516,10 @@ impl DisplayWithTypeInfo for ReflectBaseType { f: &mut std::fmt::Formatter<'_>, type_info_provider: Option<&dyn bevy_mod_scripting_display::GetTypeInfo>, ) -> std::fmt::Result { - f.write_str("base type ")?; - WithTypeInfo(&self.type_id).display_with_type_info(f, type_info_provider)?; - f.write_str(" of kind ")?; + f.write_str("base type: ")?; + WithTypeInfo::new_with_opt_info(&self.type_id, type_info_provider) + .display_with_type_info(f, type_info_provider)?; + f.write_str(", of kind: ")?; self.base_id.display_with_type_info(f, type_info_provider)?; Ok(()) } @@ -531,7 +555,7 @@ impl ReflectBaseType { /// Create a new reflection base pointing to a value which will be allocated in the allocator pub fn new_allocated_base(value: Box, allocator: &mut ReflectAllocator) -> Self { - let type_id = value.type_id(); + let type_id = (*value).type_id(); let id = allocator.allocate_boxed(value.into_partial_reflect()); Self { type_id, @@ -581,18 +605,44 @@ impl DisplayWithTypeInfo for ReflectBase { ) -> std::fmt::Result { match self { ReflectBase::Component(entity, component_id) => { - f.write_str("component ")?; - WithTypeInfo(component_id).display_with_type_info(f, type_info_provider)?; - f.write_str(" on entity ")?; + f.write_str("component: ")?; + WithTypeInfo::new_with_opt_info(component_id, type_info_provider) + .display_with_type_info(f, type_info_provider)?; + f.write_str(", on entity: ")?; entity.fmt(f) } ReflectBase::Resource(component_id) => { - f.write_str("resource ")?; - WithTypeInfo(component_id).display_with_type_info(f, type_info_provider) + f.write_str("resource: ")?; + WithTypeInfo::new_with_opt_info(component_id, type_info_provider) + .display_with_type_info(f, type_info_provider) } ReflectBase::Owned(id) => { - f.write_str("allocated value with id ")?; - WithTypeInfo(id).display_with_type_info(f, type_info_provider) + if let Some(type_info_provider) = type_info_provider { + // Safety: should generally be safe, as the world guard is invalidated once the world is out of scope for the iteration + let any: &dyn Any = unsafe { type_info_provider.as_any_static() }; + + if let Some(guard) = any.downcast_ref::() { + let allocator = guard.allocator(); + let allocator = allocator.read(); + if let Some(allocation) = allocator.get(id) { + let ptr = allocation.get_ptr(); + if let Ok(v) = guard.with_read_access(id.clone(), |_| { + // Safety:: have access to this id + PrintReflectAsDebug::new_with_opt_info( + unsafe { &*ptr }, + Some(type_info_provider), + ) + .to_string_with_type_info(f, Some(type_info_provider)) + }) { + return v; + } + } + } + } + + f.write_str("allocated value with id: ")?; + WithTypeInfo::new_with_opt_info(id, type_info_provider) + .display_with_type_info(f, type_info_provider) } } } diff --git a/crates/bevy_mod_scripting_bindings/src/script_value.rs b/crates/bevy_mod_scripting_bindings/src/script_value.rs index 45147a22f4..df024228db 100644 --- a/crates/bevy_mod_scripting_bindings/src/script_value.rs +++ b/crates/bevy_mod_scripting_bindings/src/script_value.rs @@ -1,13 +1,14 @@ //! This module contains the `ScriptValue` enum which is used to pass values between scripting languages and Rust. +use crate::error::InteropError; use bevy_mod_scripting_derive::DebugWithTypeInfo; -use bevy_mod_scripting_display::{DisplayWithTypeInfo, GetTypeInfo, WithTypeInfo}; +use bevy_mod_scripting_display::{ + DisplayWithTypeInfo, GetTypeInfo, ReflectDisplayWithTypeInfo, WithTypeInfo, +}; use bevy_platform::collections::HashMap; use bevy_reflect::{Access, OffsetAccess, ParsedPath, Reflect}; use std::borrow::Cow; -use crate::error::InteropError; - use super::{ ReflectReference, function::script_function::{DynamicScriptFunction, DynamicScriptFunctionMut}, @@ -16,7 +17,7 @@ use super::{ /// An abstraction of values that can be passed to and from scripts. /// This allows us to re-use logic between scripting languages. #[derive(Clone, Reflect, Default, DebugWithTypeInfo)] -#[reflect(opaque)] +#[reflect(opaque, DisplayWithTypeInfo)] #[debug_with_type_info(bms_display_path = "bevy_mod_scripting_display")] pub enum ScriptValue { /// Represents the absence of a value. @@ -64,7 +65,8 @@ impl DisplayWithTypeInfo for ScriptValue { f.write_str(", ")?; } first = false; - WithTypeInfo(item).display_with_type_info(f, type_info_provider)?; + WithTypeInfo::new_with_opt_info(item, type_info_provider) + .display_with_type_info(f, type_info_provider)?; } f.write_str("]") } @@ -77,7 +79,8 @@ impl DisplayWithTypeInfo for ScriptValue { } first = false; write!(f, "{key}: ")?; - WithTypeInfo(value).display_with_type_info(f, type_info_provider)?; + WithTypeInfo::new_with_opt_info(value, type_info_provider) + .display_with_type_info(f, type_info_provider)?; } f.write_str("}") } diff --git a/crates/bevy_mod_scripting_bindings/src/world.rs b/crates/bevy_mod_scripting_bindings/src/world.rs index f540c4ce6f..bb61e92046 100644 --- a/crates/bevy_mod_scripting_bindings/src/world.rs +++ b/crates/bevy_mod_scripting_bindings/src/world.rs @@ -55,7 +55,7 @@ use bevy_platform::collections::HashMap; use bevy_reflect::{TypeInfo, VariantInfo}; use bevy_system_reflection::ReflectSchedule; use std::{ - any::TypeId, + any::{Any, TypeId}, borrow::Cow, cell::RefCell, fmt::Debug, @@ -312,6 +312,40 @@ impl<'w> WorldAccessGuard<'w> { .get_resource_id(id)) } + /// A utility for running a closure with scoped read access to the given id + pub fn with_read_access, O, F: FnOnce(&Self) -> O>( + &self, + id: T, + closure: F, + ) -> Result { + let id = id.into(); + if self.claim_read_access(id) { + let out = Ok(closure(self)); + // Safety: just claimed this access + unsafe { self.release_access(id) }; + out + } else { + Err(()) + } + } + + /// A utility for running a closure with scoped write access to the given id + pub fn with_write_access, O, F: FnOnce(&Self) -> O>( + &self, + id: T, + closure: F, + ) -> Result { + let id = id.into(); + if self.claim_write_access(id) { + let out = Ok(closure(self)); + // Safety: just claimed this access + unsafe { self.release_access(id) }; + out + } else { + Err(()) + } + } + /// Get the location of the given access pub fn get_access_location( &self, @@ -1346,6 +1380,19 @@ impl GetTypeInfo for ThreadWorldContainer { registry.get(type_id).map(|r| r.type_info()) } + fn query_type_registration( + &self, + type_id: TypeId, + type_data_id: TypeId, + ) -> Option> { + let world = self.try_get_world().ok()?; + let registry = world.type_registry(); + let registry = registry.read(); + registry + .get(type_id) + .and_then(|r| r.data_by_id(type_data_id).map(|t| t.clone_type_data())) + } + fn get_component_info( &self, component_id: ComponentId, @@ -1354,6 +1401,45 @@ impl GetTypeInfo for ThreadWorldContainer { let cell = world.as_unsafe_world_cell().ok()?; cell.components().get_info(component_id) } + + unsafe fn as_any_static(&self) -> &dyn Any { + self + } +} + +impl GetTypeInfo for WorldGuard<'_> { + fn get_type_info(&self, type_id: TypeId) -> Option<&TypeInfo> { + let registry = self.type_registry(); + let registry = registry.read(); + registry.get(type_id).map(|r| r.type_info()) + } + + fn query_type_registration( + &self, + type_id: TypeId, + type_data_id: TypeId, + ) -> Option> { + let registry = self.type_registry(); + let registry = registry.read(); + registry + .get(type_id) + .and_then(|r| r.data_by_id(type_data_id).map(|t| t.clone_type_data())) + } + + fn get_component_info( + &self, + component_id: ComponentId, + ) -> Option<&bevy_ecs::component::ComponentInfo> { + let cell = self.as_unsafe_world_cell().ok()?; + cell.components().get_info(component_id) + } + + /// # Safety + /// - TODO: should generaly be safe as the guard is invalidated once the world is out of scope + unsafe fn as_any_static(&self) -> &dyn Any { + let static_self: &WorldGuard<'static> = unsafe { std::mem::transmute(self) }; + static_self as &dyn Any + } } #[cfg(test)] diff --git a/crates/bevy_mod_scripting_core/Cargo.toml b/crates/bevy_mod_scripting_core/Cargo.toml index f47c015f04..c3f6202d56 100644 --- a/crates/bevy_mod_scripting_core/Cargo.toml +++ b/crates/bevy_mod_scripting_core/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "bevy_mod_scripting_core" -version = "0.15.1" -authors = ["Maksymilian Mozolewski "] -edition = "2024" -license = "MIT OR Apache-2.0" description = "Core traits and structures required for other parts of bevy_mod_scripting" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "lua"] -categories = ["game-development"] -readme = "readme.md" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +readme.workspace = true [lib] name = "bevy_mod_scripting_core" diff --git a/crates/bevy_mod_scripting_core/readme.md b/crates/bevy_mod_scripting_core/readme.md deleted file mode 100644 index 8ab67ce5a9..0000000000 --- a/crates/bevy_mod_scripting_core/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# bevy_mod_scripting_core - -This crate is a part of the ["bevy_mod_scripting" workspace](https://github.com/makspll/bevy_mod_scripting). \ No newline at end of file diff --git a/crates/bevy_mod_scripting_core/src/commands.rs b/crates/bevy_mod_scripting_core/src/commands.rs index 7f52845d15..7e75f7644b 100644 --- a/crates/bevy_mod_scripting_core/src/commands.rs +++ b/crates/bevy_mod_scripting_core/src/commands.rs @@ -11,13 +11,13 @@ use crate::{ ScriptCallbackResponseEvent, ScriptEvent, }, extractors::CallContext, - handler::{handle_script_errors, send_callback_response}, + handler::{send_callback_response, send_script_errors}, script::{DisplayProxy, ScriptAttachment}, }; use bevy_ecs::{system::Command, world::World}; use bevy_log::{error, info, trace}; use bevy_mod_scripting_asset::ScriptAsset; -use bevy_mod_scripting_bindings::{ScriptValue, WorldGuard}; +use bevy_mod_scripting_bindings::{InteropError, ScriptValue, WorldGuard}; use parking_lot::Mutex; use { bevy_asset::{Assets, Handle}, @@ -147,7 +147,7 @@ impl CreateOrUpdateScript

{ content: &[u8], context: &mut P::C, guard: WorldGuard, - ) -> Result<(), ScriptError> { + ) -> Result<(), InteropError> { debug!("{}: reloading context {}", P::LANGUAGE, attachment); // reload context P::reload(attachment, content, context, guard.clone()) @@ -157,7 +157,7 @@ impl CreateOrUpdateScript

{ attachment: &ScriptAttachment, content: &[u8], guard: WorldGuard, - ) -> Result { + ) -> Result { debug!("{}: loading context {}", P::LANGUAGE, attachment); let context = P::load(attachment, content, guard.clone())?; Ok(context) @@ -177,7 +177,6 @@ impl CreateOrUpdateScript

{ vec![], emit_responses, ) - .with_context(P::LANGUAGE) .with_context("saving reload state") .run_with_context(world, ctxt) .ok() @@ -197,8 +196,6 @@ impl CreateOrUpdateScript

{ vec![], emit_responses, ) - .with_context(P::LANGUAGE) - .with_context("on loaded callback") .run_with_context(world.clone(), script_context.clone()); if is_reload { @@ -208,8 +205,6 @@ impl CreateOrUpdateScript

{ vec![script_state.unwrap_or(ScriptValue::Unit)], emit_responses, ) - .with_context(P::LANGUAGE) - .with_context("on reloaded callback") .run_with_context(world, script_context); } } @@ -230,8 +225,10 @@ impl CreateOrUpdateScript

{ script_context .insert_arc(attachment, ctxt.clone()) .map_err(|_| { - ScriptError::new_boxed(String::from("No context policy applied").into()) - .with_context("creating new script and context") + ScriptError::new_boxed_without_type_info( + String::from("No context policy applied").into(), + ) + .with_context("creating new script and context") })?; Self::after_load( @@ -263,7 +260,7 @@ impl CreateOrUpdateScript

{ let mut script_context = script_context.write(); script_context.insert_resident(attachment.clone()).map_err(|err| { - ScriptError::new_boxed(format!( + ScriptError::new_boxed_without_type_info(format!( "expected context to be present, could not mark attachment as resident in context, {err:?}" ).into()) })?; @@ -366,7 +363,7 @@ impl CreateOrUpdateScript

{ res.map_err(|err| { err.with_script(attachment.script().display()) - .with_context(P::LANGUAGE) + .with_language(P::LANGUAGE) }) } } @@ -403,7 +400,7 @@ impl Command for CreateOrUpdateScript

{ ); if let Err(err) = res { - handle_script_errors(WorldGuard::new_exclusive(world), vec![err].into_iter()); + send_script_errors(WorldGuard::new_exclusive(world), [&err]); } } } @@ -463,6 +460,14 @@ impl RunScriptCallback

{ self.args, guard.clone(), ); + let result = result.map_err(|e| { + let mut err = ScriptError::from(e).with_script(self.attachment.script().display()); + for ctxt in self.context { + err = err.with_context(ctxt) + } + err.with_context(format!("in callback: {}", self.callback)) + .with_language(P::LANGUAGE) + }); if self.trigger_response { trace!( @@ -483,15 +488,7 @@ impl RunScriptCallback

{ } if let Err(ref err) = result { - let mut error_with_context = err - .clone() - .with_script(self.attachment.script().display()) - .with_context(P::LANGUAGE); - for ctxt in self.context { - error_with_context = error_with_context.with_context(ctxt); - } - - handle_script_errors(guard, vec![error_with_context].into_iter()); + send_script_errors(guard, [err]); } result } @@ -507,11 +504,12 @@ impl RunScriptCallback

{ let ctxt = match ctxt { Some(ctxt) => ctxt, None => { - let err = - ScriptError::new_boxed(String::from("No context found for script").into()) - .with_script(self.attachment.script().display()) - .with_context(P::LANGUAGE); - handle_script_errors(guard, vec![err.clone()].into_iter()); + let err = ScriptError::new_boxed_without_type_info( + String::from("No context found for script").into(), + ) + .with_script(self.attachment.script().display()) + .with_language(P::LANGUAGE); + send_script_errors(guard, [&err]); return Err(err); } }; diff --git a/crates/bevy_mod_scripting_core/src/context.rs b/crates/bevy_mod_scripting_core/src/context.rs index 8f25a68f05..57e9d8fcaa 100644 --- a/crates/bevy_mod_scripting_core/src/context.rs +++ b/crates/bevy_mod_scripting_core/src/context.rs @@ -1,11 +1,9 @@ //! Traits and types for managing script contexts. use bevy_ecs::world::WorldId; -use bevy_mod_scripting_bindings::{ThreadWorldContainer, WorldContainer, WorldGuard}; +use bevy_mod_scripting_bindings::{InteropError, ThreadWorldContainer, WorldContainer, WorldGuard}; -use crate::{ - IntoScriptPluginParams, error::ScriptError, extractors::GetPluginFor, script::ScriptAttachment, -}; +use crate::{IntoScriptPluginParams, extractors::GetPluginFor, script::ScriptAttachment}; /// A trait that all script contexts must implement. /// @@ -16,18 +14,18 @@ impl Context for T {} /// Initializer run once after creating a context but before executing it for the first time as well as after re-loading the script pub type ContextInitializer

= - fn(&ScriptAttachment, &mut

::C) -> Result<(), ScriptError>; + fn(&ScriptAttachment, &mut

::C) -> Result<(), InteropError>; /// Initializer run every time before executing or loading/re-loading a script pub type ContextPreHandlingInitializer

= - fn(&ScriptAttachment, &mut

::C) -> Result<(), ScriptError>; + fn(&ScriptAttachment, &mut

::C) -> Result<(), InteropError>; /// A strategy for loading contexts pub type ContextLoadFn

= fn( attachment: &ScriptAttachment, content: &[u8], world_id: WorldId, -) -> Result<

::C, ScriptError>; +) -> Result<

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

= fn( @@ -35,7 +33,7 @@ pub type ContextReloadFn

= fn( content: &[u8], previous_context: &mut

::C, world_id: WorldId, -) -> Result<(), ScriptError>; +) -> Result<(), InteropError>; /// A utility trait for types implementing `IntoScriptPluginParams`. /// @@ -46,7 +44,7 @@ pub trait ScriptingLoader { attachment: &ScriptAttachment, content: &[u8], world: WorldGuard, - ) -> Result; + ) -> Result; /// Reloads a script context using the provided reloader function fn reload( @@ -54,7 +52,7 @@ pub trait ScriptingLoader { content: &[u8], previous_context: &mut P::C, world: WorldGuard, - ) -> Result<(), ScriptError>; + ) -> Result<(), InteropError>; } impl ScriptingLoader

for P { @@ -62,7 +60,7 @@ impl ScriptingLoader

for P { attachment: &ScriptAttachment, content: &[u8], world: WorldGuard, - ) -> Result { + ) -> Result { WorldGuard::with_existing_static_guard(world.clone(), |world| { let world_id = world.id(); ThreadWorldContainer.set_world(world)?; @@ -75,7 +73,7 @@ impl ScriptingLoader

for P { content: &[u8], previous_context: &mut P::C, world: WorldGuard, - ) -> Result<(), ScriptError> { + ) -> Result<(), InteropError> { WorldGuard::with_existing_static_guard(world, |world| { let world_id = world.id(); ThreadWorldContainer.set_world(world)?; diff --git a/crates/bevy_mod_scripting_core/src/error.rs b/crates/bevy_mod_scripting_core/src/error.rs index 68f5274982..391d81b87d 100644 --- a/crates/bevy_mod_scripting_core/src/error.rs +++ b/crates/bevy_mod_scripting_core/src/error.rs @@ -1,21 +1,34 @@ //! Errors that can occur when interacting with the scripting system use std::{ - fmt::{Debug, Display}, + borrow::Cow, + fmt::{Debug, Display, Write}, ops::Deref, str::Utf8Error, sync::Arc, }; +use bevy_mod_scripting_asset::Language; use bevy_mod_scripting_bindings::InteropError; use ::bevy_reflect::Reflect; +use bevy_mod_scripting_display::{DebugWithTypeInfo, DisplayWithTypeInfo, WithTypeInfo}; /// An error with an optional script Context #[derive(Debug, Clone, Reflect)] #[reflect(opaque)] pub struct ScriptError(pub Arc); +impl DisplayWithTypeInfo for ScriptError { + fn display_with_type_info( + &self, + f: &mut std::fmt::Formatter<'_>, + type_info_provider: Option<&dyn bevy_mod_scripting_display::GetTypeInfo>, + ) -> std::fmt::Result { + self.0.display_with_type_info(f, type_info_provider) + } +} + impl std::error::Error for ScriptError {} impl Display for ScriptError { @@ -32,47 +45,177 @@ impl Deref for ScriptError { } } +#[derive(Clone)] +/// The reason for a scripting error +pub enum Reason { + /// An error implementing the standard error interface with no need for type info + WithoutTypeInfo(Arc), + /// An error which can make use of the `GetTypeInfo` retrieval to print itself better. + WithTypeInfo(Arc), +} + +impl DebugWithTypeInfo for Reason { + fn to_string_with_type_info( + &self, + f: &mut std::fmt::Formatter<'_>, + type_info_provider: Option<&dyn bevy_mod_scripting_display::GetTypeInfo>, + ) -> std::fmt::Result { + match self { + Reason::WithoutTypeInfo(err) => write!(f, "{err}"), + Reason::WithTypeInfo(err) => err.display_with_type_info(f, type_info_provider), + } + } +} + +impl Debug for Reason { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&WithTypeInfo::new(self), f) + } +} + +impl DisplayWithTypeInfo for Reason { + fn display_with_type_info( + &self, + f: &mut std::fmt::Formatter<'_>, + type_info_provider: Option<&dyn bevy_mod_scripting_display::GetTypeInfo>, + ) -> std::fmt::Result { + match self { + Reason::WithoutTypeInfo(err) => write!(f, "{err}"), + Reason::WithTypeInfo(err) => err.display_with_type_info(f, type_info_provider), + } + } +} + +#[derive(Clone)] +/// A piece context added to a script error. +pub enum Context { + /// A piece of string context + String(Cow<'static, str>), + /// A piece of type information dependent context + WithTypeInfo( + Option>, + Arc, + ), +} + +impl DebugWithTypeInfo for Context { + fn to_string_with_type_info( + &self, + f: &mut std::fmt::Formatter<'_>, + type_info_provider: Option<&dyn bevy_mod_scripting_display::GetTypeInfo>, + ) -> std::fmt::Result { + self.display_with_type_info(f, type_info_provider) + } +} + +impl std::fmt::Debug for Context { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&WithTypeInfo::new(self), f) + } +} + +impl DisplayWithTypeInfo for Context { + fn display_with_type_info( + &self, + f: &mut std::fmt::Formatter<'_>, + type_info_provider: Option<&dyn bevy_mod_scripting_display::GetTypeInfo>, + ) -> std::fmt::Result { + match self { + Context::String(cow) => f.write_str(cow), + Context::WithTypeInfo(prefix, display_with_type_info) => { + if let Some(prefix) = prefix { + f.write_str(prefix)?; + } + display_with_type_info.display_with_type_info(f, type_info_provider) + } + } + } +} + /// 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, + pub context: Vec, /// The error that occurred - pub reason: Arc>, + pub reason: Reason, + /// The language in whose context the error happened + pub language: Language, } -impl Display for ScriptErrorInner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl DisplayWithTypeInfo for ScriptErrorInner { + fn display_with_type_info( + &self, + f: &mut std::fmt::Formatter<'_>, + type_info_provider: Option<&dyn bevy_mod_scripting_display::GetTypeInfo>, + ) -> std::fmt::Result { + f.write_str("Error ")?; if let Some(script) = &self.script { - write!( - f, - "Script Error in script '{}': {}\nContext: {}", - script, self.reason, self.context - ) - } else { - write!( - f, - "Script Error: {}\nContext: {}", - self.reason, self.context - ) + f.write_str("in script: '")?; + f.write_str(script)?; + f.write_str("' ")?; } + f.write_str(":\n")?; + + self.reason.display_with_type_info(f, type_info_provider)?; + + if !self.context.is_empty() { + f.write_str("\nContext:\n")?; + let mut first = true; + for c in &self.context { + if !first { + f.write_char('\n')?; + } + first = false; + c.display_with_type_info(f, type_info_provider)?; + } + } + + f.write_str("\nLanguage: ")?; + f.write_str(&Cow::<'static, str>::from(&self.language)) + } +} + +impl Display for ScriptErrorInner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&WithTypeInfo::new(self), f) } } impl ScriptError { /// Creates a new script error with an external error - pub fn new(reason: impl std::error::Error + Send + Sync + 'static) -> Self { - Self::new_boxed(Box::new(reason)) + pub fn new_without_type_info(reason: impl std::error::Error + Send + Sync + 'static) -> Self { + Self::new_boxed_without_type_info(Box::new(reason)) } /// Creates a new script error with an external error - pub fn new_boxed(reason: Box) -> Self { + pub fn new_boxed_without_type_info( + reason: Box, + ) -> Self { + Self(Arc::new(ScriptErrorInner { + script: None, + reason: Reason::WithoutTypeInfo(Arc::from(reason)), + context: Default::default(), + language: Default::default(), + })) + } + + /// Creates a new script error with a reason that has type info + pub fn new_with_type_info(reason: impl DisplayWithTypeInfo + Send + Sync + 'static) -> Self { + Self::new_boxed_with_type_info(Box::new(reason)) + } + + /// Creates a new script error with a reason that has type info + pub fn new_boxed_with_type_info( + reason: Box, + ) -> Self { Self(Arc::new(ScriptErrorInner { script: None, - reason: Arc::new(reason), + reason: Reason::WithTypeInfo(Arc::from(reason)), context: Default::default(), + language: Default::default(), })) } @@ -82,15 +225,76 @@ impl ScriptError { script: Some(script.to_string()), context: self.0.context.clone(), reason: self.0.reason.clone(), + language: self.0.language.clone(), })) } - /// Adds context to the error - pub fn with_context(self, context: S) -> Self { + /// Sets the language context on the error + pub fn with_language(self, language: Language) -> Self { Self(Arc::new(ScriptErrorInner { script: self.0.script.clone(), - context: format!("{}\n{}", self.0.context, context.to_string()), + context: self.0.context.clone(), reason: self.0.reason.clone(), + language, + })) + } + + /// Adds string context to the error + pub fn with_context(self, context: impl Into>) -> Self { + let script = self.0.script.clone(); + let reason = self.0.reason.clone(); + let mut new_ctxt = self.0.context.clone(); + new_ctxt.push(Context::String(context.into())); + + Self(Arc::new(ScriptErrorInner { + language: self.language.clone(), + script, + context: new_ctxt, + reason, + })) + } + + /// Adds context which requires type information to be printed + pub fn with_type_info_context( + self, + prefix: Option>>, + context: impl DisplayWithTypeInfo + Send + Sync + 'static, + ) -> Self { + let script = self.0.script.clone(); + let reason = self.0.reason.clone(); + let mut new_ctxt = self.0.context.clone(); + new_ctxt.push(Context::WithTypeInfo( + prefix.map(Into::into), + Arc::new(context), + )); + + Self(Arc::new(ScriptErrorInner { + language: self.language.clone(), + script, + context: new_ctxt, + reason, + })) + } + + /// Adds context which requires type information to be printed in boxed form. + pub fn with_type_info_context_boxed( + self, + prefix: Option>>, + context: Box, + ) -> Self { + let script = self.0.script.clone(); + let reason = self.0.reason.clone(); + let mut new_ctxt = self.0.context.clone(); + new_ctxt.push(Context::WithTypeInfo( + prefix.map(Into::into), + Arc::from(context), + )); + + Self(Arc::new(ScriptErrorInner { + language: self.language.clone(), + script, + context: new_ctxt, + reason, })) } } @@ -121,7 +325,7 @@ impl std::error::Error for MissingResourceError {} impl From for ScriptError { fn from(val: InteropError) -> Self { let (ctxt, err) = val.unwrap_context(); - let mut err = ScriptError::new(err); + let mut err = ScriptError::new_with_type_info(err); for ctxt in ctxt { err = err.with_context(ctxt); } @@ -131,7 +335,7 @@ impl From for ScriptError { impl From for ScriptError { fn from(val: Utf8Error) -> Self { - ScriptError::new(val) + ScriptError::new_without_type_info(val) } } diff --git a/crates/bevy_mod_scripting_core/src/extractors.rs b/crates/bevy_mod_scripting_core/src/extractors.rs index 7bc1cdfa0a..785ef8fda2 100644 --- a/crates/bevy_mod_scripting_core/src/extractors.rs +++ b/crates/bevy_mod_scripting_core/src/extractors.rs @@ -5,7 +5,6 @@ use crate::{ IntoScriptPluginParams, context::Context, - error::ScriptError, event::{CallbackLabel, IntoCallbackLabel}, handler::ScriptingHandler, script::ScriptAttachment, @@ -19,7 +18,8 @@ use bevy_ecs::{ world::{DeferredWorld, World, unsafe_world_cell::UnsafeWorldCell}, }; use bevy_mod_scripting_bindings::{ - WorldAccessGuard, WorldGuard, access_map::ReflectAccessId, script_value::ScriptValue, + InteropError, WorldAccessGuard, WorldGuard, access_map::ReflectAccessId, + script_value::ScriptValue, }; use fixedbitset::FixedBitSet; @@ -41,7 +41,7 @@ pub trait CallContext { label: &CallbackLabel, payload: Vec, guard: WorldGuard<'_>, - ) -> Result; + ) -> Result; /// Invoke a callback on this context fn call_context( @@ -49,7 +49,7 @@ pub trait CallContext { context_key: &ScriptAttachment, payload: Vec, guard: WorldGuard<'_>, - ) -> Result { + ) -> Result { self.call_context_dynamic(context_key, &C::into_callback_label(), payload, guard) } } @@ -61,7 +61,7 @@ impl CallContext for C { label: &CallbackLabel, payload: Vec, guard: WorldGuard<'_>, - ) -> Result { + ) -> Result { C::P::handle(payload, context_key, label, self, guard) } } diff --git a/crates/bevy_mod_scripting_core/src/handler.rs b/crates/bevy_mod_scripting_core/src/handler.rs index 5f9f8db912..84f1fba314 100644 --- a/crates/bevy_mod_scripting_core/src/handler.rs +++ b/crates/bevy_mod_scripting_core/src/handler.rs @@ -1,12 +1,14 @@ //! Contains the logic for handling script callback events use bevy_ecs::world::WorldId; use bevy_mod_scripting_bindings::{ - ScriptValue, ThreadWorldContainer, WorldAccessGuard, WorldContainer, WorldGuard, + InteropError, ScriptValue, ThreadWorldContainer, WorldAccessGuard, WorldContainer, WorldGuard, }; +use bevy_mod_scripting_display::WithTypeInfo; use crate::extractors::CallContext; +use crate::script::DisplayProxy; use crate::{ - IntoScriptPluginParams, Language, + IntoScriptPluginParams, error::ScriptError, event::{ CallbackLabel, IntoCallbackLabel, ScriptCallbackEvent, ScriptCallbackResponseEvent, @@ -32,7 +34,7 @@ pub type HandlerFn

= fn( callback: &CallbackLabel, context: &mut

::C, world_id: WorldId, -) -> Result; +) -> Result; /// A utility trait, implemented for all types implementing `IntoScriptPluginParams`. /// @@ -46,7 +48,7 @@ pub trait ScriptingHandler { callback: &CallbackLabel, script_ctxt: &mut P::C, world: WorldGuard, - ) -> Result; + ) -> Result; } impl ScriptingHandler

for P { @@ -57,7 +59,7 @@ impl ScriptingHandler

for P { callback: &CallbackLabel, script_ctxt: &mut P::C, world: WorldGuard, - ) -> Result { + ) -> Result { WorldGuard::with_existing_static_guard(world.clone(), |world| { let world_id = world.id(); ThreadWorldContainer.set_world(world)?; @@ -113,7 +115,10 @@ pub(crate) fn event_handler_inner( let events = match events { Ok(events) => events, Err(err) => { - error!("Failed to read script callback events: {err}",); + error!( + "Failed to read script callback events: {}", + WithTypeInfo::new_with_info(&err, &guard) + ); return; } }; @@ -131,6 +136,13 @@ pub(crate) fn event_handler_inner( event.args.clone(), guard.clone(), ); + let call_result = call_result.map_err(|e| { + ScriptError::from(e) + .with_script(attachment.script().display()) + .with_context(format!("callback: {}", event.label)) + .with_type_info_context(Some("args: "), event.args.clone()) + .with_language(P::LANGUAGE) + }); drop(ctxt); if event.trigger_response { @@ -144,21 +156,17 @@ pub(crate) fn event_handler_inner( ), ); } - collect_errors(call_result, P::LANGUAGE, &mut errors); + collect_errors(call_result, &mut errors); } } - handle_script_errors(guard, errors.into_iter()); + send_script_errors(guard, errors.iter()); } -fn collect_errors( - call_result: Result, - language: Language, - errors: &mut Vec, -) { +fn collect_errors(call_result: Result, errors: &mut Vec) { match call_result { Ok(_) => {} Err(e) => { - errors.push(e.with_context(format!("Event handling for language {language}"))); + errors.push(e); } } } @@ -170,23 +178,59 @@ pub fn send_callback_response(world: WorldGuard, response: ScriptCallbackRespons }); if let Err(err) = err { - error!("Failed to send script callback response: {err}",); + error!( + "Failed to send script callback response: {}", + WithTypeInfo::new_with_info(&err, &world) + ); } } -/// Handles errors caused by script execution and sends them to the error event channel -pub fn handle_script_errors + Clone>(world: WorldGuard, errors: I) { +/// sends the given errors to the error event channel +pub fn send_script_errors<'e>( + world: WorldGuard, + errors: impl IntoIterator, +) { + let iter = errors.into_iter(); let err = world.with_resource_mut(|mut error_events: Mut>| { - for error in errors.clone() { - error_events.send(ScriptErrorEvent { error }); + for error in iter { + error_events.send(ScriptErrorEvent { + error: error.clone(), + }); } }); if let Err(err) = err { - error!("Failed to send script error events: {err}",); + error!( + "Failed to send script error events: {}", + WithTypeInfo::new_with_info(&err, &world) + ); } +} - for error in errors { - error!("{error}"); +/// A system which receives all script errors and logs them to console +pub fn script_error_logger( + world: &mut World, + mut errors_cursor: Local>, +) { + let guard = WorldGuard::new_exclusive(world); + let errors = guard.with_resource(|events: &Events| { + errors_cursor + .read(events) + .map(|e| e.error.clone()) + .collect::>() + }); + + match errors { + Ok(errors) => { + for error in errors { + error!("{}", &WithTypeInfo::new_with_info(&error, &guard)) + } + } + Err(err) => { + error!( + "Script errors occured but could not be accessed:\n{}", + WithTypeInfo::new_with_info(&err, &guard) + ); + } } } diff --git a/crates/bevy_mod_scripting_core/src/lib.rs b/crates/bevy_mod_scripting_core/src/lib.rs index e0b6de4798..75a0312a51 100644 --- a/crates/bevy_mod_scripting_core/src/lib.rs +++ b/crates/bevy_mod_scripting_core/src/lib.rs @@ -6,6 +6,7 @@ use crate::{ config::{GetPluginThreadConfig, ScriptingPluginConfiguration}, context::{ContextLoadFn, ContextReloadFn}, event::ScriptErrorEvent, + handler::script_error_logger, }; use asset::{configure_asset_systems, configure_asset_systems_for_plugin}; use bevy_app::{App, Plugin, PostUpdate}; @@ -304,7 +305,14 @@ fn pre_register_components(app: &mut App) { /// A plugin defining shared settings between various scripting plugins /// It is necessary to register this plugin for any of them to work #[derive(Default)] -pub struct BMSScriptingInfrastructurePlugin; +pub struct BMSScriptingInfrastructurePlugin { + /// If set to true will log all ScriptErrorEvents using bevy_log::error. + /// + /// you can opt out of this behavior if you want to log the errors in a different way. + /// + /// see the [`crate::handler::script_error_logger`] system. + dont_log_script_event_errors: bool, +} impl Plugin for BMSScriptingInfrastructurePlugin { fn build(&self, app: &mut App) { @@ -326,6 +334,10 @@ impl Plugin for BMSScriptingInfrastructurePlugin { ((garbage_collector).in_set(ScriptingSystemSet::GarbageCollection),), ); + if !self.dont_log_script_event_errors { + app.add_systems(PostUpdate, script_error_logger); + } + app.add_plugins(configure_asset_systems); let _ = bevy_mod_scripting_display::GLOBAL_TYPE_INFO_PROVIDER @@ -401,7 +413,7 @@ mod test { assert!(app.world_mut().component_id::().is_none()); - BMSScriptingInfrastructurePlugin.finish(&mut app); + BMSScriptingInfrastructurePlugin::default().finish(&mut app); assert!(app.world_mut().component_id::().is_some()); } diff --git a/crates/bevy_mod_scripting_core/src/script_system.rs b/crates/bevy_mod_scripting_core/src/script_system.rs index 320d5728cf..313ba43847 100644 --- a/crates/bevy_mod_scripting_core/src/script_system.rs +++ b/crates/bevy_mod_scripting_core/src/script_system.rs @@ -15,7 +15,7 @@ use ::{ query::{Access, FilteredAccess, FilteredAccessSet, QueryState}, reflect::AppTypeRegistry, schedule::SystemSet, - system::{IntoSystem, System, SystemParamValidationError}, + system::{System, SystemParamValidationError}, world::{World, unsafe_world_cell::UnsafeWorldCell}, }, bevy_reflect::Reflect, @@ -155,7 +155,7 @@ impl ScriptSystemBuilder { // this is quite important, by default systems are placed in a set defined by their TYPE, i.e. in this case // all script systems would be the same - let system: DynamicScriptSystem

= IntoSystem::into_system(self); + let system: DynamicScriptSystem

= bevy_ecs::system::IntoSystem::into_system(self); let mut system_config = system.into_configs(); // let mut system_config = > + 'static)>, (Infallible, IsDynamicScriptSystem

)>>::into_configs(self); // apply ordering for (other, is_before) in before_systems @@ -255,7 +255,7 @@ pub struct DynamicScriptSystem { pub struct IsDynamicScriptSystem

(PhantomData P>); #[profiling::all_functions] -impl IntoSystem<(), (), IsDynamicScriptSystem

> +impl bevy_ecs::system::IntoSystem<(), (), IsDynamicScriptSystem

> for ScriptSystemBuilder { type System = DynamicScriptSystem

; @@ -694,7 +694,7 @@ mod test { AssetPlugin::default(), DiagnosticsPlugin, TestPlugin::default(), - BMSScriptingInfrastructurePlugin, + BMSScriptingInfrastructurePlugin::default(), )); app.init_schedule(TestSchedule); let mut main_schedule_order = app.world_mut().resource_mut::(); diff --git a/crates/bevy_mod_scripting_derive/Cargo.toml b/crates/bevy_mod_scripting_derive/Cargo.toml index 8cc2f6cd3c..1cdded28af 100644 --- a/crates/bevy_mod_scripting_derive/Cargo.toml +++ b/crates/bevy_mod_scripting_derive/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "bevy_mod_scripting_derive" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Necessary functionality for Lua support with bevy_mod_scripting" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] -readme = "readme.md" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +readme.workspace = true [dependencies] syn = { workspace = true, features = ["full"] } diff --git a/crates/bevy_mod_scripting_derive/readme.md b/crates/bevy_mod_scripting_derive/readme.md deleted file mode 100644 index e058ed4f00..0000000000 --- a/crates/bevy_mod_scripting_derive/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# bevy_mod_scripting_lua_derive - -This crate is a part of the ["bevy_mod_scripting" workspace](https://github.com/makspll/bevy_mod_scripting). \ No newline at end of file diff --git a/crates/bevy_mod_scripting_derive/src/derive/debug_with_type_info.rs b/crates/bevy_mod_scripting_derive/src/derive/debug_with_type_info.rs index bb013424d2..153d86153b 100644 --- a/crates/bevy_mod_scripting_derive/src/derive/debug_with_type_info.rs +++ b/crates/bevy_mod_scripting_derive/src/derive/debug_with_type_info.rs @@ -144,7 +144,7 @@ pub fn debug_with_type_info(input: proc_macro::TokenStream) -> proc_macro::Token } impl #impl_generics std::fmt::Debug for #name #ty_generics #where_clause { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Debug::fmt(&#bms_display_path::WithTypeInfo(self), f) + std::fmt::Debug::fmt(&#bms_display_path::WithTypeInfo::new(self), f) } } } @@ -224,7 +224,7 @@ fn build_from_fields( ); quote::quote! { - #bms_display_path::DebugWithTypeInfoBuilder::#builder_method(f, #variant_name) + #bms_display_path::DebugWithTypeInfoBuilder::#builder_method(f, #variant_name, type_info_provider) #(#members)* .finish() } diff --git a/crates/bevy_mod_scripting_display/Cargo.toml b/crates/bevy_mod_scripting_display/Cargo.toml index cb3ae721dd..14c2cc29c6 100644 --- a/crates/bevy_mod_scripting_display/Cargo.toml +++ b/crates/bevy_mod_scripting_display/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "bevy_mod_scripting_display" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" -description = "Necessary functionality for Lua support with bevy_mod_scripting" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] -readme = "readme.md" +description = "Traits focused on printing types with type information contained in the bevy type registry" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +readme.workspace = true [dependencies] bevy_reflect = { workspace = true } diff --git a/crates/bevy_mod_scripting_display/src/impls/bevy_ecs.rs b/crates/bevy_mod_scripting_display/src/impls/bevy_ecs.rs index 14533848fc..511d52641f 100644 --- a/crates/bevy_mod_scripting_display/src/impls/bevy_ecs.rs +++ b/crates/bevy_mod_scripting_display/src/impls/bevy_ecs.rs @@ -11,15 +11,18 @@ impl DebugWithTypeInfo for ComponentId { f: &mut std::fmt::Formatter<'_>, type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { - let name = type_info_provider - .and_then(|type_info_provider| { - type_info_provider - .get_component_info(*self) - .map(|info| info.name().to_string()) - }) - .unwrap_or_else(|| format!("Unregistered ComponentId - {self:?}")); - - f.debug_tuple("ComponentId").field(&name).finish() + let mut builder = f.debug_tuple_with_type_info("ComponentId", type_info_provider); + match type_info_provider { + Some(type_info_provider) => match type_info_provider + .get_component_info(*self) + .map(|info| info.name().to_string()) + { + Some(type_info) => builder.field(&type_info), + None => builder.field(&format!("Unregistered ComponentId - {self:?}")), + }, + None => builder.field(&format!("Unregistered ComponentId - {self:?}")), + }; + builder.finish() } } @@ -29,14 +32,21 @@ impl DisplayWithTypeInfo for ComponentId { f: &mut std::fmt::Formatter<'_>, type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { - let name = type_info_provider - .and_then(|type_info_provider| { - type_info_provider - .get_component_info(*self) - .map(|info| info.name().to_string()) - }) - .unwrap_or_else(|| format!("Unregistered ComponentId - {self:?}")); - - f.write_str(&name) + match type_info_provider { + Some(type_info_provider) => match type_info_provider + .get_component_info(*self) + .map(|info| info.name().to_string()) + { + Some(type_info) => f.write_str(&type_info), + None => { + f.write_str("Unregistered ComponentId - ")?; + std::fmt::Debug::fmt(self, f) + } + }, + None => { + f.write_str("component: ")?; + std::fmt::Debug::fmt(self, f) + } + } } } diff --git a/crates/bevy_mod_scripting_display/src/impls/bevy_platform.rs b/crates/bevy_mod_scripting_display/src/impls/bevy_platform.rs index e0019d5445..39c555384f 100644 --- a/crates/bevy_mod_scripting_display/src/impls/bevy_platform.rs +++ b/crates/bevy_mod_scripting_display/src/impls/bevy_platform.rs @@ -6,9 +6,9 @@ impl DebugWithTypeInfo fn to_string_with_type_info( &self, f: &mut std::fmt::Formatter<'_>, - _type_info_provider: Option<&dyn crate::GetTypeInfo>, + type_info_provider: Option<&dyn crate::GetTypeInfo>, ) -> std::fmt::Result { - f.debug_map_with_type_info() + f.debug_map_with_type_info(type_info_provider) .entries( self.iter() .map(|(k, v)| (k as &dyn DebugWithTypeInfo, v as &dyn DebugWithTypeInfo)), @@ -21,9 +21,9 @@ impl DebugWithTypeInfo for bevy_platform::collections:: fn to_string_with_type_info( &self, f: &mut std::fmt::Formatter<'_>, - _type_info_provider: Option<&dyn crate::GetTypeInfo>, + type_info_provider: Option<&dyn crate::GetTypeInfo>, ) -> std::fmt::Result { - f.debug_set_with_type_info() + f.debug_set_with_type_info(type_info_provider) .entries(self.iter().map(|v| v as &dyn DebugWithTypeInfo)) .finish() } @@ -33,9 +33,9 @@ impl DebugWithTypeInfo for bevy_platform::collections::Has fn to_string_with_type_info( &self, f: &mut std::fmt::Formatter<'_>, - _type_info_provider: Option<&dyn crate::GetTypeInfo>, + type_info_provider: Option<&dyn crate::GetTypeInfo>, ) -> std::fmt::Result { - f.debug_set_with_type_info() + f.debug_set_with_type_info(type_info_provider) .entries(self.iter().map(|v| v as &dyn DebugWithTypeInfo)) .finish() } diff --git a/crates/bevy_mod_scripting_display/src/impls/parking_lock.rs b/crates/bevy_mod_scripting_display/src/impls/parking_lock.rs index 0740a1b124..1189ad31ad 100644 --- a/crates/bevy_mod_scripting_display/src/impls/parking_lock.rs +++ b/crates/bevy_mod_scripting_display/src/impls/parking_lock.rs @@ -6,14 +6,14 @@ impl DebugWithTypeInfo for RwLock { fn to_string_with_type_info( &self, f: &mut std::fmt::Formatter<'_>, - _type_info_provider: Option<&dyn crate::GetTypeInfo>, + type_info_provider: Option<&dyn crate::GetTypeInfo>, ) -> std::fmt::Result { if let Some(read) = self.try_read() { - f.debug_tuple_with_type_info("RwLock") + f.debug_tuple_with_type_info("RwLock", type_info_provider) .field(&*read as &dyn DebugWithTypeInfo) .finish() } else { - f.debug_tuple_with_type_info("RwLock") + f.debug_tuple_with_type_info("RwLock", type_info_provider) .field(&"") .finish() } @@ -24,14 +24,14 @@ impl DebugWithTypeInfo for parking_lot::Mutex { fn to_string_with_type_info( &self, f: &mut std::fmt::Formatter<'_>, - _type_info_provider: Option<&dyn crate::GetTypeInfo>, + type_info_provider: Option<&dyn crate::GetTypeInfo>, ) -> std::fmt::Result { if let Some(guard) = self.try_lock() { - f.debug_tuple_with_type_info("Mutex") + f.debug_tuple_with_type_info("Mutex", type_info_provider) .field(&*guard as &dyn DebugWithTypeInfo) .finish() } else { - f.debug_tuple_with_type_info("Mutex") + f.debug_tuple_with_type_info("Mutex", type_info_provider) .field(&"") .finish() } diff --git a/crates/bevy_mod_scripting_display/src/impls/std.rs b/crates/bevy_mod_scripting_display/src/impls/std.rs index f254d6b1eb..e606610128 100644 --- a/crates/bevy_mod_scripting_display/src/impls/std.rs +++ b/crates/bevy_mod_scripting_display/src/impls/std.rs @@ -59,14 +59,16 @@ impl DebugWithTypeInfo for Option { fn to_string_with_type_info( &self, f: &mut std::fmt::Formatter<'_>, - _type_info_provider: Option<&dyn GetTypeInfo>, + type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { match self { Some(value) => f - .debug_tuple_with_type_info("Some") + .debug_tuple_with_type_info("Some", type_info_provider) .field(value as &dyn DebugWithTypeInfo) .finish(), - None => f.debug_tuple_with_type_info("None").finish(), + None => f + .debug_tuple_with_type_info("None", type_info_provider) + .finish(), } } } @@ -75,15 +77,15 @@ impl DebugWithTypeInfo for Result, - _type_info_provider: Option<&dyn GetTypeInfo>, + type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { match self { Ok(v) => f - .debug_tuple_with_type_info("Ok") + .debug_tuple_with_type_info("Ok", type_info_provider) .field(v as &dyn DebugWithTypeInfo) .finish(), Err(v) => f - .debug_tuple_with_type_info("Err") + .debug_tuple_with_type_info("Err", type_info_provider) .field(v as &dyn DebugWithTypeInfo) .finish(), } @@ -94,9 +96,9 @@ impl DebugWithTypeInfo for Vec { fn to_string_with_type_info( &self, f: &mut std::fmt::Formatter<'_>, - _type_info_provider: Option<&dyn GetTypeInfo>, + type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { - f.debug_list_with_type_info() + f.debug_list_with_type_info(type_info_provider) .entries(self.iter().map(|v| v as &dyn DebugWithTypeInfo)) .finish() } @@ -108,9 +110,9 @@ impl DebugWithTypeInfo fn to_string_with_type_info( &self, f: &mut std::fmt::Formatter<'_>, - _type_info_provider: Option<&dyn GetTypeInfo>, + type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { - f.debug_map_with_type_info() + f.debug_map_with_type_info(type_info_provider) .entries( self.iter() .map(|(k, v)| (k as &dyn DebugWithTypeInfo, v as &dyn DebugWithTypeInfo)), @@ -123,9 +125,9 @@ impl DebugWithTypeInfo for std::collections::HashSet, - _type_info_provider: Option<&dyn GetTypeInfo>, + type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { - f.debug_set_with_type_info() + f.debug_set_with_type_info(type_info_provider) .entries(self.iter().map(|v| v as &dyn DebugWithTypeInfo)) .finish() } @@ -135,9 +137,9 @@ impl DebugWithTypeInfo for std::collections::BTreeSet { fn to_string_with_type_info( &self, f: &mut std::fmt::Formatter<'_>, - _type_info_provider: Option<&dyn GetTypeInfo>, + type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { - f.debug_set_with_type_info() + f.debug_set_with_type_info(type_info_provider) .entries(self.iter().map(|v| v as &dyn DebugWithTypeInfo)) .finish() } @@ -149,9 +151,9 @@ impl DebugWithTypeInfo fn to_string_with_type_info( &self, f: &mut std::fmt::Formatter<'_>, - _type_info_provider: Option<&dyn GetTypeInfo>, + type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { - f.debug_map_with_type_info() + f.debug_map_with_type_info(type_info_provider) .entries( self.iter() .map(|(k, v)| (k as &dyn DebugWithTypeInfo, v as &dyn DebugWithTypeInfo)), @@ -235,6 +237,16 @@ impl DisplayWithTypeInfo for Arc { } } +impl DisplayWithTypeInfo for Arc { + fn display_with_type_info( + &self, + f: &mut std::fmt::Formatter<'_>, + type_info_provider: Option<&dyn GetTypeInfo>, + ) -> std::fmt::Result { + (**self).display_with_type_info(f, type_info_provider) + } +} + impl DisplayWithTypeInfo for Box { fn display_with_type_info( &self, @@ -245,6 +257,16 @@ impl DisplayWithTypeInfo for Box { } } +impl DisplayWithTypeInfo for Box { + fn display_with_type_info( + &self, + f: &mut std::fmt::Formatter<'_>, + type_info_provider: Option<&dyn GetTypeInfo>, + ) -> std::fmt::Result { + (**self).display_with_type_info(f, type_info_provider) + } +} + impl DisplayWithTypeInfo for Location<'_> { fn display_with_type_info( &self, @@ -255,3 +277,22 @@ impl DisplayWithTypeInfo for Location<'_> { write!(f, "{}:{}:{}", self.file(), self.line(), self.column()) } } + +impl DisplayWithTypeInfo for Vec { + fn display_with_type_info( + &self, + f: &mut std::fmt::Formatter<'_>, + type_info_provider: Option<&dyn GetTypeInfo>, + ) -> std::fmt::Result { + f.write_str("[")?; + let mut first = true; + for var in self { + if !first { + f.write_str(", ")?; + } + first = false; + var.display_with_type_info(f, type_info_provider)?; + } + f.write_str("]") + } +} diff --git a/crates/bevy_mod_scripting_display/src/lib.rs b/crates/bevy_mod_scripting_display/src/lib.rs index 7095d5e255..95e72b92ec 100644 --- a/crates/bevy_mod_scripting_display/src/lib.rs +++ b/crates/bevy_mod_scripting_display/src/lib.rs @@ -1,6 +1,9 @@ //! Abstractions for displaying reflect values, potentially with access to the type registry -use std::{any::TypeId, ops::Deref}; +use std::{ + any::{Any, TypeId}, + ops::Deref, +}; mod impls; mod printer; pub use printer::*; @@ -10,15 +13,43 @@ use bevy_ecs::{ reflect::AppTypeRegistry, world::World, }; -use bevy_reflect::{TypeInfo, TypeRegistry}; +use bevy_reflect::{TypeData, TypeInfo, TypeRegistry, reflect_trait}; /// An abstraction for getting type information, potentially using the type registry. pub trait GetTypeInfo { /// Get a string representation of the type, potentially using the type registry. fn get_type_info(&self, type_id: TypeId) -> Option<&TypeInfo>; + /// Queries against arbitrary type data + fn query_type_registration( + &self, + type_id: TypeId, + type_data_id: TypeId, + ) -> Option>; + /// Get component info for a given component id, if available fn get_component_info(&self, component_id: ComponentId) -> Option<&ComponentInfo>; + + /// A potentially unsafe function depending on the implementation which allows you to downcast to a concrete type without + /// requiring 'static on the type. + /// + /// # Safety + /// - Ensure the safety invariants for the concrete type you are expecting are respected + unsafe fn as_any_static(&self) -> &dyn Any; +} + +/// Extension trait for GetTypeInfo which provides non-type safe extensions +pub trait GetTypeInfoExtensions<'s> { + /// Typed equivalent to [`GetTypeInfo::query_type_registration`] + fn get_type_data(&'s self, type_id: TypeId) -> Option; +} + +impl<'s> GetTypeInfoExtensions<'s> for &'s dyn GetTypeInfo { + fn get_type_data(&'s self, type_id: TypeId) -> Option { + self.query_type_registration(type_id, std::any::TypeId::of::()) + .and_then(|t| t.downcast().ok()) + .map(|b| *b) + } } impl GetTypeInfo for TypeRegistry { @@ -27,9 +58,22 @@ impl GetTypeInfo for TypeRegistry { .map(|registration| registration.type_info()) } + fn query_type_registration( + &self, + type_id: TypeId, + type_data_id: TypeId, + ) -> Option> { + self.get(type_id) + .and_then(|r| r.data_by_id(type_data_id).map(|t| t.clone_type_data())) + } + fn get_component_info(&self, _component_id: ComponentId) -> Option<&ComponentInfo> { None } + + unsafe fn as_any_static(&self) -> &dyn Any { + self + } } impl GetTypeInfo for World { @@ -38,12 +82,29 @@ impl GetTypeInfo for World { .and_then(|r| r.read().get_type_info(type_id)) } + fn query_type_registration( + &self, + type_id: TypeId, + type_data_id: TypeId, + ) -> Option> { + self.get_resource::().and_then(|r| { + r.read() + .get(type_id) + .and_then(|r| r.data_by_id(type_data_id).map(|t| t.clone_type_data())) + }) + } + fn get_component_info(&self, component_id: ComponentId) -> Option<&ComponentInfo> { self.components().get_info(component_id) } + + unsafe fn as_any_static(&self) -> &dyn Any { + self + } } /// An trait for displaying values with access to type information +#[reflect_trait] pub trait DisplayWithTypeInfo { /// Format the value using the provided type info provider if available fn display_with_type_info( @@ -65,9 +126,11 @@ impl DisplayWithTypeInfo for WithTypeInfo<'_, T> { impl std::fmt::Display for WithTypeInfo<'_, T> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let provider = GLOBAL_TYPE_INFO_PROVIDER - .get() - .and_then(|get_provider| get_provider()); + let provider = self.1.or_else(|| { + GLOBAL_TYPE_INFO_PROVIDER + .get() + .and_then(|get_provider| get_provider()) + }); self.0.display_with_type_info(f, provider) } } @@ -90,21 +153,36 @@ pub static GLOBAL_TYPE_INFO_PROVIDER: std::sync::OnceLock< fn() -> Option<&'static dyn GetTypeInfo>, > = std::sync::OnceLock::new(); -/// newtype adapter for opting into DisplayWithTypeInfo for any T: DisplayWithTypeInfo +/// newtype adapter for opting into [`DisplayWithTypeInfo`] for any T: [`DisplayWithTypeInfo`] /// Use as follows /// ```rust,no_run /// use bevy_mod_scripting_display::WithTypeInfo; /// /// let my_value = std::any::TypeId::of::(); -/// format!("{:?}", WithTypeInfo(&my_value)); // non-pretty print -/// format!("{:#?}", WithTypeInfo(&my_value)); // pretty print +/// format!("{:?}", WithTypeInfo::new(&my_value)); // non-pretty print +/// format!("{:#?}", WithTypeInfo::new(&my_value)); // pretty print /// ``` -pub struct WithTypeInfo<'a, T: ?Sized>(pub &'a T); +pub struct WithTypeInfo<'a, T: ?Sized>(&'a T, Option<&'a dyn GetTypeInfo>); -impl<'a, T: DebugWithTypeInfo + ?Sized> WithTypeInfo<'a, T> { - /// Create a new WithTypeInfo wrapper +impl<'a, T: ?Sized> WithTypeInfo<'a, T> { + /// Create a new WithTypeInfo wrapper. + /// Will retrieve type information using the [`GLOBAL_TYPE_INFO_PROVIDER`]. + /// + /// If you're using this type in the context of [`DebugWithTypeInfo`] or [`DisplayWithTypeInfo`] traits, + /// use [`Self::new_with_opt_info`] instead, to pass down the context correctly. pub fn new(value: &'a T) -> Self { - Self(value) + Self(value, None) + } + + /// Create a new WithTypeInfo wrapper with a specific type info provider + pub fn new_with_info(value: &'a T, provider: &'a dyn GetTypeInfo) -> Self { + Self(value, Some(provider)) + } + + /// Create a new WithTypeInfo wrapper passing down an optional type info provider. + /// Useful for nested implementations which want to avoid multiple retrievals + pub fn new_with_opt_info(value: &'a T, provider: Option<&'a dyn GetTypeInfo>) -> Self { + Self(value, provider) } } @@ -118,9 +196,11 @@ impl Deref for WithTypeInfo<'_, T> { impl<'a, T: DebugWithTypeInfo + ?Sized> std::fmt::Debug for WithTypeInfo<'a, T> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let provider = GLOBAL_TYPE_INFO_PROVIDER - .get() - .and_then(|get_provider| get_provider()); + let provider = self.1.or_else(|| { + GLOBAL_TYPE_INFO_PROVIDER + .get() + .and_then(|get_provider| get_provider()) + }); self.0.to_string_with_type_info(f, provider) } } @@ -168,19 +248,25 @@ impl SelfBuilder for T { /// Helper wrapper around `std::fmt::DebugStruct` that formats struct fields /// using `DebugWithTypeInfo` so that type-aware formatting is available for /// each field. -pub struct DebugStruct<'a, 'b: 'a> { +pub struct DebugStruct<'a, 'b: 'a, 't> { builder: std::fmt::DebugStruct<'a, 'b>, + type_info: Option<&'t dyn GetTypeInfo>, } -impl<'a, 'b: 'a> DebugStruct<'a, 'b> { +impl<'a, 'b: 'a, 't> DebugStruct<'a, 'b, 't> { /// Create a new `DebugStruct` builder for a struct with the given name. /// /// The returned value can be used to add fields which implement /// `DebugWithTypeInfo` and then finished to produce the final formatting /// result. - pub fn new(f: &'a mut std::fmt::Formatter<'b>, name: &str) -> Self { + pub fn new( + f: &'a mut std::fmt::Formatter<'b>, + name: &str, + type_info: Option<&'t dyn GetTypeInfo>, + ) -> Self { Self { builder: f.debug_struct(name), + type_info, } } @@ -189,7 +275,10 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// The `value` will be displayed using its `DebugWithTypeInfo` /// implementation so that any available type information can be used. pub fn field(&mut self, name: &str, value: &dyn DebugWithTypeInfo) -> &mut Self { - self.builder.field(name, &WithTypeInfo::new(value)); + self.builder.field( + name, + &WithTypeInfo::new_with_opt_info(value, self.type_info), + ); self } @@ -203,23 +292,30 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// Helper wrapper around `std::fmt::DebugTuple` that formats tuple elements /// using `DebugWithTypeInfo` so elements can render with optional type /// information. -pub struct DebugTuple<'a, 'b: 'a> { +pub struct DebugTuple<'a, 'b: 'a, 't> { builder: std::fmt::DebugTuple<'a, 'b>, + type_info: Option<&'t dyn GetTypeInfo>, } -impl<'a, 'b: 'a> DebugTuple<'a, 'b> { +impl<'a, 'b: 'a, 't> DebugTuple<'a, 'b, 't> { /// Create a new `DebugTuple` builder for a tuple-like value with the /// given name. - pub fn new(f: &'a mut std::fmt::Formatter<'b>, name: &str) -> Self { + pub fn new( + f: &'a mut std::fmt::Formatter<'b>, + name: &str, + type_info: Option<&'t dyn GetTypeInfo>, + ) -> Self { Self { builder: f.debug_tuple(name), + type_info, } } /// Add an element to the tuple being formatted. The element will be /// formatted via its `DebugWithTypeInfo` implementation. pub fn field(&mut self, value: &dyn DebugWithTypeInfo) -> &mut Self { - self.builder.field(&WithTypeInfo::new(value)); + self.builder + .field(&WithTypeInfo::new_with_opt_info(value, self.type_info)); self } @@ -233,22 +329,25 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { /// Helper wrapper around `std::fmt::DebugList` which formats list entries /// using `DebugWithTypeInfo` so each entry can use available type /// information during formatting. -pub struct DebugList<'a, 'b: 'a> { +pub struct DebugList<'a, 'b: 'a, 't> { builder: std::fmt::DebugList<'a, 'b>, + type_info: Option<&'t dyn GetTypeInfo>, } -impl<'a, 'b: 'a> DebugList<'a, 'b> { +impl<'a, 'b: 'a, 't> DebugList<'a, 'b, 't> { /// Create a new `DebugList` builder. - pub fn new(f: &'a mut std::fmt::Formatter<'b>) -> Self { + pub fn new(f: &'a mut std::fmt::Formatter<'b>, type_info: Option<&'t dyn GetTypeInfo>) -> Self { Self { builder: f.debug_list(), + type_info, } } /// Add a single entry to the list. The entry will be formatted via its /// `DebugWithTypeInfo` implementation. pub fn entry(&mut self, value: &dyn DebugWithTypeInfo) -> &mut Self { - self.builder.entry(&WithTypeInfo::new(value)); + self.builder + .entry(&WithTypeInfo::new_with_opt_info(value, self.type_info)); self } @@ -258,7 +357,8 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { values: I, ) -> &mut Self { for value in values { - self.builder.entry(&WithTypeInfo::new(value)); + self.builder + .entry(&WithTypeInfo::new_with_opt_info(value, self.type_info)); } self } @@ -273,22 +373,25 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// Helper wrapper around `std::fmt::DebugSet` that formats set entries /// using `DebugWithTypeInfo` so entries can render with optional type /// information. -pub struct DebugSet<'a, 'b: 'a> { +pub struct DebugSet<'a, 'b: 'a, 't> { builder: std::fmt::DebugSet<'a, 'b>, + type_info: Option<&'t dyn GetTypeInfo>, } -impl<'a, 'b: 'a> DebugSet<'a, 'b> { +impl<'a, 'b: 'a, 't> DebugSet<'a, 'b, 't> { /// Create a new `DebugSet` builder. - pub fn new(f: &'a mut std::fmt::Formatter<'b>) -> Self { + pub fn new(f: &'a mut std::fmt::Formatter<'b>, type_info: Option<&'t dyn GetTypeInfo>) -> Self { Self { builder: f.debug_set(), + type_info, } } /// Add a single entry to the set. The entry will be formatted via its /// `DebugWithTypeInfo` implementation. pub fn entry(&mut self, value: &dyn DebugWithTypeInfo) -> &mut Self { - self.builder.entry(&WithTypeInfo::new(value)); + self.builder + .entry(&WithTypeInfo::new_with_opt_info(value, self.type_info)); self } @@ -298,7 +401,8 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { values: I, ) -> &mut Self { for value in values { - self.builder.entry(&WithTypeInfo::new(value)); + self.builder + .entry(&WithTypeInfo::new_with_opt_info(value, self.type_info)); } self } @@ -313,15 +417,17 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { /// Helper wrapper around `std::fmt::DebugMap` that formats map keys and /// values using `DebugWithTypeInfo` so both sides of each entry can render /// with optional type information. -pub struct DebugMap<'a, 'b: 'a> { +pub struct DebugMap<'a, 'b: 'a, 't> { builder: std::fmt::DebugMap<'a, 'b>, + type_info: Option<&'t dyn GetTypeInfo>, } -impl<'a, 'b: 'a> DebugMap<'a, 'b> { +impl<'a, 'b: 'a, 't> DebugMap<'a, 'b, 't> { /// Create a new `DebugMap` builder. - pub fn new(f: &'a mut std::fmt::Formatter<'b>) -> Self { + pub fn new(f: &'a mut std::fmt::Formatter<'b>, type_info: Option<&'t dyn GetTypeInfo>) -> Self { Self { builder: f.debug_map(), + type_info, } } @@ -332,8 +438,10 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { key: &dyn DebugWithTypeInfo, value: &dyn DebugWithTypeInfo, ) -> &mut Self { - self.builder - .entry(&WithTypeInfo::new(key), &WithTypeInfo::new(value)); + self.builder.entry( + &WithTypeInfo::new_with_opt_info(key, self.type_info), + &WithTypeInfo::new_with_opt_info(value, self.type_info), + ); self } @@ -345,8 +453,10 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { values: I, ) -> &mut Self { for (key, value) in values { - self.builder - .entry(&WithTypeInfo::new(key), &WithTypeInfo::new(value)); + self.builder.entry( + &WithTypeInfo::new_with_opt_info(key, self.type_info), + &WithTypeInfo::new_with_opt_info(value, self.type_info), + ); } self } @@ -354,14 +464,16 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// Add a key to the map being formatted (used when constructing an /// entry in separate steps). pub fn key(&mut self, key: &dyn DebugWithTypeInfo) -> &mut Self { - self.builder.key(&WithTypeInfo::new(key)); + self.builder + .key(&WithTypeInfo::new_with_opt_info(key, self.type_info)); self } /// Add a value to the map being formatted (used when constructing an /// entry in separate steps). pub fn value(&mut self, value: &dyn DebugWithTypeInfo) -> &mut Self { - self.builder.value(&WithTypeInfo::new(value)); + self.builder + .value(&WithTypeInfo::new_with_opt_info(value, self.type_info)); self } @@ -377,40 +489,74 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// standard formatter builders (`debug_struct`, `debug_tuple`, etc.) but /// ensure that fields, entries and keys/values are displayed through the /// `DebugWithTypeInfo` adapter. -pub trait DebugWithTypeInfoBuilder<'a, 'b: 'a> { +pub trait DebugWithTypeInfoBuilder<'a, 'b: 'a, 't> { /// Start formatting a struct with the given name using type-aware /// field formatting. - fn debug_struct_with_type_info(&'a mut self, name: &str) -> DebugStruct<'a, 'b>; + fn debug_struct_with_type_info( + &'a mut self, + name: &str, + type_info: Option<&'t dyn GetTypeInfo>, + ) -> DebugStruct<'a, 'b, 't>; /// Start formatting a tuple-like value with the given name using /// type-aware element formatting. - fn debug_tuple_with_type_info(&'a mut self, name: &str) -> DebugTuple<'a, 'b>; + fn debug_tuple_with_type_info( + &'a mut self, + name: &str, + type_info: Option<&'t dyn GetTypeInfo>, + ) -> DebugTuple<'a, 'b, 't>; /// Start formatting a list using type-aware entry formatting. - fn debug_list_with_type_info(&'a mut self) -> DebugList<'a, 'b>; + fn debug_list_with_type_info( + &'a mut self, + type_info: Option<&'t dyn GetTypeInfo>, + ) -> DebugList<'a, 'b, 't>; /// Start formatting a set using type-aware entry formatting. - fn debug_set_with_type_info(&'a mut self) -> DebugSet<'a, 'b>; + fn debug_set_with_type_info( + &'a mut self, + type_info: Option<&'t dyn GetTypeInfo>, + ) -> DebugSet<'a, 'b, 't>; /// Start formatting a map using type-aware key/value formatting. - fn debug_map_with_type_info(&'a mut self) -> DebugMap<'a, 'b>; + fn debug_map_with_type_info( + &'a mut self, + type_info: Option<&'t dyn GetTypeInfo>, + ) -> DebugMap<'a, 'b, 't>; } -impl<'a, 'b: 'a> DebugWithTypeInfoBuilder<'a, 'b> for std::fmt::Formatter<'b> { - fn debug_struct_with_type_info(&'a mut self, name: &str) -> DebugStruct<'a, 'b> { - DebugStruct::new(self, name) - } - fn debug_tuple_with_type_info(&'a mut self, name: &str) -> DebugTuple<'a, 'b> { - DebugTuple::new(self, name) - } - fn debug_list_with_type_info(&'a mut self) -> DebugList<'a, 'b> { - DebugList::new(self) - } - fn debug_set_with_type_info(&'a mut self) -> DebugSet<'a, 'b> { - DebugSet::new(self) - } - fn debug_map_with_type_info(&'a mut self) -> DebugMap<'a, 'b> { - DebugMap::new(self) +impl<'a, 'b: 'a, 't> DebugWithTypeInfoBuilder<'a, 'b, 't> for std::fmt::Formatter<'b> { + fn debug_struct_with_type_info( + &'a mut self, + name: &str, + type_info: Option<&'t dyn GetTypeInfo>, + ) -> DebugStruct<'a, 'b, 't> { + DebugStruct::new(self, name, type_info) + } + fn debug_tuple_with_type_info( + &'a mut self, + name: &str, + type_info: Option<&'t dyn GetTypeInfo>, + ) -> DebugTuple<'a, 'b, 't> { + DebugTuple::new(self, name, type_info) + } + fn debug_list_with_type_info( + &'a mut self, + type_info: Option<&'t dyn GetTypeInfo>, + ) -> DebugList<'a, 'b, 't> { + DebugList::new(self, type_info) + } + fn debug_set_with_type_info( + &'a mut self, + type_info: Option<&'t dyn GetTypeInfo>, + ) -> DebugSet<'a, 'b, 't> { + DebugSet::new(self, type_info) + } + fn debug_map_with_type_info( + &'a mut self, + type_info: Option<&'t dyn GetTypeInfo>, + ) -> DebugMap<'a, 'b, 't> { + DebugMap::new(self, type_info) } } diff --git a/crates/bevy_mod_scripting_display/src/printer/mod.rs b/crates/bevy_mod_scripting_display/src/printer/mod.rs index 1e9b54f67b..0883d3a9b2 100644 --- a/crates/bevy_mod_scripting_display/src/printer/mod.rs +++ b/crates/bevy_mod_scripting_display/src/printer/mod.rs @@ -3,17 +3,22 @@ use bevy_reflect::{DynamicTypePath, PartialReflect, ReflectRef, VariantField}; use crate::*; /// Contains a strategy for printing a `Reflect` value as if it was its native `Debug` implementation. -pub struct ReflectPrinter<'f, 'b: 'f> { +pub struct ReflectPrinter<'f, 'b: 'f, 't> { pub(crate) formatter: &'f mut std::fmt::Formatter<'b>, pub(crate) result: std::fmt::Result, + pub(crate) type_info: Option<&'t dyn GetTypeInfo>, } -impl<'f, 'b: 'f> ReflectPrinter<'f, 'b> { +impl<'f, 'b: 'f, 't> ReflectPrinter<'f, 'b, 't> { /// Creates a new `ReflectPrinter` with the given formatter. - pub fn new(formatter: &'f mut std::fmt::Formatter<'b>) -> ReflectPrinter<'f, 'b> { + pub fn new( + formatter: &'f mut std::fmt::Formatter<'b>, + type_info: Option<&'t dyn GetTypeInfo>, + ) -> ReflectPrinter<'f, 'b, 't> { ReflectPrinter { formatter, result: Ok(()), + type_info, } } @@ -23,12 +28,12 @@ impl<'f, 'b: 'f> ReflectPrinter<'f, 'b> { match value.reflect_ref() { ReflectRef::Struct(s) => self .formatter - .debug_struct_with_type_info(s.reflect_ident_or_short_path()) + .debug_struct_with_type_info(s.reflect_ident_or_short_path(), self.type_info) .build_with(|mut b| { for (i, field) in s.iter_fields().enumerate() { b.field( s.name_at(i).unwrap_or("unknown"), - &PrintReflectAsDebug(field), + &PrintReflectAsDebug::new_with_opt_info(field, self.type_info), ); } b @@ -36,60 +41,75 @@ impl<'f, 'b: 'f> ReflectPrinter<'f, 'b> { .finish(), ReflectRef::TupleStruct(s) => self .formatter - .debug_tuple_with_type_info(s.reflect_ident_or_short_path()) + .debug_tuple_with_type_info(s.reflect_ident_or_short_path(), self.type_info) .build_with(|mut b| { for field in s.iter_fields() { - b.field(&PrintReflectAsDebug(field)); + b.field(&PrintReflectAsDebug::new_with_opt_info( + field, + self.type_info, + )); } b }) .finish(), ReflectRef::Tuple(t) => self .formatter - .debug_tuple_with_type_info(t.reflect_ident_or_short_path()) + .debug_tuple_with_type_info(t.reflect_ident_or_short_path(), self.type_info) .build_with(|mut b| { for field in t.iter_fields() { - b.field(&PrintReflectAsDebug(field)); + b.field(&PrintReflectAsDebug::new_with_opt_info( + field, + self.type_info, + )); } b }) .finish(), ReflectRef::List(l) => self .formatter - .debug_list_with_type_info() + .debug_list_with_type_info(self.type_info) .build_with(|mut b| { for field in l.iter() { - b.entry(&PrintReflectAsDebug(field)); + b.entry(&PrintReflectAsDebug::new_with_opt_info( + field, + self.type_info, + )); } b }) .finish(), ReflectRef::Array(a) => self .formatter - .debug_list_with_type_info() + .debug_list_with_type_info(self.type_info) .build_with(|mut b| { for field in a.iter() { - b.entry(&PrintReflectAsDebug(field)); + b.entry(&PrintReflectAsDebug::new_with_opt_info( + field, + self.type_info, + )); } b }) .finish(), ReflectRef::Map(m) => self .formatter - .debug_map_with_type_info() + .debug_map_with_type_info(self.type_info) .build_with(|mut b| { for (k, v) in m.iter() { - b.entry(&PrintReflectAsDebug(k), &PrintReflectAsDebug(v)); + b.entry( + &PrintReflectAsDebug::new_with_opt_info(k, self.type_info), + &PrintReflectAsDebug::new_with_opt_info(v, self.type_info), + ); } b }) .finish(), ReflectRef::Set(s) => self .formatter - .debug_set_with_type_info() + .debug_set_with_type_info(self.type_info) .build_with(|mut b| { for v in s.iter() { - b.entry(&PrintReflectAsDebug(v)); + b.entry(&PrintReflectAsDebug::new_with_opt_info(v, self.type_info)); } b }) @@ -99,11 +119,14 @@ impl<'f, 'b: 'f> ReflectPrinter<'f, 'b> { let variant_path = e.variant_name(); if is_tuple { self.formatter - .debug_tuple_with_type_info(variant_path) + .debug_tuple_with_type_info(variant_path, self.type_info) .build_with(|mut b| { for f in e.iter_fields() { if let VariantField::Tuple(v) = f { - b.field(&PrintReflectAsDebug(v)); + b.field(&PrintReflectAsDebug::new_with_opt_info( + v, + self.type_info, + )); } // should not be possible } @@ -112,11 +135,17 @@ impl<'f, 'b: 'f> ReflectPrinter<'f, 'b> { .finish() } else { self.formatter - .debug_struct_with_type_info(variant_path) + .debug_struct_with_type_info(variant_path, self.type_info) .build_with(|mut b| { for f in e.iter_fields() { if let VariantField::Struct(name, value) = f { - b.field(name, &PrintReflectAsDebug(value)); + b.field( + name, + &PrintReflectAsDebug::new_with_opt_info( + value, + self.type_info, + ), + ); } // should not be possible } @@ -125,7 +154,20 @@ impl<'f, 'b: 'f> ReflectPrinter<'f, 'b> { .finish() } } - ReflectRef::Opaque(o) => o.debug(self.formatter), + ReflectRef::Opaque(o) => { + if let Some(type_info_provider) = &self.type_info { + if let Some(reflect_type) = o.try_as_reflect() + && let Some(display_type_data) = + type_info_provider + .get_type_data::(reflect_type.type_id()) + && let Some(as_dyn_trait) = display_type_data.get(reflect_type) + { + return as_dyn_trait.display_with_type_info(self.formatter, self.type_info); + } + } + + o.debug(self.formatter) + } } } @@ -148,21 +190,38 @@ impl GetIdentOrPath for T { } /// A wrapper type that implements `Debug` for any `PartialReflect` by using `ReflectPrinter`. -pub struct PrintReflectAsDebug<'a>(pub &'a dyn PartialReflect); +/// +/// For opaque types will optionally seek [`ReflectDisplayWithTypeInfo`] type data in the registry +pub struct PrintReflectAsDebug<'a, 'g>(&'a dyn PartialReflect, Option<&'g dyn GetTypeInfo>); + +impl<'a, 'g> PrintReflectAsDebug<'a, 'g> { + /// Constructs a new [`PrintReflectAsDebug`] which will use the global type info provider + pub fn new(val: &'a dyn PartialReflect) -> Self { + Self(val, None) + } + + /// Constructs a new [`PrintReflectAsDebug`] which will use the provided type info provider and fallback to the global + pub fn new_with_opt_info( + val: &'a dyn PartialReflect, + info: Option<&'g dyn GetTypeInfo>, + ) -> Self { + Self(val, info) + } +} -impl DebugWithTypeInfo for PrintReflectAsDebug<'_> { +impl DebugWithTypeInfo for PrintReflectAsDebug<'_, '_> { fn to_string_with_type_info( &self, f: &mut std::fmt::Formatter, - _type_info_provider: Option<&dyn GetTypeInfo>, + type_info_provider: Option<&dyn GetTypeInfo>, ) -> std::fmt::Result { - ReflectPrinter::new(f).debug(self.0) + ReflectPrinter::new(f, self.1.or(type_info_provider)).debug(self.0) } } -impl std::fmt::Debug for PrintReflectAsDebug<'_> { +impl std::fmt::Debug for PrintReflectAsDebug<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - WithTypeInfo(self).fmt(f) + WithTypeInfo::new_with_opt_info(self, self.1).fmt(f) } } @@ -172,36 +231,39 @@ mod test { #[test] fn test_reflect_printer() { - assert_eq!(format!("{:?}", PrintReflectAsDebug(&42u32)), "42"); - assert_eq!(format!("{:?}", PrintReflectAsDebug(&"asd")), "\"asd\""); + assert_eq!(format!("{:?}", PrintReflectAsDebug::new(&42u32)), "42"); + assert_eq!(format!("{:?}", PrintReflectAsDebug::new(&"asd")), "\"asd\""); assert_eq!( - format!("{:?}", PrintReflectAsDebug(&vec![1, 2, 3])), + format!("{:?}", PrintReflectAsDebug::new(&vec![1, 2, 3])), "[1, 2, 3]" ); assert_eq!( - format!("{:?}", PrintReflectAsDebug(&Some("value"))), + format!("{:?}", PrintReflectAsDebug::new(&Some("value"))), "Some(\"value\")" ); - assert_eq!(format!("{:?}", PrintReflectAsDebug(&None::)), "None"); assert_eq!( - format!("{:?}", PrintReflectAsDebug(&("a", 42, true))), + format!("{:?}", PrintReflectAsDebug::new(&None::)), + "None" + ); + assert_eq!( + format!("{:?}", PrintReflectAsDebug::new(&("a", 42, true))), "(&str, i32, bool)(\"a\", 42, true)" ); assert_eq!( format!( "{:?}", - PrintReflectAsDebug(&bevy_platform::collections::HashMap::::from([( - 1, "a" - ),])) + PrintReflectAsDebug::new(&bevy_platform::collections::HashMap::::from( + [(1, "a"),] + )) ), "{1: \"a\"}" ); assert_eq!( - format!("{:?}", PrintReflectAsDebug(&[1, 2, 3])), + format!("{:?}", PrintReflectAsDebug::new(&[1, 2, 3])), "[1, 2, 3]" ); assert_eq!( - format!("{:?}", PrintReflectAsDebug(&Some(vec![1, 2, 3]))), + format!("{:?}", PrintReflectAsDebug::new(&Some(vec![1, 2, 3]))), "Some([1, 2, 3])" ); } diff --git a/crates/bevy_mod_scripting_functions/Cargo.toml b/crates/bevy_mod_scripting_functions/Cargo.toml index d62c480331..5ea65d4703 100644 --- a/crates/bevy_mod_scripting_functions/Cargo.toml +++ b/crates/bevy_mod_scripting_functions/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "bevy_mod_scripting_functions" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Necessary functionality for Lua support with bevy_mod_scripting" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] -readme = "readme.md" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +readme.workspace = true [features] bevy_a11y = ["bevy_a11y_bms_bindings"] diff --git a/crates/bevy_mod_scripting_functions/readme.md b/crates/bevy_mod_scripting_functions/readme.md deleted file mode 100644 index e28c0107c6..0000000000 --- a/crates/bevy_mod_scripting_functions/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# bevy_mod_scripting_lua_functions - -This crate is a part of the ["bevy_mod_scripting" workspace](https://github.com/makspll/bevy_mod_scripting). \ No newline at end of file diff --git a/crates/bindings/bevy_a11y_bms_bindings/Cargo.toml b/crates/bindings/bevy_a11y_bms_bindings/Cargo.toml index 72682e6c44..d96004ec63 100644 --- a/crates/bindings/bevy_a11y_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_a11y_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_a11y_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_a11y crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_animation_bms_bindings/Cargo.toml b/crates/bindings/bevy_animation_bms_bindings/Cargo.toml index 29420ce96a..9fe0a95618 100644 --- a/crates/bindings/bevy_animation_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_animation_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_animation_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_animation crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_asset_bms_bindings/Cargo.toml b/crates/bindings/bevy_asset_bms_bindings/Cargo.toml index f8614c048d..bcefd2f8cc 100644 --- a/crates/bindings/bevy_asset_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_asset_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_asset_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_asset crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_color_bms_bindings/Cargo.toml b/crates/bindings/bevy_color_bms_bindings/Cargo.toml index ecad6ee17d..b80e8f213f 100644 --- a/crates/bindings/bevy_color_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_color_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_color_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_color crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] bevy_ecs = { workspace = true, features = ["std", "bevy_reflect"] } @@ -18,10 +17,10 @@ bevy_app = { workspace = true, features = ["std"] } bevy_mod_scripting_core = { workspace = true } bevy_mod_scripting_bindings = { workspace = true } bevy_mod_scripting_derive = { workspace = true } -bevy_color = { version = "0.16.2", features = ["std", "bevy_reflect", "encase", "alloc", "wgpu-types"], default-features = true} +bevy_color = { version = "0.16.2", features = ["wgpu-types", "alloc", "std", "bevy_reflect", "encase"], default-features = true} -bevy_math = { version = "^0.16.1", features = ["std", "alloc"], default-features = false} +bevy_math = { version = "^0.16.1", features = ["alloc", "std"], default-features = false} bevy_reflect = { version = "^0.16.1", features = ["std"], default-features = false} diff --git a/crates/bindings/bevy_core_pipeline_bms_bindings/Cargo.toml b/crates/bindings/bevy_core_pipeline_bms_bindings/Cargo.toml index ce470d6b4c..de3b94b10d 100644 --- a/crates/bindings/bevy_core_pipeline_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_core_pipeline_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_core_pipeline_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_core_pipeline crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_ecs_bms_bindings/Cargo.toml b/crates/bindings/bevy_ecs_bms_bindings/Cargo.toml index bf5dd80161..0fced1ff44 100644 --- a/crates/bindings/bevy_ecs_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_ecs_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_ecs_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_ecs crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] @@ -18,7 +17,7 @@ bevy_app = { workspace = true, features = ["std"] } bevy_mod_scripting_core = { workspace = true } bevy_mod_scripting_bindings = { workspace = true } bevy_mod_scripting_derive = { workspace = true } -bevy_ecs = { version = "0.16.1", features = ["std", "bevy_reflect", "async_executor", "backtrace", "multi_threaded", "serialize"], default-features = true} +bevy_ecs = { version = "0.16.1", features = ["std", "async_executor", "multi_threaded", "bevy_reflect", "backtrace", "serialize"], default-features = true} arrayvec = { version = "^0.7.4", features = ["std"], default-features = false} diff --git a/crates/bindings/bevy_gizmos_bms_bindings/Cargo.toml b/crates/bindings/bevy_gizmos_bms_bindings/Cargo.toml index d8447a5a66..43cbd09bc3 100644 --- a/crates/bindings/bevy_gizmos_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_gizmos_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_gizmos_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_gizmos crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_gltf_bms_bindings/Cargo.toml b/crates/bindings/bevy_gltf_bms_bindings/Cargo.toml index 54e687c089..ae41f2ee59 100644 --- a/crates/bindings/bevy_gltf_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_gltf_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_gltf_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_gltf crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_image_bms_bindings/Cargo.toml b/crates/bindings/bevy_image_bms_bindings/Cargo.toml index 826811e988..42a1975098 100644 --- a/crates/bindings/bevy_image_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_image_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_image_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_image crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] bevy_ecs = { workspace = true, features = ["std", "bevy_reflect"] } @@ -18,7 +17,7 @@ bevy_ecs = { workspace = true, features = ["std", "bevy_reflect"] } bevy_mod_scripting_core = { workspace = true } bevy_mod_scripting_bindings = { workspace = true } bevy_mod_scripting_derive = { workspace = true } -bevy_image = { version = "0.16.1", features = ["bevy_reflect", "hdr", "ktx2", "png", "ruzstd", "zstd"], default-features = true} +bevy_image = { version = "0.16.1", features = ["hdr", "ktx2", "png", "ruzstd", "zstd", "bevy_reflect"], default-features = true} bevy_app = { version = "^0.16.1", features = [], default-features = true} diff --git a/crates/bindings/bevy_input_bms_bindings/Cargo.toml b/crates/bindings/bevy_input_bms_bindings/Cargo.toml index 28287c6158..7efed52b19 100644 --- a/crates/bindings/bevy_input_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_input_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_input_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_input crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_input_focus_bms_bindings/Cargo.toml b/crates/bindings/bevy_input_focus_bms_bindings/Cargo.toml index 87c1854f6c..be0fc5be9d 100644 --- a/crates/bindings/bevy_input_focus_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_input_focus_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_input_focus_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_input_focus crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_math_bms_bindings/Cargo.toml b/crates/bindings/bevy_math_bms_bindings/Cargo.toml index da94c64de2..07a44d48fb 100644 --- a/crates/bindings/bevy_math_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_math_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_math_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_math crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] bevy_ecs = { workspace = true, features = ["std", "bevy_reflect"] } @@ -18,7 +17,7 @@ bevy_app = { workspace = true, features = ["std"] } bevy_mod_scripting_core = { workspace = true } bevy_mod_scripting_bindings = { workspace = true } bevy_mod_scripting_derive = { workspace = true } -bevy_math = { version = "0.16.1", features = ["std", "rand", "curve", "alloc", "bevy_reflect"], default-features = true} +bevy_math = { version = "0.16.1", features = ["alloc", "std", "rand", "curve", "bevy_reflect"], default-features = true} bevy_reflect = { version = "^0.16.1", features = ["std"], default-features = false} @@ -27,11 +26,11 @@ derive_more = { version = "^1", features = ["std"], default-features = false} glam = { version = "^0.29.3", features = ["std", "rand"], default-features = false} -itertools = { version = "^0.14.0", features = ["use_std", "use_alloc"], default-features = false} +itertools = { version = "^0.14.0", features = ["use_alloc", "use_std"], default-features = false} -rand = { version = "^0.8", features = ["std", "alloc"], default-features = false} +rand = { version = "^0.8", features = ["alloc", "std"], default-features = false} -rand_distr = { version = "^0.4.3", features = ["std", "alloc"], default-features = false} +rand_distr = { version = "^0.4.3", features = ["alloc", "std"], default-features = false} smallvec = { version = "^1.11", features = [], default-features = true} diff --git a/crates/bindings/bevy_mesh_bms_bindings/Cargo.toml b/crates/bindings/bevy_mesh_bms_bindings/Cargo.toml index 5d269b3060..29fc5b7138 100644 --- a/crates/bindings/bevy_mesh_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_mesh_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_mesh_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_mesh crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_pbr_bms_bindings/Cargo.toml b/crates/bindings/bevy_pbr_bms_bindings/Cargo.toml index ffd6f32579..ce45924217 100644 --- a/crates/bindings/bevy_pbr_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_pbr_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_pbr_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_pbr crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_picking_bms_bindings/Cargo.toml b/crates/bindings/bevy_picking_bms_bindings/Cargo.toml index c58573f977..d272760d1c 100644 --- a/crates/bindings/bevy_picking_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_picking_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_picking_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_picking crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_reflect_bms_bindings/Cargo.toml b/crates/bindings/bevy_reflect_bms_bindings/Cargo.toml index 0df424f0b2..ce7ea38aa4 100644 --- a/crates/bindings/bevy_reflect_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_reflect_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_reflect_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_reflect crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] bevy_ecs = { workspace = true, features = ["std", "bevy_reflect"] } diff --git a/crates/bindings/bevy_render_bms_bindings/Cargo.toml b/crates/bindings/bevy_render_bms_bindings/Cargo.toml index c0ea6fbf68..c62dc4d42f 100644 --- a/crates/bindings/bevy_render_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_render_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_render_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_render crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_scene_bms_bindings/Cargo.toml b/crates/bindings/bevy_scene_bms_bindings/Cargo.toml index cde7d721d6..dd573097d8 100644 --- a/crates/bindings/bevy_scene_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_scene_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_scene_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_scene crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_sprite_bms_bindings/Cargo.toml b/crates/bindings/bevy_sprite_bms_bindings/Cargo.toml index fec2dfb0e5..0a7b33cccc 100644 --- a/crates/bindings/bevy_sprite_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_sprite_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_sprite_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_sprite crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_text_bms_bindings/Cargo.toml b/crates/bindings/bevy_text_bms_bindings/Cargo.toml index 455929d5da..9bddf22dee 100644 --- a/crates/bindings/bevy_text_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_text_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_text_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_text crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_time_bms_bindings/Cargo.toml b/crates/bindings/bevy_time_bms_bindings/Cargo.toml index 46495c3283..2c35fd606b 100644 --- a/crates/bindings/bevy_time_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_time_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_time_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_time crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] diff --git a/crates/bindings/bevy_transform_bms_bindings/Cargo.toml b/crates/bindings/bevy_transform_bms_bindings/Cargo.toml index 74aad20fba..8587e5e382 100644 --- a/crates/bindings/bevy_transform_bms_bindings/Cargo.toml +++ b/crates/bindings/bevy_transform_bms_bindings/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "bevy_transform_bms_bindings" -version = "0.15.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for bevy_transform crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] @@ -18,7 +17,7 @@ readme = "readme.md" bevy_mod_scripting_core = { workspace = true } bevy_mod_scripting_bindings = { workspace = true } bevy_mod_scripting_derive = { workspace = true } -bevy_transform = { version = "0.16.1", features = ["std", "bevy-support", "bevy_reflect", "async_executor", "alloc", "bevy_log"], default-features = true} +bevy_transform = { version = "0.16.1", features = ["std", "async_executor", "alloc", "bevy_log", "bevy-support", "bevy_reflect"], default-features = true} bevy_app = { version = "^0.16.1", features = ["std", "bevy_reflect"], default-features = false} diff --git a/crates/lad_backends/mdbook_lad_preprocessor/Cargo.toml b/crates/lad_backends/mdbook_lad_preprocessor/Cargo.toml index 939873041c..83673aa31c 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/Cargo.toml +++ b/crates/lad_backends/mdbook_lad_preprocessor/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "mdbook_lad_preprocessor" version = "0.2.0" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Language Agnostic Declaration (LAD) file format for the bevy_mod_scripting crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "documentation", "generator"] -categories = ["game-development", "development-tools"] -include = ["readme.md", "/src"] readme = "readme.md" +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] env_logger = { workspace = true } diff --git a/crates/ladfile/Cargo.toml b/crates/ladfile/Cargo.toml index 01b2845579..37d9efd97e 100644 --- a/crates/ladfile/Cargo.toml +++ b/crates/ladfile/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "ladfile" version = "0.5.0" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Language Agnostic Declaration (LAD) file format for the bevy_mod_scripting crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "format", "json"] -categories = ["game-development", "parser-implementations"] -include = ["readme.md", "/src", "/test_assets"] readme = "readme.md" +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] serde = { workspace = true, features = ["derive"] } diff --git a/crates/ladfile_builder/Cargo.toml b/crates/ladfile_builder/Cargo.toml index 5fe8938840..09ee2e044a 100644 --- a/crates/ladfile_builder/Cargo.toml +++ b/crates/ladfile_builder/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "ladfile_builder" version = "0.5.1" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Language Agnostic Declaration (LAD) file format for the bevy_mod_scripting crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "format", "json"] -categories = ["game-development", "parser-implementations"] -include = ["readme.md", "/src", "/test_assets"] -readme = "readme.md" +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +readme.workspace = true [dependencies] bevy_app = { workspace = true, default-features = false, features = [] } diff --git a/crates/ladfile_builder/readme.md b/crates/ladfile_builder/readme.md deleted file mode 100644 index 08b05ead71..0000000000 --- a/crates/ladfile_builder/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# Language Agnostic Declaration file builder - -Contains utilities for building LAD's using `bevy_mod_scripting_core` and `bevy`. \ No newline at end of file diff --git a/crates/languages/bevy_mod_scripting_lua/Cargo.toml b/crates/languages/bevy_mod_scripting_lua/Cargo.toml index 562a8990de..e9801676b2 100644 --- a/crates/languages/bevy_mod_scripting_lua/Cargo.toml +++ b/crates/languages/bevy_mod_scripting_lua/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "bevy_mod_scripting_lua" -version = "0.15.1" -authors = ["Maksymilian Mozolewski "] -edition = "2024" -license = "MIT OR Apache-2.0" description = "Necessary functionality for Lua support with bevy_mod_scripting" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] -readme = "readme.md" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +readme.workspace = true [package.metadata."docs.rs"] features = ["lua54"] diff --git a/crates/languages/bevy_mod_scripting_lua/readme.md b/crates/languages/bevy_mod_scripting_lua/readme.md deleted file mode 100644 index f45d66e632..0000000000 --- a/crates/languages/bevy_mod_scripting_lua/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# bevy_mod_scripting_lua - -This crate is a part of the ["bevy_mod_scripting" workspace](https://github.com/makspll/bevy_mod_scripting). \ No newline at end of file diff --git a/crates/languages/bevy_mod_scripting_lua/src/lib.rs b/crates/languages/bevy_mod_scripting_lua/src/lib.rs index 393deed3fa..9edbba24a8 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/lib.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/lib.rs @@ -17,7 +17,6 @@ use bevy_mod_scripting_bindings::{ use bevy_mod_scripting_core::{ IntoScriptPluginParams, ScriptingPlugin, config::{GetPluginThreadConfig, ScriptingPluginConfiguration}, - error::ScriptError, event::CallbackLabel, extractors::GetPluginFor, make_plugin_config_static, @@ -207,7 +206,7 @@ fn load_lua_content_into_context( context_key: &ScriptAttachment, content: &[u8], world_id: WorldId, -) -> Result<(), ScriptError> { +) -> Result<(), InteropError> { let config = LuaScriptingPlugin::readonly_configuration(world_id); let initializers = config.context_initialization_callbacks; let pre_handling_initializers = config.pre_handling_callbacks; @@ -233,7 +232,7 @@ pub fn lua_context_load( context_key: &ScriptAttachment, content: &[u8], world_id: WorldId, -) -> Result { +) -> Result { #[cfg(feature = "unsafe_lua_modules")] let mut context = LuaContext(unsafe { Lua::unsafe_new() }); #[cfg(not(feature = "unsafe_lua_modules"))] @@ -250,7 +249,7 @@ pub fn lua_context_reload( content: &[u8], old_ctxt: &mut LuaContext, world_id: WorldId, -) -> Result<(), ScriptError> { +) -> Result<(), InteropError> { load_lua_content_into_context(old_ctxt, context_key, content, world_id)?; Ok(()) } @@ -264,7 +263,7 @@ pub fn lua_handler( callback_label: &CallbackLabel, context: &mut LuaContext, world_id: WorldId, -) -> Result { +) -> Result { let config = LuaScriptingPlugin::readonly_configuration(world_id); config diff --git a/crates/languages/bevy_mod_scripting_rhai/Cargo.toml b/crates/languages/bevy_mod_scripting_rhai/Cargo.toml index 146ac80327..5b34fbac5a 100644 --- a/crates/languages/bevy_mod_scripting_rhai/Cargo.toml +++ b/crates/languages/bevy_mod_scripting_rhai/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "bevy_mod_scripting_rhai" -version = "0.15.1" -authors = ["Maksymilian Mozolewski "] -edition = "2024" -license = "MIT OR Apache-2.0" description = "Necessary functionality for Rhai support with bevy_mod_scripting" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] -readme = "readme.md" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +readme.workspace = true [lib] name = "bevy_mod_scripting_rhai" diff --git a/crates/languages/bevy_mod_scripting_rhai/readme.md b/crates/languages/bevy_mod_scripting_rhai/readme.md deleted file mode 100644 index f53e843c70..0000000000 --- a/crates/languages/bevy_mod_scripting_rhai/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# bevy_mod_scripting_rhai - -This crate is a part of the ["bevy_mod_scripting" workspace](https://github.com/makspll/bevy_mod_scripting). \ No newline at end of file diff --git a/crates/languages/bevy_mod_scripting_rhai/src/lib.rs b/crates/languages/bevy_mod_scripting_rhai/src/lib.rs index 94b2e78be2..8d82fdb9af 100644 --- a/crates/languages/bevy_mod_scripting_rhai/src/lib.rs +++ b/crates/languages/bevy_mod_scripting_rhai/src/lib.rs @@ -1,6 +1,6 @@ //! Rhai scripting language support for Bevy. -use std::ops::Deref; +use std::{ops::Deref, str::Utf8Error}; use crate::bindings::script_value::{FromDynamic, IntoDynamic}; @@ -20,7 +20,6 @@ use bevy_mod_scripting_bindings::{ use bevy_mod_scripting_core::{ IntoScriptPluginParams, ScriptingPlugin, config::{GetPluginThreadConfig, ScriptingPluginConfiguration}, - error::ScriptError, event::CallbackLabel, extractors::GetPluginFor, make_plugin_config_static, @@ -115,6 +114,12 @@ impl IntoInteropError for ParseError { InteropError::external(self) } } + +impl IntoInteropError for Utf8Error { + fn into_bms_error(self) -> InteropError { + InteropError::external(self) + } +} /// 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 @@ -257,15 +262,19 @@ fn load_rhai_content_into_context( context_key: &ScriptAttachment, content: &[u8], world_id: WorldId, -) -> Result<(), ScriptError> { +) -> Result<(), InteropError> { let config = RhaiScriptingPlugin::readonly_configuration(world_id); let initializers = config.context_initialization_callbacks; let pre_handling_initializers = config.pre_handling_callbacks; let runtime = config.runtime.read(); - context.ast = runtime - .compile(std::str::from_utf8(content)?) - .map_err(IntoInteropError::into_bms_error)?; + context.ast = std::str::from_utf8(content) + .map_err(IntoInteropError::into_bms_error) + .and_then(|content| { + runtime + .compile(content) + .map_err(IntoInteropError::into_bms_error) + })?; context .ast .set_source(context_key.script().display().to_string()); @@ -289,7 +298,7 @@ pub fn rhai_context_load( context_key: &ScriptAttachment, content: &[u8], world_id: WorldId, -) -> Result { +) -> Result { let mut context = RhaiScriptContext { // Using an empty AST as a placeholder. ast: AST::empty(), @@ -305,7 +314,7 @@ pub fn rhai_context_reload( content: &[u8], context: &mut RhaiScriptContext, world_id: WorldId, -) -> Result<(), ScriptError> { +) -> Result<(), InteropError> { load_rhai_content_into_context(context, context_key, content, world_id) } @@ -317,7 +326,7 @@ pub fn rhai_callback_handler( callback: &CallbackLabel, context: &mut RhaiScriptContext, world_id: WorldId, -) -> Result { +) -> Result { let config = RhaiScriptingPlugin::readonly_configuration(world_id); let pre_handling_initializers = config.pre_handling_callbacks; @@ -355,7 +364,7 @@ pub fn rhai_callback_handler( ); Ok(ScriptValue::Unit) } else { - Err(ScriptError::from(e.into_bms_error())) + Err(e.into_bms_error()) } } } diff --git a/crates/testing_crates/script_integration_test_harness/src/lib.rs b/crates/testing_crates/script_integration_test_harness/src/lib.rs index 2a7a780cd5..6a1ca585e1 100644 --- a/crates/testing_crates/script_integration_test_harness/src/lib.rs +++ b/crates/testing_crates/script_integration_test_harness/src/lib.rs @@ -47,7 +47,7 @@ pub fn install_test_plugin(app: &mut App, include_test_functions: bool) { app.add_plugins(( ScriptFunctionsPlugin, CoreScriptGlobalsPlugin::default(), - BMSScriptingInfrastructurePlugin, + BMSScriptingInfrastructurePlugin::default(), )); if include_test_functions { register_test_functions(app); diff --git a/xtask/templates/bindings_crate.toml.tera b/xtask/templates/bindings_crate.toml.tera index c0fddc95b0..fff26c0d9c 100644 --- a/xtask/templates/bindings_crate.toml.tera +++ b/xtask/templates/bindings_crate.toml.tera @@ -1,16 +1,15 @@ [package] name = "{{ crate_name }}_bms_bindings" -version = "{{ crate_version }}" -edition = "2024" -authors = ["Maksymilian Mozolewski "] -license = "MIT OR Apache-2.0" description = "Automatically generated bindings for {{ crate_name }} crate" -repository = "https://github.com/makspll/bevy_mod_scripting" -homepage = "https://github.com/makspll/bevy_mod_scripting" -keywords = ["bevy", "gamedev", "scripting", "rhai"] -categories = ["game-development"] readme = "readme.md" - +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] {% if include_bevy_ecs_dep -%} From 3a7be5ac1fc1b8b1666d3c0a8d505465c81b0fc9 Mon Sep 17 00:00:00 2001 From: makspll Date: Mon, 8 Sep 2025 08:48:36 +0100 Subject: [PATCH 2/5] compilation problem --- examples/docgen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/docgen.rs b/examples/docgen.rs index 1b5960142d..9ec757e8f1 100644 --- a/examples/docgen.rs +++ b/examples/docgen.rs @@ -19,7 +19,7 @@ fn main() -> std::io::Result<()> { // the definitions by themselves CoreScriptGlobalsPlugin::default(), ScriptFunctionsPlugin, - BMSScriptingInfrastructurePlugin, + BMSScriptingInfrastructurePlugin::default(), )); // there are two ways to generate the ladfile From 0197fe4c5b3c5fcaad37738a3a5c0329eff5490b Mon Sep 17 00:00:00 2001 From: makspll Date: Mon, 8 Sep 2025 08:55:16 +0100 Subject: [PATCH 3/5] update docs, allow overriding all types --- .../src/printer/mod.rs | 25 ++++++++----------- .../Summary/controlling-script-bindings.md | 6 +++-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/crates/bevy_mod_scripting_display/src/printer/mod.rs b/crates/bevy_mod_scripting_display/src/printer/mod.rs index 0883d3a9b2..affe7fc9ac 100644 --- a/crates/bevy_mod_scripting_display/src/printer/mod.rs +++ b/crates/bevy_mod_scripting_display/src/printer/mod.rs @@ -24,6 +24,16 @@ impl<'f, 'b: 'f, 't> ReflectPrinter<'f, 'b, 't> { /// Prints a `Reflect` value as if it was its native `Debug` implementation. pub fn debug(&mut self, value: &dyn PartialReflect) -> std::fmt::Result { + if let Some(type_info_provider) = &self.type_info { + if let Some(reflect_type) = value.try_as_reflect() + && let Some(display_type_data) = type_info_provider + .get_type_data::(reflect_type.type_id()) + && let Some(as_dyn_trait) = display_type_data.get(reflect_type) + { + return as_dyn_trait.display_with_type_info(self.formatter, self.type_info); + } + } + // try to print the value as if it was its native Debug implementation match value.reflect_ref() { ReflectRef::Struct(s) => self @@ -154,20 +164,7 @@ impl<'f, 'b: 'f, 't> ReflectPrinter<'f, 'b, 't> { .finish() } } - ReflectRef::Opaque(o) => { - if let Some(type_info_provider) = &self.type_info { - if let Some(reflect_type) = o.try_as_reflect() - && let Some(display_type_data) = - type_info_provider - .get_type_data::(reflect_type.type_id()) - && let Some(as_dyn_trait) = display_type_data.get(reflect_type) - { - return as_dyn_trait.display_with_type_info(self.formatter, self.type_info); - } - } - - o.debug(self.formatter) - } + ReflectRef::Opaque(o) => o.debug(self.formatter), } } diff --git a/docs/src/Summary/controlling-script-bindings.md b/docs/src/Summary/controlling-script-bindings.md index 13479491bb..4a35058745 100644 --- a/docs/src/Summary/controlling-script-bindings.md +++ b/docs/src/Summary/controlling-script-bindings.md @@ -153,8 +153,10 @@ There are a few reserved functions that you can override by registering them on | 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 | a display function, used for displaying a reference to a value | ❌ | ✅ | -| debug | a display function, used for displaying a mutable reference to a value | ❌ | ✅ | +| display | a display function, used for pretty printing values | ❌\* | ✅ | +| debug | a display function, used for displaying the internals of a value | ❌\* | ✅ | + +\* - You can register an instance of `ReflectDisplayWithTypeInfo` type data on `Reflect` implementing types to override their printing behavior 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. From d31d3f0207402de4c459dd0b61f61a8f918eab4a Mon Sep 17 00:00:00 2001 From: makspll Date: Mon, 8 Sep 2025 09:11:37 +0100 Subject: [PATCH 4/5] fix things --- .../added_systems_run_in_parallel.lua | 37 ++++++++++--------- .../added_systems_run_in_parallel.rhai | 37 ++++++++++--------- .../src/reference.rs | 19 ++++++++-- 3 files changed, 53 insertions(+), 40 deletions(-) diff --git a/assets/tests/add_system/added_systems_run_in_parallel.lua b/assets/tests/add_system/added_systems_run_in_parallel.lua index db2421ea49..352287363c 100644 --- a/assets/tests/add_system/added_systems_run_in_parallel.lua +++ b/assets/tests/add_system/added_systems_run_in_parallel.lua @@ -27,25 +27,26 @@ digraph { node_2 [label="bevy_asset::assets::Assets<()>::asset_events"]; node_3 [label="bevy_asset::assets::Assets::asset_events"]; node_4 [label="bevy_mod_scripting_bindings::allocator::garbage_collector"]; - node_5 [label="script_integration_test_harness::dummy_before_post_update_system"]; - node_6 [label="script_integration_test_harness::dummy_post_update_system"]; - node_7 [label="on_test_post_update"]; - node_8 [label="custom_system_a"]; - node_9 [label="custom_system_b"]; - node_10 [label="SystemSet AssetEvents"]; - node_11 [label="SystemSet GarbageCollection"]; - node_12 [label="SystemSet ScriptSystem(custom_system_a)"]; - node_13 [label="SystemSet ScriptSystem(custom_system_b)"]; - node_0 -> node_10 [color=red, label="child of", arrowhead=diamond]; - node_1 -> node_10 [color=red, label="child of", arrowhead=diamond]; - node_2 -> node_10 [color=red, label="child of", arrowhead=diamond]; - node_3 -> node_10 [color=red, label="child of", arrowhead=diamond]; - node_4 -> node_11 [color=red, label="child of", arrowhead=diamond]; - node_8 -> node_12 [color=red, label="child of", arrowhead=diamond]; + node_5 [label="bevy_mod_scripting_core::handler::script_error_logger"]; + node_6 [label="script_integration_test_harness::dummy_before_post_update_system"]; + node_7 [label="script_integration_test_harness::dummy_post_update_system"]; + node_8 [label="on_test_post_update"]; + node_9 [label="custom_system_a"]; + node_10 [label="custom_system_b"]; + node_11 [label="SystemSet AssetEvents"]; + node_12 [label="SystemSet GarbageCollection"]; + node_13 [label="SystemSet ScriptSystem(custom_system_a)"]; + node_14 [label="SystemSet ScriptSystem(custom_system_b)"]; + node_0 -> node_11 [color=red, label="child of", arrowhead=diamond]; + node_1 -> node_11 [color=red, label="child of", arrowhead=diamond]; + node_2 -> node_11 [color=red, label="child of", arrowhead=diamond]; + node_3 -> node_11 [color=red, label="child of", arrowhead=diamond]; + node_4 -> node_12 [color=red, label="child of", arrowhead=diamond]; node_9 -> node_13 [color=red, label="child of", arrowhead=diamond]; - node_5 -> node_6 [color=blue, label="runs before", arrowhead=normal]; - node_7 -> node_8 [color=blue, label="runs before", arrowhead=normal]; - node_7 -> node_9 [color=blue, label="runs before", arrowhead=normal]; + node_10 -> node_14 [color=red, label="child of", arrowhead=diamond]; + node_6 -> node_7 [color=blue, label="runs before", arrowhead=normal]; + node_8 -> node_9 [color=blue, label="runs before", arrowhead=normal]; + node_8 -> node_10 [color=blue, label="runs before", arrowhead=normal]; } ]] assert_str_eq(dot_graph, expected_dot_graph, "Expected the schedule graph to match the expected graph") diff --git a/assets/tests/add_system/added_systems_run_in_parallel.rhai b/assets/tests/add_system/added_systems_run_in_parallel.rhai index c080633633..b6c797324f 100644 --- a/assets/tests/add_system/added_systems_run_in_parallel.rhai +++ b/assets/tests/add_system/added_systems_run_in_parallel.rhai @@ -26,25 +26,26 @@ digraph { node_2 [label="bevy_asset::assets::Assets<()>::asset_events"]; node_3 [label="bevy_asset::assets::Assets::asset_events"]; node_4 [label="bevy_mod_scripting_bindings::allocator::garbage_collector"]; - node_5 [label="script_integration_test_harness::dummy_before_post_update_system"]; - node_6 [label="script_integration_test_harness::dummy_post_update_system"]; - node_7 [label="on_test_post_update"]; - node_8 [label="custom_system_a"]; - node_9 [label="custom_system_b"]; - node_10 [label="SystemSet AssetEvents"]; - node_11 [label="SystemSet GarbageCollection"]; - node_12 [label="SystemSet ScriptSystem(custom_system_a)"]; - node_13 [label="SystemSet ScriptSystem(custom_system_b)"]; - node_0 -> node_10 [color=red, label="child of", arrowhead=diamond]; - node_1 -> node_10 [color=red, label="child of", arrowhead=diamond]; - node_2 -> node_10 [color=red, label="child of", arrowhead=diamond]; - node_3 -> node_10 [color=red, label="child of", arrowhead=diamond]; - node_4 -> node_11 [color=red, label="child of", arrowhead=diamond]; - node_8 -> node_12 [color=red, label="child of", arrowhead=diamond]; + node_5 [label="bevy_mod_scripting_core::handler::script_error_logger"]; + node_6 [label="script_integration_test_harness::dummy_before_post_update_system"]; + node_7 [label="script_integration_test_harness::dummy_post_update_system"]; + node_8 [label="on_test_post_update"]; + node_9 [label="custom_system_a"]; + node_10 [label="custom_system_b"]; + node_11 [label="SystemSet AssetEvents"]; + node_12 [label="SystemSet GarbageCollection"]; + node_13 [label="SystemSet ScriptSystem(custom_system_a)"]; + node_14 [label="SystemSet ScriptSystem(custom_system_b)"]; + node_0 -> node_11 [color=red, label="child of", arrowhead=diamond]; + node_1 -> node_11 [color=red, label="child of", arrowhead=diamond]; + node_2 -> node_11 [color=red, label="child of", arrowhead=diamond]; + node_3 -> node_11 [color=red, label="child of", arrowhead=diamond]; + node_4 -> node_12 [color=red, label="child of", arrowhead=diamond]; node_9 -> node_13 [color=red, label="child of", arrowhead=diamond]; - node_5 -> node_6 [color=blue, label="runs before", arrowhead=normal]; - node_7 -> node_8 [color=blue, label="runs before", arrowhead=normal]; - node_7 -> node_9 [color=blue, label="runs before", arrowhead=normal]; + node_10 -> node_14 [color=red, label="child of", arrowhead=diamond]; + node_6 -> node_7 [color=blue, label="runs before", arrowhead=normal]; + node_8 -> node_9 [color=blue, label="runs before", arrowhead=normal]; + node_8 -> node_10 [color=blue, label="runs before", arrowhead=normal]; }`; assert_str_eq.call(dot_graph, expected_dot_graph, "Expected the schedule graph to match the expected graph"); diff --git a/crates/bevy_mod_scripting_bindings/src/reference.rs b/crates/bevy_mod_scripting_bindings/src/reference.rs index b07cf96375..a9f4a08f80 100644 --- a/crates/bevy_mod_scripting_bindings/src/reference.rs +++ b/crates/bevy_mod_scripting_bindings/src/reference.rs @@ -6,8 +6,9 @@ //! we need wrapper types which have owned and ref variants. use super::{WorldGuard, access_map::ReflectAccessId}; use crate::{ - ReflectAllocationId, ReflectAllocator, error::InteropError, - reflection_extensions::PartialReflectExt, with_access_read, with_access_write, + ReflectAllocationId, ReflectAllocator, ThreadWorldContainer, WorldContainer, + error::InteropError, reflection_extensions::PartialReflectExt, with_access_read, + with_access_write, }; use bevy_ecs::{component::Component, ptr::Ptr, resource::Resource}; use bevy_mod_scripting_derive::DebugWithTypeInfo; @@ -64,7 +65,12 @@ impl DisplayWithTypeInfo for ReflectReference { // Safety: should be safe as the guard is invalidated when world is released per iteration let any: &dyn Any = unsafe { type_info_provider.as_any_static() }; - if let Some(guard) = any.downcast_ref::() { + let guard = any.downcast_ref::().cloned().or_else(|| { + any.downcast_ref::() + .and_then(|t| t.try_get_world().ok()) + }); + + if let Some(guard) = guard { if let Ok(r) = self.with_reflect(guard.clone(), |s| { PrintReflectAsDebug::new_with_opt_info(s, Some(type_info_provider)) .to_string_with_type_info(f, Some(type_info_provider)) @@ -621,7 +627,12 @@ impl DisplayWithTypeInfo for ReflectBase { // Safety: should generally be safe, as the world guard is invalidated once the world is out of scope for the iteration let any: &dyn Any = unsafe { type_info_provider.as_any_static() }; - if let Some(guard) = any.downcast_ref::() { + let guard = any.downcast_ref::().cloned().or_else(|| { + any.downcast_ref::() + .and_then(|t| t.try_get_world().ok()) + }); + + if let Some(guard) = guard { let allocator = guard.allocator(); let allocator = allocator.read(); if let Some(allocation) = allocator.get(id) { From 4aece5e9e0707e4c734157c8b51ccd7a4abd2fff Mon Sep 17 00:00:00 2001 From: makspll Date: Mon, 8 Sep 2025 09:13:34 +0100 Subject: [PATCH 5/5] fix test --- assets/tests/display/print_value_by_default.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/tests/display/print_value_by_default.lua b/assets/tests/display/print_value_by_default.lua index eb78ddcc00..e4cbdba284 100644 --- a/assets/tests/display/print_value_by_default.lua +++ b/assets/tests/display/print_value_by_default.lua @@ -45,7 +45,7 @@ ReflectReference { ), base_id: Resource( ComponentId( - "test_utils::test_data::TestResource", + test_utils::test_data::TestResource, ), ), },