diff --git a/Cargo.toml b/Cargo.toml index 6a9499c167..8912ebd0b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -112,6 +112,7 @@ bevy_mod_scripting_derive = { workspace = true } bevy_mod_scripting_asset = { workspace = true } bevy_mod_scripting_bindings = { workspace = true } bevy_mod_scripting_display = { workspace = true } +bevy_mod_scripting_script = { workspace = true } [workspace.dependencies] # local crates @@ -127,6 +128,7 @@ bevy_mod_scripting_rhai = { path = "crates/languages/bevy_mod_scripting_rhai", v bevy_mod_scripting_asset = { path = "crates/bevy_mod_scripting_asset", version = "0.16.0", default-features = false } bevy_mod_scripting_bindings = { path = "crates/bevy_mod_scripting_bindings", version = "0.16.0", default-features = false } bevy_mod_scripting_display = { path = "crates/bevy_mod_scripting_display", version = "0.16.0", default-features = false } +bevy_mod_scripting_script = { path = "crates/bevy_mod_scripting_script", version = "0.16.0", default-features = false } # bevy bevy_mod_scripting_core = { path = "crates/bevy_mod_scripting_core", version = "0.16.0" } @@ -261,6 +263,7 @@ members = [ "crates/bevy_mod_scripting_asset", "crates/bevy_mod_scripting_bindings", "crates/bevy_mod_scripting_display", + "crates/bevy_mod_scripting_script", ] resolver = "2" exclude = ["codegen", "crates/macro_tests", "xtask"] diff --git a/assets/tests/register_callback/dynamic_on_test.lua b/assets/tests/register_callback/dynamic_on_test.lua new file mode 100644 index 0000000000..3dc09a778e --- /dev/null +++ b/assets/tests/register_callback/dynamic_on_test.lua @@ -0,0 +1,12 @@ +function on_script_loaded() + register_callback("on_test", dynamic_on_test) +end + +function dynamic_on_test() + register_callback("on_test_last", dynamic_on_test_last) + return "on test: I am dynamically registered from a normal callback!" +end + +function dynamic_on_test_last() + return "on test last: I am dynamically registered from another dynamic callback!" +end diff --git a/assets/tests/register_callback/empty.lua b/assets/tests/register_callback/empty.lua new file mode 100644 index 0000000000..e69de29bb2 diff --git a/assets/tests/register_callback/scenario.txt b/assets/tests/register_callback/scenario.txt new file mode 100644 index 0000000000..a0086eb45e --- /dev/null +++ b/assets/tests/register_callback/scenario.txt @@ -0,0 +1,27 @@ +// #main_script dynamic_on_test.lua +SetCurrentLanguage language="@this_script_language" +InstallPlugin miliseconds_budget=999999 +SetupHandler OnTest=null, Update=null +SetupHandler OnTestPostUpdate=null, PostUpdate=null +SetupHandler Last=null, OnTestLast=null +FinalizeApp + +LoadScriptAs as_name="@this_script", path="@this_script" +WaitForScriptLoaded name="@this_script" +SpawnEntityWithScript name="test_entity", script="@this_script" +RunUpdateOnce +EmitScriptCallbackEvent emit_response=true, entity="test_entity", label="OnTest", language=null, recipients="EntityScript", script="@this_script" +EmitScriptCallbackEvent emit_response=true, entity="test_entity", label="OnTestLast", language=null, recipients="EntityScript", script="@this_script" +RunUpdateOnce +AssertCallbackSuccess attachment="EntityScript", entity="test_entity", label="OnTest", script="@this_script", expect_string_value="on test: I am dynamically registered from a normal callback!" +AssertCallbackSuccess attachment="EntityScript", entity="test_entity", label="OnTestLast", script="@this_script", expect_string_value="on test last: I am dynamically registered from another dynamic callback!" + +// reload, deleting old callbacks, expect stored callbacks to still work +ReloadScriptFrom script="@this_script", path="empty.lua" +RunUpdateOnce +EmitScriptCallbackEvent emit_response=true, entity="test_entity", label="OnTest", language=null, recipients="EntityScript", script="@this_script" +EmitScriptCallbackEvent emit_response=true, entity="test_entity", label="OnTestLast", language=null, recipients="EntityScript", script="@this_script" +RunUpdateOnce +AssertCallbackSuccess attachment="EntityScript", entity="test_entity", label="OnTest", script="@this_script", expect_string_value="on test: I am dynamically registered from a normal callback!" +AssertCallbackSuccess attachment="EntityScript", entity="test_entity", label="OnTestLast", script="@this_script", expect_string_value="on test last: I am dynamically registered from another dynamic callback!" + diff --git a/badges/coverage.svg b/badges/coverage.svg index a1bdb9c62c..ca989cef36 100644 --- a/badges/coverage.svg +++ b/badges/coverage.svg @@ -1,13 +1,13 @@ - COVERAGE: 75% + xmlns:xlink="http://www.w3.org/1999/xlink" width="142.5" height="28" role="img" aria-label="COVERAGE: 37%"> + COVERAGE: 37% - + COVERAGE - 75% + 37% \ No newline at end of file diff --git a/crates/bevy_mod_scripting_bindings/Cargo.toml b/crates/bevy_mod_scripting_bindings/Cargo.toml index a1c0a3fbcb..a94123c391 100644 --- a/crates/bevy_mod_scripting_bindings/Cargo.toml +++ b/crates/bevy_mod_scripting_bindings/Cargo.toml @@ -16,6 +16,7 @@ readme.workspace = true bevy_mod_scripting_asset = { workspace = true } bevy_mod_scripting_derive = { workspace = true } bevy_mod_scripting_display = { workspace = true } +bevy_mod_scripting_script = { workspace = true } bevy_system_reflection = { workspace = true } bevy_diagnostic = { workspace = true } bevy_ecs = { workspace = true } 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 75d57a04f1..d563aff1a1 100644 --- a/crates/bevy_mod_scripting_bindings/src/function/script_function.rs +++ b/crates/bevy_mod_scripting_bindings/src/function/script_function.rs @@ -4,7 +4,7 @@ use super::MagicFunctions; use super::{from::FromScript, into::IntoScript, namespace::Namespace}; use crate::docgen::info::{FunctionInfo, GetFunctionInfo}; use crate::function::arg_meta::ArgMeta; -use crate::{ScriptValue, ThreadWorldContainer, WorldContainer, WorldGuard, error::InteropError}; +use crate::{ScriptValue, ThreadWorldContainer, WorldGuard, error::InteropError}; use bevy_ecs::prelude::Resource; use bevy_mod_scripting_asset::Language; use bevy_mod_scripting_derive::DebugWithTypeInfo; @@ -55,7 +55,7 @@ impl FunctionCallContext { /// Tries to access the world, returning an error if the world is not available #[profiling::function] pub fn world<'l>(&self) -> Result, InteropError> { - ThreadWorldContainer.try_get_world() + ThreadWorldContainer.try_get_context().map(|c| c.world) } /// Whether the caller uses 1-indexing on all indexes and expects 0-indexing conversions to be performed. #[profiling::function] @@ -661,12 +661,19 @@ variadics_please::all_tuples!(impl_script_function, 0, 13, T); #[cfg(test)] mod test { use super::*; + use bevy_asset::{AssetId, Handle}; use bevy_ecs::{prelude::Component, world::World}; + use bevy_mod_scripting_script::ScriptAttachment; fn with_local_world(f: F) { let mut world = World::default(); WorldGuard::with_static_guard(&mut world, |world| { - ThreadWorldContainer.set_world(world).unwrap(); + ThreadWorldContainer + .set_context(crate::ThreadScriptContext { + world, + attachment: ScriptAttachment::StaticScript(Handle::Weak(AssetId::invalid())), + }) + .unwrap(); f() }); } diff --git a/crates/bevy_mod_scripting_bindings/src/reference.rs b/crates/bevy_mod_scripting_bindings/src/reference.rs index 664cabcd43..6a6259f798 100644 --- a/crates/bevy_mod_scripting_bindings/src/reference.rs +++ b/crates/bevy_mod_scripting_bindings/src/reference.rs @@ -6,9 +6,8 @@ //! we need wrapper types which have owned and ref variants. use super::{WorldGuard, access_map::ReflectAccessId}; use crate::{ - ReflectAllocationId, ReflectAllocator, ThreadWorldContainer, WorldContainer, - error::InteropError, reflection_extensions::PartialReflectExt, with_access_read, - with_access_write, + ReflectAllocationId, ReflectAllocator, ThreadWorldContainer, 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}; @@ -68,7 +67,7 @@ impl DisplayWithTypeInfo for ReflectReference { let guard = any.downcast_ref::().cloned().or_else(|| { any.downcast_ref::() - .and_then(|t| t.try_get_world().ok()) + .and_then(|t| t.try_get_context().ok().map(|c| c.world)) }); if let Some(guard) = guard { @@ -767,7 +766,7 @@ impl DisplayWithTypeInfo for ReflectBase { let guard = any.downcast_ref::().cloned().or_else(|| { any.downcast_ref::() - .and_then(|t| t.try_get_world().ok()) + .and_then(|t| t.try_get_context().ok().map(|c| c.world)) }); if let Some(guard) = guard { diff --git a/crates/bevy_mod_scripting_bindings/src/world.rs b/crates/bevy_mod_scripting_bindings/src/world.rs index bb61e92046..2a76e1cfa3 100644 --- a/crates/bevy_mod_scripting_bindings/src/world.rs +++ b/crates/bevy_mod_scripting_bindings/src/world.rs @@ -51,6 +51,7 @@ use bevy_ecs::{ }; use bevy_mod_scripting_asset::ScriptAsset; use bevy_mod_scripting_display::GetTypeInfo; +use bevy_mod_scripting_script::ScriptAttachment; use bevy_platform::collections::HashMap; use bevy_reflect::{TypeInfo, VariantInfo}; use bevy_system_reflection::ReflectSchedule; @@ -1337,44 +1338,45 @@ impl WorldAccessGuard<'_> { } } -/// Utility type for accessing the world in a callback -pub trait WorldContainer { - /// The error type for the container - type Error: Debug; - /// Sets the world to the given value in the container - fn set_world(&mut self, world: WorldGuard<'static>) -> Result<(), Self::Error>; - - /// Tries to get the world from the container - fn try_get_world<'l>(&self) -> Result, Self::Error>; -} - /// A world container that stores the world in a thread local pub struct ThreadWorldContainer; +#[derive(Clone)] +/// Context passed down indirectly to script related functions, used to avoid prop drilling problems. +pub struct ThreadScriptContext<'l> { + /// The world pointer + pub world: WorldGuard<'l>, + /// The currently active script attachment + pub attachment: ScriptAttachment, +} + thread_local! { - static WORLD_CALLBACK_ACCESS: RefCell>> = const { RefCell::new(None) }; + static WORLD_CALLBACK_ACCESS: RefCell>> = const { RefCell::new(None) }; } #[profiling::all_functions] -impl WorldContainer for ThreadWorldContainer { - type Error = InteropError; - - fn set_world(&mut self, world: WorldGuard<'static>) -> Result<(), Self::Error> { +impl ThreadWorldContainer { + /// Tries to set the thread context to the given value + pub fn set_context(&mut self, world: ThreadScriptContext<'static>) -> Result<(), InteropError> { WORLD_CALLBACK_ACCESS.with(|w| { w.replace(Some(world)); }); Ok(()) } - fn try_get_world<'l>(&self) -> Result, Self::Error> { + /// Tries to get the world from the container + pub fn try_get_context<'l>(&self) -> Result, InteropError> { WORLD_CALLBACK_ACCESS .with(|w| w.borrow().clone().ok_or_else(InteropError::missing_world)) - .map(|v| v.shorten_lifetime()) + .map(|v| ThreadScriptContext { + world: v.world.shorten_lifetime(), + attachment: v.attachment, + }) } } impl GetTypeInfo for ThreadWorldContainer { fn get_type_info(&self, type_id: TypeId) -> Option<&TypeInfo> { - let world = self.try_get_world().ok()?; + let world = self.try_get_context().ok()?.world; let registry = world.type_registry(); let registry = registry.read(); registry.get(type_id).map(|r| r.type_info()) @@ -1385,7 +1387,7 @@ impl GetTypeInfo for ThreadWorldContainer { type_id: TypeId, type_data_id: TypeId, ) -> Option> { - let world = self.try_get_world().ok()?; + let world = self.try_get_context().ok()?.world; let registry = world.type_registry(); let registry = registry.read(); registry @@ -1397,7 +1399,7 @@ impl GetTypeInfo for ThreadWorldContainer { &self, component_id: ComponentId, ) -> Option<&bevy_ecs::component::ComponentInfo> { - let world = self.try_get_world().ok()?; + let world = self.try_get_context().ok()?.world; let cell = world.as_unsafe_world_cell().ok()?; cell.components().get_info(component_id) } diff --git a/crates/bevy_mod_scripting_core/Cargo.toml b/crates/bevy_mod_scripting_core/Cargo.toml index dd9de7dc6e..34d187e9d8 100644 --- a/crates/bevy_mod_scripting_core/Cargo.toml +++ b/crates/bevy_mod_scripting_core/Cargo.toml @@ -27,7 +27,7 @@ bevy_mod_scripting_asset = { workspace = true } bevy_mod_scripting_bindings = { workspace = true } bevy_system_reflection = { workspace = true } bevy_mod_scripting_display = { workspace = true } - +bevy_mod_scripting_script = { workspace = true } bevy_reflect = { workspace = true, default-features = false, features = [] } bevy_ecs = { workspace = true, default-features = false, features = [] } bevy_app = { workspace = true, default-features = false, features = [] } diff --git a/crates/bevy_mod_scripting_core/src/callbacks.rs b/crates/bevy_mod_scripting_core/src/callbacks.rs new file mode 100644 index 0000000000..d85b0da015 --- /dev/null +++ b/crates/bevy_mod_scripting_core/src/callbacks.rs @@ -0,0 +1,65 @@ +//! Module managing script registered callbacks +use std::{marker::PhantomData, sync::Arc}; + +use bevy_app::Plugin; +use bevy_ecs::{resource::Resource, world::WorldId}; +use bevy_mod_scripting_bindings::{InteropError, ScriptValue}; +use bevy_mod_scripting_script::ScriptAttachment; +use bevy_platform::collections::HashMap; +use parking_lot::RwLock; + +use crate::IntoScriptPluginParams; + +/// A callback function encapsulating the callback context +pub type CallbackFn

