diff --git a/assets/tests/globals/type_cache_available.lua b/assets/tests/globals/type_cache_available.lua new file mode 100644 index 0000000000..f9cc8435dc --- /dev/null +++ b/assets/tests/globals/type_cache_available.lua @@ -0,0 +1,6 @@ + +function on_test() + local my_type = types.TestResource; + assert(my_type ~= nil, "Type TestResource is not available in type cache"); + assert(my_type:short_name() == "TestResource", "Type t.TestResource:short_name() is not correct: " .. my_type:short_name()); +end \ No newline at end of file diff --git a/assets/tests/globals/type_cache_available.rhai b/assets/tests/globals/type_cache_available.rhai new file mode 100644 index 0000000000..ff736a7afb --- /dev/null +++ b/assets/tests/globals/type_cache_available.rhai @@ -0,0 +1,5 @@ +fn on_test() { + let my_type = types.TestResource; + assert(type_of(my_type) != "()", "Type TestResource is not available in type cache"); + assert(my_type.short_name.call() == "TestResource", "Type t.TestResource:short_name() is not correct: " + my_type.short_name.call()); +} \ No newline at end of file diff --git a/crates/bevy_mod_scripting_core/src/bindings/globals/core.rs b/crates/bevy_mod_scripting_core/src/bindings/globals/core.rs index e65cd9dff6..5f5d46f459 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/globals/core.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/globals/core.rs @@ -1,6 +1,11 @@ //! Core globals exposed by the BMS framework +use std::{collections::HashMap, sync::Arc}; + use bevy::{app::Plugin, ecs::reflect::AppTypeRegistry}; +use bevy_mod_scripting_derive::script_globals; + +use crate::{bindings::{function::from::{Union, Val}, ScriptComponentRegistration, ScriptResourceRegistration, ScriptTypeRegistration, WorldGuard}, error::InteropError}; use super::AppScriptGlobalsRegistry; @@ -10,12 +15,16 @@ pub struct CoreScriptGlobalsPlugin; impl Plugin for CoreScriptGlobalsPlugin { fn build(&self, _app: &mut bevy::app::App) {} fn finish(&self, app: &mut bevy::app::App) { - let global_registry = app - .world_mut() + register_static_core_globals(app.world_mut()); + register_core_globals(app.world_mut()); + } +} + +fn register_static_core_globals(world: &mut bevy::ecs::world::World) { + let global_registry = world .get_resource_or_init::() .clone(); - let type_registry = app - .world_mut() + let type_registry = world .get_resource_or_init::() .clone(); let mut global_registry = global_registry.write(); @@ -34,8 +43,32 @@ impl Plugin for CoreScriptGlobalsPlugin { None, global_name.into(), documentation.into(), - ) + ); } } - } } + +#[script_globals( + bms_core_path = "crate", + name = "core_globals", +)] +impl CoreGlobals { + /// A cache of types normally available through the `world.get_type_by_name` function. + /// + /// You can use this to avoid having to store type references. + fn types(guard: WorldGuard) -> Result, Union, Val>>>, InteropError> { + let type_registry = guard.type_registry(); + let type_registry = type_registry.read(); + let mut type_cache = HashMap::::default(); + for registration in type_registry.iter(){ + if let Some(ident) = registration.type_info().type_path_table().ident() { + let registration = ScriptTypeRegistration::new(Arc::new(registration.clone())); + let registration = guard.clone().get_type_registration(registration)?; + let registration = registration.map_both(Val::from, |u| u.map_both(Val::from, Val::from)); + type_cache.insert(ident.to_string(), registration); + } + } + + Ok(type_cache) + } +} \ No newline at end of file diff --git a/crates/bevy_mod_scripting_core/src/bindings/globals/mod.rs b/crates/bevy_mod_scripting_core/src/bindings/globals/mod.rs index 7eb8bfa7d9..fedb8197be 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/globals/mod.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/globals/mod.rs @@ -100,11 +100,11 @@ impl ScriptGlobalsRegistry { F: Fn(WorldGuard) -> Result + 'static + Send + Sync, >( &mut self, - name: Cow<'static, str>, + name: impl Into>, maker: F, ) -> Option { self.globals.insert( - name, + name.into(), ScriptGlobal { maker: Some(Self::type_erase_maker(maker)), documentation: None, @@ -122,15 +122,15 @@ impl ScriptGlobalsRegistry { F: Fn(WorldGuard) -> Result + 'static + Send + Sync, >( &mut self, - name: Cow<'static, str>, + name: impl Into>, maker: F, - documentation: Cow<'static, str>, + documentation: impl Into>, ) -> Option { self.globals.insert( - name, + name.into(), ScriptGlobal { maker: Some(Self::type_erase_maker(maker)), - documentation: Some(documentation), + documentation: Some(documentation.into()), type_id: TypeId::of::(), type_information: Some(T::through_type_info()), }, diff --git a/crates/bevy_mod_scripting_core/src/bindings/world.rs b/crates/bevy_mod_scripting_core/src/bindings/world.rs index 95be1569cb..b6ef742261 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/world.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/world.rs @@ -12,7 +12,7 @@ use super::{ }, function::{ namespace::Namespace, script_function::{AppScriptFunctionRegistry, DynamicScriptFunction, FunctionCallContext}, - }, pretty_print::DisplayWithWorld, schedule::AppScheduleRegistry, script_value::ScriptValue, with_global_access, AppReflectAllocator, ReflectBase, ReflectBaseType, ReflectReference, ScriptComponentRegistration, ScriptResourceRegistration, ScriptTypeRegistration + }, pretty_print::DisplayWithWorld, schedule::AppScheduleRegistry, script_value::ScriptValue, with_global_access, AppReflectAllocator, ReflectBase, ReflectBaseType, ReflectReference, ScriptComponentRegistration, ScriptResourceRegistration, ScriptTypeRegistration, Union }; use crate::{ bindings::{function::{from::FromScript, from_ref::FromScriptRef}, with_access_read, with_access_write}, @@ -542,6 +542,7 @@ impl<'w> WorldAccessGuard<'w> { /// Impl block for higher level world methods #[profiling::all_functions] impl WorldAccessGuard<'_> { + fn construct_from_script_value( &self, descriptor: impl Into>, @@ -800,7 +801,7 @@ impl WorldAccessGuard<'_> { }) } - /// get a type registration for the type + /// get a type registration for the type, without checking if it's a component or resource pub fn get_type_by_name(&self, type_name: String) -> Option { let type_registry = self.type_registry(); let type_registry = type_registry.read(); @@ -810,6 +811,38 @@ impl WorldAccessGuard<'_> { .map(|registration| ScriptTypeRegistration::new(Arc::new(registration.clone()))) } + /// get a type erased type registration for the type including information about whether it's a component or resource + pub(crate) fn get_type_registration(&self, registration: ScriptTypeRegistration) -> Result>, InteropError> { + + let registration = match self.get_resource_type(registration)? { + Ok(res) => { + return Ok(Union::new_right(Union::new_right(res))); + } + Err(registration) => registration, + }; + + let registration = match self.get_component_type(registration)? { + Ok(comp) => { + return Ok(Union::new_right(Union::new_left(comp))); + } + Err(registration) => registration, + }; + + Ok(Union::new_left(registration)) + } + + /// Similar to [`Self::get_type_by_name`] but returns a type erased [`ScriptTypeRegistration`], [`ScriptComponentRegistration`] or [`ScriptResourceRegistration`] + /// depending on the underlying type and state of the world. + pub fn get_type_registration_by_name(&self, type_name: String) -> Result>>, InteropError> { + let val = self.get_type_by_name(type_name); + Ok(match val { + Some(registration) => { + Some(self.get_type_registration(registration)?) + } + None => None, + }) + } + /// get a schedule by name pub fn get_schedule_by_name(&self, schedule_name: String) -> Option { let schedule_registry = self.schedule_registry(); diff --git a/crates/bevy_mod_scripting_functions/src/core.rs b/crates/bevy_mod_scripting_functions/src/core.rs index cd3bbffbba..13d7c4d280 100644 --- a/crates/bevy_mod_scripting_functions/src/core.rs +++ b/crates/bevy_mod_scripting_functions/src/core.rs @@ -30,6 +30,7 @@ use bindings::{ }; use error::InteropError; use reflection_extensions::{PartialReflectExt, TypeIdExtensions}; + pub fn register_bevy_bindings(app: &mut App) { #[cfg(feature = "bevy_bindings")] app.add_plugins(crate::bevy_bindings::LuaBevyScriptingPlugin); @@ -45,39 +46,20 @@ impl World { fn get_type_by_name( ctxt: FunctionCallContext, type_name: String, - ) -> Result, InteropError> { + ) -> Result< + Option< + Union< + Val, + Union, Val>, + >, + >, + InteropError, + > { profiling::function_scope!("get_type_by_name"); let world = ctxt.world()?; - let val = world.get_type_by_name(type_name); - - Ok(match val { - Some(registration) => { - let allocator = world.allocator(); - - let registration = match world.get_resource_type(registration)? { - Ok(res) => { - let mut allocator = allocator.write(); - return Ok(Some(ReflectReference::new_allocated(res, &mut allocator))); - } - Err(registration) => registration, - }; - - let registration = match world.get_component_type(registration)? { - Ok(comp) => { - let mut allocator = allocator.write(); - return Ok(Some(ReflectReference::new_allocated(comp, &mut allocator))); - } - Err(registration) => registration, - }; - - let mut allocator = allocator.write(); - Some(ReflectReference::new_allocated( - registration, - &mut allocator, - )) - } - None => None, - }) + world + .get_type_registration_by_name(type_name) + .map(|v| v.map(|v| v.map_both(Val::from, |u| u.map_both(Val::from, Val::from)))) } /// Retrieves the schedule with the given name, Also ensures the schedule is initialized before returning it.