From c376dbe8ec59073c2ae80a82d80e68bf6314b0be Mon Sep 17 00:00:00 2001 From: aggyomfg Date: Mon, 29 Sep 2025 00:28:52 +0400 Subject: [PATCH 01/10] feat: add asset operations via reflect reference --- .../asset_operations/asset_operations.lua | 16 ++ .../asset_operations/asset_operations.rhai | 16 ++ .../src/access_map.rs | 1 + .../src/reference.rs | 144 +++++++++++++++++- crates/bevy_mod_scripting_display/Cargo.toml | 1 + .../src/impls/bevy_asset.rs | 9 ++ .../src/impls/mod.rs | 1 + .../bevy_mod_scripting_functions/src/core.rs | 27 ++++ .../src/test_functions.rs | 28 +++- .../test_utils/src/test_data.rs | 19 ++- 10 files changed, 258 insertions(+), 4 deletions(-) create mode 100644 assets/tests/asset_operations/asset_operations.lua create mode 100644 assets/tests/asset_operations/asset_operations.rhai create mode 100644 crates/bevy_mod_scripting_display/src/impls/bevy_asset.rs diff --git a/assets/tests/asset_operations/asset_operations.lua b/assets/tests/asset_operations/asset_operations.lua new file mode 100644 index 0000000000..77f56e2089 --- /dev/null +++ b/assets/tests/asset_operations/asset_operations.lua @@ -0,0 +1,16 @@ +function on_test() + local asset_data = create_test_asset(42, "TestAssetName") + local test_handle = asset_data[1] + local asset_type_reg = asset_data[2] + + assert(test_handle ~= nil, "Test asset handle should not be nil") + assert(asset_type_reg ~= nil, "TestAsset type registration should exist") + + -- Check asset exists and retrieve it + assert(world.has_asset(test_handle) == true, "has_asset should return true") + + local retrieved_asset = world.get_asset(test_handle, asset_type_reg) + assert(retrieved_asset ~= nil, "Should be able to retrieve the test asset") + assert(retrieved_asset.value == 42, "Asset value should be 42") + assert(retrieved_asset.name == "TestAssetName", "Asset name should be 'TestAssetName'") +end diff --git a/assets/tests/asset_operations/asset_operations.rhai b/assets/tests/asset_operations/asset_operations.rhai new file mode 100644 index 0000000000..d88421208b --- /dev/null +++ b/assets/tests/asset_operations/asset_operations.rhai @@ -0,0 +1,16 @@ +fn on_test() { + let asset_data = create_test_asset(42, "TestAssetName"); + let test_handle = asset_data[0]; + let asset_type_reg = asset_data[1]; + + assert(test_handle != (), "Test asset handle should not be nil"); + assert(asset_type_reg != (), "TestAsset type registration should exist"); + + // Check asset exists and retrieve it + assert(world.has_asset.call(test_handle) == true, "has_asset should return true"); + + let retrieved_asset = world.get_asset.call(test_handle, asset_type_reg); + assert(retrieved_asset != (), "Should be able to retrieve the test asset"); + assert(retrieved_asset.value == 42, "Asset value should be 42"); + assert(retrieved_asset.name == "TestAssetName", "Asset name should be 'TestAssetName'"); +} \ No newline at end of file diff --git a/crates/bevy_mod_scripting_bindings/src/access_map.rs b/crates/bevy_mod_scripting_bindings/src/access_map.rs index efa1524d5e..5ff6961e40 100644 --- a/crates/bevy_mod_scripting_bindings/src/access_map.rs +++ b/crates/bevy_mod_scripting_bindings/src/access_map.rs @@ -223,6 +223,7 @@ impl ReflectAccessId { ReflectBase::Resource(id) => Self::for_component_id(id), ReflectBase::Component(_, id) => Self::for_component_id(id), ReflectBase::Owned(id) => Self::for_allocation(id), + ReflectBase::Asset(_, assets_resource_id) => Self::for_component_id(assets_resource_id), } } } diff --git a/crates/bevy_mod_scripting_bindings/src/reference.rs b/crates/bevy_mod_scripting_bindings/src/reference.rs index a9f4a08f80..3bfc8f34fc 100644 --- a/crates/bevy_mod_scripting_bindings/src/reference.rs +++ b/crates/bevy_mod_scripting_bindings/src/reference.rs @@ -10,6 +10,7 @@ use crate::{ error::InteropError, reflection_extensions::PartialReflectExt, with_access_read, with_access_write, }; +use bevy_asset::{ReflectAsset, UntypedHandle}; use bevy_ecs::{component::Component, ptr::Ptr, resource::Resource}; use bevy_mod_scripting_derive::DebugWithTypeInfo; use bevy_mod_scripting_display::{ @@ -252,6 +253,61 @@ impl ReflectReference { } } + /// Create a new reference to an asset by untyped handle. + /// If the type id is incorrect, you will get runtime errors when trying to access the value. + pub fn new_asset_ref( + handle: UntypedHandle, + asset_type_id: TypeId, + world: WorldGuard, + ) -> Result { + Ok(Self { + base: ReflectBaseType::new_asset_base(handle, asset_type_id, world)?, + reflect_path: ParsedPath(Vec::default()), + }) + } + + /// Tries get an untyped asset handle from this reference. + pub fn try_untyped_asset_handle( + &self, + world: WorldGuard, + ) -> Result { + let handle_type_id = self.tail_type_id(world.clone())?.ok_or_else(|| { + InteropError::invariant("Cannot determine handle type ID from reflection") + .with_context("Asset handle reflection failed - handle may be invalid or corrupted") + })?; + + let type_registry = world.type_registry(); + let type_registry = type_registry.read(); + let reflect_handle = type_registry + .get_type_data::(handle_type_id) + .ok_or_else(|| { + InteropError::missing_type_data( + handle_type_id, + "ReflectHandle".to_string(), + ) + .with_context("Handle type is not registered for asset operations - ensure the asset type is properly registered with ReflectHandle type data") + })?; + + let untyped_handle = self.with_reflect(world.clone(), |reflect| { + let reflect_any = reflect.try_as_reflect().ok_or_else(|| { + InteropError::type_mismatch( + std::any::TypeId::of::(), + Some(handle_type_id), + ) + .with_context("Handle must implement Reflect trait for asset operations") + })?; + + reflect_handle + .downcast_handle_untyped(reflect_any.as_any()) + .ok_or_else(|| { + InteropError::invariant("Failed to get UntypedHandle") + .with_context("Handle downcast failed - handle may be of wrong type or corrupted") + }) + })??; + + Ok(untyped_handle) + } + /// Indexes into the reflect path inside this reference. /// You can use [`Self::reflect`] and [`Self::reflect_mut`] to get the actual value. pub fn index_path>(&mut self, index: T) { @@ -399,6 +455,11 @@ impl ReflectReference { return self.walk_path(unsafe { &*arc.get_ptr() }); } + if let ReflectBase::Asset(handle, _) = &self.base.base_id { + let asset = unsafe { self.load_asset_mut(handle, world.clone())? }; + return self.walk_path(asset.as_partial_reflect()); + } + let type_registry = world.type_registry(); let type_registry = type_registry.read(); @@ -454,6 +515,11 @@ impl ReflectReference { return self.walk_path_mut(unsafe { &mut *arc.get_ptr() }); }; + if let ReflectBase::Asset(handle, _) = &self.base.base_id { + let asset = unsafe { self.load_asset_mut(handle, world.clone())? }; + return self.walk_path_mut(asset.as_partial_reflect_mut()); + }; + let type_registry = world.type_registry(); let type_registry = type_registry.read(); @@ -486,6 +552,32 @@ impl ReflectReference { self.walk_path_mut(base.as_partial_reflect_mut()) } + /// Get asset from world and return a mutable reference to it + unsafe fn load_asset_mut<'w>( + &self, + handle: &UntypedHandle, + world: WorldGuard<'w>, + ) -> Result<&'w mut dyn Reflect, InteropError> { + let type_registry = world.type_registry(); + let type_registry = type_registry.read(); + + let reflect_asset: &ReflectAsset = type_registry + .get_type_data(self.base.type_id) + .ok_or_else(|| InteropError::unregistered_base(self.base.clone()))?; + + let world_cell = world.as_unsafe_world_cell()?; + let asset = unsafe { reflect_asset.get_unchecked_mut(world_cell, handle.clone()) } + .ok_or_else(|| { + InteropError::unsupported_operation( + Some(self.base.type_id), + None, + "Asset not loaded or handle is invalid", + ) + })?; + + Ok(asset) + } + fn walk_path<'a>( &self, root: &'a dyn PartialReflect, @@ -589,6 +681,47 @@ impl ReflectBaseType { )), } } + + /// Create a new reflection base pointing to an asset with untyped handle + pub fn new_asset_base( + handle: UntypedHandle, + asset_type_id: TypeId, + world: WorldGuard, + ) -> Result { + // We need to get the Assets resource ComponentId by type registry lookup + let type_registry = world.type_registry(); + let type_registry = type_registry.read(); + + // Get the ReflectAsset data to find the Assets resource type ID + let reflect_asset: &ReflectAsset = + type_registry.get_type_data(asset_type_id).ok_or_else(|| { + InteropError::unsupported_operation( + Some(asset_type_id), + None, + "Asset type is not registered with ReflectAsset type data", + ) + })?; + + let assets_resource_type_id = reflect_asset.assets_resource_type_id(); + + // Convert the TypeId to ComponentId via unsafe world cell + let world_cell = world.as_unsafe_world_cell()?; + let components = world_cell.components(); + let assets_resource_id = components + .get_resource_id(assets_resource_type_id) + .ok_or_else(|| { + InteropError::unsupported_operation( + Some(assets_resource_type_id), + None, + "Assets resource is not registered in the world", + ) + })?; + + Ok(Self { + type_id: asset_type_id, + base_id: ReflectBase::Asset(handle, assets_resource_id), + }) + } } /// The Id of the kind of reflection base being pointed to @@ -599,8 +732,10 @@ pub enum ReflectBase { Component(Entity, ComponentId), /// A resource Resource(ComponentId), - /// an allocation + /// An allocation Owned(ReflectAllocationId), + /// An asset accessed by handle + Asset(UntypedHandle, ComponentId), } impl DisplayWithTypeInfo for ReflectBase { @@ -655,6 +790,13 @@ impl DisplayWithTypeInfo for ReflectBase { WithTypeInfo::new_with_opt_info(id, type_info_provider) .display_with_type_info(f, type_info_provider) } + ReflectBase::Asset(handle, assets_resource_id) => { + f.write_str("asset with handle: ")?; + write!(f, "{:?}", handle)?; + f.write_str(", in Assets resource: ")?; + WithTypeInfo::new_with_opt_info(assets_resource_id, type_info_provider) + .display_with_type_info(f, type_info_provider) + } } } } diff --git a/crates/bevy_mod_scripting_display/Cargo.toml b/crates/bevy_mod_scripting_display/Cargo.toml index 14c2cc29c6..5971727727 100644 --- a/crates/bevy_mod_scripting_display/Cargo.toml +++ b/crates/bevy_mod_scripting_display/Cargo.toml @@ -13,6 +13,7 @@ readme.workspace = true [dependencies] bevy_reflect = { workspace = true } +bevy_asset = { workspace = true } bevy_ecs = { workspace = true, features = ["bevy_reflect"] } bevy_platform = { workspace = true } parking_lot = { workspace = true } diff --git a/crates/bevy_mod_scripting_display/src/impls/bevy_asset.rs b/crates/bevy_mod_scripting_display/src/impls/bevy_asset.rs new file mode 100644 index 0000000000..2ffd4758f3 --- /dev/null +++ b/crates/bevy_mod_scripting_display/src/impls/bevy_asset.rs @@ -0,0 +1,9 @@ +impl crate::DebugWithTypeInfo for bevy_asset::UntypedHandle { + fn to_string_with_type_info( + &self, + f: &mut std::fmt::Formatter<'_>, + _type_info_provider: Option<&dyn crate::GetTypeInfo>, + ) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} diff --git a/crates/bevy_mod_scripting_display/src/impls/mod.rs b/crates/bevy_mod_scripting_display/src/impls/mod.rs index 6a22bf2bb2..ea627420e3 100644 --- a/crates/bevy_mod_scripting_display/src/impls/mod.rs +++ b/crates/bevy_mod_scripting_display/src/impls/mod.rs @@ -1,3 +1,4 @@ +mod bevy_asset; mod bevy_ecs; mod bevy_platform; mod bevy_reflect; diff --git a/crates/bevy_mod_scripting_functions/src/core.rs b/crates/bevy_mod_scripting_functions/src/core.rs index 8fc5c8bdaa..989bb1024c 100644 --- a/crates/bevy_mod_scripting_functions/src/core.rs +++ b/crates/bevy_mod_scripting_functions/src/core.rs @@ -512,6 +512,33 @@ impl World { let world = ctxt.world()?; world.register_script_component(name).map(Val) } + + /// Retrieves an asset by its handle and asset type registration. + /// + /// Arguments: + /// * `ctxt`: The function call context. + /// * `handle_reference`: The handle to the asset (as a reflect reference). + /// * `registration`: The type registration of the asset type. + /// Returns: + /// * `asset`: The asset reference, if the asset is loaded. + fn get_asset( + ctxt: FunctionCallContext, + handle_reference: ReflectReference, + registration: Val, + ) -> Result, InteropError> { + profiling::function_scope!("get_asset"); + let untyped_handle = handle_reference.try_untyped_asset_handle(ctxt.world()?)?; + Ok(Some(ReflectReference::new_asset_ref(untyped_handle, registration.type_id(), ctxt.world()?)?)) + } + + /// Checks if can get asset handle + fn has_asset( + ctxt: FunctionCallContext, + handle_reference: ReflectReference, + ) -> Result { + profiling::function_scope!("has_asset"); + Ok(handle_reference.try_untyped_asset_handle(ctxt.world()?).is_ok()) + } } #[script_bindings( diff --git a/crates/testing_crates/script_integration_test_harness/src/test_functions.rs b/crates/testing_crates/script_integration_test_harness/src/test_functions.rs index 5c4ed1c694..a152cec7ad 100644 --- a/crates/testing_crates/script_integration_test_harness/src/test_functions.rs +++ b/crates/testing_crates/script_integration_test_harness/src/test_functions.rs @@ -5,7 +5,8 @@ use std::{ use ::{ bevy_app::App, - bevy_ecs::{component::ComponentId, entity::Entity, world::World}, + bevy_asset::Assets, + bevy_ecs::{change_detection::Mut, component::ComponentId, entity::Entity, world::World}, bevy_reflect::{Reflect, TypeRegistration}, }; use bevy_mod_scripting_asset::Language; @@ -20,7 +21,7 @@ use bevy_mod_scripting_bindings::{ }; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha12Rng; -use test_utils::test_data::EnumerateTestComponents; +use test_utils::test_data::{EnumerateTestComponents, TestAsset}; // lazy lock rng state pub static RNG: std::sync::LazyLock> = std::sync::LazyLock::new(|| { @@ -146,5 +147,28 @@ pub fn register_test_functions(world: &mut App) { reason.unwrap_or_default() ) }, + ) + .register( + "create_test_asset", + |s: FunctionCallContext, value: i32, name: String| { + let world = s.world()?; + let test_asset = TestAsset::new(value, name); + + let handle = world.with_resource_mut(|mut assets: Mut>| { + assets.add(test_asset) + })?; + let type_registry = world.type_registry(); + let type_registry = type_registry.read(); + let registration = type_registry.get(std::any::TypeId::of::()) + .ok_or_else(|| InteropError::str("TestAsset type not registered"))?; + let reg = ScriptTypeRegistration::new(Arc::new(registration.clone())); + + let allocator = world.allocator(); + let mut allocator = allocator.write(); + Ok(vec![ + ReflectReference::new_allocated(handle, &mut allocator), + ReflectReference::new_allocated(reg, &mut allocator) + ]) + }, ); } diff --git a/crates/testing_crates/test_utils/src/test_data.rs b/crates/testing_crates/test_utils/src/test_data.rs index 5cadbaf63f..2f3a918b49 100644 --- a/crates/testing_crates/test_utils/src/test_data.rs +++ b/crates/testing_crates/test_utils/src/test_data.rs @@ -1,12 +1,13 @@ use std::{alloc::Layout, collections::HashMap}; + use bevy_app::{App, ScheduleRunnerPlugin, TaskPoolPlugin}; use bevy_diagnostic::FrameCountPlugin; use bevy_log::LogPlugin; use bevy_time::TimePlugin; use ::{ - bevy_asset::AssetPlugin, + bevy_asset::{Asset, AssetApp, AssetPlugin}, bevy_diagnostic::DiagnosticsPlugin, bevy_ecs::{component::*, prelude::*, world::World}, bevy_reflect::{prelude::*, *}, @@ -27,6 +28,18 @@ impl TestComponent { } } +#[derive(Asset, Reflect, PartialEq, Debug, Clone)] +pub struct TestAsset { + pub value: i32, + pub name: String, +} + +impl TestAsset { + pub fn new(value: i32, name: String) -> Self { + Self { value, name } + } +} + #[derive(Component, Reflect, PartialEq, Eq, Debug, Default)] #[reflect(Component)] pub struct GenericComponent { @@ -361,6 +374,10 @@ pub fn setup_integration_test(init: F) ..Default::default() }, )); + + app.init_asset::(); + app.register_asset_reflect::(); + app } From 73eaa2a10e9714f3e3cc5b68f93dfd9d3a8e43a3 Mon Sep 17 00:00:00 2001 From: aggyomfg Date: Mon, 29 Sep 2025 10:35:31 +0400 Subject: [PATCH 02/10] simplify tests --- assets/tests/asset_operations/asset_operations.lua | 10 ++-------- assets/tests/asset_operations/asset_operations.rhai | 10 ++-------- .../src/test_functions.rs | 12 +----------- 3 files changed, 5 insertions(+), 27 deletions(-) diff --git a/assets/tests/asset_operations/asset_operations.lua b/assets/tests/asset_operations/asset_operations.lua index 77f56e2089..d77fe4a05b 100644 --- a/assets/tests/asset_operations/asset_operations.lua +++ b/assets/tests/asset_operations/asset_operations.lua @@ -1,16 +1,10 @@ function on_test() - local asset_data = create_test_asset(42, "TestAssetName") - local test_handle = asset_data[1] - local asset_type_reg = asset_data[2] + local test_handle = create_test_asset(42, "TestAssetName") assert(test_handle ~= nil, "Test asset handle should not be nil") - assert(asset_type_reg ~= nil, "TestAsset type registration should exist") - - -- Check asset exists and retrieve it assert(world.has_asset(test_handle) == true, "has_asset should return true") - local retrieved_asset = world.get_asset(test_handle, asset_type_reg) - assert(retrieved_asset ~= nil, "Should be able to retrieve the test asset") + local retrieved_asset = world.get_asset(test_handle, types.TestAsset) assert(retrieved_asset.value == 42, "Asset value should be 42") assert(retrieved_asset.name == "TestAssetName", "Asset name should be 'TestAssetName'") end diff --git a/assets/tests/asset_operations/asset_operations.rhai b/assets/tests/asset_operations/asset_operations.rhai index d88421208b..6a82caac84 100644 --- a/assets/tests/asset_operations/asset_operations.rhai +++ b/assets/tests/asset_operations/asset_operations.rhai @@ -1,16 +1,10 @@ fn on_test() { - let asset_data = create_test_asset(42, "TestAssetName"); - let test_handle = asset_data[0]; - let asset_type_reg = asset_data[1]; + let test_handle = create_test_asset(42, "TestAssetName"); assert(test_handle != (), "Test asset handle should not be nil"); - assert(asset_type_reg != (), "TestAsset type registration should exist"); - - // Check asset exists and retrieve it assert(world.has_asset.call(test_handle) == true, "has_asset should return true"); - let retrieved_asset = world.get_asset.call(test_handle, asset_type_reg); - assert(retrieved_asset != (), "Should be able to retrieve the test asset"); + let retrieved_asset = world.get_asset.call(test_handle, types.TestAsset); assert(retrieved_asset.value == 42, "Asset value should be 42"); assert(retrieved_asset.name == "TestAssetName", "Asset name should be 'TestAssetName'"); } \ No newline at end of file diff --git a/crates/testing_crates/script_integration_test_harness/src/test_functions.rs b/crates/testing_crates/script_integration_test_harness/src/test_functions.rs index a152cec7ad..1070b3eaba 100644 --- a/crates/testing_crates/script_integration_test_harness/src/test_functions.rs +++ b/crates/testing_crates/script_integration_test_harness/src/test_functions.rs @@ -153,22 +153,12 @@ pub fn register_test_functions(world: &mut App) { |s: FunctionCallContext, value: i32, name: String| { let world = s.world()?; let test_asset = TestAsset::new(value, name); - let handle = world.with_resource_mut(|mut assets: Mut>| { assets.add(test_asset) })?; - let type_registry = world.type_registry(); - let type_registry = type_registry.read(); - let registration = type_registry.get(std::any::TypeId::of::()) - .ok_or_else(|| InteropError::str("TestAsset type not registered"))?; - let reg = ScriptTypeRegistration::new(Arc::new(registration.clone())); - let allocator = world.allocator(); let mut allocator = allocator.write(); - Ok(vec![ - ReflectReference::new_allocated(handle, &mut allocator), - ReflectReference::new_allocated(reg, &mut allocator) - ]) + Ok(ReflectReference::new_allocated(handle, &mut allocator)) }, ); } From 3e33498ca27992847d203c074c3b815ebbae3f85 Mon Sep 17 00:00:00 2001 From: aggyomfg Date: Mon, 29 Sep 2025 12:06:22 +0400 Subject: [PATCH 03/10] better error handling and safety comment --- .../src/reference.rs | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/crates/bevy_mod_scripting_bindings/src/reference.rs b/crates/bevy_mod_scripting_bindings/src/reference.rs index 3bfc8f34fc..d8707397cc 100644 --- a/crates/bevy_mod_scripting_bindings/src/reference.rs +++ b/crates/bevy_mod_scripting_bindings/src/reference.rs @@ -283,9 +283,9 @@ impl ReflectReference { .ok_or_else(|| { InteropError::missing_type_data( handle_type_id, - "ReflectHandle".to_string(), + stringify!(ReflectHandle).into(), ) - .with_context("Handle type is not registered for asset operations - ensure the asset type is properly registered with ReflectHandle type data") + .with_context("Handle type is not registered for asset operations - ensure that you registered it with bevy::App::register_asset_reflect::()") })?; let untyped_handle = self.with_reflect(world.clone(), |reflect| { @@ -300,14 +300,42 @@ impl ReflectReference { reflect_handle .downcast_handle_untyped(reflect_any.as_any()) .ok_or_else(|| { - InteropError::invariant("Failed to get UntypedHandle") - .with_context("Handle downcast failed - handle may be of wrong type or corrupted") + InteropError::could_not_downcast(self.clone(), handle_type_id) + .with_context("UntypedHandle downcast failed - handle may be of wrong type or corrupted") }) })??; - Ok(untyped_handle) } + /// Get asset from world and return a mutable reference to it + unsafe fn load_asset_mut<'w>( + &self, + handle: &UntypedHandle, + world: WorldGuard<'w>, + ) -> Result<&'w mut dyn Reflect, InteropError> { + let type_registry = world.type_registry(); + let type_registry = type_registry.read(); + + let reflect_asset: &ReflectAsset = type_registry + .get_type_data(self.base.type_id) + .ok_or_else(|| InteropError::unregistered_base(self.base.clone()))?; + + let world_cell = world.as_unsafe_world_cell()?; + // Safety: The caller guarantees exclusive access to the asset through the WorldGuard, + // and we've validated that the type_id matches the ReflectAsset type data. + // The UnsafeWorldCell is valid for the lifetime 'w of the WorldGuard. + let asset = unsafe { reflect_asset.get_unchecked_mut(world_cell, handle.clone()) } + .ok_or_else(|| { + InteropError::unsupported_operation( + Some(self.base.type_id), + None, + "Asset not loaded or handle is invalid", + ) + })?; + + Ok(asset) + } + /// Indexes into the reflect path inside this reference. /// You can use [`Self::reflect`] and [`Self::reflect_mut`] to get the actual value. pub fn index_path>(&mut self, index: T) { @@ -552,32 +580,6 @@ impl ReflectReference { self.walk_path_mut(base.as_partial_reflect_mut()) } - /// Get asset from world and return a mutable reference to it - unsafe fn load_asset_mut<'w>( - &self, - handle: &UntypedHandle, - world: WorldGuard<'w>, - ) -> Result<&'w mut dyn Reflect, InteropError> { - let type_registry = world.type_registry(); - let type_registry = type_registry.read(); - - let reflect_asset: &ReflectAsset = type_registry - .get_type_data(self.base.type_id) - .ok_or_else(|| InteropError::unregistered_base(self.base.clone()))?; - - let world_cell = world.as_unsafe_world_cell()?; - let asset = unsafe { reflect_asset.get_unchecked_mut(world_cell, handle.clone()) } - .ok_or_else(|| { - InteropError::unsupported_operation( - Some(self.base.type_id), - None, - "Asset not loaded or handle is invalid", - ) - })?; - - Ok(asset) - } - fn walk_path<'a>( &self, root: &'a dyn PartialReflect, From ef823aac0ca1f947db86f82524b48206d7ff17db Mon Sep 17 00:00:00 2001 From: aggyomfg Date: Mon, 29 Sep 2025 21:23:05 +0400 Subject: [PATCH 04/10] docs + version bump --- Cargo.toml | 2 +- docs/src/SUMMARY.md | 1 + .../ScriptingReference/asset-operations.md | 41 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 docs/src/ScriptingReference/asset-operations.md diff --git a/Cargo.toml b/Cargo.toml index 6a9499c167..23fd4c22fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ categories.workspace = true readme.workspace = true [workspace.package] -version = "0.16.0" +version = "0.16.1" edition = "2024" authors = ["Maksymilian Mozolewski "] license = "MIT OR Apache-2.0" diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 81a4420c42..bceac67b13 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -30,6 +30,7 @@ - [Constructing Arbitrary Types](./ScriptingReference/constructing-arbitrary-types.md) - [Core Bindings](./ladfiles/bindings.lad.json) - [Core Callbacks](./ScriptingReference/core-callbacks.md) +- [Asset Operations](./ScriptingReference/asset-operations.md) # Developing BMS diff --git a/docs/src/ScriptingReference/asset-operations.md b/docs/src/ScriptingReference/asset-operations.md new file mode 100644 index 0000000000..075965ace7 --- /dev/null +++ b/docs/src/ScriptingReference/asset-operations.md @@ -0,0 +1,41 @@ +# Asset Operations + +BMS provides built-in support for working with Bevy assets from scripts. You can check for asset existence, retrieve assets by handle, and manipulate asset data through the reflection system. + +## Prerequisites + +To use asset operations in your scripts, you need to ensure that: + +1. Your asset type implements `Asset` and `Reflect` +2. The asset type is registered with `app.register_asset_reflect::()` +3. The asset handle type has `ReflectHandle` type data registered + +## Available Functions + +### `world.has_asset(handle)` + +Checks if an asset exists and is loaded for the given handle. + +**Parameters:** + +- `handle`: A ReflectReference to an existing asset handle + +**Returns:** + +- `boolean`: `true` if the asset exists and is loaded, `false` otherwise + +### `world.get_asset(handle, asset_type)` + +Retrieves a loaded asset by its handle and returns a reflected reference to it. + +**Parameters:** + +- `handle`: A reflected reference to an asset handle +- `asset_type`: The type registration of the asset (e.g., `types.Image`, `types.Mesh`) + +**Returns:** + +- `ReflectReference`: A reference to the asset data, or `nil`/`()` if not loaded + +Examples: +See usage examples in the `assets\tests\asset_operations` directory. From 4eca3ccd3ae170be0b0673ba2e29b7d653b969ea Mon Sep 17 00:00:00 2001 From: aggyomfg Date: Fri, 3 Oct 2025 19:52:21 +0400 Subject: [PATCH 05/10] review fixes --- Cargo.toml | 2 +- crates/bevy_mod_scripting_bindings/src/reference.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 23fd4c22fb..6a9499c167 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ categories.workspace = true readme.workspace = true [workspace.package] -version = "0.16.1" +version = "0.16.0" edition = "2024" authors = ["Maksymilian Mozolewski "] license = "MIT OR Apache-2.0" diff --git a/crates/bevy_mod_scripting_bindings/src/reference.rs b/crates/bevy_mod_scripting_bindings/src/reference.rs index d8707397cc..eef2171685 100644 --- a/crates/bevy_mod_scripting_bindings/src/reference.rs +++ b/crates/bevy_mod_scripting_bindings/src/reference.rs @@ -290,11 +290,11 @@ impl ReflectReference { let untyped_handle = self.with_reflect(world.clone(), |reflect| { let reflect_any = reflect.try_as_reflect().ok_or_else(|| { - InteropError::type_mismatch( - std::any::TypeId::of::(), + InteropError::unsupported_operation( Some(handle_type_id), + None, + "Asset handle must implement Reflect trait for asset operations", ) - .with_context("Handle must implement Reflect trait for asset operations") })?; reflect_handle From 2219b0e24c2b1e41b2b4e7996e85a6d80251f3df Mon Sep 17 00:00:00 2001 From: Maksymilian Mozolewski Date: Sat, 4 Oct 2025 16:00:01 +0100 Subject: [PATCH 06/10] clippy --- crates/bevy_mod_scripting_display/src/impls/bevy_asset.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_mod_scripting_display/src/impls/bevy_asset.rs b/crates/bevy_mod_scripting_display/src/impls/bevy_asset.rs index 2ffd4758f3..8f5c2bfe2c 100644 --- a/crates/bevy_mod_scripting_display/src/impls/bevy_asset.rs +++ b/crates/bevy_mod_scripting_display/src/impls/bevy_asset.rs @@ -4,6 +4,6 @@ impl crate::DebugWithTypeInfo for bevy_asset::UntypedHandle { f: &mut std::fmt::Formatter<'_>, _type_info_provider: Option<&dyn crate::GetTypeInfo>, ) -> std::fmt::Result { - write!(f, "{:?}", self) + write!(f, "{self:?}") } } From df06379f3dc709c907af2afe95490829b0a19d44 Mon Sep 17 00:00:00 2001 From: Maksymilian Mozolewski Date: Sat, 4 Oct 2025 16:18:16 +0100 Subject: [PATCH 07/10] clippy --- crates/bevy_mod_scripting_bindings/src/reference.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_mod_scripting_bindings/src/reference.rs b/crates/bevy_mod_scripting_bindings/src/reference.rs index eef2171685..6e568711dc 100644 --- a/crates/bevy_mod_scripting_bindings/src/reference.rs +++ b/crates/bevy_mod_scripting_bindings/src/reference.rs @@ -794,7 +794,7 @@ impl DisplayWithTypeInfo for ReflectBase { } ReflectBase::Asset(handle, assets_resource_id) => { f.write_str("asset with handle: ")?; - write!(f, "{:?}", handle)?; + write!(f, "{handle:?}")?; f.write_str(", in Assets resource: ")?; WithTypeInfo::new_with_opt_info(assets_resource_id, type_info_provider) .display_with_type_info(f, type_info_provider) From 8cbc774f654406063dbbe7f4e7e502850d2f3ccc Mon Sep 17 00:00:00 2001 From: makspll Date: Sat, 4 Oct 2025 16:44:30 +0100 Subject: [PATCH 08/10] fmt --- crates/bevy_mod_scripting_bindings/src/reference.rs | 7 ++++--- crates/bevy_mod_scripting_functions/src/core.rs | 10 ++++++++-- crates/testing_crates/test_utils/src/test_data.rs | 1 - 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/bevy_mod_scripting_bindings/src/reference.rs b/crates/bevy_mod_scripting_bindings/src/reference.rs index 6e568711dc..664cabcd43 100644 --- a/crates/bevy_mod_scripting_bindings/src/reference.rs +++ b/crates/bevy_mod_scripting_bindings/src/reference.rs @@ -275,7 +275,7 @@ impl ReflectReference { InteropError::invariant("Cannot determine handle type ID from reflection") .with_context("Asset handle reflection failed - handle may be invalid or corrupted") })?; - + let type_registry = world.type_registry(); let type_registry = type_registry.read(); let reflect_handle = type_registry @@ -300,8 +300,9 @@ impl ReflectReference { reflect_handle .downcast_handle_untyped(reflect_any.as_any()) .ok_or_else(|| { - InteropError::could_not_downcast(self.clone(), handle_type_id) - .with_context("UntypedHandle downcast failed - handle may be of wrong type or corrupted") + InteropError::could_not_downcast(self.clone(), handle_type_id).with_context( + "UntypedHandle downcast failed - handle may be of wrong type or corrupted", + ) }) })??; Ok(untyped_handle) diff --git a/crates/bevy_mod_scripting_functions/src/core.rs b/crates/bevy_mod_scripting_functions/src/core.rs index 989bb1024c..c3ffd8584a 100644 --- a/crates/bevy_mod_scripting_functions/src/core.rs +++ b/crates/bevy_mod_scripting_functions/src/core.rs @@ -528,7 +528,11 @@ impl World { ) -> Result, InteropError> { profiling::function_scope!("get_asset"); let untyped_handle = handle_reference.try_untyped_asset_handle(ctxt.world()?)?; - Ok(Some(ReflectReference::new_asset_ref(untyped_handle, registration.type_id(), ctxt.world()?)?)) + Ok(Some(ReflectReference::new_asset_ref( + untyped_handle, + registration.type_id(), + ctxt.world()?, + )?)) } /// Checks if can get asset handle @@ -537,7 +541,9 @@ impl World { handle_reference: ReflectReference, ) -> Result { profiling::function_scope!("has_asset"); - Ok(handle_reference.try_untyped_asset_handle(ctxt.world()?).is_ok()) + Ok(handle_reference + .try_untyped_asset_handle(ctxt.world()?) + .is_ok()) } } diff --git a/crates/testing_crates/test_utils/src/test_data.rs b/crates/testing_crates/test_utils/src/test_data.rs index 2f3a918b49..e25ce138be 100644 --- a/crates/testing_crates/test_utils/src/test_data.rs +++ b/crates/testing_crates/test_utils/src/test_data.rs @@ -1,6 +1,5 @@ use std::{alloc::Layout, collections::HashMap}; - use bevy_app::{App, ScheduleRunnerPlugin, TaskPoolPlugin}; use bevy_diagnostic::FrameCountPlugin; use bevy_log::LogPlugin; From 8aca92d724e29c2e62facb2ce0de727752eb15c4 Mon Sep 17 00:00:00 2001 From: makspll Date: Sat, 4 Oct 2025 17:43:23 +0100 Subject: [PATCH 09/10] pass system tests --- .../added_systems_run_in_parallel.lua | 82 ++++++++++--------- .../added_systems_run_in_parallel.rhai | 82 ++++++++++--------- 2 files changed, 84 insertions(+), 80 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 deecdf06ad..f5c129280c 100644 --- a/assets/tests/add_system/added_systems_run_in_parallel.lua +++ b/assets/tests/add_system/added_systems_run_in_parallel.lua @@ -25,51 +25,53 @@ digraph { node_0 [label="bevy_asset::assets::Assets::asset_events"]; node_1 [label="bevy_asset::assets::Assets::asset_events"]; 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="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="bevy_mod_scripting_core::pipeline::start::filter_script_attachments"]; - node_9 [label="bevy_mod_scripting_core::pipeline::start::filter_script_detachments"]; - node_10 [label="bevy_mod_scripting_core::pipeline::start::filter_script_modifications"]; - node_11 [label="bevy_mod_scripting_core::pipeline::start::process_attachments"]; - node_12 [label="bevy_mod_scripting_core::pipeline::start::process_detachments"]; - node_13 [label="bevy_mod_scripting_core::pipeline::start::process_asset_modifications"]; - node_14 [label="bevy_mod_scripting_core::pipeline::automatic_pipeline_runner"]; - node_15 [label="on_test_post_update"]; - node_16 [label="custom_system_a"]; - node_17 [label="custom_system_b"]; - node_18 [label="SystemSet AssetEvents"]; - node_19 [label="SystemSet GarbageCollection"]; - node_20 [label="SystemSet ListeningPhase"]; - node_21 [label="SystemSet MachineStartPhase"]; - node_22 [label="SystemSet ScriptSystem(custom_system_a)"]; - node_23 [label="SystemSet ScriptSystem(custom_system_b)"]; - node_0 -> node_18 [color=red, label="child of", arrowhead=diamond]; - node_1 -> node_18 [color=red, label="child of", arrowhead=diamond]; - node_2 -> node_18 [color=red, label="child of", arrowhead=diamond]; - node_3 -> node_18 [color=red, label="child of", arrowhead=diamond]; + node_3 [label="bevy_asset::assets::Assets::asset_events"]; + node_4 [label="bevy_asset::assets::Assets::asset_events"]; + node_5 [label="bevy_mod_scripting_bindings::allocator::garbage_collector"]; + node_6 [label="bevy_mod_scripting_core::handler::script_error_logger"]; + node_7 [label="script_integration_test_harness::dummy_before_post_update_system"]; + node_8 [label="script_integration_test_harness::dummy_post_update_system"]; + node_9 [label="bevy_mod_scripting_core::pipeline::start::filter_script_attachments"]; + node_10 [label="bevy_mod_scripting_core::pipeline::start::filter_script_detachments"]; + node_11 [label="bevy_mod_scripting_core::pipeline::start::filter_script_modifications"]; + node_12 [label="bevy_mod_scripting_core::pipeline::start::process_attachments"]; + node_13 [label="bevy_mod_scripting_core::pipeline::start::process_detachments"]; + node_14 [label="bevy_mod_scripting_core::pipeline::start::process_asset_modifications"]; + node_15 [label="bevy_mod_scripting_core::pipeline::automatic_pipeline_runner"]; + node_16 [label="on_test_post_update"]; + node_17 [label="custom_system_a"]; + node_18 [label="custom_system_b"]; + node_19 [label="SystemSet AssetEvents"]; + node_20 [label="SystemSet GarbageCollection"]; + node_21 [label="SystemSet ListeningPhase"]; + node_22 [label="SystemSet MachineStartPhase"]; + node_23 [label="SystemSet ScriptSystem(custom_system_a)"]; + node_24 [label="SystemSet ScriptSystem(custom_system_b)"]; + node_0 -> node_19 [color=red, label="child of", arrowhead=diamond]; + node_1 -> node_19 [color=red, label="child of", arrowhead=diamond]; + node_2 -> node_19 [color=red, label="child of", arrowhead=diamond]; + node_3 -> node_19 [color=red, label="child of", arrowhead=diamond]; node_4 -> node_19 [color=red, label="child of", arrowhead=diamond]; - node_8 -> node_20 [color=red, label="child of", arrowhead=diamond]; - node_9 -> node_20 [color=red, label="child of", arrowhead=diamond]; - node_10 -> node_20 [color=red, label="child of", arrowhead=diamond]; + node_5 -> node_20 [color=red, label="child of", arrowhead=diamond]; + node_9 -> node_21 [color=red, label="child of", arrowhead=diamond]; + node_10 -> node_21 [color=red, label="child of", arrowhead=diamond]; node_11 -> node_21 [color=red, label="child of", arrowhead=diamond]; - node_12 -> node_21 [color=red, label="child of", arrowhead=diamond]; - node_13 -> node_21 [color=red, label="child of", arrowhead=diamond]; - node_16 -> node_22 [color=red, label="child of", arrowhead=diamond]; + node_12 -> node_22 [color=red, label="child of", arrowhead=diamond]; + node_13 -> node_22 [color=red, label="child of", arrowhead=diamond]; + node_14 -> node_22 [color=red, label="child of", arrowhead=diamond]; node_17 -> node_23 [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_9 [color=blue, label="runs before", arrowhead=normal]; - node_8 -> node_10 [color=blue, label="runs before", arrowhead=normal]; + node_18 -> node_24 [color=red, label="child of", arrowhead=diamond]; + node_7 -> node_8 [color=blue, label="runs before", arrowhead=normal]; node_9 -> node_10 [color=blue, label="runs before", arrowhead=normal]; - node_11 -> node_12 [color=blue, label="runs before", arrowhead=normal]; + node_9 -> node_10 [color=blue, label="runs before", arrowhead=normal]; + node_9 -> node_11 [color=blue, label="runs before", arrowhead=normal]; + node_10 -> node_11 [color=blue, label="runs before", arrowhead=normal]; node_12 -> node_13 [color=blue, label="runs before", arrowhead=normal]; - node_15 -> node_16 [color=blue, label="runs before", arrowhead=normal]; - node_15 -> node_17 [color=blue, label="runs before", arrowhead=normal]; - node_20 -> node_21 [color=blue, label="runs before", arrowhead=normal]; - node_21 -> node_14 [color=blue, label="runs before", arrowhead=normal]; + node_13 -> node_14 [color=blue, label="runs before", arrowhead=normal]; + node_16 -> node_17 [color=blue, label="runs before", arrowhead=normal]; + node_16 -> node_18 [color=blue, label="runs before", arrowhead=normal]; + node_21 -> node_22 [color=blue, label="runs before", arrowhead=normal]; + node_22 -> node_15 [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 ef83796407..81fed86de6 100644 --- a/assets/tests/add_system/added_systems_run_in_parallel.rhai +++ b/assets/tests/add_system/added_systems_run_in_parallel.rhai @@ -24,51 +24,53 @@ digraph { node_0 [label="bevy_asset::assets::Assets::asset_events"]; node_1 [label="bevy_asset::assets::Assets::asset_events"]; 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="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="bevy_mod_scripting_core::pipeline::start::filter_script_attachments"]; - node_9 [label="bevy_mod_scripting_core::pipeline::start::filter_script_detachments"]; - node_10 [label="bevy_mod_scripting_core::pipeline::start::filter_script_modifications"]; - node_11 [label="bevy_mod_scripting_core::pipeline::start::process_attachments"]; - node_12 [label="bevy_mod_scripting_core::pipeline::start::process_detachments"]; - node_13 [label="bevy_mod_scripting_core::pipeline::start::process_asset_modifications"]; - node_14 [label="bevy_mod_scripting_core::pipeline::automatic_pipeline_runner"]; - node_15 [label="on_test_post_update"]; - node_16 [label="custom_system_a"]; - node_17 [label="custom_system_b"]; - node_18 [label="SystemSet AssetEvents"]; - node_19 [label="SystemSet GarbageCollection"]; - node_20 [label="SystemSet ListeningPhase"]; - node_21 [label="SystemSet MachineStartPhase"]; - node_22 [label="SystemSet ScriptSystem(custom_system_a)"]; - node_23 [label="SystemSet ScriptSystem(custom_system_b)"]; - node_0 -> node_18 [color=red, label="child of", arrowhead=diamond]; - node_1 -> node_18 [color=red, label="child of", arrowhead=diamond]; - node_2 -> node_18 [color=red, label="child of", arrowhead=diamond]; - node_3 -> node_18 [color=red, label="child of", arrowhead=diamond]; + node_3 [label="bevy_asset::assets::Assets::asset_events"]; + node_4 [label="bevy_asset::assets::Assets::asset_events"]; + node_5 [label="bevy_mod_scripting_bindings::allocator::garbage_collector"]; + node_6 [label="bevy_mod_scripting_core::handler::script_error_logger"]; + node_7 [label="script_integration_test_harness::dummy_before_post_update_system"]; + node_8 [label="script_integration_test_harness::dummy_post_update_system"]; + node_9 [label="bevy_mod_scripting_core::pipeline::start::filter_script_attachments"]; + node_10 [label="bevy_mod_scripting_core::pipeline::start::filter_script_detachments"]; + node_11 [label="bevy_mod_scripting_core::pipeline::start::filter_script_modifications"]; + node_12 [label="bevy_mod_scripting_core::pipeline::start::process_attachments"]; + node_13 [label="bevy_mod_scripting_core::pipeline::start::process_detachments"]; + node_14 [label="bevy_mod_scripting_core::pipeline::start::process_asset_modifications"]; + node_15 [label="bevy_mod_scripting_core::pipeline::automatic_pipeline_runner"]; + node_16 [label="on_test_post_update"]; + node_17 [label="custom_system_a"]; + node_18 [label="custom_system_b"]; + node_19 [label="SystemSet AssetEvents"]; + node_20 [label="SystemSet GarbageCollection"]; + node_21 [label="SystemSet ListeningPhase"]; + node_22 [label="SystemSet MachineStartPhase"]; + node_23 [label="SystemSet ScriptSystem(custom_system_a)"]; + node_24 [label="SystemSet ScriptSystem(custom_system_b)"]; + node_0 -> node_19 [color=red, label="child of", arrowhead=diamond]; + node_1 -> node_19 [color=red, label="child of", arrowhead=diamond]; + node_2 -> node_19 [color=red, label="child of", arrowhead=diamond]; + node_3 -> node_19 [color=red, label="child of", arrowhead=diamond]; node_4 -> node_19 [color=red, label="child of", arrowhead=diamond]; - node_8 -> node_20 [color=red, label="child of", arrowhead=diamond]; - node_9 -> node_20 [color=red, label="child of", arrowhead=diamond]; - node_10 -> node_20 [color=red, label="child of", arrowhead=diamond]; + node_5 -> node_20 [color=red, label="child of", arrowhead=diamond]; + node_9 -> node_21 [color=red, label="child of", arrowhead=diamond]; + node_10 -> node_21 [color=red, label="child of", arrowhead=diamond]; node_11 -> node_21 [color=red, label="child of", arrowhead=diamond]; - node_12 -> node_21 [color=red, label="child of", arrowhead=diamond]; - node_13 -> node_21 [color=red, label="child of", arrowhead=diamond]; - node_16 -> node_22 [color=red, label="child of", arrowhead=diamond]; + node_12 -> node_22 [color=red, label="child of", arrowhead=diamond]; + node_13 -> node_22 [color=red, label="child of", arrowhead=diamond]; + node_14 -> node_22 [color=red, label="child of", arrowhead=diamond]; node_17 -> node_23 [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_9 [color=blue, label="runs before", arrowhead=normal]; - node_8 -> node_10 [color=blue, label="runs before", arrowhead=normal]; + node_18 -> node_24 [color=red, label="child of", arrowhead=diamond]; + node_7 -> node_8 [color=blue, label="runs before", arrowhead=normal]; node_9 -> node_10 [color=blue, label="runs before", arrowhead=normal]; - node_11 -> node_12 [color=blue, label="runs before", arrowhead=normal]; + node_9 -> node_10 [color=blue, label="runs before", arrowhead=normal]; + node_9 -> node_11 [color=blue, label="runs before", arrowhead=normal]; + node_10 -> node_11 [color=blue, label="runs before", arrowhead=normal]; node_12 -> node_13 [color=blue, label="runs before", arrowhead=normal]; - node_15 -> node_16 [color=blue, label="runs before", arrowhead=normal]; - node_15 -> node_17 [color=blue, label="runs before", arrowhead=normal]; - node_20 -> node_21 [color=blue, label="runs before", arrowhead=normal]; - node_21 -> node_14 [color=blue, label="runs before", arrowhead=normal]; + node_13 -> node_14 [color=blue, label="runs before", arrowhead=normal]; + node_16 -> node_17 [color=blue, label="runs before", arrowhead=normal]; + node_16 -> node_18 [color=blue, label="runs before", arrowhead=normal]; + node_21 -> node_22 [color=blue, label="runs before", arrowhead=normal]; + node_22 -> node_15 [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"); From df68dad9a7f83539c323c10663e125fa74040a87 Mon Sep 17 00:00:00 2001 From: makspll Date: Sun, 5 Oct 2025 09:09:46 +0100 Subject: [PATCH 10/10] pass rhai test --- .../add_system/added_systems_run_in_parallel.rhai | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 81fed86de6..b87e5e75a9 100644 --- a/assets/tests/add_system/added_systems_run_in_parallel.rhai +++ b/assets/tests/add_system/added_systems_run_in_parallel.rhai @@ -30,13 +30,13 @@ digraph { node_6 [label="bevy_mod_scripting_core::handler::script_error_logger"]; node_7 [label="script_integration_test_harness::dummy_before_post_update_system"]; node_8 [label="script_integration_test_harness::dummy_post_update_system"]; - node_9 [label="bevy_mod_scripting_core::pipeline::start::filter_script_attachments"]; - node_10 [label="bevy_mod_scripting_core::pipeline::start::filter_script_detachments"]; - node_11 [label="bevy_mod_scripting_core::pipeline::start::filter_script_modifications"]; - node_12 [label="bevy_mod_scripting_core::pipeline::start::process_attachments"]; - node_13 [label="bevy_mod_scripting_core::pipeline::start::process_detachments"]; - node_14 [label="bevy_mod_scripting_core::pipeline::start::process_asset_modifications"]; - node_15 [label="bevy_mod_scripting_core::pipeline::automatic_pipeline_runner"]; + node_9 [label="bevy_mod_scripting_core::pipeline::start::filter_script_attachments"]; + node_10 [label="bevy_mod_scripting_core::pipeline::start::filter_script_detachments"]; + node_11 [label="bevy_mod_scripting_core::pipeline::start::filter_script_modifications"]; + node_12 [label="bevy_mod_scripting_core::pipeline::start::process_attachments"]; + node_13 [label="bevy_mod_scripting_core::pipeline::start::process_detachments"]; + node_14 [label="bevy_mod_scripting_core::pipeline::start::process_asset_modifications"]; + node_15 [label="bevy_mod_scripting_core::pipeline::automatic_pipeline_runner"]; node_16 [label="on_test_post_update"]; node_17 [label="custom_system_a"]; node_18 [label="custom_system_b"];