From 2a79bf80d99199d5385c699ff418c10b9e792c66 Mon Sep 17 00:00:00 2001 From: makspll Date: Sat, 30 Aug 2025 10:23:33 +0100 Subject: [PATCH 1/5] refactor: reduce property passing, pass around WorldId instead --- .../src/bindings/script_system.rs | 3 +- crates/bevy_mod_scripting_core/src/context.rs | 27 +++------- .../bevy_mod_scripting_core/src/extractors.rs | 5 +- crates/bevy_mod_scripting_core/src/handler.rs | 17 ++----- .../bevy_mod_scripting_lua/src/lib.rs | 50 ++++++------------- .../bevy_mod_scripting_rhai/src/lib.rs | 45 ++++++----------- .../test_utils/src/test_plugin.rs | 13 ++--- 7 files changed, 48 insertions(+), 112 deletions(-) diff --git a/crates/bevy_mod_scripting_core/src/bindings/script_system.rs b/crates/bevy_mod_scripting_core/src/bindings/script_system.rs index e6cd8390a6..e54fca8503 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/script_system.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/script_system.rs @@ -247,11 +247,10 @@ impl<'w, P: IntoScriptPluginParams> DynamicHandlerContext<'w, P> { }; // call the script - let runtime = P::readonly_configuration(guard.id()).runtime; let mut context = context.lock(); - P::handle(payload, context_key, label, &mut context, runtime, guard) + P::handle(payload, context_key, label, &mut context, guard) } } diff --git a/crates/bevy_mod_scripting_core/src/context.rs b/crates/bevy_mod_scripting_core/src/context.rs index 2a50e29374..b9b65b9131 100644 --- a/crates/bevy_mod_scripting_core/src/context.rs +++ b/crates/bevy_mod_scripting_core/src/context.rs @@ -1,5 +1,7 @@ //! Traits and types for managing script contexts. +use bevy_ecs::world::WorldId; + use crate::{ IntoScriptPluginParams, bindings::{ThreadWorldContainer, WorldContainer, WorldGuard}, @@ -26,9 +28,7 @@ pub type ContextPreHandlingInitializer

= pub type ContextLoadFn

= fn( attachment: &ScriptAttachment, content: &[u8], - context_initializers: &[ContextInitializer

], - pre_handling_initializers: &[ContextPreHandlingInitializer

], - runtime: &

::R, + world_id: WorldId, ) -> Result<

::C, ScriptError>; /// A strategy for reloading contexts @@ -36,9 +36,7 @@ pub type ContextReloadFn

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

::C, - context_initializers: &[ContextInitializer

], - pre_handling_initializers: &[ContextPreHandlingInitializer

], - runtime: &

::R, + world_id: WorldId, ) -> Result<(), ScriptError>; /// A utility trait for types implementing `IntoScriptPluginParams`. @@ -70,13 +68,7 @@ impl ScriptingLoader

for P { WorldGuard::with_existing_static_guard(world.clone(), |world| { let world_id = world.id(); ThreadWorldContainer.set_world(world)?; - Self::context_loader()( - attachment, - content, - P::readonly_configuration(world_id).context_initialization_callbacks, - P::readonly_configuration(world_id).pre_handling_callbacks, - P::readonly_configuration(world_id).runtime, - ) + Self::context_loader()(attachment, content, world_id) }) } @@ -89,14 +81,7 @@ impl ScriptingLoader

for P { WorldGuard::with_existing_static_guard(world, |world| { let world_id = world.id(); ThreadWorldContainer.set_world(world)?; - Self::context_reloader()( - attachment, - content, - previous_context, - P::readonly_configuration(world_id).context_initialization_callbacks, - P::readonly_configuration(world_id).pre_handling_callbacks, - P::readonly_configuration(world_id).runtime, - ) + Self::context_reloader()(attachment, content, previous_context, world_id) }) } } diff --git a/crates/bevy_mod_scripting_core/src/extractors.rs b/crates/bevy_mod_scripting_core/src/extractors.rs index 253a7c5adc..8d0d8235e4 100644 --- a/crates/bevy_mod_scripting_core/src/extractors.rs +++ b/crates/bevy_mod_scripting_core/src/extractors.rs @@ -103,12 +103,9 @@ impl HandlerContext