= dyn Fn( + Vec, + &mut

::C, + WorldId, + ) -> Result + + Send + + Sync + + 'static; + +/// A resource containing the callbacks for this script plugin. +/// +/// Scripts can opt to register callbacks which are then used to invoke event callbacks. +#[derive(Resource)] +pub struct ScriptCallbacks { + /// The callbacks registered per script + pub callbacks: Arc>>>>, +} + +impl Clone for ScriptCallbacks

{ + fn clone(&self) -> Self { + Self { + callbacks: self.callbacks.clone(), + } + } +} + +impl Default for ScriptCallbacks

{ + fn default() -> Self { + Self { + callbacks: Default::default(), + } + } +} + +/// Plugin adding necessary resources to handle callback storage for script plugins. +pub struct ScriptCallbacksPlugin { + _ph: PhantomData, +} + +impl Default for ScriptCallbacksPlugin

{ + fn default() -> Self { + Self { + _ph: Default::default(), + } + } +} + +impl Plugin for ScriptCallbacksPlugin

{ + fn build(&self, app: &mut bevy_app::App) { + app.init_resource::>(); + } +} diff --git a/crates/bevy_mod_scripting_core/src/commands.rs b/crates/bevy_mod_scripting_core/src/commands.rs index 88d5d0681b..ade22041f1 100644 --- a/crates/bevy_mod_scripting_core/src/commands.rs +++ b/crates/bevy_mod_scripting_core/src/commands.rs @@ -4,19 +4,20 @@ use std::{sync::Arc, time::Duration}; use crate::{ IntoScriptPluginParams, ScriptContext, + callbacks::ScriptCallbacks, error::ScriptError, event::{ CallbackLabel, ForPlugin, ScriptAttachedEvent, ScriptCallbackResponseEvent, ScriptDetachedEvent, }, - extractors::CallContext, - handler::{send_callback_response, send_script_errors}, + handler::{ScriptingHandler, send_callback_response, send_script_errors}, pipeline::RunProcessingPipelineOnce, - script::{DisplayProxy, ScriptAttachment}, }; use bevy_ecs::{system::Command, world::World}; use bevy_log::trace; use bevy_mod_scripting_bindings::{ScriptValue, WorldGuard}; +use bevy_mod_scripting_display::DisplayProxy; +use bevy_mod_scripting_script::ScriptAttachment; use parking_lot::Mutex; /// Runs a callback on the script with the given ID if it exists @@ -66,12 +67,15 @@ impl RunScriptCallback

