From d64387908d357d35336b36196fe5ae52b9d30961 Mon Sep 17 00:00:00 2001 From: makspll Date: Sat, 25 Oct 2025 13:12:10 +0100 Subject: [PATCH 1/2] feat: add script asset path to function call context --- .../bevy_mod_scripting_bindings/src/error.rs | 2 +- .../src/function/script_function.rs | 16 +++++++++++--- .../src/pipeline/machines.rs | 8 ++++--- .../src/pipeline/mod.rs | 2 +- .../src/pipeline/start.rs | 22 +++++++++---------- .../src/bindings/script_value.rs | 12 +++++++--- .../bevy_mod_scripting_lua/src/lib.rs | 12 ++++++++++ .../src/scenario.rs | 8 +++++-- 8 files changed, 58 insertions(+), 24 deletions(-) diff --git a/crates/bevy_mod_scripting_bindings/src/error.rs b/crates/bevy_mod_scripting_bindings/src/error.rs index 254288cafd..7881857604 100644 --- a/crates/bevy_mod_scripting_bindings/src/error.rs +++ b/crates/bevy_mod_scripting_bindings/src/error.rs @@ -577,7 +577,7 @@ impl DisplayWithTypeInfo for InteropError { } => { write!( f, - "Error {} in function {} on {}: {}", + "Error {}\n in function {} on {}:\n {}", context .clone() .unwrap_or(FunctionCallContext::new(Language::Unknown)), diff --git a/crates/bevy_mod_scripting_bindings/src/function/script_function.rs b/crates/bevy_mod_scripting_bindings/src/function/script_function.rs index f642175e8f..e8860ea70c 100644 --- a/crates/bevy_mod_scripting_bindings/src/function/script_function.rs +++ b/crates/bevy_mod_scripting_bindings/src/function/script_function.rs @@ -53,10 +53,18 @@ impl std::fmt::Display for FunctionCallContext { f.write_str("in language: ")?; self.language.fmt(f)?; if let Some(context) = &self.location_context { + if let Some(script_name) = &context.script_name { + f.write_str(", in script: ")?; + script_name.fmt(f)?; + } + f.write_str(", at line: ")?; context.line.fmt(f)?; - f.write_str(", at column: ")?; - context.col.fmt(f)?; + + if let Some(col) = &context.col { + f.write_str(", at column: ")?; + col.fmt(f)?; + } } Ok(()) } @@ -66,10 +74,12 @@ impl std::fmt::Display for FunctionCallContext { #[debug_with_type_info(bms_display_path = "bevy_mod_scripting_display")] /// Describes a location within a script pub struct LocationContext { + /// The name of the script the function call originates from + pub script_name: Option, /// The line number pub line: u32, /// The column number - pub col: u32, + pub col: Option, } impl FunctionCallContext { diff --git a/crates/bevy_mod_scripting_core/src/pipeline/machines.rs b/crates/bevy_mod_scripting_core/src/pipeline/machines.rs index 1849daaa0b..2c777a2844 100644 --- a/crates/bevy_mod_scripting_core/src/pipeline/machines.rs +++ b/crates/bevy_mod_scripting_core/src/pipeline/machines.rs @@ -229,9 +229,10 @@ impl ScriptMachine

{ match &mut self.internal_state { MachineExecutionState::Initialized(machine_state) => { debug!( - "State '{}' entered. For script: {}", + "State '{}' entered. For script: {}, {:?}", machine_state.state_name(), - self.context.attachment + self.context.attachment, + self.context.attachment.script(), ); if let Some(listeners) = listeners.get(&machine_state.as_ref().type_id()) { @@ -517,7 +518,8 @@ impl MachineState

for ContextAssigned

{ let contexts = world.get_resource_or_init::>(); let mut contexts_guard = contexts.write(); - match contexts_guard.insert(attachment.clone(), self.context.clone()) { + // drop any strong handles + match contexts_guard.insert(attachment.clone().into_weak(), self.context.clone()) { Ok(_) => {} Err(_) => { drop(contexts_guard); diff --git a/crates/bevy_mod_scripting_core/src/pipeline/mod.rs b/crates/bevy_mod_scripting_core/src/pipeline/mod.rs index 6fb5a13ba5..1c8d4c4b6c 100644 --- a/crates/bevy_mod_scripting_core/src/pipeline/mod.rs +++ b/crates/bevy_mod_scripting_core/src/pipeline/mod.rs @@ -29,7 +29,7 @@ use crate::{ OnLoadedListener, OnReloadedListener, OnUnloadedForReloadListener, OnUnloadedForUnloadListener, }, - script::{ScriptContext, ScriptId}, + script::ScriptContext, }; mod hooks; diff --git a/crates/bevy_mod_scripting_core/src/pipeline/start.rs b/crates/bevy_mod_scripting_core/src/pipeline/start.rs index 3b90e69bfe..b5ecb2cb8a 100644 --- a/crates/bevy_mod_scripting_core/src/pipeline/start.rs +++ b/crates/bevy_mod_scripting_core/src/pipeline/start.rs @@ -38,12 +38,10 @@ impl StrongScriptHandle { handle: Handle, assets: &mut Assets, ) -> Option { - assets.get_strong_handle(handle.id()).map(Self) - } - - /// Upgrades an asset Id pointing to a script to a strong handle if the asset hasn't been dropped - pub fn upgrade(id: ScriptId, assets: &mut Assets) -> Option { - assets.get_strong_handle(id).map(Self) + match handle { + s @ Handle::Strong(_) => Some(Self(s)), + _ => assets.get_strong_handle(handle.id()).map(Self), + } } /// Returns a reference to the strong handle inside @@ -122,12 +120,14 @@ pub fn process_attachments( let contexts = contexts.read(); events.read().for_each(|wrapper| { let attachment_event = wrapper.event(); - let id = attachment_event.0.script().id(); - let context = Context { + let id = attachment_event.0.script(); + let mut context = Context { attachment: attachment_event.0.clone(), blackboard: Default::default(), }; - if let Some(strong_handle) = StrongScriptHandle::upgrade(id, &mut assets) { + if let Some(strong_handle) = StrongScriptHandle::from_assets(id, &mut assets) { + // we want the loading process to have access to asset paths, we will weaken the handle at the end. + *context.attachment.script_mut() = strong_handle.0.clone(); let content = strong_handle.get(&assets); if let Some(existing_context) = contexts.get_context(&attachment_event.0) { machines.queue_machine( @@ -193,8 +193,8 @@ pub fn process_asset_modifications( affected_attachments .into_iter() .for_each(|(attachment, existing_context)| { - let id = attachment.script().id(); - if let Some(strong_handle) = StrongScriptHandle::upgrade(id, &mut assets) { + let id = attachment.script(); + if let Some(strong_handle) = StrongScriptHandle::from_assets(id, &mut assets) { let content = strong_handle.get(&assets); machines.queue_machine( Context { diff --git a/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs b/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs index bd288cc538..10de09ff22 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs @@ -11,7 +11,7 @@ use bevy_mod_scripting_bindings::{ use bevy_platform::collections::HashMap; use mlua::{FromLua, IntoLua, Value, Variadic}; -use crate::IntoMluaError; +use crate::{IntoMluaError, LuaContextAppData}; use super::reference::LuaReflectReference; @@ -144,7 +144,10 @@ impl IntoLua for LuaScriptValue { .create_function(move |lua, args: Variadic| { let loc = lua.inspect_stack(1).map(|debug| LocationContext { line: debug.curr_line().try_into().unwrap_or_default(), - col: 0, + col: None, + script_name: lua.app_data_ref::().and_then(|v| { + v.last_loaded_script_name.as_ref().map(|n| n.to_string()) + }), }); let out = function .call( @@ -160,7 +163,10 @@ impl IntoLua for LuaScriptValue { .create_function(move |lua, args: Variadic| { let loc = lua.inspect_stack(0).map(|debug| LocationContext { line: debug.curr_line() as u32, - col: 0, + col: None, + script_name: lua.app_data_ref::().and_then(|v| { + v.last_loaded_script_name.as_ref().map(|n| n.to_string()) + }), }); let out = function .call( diff --git a/crates/languages/bevy_mod_scripting_lua/src/lib.rs b/crates/languages/bevy_mod_scripting_lua/src/lib.rs index 39d771f9f4..43090ee3ed 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/lib.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/lib.rs @@ -10,6 +10,7 @@ use ::{ bevy_ecs::{entity::Entity, world::World}, }; use bevy_app::App; +use bevy_asset::AssetPath; use bevy_ecs::world::{Mut, WorldId}; use bevy_log::trace; use bevy_mod_scripting_asset::{Language, ScriptAsset}; @@ -275,6 +276,13 @@ fn load_lua_content_into_context( Ok(()) } +/// App data which can be retrieved via [`mlua::Lua::app_data_ref`], containing some metadata about scripts present +#[derive(Default, Debug)] +pub struct LuaContextAppData { + /// the asset path of the script loaded last if this is a shared context, or the only script if it's not. + pub last_loaded_script_name: Option>, +} + #[profiling::function] /// Load a lua context from a script pub fn lua_context_load( @@ -287,6 +295,10 @@ pub fn lua_context_load( #[cfg(not(feature = "unsafe_lua_modules"))] let mut context = LuaContext(Lua::new()); + context.set_app_data(LuaContextAppData { + last_loaded_script_name: context_key.script().path().cloned(), + }); + load_lua_content_into_context(&mut context, context_key, content, world_id)?; Ok(context) } diff --git a/crates/testing_crates/script_integration_test_harness/src/scenario.rs b/crates/testing_crates/script_integration_test_harness/src/scenario.rs index 30a9435515..9523806819 100644 --- a/crates/testing_crates/script_integration_test_harness/src/scenario.rs +++ b/crates/testing_crates/script_integration_test_harness/src/scenario.rs @@ -32,6 +32,7 @@ use bevy_mod_scripting_core::{ handler::event_handler, script::{ContextPolicy, ScriptComponent, ScriptContext}, }; +use bevy_mod_scripting_display::DisplayProxy; use bevy_mod_scripting_script::ScriptAttachment; use test_utils::test_data::setup_integration_test; @@ -593,7 +594,7 @@ impl ScenarioStep { return Err(anyhow!("Failed to load script: {e}")); } - info!("Script '{}' loaded successfully", script.id()); + info!("Script '{}' loaded successfully", script.display()); } ScenarioStep::SetupHandler { schedule, label } => { match label.to_string().as_str() { @@ -629,7 +630,10 @@ impl ScenarioStep { .id(); context.entities.insert(name.to_string(), entity); - info!("Spawned entity '{entity}' with script '{}'", script.id()); + info!( + "Spawned entity '{entity}' with script '{}'", + script.display() + ); } ScenarioStep::EmitScriptCallbackEvent { event } => { app.world_mut().send_event(event.clone()); From 17831e5df9b685741ef0370c6785da1ea5676563 Mon Sep 17 00:00:00 2001 From: makspll Date: Sat, 25 Oct 2025 14:16:04 +0100 Subject: [PATCH 2/2] add some tracing to context creation --- .../src/bindings/script_value.rs | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs b/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs index 10de09ff22..98870b9603 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs @@ -142,13 +142,16 @@ impl IntoLua for LuaScriptValue { ScriptValue::Error(script_error) => return Err(mlua::Error::external(script_error)), ScriptValue::Function(function) => lua .create_function(move |lua, args: Variadic| { - let loc = lua.inspect_stack(1).map(|debug| LocationContext { - line: debug.curr_line().try_into().unwrap_or_default(), - col: None, - script_name: lua.app_data_ref::().and_then(|v| { - v.last_loaded_script_name.as_ref().map(|n| n.to_string()) - }), - }); + let loc = { + profiling::scope!("function call context"); + lua.inspect_stack(1).map(|debug| LocationContext { + line: debug.curr_line().try_into().unwrap_or_default(), + col: None, + script_name: lua.app_data_ref::().and_then(|v| { + v.last_loaded_script_name.as_ref().map(|n| n.to_string()) + }), + }) + }; let out = function .call( args.into_iter().map(Into::into), @@ -161,13 +164,16 @@ impl IntoLua for LuaScriptValue { .into_lua(lua)?, ScriptValue::FunctionMut(function) => lua .create_function(move |lua, args: Variadic| { - let loc = lua.inspect_stack(0).map(|debug| LocationContext { - line: debug.curr_line() as u32, - col: None, - script_name: lua.app_data_ref::().and_then(|v| { - v.last_loaded_script_name.as_ref().map(|n| n.to_string()) - }), - }); + let loc = { + profiling::scope!("function call context"); + lua.inspect_stack(1).map(|debug| LocationContext { + line: debug.curr_line().try_into().unwrap_or_default(), + col: None, + script_name: lua.app_data_ref::().and_then(|v| { + v.last_loaded_script_name.as_ref().map(|n| n.to_string()) + }), + }) + }; let out = function .call( args.into_iter().map(Into::into),