{ return Err(InteropError::missing_context(context_key.clone()).into()); }; - // call the script - let runtime = P::readonly_configuration(guard.id()).runtime; - let mut context = context.lock(); - P::handle(payload, context_key, label, &mut context, runtime, guard) + P::handle(payload, context_key, label, &mut context, guard) } /// Invoke a callback in a script immediately. diff --git a/crates/bevy_mod_scripting_core/src/handler.rs b/crates/bevy_mod_scripting_core/src/handler.rs index 589768f0e9..df6478db85 100644 --- a/crates/bevy_mod_scripting_core/src/handler.rs +++ b/crates/bevy_mod_scripting_core/src/handler.rs @@ -1,4 +1,6 @@ //! Contains the logic for handling script callback events +use bevy_ecs::world::WorldId; + use { bevy_ecs::{ event::EventCursor, @@ -15,7 +17,6 @@ use crate::{ ThreadWorldContainer, WorldAccessGuard, WorldContainer, WorldGuard, pretty_print::DisplayWithWorld, script_value::ScriptValue, }, - context::ContextPreHandlingInitializer, error::ScriptError, event::{ CallbackLabel, IntoCallbackLabel, ScriptCallbackEvent, ScriptCallbackResponseEvent, @@ -31,8 +32,7 @@ pub type HandlerFn

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

::C, - pre_handling_initializers: &[ContextPreHandlingInitializer

], - runtime: &

::R, + world_id: WorldId, ) -> Result; /// A utility trait, implemented for all types implementing `IntoScriptPluginParams`. @@ -46,7 +46,6 @@ pub trait ScriptingHandler { context_key: &ScriptAttachment, callback: &CallbackLabel, script_ctxt: &mut P::C, - runtime: &P::R, world: WorldGuard, ) -> Result; } @@ -58,20 +57,12 @@ impl ScriptingHandler