{ self, guard: WorldGuard, ctxt: Arc>, + script_callbacks: ScriptCallbacks

, ) -> Result { let mut ctxt_guard = ctxt.lock(); - let result = ctxt_guard.call_context_dynamic( + let result = P::handle( + self.args, &self.attachment, &self.callback, - self.args, + &mut ctxt_guard, + script_callbacks, guard.clone(), ); let result = result.map_err(|e| { @@ -109,6 +113,7 @@ impl RunScriptCallback

{ self, guard: WorldGuard, script_contexts: ScriptContext

, + script_callbacks: ScriptCallbacks

, ) -> Result { let script_contexts = script_contexts.read(); let ctxt = script_contexts.get_context(&self.attachment); @@ -125,7 +130,7 @@ impl RunScriptCallback

{ } }; - self.run_with_context(guard, ctxt.clone()) + self.run_with_context(guard, ctxt.clone(), script_callbacks) } /// Equivalent to running the command, but also returns the result of the callback. @@ -133,8 +138,9 @@ impl RunScriptCallback

{ /// The returned errors will NOT be sent as events or printed pub fn run(self, world: &mut World) -> Result { let script_contexts = world.get_resource_or_init::>().clone(); + let script_callbacks = world.get_resource_or_init::>().clone(); let guard = WorldGuard::new_exclusive(world); - self.run_with_contexts(guard, script_contexts) + self.run_with_contexts(guard, script_contexts, script_callbacks) } } diff --git a/crates/bevy_mod_scripting_core/src/context.rs b/crates/bevy_mod_scripting_core/src/context.rs index 57e9d8fcaa..18bc6ccee2 100644 --- a/crates/bevy_mod_scripting_core/src/context.rs +++ b/crates/bevy_mod_scripting_core/src/context.rs @@ -1,16 +1,21 @@ //! Traits and types for managing script contexts. +use std::any::Any; + use bevy_ecs::world::WorldId; -use bevy_mod_scripting_bindings::{InteropError, ThreadWorldContainer, WorldContainer, WorldGuard}; +use bevy_mod_scripting_bindings::{ + InteropError, ThreadScriptContext, ThreadWorldContainer, WorldGuard, +}; +use bevy_mod_scripting_script::ScriptAttachment; -use crate::{IntoScriptPluginParams, extractors::GetPluginFor, script::ScriptAttachment}; +use crate::IntoScriptPluginParams; /// A trait that all script contexts must implement. /// /// Contexts are not required to be `Sync` as they are internally stored behind a `Mutex` but they must satisfy `Send` so they can be /// freely sent between threads. -pub trait Context: 'static + Send + GetPluginFor {} -impl Context for T {} +pub trait Context: 'static + Send + Any {} +impl Context for T {} /// Initializer run once after creating a context but before executing it for the first time as well as after re-loading the script pub type ContextInitializer

= @@ -63,7 +68,10 @@ impl ScriptingLoader

for P { ) -> Result { WorldGuard::with_existing_static_guard(world.clone(), |world| { let world_id = world.id(); - ThreadWorldContainer.set_world(world)?; + ThreadWorldContainer.set_context(ThreadScriptContext { + world, + attachment: attachment.clone(), + })?; Self::context_loader()(attachment, content, world_id) }) } @@ -76,7 +84,10 @@ impl ScriptingLoader

for P { ) -> Result<(), InteropError> { WorldGuard::with_existing_static_guard(world, |world| { let world_id = world.id(); - ThreadWorldContainer.set_world(world)?; + ThreadWorldContainer.set_context(ThreadScriptContext { + world, + attachment: attachment.clone(), + })?; Self::context_reloader()(attachment, content, previous_context, world_id) }) } diff --git a/crates/bevy_mod_scripting_core/src/event.rs b/crates/bevy_mod_scripting_core/src/event.rs index ad104da2bd..86667e589b 100644 --- a/crates/bevy_mod_scripting_core/src/event.rs +++ b/crates/bevy_mod_scripting_core/src/event.rs @@ -6,12 +6,13 @@ use ::{bevy_asset::Handle, bevy_ecs::entity::Entity, bevy_reflect::Reflect}; use bevy_ecs::event::Event; use bevy_mod_scripting_asset::Language; use bevy_mod_scripting_bindings::ScriptValue; +use bevy_mod_scripting_script::ScriptAttachment; use parking_lot::Mutex; use crate::{ IntoScriptPluginParams, error::ScriptError, - script::{ScriptAttachment, ScriptContext, ScriptId}, + script::{ScriptContext, ScriptId}, }; /// An error coming from a script @@ -428,7 +429,7 @@ mod test { use crate::{ config::{GetPluginThreadConfig, ScriptingPluginConfiguration}, event::Recipients, - script::{ContextPolicy, ScriptAttachment, ScriptContext}, + script::{ContextPolicy, ScriptContext}, }; #[test] diff --git a/crates/bevy_mod_scripting_core/src/extractors.rs b/crates/bevy_mod_scripting_core/src/extractors.rs index 785ef8fda2..4973e199c6 100644 --- a/crates/bevy_mod_scripting_core/src/extractors.rs +++ b/crates/bevy_mod_scripting_core/src/extractors.rs @@ -2,13 +2,6 @@ //! //! These are designed to be used to pipe inputs into other systems which require them, while handling any configuration erorrs nicely. -use crate::{ - IntoScriptPluginParams, - context::Context, - event::{CallbackLabel, IntoCallbackLabel}, - handler::ScriptingHandler, - script::ScriptAttachment, -}; use bevy_ecs::{ archetype::Archetype, component::{ComponentId, Tick}, @@ -17,54 +10,10 @@ use bevy_ecs::{ system::{SystemMeta, SystemParam, SystemParamValidationError}, world::{DeferredWorld, World, unsafe_world_cell::UnsafeWorldCell}, }; -use bevy_mod_scripting_bindings::{ - InteropError, WorldAccessGuard, WorldGuard, access_map::ReflectAccessId, - script_value::ScriptValue, -}; +use bevy_mod_scripting_bindings::{WorldAccessGuard, WorldGuard, access_map::ReflectAccessId}; use fixedbitset::FixedBitSet; -/// A reverse mapping from plugin context types to their plugin types. -/// Useful in implementing generic traits on context types. -pub trait GetPluginFor { - /// The plugin type associated with this context type - type P: IntoScriptPluginParams; -} - -/// A utility trait extending arbitrary script contexts with the ability to call callbacks on themselves using their -/// plugin handler function. -pub trait CallContext { - /// Invoke a callback on this context - fn call_context_dynamic( - &mut self, - context_key: &ScriptAttachment, - label: &CallbackLabel, - payload: Vec, - guard: WorldGuard<'_>, - ) -> Result; - - /// Invoke a callback on this context - fn call_context( - &mut self, - context_key: &ScriptAttachment, - payload: Vec, - guard: WorldGuard<'_>, - ) -> Result { - self.call_context_dynamic(context_key, &C::into_callback_label(), payload, guard) - } -} - -impl CallContext for C { - fn call_context_dynamic( - &mut self, - context_key: &ScriptAttachment, - label: &CallbackLabel, - payload: Vec, - guard: WorldGuard<'_>, - ) -> Result { - C::P::handle(payload, context_key, label, self, guard) - } -} /// A wrapper around a world which pre-populates access, to safely co-exist with other system params, /// acts exactly like `&mut World` so this should be your only top-level system param /// @@ -219,6 +168,7 @@ pub(crate) fn get_all_access_ids(access: &Access) -> Vec<(ReflectAc mod test { use crate::config::{GetPluginThreadConfig, ScriptingPluginConfiguration}; use bevy_ecs::resource::Resource; + use bevy_mod_scripting_bindings::ScriptValue; use test_utils::make_test_plugin; use { diff --git a/crates/bevy_mod_scripting_core/src/handler.rs b/crates/bevy_mod_scripting_core/src/handler.rs index 84f1fba314..631e3d15b7 100644 --- a/crates/bevy_mod_scripting_core/src/handler.rs +++ b/crates/bevy_mod_scripting_core/src/handler.rs @@ -1,21 +1,22 @@ //! Contains the logic for handling script callback events use bevy_ecs::world::WorldId; use bevy_mod_scripting_bindings::{ - InteropError, ScriptValue, ThreadWorldContainer, WorldAccessGuard, WorldContainer, WorldGuard, + InteropError, ScriptValue, ThreadScriptContext, ThreadWorldContainer, WorldAccessGuard, + WorldGuard, }; -use bevy_mod_scripting_display::WithTypeInfo; +use bevy_mod_scripting_display::{DisplayProxy, WithTypeInfo}; +use bevy_mod_scripting_script::ScriptAttachment; -use crate::extractors::CallContext; -use crate::script::DisplayProxy; use crate::{ IntoScriptPluginParams, + callbacks::ScriptCallbacks, error::ScriptError, event::{ CallbackLabel, IntoCallbackLabel, ScriptCallbackEvent, ScriptCallbackResponseEvent, ScriptErrorEvent, }, extractors::WithWorldGuard, - script::{ScriptAttachment, ScriptContext}, + script::ScriptContext, }; use { bevy_ecs::{ @@ -47,6 +48,7 @@ pub trait ScriptingHandler { context_key: &ScriptAttachment, callback: &CallbackLabel, script_ctxt: &mut P::C, + script_callbacks: ScriptCallbacks

, world: WorldGuard, ) -> Result; } @@ -55,15 +57,29 @@ impl ScriptingHandler

for P { /// Calls the handler function while providing the necessary thread local context fn handle( args: Vec, - context_key: &ScriptAttachment, + attachment: &ScriptAttachment, callback: &CallbackLabel, script_ctxt: &mut P::C, + script_callbacks: ScriptCallbacks

, 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, world_id) + ThreadWorldContainer.set_context(ThreadScriptContext { + world, + attachment: attachment.clone(), + })?; + let callbacks = script_callbacks.callbacks.read(); + if let Some(callback) = callbacks + .get(&(attachment.clone(), callback.to_string())) + .cloned() + { + drop(callbacks); + callback(args, script_ctxt, world_id) + } else { + drop(callbacks); + Self::handler()(args, attachment, callback, script_ctxt, world_id) + } }) } } @@ -79,12 +95,14 @@ pub fn event_handler( // we wrap the inner event handler, so that we can guarantee that the handler context is released statically { let script_context = world.get_resource_or_init::>().clone(); + let script_callbacks = world.get_resource_or_init::>().clone(); let (event_cursor, mut guard) = state.get_mut(world); let (guard, _) = guard.get_mut(); event_handler_inner::

( L::into_callback_label(), event_cursor, script_context, + script_callbacks, guard, ); } @@ -101,6 +119,7 @@ pub(crate) fn event_handler_inner( callback_label: CallbackLabel, mut event_cursor: Local>, script_context: ScriptContext

, + script_callbacks: ScriptCallbacks

, guard: WorldAccessGuard, ) { let mut errors = Vec::default(); @@ -130,10 +149,13 @@ pub(crate) fn event_handler_inner( for (attachment, ctxt) in recipients { let mut ctxt = ctxt.lock(); - let call_result = ctxt.call_context_dynamic( + + let call_result = P::handle( + event.args.clone(), &attachment, &callback_label, - event.args.clone(), + &mut ctxt, + script_callbacks.clone(), guard.clone(), ); let call_result = call_result.map_err(|e| { diff --git a/crates/bevy_mod_scripting_core/src/lib.rs b/crates/bevy_mod_scripting_core/src/lib.rs index 7fbfe3185b..0067463b84 100644 --- a/crates/bevy_mod_scripting_core/src/lib.rs +++ b/crates/bevy_mod_scripting_core/src/lib.rs @@ -3,6 +3,7 @@ //! Contains language agnostic systems and types for handling scripting in bevy. use crate::{ + callbacks::ScriptCallbacksPlugin, config::{GetPluginThreadConfig, ScriptingPluginConfiguration}, context::{ContextLoadFn, ContextReloadFn}, event::ScriptErrorEvent, @@ -30,6 +31,7 @@ use handler::HandlerFn; use runtime::{Runtime, RuntimeInitializer}; use script::{ContextPolicy, ScriptComponent, ScriptContext}; +pub mod callbacks; pub mod commands; pub mod config; pub mod context; @@ -173,7 +175,10 @@ impl Plugin for ScriptingPlugin

{ app.insert_resource(ScriptContext::

::new(self.context_policy.clone())); app.register_asset_loader(ScriptAssetLoader::new(config.language_extensions)); - app.add_plugins(self.processing_pipeline_plugin.clone()); + app.add_plugins(( + self.processing_pipeline_plugin.clone(), + ScriptCallbacksPlugin::

::default(), + )); register_types(app); } diff --git a/crates/bevy_mod_scripting_core/src/pipeline/hooks.rs b/crates/bevy_mod_scripting_core/src/pipeline/hooks.rs index 50ba89d223..7f1d846153 100644 --- a/crates/bevy_mod_scripting_core/src/pipeline/hooks.rs +++ b/crates/bevy_mod_scripting_core/src/pipeline/hooks.rs @@ -1,6 +1,7 @@ use bevy_mod_scripting_bindings::ScriptValue; use crate::{ + callbacks::ScriptCallbacks, commands::RunScriptCallback, event::{IntoCallbackLabel, OnScriptLoaded, OnScriptReloaded, OnScriptUnloaded}, }; @@ -20,6 +21,7 @@ impl TransitionListener> for OnLoa ) -> Result<(), ScriptError> { let world_id = world.id(); let emit_responses = P::readonly_configuration(world_id).emit_responses; + let callbacks = world.get_resource_or_init::>().clone(); let guard = WorldGuard::new_exclusive(world); RunScriptCallback::

::new( @@ -28,7 +30,7 @@ impl TransitionListener> for OnLoa vec![], emit_responses, ) - .run_with_context(guard.clone(), state.context.clone()) + .run_with_context(guard.clone(), state.context.clone(), callbacks) .map(|_| ()) } } @@ -45,6 +47,7 @@ impl TransitionListener> ) -> Result<(), ScriptError> { let world_id = world.id(); let emit_responses = P::readonly_configuration(world_id).emit_responses; + let callbacks = world.get_resource_or_init::>().clone(); let guard = WorldGuard::new_exclusive(world); let v = RunScriptCallback::

::new( @@ -53,7 +56,7 @@ impl TransitionListener> vec![], emit_responses, ) - .run_with_context(guard.clone(), state.existing_context.clone())?; + .run_with_context(guard.clone(), state.existing_context.clone(), callbacks)?; ctxt.insert(UNLOADED_SCRIPT_STATE_KEY, v); Ok(()) } @@ -71,6 +74,7 @@ impl TransitionListener> ) -> Result<(), ScriptError> { let world_id = world.id(); let emit_responses = P::readonly_configuration(world_id).emit_responses; + let callbacks = world.get_resource_or_init::>().clone(); let guard = WorldGuard::new_exclusive(world); let v = RunScriptCallback::

::new( @@ -79,7 +83,7 @@ impl TransitionListener> vec![], emit_responses, ) - .run_with_context(guard.clone(), state.existing_context.clone())?; + .run_with_context(guard.clone(), state.existing_context.clone(), callbacks)?; ctxt.insert(UNLOADED_SCRIPT_STATE_KEY, v); Ok(()) } @@ -95,6 +99,7 @@ impl TransitionListener> for OnRel ) -> Result<(), ScriptError> { let world_id = world.id(); let emit_responses = P::readonly_configuration(world_id).emit_responses; + let callbacks = world.get_resource_or_init::>().clone(); let guard = WorldGuard::new_exclusive(world); if state.is_new_context { @@ -110,7 +115,7 @@ impl TransitionListener> for OnRel vec![unload_state], emit_responses, ) - .run_with_context(guard.clone(), state.context.clone()) + .run_with_context(guard.clone(), state.context.clone(), callbacks) .map(|_| ()) } } diff --git a/crates/bevy_mod_scripting_core/src/pipeline/machines.rs b/crates/bevy_mod_scripting_core/src/pipeline/machines.rs index ed2709be3f..1849daaa0b 100644 --- a/crates/bevy_mod_scripting_core/src/pipeline/machines.rs +++ b/crates/bevy_mod_scripting_core/src/pipeline/machines.rs @@ -8,6 +8,7 @@ use std::{ use bevy_log::debug; use bevy_mod_scripting_bindings::InteropError; +use bevy_mod_scripting_script::ScriptAttachment; use bevy_platform::collections::HashMap; use super::*; diff --git a/crates/bevy_mod_scripting_core/src/pipeline/mod.rs b/crates/bevy_mod_scripting_core/src/pipeline/mod.rs index 821c485f9f..cf7dc2936e 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::{ScriptAttachment, ScriptContext, ScriptId}, + script::{ScriptContext, ScriptId}, }; mod hooks; @@ -351,6 +351,7 @@ mod test { use bevy_ecs::{entity::Entity, system::SystemState, world::FromWorld}; use bevy_mod_scripting_asset::Language; use bevy_mod_scripting_bindings::ScriptValue; + use bevy_mod_scripting_script::ScriptAttachment; use test_utils::make_test_plugin; use crate::config::{GetPluginThreadConfig, ScriptingPluginConfiguration}; 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 026c42a1fd..640d32abfe 100644 --- a/crates/bevy_mod_scripting_core/src/script/context_key.rs +++ b/crates/bevy_mod_scripting_core/src/script/context_key.rs @@ -1,85 +1,11 @@ use std::fmt; use bevy_ecs::entity::Entity; +use bevy_mod_scripting_display::DisplayProxy; use super::*; use crate::ScriptAsset; -/// Specifies a unique attachment of a script. These attachments are mapped to [`ContextKey`]'s depending on the context policy used. -#[derive(Debug, Hash, Clone, PartialEq, Eq, Reflect)] -pub enum ScriptAttachment { - /// a script attached to an entity, with an optional domain. By default selecting a domain will put the context of this script on a per-domain basis. - EntityScript(Entity, Handle), - /// a static script, with an optional domain. By default selecting a domain will put the context of this script on a per-domain basis. - StaticScript(Handle), -} - -impl std::fmt::Display for ScriptAttachment { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ScriptAttachment::EntityScript(entity, script) => { - write!( - f, - "EntityScript(entity: {}, script: {})", - entity, - script.display(), - ) - } - ScriptAttachment::StaticScript(script) => { - write!(f, "StaticScript(script: {})", script.display()) - } - } - } -} - -impl ScriptAttachment { - /// Returns the script handle. - pub fn script(&self) -> Handle { - match self { - ScriptAttachment::EntityScript(_, script) => script.clone(), - ScriptAttachment::StaticScript(script) => script.clone(), - } - } - - /// Returns a mutable reference to the underlying script handle. - pub fn script_mut(&mut self) -> &mut Handle { - match self { - ScriptAttachment::EntityScript(_, script) => script, - ScriptAttachment::StaticScript(script) => script, - } - } - - /// Returns the entity if it exists. - pub fn entity(&self) -> Option { - match self { - ScriptAttachment::EntityScript(entity, _) => Some(*entity), - ScriptAttachment::StaticScript(_) => None, - } - } - - /// Downcasts any script handles into weak handles. - pub fn into_weak(self) -> Self { - match self { - ScriptAttachment::EntityScript(entity, script) => { - ScriptAttachment::EntityScript(entity, script.clone_weak()) - } - ScriptAttachment::StaticScript(script) => { - ScriptAttachment::StaticScript(script.clone_weak()) - } - } - } - - /// 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 { fn from(val: ScriptAttachment) -> Self { match val { diff --git a/crates/bevy_mod_scripting_core/src/script/mod.rs b/crates/bevy_mod_scripting_core/src/script/mod.rs index e7b42faa56..f8d9732da8 100644 --- a/crates/bevy_mod_scripting_core/src/script/mod.rs +++ b/crates/bevy_mod_scripting_core/src/script/mod.rs @@ -2,14 +2,13 @@ use std::{ collections::{HashMap, HashSet}, - fmt, ops::Deref, }; use crate::event::{ScriptAttachedEvent, ScriptDetachedEvent}; use ::{ - bevy_asset::{Asset, AssetId, Handle}, + bevy_asset::{AssetId, Handle}, bevy_ecs::{ component::HookContext, entity::Entity, prelude::ReflectComponent, resource::Resource, world::DeferredWorld, @@ -21,6 +20,7 @@ mod context_key; mod script_context; use bevy_ecs::component::Component; use bevy_mod_scripting_asset::ScriptAsset; +use bevy_mod_scripting_script::ScriptAttachment; pub use context_key::*; pub use script_context::*; @@ -29,48 +29,6 @@ pub use script_context::*; /// I.e. an asset with the path `path/to/asset.ext` will have the script id `path/to/asset.ext` pub type ScriptId = AssetId; -/// Display the path of a script or its asset ID. -#[doc(hidden)] -pub struct HandleDisplay<'a, T: Asset>(&'a Handle); - -impl<'a, A: Asset> fmt::Display for HandleDisplay<'a, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(path) = self.0.path() { - write!(f, "path {path}") - } else { - write!(f, "id {}", self.0.id()) - } - } -} - -impl<'a, A: Asset> fmt::Debug for HandleDisplay<'a, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(path) = self.0.path() { - write!(f, "path {path:?}") - } else { - write!(f, "id {:?}", self.0.id()) - } - } -} - -/// Make a type display-able. -pub trait DisplayProxy { - /// The type that does the displaying. - type D<'a>: fmt::Display + fmt::Debug - where - Self: 'a; - /// Return a display-able reference. - fn display<'a>(&'a self) -> Self::D<'a>; -} - -impl DisplayProxy for Handle { - type D<'a> = HandleDisplay<'a, A>; - - fn display<'a>(&'a self) -> Self::D<'a> { - HandleDisplay(self) - } -} - #[derive(Component, Reflect, Clone, Default, Debug)] #[reflect(Component)] #[component(on_remove=Self::on_remove, on_add=Self::on_add)] diff --git a/crates/bevy_mod_scripting_core/src/script/script_context.rs b/crates/bevy_mod_scripting_core/src/script/script_context.rs index 3a6e0d8faf..fa622a077a 100644 --- a/crates/bevy_mod_scripting_core/src/script/script_context.rs +++ b/crates/bevy_mod_scripting_core/src/script/script_context.rs @@ -1,5 +1,6 @@ use std::{hash::Hash, sync::Arc}; +use bevy_mod_scripting_script::ScriptAttachment; use parking_lot::{Mutex, RwLock}; use super::*; diff --git a/crates/bevy_mod_scripting_core/src/script_system.rs b/crates/bevy_mod_scripting_core/src/script_system.rs index 313ba43847..9ad6b54641 100644 --- a/crates/bevy_mod_scripting_core/src/script_system.rs +++ b/crates/bevy_mod_scripting_core/src/script_system.rs @@ -1,10 +1,8 @@ //! everything to do with dynamically added script systems use crate::{ - IntoScriptPluginParams, - event::CallbackLabel, - extractors::{CallContext, get_all_access_ids}, - script::{ScriptAttachment, ScriptContext}, + IntoScriptPluginParams, callbacks::ScriptCallbacks, event::CallbackLabel, + extractors::get_all_access_ids, handler::ScriptingHandler, script::ScriptContext, }; use ::{ @@ -33,6 +31,7 @@ use bevy_mod_scripting_bindings::{ ScriptQueryBuilder, ScriptQueryResult, ScriptResourceRegistration, Val, WorldAccessGuard, WorldGuard, }; +use bevy_mod_scripting_script::ScriptAttachment; use bevy_system_reflection::{ReflectSchedule, ReflectSystem}; use std::{ any::TypeId, borrow::Cow, collections::HashSet, hash::Hash, marker::PhantomData, ops::Deref, @@ -203,6 +202,7 @@ struct ScriptSystemState { callback_label: CallbackLabel, system_params: Vec, script_contexts: ScriptContext

, + script_callbacks: ScriptCallbacks

, } /// Equivalent of [`SystemParam`] but for dynamic systems, these are the kinds of things @@ -387,10 +387,12 @@ impl System for DynamicScriptSystem