for P { context_key: &ScriptAttachment, callback: &CallbackLabel, script_ctxt: &mut P::C, - runtime: &P::R, world: WorldGuard, ) -> Result { WorldGuard::with_existing_static_guard(world.clone(), |world| { let world_id = world.id(); ThreadWorldContainer.set_world(world)?; - Self::handler()( - args, - context_key, - callback, - script_ctxt, - P::readonly_configuration(world_id).pre_handling_callbacks, - runtime, - ) + Self::handler()(args, context_key, callback, script_ctxt, world_id) }) } } diff --git a/crates/languages/bevy_mod_scripting_lua/src/lib.rs b/crates/languages/bevy_mod_scripting_lua/src/lib.rs index 5b8da7e627..3c7ca7df7f 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/lib.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/lib.rs @@ -5,6 +5,7 @@ use ::{ bevy_ecs::{entity::Entity, world::World}, }; use bevy_app::App; +use bevy_ecs::world::WorldId; use bevy_log::trace; use bevy_mod_scripting_core::{ IntoScriptPluginParams, ScriptingPlugin, @@ -14,7 +15,6 @@ use bevy_mod_scripting_core::{ globals::AppScriptGlobalsRegistry, script_value::ScriptValue, }, config::{GetPluginThreadConfig, ScriptingPluginConfiguration}, - context::{ContextInitializer, ContextPreHandlingInitializer}, error::ScriptError, event::CallbackLabel, make_plugin_config_static, @@ -177,9 +177,11 @@ fn load_lua_content_into_context( context: &mut Lua, context_key: &ScriptAttachment, content: &[u8], - initializers: &[ContextInitializer], - pre_handling_initializers: &[ContextPreHandlingInitializer], + world_id: WorldId, ) -> Result<(), ScriptError> { + let config = LuaScriptingPlugin::readonly_configuration(world_id); + let initializers = config.context_initialization_callbacks; + let pre_handling_initializers = config.pre_handling_callbacks; initializers .iter() .try_for_each(|init| init(context_key, context))?; @@ -201,22 +203,14 @@ fn load_lua_content_into_context( pub fn lua_context_load( context_key: &ScriptAttachment, content: &[u8], - initializers: &[ContextInitializer], - pre_handling_initializers: &[ContextPreHandlingInitializer], - _: &(), + world_id: WorldId, ) -> Result { #[cfg(feature = "unsafe_lua_modules")] let mut context = unsafe { Lua::unsafe_new() }; #[cfg(not(feature = "unsafe_lua_modules"))] let mut context = Lua::new(); - load_lua_content_into_context( - &mut context, - context_key, - content, - initializers, - pre_handling_initializers, - )?; + load_lua_content_into_context(&mut context, context_key, content, world_id)?; Ok(context) } @@ -226,17 +220,9 @@ pub fn lua_context_reload( context_key: &ScriptAttachment, content: &[u8], old_ctxt: &mut Lua, - initializers: &[ContextInitializer], - pre_handling_initializers: &[ContextPreHandlingInitializer], - _: &(), + world_id: WorldId, ) -> Result<(), ScriptError> { - load_lua_content_into_context( - old_ctxt, - context_key, - content, - initializers, - pre_handling_initializers, - )?; + load_lua_content_into_context(old_ctxt, context_key, content, world_id)?; Ok(()) } @@ -248,10 +234,12 @@ pub fn lua_handler( context_key: &ScriptAttachment, callback_label: &CallbackLabel, context: &mut Lua, - pre_handling_initializers: &[ContextPreHandlingInitializer], - _: &(), + world_id: WorldId, ) -> Result { - pre_handling_initializers + let config = LuaScriptingPlugin::readonly_configuration(world_id); + + config + .pre_handling_callbacks .iter() .try_for_each(|init| init(context_key, context))?; @@ -288,8 +276,6 @@ mod test { #[test] fn test_reload_doesnt_overwrite_old_context() { let lua = Lua::new(); - let initializers = vec![]; - let pre_handling_initializers = vec![]; let mut old_ctxt = lua.clone(); let handle = Handle::Weak(AssetId::from(AssetIndex::from_bits(0))); let context_key = ScriptAttachment::EntityScript(Entity::from_raw(1), handle); @@ -300,9 +286,7 @@ mod test { end" .as_bytes(), - &initializers, - &pre_handling_initializers, - &(), + WorldId::new().unwrap(), ) .unwrap(); @@ -313,9 +297,7 @@ mod test { end" .as_bytes(), &mut old_ctxt, - &initializers, - &pre_handling_initializers, - &(), + WorldId::new().unwrap(), ) .unwrap(); diff --git a/crates/languages/bevy_mod_scripting_rhai/src/lib.rs b/crates/languages/bevy_mod_scripting_rhai/src/lib.rs index 266713fe21..f25023f7a6 100644 --- a/crates/languages/bevy_mod_scripting_rhai/src/lib.rs +++ b/crates/languages/bevy_mod_scripting_rhai/src/lib.rs @@ -8,6 +8,7 @@ use ::{ bevy_ecs::{entity::Entity, world::World}, }; use bevy_app::App; +use bevy_ecs::world::WorldId; use bevy_log::trace; use bevy_mod_scripting_core::{ IntoScriptPluginParams, ScriptingPlugin, @@ -17,7 +18,6 @@ use bevy_mod_scripting_core::{ globals::AppScriptGlobalsRegistry, script_value::ScriptValue, }, config::{GetPluginThreadConfig, ScriptingPluginConfiguration}, - context::{ContextInitializer, ContextPreHandlingInitializer}, error::ScriptError, event::CallbackLabel, make_plugin_config_static, @@ -206,11 +206,12 @@ fn load_rhai_content_into_context( context: &mut RhaiScriptContext, context_key: &ScriptAttachment, content: &[u8], - initializers: &[ContextInitializer], - pre_handling_initializers: &[ContextPreHandlingInitializer], - runtime: &RhaiRuntime, + world_id: WorldId, ) -> Result<(), ScriptError> { - let runtime = runtime.read(); + 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)?)?; context @@ -233,23 +234,14 @@ fn load_rhai_content_into_context( pub fn rhai_context_load( context_key: &ScriptAttachment, content: &[u8], - initializers: &[ContextInitializer], - pre_handling_initializers: &[ContextPreHandlingInitializer], - runtime: &RhaiRuntime, + world_id: WorldId, ) -> Result { let mut context = RhaiScriptContext { // Using an empty AST as a placeholder. ast: AST::empty(), scope: Scope::new(), }; - load_rhai_content_into_context( - &mut context, - context_key, - content, - initializers, - pre_handling_initializers, - runtime, - )?; + load_rhai_content_into_context(&mut context, context_key, content, world_id)?; Ok(context) } @@ -258,18 +250,9 @@ pub fn rhai_context_reload( context_key: &ScriptAttachment, content: &[u8], context: &mut RhaiScriptContext, - initializers: &[ContextInitializer], - pre_handling_initializers: &[ContextPreHandlingInitializer], - runtime: &RhaiRuntime, + world_id: WorldId, ) -> Result<(), ScriptError> { - load_rhai_content_into_context( - context, - context_key, - content, - initializers, - pre_handling_initializers, - runtime, - ) + load_rhai_content_into_context(context, context_key, content, world_id) } #[allow(clippy::too_many_arguments)] @@ -279,9 +262,11 @@ pub fn rhai_callback_handler( context_key: &ScriptAttachment, callback: &CallbackLabel, context: &mut RhaiScriptContext, - pre_handling_initializers: &[ContextPreHandlingInitializer], - runtime: &RhaiRuntime, + world_id: WorldId, ) -> Result { + let config = RhaiScriptingPlugin::readonly_configuration(world_id); + let pre_handling_initializers = config.pre_handling_callbacks; + pre_handling_initializers .iter() .try_for_each(|init| init(context_key, context))?; @@ -297,7 +282,7 @@ pub fn rhai_callback_handler( "Calling callback {} in context {} with args: {:?}", callback, context_key, args ); - let runtime = runtime.read(); + let runtime = config.runtime.read(); match runtime.call_fn_with_options::( options, diff --git a/crates/testing_crates/test_utils/src/test_plugin.rs b/crates/testing_crates/test_utils/src/test_plugin.rs index c4c71ed79b..670c9deccc 100644 --- a/crates/testing_crates/test_utils/src/test_plugin.rs +++ b/crates/testing_crates/test_utils/src/test_plugin.rs @@ -33,7 +33,9 @@ macro_rules! make_test_plugin { } fn handler() -> $ident::HandlerFn { - (|args, context_key, callback, script_ctxt, pre_handling_initializers, runtime| { + (|args, context_key, callback, script_ctxt, world_id| { + let config = TestPlugin::readonly_configuration(world_id); + let runtime = config.runtime; runtime .invocations .lock() @@ -43,7 +45,7 @@ macro_rules! make_test_plugin { } fn context_loader() -> $ident::ContextLoadFn { - (|attachment, content, context_initializers, pre_handling_initializers, runtime| { + (|attachment, content, world_id| { Ok(TestContext { invocations: vec![], }) @@ -51,12 +53,7 @@ macro_rules! make_test_plugin { } fn context_reloader() -> $ident::ContextReloadFn { - (|attachment, - content, - previous_context, - context_initializers, - pre_handling_initializers, - runtime| { + (|attachment, content, previous_context, world_id| { previous_context.invocations.clear(); Ok(()) }) From 44fabf8450c8227cd95de03b90d170e1bd1613dd Mon Sep 17 00:00:00 2001 From: makspll Date: Sat, 30 Aug 2025 10:31:02 +0100 Subject: [PATCH 2/5] fix failing test, rename thing --- crates/bevy_mod_scripting_core/src/config.rs | 4 ++-- crates/bevy_mod_scripting_core/src/lib.rs | 2 +- .../languages/bevy_mod_scripting_lua/src/lib.rs | 15 ++++++++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/crates/bevy_mod_scripting_core/src/config.rs b/crates/bevy_mod_scripting_core/src/config.rs index 5c48a80477..b327edb5af 100644 --- a/crates/bevy_mod_scripting_core/src/config.rs +++ b/crates/bevy_mod_scripting_core/src/config.rs @@ -46,7 +46,7 @@ pub trait GetPluginThreadConfig { fn readonly_configuration(world: WorldId) -> ScriptingPluginConfiguration

; /// Set the configuration or overwrites it if already set. - fn set_thread_config(world: WorldId, config: ScriptingPluginConfiguration

); + fn set_world_local_config(world: WorldId, config: ScriptingPluginConfiguration

); } #[macro_export] @@ -76,7 +76,7 @@ macro_rules! make_plugin_config_static { ) } - fn set_thread_config( + fn set_world_local_config( world: bevy_ecs::world::WorldId, config: ScriptingPluginConfiguration<$ty>, ) { diff --git a/crates/bevy_mod_scripting_core/src/lib.rs b/crates/bevy_mod_scripting_core/src/lib.rs index aa51e17467..91e8b71cce 100644 --- a/crates/bevy_mod_scripting_core/src/lib.rs +++ b/crates/bevy_mod_scripting_core/src/lib.rs @@ -165,7 +165,7 @@ impl Plugin for ScriptingPlugin

{ runtime: Box::leak(Box::new(runtime)), }; - P::set_thread_config(app.world().id(), config); + P::set_world_local_config(app.world().id(), config); app.insert_resource(ScriptContext::

::new(self.context_policy.clone())); diff --git a/crates/languages/bevy_mod_scripting_lua/src/lib.rs b/crates/languages/bevy_mod_scripting_lua/src/lib.rs index 3c7ca7df7f..e884889352 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/lib.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/lib.rs @@ -279,14 +279,23 @@ mod test { let mut old_ctxt = lua.clone(); let handle = Handle::Weak(AssetId::from(AssetIndex::from_bits(0))); let context_key = ScriptAttachment::EntityScript(Entity::from_raw(1), handle); - + let world_id = WorldId::new().unwrap(); + LuaScriptingPlugin::set_world_local_config( + world_id, + ScriptingPluginConfiguration { + pre_handling_callbacks: &[], + context_initialization_callbacks: &[], + emit_responses: false, + runtime: &(), + }, + ); lua_context_load( &context_key, "function hello_world_from_first_load() end" .as_bytes(), - WorldId::new().unwrap(), + world_id, ) .unwrap(); @@ -297,7 +306,7 @@ mod test { end" .as_bytes(), &mut old_ctxt, - WorldId::new().unwrap(), + world_id, ) .unwrap(); From 8588f8a5a0b6512068924709ea192409eed2de63 Mon Sep 17 00:00:00 2001 From: makspll Date: Sat, 30 Aug 2025 11:02:41 +0100 Subject: [PATCH 3/5] refactor: remove resource --- crates/bevy_mod_scripting_core/src/asset.rs | 19 +++-- .../bevy_mod_scripting_core/src/commands.rs | 23 +----- .../bevy_mod_scripting_core/src/extractors.rs | 11 +-- crates/bevy_mod_scripting_core/src/lib.rs | 3 +- .../bevy_mod_scripting_core/src/script/mod.rs | 76 ------------------- examples/game_of_life.rs | 7 +- tests/script_tests.rs | 5 +- 7 files changed, 19 insertions(+), 125 deletions(-) diff --git a/crates/bevy_mod_scripting_core/src/asset.rs b/crates/bevy_mod_scripting_core/src/asset.rs index f8d84cbeb5..adeac856f3 100644 --- a/crates/bevy_mod_scripting_core/src/asset.rs +++ b/crates/bevy_mod_scripting_core/src/asset.rs @@ -12,7 +12,7 @@ use ::{ // }, bevy_reflect::Reflect, }; -use bevy_asset::AssetServer; +use bevy_asset::{AssetServer, Handle}; use bevy_ecs::{ entity::Entity, event::{EventReader, EventWriter}, @@ -23,7 +23,7 @@ use bevy_ecs::{ use serde::{Deserialize, Serialize}; use crate::{ - IntoScriptPluginParams, LanguageExtensions, ScriptComponent, ScriptingSystemSet, StaticScripts, + IntoScriptPluginParams, LanguageExtensions, ScriptComponent, ScriptingSystemSet, commands::{CreateOrUpdateScript, DeleteScript}, error::ScriptError, event::ScriptEvent, @@ -215,7 +215,6 @@ fn sync_assets( fn handle_script_events( mut events: EventReader, script_assets: Res>, - static_scripts: Res, scripts: Query<(Entity, &ScriptComponent)>, asset_server: Res, mut script_queue: Local, @@ -233,6 +232,9 @@ fn handle_script_events( // We need to reload the script for any context it's // associated with. That could be static scripts, script // components. + + let mut found_entity = false; + for (entity, script_component) in &scripts { if let Some(handle) = script_component.0.iter().find(|handle| handle.id() == *id) @@ -244,15 +246,16 @@ fn handle_script_events( )) .with_responses(P::readonly_configuration(world_id).emit_responses), ); + found_entity = true; } } - if let Some(handle) = static_scripts.scripts.iter().find(|s| s.id() == *id) { + if !found_entity { + let handle = Handle::Weak(*id); + // if the script does not have any associated entity it's static. commands.queue( - CreateOrUpdateScript::

::new(ScriptAttachment::StaticScript( - handle.clone(), - )) - .with_responses(P::readonly_configuration(world_id).emit_responses), + CreateOrUpdateScript::

::new(ScriptAttachment::StaticScript(handle)) + .with_responses(P::readonly_configuration(world_id).emit_responses), ); } } diff --git a/crates/bevy_mod_scripting_core/src/commands.rs b/crates/bevy_mod_scripting_core/src/commands.rs index 3ad3b35eb9..5654956f49 100644 --- a/crates/bevy_mod_scripting_core/src/commands.rs +++ b/crates/bevy_mod_scripting_core/src/commands.rs @@ -14,7 +14,7 @@ use crate::{ }, extractors::{HandlerContext, with_handler_system_state}, handler::{handle_script_errors, send_callback_response}, - script::{DisplayProxy, ScriptAttachment, StaticScripts}, + script::{DisplayProxy, ScriptAttachment}, }; use bevy_ecs::{system::Command, world::World}; use bevy_log::{error, info, trace}; @@ -66,23 +66,6 @@ impl Command for DeleteScript

{ ), world, ); - match &self.context_key { - ScriptAttachment::EntityScript(_, _) => { - // nothing special needs to be done, just the context removal - } - ScriptAttachment::StaticScript(script) => { - // remove the static script - let mut scripts = world.get_resource_or_init::(); - if scripts.remove(script.id()) { - debug!("Deleted static script {}", script.display()); - } else { - warn!( - "Attempted to delete static script {}, but it was not found", - script.display() - ); - } - } - } let mut script_contexts = world.get_resource_or_init::>(); let residents_count = script_contexts.residents_len(&self.context_key); @@ -225,10 +208,6 @@ impl CreateOrUpdateScript

{ ) -> Result<(), ScriptError> { // we demote to weak from here on out, so as not to hold the asset hostage let attachment = attachment.clone().into_weak(); - if let ScriptAttachment::StaticScript(id) = &attachment { - // add to static scripts - handler_ctxt.static_scripts.insert(id.clone()); - } let script_id = attachment.script(); diff --git a/crates/bevy_mod_scripting_core/src/extractors.rs b/crates/bevy_mod_scripting_core/src/extractors.rs index 8d0d8235e4..b3059cfe12 100644 --- a/crates/bevy_mod_scripting_core/src/extractors.rs +++ b/crates/bevy_mod_scripting_core/src/extractors.rs @@ -24,7 +24,7 @@ use crate::{ error::{InteropError, ScriptError}, event::{CallbackLabel, IntoCallbackLabel}, handler::ScriptingHandler, - script::{ScriptAttachment, ScriptContext, StaticScripts}, + script::{ScriptAttachment, ScriptContext}, }; /// Executes `system_state.get_mut` followed by `system_state.apply` after running the given closure, makes sure state is correctly handled in the context of an exclusive system. @@ -47,8 +47,6 @@ pub fn with_handler_system_state< /// Context for systems which handle events for scripts pub struct HandlerContext { - /// List of static scripts - pub(crate) static_scripts: StaticScripts, /// Script context pub(crate) script_context: ScriptContext

, } @@ -58,7 +56,6 @@ impl HandlerContext

{ /// Every call to this function must be paired with a call to [`Self::release`]. pub fn yoink(world: &mut World) -> Self { Self { - static_scripts: world.remove_resource().unwrap_or_default(), script_context: world.remove_resource().unwrap_or_default(), } } @@ -67,15 +64,9 @@ impl HandlerContext

{ /// Only call this if you have previously yoinked the handler context from the world. pub fn release(self, world: &mut World) { // insert the handler context back into the world - world.insert_resource(self.static_scripts); world.insert_resource(self.script_context); } - /// Get the static scripts - pub fn static_scripts(&mut self) -> &mut StaticScripts { - &mut self.static_scripts - } - /// Get the static scripts pub fn script_context(&mut self) -> &mut ScriptContext

{ &mut self.script_context diff --git a/crates/bevy_mod_scripting_core/src/lib.rs b/crates/bevy_mod_scripting_core/src/lib.rs index 91e8b71cce..45303ed9cd 100644 --- a/crates/bevy_mod_scripting_core/src/lib.rs +++ b/crates/bevy_mod_scripting_core/src/lib.rs @@ -33,7 +33,7 @@ use context::{Context, ContextInitializer, ContextPreHandlingInitializer}; use event::{ScriptCallbackEvent, ScriptCallbackResponseEvent, ScriptEvent}; use handler::HandlerFn; use runtime::{Runtime, RuntimeInitializer}; -use script::{ContextPolicy, ScriptComponent, ScriptContext, StaticScripts}; +use script::{ContextPolicy, ScriptComponent, ScriptContext}; use std::ops::{Deref, DerefMut}; pub mod asset; @@ -294,7 +294,6 @@ impl Plugin for BMSScriptingInfrastructurePlugin { .add_event::() .add_event::() .init_resource::() - .init_resource::() .init_asset::() .init_resource::() .insert_resource(AppScheduleRegistry::new()); diff --git a/crates/bevy_mod_scripting_core/src/script/mod.rs b/crates/bevy_mod_scripting_core/src/script/mod.rs index 7d9ab9cb7d..30a56dc404 100644 --- a/crates/bevy_mod_scripting_core/src/script/mod.rs +++ b/crates/bevy_mod_scripting_core/src/script/mod.rs @@ -125,88 +125,12 @@ impl ScriptComponent { } } -/// A collection of scripts, not associated with any entity. -/// -/// Useful for `global` or `static` scripts which operate over a larger scope than a single entity. -#[derive(Default, Resource)] -pub struct StaticScripts { - pub(crate) scripts: HashSet>, -} - -#[profiling::all_functions] -impl StaticScripts { - /// Inserts a static script into the collection - pub fn insert>>(&mut self, script: S) { - self.scripts.insert(script.into()); - } - - /// Removes a static script from the collection, returning `true` if the script was in the collection, `false` otherwise - pub fn remove(&mut self, script_id: impl Into) -> bool { - let script_id = script_id.into(); - self.scripts - .extract_if(|handle| handle.id() == script_id) - .next() - .is_some() - } - - /// Checks if a static script is in the collection - /// Returns `true` if the script is in the collection, `false` otherwise - pub fn contains(&self, script_id: impl Into) -> bool { - let script_id = script_id.into(); - self.scripts.iter().any(|handle| handle.id() == script_id) - } - - /// Returns an iterator over the static scripts - pub fn values(&self) -> impl Iterator> { - self.scripts.iter() - } -} - #[cfg(test)] mod tests { use bevy_ecs::{event::Events, world::World}; use super::*; - #[test] - fn static_scripts_insert() { - let mut static_scripts = StaticScripts::default(); - let script1 = Handle::default(); - static_scripts.insert(script1.clone()); - assert_eq!(static_scripts.scripts.len(), 1); - assert!(static_scripts.scripts.contains(&script1)); - } - - #[test] - fn static_scripts_remove() { - let mut static_scripts = StaticScripts::default(); - let script1 = Handle::default(); - static_scripts.insert(script1.clone()); - assert_eq!(static_scripts.scripts.len(), 1); - assert!(static_scripts.scripts.contains(&script1)); - assert!(static_scripts.remove(&script1)); - assert_eq!(static_scripts.scripts.len(), 0); - assert!(!static_scripts.scripts.contains(&script1)); - } - - fn scriptid_from_u128(uuid: u128) -> ScriptId { - ScriptId::from(uuid::Builder::from_random_bytes(uuid.to_le_bytes()).into_uuid()) - } - - fn handle_from_u128(uuid: u128) -> Handle { - Handle::Weak(scriptid_from_u128(uuid)) - } - - #[test] - fn static_scripts_contains() { - let mut static_scripts = StaticScripts::default(); - let script1 = handle_from_u128(0); - let script2 = handle_from_u128(1); - static_scripts.insert(script1.clone()); - assert!(static_scripts.contains(&script1)); - assert!(!static_scripts.contains(&script2)); - } - #[test] fn test_component_add() { let mut world = World::new(); diff --git a/examples/game_of_life.rs b/examples/game_of_life.rs index 4c769b462d..08b41c9f9c 100644 --- a/examples/game_of_life.rs +++ b/examples/game_of_life.rs @@ -16,7 +16,7 @@ use bevy::{ }; use bevy_console::{AddConsoleCommand, ConsoleCommand, ConsoleOpen, ConsolePlugin, make_layer}; use bevy_mod_scripting::{core::bindings::AllocatorDiagnosticPlugin, prelude::*}; -use bevy_mod_scripting_core::{commands::RemoveStaticScript, script::StaticScripts}; +use bevy_mod_scripting_core::commands::RemoveStaticScript; use clap::Parser; // CONSOLE SETUP @@ -42,7 +42,7 @@ fn run_script_cmd( mut commands: Commands, asset_server: Res, script_comps: Query<(Entity, &ScriptComponent)>, - static_scripts: Res, + mut static_scripts: Local>>, ) { if let Some(Ok(command)) = log.take() { match command { @@ -60,6 +60,7 @@ fn run_script_cmd( } else { bevy::log::info!("Using static script instead of spawning an entity"); let handle = asset_server.load(script_path); + static_scripts.push(handle.clone()); commands.queue(AddStaticScript::new(handle)) } } @@ -72,7 +73,7 @@ fn run_script_cmd( commands.entity(id).despawn(); } - for script in static_scripts.values() { + for script in static_scripts.iter() { commands.queue(RemoveStaticScript::new(script.clone())); } } diff --git a/tests/script_tests.rs b/tests/script_tests.rs index 737896b47c..05fec9dc1c 100644 --- a/tests/script_tests.rs +++ b/tests/script_tests.rs @@ -28,10 +28,7 @@ impl TestExecutor for Test { // do this in a separate thread to isolate the thread locals - match execute_integration_test(scenario) { - Ok(_) => Ok(()), - Err(e) => Err(Failed::from(format!("{e:?}"))), // print whole error from anyhow including source and backtrace - } + Ok(execute_integration_test(scenario)?) } fn name(&self) -> String { From 1ca419b3195dced41dd44b35b64fa4b76f2476aa Mon Sep 17 00:00:00 2001 From: makspll Date: Sat, 30 Aug 2025 11:38:09 +0100 Subject: [PATCH 4/5] fix some bugs --- Cargo.toml | 1 + crates/bevy_mod_scripting_core/src/asset.rs | 22 ++++++++++--------- .../src/script/context_key.rs | 10 +++++++++ examples/game_of_life.rs | 2 -- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e9a301cd9a..41d08e564a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -211,6 +211,7 @@ bevy = { workspace = true, features = [ "bevy_asset", "bevy_core_pipeline", "bevy_sprite", + "x11", ] } bevy_platform = { workspace = true } clap = { workspace = true, features = ["derive"] } diff --git a/crates/bevy_mod_scripting_core/src/asset.rs b/crates/bevy_mod_scripting_core/src/asset.rs index adeac856f3..44b93378f0 100644 --- a/crates/bevy_mod_scripting_core/src/asset.rs +++ b/crates/bevy_mod_scripting_core/src/asset.rs @@ -27,7 +27,7 @@ use crate::{ commands::{CreateOrUpdateScript, DeleteScript}, error::ScriptError, event::ScriptEvent, - script::{ContextKey, DisplayProxy, ScriptAttachment}, + script::{ContextKey, DisplayProxy, ScriptAttachment, ScriptContext}, }; /// Represents a scripting language. Languages which compile into another language should use the target language as their language. @@ -218,6 +218,7 @@ fn handle_script_events( scripts: Query<(Entity, &ScriptComponent)>, asset_server: Res, mut script_queue: Local, + script_contexts: Res>, mut commands: Commands, world_id: WorldId, ) { @@ -233,8 +234,6 @@ fn handle_script_events( // associated with. That could be static scripts, script // components. - let mut found_entity = false; - for (entity, script_component) in &scripts { if let Some(handle) = script_component.0.iter().find(|handle| handle.id() == *id) @@ -246,17 +245,20 @@ fn handle_script_events( )) .with_responses(P::readonly_configuration(world_id).emit_responses), ); - found_entity = true; } } - if !found_entity { - let handle = Handle::Weak(*id); + let handle = Handle::Weak(*id); + let attachment = ScriptAttachment::StaticScript(handle.clone()); + for (resident, _) in script_contexts + .residents(&attachment) + .filter(|(r, _)| r.script() == handle && r.is_static()) + { // if the script does not have any associated entity it's static. - commands.queue( - CreateOrUpdateScript::

::new(ScriptAttachment::StaticScript(handle)) - .with_responses(P::readonly_configuration(world_id).emit_responses), - ); + commands + .queue(CreateOrUpdateScript::

::new(resident).with_responses( + P::readonly_configuration(world_id).emit_responses, + )); } } } diff --git a/crates/bevy_mod_scripting_core/src/script/context_key.rs b/crates/bevy_mod_scripting_core/src/script/context_key.rs index 294ba51790..45f695441a 100644 --- a/crates/bevy_mod_scripting_core/src/script/context_key.rs +++ b/crates/bevy_mod_scripting_core/src/script/context_key.rs @@ -60,6 +60,16 @@ impl ScriptAttachment { } } } + + /// Returns true if the attachment is a static script. + pub fn is_static(&self) -> bool { + matches!(self, ScriptAttachment::StaticScript(_)) + } + + /// Returns true if the attachment is an entity script. + pub fn is_entity_script(&self) -> bool { + matches!(self, ScriptAttachment::EntityScript(_, _)) + } } impl From for ContextKey { diff --git a/examples/game_of_life.rs b/examples/game_of_life.rs index 08b41c9f9c..2b796a4a00 100644 --- a/examples/game_of_life.rs +++ b/examples/game_of_life.rs @@ -116,10 +116,8 @@ fn game_of_life_app(app: &mut App) -> &mut App { send_on_update.after(update_rendered_state), ( event_handler::, - #[cfg(feature = "rhai")] event_handler::, event_handler::, - #[cfg(feature = "rhai")] event_handler::, ) .after(send_on_update), From 852ce00d2228d68e170399122c51abb3ecc4c657 Mon Sep 17 00:00:00 2001 From: makspll Date: Sat, 30 Aug 2025 11:51:32 +0100 Subject: [PATCH 5/5] dont error on deleting non existing script --- crates/bevy_mod_scripting_core/src/commands.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/bevy_mod_scripting_core/src/commands.rs b/crates/bevy_mod_scripting_core/src/commands.rs index 5654956f49..998c351c23 100644 --- a/crates/bevy_mod_scripting_core/src/commands.rs +++ b/crates/bevy_mod_scripting_core/src/commands.rs @@ -56,7 +56,20 @@ impl Command for DeleteScript

{ // we demote to weak from here on out, so as not to hold the asset hostage self.context_key = self.context_key.into_weak(); - // first apply unload callback + // first check the script exists, if it does not it could have been deleted by another command + { + let script_contexts = world.get_resource_or_init::>(); + if !script_contexts.contains(&self.context_key) { + debug!( + "{}: No context found for {}, not deleting.", + P::LANGUAGE, + self.context_key + ); + return; + } + } + + // apply unload callback Command::apply( RunScriptCallback::

::new( self.context_key.clone(),