{ if let Some(context) = script_context.get_context(&self.target_attachment) { let mut context = context.lock(); - let result = context.call_context_dynamic( + let result = P::handle( + payload, &self.target_attachment, &state.callback_label, - payload, + &mut context, + state.script_callbacks.clone(), guard.clone(), ); drop(context); @@ -493,6 +495,7 @@ impl System for DynamicScriptSystem

{ callback_label: self.name.to_string().into(), system_params, script_contexts: world.get_resource_or_init::>().clone(), + script_callbacks: world.get_resource_or_init::>().clone(), }) } diff --git a/crates/bevy_mod_scripting_display/src/handle.rs b/crates/bevy_mod_scripting_display/src/handle.rs new file mode 100644 index 0000000000..87fd1add50 --- /dev/null +++ b/crates/bevy_mod_scripting_display/src/handle.rs @@ -0,0 +1,44 @@ +use bevy_asset::{Asset, Handle}; +use std::fmt; + +/// Display the path of a script or its asset ID. +#[doc(hidden)] +pub struct HandleDisplay<'a, T: Asset>(&'a Handle); + +impl<'a, A: Asset> fmt::Display for HandleDisplay<'a, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(path) = self.0.path() { + write!(f, "path {path}") + } else { + write!(f, "id {}", self.0.id()) + } + } +} + +impl<'a, A: Asset> fmt::Debug for HandleDisplay<'a, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(path) = self.0.path() { + write!(f, "path {path:?}") + } else { + write!(f, "id {:?}", self.0.id()) + } + } +} + +/// Make a type display-able. +pub trait DisplayProxy { + /// The type that does the displaying. + type D<'a>: fmt::Display + fmt::Debug + where + Self: 'a; + /// Return a display-able reference. + fn display<'a>(&'a self) -> Self::D<'a>; +} + +impl DisplayProxy for Handle { + type D<'a> = HandleDisplay<'a, A>; + + fn display<'a>(&'a self) -> Self::D<'a> { + HandleDisplay(self) + } +} diff --git a/crates/bevy_mod_scripting_display/src/lib.rs b/crates/bevy_mod_scripting_display/src/lib.rs index 95e72b92ec..7a873dfa59 100644 --- a/crates/bevy_mod_scripting_display/src/lib.rs +++ b/crates/bevy_mod_scripting_display/src/lib.rs @@ -4,9 +4,10 @@ use std::{ any::{Any, TypeId}, ops::Deref, }; +mod handle; mod impls; mod printer; -pub use printer::*; +pub use {handle::*, printer::*}; use bevy_ecs::{ component::{ComponentId, ComponentInfo}, diff --git a/crates/bevy_mod_scripting_functions/Cargo.toml b/crates/bevy_mod_scripting_functions/Cargo.toml index 6127883653..fb2c1a13b7 100644 --- a/crates/bevy_mod_scripting_functions/Cargo.toml +++ b/crates/bevy_mod_scripting_functions/Cargo.toml @@ -46,6 +46,7 @@ bevy_mod_scripting_core = { workspace = true } bevy_mod_scripting_bindings = { workspace = true } bevy_mod_scripting_display = { workspace = true } bevy_mod_scripting_asset = { workspace = true } +bevy_mod_scripting_script = { workspace = true } bevy_mod_scripting_derive = { workspace = true } bevy_mod_scripting_lua = { path = "../languages/bevy_mod_scripting_lua", optional = true, version = "0.16.0" } bevy_mod_scripting_rhai = { path = "../languages/bevy_mod_scripting_rhai", optional = true, version = "0.16.0" } diff --git a/crates/bevy_mod_scripting_functions/src/core.rs b/crates/bevy_mod_scripting_functions/src/core.rs index c3ffd8584a..7c5c3227e2 100644 --- a/crates/bevy_mod_scripting_functions/src/core.rs +++ b/crates/bevy_mod_scripting_functions/src/core.rs @@ -1,6 +1,7 @@ //! Contains functions defined by the [`bevy_mod_scripting_core`] crate use bevy_mod_scripting_asset::ScriptAsset; +use bevy_mod_scripting_script::ScriptAttachment; use bevy_platform::collections::HashMap; use std::ops::Deref; @@ -11,7 +12,6 @@ use bevy_mod_scripting_bindings::{ DynamicScriptFunctionMut, FunctionInfo, GlobalNamespace, InteropError, PartialReflectExt, ReflectReference, ScriptComponentRegistration, ScriptQueryBuilder, ScriptQueryResult, ScriptResourceRegistration, ScriptTypeRegistration, ThreadWorldContainer, Union, - WorldContainer, function::{ from::{Ref, Val}, from_ref::FromScriptRef, @@ -20,10 +20,7 @@ use bevy_mod_scripting_bindings::{ }, script_value::ScriptValue, }; -use bevy_mod_scripting_core::{ - script::ScriptAttachment, - script_system::{ManageScriptSystems, ScriptSystemBuilder}, -}; +use bevy_mod_scripting_core::script_system::{ManageScriptSystems, ScriptSystemBuilder}; use bevy_mod_scripting_derive::script_bindings; use bevy_mod_scripting_display::{OrFakeId, WithTypeInfo}; use bevy_reflect::PartialReflect; @@ -832,7 +829,7 @@ impl ReflectReference { let iter_function = move || { // world is not thread safe, we can't capture it in the closure // or it will also be non-thread safe - let world = ThreadWorldContainer.try_get_world()?; + let world = ThreadWorldContainer.try_get_context()?.world; if len == 0 { return Ok(ScriptValue::Unit); } diff --git a/crates/bevy_mod_scripting_script/Cargo.toml b/crates/bevy_mod_scripting_script/Cargo.toml new file mode 100644 index 0000000000..f3a6599e37 --- /dev/null +++ b/crates/bevy_mod_scripting_script/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "bevy_mod_scripting_script" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +readme.workspace = true + +[dependencies] +bevy_mod_scripting_asset = { workspace = true } +bevy_mod_scripting_display = { workspace = true } +bevy_ecs = { workspace = true } +bevy_reflect = { workspace = true } +bevy_asset = { workspace = true } + +[lints] +workspace = true diff --git a/crates/bevy_mod_scripting_script/src/lib.rs b/crates/bevy_mod_scripting_script/src/lib.rs new file mode 100644 index 0000000000..ecb62e5d76 --- /dev/null +++ b/crates/bevy_mod_scripting_script/src/lib.rs @@ -0,0 +1,83 @@ +//! Contains definitions for script related structures + +use bevy_asset::Handle; +use bevy_ecs::entity::Entity; +use bevy_mod_scripting_asset::ScriptAsset; +use bevy_mod_scripting_display::DisplayProxy; +use bevy_reflect::Reflect; +use std::fmt; + +/// Specifies a unique attachment of a script. These attachments are mapped to [`ContextKey`]'s depending on the context policy used. +#[derive(Debug, Hash, Clone, PartialEq, Eq, Reflect)] +pub enum ScriptAttachment { + /// a script attached to an entity, with an optional domain. By default selecting a domain will put the context of this script on a per-domain basis. + EntityScript(Entity, Handle), + /// a static script, with an optional domain. By default selecting a domain will put the context of this script on a per-domain basis. + StaticScript(Handle), +} + +impl std::fmt::Display for ScriptAttachment { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ScriptAttachment::EntityScript(entity, script) => { + write!( + f, + "EntityScript(entity: {}, script: {})", + entity, + script.display(), + ) + } + ScriptAttachment::StaticScript(script) => { + write!(f, "StaticScript(script: {})", script.display()) + } + } + } +} + +impl ScriptAttachment { + /// Returns the script handle. + pub fn script(&self) -> Handle { + match self { + ScriptAttachment::EntityScript(_, script) => script.clone(), + ScriptAttachment::StaticScript(script) => script.clone(), + } + } + + /// Returns a mutable reference to the underlying script handle. + pub fn script_mut(&mut self) -> &mut Handle { + match self { + ScriptAttachment::EntityScript(_, script) => script, + ScriptAttachment::StaticScript(script) => script, + } + } + + /// Returns the entity if it exists. + pub fn entity(&self) -> Option { + match self { + ScriptAttachment::EntityScript(entity, _) => Some(*entity), + ScriptAttachment::StaticScript(_) => None, + } + } + + /// Downcasts any script handles into weak handles. + pub fn into_weak(self) -> Self { + match self { + ScriptAttachment::EntityScript(entity, script) => { + ScriptAttachment::EntityScript(entity, script.clone_weak()) + } + ScriptAttachment::StaticScript(script) => { + ScriptAttachment::StaticScript(script.clone_weak()) + } + } + } + + /// 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(_, _)) + } +} diff --git a/crates/languages/bevy_mod_scripting_lua/Cargo.toml b/crates/languages/bevy_mod_scripting_lua/Cargo.toml index e9801676b2..7740b36ac4 100644 --- a/crates/languages/bevy_mod_scripting_lua/Cargo.toml +++ b/crates/languages/bevy_mod_scripting_lua/Cargo.toml @@ -46,6 +46,7 @@ bevy_mod_scripting_core = { workspace = true } bevy_mod_scripting_display = { workspace = true } bevy_mod_scripting_bindings = { workspace = true } bevy_mod_scripting_asset = { workspace = true } +bevy_mod_scripting_script = { workspace = true } mlua = { workspace = true, features = ["vendored", "send", "macros"] } profiling = { workspace = true } diff --git a/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs b/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs index 23f1a85643..dc676b48ad 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs @@ -1,8 +1,7 @@ use std::any::TypeId; use bevy_mod_scripting_bindings::{ - ReflectReference, ThreadWorldContainer, WorldContainer, error::InteropError, - script_value::ScriptValue, + ReflectReference, ThreadWorldContainer, error::InteropError, script_value::ScriptValue, }; use bevy_mod_scripting_display::OrFakeId; use mlua::{ExternalError, MetaMethod, UserData, UserDataMethods}; @@ -41,8 +40,9 @@ impl UserData for LuaReflectReference { |_, (self_, key): (LuaReflectReference, LuaScriptValue)| { profiling::function_scope!("MetaMethod::Index"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let self_: ReflectReference = self_.into(); let type_id = self_ .tail_type_id(world.clone()) @@ -80,8 +80,9 @@ impl UserData for LuaReflectReference { |_, (self_, key, value): (LuaReflectReference, LuaScriptValue, LuaScriptValue)| { profiling::function_scope!("MetaMethod::NewIndex"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let self_: ReflectReference = self_.into(); let key: ScriptValue = key.into(); let value: ScriptValue = value.into(); @@ -103,8 +104,9 @@ impl UserData for LuaReflectReference { |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { profiling::function_scope!("MetaMethod::Sub"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); let target_type_id = self_ @@ -124,8 +126,9 @@ impl UserData for LuaReflectReference { |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { profiling::function_scope!("MetaMethod::Add"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); let target_type_id = self_ @@ -145,8 +148,9 @@ impl UserData for LuaReflectReference { |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { profiling::function_scope!("MetaMethod::Mul"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); let target_type_id = self_ @@ -166,8 +170,9 @@ impl UserData for LuaReflectReference { |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { profiling::function_scope!("MetaMethod::Div"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); let target_type_id = self_ @@ -187,8 +192,9 @@ impl UserData for LuaReflectReference { |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { profiling::function_scope!("MetaMethod::Mod"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); let target_type_id = self_ @@ -206,8 +212,9 @@ impl UserData for LuaReflectReference { m.add_meta_function(MetaMethod::Unm, |_, self_: LuaReflectReference| { profiling::function_scope!("MetaMethod::Unm"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let self_: ReflectReference = self_.into(); let target_type_id = self_ .tail_type_id(world.clone()) @@ -225,8 +232,9 @@ impl UserData for LuaReflectReference { |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { profiling::function_scope!("MetaMethod::Pow"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); let target_type_id = self_ @@ -246,8 +254,9 @@ impl UserData for LuaReflectReference { |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { profiling::function_scope!("MetaMethod::Eq"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); let target_type_id = self_ @@ -267,8 +276,9 @@ impl UserData for LuaReflectReference { |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { profiling::function_scope!("MetaMethod::Lt"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); let target_type_id = self_ @@ -286,8 +296,9 @@ impl UserData for LuaReflectReference { m.add_meta_function(MetaMethod::Len, |_lua, self_: LuaScriptValue| { profiling::function_scope!("MetaMethod::Len"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let script_value: ScriptValue = self_.into(); Ok(match script_value { ScriptValue::Reference(r) => r.len(world).map_err(IntoMluaError::to_lua_error)?, @@ -307,8 +318,9 @@ impl UserData for LuaReflectReference { // let mut iter_func = lookup_dynamic_function_typed::(l, "iter") // .expect("No iter function registered"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let iter_func = world .lookup_function([TypeId::of::()], "iter") @@ -327,8 +339,9 @@ impl UserData for LuaReflectReference { m.add_meta_function(MetaMethod::ToString, |_, self_: LuaReflectReference| { profiling::function_scope!("MetaMethod::ToString"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let reflect_reference: ReflectReference = self_.into(); let func = world @@ -362,8 +375,9 @@ impl UserData for LuaStaticReflectReference { |_, (self_, key): (LuaStaticReflectReference, LuaScriptValue)| { profiling::function_scope!("MetaMethod::Index"); let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoMluaError::to_lua_error)?; + .try_get_context() + .map_err(IntoMluaError::to_lua_error)? + .world; let type_id = self_.0; let key: ScriptValue = key.into(); diff --git a/crates/languages/bevy_mod_scripting_lua/src/lib.rs b/crates/languages/bevy_mod_scripting_lua/src/lib.rs index f60c605684..39d771f9f4 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/lib.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/lib.rs @@ -1,5 +1,8 @@ //! Lua integration for the bevy_mod_scripting system. -use std::ops::{Deref, DerefMut}; +use std::{ + ops::{Deref, DerefMut}, + sync::Arc, +}; use ::{ bevy_app::Plugin, @@ -7,27 +10,28 @@ use ::{ bevy_ecs::{entity::Entity, world::World}, }; use bevy_app::App; -use bevy_ecs::world::WorldId; +use bevy_ecs::world::{Mut, WorldId}; use bevy_log::trace; use bevy_mod_scripting_asset::{Language, ScriptAsset}; use bevy_mod_scripting_bindings::{ - InteropError, PartialReflectExt, ThreadWorldContainer, WorldContainer, - function::namespace::Namespace, globals::AppScriptGlobalsRegistry, script_value::ScriptValue, + InteropError, PartialReflectExt, ThreadWorldContainer, function::namespace::Namespace, + globals::AppScriptGlobalsRegistry, script_value::ScriptValue, }; use bevy_mod_scripting_core::{ IntoScriptPluginParams, ScriptingPlugin, + callbacks::ScriptCallbacks, config::{GetPluginThreadConfig, ScriptingPluginConfiguration}, event::CallbackLabel, - extractors::GetPluginFor, make_plugin_config_static, - script::{ContextPolicy, ScriptAttachment}, + script::ContextPolicy, }; +use bevy_mod_scripting_script::ScriptAttachment; use bindings::{ reference::{LuaReflectReference, LuaStaticReflectReference}, script_value::LuaScriptValue, }; pub use mlua; -use mlua::{Function, IntoLua, Lua, MultiValue}; +use mlua::{Function, IntoLua, Lua, MultiValue, Variadic}; /// Bindings for lua. pub mod bindings; @@ -52,10 +56,6 @@ impl DerefMut for LuaContext { } } -impl GetPluginFor for LuaContext { - type P = LuaScriptingPlugin; -} - impl IntoScriptPluginParams for LuaScriptingPlugin { type C = LuaContext; type R = (); @@ -89,6 +89,51 @@ pub struct LuaScriptingPlugin { pub scripting_plugin: ScriptingPlugin, } +fn register_plugin_globals(lua: &mut Lua) -> Result<(), mlua::Error> { + lua.globals().set( + "register_callback", + lua.create_function(|_lua: &Lua, (callback, func): (String, Function)| { + let thread_ctxt = ThreadWorldContainer + .try_get_context() + .map_err(mlua::Error::external)?; + let world = thread_ctxt.world; + let attachment = thread_ctxt.attachment; + world + .with_resource_mut(|res: Mut>| { + let mut callbacks = res.callbacks.write(); + callbacks.insert( + (attachment.clone(), callback), + Arc::new( + move |args: Vec, + lua: &mut LuaContext, + world_id: WorldId| { + let pre_handling_callbacks = + LuaScriptingPlugin::readonly_configuration(world_id) + .pre_handling_callbacks; + + pre_handling_callbacks + .iter() + .try_for_each(|init| init(&attachment, lua))?; + + let args = args + .into_iter() + .map(LuaScriptValue) + .collect::>(); + + func.call::(args) + .map_err(IntoInteropError::to_bms_error) + .map(ScriptValue::from) + }, + ), + ) + }) + .map_err(mlua::Error::external)?; + Ok(()) + })?, + )?; + Ok(()) +} + impl Default for LuaScriptingPlugin { fn default() -> Self { LuaScriptingPlugin { @@ -98,19 +143,22 @@ impl Default for LuaScriptingPlugin { context_initializers: vec![ |_script_id, context| { // set the world global - context - .globals() + let globals = context.globals(); + + globals .set( "world", LuaStaticReflectReference(std::any::TypeId::of::()), ) .map_err(IntoInteropError::to_bms_error)?; + register_plugin_globals(context).map_err(IntoInteropError::to_bms_error)?; + Ok(()) }, |_script_id, context| { // set static globals - let world = ThreadWorldContainer.try_get_world()?; + let world = ThreadWorldContainer.try_get_context()?.world; let globals_registry = world.with_resource(|r: &AppScriptGlobalsRegistry| r.clone())?; let globals_registry = globals_registry.read(); @@ -157,7 +205,7 @@ impl Default for LuaScriptingPlugin { ], context_pre_handling_initializers: vec![|context_key, context| { // TODO: convert these to functions - let world = ThreadWorldContainer.try_get_world()?; + let world = ThreadWorldContainer.try_get_context()?.world; if let Some(entity) = context_key.entity() { context .globals() diff --git a/crates/languages/bevy_mod_scripting_rhai/Cargo.toml b/crates/languages/bevy_mod_scripting_rhai/Cargo.toml index 5b34fbac5a..d791c59c95 100644 --- a/crates/languages/bevy_mod_scripting_rhai/Cargo.toml +++ b/crates/languages/bevy_mod_scripting_rhai/Cargo.toml @@ -26,6 +26,7 @@ bevy_mod_scripting_core = { workspace = true } bevy_mod_scripting_display = { workspace = true } bevy_mod_scripting_bindings = { workspace = true } bevy_mod_scripting_asset = { workspace = true } +bevy_mod_scripting_script = { workspace = true } strum = { workspace = true, features = ["derive"] } parking_lot = { workspace = true } diff --git a/crates/languages/bevy_mod_scripting_rhai/src/bindings/reference.rs b/crates/languages/bevy_mod_scripting_rhai/src/bindings/reference.rs index a5799c3427..ecf80ce8f4 100644 --- a/crates/languages/bevy_mod_scripting_rhai/src/bindings/reference.rs +++ b/crates/languages/bevy_mod_scripting_rhai/src/bindings/reference.rs @@ -5,7 +5,7 @@ use std::{ use crate::IntoRhaiError; use bevy_mod_scripting_bindings::{ - ReflectReference, ScriptValue, ThreadWorldContainer, WorldContainer, error::InteropError, + ReflectReference, ScriptValue, ThreadWorldContainer, error::InteropError, function::script_function::DynamicScriptFunctionMut, }; use bevy_mod_scripting_display::OrFakeId; @@ -256,7 +256,7 @@ impl IntoIterator for RhaiReflectReference { fn into_iter(self) -> Self::IntoIter { let result = (|| { - let world = ThreadWorldContainer.try_get_world()?; + let world = ThreadWorldContainer.try_get_context()?.world; let iter_func = world .lookup_function([TypeId::of::()], "iter") @@ -295,8 +295,9 @@ impl CustomType for RhaiReflectReference { .with_name(std::any::type_name::()) .with_indexer_get(|self_: &mut Self, index: Dynamic| { let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoRhaiError::into_rhai_error)?; + .try_get_context() + .map_err(IntoRhaiError::into_rhai_error)? + .world; let self_ = &self_.0; let type_id = self_ .tail_type_id(world.clone()) @@ -332,8 +333,9 @@ impl CustomType for RhaiReflectReference { }) .with_indexer_set(|self_: &mut Self, index: Dynamic, value: Dynamic| { let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoRhaiError::into_rhai_error)?; + .try_get_context() + .map_err(IntoRhaiError::into_rhai_error)? + .world; let self_ = self_.0.clone(); let key = ScriptValue::from_dynamic(index)?; let value = ScriptValue::from_dynamic(value)?; @@ -351,8 +353,9 @@ impl CustomType for RhaiReflectReference { RhaiOperator::Sub.function_name(), |self_: Self, other: Dynamic| { let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoRhaiError::into_rhai_error)?; + .try_get_context() + .map_err(IntoRhaiError::into_rhai_error)? + .world; let self_: ReflectReference = self_.0.clone(); let other: ScriptValue = ScriptValue::from_dynamic(other)?; let target_type_id = self_ @@ -370,8 +373,9 @@ impl CustomType for RhaiReflectReference { RhaiOperator::Add.function_name(), |self_: Self, other: Dynamic| { let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoRhaiError::into_rhai_error)?; + .try_get_context() + .map_err(IntoRhaiError::into_rhai_error)? + .world; let self_: ReflectReference = self_.0.clone(); let other: ScriptValue = ScriptValue::from_dynamic(other)?; let target_type_id = self_ @@ -389,8 +393,9 @@ impl CustomType for RhaiReflectReference { RhaiOperator::Mul.function_name(), |self_: Self, other: Dynamic| { let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoRhaiError::into_rhai_error)?; + .try_get_context() + .map_err(IntoRhaiError::into_rhai_error)? + .world; let self_: ReflectReference = self_.0.clone(); let other: ScriptValue = ScriptValue::from_dynamic(other)?; let target_type_id = self_ @@ -408,8 +413,9 @@ impl CustomType for RhaiReflectReference { RhaiOperator::Div.function_name(), |self_: Self, other: Dynamic| { let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoRhaiError::into_rhai_error)?; + .try_get_context() + .map_err(IntoRhaiError::into_rhai_error)? + .world; let self_: ReflectReference = self_.0.clone(); let other: ScriptValue = ScriptValue::from_dynamic(other)?; let target_type_id = self_ @@ -427,8 +433,9 @@ impl CustomType for RhaiReflectReference { RhaiOperator::Mod.function_name(), |self_: Self, other: Dynamic| { let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoRhaiError::into_rhai_error)?; + .try_get_context() + .map_err(IntoRhaiError::into_rhai_error)? + .world; let self_: ReflectReference = self_.0.clone(); let other: ScriptValue = ScriptValue::from_dynamic(other)?; let target_type_id = self_ @@ -444,8 +451,9 @@ impl CustomType for RhaiReflectReference { ) .with_fn(RhaiOperator::Unm.function_name(), |self_: Self| { let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoRhaiError::into_rhai_error)?; + .try_get_context() + .map_err(IntoRhaiError::into_rhai_error)? + .world; let self_: ReflectReference = self_.0.clone(); let target_type_id = self_ .tail_type_id(world.clone()) @@ -461,8 +469,9 @@ impl CustomType for RhaiReflectReference { RhaiOperator::Pow.function_name(), |self_: Self, other: Dynamic| { let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoRhaiError::into_rhai_error)?; + .try_get_context() + .map_err(IntoRhaiError::into_rhai_error)? + .world; let self_: ReflectReference = self_.0.clone(); let other: ScriptValue = ScriptValue::from_dynamic(other)?; let target_type_id = self_ @@ -480,8 +489,9 @@ impl CustomType for RhaiReflectReference { RhaiOperator::Eq.function_name(), |self_: Self, other: Dynamic| { let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoRhaiError::into_rhai_error)?; + .try_get_context() + .map_err(IntoRhaiError::into_rhai_error)? + .world; let self_: ReflectReference = self_.0.clone(); let other: ScriptValue = ScriptValue::from_dynamic(other)?; let target_type_id = self_ @@ -499,8 +509,9 @@ impl CustomType for RhaiReflectReference { RhaiOperator::Ne.function_name(), |self_: Self, other: Dynamic| { let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoRhaiError::into_rhai_error)?; + .try_get_context() + .map_err(IntoRhaiError::into_rhai_error)? + .world; let self_: ReflectReference = self_.0.clone(); let other: ScriptValue = ScriptValue::from_dynamic(other)?; let target_type_id = self_ @@ -524,8 +535,9 @@ impl CustomType for RhaiReflectReference { RhaiOperator::Lt.function_name(), |self_: Self, other: Dynamic| { let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoRhaiError::into_rhai_error)?; + .try_get_context() + .map_err(IntoRhaiError::into_rhai_error)? + .world; let self_: ReflectReference = self_.0.clone(); let other: ScriptValue = ScriptValue::from_dynamic(other)?; let target_type_id = self_ @@ -541,7 +553,7 @@ impl CustomType for RhaiReflectReference { ) .on_debug(|self_| { let result: Result<_, InteropError> = (|| { - let world = ThreadWorldContainer.try_get_world()?; + let world = ThreadWorldContainer.try_get_context()?.world; let reflect_reference = self_.0.clone(); let func = world @@ -571,7 +583,7 @@ impl CustomType for RhaiReflectReference { }) .on_print(|self_| { let result: Result<_, InteropError> = (|| { - let world = ThreadWorldContainer.try_get_world()?; + let world = ThreadWorldContainer.try_get_context()?.world; let reflect_reference = self_.0.clone(); let func = world @@ -612,8 +624,9 @@ impl CustomType for RhaiStaticReflectReference { .with_name(std::any::type_name::()) .with_indexer_get(|self_: &mut Self, index: Dynamic| { let world = ThreadWorldContainer - .try_get_world() - .map_err(IntoRhaiError::into_rhai_error)?; + .try_get_context() + .map_err(IntoRhaiError::into_rhai_error)? + .world; let type_id = self_.0; let key: ScriptValue = ScriptValue::from_dynamic(index)?; diff --git a/crates/languages/bevy_mod_scripting_rhai/src/lib.rs b/crates/languages/bevy_mod_scripting_rhai/src/lib.rs index e6de10e1ff..0db90e9532 100644 --- a/crates/languages/bevy_mod_scripting_rhai/src/lib.rs +++ b/crates/languages/bevy_mod_scripting_rhai/src/lib.rs @@ -15,16 +15,17 @@ use bevy_log::trace; use bevy_mod_scripting_asset::{Language, ScriptAsset}; use bevy_mod_scripting_bindings::{ AppScriptGlobalsRegistry, InteropError, Namespace, PartialReflectExt, ScriptValue, - ThreadWorldContainer, WorldContainer, + ThreadWorldContainer, }; use bevy_mod_scripting_core::{ IntoScriptPluginParams, ScriptingPlugin, config::{GetPluginThreadConfig, ScriptingPluginConfiguration}, event::CallbackLabel, - extractors::GetPluginFor, make_plugin_config_static, - script::{ContextPolicy, DisplayProxy, ScriptAttachment}, + script::ContextPolicy, }; +use bevy_mod_scripting_display::DisplayProxy; +use bevy_mod_scripting_script::ScriptAttachment; use bindings::reference::{ReservedKeyword, RhaiReflectReference, RhaiStaticReflectReference}; use parking_lot::RwLock; pub use rhai; @@ -44,10 +45,6 @@ pub struct RhaiScriptContext { pub scope: Scope<'static>, } -impl GetPluginFor for RhaiScriptContext { - type P = RhaiScriptingPlugin; -} - make_plugin_config_static!(RhaiScriptingPlugin); impl IntoScriptPluginParams for RhaiScriptingPlugin { @@ -155,7 +152,7 @@ impl Default for RhaiScriptingPlugin { }, |_, context| { // initialize global functions - let world = ThreadWorldContainer.try_get_world()?; + let world = ThreadWorldContainer.try_get_context()?.world; let globals_registry = world.with_resource(|r: &AppScriptGlobalsRegistry| r.clone())?; let globals_registry = globals_registry.read(); @@ -216,7 +213,7 @@ impl Default for RhaiScriptingPlugin { }, ], context_pre_handling_initializers: vec![|context_key, context| { - let world = ThreadWorldContainer.try_get_world()?; + let world = ThreadWorldContainer.try_get_context()?.world; if let Some(entity) = context_key.entity() { context.scope.set_or_push( diff --git a/crates/testing_crates/script_integration_test_harness/Cargo.toml b/crates/testing_crates/script_integration_test_harness/Cargo.toml index 7a08a71516..2ccc2e43bc 100644 --- a/crates/testing_crates/script_integration_test_harness/Cargo.toml +++ b/crates/testing_crates/script_integration_test_harness/Cargo.toml @@ -26,6 +26,8 @@ bevy_mod_scripting_asset = { workspace = true } bevy_mod_scripting_functions = { workspace = true, features = [ "core_functions", ] } +bevy_mod_scripting_display = { workspace = true } +bevy_mod_scripting_script = { workspace = true } regex = { workspace = true } pretty_assertions = { workspace = true } bevy_mod_scripting_lua = { workspace = true, optional = true } diff --git a/crates/testing_crates/script_integration_test_harness/src/lib.rs b/crates/testing_crates/script_integration_test_harness/src/lib.rs index 90596f3ca7..03367008d5 100644 --- a/crates/testing_crates/script_integration_test_harness/src/lib.rs +++ b/crates/testing_crates/script_integration_test_harness/src/lib.rs @@ -23,17 +23,19 @@ use ::{ use bevy_asset::Assets; use bevy_mod_scripting_asset::ScriptAsset; use bevy_mod_scripting_bindings::{ - CoreScriptGlobalsPlugin, ReflectAccessId, ThreadWorldContainer, WorldAccessGuard, - WorldContainer, WorldGuard, + CoreScriptGlobalsPlugin, ReflectAccessId, ThreadScriptContext, ThreadWorldContainer, + WorldAccessGuard, WorldGuard, }; use bevy_mod_scripting_core::{ BMSScriptingInfrastructurePlugin, IntoScriptPluginParams, commands::AttachScript, error::ScriptError, pipeline::PipelineRun, - script::{DisplayProxy, ScriptAttachment, ScriptComponent, ScriptContext}, + script::{ScriptComponent, ScriptContext}, }; +use bevy_mod_scripting_display::DisplayProxy; use bevy_mod_scripting_functions::ScriptFunctionsPlugin; +use bevy_mod_scripting_script::ScriptAttachment; use criterion::{BatchSize, measurement::Measurement}; use rand::{Rng, SeedableRng}; use test_functions::{RNG, register_test_functions}; @@ -328,7 +330,10 @@ where let _ = WorldAccessGuard::with_existing_static_guard(guard, |guard| { // Ensure the world is available via ThreadWorldContainer ThreadWorldContainer - .set_world(guard.clone()) + .set_context(ThreadScriptContext { + world: guard.clone(), + attachment: ScriptAttachment::StaticScript(Handle::Weak(script_id)), + }) .map_err(|e| format!("{e:#?}"))?; // Pass the locked context to the closure for benchmarking its Lua (or generic) part bench_fn(&mut ctxt_locked, runtime, label, criterion) diff --git a/crates/testing_crates/script_integration_test_harness/src/parse.rs b/crates/testing_crates/script_integration_test_harness/src/parse.rs index 0e007d466c..0a4406f323 100644 --- a/crates/testing_crates/script_integration_test_harness/src/parse.rs +++ b/crates/testing_crates/script_integration_test_harness/src/parse.rs @@ -9,8 +9,9 @@ use bevy_mod_scripting_core::{ CallbackLabel, OnScriptLoaded, OnScriptReloaded, OnScriptUnloaded, Recipients, ScriptCallbackEvent, }, - script::{ContextPolicy, ScriptAttachment}, + script::ContextPolicy, }; +use bevy_mod_scripting_script::ScriptAttachment; use crate::scenario::{SCENARIO_SELF_LANGUAGE_NAME, ScenarioContext, ScenarioStep}; 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 79976c2379..8700d6bcac 100644 --- a/crates/testing_crates/script_integration_test_harness/src/scenario.rs +++ b/crates/testing_crates/script_integration_test_harness/src/scenario.rs @@ -30,8 +30,9 @@ use bevy_mod_scripting_core::{ ScriptCallbackResponseEvent, ScriptDetachedEvent, }, handler::event_handler, - script::{ContextPolicy, ScriptAttachment, ScriptComponent, ScriptContext}, + script::{ContextPolicy, ScriptComponent, ScriptContext}, }; +use bevy_mod_scripting_script::ScriptAttachment; use test_utils::test_data::setup_integration_test; use crate::{install_test_plugin, parse::*}; diff --git a/crates/testing_crates/test_utils/src/test_plugin.rs b/crates/testing_crates/test_utils/src/test_plugin.rs index d7308a303c..f0c36ea096 100644 --- a/crates/testing_crates/test_utils/src/test_plugin.rs +++ b/crates/testing_crates/test_utils/src/test_plugin.rs @@ -20,10 +20,6 @@ macro_rules! make_test_plugin { $ident::make_plugin_config_static!(TestPlugin); - impl $ident::extractors::GetPluginFor for TestContext { - type P = TestPlugin; - } - impl $ident::IntoScriptPluginParams for TestPlugin { type C = TestContext; type R = TestRuntime; diff --git a/examples/game_of_life.rs b/examples/game_of_life.rs index 7589224ccd..14d82ef684 100644 --- a/examples/game_of_life.rs +++ b/examples/game_of_life.rs @@ -17,10 +17,6 @@ use bevy::{ use bevy_console::{AddConsoleCommand, ConsoleCommand, ConsoleOpen, ConsolePlugin, make_layer}; use bevy_mod_scripting::prelude::*; use bevy_mod_scripting_bindings::AllocatorDiagnosticPlugin; -use bevy_mod_scripting_core::{ - commands::{AttachScript, DetachScript}, - script::ScriptAttachment, -}; use clap::Parser; // CONSOLE SETUP diff --git a/src/lib.rs b/src/lib.rs index dd724a1282..51abdb5713 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,10 @@ pub mod asset { pub use bevy_mod_scripting_asset::*; } +pub mod script { + pub use bevy_mod_scripting_script::*; +} + pub mod prelude; #[cfg(feature = "lua")] diff --git a/src/prelude.rs b/src/prelude.rs index 697888ed01..c3c0e6d7b6 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,8 +11,12 @@ pub use bevy_mod_scripting_bindings::{ script_value::ScriptValue, }; +pub use bevy_mod_scripting_script::*; + pub use bevy_mod_scripting_asset::*; +pub use bevy_mod_scripting_core::commands::*; + #[cfg(feature = "lua")] pub use bevy_mod_scripting_lua::LuaScriptingPlugin; #[cfg(feature = "rhai")]