diff --git a/crates/bevy_mod_scripting_core/src/bindings/access_map.rs b/crates/bevy_mod_scripting_core/src/bindings/access_map.rs index 543712fda8..02ba704485 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/access_map.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/access_map.rs @@ -1,10 +1,11 @@ -use std::{sync::atomic::AtomicBool, thread::ThreadId}; +use std::thread::ThreadId; use bevy::{ ecs::{component::ComponentId, world::unsafe_world_cell::UnsafeWorldCell}, prelude::Resource, }; use dashmap::{DashMap, Entry}; +use parking_lot::RwLock; use smallvec::SmallVec; use crate::error::InteropError; @@ -56,8 +57,14 @@ impl AccessCount { } } +/// For structs which can be mapped to a u64 index pub trait AccessMapKey { + /// Convert the key to an index + /// + /// The key 0 must not be be used as it's reserved for global access fn as_index(&self) -> u64; + + /// Convert an index back to the original struct fn from_index(value: u64) -> Self; } @@ -76,6 +83,7 @@ impl AccessMapKey for u64 { pub enum ReflectAccessKind { ComponentOrResource, Allocation, + Global, } /// Describes the id pointing to the base value we are accessing via reflection, for components and resources this is the ComponentId @@ -88,12 +96,13 @@ pub struct ReflectAccessId { impl AccessMapKey for ReflectAccessId { fn as_index(&self) -> u64 { - // project two linear non-negative ranges to a single linear non-negative range - // y1 = 2x - 0 - // y2 = 2x - 1 + // project two linear non-negative ranges to a single linear non-negative range, offset by 1 to avoid 0 + // y1 = 2x - 0 + 1 + // y2 = 2x - 1 + 1 match self.kind { - ReflectAccessKind::ComponentOrResource => self.id * 2, - ReflectAccessKind::Allocation => self.id * 2 + 1, + ReflectAccessKind::ComponentOrResource => (self.id * 2) + 1, + ReflectAccessKind::Allocation => (self.id * 2) + 1, + ReflectAccessKind::Global => 0, } } @@ -101,18 +110,28 @@ impl AccessMapKey for ReflectAccessId { // retrieve the kind of range based on if the value is odd or even // y1 if even, y2 if odd // to retrieve value of x: - // x1 = y / 2 - // x2 = (y - 1) / 2 - let (kind, id) = if value % 2 == 0 { - (ReflectAccessKind::ComponentOrResource, value / 2) + // x1 = (y / 2) - 1 + // x2 = ((y - 1) / 2) - 1 + + let (kind, id) = if value == 0 { + (ReflectAccessKind::Global, 0) + } else if value % 2 == 0 { + (ReflectAccessKind::ComponentOrResource, (value / 2) - 1) } else { - (ReflectAccessKind::Allocation, (value - 1) / 2) + (ReflectAccessKind::Allocation, ((value - 1) / 2) - 1) }; Self { kind, id } } } impl ReflectAccessId { + pub fn for_global() -> Self { + Self { + kind: ReflectAccessKind::Global, + id: 0, + } + } + pub fn for_resource(cell: &UnsafeWorldCell) -> Result { let resource_id = cell.components().resource_id::().ok_or_else(|| { InteropError::unregistered_component_or_resource_type(std::any::type_name::()) @@ -190,16 +209,27 @@ impl From for ReflectAllocationId { #[derive(Debug, Default)] pub struct AccessMap { individual_accesses: DashMap, - global_lock: AtomicBool, + global_lock: RwLock, } impl AccessMap { + pub fn is_locked_exclusively(&self) -> bool { + let global_lock = self.global_lock.read(); + !global_lock.can_write() + } + + pub fn global_access_location(&self) -> Option> { + let global_lock = self.global_lock.read(); + global_lock.as_location() + } + /// Tries to claim read access, will return false if somebody else is writing to the same key, or holding a global lock #[track_caller] pub fn claim_read_access(&self, key: K) -> bool { - if self.global_lock.load(std::sync::atomic::Ordering::Relaxed) { + if self.is_locked_exclusively() { return false; } + let key = key.as_index(); let access = self.individual_accesses.try_entry(key); match access.map(Entry::or_default) { @@ -217,7 +247,7 @@ impl AccessMap { #[track_caller] /// Tries to claim write access, will return false if somebody else is reading or writing to the same key, or holding a global lock pub fn claim_write_access(&self, key: K) -> bool { - if self.global_lock.load(std::sync::atomic::Ordering::Relaxed) { + if self.is_locked_exclusively() { return false; } let key = key.as_index(); @@ -237,17 +267,19 @@ impl AccessMap { /// Tries to claim global access. This type of access prevents any other access from happening simulatenously /// Will return false if anybody else is currently accessing any part of the map + #[track_caller] pub fn claim_global_access(&self) -> bool { - self.individual_accesses.is_empty() - && self - .global_lock - .compare_exchange( - false, - true, - std::sync::atomic::Ordering::Relaxed, - std::sync::atomic::Ordering::Relaxed, - ) - .is_ok() + let mut global_lock = self.global_lock.write(); + + if !self.individual_accesses.is_empty() || !global_lock.can_write() { + return false; + } + global_lock.read_by.push(ClaimOwner { + id: std::thread::current().id(), + location: *std::panic::Location::caller(), + }); + global_lock.written = true; + true } /// Releases an access @@ -278,8 +310,15 @@ impl AccessMap { /// Releases a global access pub fn release_global_access(&self) { - self.global_lock - .store(false, std::sync::atomic::Ordering::Relaxed); + let mut global_lock = self.global_lock.write(); + global_lock.written = false; + if let Some(p) = global_lock.read_by.pop() { + assert!( + p.id == std::thread::current().id(), + "Access released from wrong thread, claimed at {}", + p.location.display_location() + ); + } } pub fn list_accesses(&self) -> Vec<(K, AccessCount)> { @@ -295,14 +334,17 @@ impl AccessMap { pub fn release_all_accesses(&self) { self.individual_accesses.clear(); - self.global_lock - .store(false, std::sync::atomic::Ordering::Relaxed); + self.release_global_access(); } pub fn access_location( &self, key: K, ) -> Option> { + if key.as_index() == 0 { + return self.global_access_location(); + } + self.individual_accesses .try_get(&key.as_index()) .try_unwrap() @@ -371,18 +413,17 @@ macro_rules! with_access_write { macro_rules! with_global_access { ($access_map:expr, $msg:expr, $body:block) => { if !$access_map.claim_global_access() { - panic!( - "{}. Another access is held somewhere else preventing locking the world: {}", + Err($crate::error::InteropError::cannot_claim_access( + $crate::bindings::access_map::ReflectAccessId::for_global(), + $access_map + .access_location($crate::bindings::access_map::ReflectAccessId::for_global()), $msg, - $crate::bindings::access_map::DisplayCodeLocation::display_location( - $access_map.access_first_location() - ) - ); + )) } else { #[allow(clippy::redundant_closure_call)] let result = (|| $body)(); $access_map.release_global_access(); - result + Ok(result) } }; } diff --git a/crates/bevy_mod_scripting_core/src/bindings/function/script_function.rs b/crates/bevy_mod_scripting_core/src/bindings/function/script_function.rs index 2e1768c87f..8ef4056489 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/function/script_function.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/function/script_function.rs @@ -2,10 +2,10 @@ use super::{from::FromScript, into::IntoScript, namespace::Namespace}; use crate::{ bindings::{ function::from::{Mut, Ref, Val}, - ReflectReference, WorldGuard, + ReflectReference, ThreadWorldContainer, WorldContainer, WorldGuard, }, error::InteropError, - ScriptValue, WorldCallbackAccess, + ScriptValue, }; use bevy::{ prelude::{Reflect, Resource}, @@ -96,7 +96,8 @@ macro_rules! register_tuple_dependencies { } no_type_dependencies!(InteropError); -self_type_dependency_only!(WorldCallbackAccess, CallerContext, ReflectReference); +no_type_dependencies!(WorldGuard<'static>); +self_type_dependency_only!(FunctionCallContext, ReflectReference); recursive_type_dependencies!( (Val where T: GetTypeRegistration), @@ -118,9 +119,21 @@ pub trait GetFunctionTypeDependencies { /// Functions can choose to react to caller preferences such as converting 1-indexed numbers to 0-indexed numbers #[derive(Clone, Copy, Debug, Reflect, Default)] #[reflect(opaque)] -pub struct CallerContext { +pub struct FunctionCallContext { pub convert_to_0_indexed: bool, } +impl FunctionCallContext { + pub fn new(convert_to_0_indexed: bool) -> Self { + Self { + convert_to_0_indexed, + } + } + + /// Tries to access the world, returning an error if the world is not available + pub fn world(&self) -> Result, InteropError> { + ThreadWorldContainer.try_get_world() + } +} #[derive(Clone, Debug, PartialEq, Default)] pub struct FunctionInfo { @@ -146,12 +159,7 @@ impl FunctionInfo { pub struct DynamicScriptFunction { pub info: FunctionInfo, // TODO: info about the function, this is hard right now because of non 'static lifetimes in wrappers, we can't use TypePath etc - func: Arc< - dyn Fn(CallerContext, WorldCallbackAccess, Vec) -> ScriptValue - + Send - + Sync - + 'static, - >, + func: Arc) -> ScriptValue + Send + Sync + 'static>, } impl PartialEq for DynamicScriptFunction { @@ -167,10 +175,7 @@ pub struct DynamicScriptFunctionMut { func: Arc< RwLock< // I'd rather consume an option or something instead of having the RWLock but I just wanna get this release out - dyn FnMut(CallerContext, WorldCallbackAccess, Vec) -> ScriptValue - + Send - + Sync - + 'static, + dyn FnMut(FunctionCallContext, Vec) -> ScriptValue + Send + Sync + 'static, >, >, } @@ -188,13 +193,11 @@ impl DynamicScriptFunction { pub fn call>( &self, args: I, - world: WorldGuard, - context: CallerContext, + context: FunctionCallContext, ) -> Result { let args = args.into_iter().collect::>(); - let world_callback_access = WorldCallbackAccess::from_guard(world.clone()); // should we be inlining call errors into the return value? - let return_val = (self.func)(context, world_callback_access, args); + let return_val = (self.func)(context, args); match return_val { ScriptValue::Error(e) => Err(InteropError::function_interop_error( self.name(), @@ -237,14 +240,12 @@ impl DynamicScriptFunctionMut { pub fn call>( &self, args: I, - world: WorldGuard, - context: CallerContext, + context: FunctionCallContext, ) -> Result { let args = args.into_iter().collect::>(); - let world_callback_access = WorldCallbackAccess::from_guard(world.clone()); // should we be inlining call errors into the return value? let mut write = self.func.write(); - let return_val = (write)(context, world_callback_access, args); + let return_val = (write)(context, args); match return_val { ScriptValue::Error(e) => Err(InteropError::function_interop_error( self.name(), @@ -297,10 +298,7 @@ impl std::fmt::Debug for DynamicScriptFunctionMut { impl From for DynamicScriptFunction where - F: Fn(CallerContext, WorldCallbackAccess, Vec) -> ScriptValue - + Send - + Sync - + 'static, + F: Fn(FunctionCallContext, Vec) -> ScriptValue + Send + Sync + 'static, { fn from(fn_: F) -> Self { DynamicScriptFunction { @@ -313,10 +311,7 @@ where impl From for DynamicScriptFunctionMut where - F: FnMut(CallerContext, WorldCallbackAccess, Vec) -> ScriptValue - + Send - + Sync - + 'static, + F: FnMut(FunctionCallContext, Vec) -> ScriptValue + Send + Sync + 'static, { fn from(fn_: F) -> Self { DynamicScriptFunctionMut { @@ -527,52 +522,43 @@ macro_rules! impl_script_function { // FnMut(T1...Tn) -> O impl_script_function!(@ ScriptFunctionMut FnMut DynamicScriptFunctionMut into_dynamic_script_function_mut $( $param ),* : -> O => O ); - // Fn(WorldCallbackAccess, T1...Tn) -> O - impl_script_function!(@ ScriptFunction Fn DynamicScriptFunction into_dynamic_script_function $( $param ),* : ,(callback: WorldCallbackAccess) -> O => O); - // FnMut(WorldCallbackAccess, T1...Tn) -> O - impl_script_function!(@ ScriptFunctionMut FnMut DynamicScriptFunctionMut into_dynamic_script_function_mut $( $param ),* : ,(callback: WorldCallbackAccess) -> O => O); - - // Fn(CallerContext, WorldCallbackAccess, T1...Tn) -> O - impl_script_function!(@ ScriptFunction Fn DynamicScriptFunction into_dynamic_script_function $( $param ),* : (context: CallerContext),(callback: WorldCallbackAccess) -> O => O); - // FnMut(CallerContext, WorldCallbackAccess, T1...Tn) -> O - impl_script_function!(@ ScriptFunctionMut FnMut DynamicScriptFunctionMut into_dynamic_script_function_mut $( $param ),* : (context: CallerContext),(callback: WorldCallbackAccess) -> O => O); + // Fn(CallerContext, T1...Tn) -> O + impl_script_function!(@ ScriptFunction Fn DynamicScriptFunction into_dynamic_script_function $( $param ),* : (context: FunctionCallContext) -> O => O); + // FnMut(FunctionCallContext, T1...Tn) -> O + impl_script_function!(@ ScriptFunctionMut FnMut DynamicScriptFunctionMut into_dynamic_script_function_mut $( $param ),* : (context: FunctionCallContext) -> O => O); // Fn(T1...Tn) -> Result impl_script_function!(@ ScriptFunction Fn DynamicScriptFunction into_dynamic_script_function $( $param ),* : -> O => Result where s); // FnMut(T1...Tn) -> Result impl_script_function!(@ ScriptFunctionMut FnMut DynamicScriptFunctionMut into_dynamic_script_function_mut $( $param ),* : -> O => Result where s); - // Fn(WorldCallbackAccess, T1...Tn) -> Result - impl_script_function!(@ ScriptFunction Fn DynamicScriptFunction into_dynamic_script_function $( $param ),* : ,(callback: WorldCallbackAccess) -> O => Result where s); - // FnMut(WorldCallbackAccess, T1...Tn) -> Result - impl_script_function!(@ ScriptFunctionMut FnMut DynamicScriptFunctionMut into_dynamic_script_function_mut $( $param ),* : ,(callback: WorldCallbackAccess) -> O => Result where s); - - // Fn(CallerContext, WorldCallbackAccess, T1...Tn) -> Result - impl_script_function!(@ ScriptFunction Fn DynamicScriptFunction into_dynamic_script_function $( $param ),* : (context: CallerContext),(callback: WorldCallbackAccess) -> O => Result where s); - // FnMut(CallerContext, WorldCallbackAccess, T1...Tn) -> Result - impl_script_function!(@ ScriptFunctionMut FnMut DynamicScriptFunctionMut into_dynamic_script_function_mut $( $param ),* : (context: CallerContext),(callback: WorldCallbackAccess) -> O => Result where s); + // Fn(FunctionCallContext, WorldGuard<'w>, T1...Tn) -> Result + impl_script_function!(@ ScriptFunction Fn DynamicScriptFunction into_dynamic_script_function $( $param ),* : (context: FunctionCallContext)-> O => Result where s); + // FnMut(FunctionCallContext, WorldGuard<'w>, T1...Tn) -> Result + impl_script_function!(@ ScriptFunctionMut FnMut DynamicScriptFunctionMut into_dynamic_script_function_mut $( $param ),* : (context: FunctionCallContext) -> O => Result where s); }; - (@ $trait_type:ident $fn_type:ident $dynamic_type:ident $trait_fn_name:ident $( $param:ident ),* : $(($context:ident: $contextty:ty))? $(,($callback:ident: $callbackty:ty))? -> O => $res:ty $(where $out:ident)?) => { + (@ $trait_type:ident $fn_type:ident $dynamic_type:ident $trait_fn_name:ident $( $param:ident ),* : $(($context:ident: $contextty:ty))? -> O => $res:ty $(where $out:ident)?) => { #[allow(non_snake_case)] impl< 'env, + 'w : 'static, $( $param: FromScript, )* O, F > $trait_type<'env, - fn( $($contextty,)? $( $callbackty, )? $($param ),* ) -> $res + fn( $($contextty,)? $($param ),* ) -> $res > for F where O: IntoScript + TypePath + GetOwnership, - F: $fn_type( $($contextty,)? $( $callbackty, )? $($param ),* ) -> $res + Send + Sync + 'static, - $( $param::This<'env>: Into<$param>,)* + F: $fn_type( $($contextty,)? $($param ),* ) -> $res + Send + Sync + 'static , + $( $param::This<'w>: Into<$param>,)* { #[allow(unused_mut,unused_variables)] fn $trait_fn_name(mut self) -> $dynamic_type { - let func = (move |caller_context: CallerContext, world: WorldCallbackAccess, args: Vec | { + let func = (move |caller_context: FunctionCallContext, args: Vec | { let res: Result = (|| { let expected_arg_count = count!($($param )*); if args.len() < expected_arg_count { @@ -582,8 +568,7 @@ macro_rules! impl_script_function { })); } $( let $context = caller_context; )? - $( let $callback = world.clone(); )? - let world = world.try_read()?; + let world = caller_context.world()?; world.begin_access_scope()?; let ret = { let mut current_arg = 0; @@ -593,7 +578,7 @@ macro_rules! impl_script_function { let $param = <$param>::from_script(arg_iter.next().expect("invariant"), world.clone()) .map_err(|e| InteropError::function_arg_conversion_error(current_arg.to_string(), e))?; )* - let out = self( $( $context,)? $( $callback, )? $( $param.into(), )* ); + let out = self( $( $context,)? $( $param.into(), )* ); $( let $out = out?; let out = $out; diff --git a/crates/bevy_mod_scripting_core/src/bindings/pretty_print.rs b/crates/bevy_mod_scripting_core/src/bindings/pretty_print.rs index 35e1090ed3..ffb1645346 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/pretty_print.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/pretty_print.rs @@ -373,8 +373,8 @@ impl DisplayWithWorld for ComponentId { fn display_with_world(&self, world: WorldGuard) -> String { let component_name = world .as_unsafe_world_cell() - .components() - .get_info(*self) + .ok() + .and_then(|c| c.components().get_info(*self)) .map(|info| info.name()); match component_name { @@ -394,6 +394,7 @@ impl DisplayWithWorld for ReflectAccessId { super::access_map::ReflectAccessKind::Allocation => { format!("Allocation({})", self.id) } + super::access_map::ReflectAccessKind::Global => "Global".to_owned(), } } @@ -410,6 +411,7 @@ impl DisplayWithWorld for ReflectAccessId { let type_id = allocator.get_type_id(&allocation_id).or_fake_id(); format!("Allocation({})", type_id.display_with_world(world)) } + super::access_map::ReflectAccessKind::Global => "Global".to_owned(), } } } @@ -543,7 +545,7 @@ mod test { use crate::bindings::{ function::script_function::AppScriptFunctionRegistry, AppReflectAllocator, - ReflectAllocationId, WorldAccessGuard, + ReflectAllocationId, }; use super::*; @@ -566,7 +568,7 @@ mod test { #[test] fn test_type_id() { let mut world = setup_world(); - let world = WorldGuard::new(WorldAccessGuard::new(&mut world)); + let world = WorldGuard::new(&mut world); let type_id = TypeId::of::(); assert_eq!(type_id.display_with_world(world.clone()), "usize"); @@ -585,7 +587,7 @@ mod test { #[test] fn test_reflect_base_type() { let mut world = setup_world(); - let world = WorldGuard::new(WorldAccessGuard::new(&mut world)); + let world = WorldGuard::new(&mut world); let type_id = TypeId::of::(); @@ -621,7 +623,7 @@ mod test { fn test_reflect_reference() { let mut world = setup_world(); - let world = WorldGuard::new(WorldAccessGuard::new(&mut world)); + let world = WorldGuard::new(&mut world); let type_id = TypeId::of::(); diff --git a/crates/bevy_mod_scripting_core/src/bindings/query.rs b/crates/bevy_mod_scripting_core/src/bindings/query.rs index 2f7fa31a4e..78ebe6b9fe 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/query.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/query.rs @@ -1,4 +1,4 @@ -use super::{ReflectReference, WorldAccessGuard, WorldCallbackAccess}; +use super::{ReflectReference, WorldAccessGuard}; use crate::{error::InteropError, with_global_access}; use bevy::{ ecs::{component::ComponentId, entity::Entity}, @@ -152,15 +152,15 @@ pub struct ScriptQueryResult { pub components: Vec, } -impl WorldCallbackAccess { - pub fn query( - &self, - query: ScriptQueryBuilder, - ) -> Result, InteropError> { - // find the set of components - self.try_read().and_then(|world| world.query(query)) - } -} +// impl WorldCallbackAccess { +// pub fn query( +// &self, +// query: ScriptQueryBuilder, +// ) -> Result, InteropError> { +// // find the set of components +// self.try_read().and_then(|world| world.query(query)) +// } +// } impl WorldAccessGuard<'_> { pub fn query( @@ -168,7 +168,7 @@ impl WorldAccessGuard<'_> { query: ScriptQueryBuilder, ) -> Result, InteropError> { with_global_access!(self.0.accesses, "Could not query", { - let world = unsafe { self.as_unsafe_world_cell().world_mut() }; + let world = unsafe { self.as_unsafe_world_cell()?.world_mut() }; let mut dynamic_query = QueryBuilder::::new(world); // we don't actually want to fetch the data for components now, only figure out @@ -208,6 +208,6 @@ impl WorldAccessGuard<'_> { } }) .collect()) - }) + })? } } diff --git a/crates/bevy_mod_scripting_core/src/bindings/reference.rs b/crates/bevy_mod_scripting_core/src/bindings/reference.rs index 10ca53a317..dfc8dc01a9 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/reference.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/reference.rs @@ -139,7 +139,7 @@ impl ReflectReference { } pub fn new_resource_ref(world: WorldGuard) -> Result { - let reflect_id = ReflectAccessId::for_resource::(&world.as_unsafe_world_cell())?; + let reflect_id = ReflectAccessId::for_resource::(&world.as_unsafe_world_cell()?)?; Ok(Self { base: ReflectBaseType { type_id: TypeId::of::(), @@ -153,7 +153,7 @@ impl ReflectReference { entity: Entity, world: WorldGuard, ) -> Result { - let reflect_id = ReflectAccessId::for_component::(&world.as_unsafe_world_cell())?; + let reflect_id = ReflectAccessId::for_component::(&world.as_unsafe_world_cell()?)?; Ok(Self { base: ReflectBaseType { type_id: TypeId::of::(), @@ -317,7 +317,7 @@ impl ReflectReference { .base .base_id .clone() - .into_ptr(world.as_unsafe_world_cell()) + .into_ptr(world.as_unsafe_world_cell()?) .ok_or_else(|| InteropError::unregistered_base(self.base.clone()))?; // (Ptr) Safety: we use the same type_id to both @@ -371,7 +371,7 @@ impl ReflectReference { .base .base_id .clone() - .into_ptr_mut(world.as_unsafe_world_cell()) + .into_ptr_mut(world.as_unsafe_world_cell()?) .ok_or_else(|| InteropError::unregistered_base(self.base.clone()))?; // (Ptr) Safety: we use the same type_id to both @@ -563,7 +563,7 @@ mod test { use bevy::prelude::{AppTypeRegistry, World}; use crate::bindings::{ - function::script_function::AppScriptFunctionRegistry, AppReflectAllocator, WorldAccessGuard, + function::script_function::AppScriptFunctionRegistry, AppReflectAllocator, }; use super::*; @@ -603,7 +603,7 @@ mod test { .spawn(Component(vec!["hello".to_owned(), "world".to_owned()])) .id(); - let world_guard = WorldGuard::new(WorldAccessGuard::new(&mut world)); + let world_guard = WorldGuard::new(&mut world); let mut component_ref = ReflectReference::new_component_ref::(entity, world_guard.clone()) @@ -683,7 +683,7 @@ mod test { world.insert_resource(Resource(vec!["hello".to_owned(), "world".to_owned()])); - let world_guard = WorldGuard::new(WorldAccessGuard::new(&mut world)); + let world_guard = WorldGuard::new(&mut world); let mut resource_ref = ReflectReference::new_resource_ref::(world_guard.clone()) .expect("could not create resource reference"); @@ -762,7 +762,7 @@ mod test { let value = Component(vec!["hello".to_owned(), "world".to_owned()]); - let world_guard = WorldGuard::new(WorldAccessGuard::new(&mut world)); + let world_guard = WorldGuard::new(&mut world); let allocator = world_guard.allocator(); let mut allocator_write = allocator.write(); let mut allocation_ref = ReflectReference::new_allocated(value, &mut allocator_write); diff --git a/crates/bevy_mod_scripting_core/src/bindings/world.rs b/crates/bevy_mod_scripting_core/src/bindings/world.rs index ffebe007b2..2921db9a0e 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/world.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/world.rs @@ -9,7 +9,7 @@ use super::{ access_map::{AccessCount, AccessMap, ReflectAccessId}, function::{ namespace::Namespace, - script_function::{AppScriptFunctionRegistry, CallerContext, DynamicScriptFunction}, + script_function::{AppScriptFunctionRegistry, DynamicScriptFunction, FunctionCallContext}, }, pretty_print::DisplayWithWorld, script_value::ScriptValue, @@ -27,272 +27,33 @@ use bevy::{ world::{unsafe_world_cell::UnsafeWorldCell, CommandQueue, Mut, World}, }, hierarchy::{BuildChildren, Children, DespawnRecursiveExt, Parent}, - reflect::{std_traits::ReflectDefault, ParsedPath, Reflect, TypeRegistryArc}, + reflect::{std_traits::ReflectDefault, ParsedPath, TypeRegistryArc}, }; use std::{ any::TypeId, borrow::Cow, - cell::RefCell, + cell::{Cell, RefCell}, fmt::Debug, - sync::{Arc, Weak}, + rc::Rc, + sync::Arc, time::Duration, }; /// Prefer to directly using [`WorldAccessGuard`]. If the underlying type changes, this alias will be updated. -pub type WorldGuard<'w> = Arc>; +pub type WorldGuard<'w> = WorldAccessGuard<'w>; /// Similar to [`WorldGuard`], but without the arc, use for when you don't need the outer Arc. pub type WorldGuardRef<'w> = &'w WorldAccessGuard<'w>; -/// While [`WorldAccessGuard`] prevents aliasing at runtime and also makes sure world exists at least as long as the guard itself, -/// borrows sadly do not persist the script-host boundary :(. That is to be expected, but instead we can make an abstraction which removes the lifetime parameter, making the outer type 'static, -/// while making sure the lifetime is still satisfied! -#[derive(Clone, Debug, Reflect)] -#[reflect(opaque)] -pub struct WorldCallbackAccess(pub(crate) Weak>); - -impl WorldCallbackAccess { - /// Wraps a callback which requires access to the world in a 'static way via [`WorldCallbackAccess`]. - pub fn with_callback_access( - world: &mut World, - callback: impl FnOnce(&WorldCallbackAccess) -> T, - ) -> T { - // - the world cannot be dropped before the world drops since we have mutable reference to it in this entire function - // - nothing can alias inappropriately WorldAccessGuard since it's only instance is behind the raw Arc - let world_guard_arc = Arc::new(WorldAccessGuard::new(world)); - let world_guard = unsafe { WorldCallbackAccess::new(Arc::downgrade(&world_guard_arc)) }; - callback(&world_guard) - } - - /// Creates a new [`WorldCallbackAccess`] with an erased lifetime. - /// - /// For safe alternative see [`Self::from_guard`] - /// - /// # Safety - /// - The caller must ensure the [`WorldAccessGuard`] will not outlive the 'w lifetime - /// - In practice this means that between the moment the original Arc is dropped, the lifetime 'w must be valid - /// - I.e. you *must* drop the original [`Arc`] before the original 'w scope ends - pub unsafe fn new<'w>(world: Weak>) -> Self { - // Safety: the caller ensures `WorldAccessGuard` does not outlive the original lifetime 'w - - let world = unsafe { - std::mem::transmute::>, Weak>>( - world, - ) - }; - - Self(world) - } - - pub fn from_guard(world: WorldGuard<'_>) -> Self { - // Safety: the caller ensures `WorldAccessGuard` does not outlive the original lifetime 'w - unsafe { Self::new(Arc::downgrade(&world)) } - } - - /// Attempts to read the world access guard, if it still exists - pub fn try_read(&self) -> Result, InteropError> { - self.0 - .upgrade() - .ok_or_else(InteropError::stale_world_access) - } -} - -/// common world methods, see: -/// - [`crate::bindings::query`] for query related functionality -impl WorldCallbackAccess { - pub fn spawn(&self) -> Result { - let world = self.try_read()?; - Ok(world.spawn()) - } - - // TODO: uses `String` for type_name to avoid lifetime issues with types proxying this via macros - pub fn get_type_by_name( - &self, - type_name: String, - ) -> Result, InteropError> { - let world = self.try_read()?; - Ok(world.get_type_by_name(type_name)) - } - - pub fn get_component_type( - &self, - registration: ScriptTypeRegistration, - ) -> Result, InteropError> { - let world = self.try_read()?; - Ok(world.get_component_type(registration)) - } - - pub fn get_resource_type( - &self, - registration: ScriptTypeRegistration, - ) -> Result, InteropError> { - let world = self.try_read()?; - Ok(world.get_resource_type(registration)) - } - - pub fn add_default_component( - &self, - entity: Entity, - registration: ScriptComponentRegistration, - ) -> Result<(), InteropError> { - let world = self.try_read()?; - world.add_default_component(entity, registration) - } - - pub fn insert_component( - &self, - entity: Entity, - registration: ScriptComponentRegistration, - value: ReflectReference, - ) -> Result<(), InteropError> { - let world = self.try_read()?; - world.insert_component(entity, registration, value) - } - - pub fn get_component( - &self, - entity: Entity, - component_id: ComponentId, - ) -> Result, InteropError> { - let world = self.try_read()?; - world.get_component(entity, component_id) - } - - pub fn has_component( - &self, - entity: Entity, - component_id: ComponentId, - ) -> Result { - let world = self.try_read()?; - world.has_component(entity, component_id) - } - - pub fn remove_component( - &self, - entity: Entity, - registration: ScriptComponentRegistration, - ) -> Result<(), InteropError> { - let world = self.try_read()?; - world.remove_component(entity, registration) - } - - pub fn get_resource( - &self, - resource_id: ComponentId, - ) -> Result, InteropError> { - let world = self.try_read()?; - world.get_resource(resource_id) - } - - pub fn remove_resource( - &self, - registration: ScriptResourceRegistration, - ) -> Result<(), InteropError> { - let world = self.try_read()?; - world.remove_resource(registration) - } - - pub fn has_resource(&self, resource_id: ComponentId) -> Result { - let world = self.try_read()?; - Ok(world.has_resource(resource_id)) - } - - pub fn has_entity(&self, entity: Entity) -> Result { - let world = self.try_read()?; - Ok(world.has_entity(entity)) - } - - pub fn get_children(&self, entity: Entity) -> Result, InteropError> { - let world = self.try_read()?; - world.get_children(entity) - } - - pub fn get_parent(&self, entity: Entity) -> Result, InteropError> { - let world = self.try_read()?; - world.get_parent(entity) - } - - pub fn push_children(&self, parent: Entity, children: &[Entity]) -> Result<(), InteropError> { - let world = self.try_read()?; - world.push_children(parent, children) - } - - pub fn remove_children(&self, parent: Entity, children: &[Entity]) -> Result<(), InteropError> { - let world = self.try_read()?; - world.remove_children(parent, children) - } - - pub fn insert_children( - &self, - parent: Entity, - index: usize, - children: &[Entity], - ) -> Result<(), InteropError> { - let world = self.try_read()?; - world.insert_children(parent, index, children) - } - - pub fn despawn_recursive(&self, entity: Entity) -> Result<(), InteropError> { - let world = self.try_read()?; - world.despawn_recursive(entity) - } - - pub fn despawn(&self, entity: Entity) -> Result<(), InteropError> { - let world = self.try_read()?; - world.despawn(entity) - } - - pub fn despawn_descendants(&self, entity: Entity) -> Result<(), InteropError> { - let world = self.try_read()?; - world.despawn_descendants(entity) - } - - pub fn exit(&self) -> Result<(), InteropError> { - let world = self.try_read()?; - world.exit(); - Ok(()) - } - - /// Tries to call a fitting overload of the function with the given name and in the type id's namespace based on the arguments provided. - /// Currently does this by repeatedly trying each overload until one succeeds or all fail. - pub fn try_call_overloads( - &self, - type_id: TypeId, - name: impl Into>, - args: Vec, - context: CallerContext, - ) -> Result { - let world = self.try_read()?; - let registry = world.script_function_registry(); - let registry = registry.read(); - - let name = name.into(); - let overload_iter = match registry.iter_overloads(Namespace::OnType(type_id), name) { - Ok(iter) => iter, - Err(name) => return Err(InteropError::missing_function(type_id, name.to_string())), - }; - - let mut last_error = None; - for overload in overload_iter { - match overload.call(args.clone(), world.clone(), context) { - Ok(out) => return Ok(out), - Err(e) => last_error = Some(e), - } - } - - Err(last_error.ok_or_else(|| InteropError::invariant("invariant, iterator should always return at least one item, and if the call fails it should return an error"))?) - } -} - pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5); pub const DEFAULT_INTERVAL: Duration = Duration::from_millis(10); /// Provides safe access to the world via [`WorldAccess`] permissions, which enforce aliasing rules at runtime in multi-thread environments #[derive(Clone)] -pub struct WorldAccessGuard<'w>(pub(crate) Arc>); +pub struct WorldAccessGuard<'w>(pub(crate) Rc>); /// Used to decrease the stack size of [`WorldAccessGuard`] pub(crate) struct WorldAccessGuardInner<'w> { - cell: UnsafeWorldCell<'w>, + cell: Cell>>, // TODO: this is fairly hefty, explore sparse sets, bit fields etc pub(crate) accesses: AccessMap, /// Cached for convenience, since we need it for most operations, means we don't need to lock the type registry every time @@ -302,6 +63,22 @@ pub(crate) struct WorldAccessGuardInner<'w> { } impl<'w> WorldAccessGuard<'w> { + /// Safely allows access to the world for the duration of the closure via a static [`WorldAccessGuard`]. + /// + /// The guard is invalidated at the end of the closure, meaning the world cannot be accessed at all after the closure ends. + pub fn with_static_guard( + world: &'w mut World, + f: impl FnOnce(WorldGuard<'static>) -> O, + ) -> O { + let guard = WorldAccessGuard::new(world); + // safety: we invalidate the guard after the closure is called, meaning the world cannot be accessed at all after the 'w lifetime ends + let static_guard: WorldAccessGuard<'static> = unsafe { std::mem::transmute(guard) }; + let o = f(static_guard.clone()); + + static_guard.invalidate(); + o + } + /// Creates a new [`WorldAccessGuard`] for the given mutable borrow of the world. /// /// Creating a guard requires that some resources exist in the world, namely: @@ -318,9 +95,9 @@ impl<'w> WorldAccessGuard<'w> { let function_registry = world .get_resource_or_init::() .clone(); - - Self(Arc::new(WorldAccessGuardInner { - cell: world.as_unsafe_world_cell(), + let cell = Cell::new(Some(world.as_unsafe_world_cell())); + Self(Rc::new(WorldAccessGuardInner { + cell, accesses: Default::default(), allocator, type_registry, @@ -328,6 +105,11 @@ impl<'w> WorldAccessGuard<'w> { })) } + /// Invalidates the world access guard, making it unusable. + pub fn invalidate(&self) { + self.0.cell.replace(None); + } + /// Begins a new access scope. Currently this simply throws an erorr if there are any accesses held. Should only be used in a single-threaded context pub(crate) fn begin_access_scope(&self) -> Result<(), InteropError> { if self.0.accesses.count_accesses() != 0 { @@ -351,23 +133,29 @@ impl<'w> WorldAccessGuard<'w> { /// Retrieves the underlying unsafe world cell, with no additional guarantees of safety /// proceed with caution and only use this if you understand what you're doing - pub fn as_unsafe_world_cell(&self) -> UnsafeWorldCell<'w> { - self.0.cell + pub fn as_unsafe_world_cell(&self) -> Result, InteropError> { + self.0.cell.get().ok_or_else(InteropError::missing_world) } /// Retrieves the underlying read only unsafe world cell, with no additional guarantees of safety /// proceed with caution and only use this if you understand what you're doing - pub fn as_unsafe_world_cell_readonly(&self) -> UnsafeWorldCell<'w> { - self.0.cell + pub fn as_unsafe_world_cell_readonly(&self) -> Result, InteropError> { + self.0.cell.get().ok_or_else(InteropError::missing_world) } /// Gets the component id of the given component or resource - pub fn get_component_id(&self, id: TypeId) -> Option { - self.0.cell.components().get_id(id) + pub fn get_component_id(&self, id: TypeId) -> Result, InteropError> { + Ok(self + .as_unsafe_world_cell_readonly()? + .components() + .get_id(id)) } - pub fn get_resource_id(&self, id: TypeId) -> Option { - self.0.cell.components().get_resource_id(id) + pub fn get_resource_id(&self, id: TypeId) -> Result, InteropError> { + Ok(self + .as_unsafe_world_cell_readonly()? + .components() + .get_resource_id(id)) } pub fn get_access_location( @@ -424,6 +212,19 @@ impl<'w> WorldAccessGuard<'w> { self.0.function_registry.clone() } + /// Claims access to the world for the duration of the closure, allowing for global access to the world. + #[track_caller] + pub fn with_global_access O, O>( + &self, + f: F, + ) -> Result { + with_global_access!(self.0.accesses, "Could not claim exclusive world access", { + // safety: we have global access for the duration of the closure + let world = unsafe { self.as_unsafe_world_cell()?.world_mut() }; + Ok(f(world)) + })? + } + /// Safely accesses the resource by claiming and releasing access to it. /// /// # Panics @@ -433,7 +234,8 @@ impl<'w> WorldAccessGuard<'w> { R: Resource, F: FnOnce(&R) -> O, { - let access_id = ReflectAccessId::for_resource::(&self.0.cell)?; + let cell = self.as_unsafe_world_cell()?; + let access_id = ReflectAccessId::for_resource::(&cell)?; with_access_read!( self.0.accesses, @@ -442,7 +244,7 @@ impl<'w> WorldAccessGuard<'w> { { // Safety: we have acquired access for the duration of the closure f(unsafe { - self.0.cell.get_resource::().ok_or_else(|| { + cell.get_resource::().ok_or_else(|| { InteropError::unregistered_component_or_resource_type( std::any::type_name::(), ) @@ -461,7 +263,8 @@ impl<'w> WorldAccessGuard<'w> { R: Resource, F: FnOnce(Mut) -> O, { - let access_id = ReflectAccessId::for_resource::(&self.0.cell)?; + let cell = self.as_unsafe_world_cell()?; + let access_id = ReflectAccessId::for_resource::(&cell)?; with_access_write!( self.0.accesses, access_id, @@ -469,7 +272,7 @@ impl<'w> WorldAccessGuard<'w> { { // Safety: we have acquired access for the duration of the closure f(unsafe { - self.0.cell.get_resource_mut::().ok_or_else(|| { + cell.get_resource_mut::().ok_or_else(|| { InteropError::unregistered_component_or_resource_type( std::any::type_name::(), ) @@ -488,14 +291,15 @@ impl<'w> WorldAccessGuard<'w> { T: Component, F: FnOnce(Option<&T>) -> O, { - let access_id = ReflectAccessId::for_component::(&self.0.cell)?; + let cell = self.as_unsafe_world_cell()?; + let access_id = ReflectAccessId::for_component::(&cell)?; with_access_read!( self.0.accesses, access_id, format!("Could not access component: {}", std::any::type_name::()), { // Safety: we have acquired access for the duration of the closure - f(unsafe { self.0.cell.get_entity(entity).and_then(|e| e.get::()) }) + f(unsafe { cell.get_entity(entity).and_then(|e| e.get::()) }) } ) } @@ -509,7 +313,8 @@ impl<'w> WorldAccessGuard<'w> { T: Component, F: FnOnce(Option>) -> O, { - let access_id = ReflectAccessId::for_component::(&self.0.cell)?; + let cell = self.as_unsafe_world_cell()?; + let access_id = ReflectAccessId::for_component::(&cell)?; with_access_write!( self.0.accesses, @@ -517,12 +322,7 @@ impl<'w> WorldAccessGuard<'w> { format!("Could not access component: {}", std::any::type_name::()), { // Safety: we have acquired access for the duration of the closure - f(unsafe { - self.0 - .cell - .get_entity(entity) - .and_then(|e| e.get_mut::()) - }) + f(unsafe { cell.get_entity(entity).and_then(|e| e.get_mut::()) }) } ) } @@ -550,17 +350,46 @@ impl<'w> WorldAccessGuard<'w> { } /// checks if a given entity exists and is valid - pub fn is_valid_entity(&self, entity: Entity) -> bool { - self.0.cell.get_entity(entity).is_some() + pub fn is_valid_entity(&self, entity: Entity) -> Result { + let cell = self.as_unsafe_world_cell()?; + Ok(cell.get_entity(entity).is_some()) + } + + /// Tries to call a fitting overload of the function with the given name and in the type id's namespace based on the arguments provided. + /// Currently does this by repeatedly trying each overload until one succeeds or all fail. + pub fn try_call_overloads( + &self, + type_id: TypeId, + name: impl Into>, + args: Vec, + context: FunctionCallContext, + ) -> Result { + let registry = self.script_function_registry(); + let registry = registry.read(); + + let name = name.into(); + let overload_iter = match registry.iter_overloads(Namespace::OnType(type_id), name) { + Ok(iter) => iter, + Err(name) => return Err(InteropError::missing_function(type_id, name.to_string())), + }; + + let mut last_error = None; + for overload in overload_iter { + match overload.call(args.clone(), context) { + Ok(out) => return Ok(out), + Err(e) => last_error = Some(e), + } + } + + Err(last_error.ok_or_else(|| InteropError::invariant("invariant, iterator should always return at least one item, and if the call fails it should return an error"))?) } } /// Impl block for higher level world methods impl WorldAccessGuard<'_> { - pub fn spawn(&self) -> Entity { - with_global_access!(self.0.accesses, "Could not spawn entity", { - // Safety we have global access - let entity = unsafe { self.0.cell.world_mut().spawn_empty() }; + pub fn spawn(&self) -> Result { + self.with_global_access(|world| { + let entity = world.spawn_empty(); entity.id() }) } @@ -577,21 +406,21 @@ impl WorldAccessGuard<'_> { pub fn get_component_type( &self, registration: ScriptTypeRegistration, - ) -> Result { - match self.get_component_id(registration.type_id()) { + ) -> Result, InteropError> { + Ok(match self.get_component_id(registration.type_id())? { Some(comp_id) => Ok(ScriptComponentRegistration::new(registration, comp_id)), None => Err(registration), - } + }) } pub fn get_resource_type( &self, registration: ScriptTypeRegistration, - ) -> Result { - match self.get_resource_id(registration.type_id()) { + ) -> Result, InteropError> { + Ok(match self.get_resource_id(registration.type_id())? { Some(resource_id) => Ok(ScriptResourceRegistration::new(registration, resource_id)), None => Err(registration), - } + }) } pub fn add_default_component( @@ -599,6 +428,7 @@ impl WorldAccessGuard<'_> { entity: Entity, registration: ScriptComponentRegistration, ) -> Result<(), InteropError> { + // let cell = self.as_unsafe_world_cell()?; let component_data = registration .type_registration() .type_registration() @@ -622,10 +452,7 @@ impl WorldAccessGuard<'_> { .type_registration() .data::() { - with_global_access!(self.0.accesses, "Could not add default component", { - let world = unsafe { self.0.cell.world_mut() }; - from_world_td.from_world(world) - }) + self.with_global_access(|world| from_world_td.from_world(world))? } else { return Err(InteropError::missing_type_data( registration.registration.type_id(), @@ -634,9 +461,8 @@ impl WorldAccessGuard<'_> { }; // TODO: this shouldn't need entire world access it feels - with_global_access!(self.0.accesses, "Could not add default component", { + self.with_global_access(|world| { let type_registry = self.type_registry(); - let world = unsafe { self.0.cell.world_mut() }; let mut entity = world .get_entity_mut(entity) @@ -646,7 +472,7 @@ impl WorldAccessGuard<'_> { component_data.insert(&mut entity, instance.as_partial_reflect(), ®istry); } Ok(()) - }) + })? } pub fn insert_component( @@ -667,19 +493,19 @@ impl WorldAccessGuard<'_> { })?; with_global_access!(self.0.accesses, "Could not insert element", { + let cell = self.as_unsafe_world_cell()?; let type_registry = self.type_registry(); let type_registry = type_registry.read(); - let world_mut = unsafe { self.0.cell.world_mut() }; + let world_mut = unsafe { cell.world_mut() }; let mut entity = world_mut .get_entity_mut(entity) .map_err(|_| InteropError::missing_entity(entity))?; - // TODO: is this fine? creating a new arc here? - // Safety: we have global access, we are only accessing the entity and component - let ref_ = unsafe { value.reflect_unsafe(Arc::new(self.clone()))? }; + + let ref_ = unsafe { value.reflect_unsafe(self.clone())? }; component_data.apply_or_insert(&mut entity, ref_, &type_registry); Ok(()) - }) + })? } pub fn get_component( @@ -687,15 +513,12 @@ impl WorldAccessGuard<'_> { entity: Entity, component_id: ComponentId, ) -> Result, InteropError> { - let entity = self - .0 - .cell + let cell = self.as_unsafe_world_cell()?; + let entity = cell .get_entity(entity) .ok_or_else(|| InteropError::missing_entity(entity))?; - let component_info = self - .0 - .cell + let component_info = cell .components() .get_info(component_id) .ok_or_else(|| InteropError::invalid_component(component_id))?; @@ -727,9 +550,8 @@ impl WorldAccessGuard<'_> { entity: Entity, component_id: ComponentId, ) -> Result { - let entity = self - .0 - .cell + let cell = self.as_unsafe_world_cell()?; + let entity = cell .get_entity(entity) .ok_or_else(|| InteropError::missing_entity(entity))?; @@ -753,21 +575,21 @@ impl WorldAccessGuard<'_> { })?; // TODO: this shouldn't need entire world access it feels - with_global_access!(self.0.accesses, "Could not remove component", { - let world = unsafe { self.0.cell.world_mut() }; + self.with_global_access(|world| { let mut entity = world .get_entity_mut(entity) .map_err(|_| InteropError::missing_entity(entity))?; component_data.remove(&mut entity); Ok(()) - }) + })? } pub fn get_resource( &self, resource_id: ComponentId, ) -> Result, InteropError> { - let component_info = match self.0.cell.components().get_info(resource_id) { + let cell = self.as_unsafe_world_cell()?; + let component_info = match cell.components().get_info(resource_id) { Some(info) => info, None => return Ok(None), }; @@ -808,25 +630,22 @@ impl WorldAccessGuard<'_> { })?; // TODO: this shouldn't need entire world access it feels - with_global_access!(self.0.accesses, "Could not remove resource", { - let world = unsafe { self.0.cell.world_mut() }; - component_data.remove(world); - Ok(()) - }) + self.with_global_access(|world| component_data.remove(world)) } - pub fn has_resource(&self, resource_id: ComponentId) -> bool { + pub fn has_resource(&self, resource_id: ComponentId) -> Result { + let cell = self.as_unsafe_world_cell()?; // Safety: we are not reading the value at all - let res_ptr = unsafe { self.0.cell.get_resource_by_id(resource_id) }; - res_ptr.is_some() + let res_ptr = unsafe { cell.get_resource_by_id(resource_id) }; + Ok(res_ptr.is_some()) } - pub fn has_entity(&self, entity: Entity) -> bool { + pub fn has_entity(&self, entity: Entity) -> Result { self.is_valid_entity(entity) } pub fn get_children(&self, entity: Entity) -> Result, InteropError> { - if !self.is_valid_entity(entity) { + if !self.is_valid_entity(entity)? { return Err(InteropError::missing_entity(entity)); } @@ -836,7 +655,7 @@ impl WorldAccessGuard<'_> { } pub fn get_parent(&self, entity: Entity) -> Result, InteropError> { - if !self.is_valid_entity(entity) { + if !self.is_valid_entity(entity)? { return Err(InteropError::missing_entity(entity)); } @@ -845,46 +664,38 @@ impl WorldAccessGuard<'_> { pub fn push_children(&self, parent: Entity, children: &[Entity]) -> Result<(), InteropError> { // verify entities exist - if !self.is_valid_entity(parent) { + if !self.is_valid_entity(parent)? { return Err(InteropError::missing_entity(parent)); } for c in children { - if !self.is_valid_entity(*c) { + if !self.is_valid_entity(*c)? { return Err(InteropError::missing_entity(*c)); } } - - with_global_access!(self.0.accesses, "Could not push children", { - let world = unsafe { self.0.cell.world_mut() }; + self.with_global_access(|world| { let mut queue = CommandQueue::default(); let mut commands = Commands::new(&mut queue, world); commands.entity(parent).add_children(children); queue.apply(world); - }); - - Ok(()) + }) } pub fn remove_children(&self, parent: Entity, children: &[Entity]) -> Result<(), InteropError> { - if !self.is_valid_entity(parent) { + if !self.is_valid_entity(parent)? { return Err(InteropError::missing_entity(parent)); } for c in children { - if !self.is_valid_entity(*c) { + if !self.is_valid_entity(*c)? { return Err(InteropError::missing_entity(*c)); } } - - with_global_access!(self.0.accesses, "Could not remove children", { - let world = unsafe { self.0.cell.world_mut() }; + self.with_global_access(|world| { let mut queue = CommandQueue::default(); let mut commands = Commands::new(&mut queue, world); commands.entity(parent).remove_children(children); queue.apply(world); - }); - - Ok(()) + }) } pub fn insert_children( @@ -893,1028 +704,98 @@ impl WorldAccessGuard<'_> { index: usize, children: &[Entity], ) -> Result<(), InteropError> { - if !self.is_valid_entity(parent) { + if !self.is_valid_entity(parent)? { return Err(InteropError::missing_entity(parent)); } for c in children { - if !self.is_valid_entity(*c) { + if !self.is_valid_entity(*c)? { return Err(InteropError::missing_entity(*c)); } } - with_global_access!(self.0.accesses, "Could not insert children", { - let world = unsafe { self.0.cell.world_mut() }; + self.with_global_access(|world| { let mut queue = CommandQueue::default(); let mut commands = Commands::new(&mut queue, world); commands.entity(parent).insert_children(index, children); queue.apply(world); - }); - - Ok(()) + }) } pub fn despawn_recursive(&self, parent: Entity) -> Result<(), InteropError> { - if !self.is_valid_entity(parent) { + if !self.is_valid_entity(parent)? { return Err(InteropError::missing_entity(parent)); } - - with_global_access!(self.0.accesses, "Could not despawn entity", { - let world = unsafe { self.0.cell.world_mut() }; + self.with_global_access(|world| { let mut queue = CommandQueue::default(); let mut commands = Commands::new(&mut queue, world); commands.entity(parent).despawn_recursive(); queue.apply(world); - }); - - Ok(()) + }) } pub fn despawn(&self, entity: Entity) -> Result<(), InteropError> { - if !self.is_valid_entity(entity) { + if !self.is_valid_entity(entity)? { return Err(InteropError::missing_entity(entity)); } - with_global_access!(self.0.accesses, "Could not despawn entity", { - let world = unsafe { self.0.cell.world_mut() }; + self.with_global_access(|world| { let mut queue = CommandQueue::default(); let mut commands = Commands::new(&mut queue, world); commands.entity(entity).despawn(); queue.apply(world); - }); - - Ok(()) + }) } pub fn despawn_descendants(&self, parent: Entity) -> Result<(), InteropError> { - if !self.is_valid_entity(parent) { + if !self.is_valid_entity(parent)? { return Err(InteropError::missing_entity(parent)); } - with_global_access!(self.0.accesses, "Could not despawn descendants", { - let world = unsafe { self.0.cell.world_mut() }; + self.with_global_access(|world| { let mut queue = CommandQueue::default(); let mut commands = Commands::new(&mut queue, world); commands.entity(parent).despawn_descendants(); queue.apply(world); - }); - - Ok(()) + }) } /// Sends AppExit event to the world with success status - pub fn exit(&self) { - with_global_access!(self.0.accesses, "Could not exit the app", { - let world = unsafe { self.0.cell.world_mut() }; + pub fn exit(&self) -> Result<(), InteropError> { + self.with_global_access(|world| { world.send_event(AppExit::Success); - }); + }) } } /// Utility type for accessing the world in a callback pub trait WorldContainer { type Error: Debug; - /// Sets the world to the given value - fn set_world(&mut self, world: WorldCallbackAccess) -> Result<(), Self::Error>; - - /// Tries to get the world - fn try_get_world(&self) -> Result>, Self::Error>; + /// Sets the world to the given value in the container + fn set_world(&mut self, world: WorldGuard<'static>) -> Result<(), Self::Error>; - fn try_get_callback_world(&self) -> Result; + /// Tries to get the world from the container + fn try_get_world(&self) -> Result, Self::Error>; } /// A world container that stores the world in a thread local pub struct ThreadWorldContainer; thread_local! { - static WORLD_CALLBACK_ACCESS: RefCell> = const { RefCell::new(None) }; + static WORLD_CALLBACK_ACCESS: RefCell>> = const { RefCell::new(None) }; } impl WorldContainer for ThreadWorldContainer { type Error = InteropError; - fn set_world(&mut self, world: WorldCallbackAccess) -> Result<(), Self::Error> { + fn set_world(&mut self, world: WorldGuard<'static>) -> Result<(), Self::Error> { WORLD_CALLBACK_ACCESS.with(|w| { w.replace(Some(world)); }); Ok(()) } - fn try_get_world(&self) -> Result>, Self::Error> { - WORLD_CALLBACK_ACCESS.with(|w| { - w.borrow() - .as_ref() - .map(|w| w.try_read()) - .ok_or_else(InteropError::missing_world) - })? - } - - fn try_get_callback_world(&self) -> Result { - WORLD_CALLBACK_ACCESS.with(|w| { - w.borrow() - .as_ref() - .cloned() - // .map(|w| w.try_read()) - .ok_or_else(InteropError::missing_world) - }) + fn try_get_world(&self) -> Result, Self::Error> { + WORLD_CALLBACK_ACCESS.with(|w| w.borrow().clone().ok_or_else(InteropError::missing_world)) } } - -// #[cfg(test)] -// mod test { -// use crate::bindings::ScriptTypeRegistration; -// use crate::prelude::ScriptErrorKind; -// use bevy::ecs::system::Commands; -// use bevy::hierarchy::BuildChildren; -// use bevy::reflect::{ParsedPath, Reflect}; - -// use super::*; -// use std::any::TypeId; - -// use crate::{ -// bindings::ReflectAllocator, -// bindings::{ -// DeferredReflection, ReflectBase, ReflectBaseType, ReflectReference, ReflectionPathElem, -// }, -// }; - -// use bevy::ecs::world::World; -// use test_utils::test_data::{ -// setup_world, CompWithFromWorld, GetTestComponentId, TestComponent, TestResource, -// }; - -// fn init_world() -> World { -// setup_world(|w, _| { -// w.init_resource::(); -// }) -// } - -// /// Tests that the given ref_ can be accessed and the value is as expected and access is released correctly (not for allocated values) -// fn assert_access_yields< -// O: Reflect + PartialEq + Debug, -// F: FnOnce(&mut World) -> ReflectReference, -// G: FnOnce(&WorldAccessGuard), -// >( -// init: F, -// post_check: G, -// expected: O, -// ) { -// let mut world = init_world(); -// let ref_ = init(&mut world); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let world = world.read().unwrap(); - -// // test read -// ref_.with_reflect(&world, |reflect| { -// let orig = reflect.try_downcast_ref::(); - -// let orig = match orig { -// Some(v) => v, -// None => { -// panic!( -// "Could not downcast value {reflect:?} to {}", -// std::any::type_name::() -// ) -// } -// }; - -// assert_eq!(orig, &expected); -// }); - -// assert!( -// world -// .get_component_access(TestComponent::test_component_id(), true) -// .is_some(), -// "access to component was not release correctly" -// ); - -// assert!( -// world -// .get_resource_access(TestResource::test_component_id()) -// .is_some(), -// "access to component was not release correctly" -// ); - -// post_check(&world); -// }); -// } - -// /// Tests that setting to the expected value works as well as follow up reads give the expected value -// fn assert_set_then_get_yields< -// O: Reflect + PartialEq + Debug + Clone, -// F: FnOnce(&mut World) -> ReflectReference, -// G: FnOnce(&WorldAccessGuard), -// >( -// init: F, -// post_check: G, -// expected: O, -// ) { -// let mut world = init_world(); -// let ref_ = init(&mut world); -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let world = world.read().unwrap(); -// // test set -// ref_.with_reflect_mut(&world, |reflect, _, _| { -// let orig = reflect.try_downcast_mut::(); - -// let orig = match orig { -// Some(v) => v, -// None => { -// panic!( -// "Could not downcast value {reflect:?} to {}", -// std::any::type_name::() -// ) -// } -// }; - -// *orig = expected.clone(); -// }); - -// // test read -// ref_.with_reflect(&world, |reflect, _, _| { -// let orig = reflect.try_downcast_ref::(); - -// let orig = match orig { -// Some(v) => v, -// None => { -// panic!( -// "Could not downcast value {reflect:?} to {}", -// std::any::type_name::() -// ) -// } -// }; - -// assert_eq!(orig, &expected); -// }); -// post_check(&world); -// }); -// } - -// #[test] -// fn test_component_access() { -// let init = |world: &mut World| { -// let entity = world -// .spawn(TestComponent { -// strings: vec![String::from("initial")], -// }) -// .id(); - -// ReflectReference { -// base: ReflectBaseType { -// base_id: ReflectBase::Component(entity, TestComponent::test_component_id()), -// type_id: TypeId::of::(), -// }, -// reflect_path: vec![ -// ReflectionPathElem::Reflection(ParsedPath::parse_static(".strings").unwrap()), -// ReflectionPathElem::DeferredReflection(DeferredReflection { -// get: Arc::new(|root| { -// let strings = root.try_downcast_ref::>().unwrap(); -// Ok(strings.first().unwrap()) -// }), -// get_mut: Arc::new(|root| { -// let strings = root.try_downcast_mut::>().unwrap(); -// Ok(strings.first_mut().unwrap()) -// }), -// }), -// ], -// } -// }; - -// assert_access_yields(init, |_| {}, String::from("initial")); -// assert_set_then_get_yields(init, |_| {}, String::from("set")); -// } - -// #[test] -// fn test_resource_access() { -// let init = |world: &mut World| { -// world.insert_resource(TestResource { bytes: vec![42u8] }); - -// ReflectReference { -// base: ReflectBaseType { -// base_id: ReflectBase::Resource(TestResource::test_component_id()), -// type_id: TypeId::of::(), -// }, -// reflect_path: vec![ -// ReflectionPathElem::Reflection(ParsedPath::parse_static(".bytes").unwrap()), -// ReflectionPathElem::DeferredReflection(DeferredReflection { -// get: Arc::new(|root| { -// let strings = root.try_downcast_ref::>().unwrap(); -// Ok(strings.first().unwrap()) -// }), -// get_mut: Arc::new(|root| { -// let strings = root.try_downcast_mut::>().unwrap(); -// Ok(strings.first_mut().unwrap()) -// }), -// }), -// ], -// } -// }; -// assert_access_yields(init, |_| {}, 42u8); -// assert_set_then_get_yields(init, |_| {}, 69u8); -// } - -// #[test] -// fn test_script_alloc_access() { -// let init = |world: &mut World| { -// let mut script_allocator = ReflectAllocator::default(); -// let mut ref_ = ReflectReference::new_allocated( -// TestComponent { -// strings: vec![String::from("initial")], -// }, -// &mut script_allocator, -// ); -// ref_.index_path(ParsedPath::parse_static(".strings").unwrap()); -// ref_.index_path(DeferredReflection { -// get: Arc::new(|root| { -// let strings = root.try_downcast_ref::>().unwrap(); -// Ok(strings.first().unwrap()) -// }), -// get_mut: Arc::new(|root| { -// let strings = root.try_downcast_mut::>().unwrap(); -// Ok(strings.first_mut().unwrap()) -// }), -// }); -// world.insert_resource(script_allocator); -// ref_ -// }; -// let post_check = |world: &WorldAccessGuard| { -// assert!( -// world -// .get_allocation_access(ReflectAllocationId(0)) -// .is_some(), -// "allocation access was not released correctly" -// ); -// }; -// assert_access_yields(init, post_check, String::from("initial")); -// assert_set_then_get_yields(init, post_check, String::from("set")); -// } - -// #[test] -// #[allow(clippy::drop_non_drop)] -// fn test_invalid_runtime_access() { -// let mut world = World::new(); -// let world = WorldAccessGuard::new(&mut world); -// let access = world.get_component_access(ComponentId::new(0)); -// assert!( -// world.get_component_access(ComponentId::new(0)).is_none(), -// "access was allowed to alias" -// ); -// drop(access); -// } - -// fn get_reg(world: &WorldCallbackAccess, name: &str) -> ScriptTypeRegistration { -// world -// .get_type_by_name(name.to_owned()) -// .expect("Type not found") -// } - -// fn test_comp_reg(world: &WorldCallbackAccess) -> ScriptTypeRegistration { -// world -// .get_type_by_name("TestComponent".to_owned()) -// .expect("Component not found") -// } - -// fn test_resource_reg(world: &WorldCallbackAccess) -> ScriptTypeRegistration { -// world -// .get_type_by_name("TestResource".to_owned()) -// .expect("Resource not found") -// } - -// #[test] -// fn test_get_type_by_name() { -// let mut world = init_world(); -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let comp_reg = world.get_type_by_name("TestComponent".to_owned()).unwrap(); -// let resource_reg = world.get_type_by_name("TestResource".to_owned()).unwrap(); - -// assert_eq!( -// comp_reg.registration.type_info().type_id(), -// std::any::TypeId::of::() -// ); -// assert_eq!( -// resource_reg.registration.type_info().type_id(), -// std::any::TypeId::of::() -// ); -// }); -// } - -// #[test] -// fn test_get_type_by_name_invalid() { -// let mut world = init_world(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let comp_reg = world.get_type_by_name("x".to_owned()); -// let resource_reg = world.get_type_by_name("z".to_owned()); - -// assert!(comp_reg.is_none()); -// assert!(resource_reg.is_none()); -// }); -// } - -// #[test] -// fn test_add_default_component_from_world() { -// let mut world = init_world(); - -// let entity = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let comp_reg = get_reg(world, "CompWithFromWorld"); -// world.add_default_component(entity, comp_reg).unwrap() -// }); - -// assert_eq!( -// world.get_entity(entity).unwrap().get::(), -// Some(&CompWithFromWorld(String::from("FromWorld"))) -// ); -// } - -// #[test] -// fn test_add_default_component_default() { -// let mut world = init_world(); - -// let entity = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let comp_reg = get_reg(world, "CompWithFromWorld"); -// world.add_default_component(entity, comp_reg).unwrap() -// }); - -// assert_eq!( -// world.get_entity(entity).unwrap().get::(), -// Some(&CompWithFromWorld(String::from("Default"))) -// ); -// } - -// #[test] -// fn test_add_default_component_missing_from_world_and_default() { -// let mut world = init_world(); - -// let entity = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let comp_reg = get_reg(world, "CompWithFromWorld"); -// match world.add_default_component(entity, comp_reg.clone()) { -// Ok(_) => { -// panic!("Expected error") -// } -// Err(ScriptError(inner)) => { -// assert_eq!(inner.kind, ScriptErrorKind::RuntimeError); -// assert_eq!(inner.reason.to_string(), format!("Cannot add default component since type: `{}`, Does not have ReflectDefault or ReflectFromWorld data registered.", comp_reg.registration.type_info().type_path())); -// } -// } -// }); -// } - -// #[test] -// fn test_add_default_component_missing_component_data() { -// let mut world = init_world(); - -// let entity = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let comp_reg = get_reg(world, "CompWithFromWorld"); -// match world.add_default_component(entity, comp_reg.clone()) { -// Ok(_) => { -// panic!("Expected error") -// } -// Err(ScriptError(inner)) => { -// assert_eq!(inner.kind, ScriptErrorKind::RuntimeError); -// assert_eq!(inner.reason.to_string(), format!("Cannot add default component since type: `{}`, Does not have ReflectComponent data registered.", comp_reg.registration.type_info().type_path())); -// } -// } -// }); -// } - -// #[test] -// fn test_get_component_existing() { -// let mut world = init_world(); - -// let entity = world.spawn(TestComponent { strings: vec![] }).id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let comp_ref = world -// .get_component(entity, TestComponent::test_component_id()) -// .unwrap() -// .unwrap(); -// assert_eq!( -// comp_ref, -// ReflectReference { -// base: ReflectBaseType { -// type_id: std::any::TypeId::of::(), -// base_id: ReflectBase::Component(entity, TestComponent::test_component_id()), -// }, -// reflect_path: Default::default(), -// } -// ); -// }); -// } - -// #[test] -// fn test_get_component_missing() { -// let mut world = init_world(); - -// let entity = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let comp_ref = world -// .get_component(entity, TestComponent::test_component_id()) -// .unwrap(); -// assert_eq!(comp_ref, None); -// }); -// } - -// #[test] -// fn test_get_component_missing_entity() { -// let mut world = init_world(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let comp_ref = -// world.get_component(Entity::from_raw(0), TestComponent::test_component_id()); -// match comp_ref { -// Ok(_) => { -// panic!("Expected error") -// } -// Err(e) => { -// assert_eq!(e.kind, ScriptErrorKind::RuntimeError); -// assert_eq!(e.reason.to_string(), "Entity does not exist: 0v1"); -// } -// } -// }); -// } - -// #[test] -// fn test_get_component_unregistered_component() { -// let mut world = init_world(); - -// let entity = world.spawn_empty().id(); -// let fake_id = ComponentId::new(999); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let comp_ref = world.get_component(entity, fake_id); -// match comp_ref { -// Ok(_) => { -// panic!("Expected error") -// } -// Err(e) => { -// assert_eq!(e.kind, ScriptErrorKind::RuntimeError); -// assert_eq!( -// e.reason.to_string(), -// format!("Component does not exist: {fake_id:?}"), -// ); -// } -// } -// }); -// } - -// #[test] -// fn test_remove_component() { -// let mut world = init_world(); - -// let entity = world -// .spawn(TestComponent { -// strings: vec![String::from("strings")], -// }) -// .id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world -// .remove_component(entity, test_comp_reg(world)) -// .unwrap(); -// }); - -// assert_eq!( -// world.get_entity(entity).unwrap().get::(), -// None -// ); -// } - -// #[test] -// fn test_remove_component_empty_idempotent() { -// let mut world = init_world(); - -// let entity = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world -// .remove_component(entity, test_comp_reg(world)) -// .unwrap(); -// }); - -// assert_eq!( -// world.get_entity(entity).unwrap().get::(), -// None -// ); -// } - -// #[test] -// fn test_remove_component_missing_comp_registration() { -// let mut world = init_world(); - -// let entity = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let result = world.remove_component(entity, test_resource_reg(world)); -// match result { -// Ok(_) => { -// panic!("Expected error") -// } -// Err(e) => { -// assert_eq!(e.kind, ScriptErrorKind::RuntimeError); -// assert_eq!( -// e.reason.to_string(), -// format!("Cannot remove component since type: `{}`, Does not have ReflectComponent data registered.", test_resource_reg(world).registration.type_info().type_path()) -// ); -// } -// } -// }); - -// assert_eq!( -// world.get_entity(entity).unwrap().get::(), -// None -// ); -// } - -// #[test] -// fn test_remove_component_missing_entity() { -// let mut world = init_world(); - -// let fake_entity = Entity::from_raw(0); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let result = world.remove_component(fake_entity, test_comp_reg(world)); -// match result { -// Ok(_) => { -// panic!("Expected error") -// } -// Err(e) => { -// assert_eq!(e.kind, ScriptErrorKind::RuntimeError); -// assert_eq!(e.reason.to_string(), "Entity does not exist: 0v1"); -// } -// } -// }); -// } - -// #[test] -// fn test_get_resource_existing() { -// let mut world = init_world(); - -// world.insert_resource(TestResource { bytes: vec![1] }); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let comp_ref = world -// .get_resource(TestResource::test_component_id()) -// .unwrap() -// .unwrap(); -// assert_eq!( -// comp_ref, -// ReflectReference { -// base: ReflectBaseType { -// type_id: std::any::TypeId::of::(), -// base_id: ReflectBase::Resource(TestResource::test_component_id()), -// }, -// reflect_path: Default::default(), -// } -// ); -// }); -// } - -// #[test] -// fn test_get_resource_non_existing() { -// let mut world = init_world(); - -// let fake_comp = ComponentId::new(999); -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let comp_ref = world.get_resource(fake_comp); -// match comp_ref { -// Ok(None) => {} -// e => { -// panic!("Expected ok with none, got: {:?}", e); -// } -// } -// }); -// } - -// #[test] -// fn test_remove_resource() { -// let mut world = init_world(); - -// world.insert_resource(TestResource { bytes: vec![1] }); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world.remove_resource(test_resource_reg(world)).unwrap(); -// }); - -// assert_eq!(world.get_resource::(), None); -// } - -// #[test] -// fn test_remove_resource_missing_idempotent() { -// let mut world = init_world(); - -// world.remove_resource::(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world.remove_resource(test_resource_reg(world)).unwrap(); -// }); - -// assert_eq!(world.get_resource::(), None); -// } - -// #[test] -// fn test_remove_resource_missing_resource_registration() { -// let mut world = init_world(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// match world.remove_resource(test_comp_reg(world)) { -// Ok(_) => panic!("Expected error"), -// Err(e) => { -// assert_eq!(e.kind, ScriptErrorKind::RuntimeError); -// assert_eq!(e.reason.to_string(), format!("Cannot remove resource since type: `{}`, Does not have ReflectResource data registered.", test_comp_reg(world).registration.type_info().type_path())); -// } -// } -// }); -// } - -// #[test] -// fn test_has_resource_existing() { -// let mut world = init_world(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// assert!(world.has_resource(TestResource::test_component_id())); -// }); -// } - -// #[test] -// fn test_has_resource_missing() { -// let mut world = init_world(); - -// world.remove_resource::(); -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// assert!(world.has_resource(TestResource::test_component_id())); -// }); -// } - -// #[test] -// fn test_get_children_1_child() { -// let mut world = init_world(); - -// let parent = world.spawn_empty().id(); -// let child = world.spawn_empty().id(); -// let mut cmnds = CommandQueue::default(); -// let mut cmnd = Commands::new(&mut cmnds, &world); -// cmnd.entity(parent).add_children(&[child]); -// cmnds.apply(&mut world); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let children = world.get_children(parent).unwrap(); -// assert_eq!(children.len(), 1); -// assert_eq!(children[0], child); -// }); -// } - -// #[test] -// #[should_panic( -// expected = "Component not registered: `bevy_hierarchy::components::children::Children`" -// )] -// fn test_get_children_children_component_unregistered() { -// let mut world = init_world(); - -// let parent = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world.get_children(parent).unwrap(); -// }); -// } - -// #[test] -// fn test_get_children_no_children() { -// let mut world = init_world(); - -// world.register_component::(); -// let parent = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let children = world.get_children(parent).unwrap(); -// assert_eq!(children.len(), 0); -// }); -// } - -// #[test] -// fn test_get_parent_1_parent() { -// let mut world = init_world(); - -// let parent = world.spawn_empty().id(); -// let child = world.spawn_empty().id(); -// let mut cmnds = CommandQueue::default(); -// let mut cmnd = Commands::new(&mut cmnds, &world); -// cmnd.entity(parent).add_children(&[child]); -// cmnds.apply(&mut world); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let found_parent = world.get_parent(child).unwrap(); -// assert_eq!(found_parent, Some(parent)); -// }); -// } - -// #[test] -// fn test_get_parent_no_parent() { -// let mut world = init_world(); - -// world.register_component::(); - -// let child = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// let found_parent = world.get_parent(child).unwrap(); -// assert_eq!(found_parent, None); -// }); -// } - -// #[test] -// #[should_panic( -// expected = "Component not registered: `bevy_hierarchy::components::parent::Parent`" -// )] -// fn test_get_parent_parent_component_unregistered() { -// let mut world = init_world(); - -// let child = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world.get_parent(child).unwrap(); -// }); -// } - -// #[test] -// fn test_push_children_empty_entity() { -// let mut world = init_world(); - -// let parent = world.spawn_empty().id(); -// let child = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world.push_children(parent, &[child]).unwrap(); -// }); - -// let children = world.get::(parent).unwrap(); -// assert_eq!(children.len(), 1); -// assert_eq!(children[0], child); -// } - -// #[test] -// fn test_push_children_entity_with_1_child() { -// let mut world = init_world(); - -// let parent = world.spawn_empty().id(); -// let child = world.spawn_empty().id(); -// let mut cmnds = CommandQueue::default(); -// let mut cmnd = Commands::new(&mut cmnds, &world); -// cmnd.entity(parent).add_children(&[child]); -// cmnds.apply(&mut world); - -// let child_2 = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world.push_children(parent, &[child_2]).unwrap(); -// }); - -// let children = world.get::(parent).unwrap(); -// assert_eq!(children.len(), 2); -// assert_eq!(children[0], child); -// assert_eq!(children[1], child_2); -// } - -// #[test] -// fn test_remove_children_entity_with_1_child() { -// let mut world = init_world(); - -// let parent = world.spawn_empty().id(); -// let child = world.spawn_empty().id(); -// let mut cmnds = CommandQueue::default(); -// let mut cmnd = Commands::new(&mut cmnds, &world); -// cmnd.entity(parent).add_children(&[child]); -// cmnds.apply(&mut world); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world.remove_children(parent, &[child]).unwrap(); -// }); - -// let children = world.get::(parent); -// assert!(children.is_none()); -// } - -// #[test] -// fn test_remove_children_remove_half_children() { -// let mut world = init_world(); - -// let parent = world.spawn_empty().id(); -// let child = world.spawn_empty().id(); -// let child_2 = world.spawn_empty().id(); -// let mut cmnds = CommandQueue::default(); -// let mut cmnd = Commands::new(&mut cmnds, &world); -// cmnd.entity(parent).add_children(&[child, child_2]); -// cmnds.apply(&mut world); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world.remove_children(parent, &[child]).unwrap(); -// }); - -// let children = world.get::(parent).unwrap(); -// assert_eq!(children.len(), 1); -// assert_eq!(children[0], child_2); -// } - -// #[test] -// fn test_insert_children_empty() { -// let mut world = init_world(); - -// let parent = world.spawn_empty().id(); -// let child = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world.insert_children(parent, 0, &[child]).unwrap(); -// }); - -// let children = world.get::(parent).unwrap(); -// assert_eq!(children.len(), 1); -// assert_eq!(children[0], child); -// } - -// #[test] -// fn test_insert_children_middle() { -// let mut world = init_world(); - -// let parent = world.spawn_empty().id(); -// let child = world.spawn_empty().id(); -// let child_2 = world.spawn_empty().id(); -// let mut cmnds = CommandQueue::default(); -// let mut cmnd = Commands::new(&mut cmnds, &world); -// cmnd.entity(parent).add_children(&[child, child_2]); -// cmnds.apply(&mut world); - -// let child_to_insert = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world -// .insert_children(parent, 1, &[child_to_insert]) -// .unwrap(); -// }); - -// let children = world.get::(parent).unwrap(); -// assert_eq!(children.len(), 3); -// assert_eq!(children[0], child); -// assert_eq!(children[1], child_to_insert); -// assert_eq!(children[2], child_2); -// } - -// #[test] -// fn test_despawn_entity() { -// let mut world = init_world(); - -// let entity = world.spawn_empty().id(); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world.despawn(entity).unwrap(); -// }); - -// assert!(world.get_entity(entity).is_err()); -// } - -// #[test] -// fn test_despawn_recursive() { -// let mut world = init_world(); - -// let parent = world.spawn_empty().id(); -// let child = world.spawn_empty().id(); -// let mut cmnds = CommandQueue::default(); -// let mut cmnd = Commands::new(&mut cmnds, &world); -// cmnd.entity(parent).add_children(&[child]); -// cmnds.apply(&mut world); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world.despawn_recursive(parent).unwrap(); -// }); - -// assert!(world.get_entity(parent).is_err()); -// assert!(world.get_entity(child).is_err()); -// } - -// #[test] -// fn test_despawn_descendants() { -// let mut world = init_world(); - -// let parent = world.spawn_empty().id(); -// let child = world.spawn_empty().id(); -// let mut cmnds = CommandQueue::default(); -// let mut cmnd = Commands::new(&mut cmnds, &world); -// cmnd.entity(parent).add_children(&[child]); -// cmnds.apply(&mut world); - -// WorldCallbackAccess::with_callback_access(&mut world, |world| { -// world.despawn_descendants(parent).unwrap(); -// }); - -// assert!(world.get_entity(parent).is_ok()); -// assert!(world.get_entity(child).is_err()); -// } -// } diff --git a/crates/bevy_mod_scripting_core/src/commands.rs b/crates/bevy_mod_scripting_core/src/commands.rs index a26b2df197..de12a29474 100644 --- a/crates/bevy_mod_scripting_core/src/commands.rs +++ b/crates/bevy_mod_scripting_core/src/commands.rs @@ -1,8 +1,9 @@ use crate::{ asset::ScriptAsset, + context::ContextBuilder, event::{IntoCallbackLabel, OnScriptLoaded, OnScriptUnloaded}, extractors::{extract_handler_context, yield_handler_context, HandlerContext}, - handler::handle_script_errors, + handler::{handle_script_errors, CallbackSettings}, script::{Script, ScriptId}, IntoScriptPluginParams, }; @@ -44,7 +45,8 @@ impl Command for DeleteScript

{ match res_ctxt.script_contexts.get_mut(script.context_id) { Some(context) => { // first let the script uninstall itself - match (res_ctxt.callback_settings.callback_handler)( + match (CallbackSettings::

::call)( + res_ctxt.callback_settings.callback_handler, vec![], bevy::ecs::entity::Entity::from_raw(0), &self.id, @@ -130,7 +132,8 @@ impl CreateOrUpdateScript

{ self.id ); - match (res_ctxt.callback_settings.callback_handler)( + match (CallbackSettings::

::call)( + res_ctxt.callback_settings.callback_handler, vec![], bevy::ecs::entity::Entity::from_raw(0), &self.id, @@ -165,7 +168,8 @@ impl CreateOrUpdateScript

{ previous_context_id: u32, ) { if let Some(mut previous_context) = res_ctxt.script_contexts.remove(previous_context_id) { - match (res_ctxt.context_loading_settings.loader.reload)( + match (ContextBuilder::

::reload)( + res_ctxt.context_loading_settings.loader.reload, &self.id, &self.content, &mut previous_context, @@ -231,7 +235,8 @@ impl CreateOrUpdateScript

{ } else { // load new context bevy::log::debug!("{}", log_context); - let ctxt = (res_ctxt.context_loading_settings.loader.load)( + let ctxt = (ContextBuilder::

::load)( + res_ctxt.context_loading_settings.loader.load, &self.id, &self.content, &res_ctxt.context_loading_settings.context_initializers, @@ -333,7 +338,7 @@ mod test { app.insert_resource(ContextLoadingSettings:: { loader: ContextBuilder { - load: |name, c, init, pre_run_init, _, _| { + load: |name, c, init, pre_run_init, _| { let mut context = String::from_utf8_lossy(c).into(); for init in init { init(name, &mut context)?; @@ -343,7 +348,7 @@ mod test { } Ok(context) }, - reload: |name, new, existing, init, pre_run_init, _, _| { + reload: |name, new, existing, init, pre_run_init, _| { *existing = String::from_utf8_lossy(new).into(); for init in init { init(name, existing)?; @@ -371,7 +376,7 @@ mod test { runtime: "Runtime".to_string(), }) .insert_resource(CallbackSettings:: { - callback_handler: |_, _, _, callback, c, _, _, _| { + callback_handler: |_, _, _, callback, c, _, _| { c.push_str(format!(" callback-ran-{}", callback).as_str()); Ok(ScriptValue::Unit) }, diff --git a/crates/bevy_mod_scripting_core/src/context.rs b/crates/bevy_mod_scripting_core/src/context.rs index a71ff98c18..bf2c50184e 100644 --- a/crates/bevy_mod_scripting_core/src/context.rs +++ b/crates/bevy_mod_scripting_core/src/context.rs @@ -1,4 +1,5 @@ use crate::{ + bindings::{ThreadWorldContainer, WorldContainer, WorldGuard}, error::ScriptError, script::{Script, ScriptId}, IntoScriptPluginParams, @@ -91,26 +92,73 @@ impl Clone for ContextLoadingSettings { } } } +pub type ContextLoadFn

= fn( + script_id: &ScriptId, + content: &[u8], + context_initializers: &[ContextInitializer

], + pre_handling_initializers: &[ContextPreHandlingInitializer

], + runtime: &mut

::R, +) -> Result<

::C, ScriptError>; + +pub type ContextReloadFn

= fn( + script_id: &ScriptId, + content: &[u8], + previous_context: &mut

::C, + context_initializers: &[ContextInitializer

], + pre_handling_initializers: &[ContextPreHandlingInitializer

], + runtime: &mut

::R, +) -> Result<(), ScriptError>; /// A strategy for loading and reloading contexts pub struct ContextBuilder { - pub load: fn( + pub load: ContextLoadFn

, + pub reload: ContextReloadFn

, +} + +impl ContextBuilder

{ + pub fn load( + loader: ContextLoadFn

, script: &ScriptId, content: &[u8], context_initializers: &[ContextInitializer

], pre_handling_initializers: &[ContextPreHandlingInitializer

], world: &mut World, runtime: &mut P::R, - ) -> Result, - pub reload: fn( + ) -> Result { + WorldGuard::with_static_guard(world, |world| { + ThreadWorldContainer.set_world(world)?; + (loader)( + script, + content, + context_initializers, + pre_handling_initializers, + runtime, + ) + }) + } + + pub fn reload( + reloader: ContextReloadFn

, script: &ScriptId, - new_content: &[u8], - context: &mut P::C, + content: &[u8], + previous_context: &mut P::C, context_initializers: &[ContextInitializer

], pre_handling_initializers: &[ContextPreHandlingInitializer

], world: &mut World, runtime: &mut P::R, - ) -> Result<(), ScriptError>, + ) -> Result<(), ScriptError> { + WorldGuard::with_static_guard(world, |world| { + ThreadWorldContainer.set_world(world)?; + (reloader)( + script, + content, + previous_context, + context_initializers, + pre_handling_initializers, + runtime, + ) + }) + } } impl Clone for ContextBuilder

{ diff --git a/crates/bevy_mod_scripting_core/src/error.rs b/crates/bevy_mod_scripting_core/src/error.rs index 8d4a6887bd..ac5ec7bd93 100644 --- a/crates/bevy_mod_scripting_core/src/error.rs +++ b/crates/bevy_mod_scripting_core/src/error.rs @@ -1081,8 +1081,7 @@ mod test { use bevy::prelude::{AppTypeRegistry, World}; use crate::bindings::{ - function::script_function::AppScriptFunctionRegistry, AppReflectAllocator, - WorldAccessGuard, WorldGuard, + function::script_function::AppScriptFunctionRegistry, AppReflectAllocator, WorldGuard, }; use super::*; @@ -1101,7 +1100,7 @@ mod test { let script_function_registry = AppScriptFunctionRegistry::default(); world.insert_resource(script_function_registry); - let world_guard = WorldGuard::new(WorldAccessGuard::new(&mut world)); + let world_guard = WorldGuard::new(&mut world); assert_eq!( error.display_with_world(world_guard), format!( diff --git a/crates/bevy_mod_scripting_core/src/handler.rs b/crates/bevy_mod_scripting_core/src/handler.rs index d637b15ff1..f540cc23e7 100644 --- a/crates/bevy_mod_scripting_core/src/handler.rs +++ b/crates/bevy_mod_scripting_core/src/handler.rs @@ -1,6 +1,7 @@ use crate::{ bindings::{ - pretty_print::DisplayWithWorld, script_value::ScriptValue, WorldAccessGuard, WorldGuard, + pretty_print::DisplayWithWorld, script_value::ScriptValue, ThreadWorldContainer, + WorldContainer, WorldGuard, }, context::ContextPreHandlingInitializer, error::ScriptError, @@ -30,7 +31,6 @@ pub type HandlerFn

= fn( context: &mut

::C, pre_handling_initializers: &[ContextPreHandlingInitializer

], runtime: &mut

::R, - world: &mut World, ) -> Result; /// A resource that holds the settings for the callback handler for a specific combination of type parameters @@ -39,6 +39,46 @@ pub struct CallbackSettings { pub callback_handler: HandlerFn

, } +impl Clone for CallbackSettings

{ + fn clone(&self) -> Self { + Self { + callback_handler: self.callback_handler, + } + } +} + +impl CallbackSettings

{ + pub fn new(callback_handler: HandlerFn

) -> Self { + Self { callback_handler } + } + + /// Calls the handler function while providing the necessary thread local context + pub fn call( + handler: HandlerFn

, + args: Vec, + entity: Entity, + script_id: &ScriptId, + callback: &CallbackLabel, + script_ctxt: &mut P::C, + pre_handling_initializers: &[ContextPreHandlingInitializer

], + runtime: &mut P::R, + world: &mut World, + ) -> Result { + WorldGuard::with_static_guard(world, |world| { + ThreadWorldContainer.set_world(world)?; + (handler)( + args, + entity, + script_id, + callback, + script_ctxt, + pre_handling_initializers, + runtime, + ) + }) + } +} + macro_rules! push_err_and_continue { ($errors:ident, $expr:expr) => { match $expr { @@ -116,7 +156,8 @@ pub(crate) fn event_handler_internal::call)( + res_ctxt.callback_settings.callback_handler, event.args.clone(), *entity, &script.id, @@ -180,7 +221,7 @@ pub(crate) fn handle_script_errors + Clone>( error_events.send(ScriptErrorEvent { error }); } for error in errors { - let arc_world = WorldGuard::new(WorldAccessGuard::new(world)); + let arc_world = WorldGuard::new(world); bevy::log::error!("{}", error.display_with_world(arc_world)); } } @@ -252,8 +293,8 @@ mod test { app.insert_non_send_resource(ScriptContexts::

{ contexts }); app.insert_resource(ContextLoadingSettings::

{ loader: ContextBuilder { - load: |_, _, _, _, _, _| todo!(), - reload: |_, _, _, _, _, _, _| todo!(), + load: |_, _, _, _, _| todo!(), + reload: |_, _, _, _, _, _| todo!(), }, assigner: ContextAssigner { assign: |_, _, _| todo!(), @@ -287,7 +328,7 @@ mod test { invocations: vec![], }; let mut app = setup_app::( - |args, entity, script, _, ctxt, _, runtime, _| { + |args, entity, script, _, ctxt, _, runtime| { ctxt.invocations.extend(args); runtime.invocations.push((entity, script.clone())); Ok(ScriptValue::Unit) @@ -374,7 +415,7 @@ mod test { invocations: vec![], }; let mut app = setup_app::( - |args, entity, script, _, ctxt, _, runtime, _| { + |args, entity, script, _, ctxt, _, runtime| { ctxt.invocations.extend(args); runtime.invocations.push((entity, script.clone())); Ok(ScriptValue::Unit) diff --git a/crates/bevy_mod_scripting_core/src/lib.rs b/crates/bevy_mod_scripting_core/src/lib.rs index 7a36abe00f..78fdd6e73c 100644 --- a/crates/bevy_mod_scripting_core/src/lib.rs +++ b/crates/bevy_mod_scripting_core/src/lib.rs @@ -7,7 +7,7 @@ use bevy::prelude::*; use bindings::{ function::script_function::AppScriptFunctionRegistry, garbage_collector, script_value::ScriptValue, AppReflectAllocator, ReflectAllocator, ReflectReference, - ScriptTypeRegistration, WorldCallbackAccess, + ScriptTypeRegistration, }; use context::{ Context, ContextAssigner, ContextBuilder, ContextInitializer, ContextLoadingSettings, @@ -221,7 +221,6 @@ fn register_script_plugin_systems(app: &mut App) { /// Register all types that need to be accessed via reflection fn register_types(app: &mut App) { - app.register_type::(); app.register_type::(); app.register_type::(); app.register_type::(); diff --git a/crates/bevy_mod_scripting_functions/src/core.rs b/crates/bevy_mod_scripting_functions/src/core.rs index 241657b07f..8f62ba392e 100644 --- a/crates/bevy_mod_scripting_functions/src/core.rs +++ b/crates/bevy_mod_scripting_functions/src/core.rs @@ -11,12 +11,13 @@ use bindings::{ from_ref::FromScriptRef, into_ref::IntoScriptRef, namespace::NamespaceBuilder, - script_function::{CallerContext, ScriptFunctionMut}, + script_function::{FunctionCallContext, ScriptFunctionMut}, }, pretty_print::DisplayWithWorld, script_value::ScriptValue, ReflectReference, ReflectionPathExt, ScriptComponentRegistration, ScriptQueryBuilder, - ScriptQueryResult, ScriptResourceRegistration, ScriptTypeRegistration, WorldCallbackAccess, + ScriptQueryResult, ScriptResourceRegistration, ScriptTypeRegistration, ThreadWorldContainer, + WorldContainer, }; use error::InteropError; use reflection_extensions::{PartialReflectExt, TypeIdExtensions}; @@ -30,15 +31,15 @@ pub fn register_world_functions(reg: &mut World) -> Result<(), FunctionRegistrat NamespaceBuilder::::new_unregistered(reg) .register( "get_type_by_name", - |world: WorldCallbackAccess, type_name: String| { - let world = world.try_read()?; + |ctxt: FunctionCallContext, type_name: String| { + 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) { + let registration = match world.get_resource_type(registration)? { Ok(res) => { let mut allocator = allocator.write(); return Ok(Some(ReflectReference::new_allocated( @@ -49,7 +50,7 @@ pub fn register_world_functions(reg: &mut World) -> Result<(), FunctionRegistrat Err(registration) => registration, }; - let registration = match world.get_component_type(registration) { + let registration = match world.get_component_type(registration)? { Ok(comp) => { let mut allocator = allocator.write(); return Ok(Some(ReflectReference::new_allocated( @@ -72,106 +73,133 @@ pub fn register_world_functions(reg: &mut World) -> Result<(), FunctionRegistrat ) .register( "get_component", - |world: WorldCallbackAccess, + |ctxt: FunctionCallContext, entity: Val, registration: Val| { + let world = ctxt.world()?; world.get_component(*entity, registration.component_id()) }, ) .register( "has_component", - |world: WorldCallbackAccess, + |ctxt: FunctionCallContext, entity: Val, registration: Val| { + let world = ctxt.world()?; world.has_component(*entity, registration.component_id()) }, ) .register( "remove_component", - |world: WorldCallbackAccess, e: Val, r: Val| { + |ctxt: FunctionCallContext, e: Val, r: Val| { + let world = ctxt.world()?; world.remove_component(*e, r.clone()) }, ) .register( "get_resource", - |world: WorldCallbackAccess, registration: Val| { + |ctxt: FunctionCallContext, registration: Val| { + let world = ctxt.world()?; world.get_resource(registration.resource_id()) }, ) .register( "has_resource", - |world: WorldCallbackAccess, registration: Val| { + |ctxt: FunctionCallContext, registration: Val| { + let world = ctxt.world()?; world.has_resource(registration.resource_id()) }, ) .register( "remove_resource", - |s: WorldCallbackAccess, r: Val| { - s.remove_resource(r.into_inner()) + |ctxt: FunctionCallContext, r: Val| { + let world = ctxt.world()?; + world.remove_resource(r.into_inner()) }, ) .register( "add_default_component", - |w: WorldCallbackAccess, e: Val, r: Val| { - w.add_default_component(*e, r.clone()) + |ctxt: FunctionCallContext, e: Val, r: Val| { + let world = ctxt.world()?; + world.add_default_component(*e, r.clone()) }, ) + .register("spawn", |ctxt: FunctionCallContext| { + let world = ctxt.world()?; + Ok(Val(world.spawn()?)) + }) .register( "insert_component", - |w: WorldCallbackAccess, + |ctxt: FunctionCallContext, e: Val, r: Val, - v: ReflectReference| { w.insert_component(*e, r.into_inner(), v) }, + v: ReflectReference| { + let world = ctxt.world()?; + world.insert_component(*e, r.into_inner(), v) + }, ) - .register("spawn", |s: WorldCallbackAccess| Ok(Val(s.spawn()?))) .register( "insert_children", - |caller_context: CallerContext, - w: WorldCallbackAccess, - e: Val, - index: usize, - c: Vec>| { - let index = if caller_context.convert_to_0_indexed { + |ctxt: FunctionCallContext, e: Val, index: usize, c: Vec>| { + let world = ctxt.world()?; + let index = if ctxt.convert_to_0_indexed { index - 1 } else { index }; - w.insert_children(*e, index, &c.into_iter().map(|v| *v).collect::>()) + world.insert_children(*e, index, &c.into_iter().map(|v| *v).collect::>()) }, ) .register( "push_children", - |w: WorldCallbackAccess, e: Val, c: Vec>| { - w.push_children(*e, &c.into_iter().map(|v| *v).collect::>()) + |ctxt: FunctionCallContext, e: Val, c: Vec>| { + let world = ctxt.world()?; + world.push_children(*e, &c.into_iter().map(|v| *v).collect::>()) }, ) - .register("get_children", |w: WorldCallbackAccess, e: Val| { - let children = w.get_children(*e)?; - Ok(children.into_iter().map(Val).collect::>()) - }) - .register("get_parent", |w: WorldCallbackAccess, e: Val| { - let parent = w.get_parent(*e)?; + .register( + "get_children", + |ctxt: FunctionCallContext, e: Val| { + let world = ctxt.world()?; + let children = world.get_children(*e)?; + Ok(children.into_iter().map(Val).collect::>()) + }, + ) + .register("get_parent", |ctxt: FunctionCallContext, e: Val| { + let world = ctxt.world()?; + let parent = world.get_parent(*e)?; Ok(parent.map(Val)) }) - .register("despawn", |s: WorldCallbackAccess, e: Val| { - s.despawn(*e) + .register("despawn", |ctxt: FunctionCallContext, e: Val| { + let world = ctxt.world()?; + world.despawn(*e) }) .register( "despawn_descendants", - |s: WorldCallbackAccess, e: Val| s.despawn_descendants(*e), + |ctxt: FunctionCallContext, e: Val| { + let world = ctxt.world()?; + world.despawn_descendants(*e) + }, ) .register( "despawn_recursive", - |s: WorldCallbackAccess, e: Val| s.despawn_recursive(*e), + |ctxt: FunctionCallContext, e: Val| { + let world = ctxt.world()?; + world.despawn_recursive(*e) + }, ) - .register("has_entity", |s: WorldCallbackAccess, e: Val| { - s.has_entity(*e) + .register("has_entity", |ctxt: FunctionCallContext, e: Val| { + let world = ctxt.world()?; + world.has_entity(*e) }) .register("query", || { let query_builder = ScriptQueryBuilder::default(); Ok(Val(query_builder)) }) - .register("exit", |s: WorldCallbackAccess| s.exit()); + .register("exit", |ctxt: FunctionCallContext| { + let world = ctxt.world()?; + world.exit() + }); Ok(()) } @@ -181,41 +209,39 @@ pub fn register_reflect_reference_functions( NamespaceBuilder::::new(reg) .register( "display_ref", - |w: WorldCallbackAccess, s: ReflectReference| { - let world = w.try_read()?; + |ctxt: FunctionCallContext, s: ReflectReference| { + let world = ctxt.world()?; Ok(s.display_with_world(world)) }, ) - .register("display_value", |w: WorldCallbackAccess, s: ReflectReference| { - let world = w.try_read()?; + .register("display_value", |ctxt: FunctionCallContext, s: ReflectReference| { + let world = ctxt.world()?; Ok(s.display_value_with_world(world)) }) .register( "get", - |caller_context: CallerContext, - world: WorldCallbackAccess, + |ctxt: FunctionCallContext, mut self_: ReflectReference, key: ScriptValue| { let mut path: ParsedPath = key.try_into()?; - if caller_context.convert_to_0_indexed { + if ctxt.convert_to_0_indexed { path.convert_to_0_indexed(); } self_.index_path(path); - let world = world.try_read()?; + let world = ctxt.world()?; ReflectReference::into_script_ref(self_, world) }, ) .register( "set", - |caller_context: CallerContext, - world: WorldCallbackAccess, + |ctxt: FunctionCallContext, self_: ScriptValue, key: ScriptValue, value: ScriptValue| { if let ScriptValue::Reference(mut self_) = self_ { - let world = world.try_read()?; + let world = ctxt.world()?; let mut path: ParsedPath = key.try_into()?; - if caller_context.convert_to_0_indexed { + if ctxt.convert_to_0_indexed { path.convert_to_0_indexed(); } self_.index_path(path); @@ -241,8 +267,8 @@ pub fn register_reflect_reference_functions( ) .register( "push", - |w: WorldCallbackAccess, s: ReflectReference, v: ScriptValue| { - let world = w.try_read()?; + |ctxt: FunctionCallContext, s: ReflectReference, v: ScriptValue| { + let world = ctxt.world()?; let target_type_id = s.element_type_id(world.clone())?.ok_or_else(|| { InteropError::unsupported_operation( s.tail_type_id(world.clone()).unwrap_or_default(), @@ -254,8 +280,8 @@ pub fn register_reflect_reference_functions( s.with_reflect_mut(world, |s| s.try_push_boxed(other))? }, ) - .register("pop", |w: WorldCallbackAccess, s: ReflectReference| { - let world = w.try_read()?; + .register("pop", |ctxt: FunctionCallContext, s: ReflectReference| { + let world = ctxt.world()?; let o = s.with_reflect_mut(world.clone(), |s| s.try_pop_boxed())??; let reference = { let allocator = world.allocator(); @@ -265,8 +291,8 @@ pub fn register_reflect_reference_functions( ReflectReference::into_script_ref(reference, world) }) - .register("insert", |caller_context: CallerContext, w: WorldCallbackAccess, s: ReflectReference, k: ScriptValue, v: ScriptValue| { - let world = w.try_read()?; + .register("insert", |ctxt: FunctionCallContext, s: ReflectReference, k: ScriptValue, v: ScriptValue| { + let world = ctxt.world()?; let key_type_id = s.key_type_id(world.clone())?.ok_or_else(|| { InteropError::unsupported_operation( s.tail_type_id(world.clone()).unwrap_or_default(), @@ -277,7 +303,7 @@ pub fn register_reflect_reference_functions( let mut key = >::from_script_ref(key_type_id, k, world.clone())?; - if caller_context.convert_to_0_indexed { + if ctxt.convert_to_0_indexed { key.convert_to_0_indexed_key(); } @@ -293,16 +319,16 @@ pub fn register_reflect_reference_functions( s.with_reflect_mut(world, |s| s.try_insert_boxed(key, value))? }) - .register("clear", |w: WorldCallbackAccess, s: ReflectReference| { - let world = w.try_read()?; + .register("clear", |ctxt: FunctionCallContext, s: ReflectReference| { + let world = ctxt.world()?; s.with_reflect_mut(world, |s| s.try_clear())? }) - .register("len", |w: WorldCallbackAccess, s: ReflectReference| { - let world = w.try_read()?; + .register("len", |ctxt: FunctionCallContext, s: ReflectReference| { + let world = ctxt.world()?; s.len(world) }) - .register("remove", |caller_context: CallerContext, w: WorldCallbackAccess, s: ReflectReference, k: ScriptValue| { - let world = w.try_read()?; + .register("remove", |ctxt: FunctionCallContext, s: ReflectReference, k: ScriptValue| { + let world = ctxt.world()?; let key_type_id = s.key_type_id(world.clone())?.ok_or_else(|| { InteropError::unsupported_operation( s.tail_type_id(world.clone()).unwrap_or_default(), @@ -313,7 +339,7 @@ pub fn register_reflect_reference_functions( let mut key = >::from_script_ref(key_type_id, k, world.clone())?; - if caller_context.convert_to_0_indexed { + if ctxt.convert_to_0_indexed { key.convert_to_0_indexed_key(); } @@ -330,18 +356,21 @@ pub fn register_reflect_reference_functions( None => Ok(ScriptValue::Unit), } }) - .register("iter", |w: WorldCallbackAccess, s: ReflectReference| { - let world = w.try_read()?; + .register("iter", |ctxt: FunctionCallContext, s: ReflectReference| { + let world = ctxt.world()?; let mut len = s.len(world.clone())?.unwrap_or_default(); let mut infinite_iter = s.into_iter_infinite(); 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()?; if len == 0 { return Ok(ScriptValue::Unit); } let (next_ref, _) = infinite_iter.next_ref(); - let converted = ReflectReference::into_script_ref(next_ref, world.clone()); + let converted = ReflectReference::into_script_ref(next_ref, world); // println!("idx: {idx:?}, converted: {converted:?}"); len -= 1; // we stop once the reflection path is invalid @@ -412,7 +441,8 @@ pub fn register_script_query_builder_functions( ) .register( "build", - |world: WorldCallbackAccess, s: Val| { + |ctxt: FunctionCallContext, s: Val| { + let world = ctxt.world()?; let builder = s.into_inner(); let result = world.query(builder)?; let result = result.into_iter().map(Val).collect::>(); 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 1b44ce03d7..9b267249bd 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs @@ -63,11 +63,8 @@ impl UserData for LuaReflectReference { })?; // call the function with the key - let out = func.call( - vec![ScriptValue::Reference(self_), key], - world, - LUA_CALLER_CONTEXT, - )?; + let out = + func.call(vec![ScriptValue::Reference(self_), key], LUA_CALLER_CONTEXT)?; Ok(LuaScriptValue(out)) }, ); @@ -88,7 +85,6 @@ impl UserData for LuaReflectReference { let out = func.call( vec![ScriptValue::Reference(self_), key, value], - world, LUA_CALLER_CONTEXT, )?; @@ -99,11 +95,10 @@ impl UserData for LuaReflectReference { m.add_meta_function( MetaMethod::Sub, |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { - let world = ThreadWorldContainer.try_get_callback_world()?; - let guard = world.try_read()?; + let world = ThreadWorldContainer.try_get_world()?; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); - let target_type_id = self_.tail_type_id(guard)?.or_fake_id(); + let target_type_id = self_.tail_type_id(world.clone())?.or_fake_id(); let args = vec![ScriptValue::Reference(self_), other]; let out = world.try_call_overloads(target_type_id, "sub", args, LUA_CALLER_CONTEXT)?; @@ -114,11 +109,10 @@ impl UserData for LuaReflectReference { m.add_meta_function( MetaMethod::Add, |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { - let world = ThreadWorldContainer.try_get_callback_world()?; - let guard = world.try_read()?; + let world = ThreadWorldContainer.try_get_world()?; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); - let target_type_id = self_.tail_type_id(guard)?.or_fake_id(); + let target_type_id = self_.tail_type_id(world.clone())?.or_fake_id(); let args = vec![ScriptValue::Reference(self_), other]; let out = world.try_call_overloads(target_type_id, "add", args, LUA_CALLER_CONTEXT)?; @@ -129,11 +123,10 @@ impl UserData for LuaReflectReference { m.add_meta_function( MetaMethod::Mul, |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { - let world = ThreadWorldContainer.try_get_callback_world()?; - let guard = world.try_read()?; + let world = ThreadWorldContainer.try_get_world()?; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); - let target_type_id = self_.tail_type_id(guard)?.or_fake_id(); + let target_type_id = self_.tail_type_id(world.clone())?.or_fake_id(); let args = vec![ScriptValue::Reference(self_), other]; let out = world.try_call_overloads(target_type_id, "mul", args, LUA_CALLER_CONTEXT)?; @@ -144,11 +137,10 @@ impl UserData for LuaReflectReference { m.add_meta_function( MetaMethod::Div, |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { - let world = ThreadWorldContainer.try_get_callback_world()?; - let guard = world.try_read()?; + let world = ThreadWorldContainer.try_get_world()?; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); - let target_type_id = self_.tail_type_id(guard)?.or_fake_id(); + let target_type_id = self_.tail_type_id(world.clone())?.or_fake_id(); let args = vec![ScriptValue::Reference(self_), other]; let out = world.try_call_overloads(target_type_id, "div", args, LUA_CALLER_CONTEXT)?; @@ -159,11 +151,10 @@ impl UserData for LuaReflectReference { m.add_meta_function( MetaMethod::Mod, |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { - let world = ThreadWorldContainer.try_get_callback_world()?; - let guard = world.try_read()?; + let world = ThreadWorldContainer.try_get_world()?; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); - let target_type_id = self_.tail_type_id(guard)?.or_fake_id(); + let target_type_id = self_.tail_type_id(world.clone())?.or_fake_id(); let args = vec![ScriptValue::Reference(self_), other]; let out = world.try_call_overloads(target_type_id, "rem", args, LUA_CALLER_CONTEXT)?; @@ -172,10 +163,9 @@ impl UserData for LuaReflectReference { ); m.add_meta_function(MetaMethod::Unm, |_, self_: LuaReflectReference| { - let world = ThreadWorldContainer.try_get_callback_world()?; - let guard = world.try_read()?; + let world = ThreadWorldContainer.try_get_world()?; let self_: ReflectReference = self_.into(); - let target_type_id = self_.tail_type_id(guard)?.or_fake_id(); + let target_type_id = self_.tail_type_id(world.clone())?.or_fake_id(); let args = vec![ScriptValue::Reference(self_)]; let out = world.try_call_overloads(target_type_id, "neg", args, LUA_CALLER_CONTEXT)?; Ok(LuaScriptValue(out)) @@ -184,11 +174,10 @@ impl UserData for LuaReflectReference { m.add_meta_function( MetaMethod::Pow, |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { - let world = ThreadWorldContainer.try_get_callback_world()?; - let guard = world.try_read()?; + let world = ThreadWorldContainer.try_get_world()?; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); - let target_type_id = self_.tail_type_id(guard)?.or_fake_id(); + let target_type_id = self_.tail_type_id(world.clone())?.or_fake_id(); let args = vec![ScriptValue::Reference(self_), other]; let out = world.try_call_overloads(target_type_id, "pow", args, LUA_CALLER_CONTEXT)?; @@ -199,11 +188,10 @@ impl UserData for LuaReflectReference { m.add_meta_function( MetaMethod::Eq, |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { - let world = ThreadWorldContainer.try_get_callback_world()?; - let guard = world.try_read()?; + let world = ThreadWorldContainer.try_get_world()?; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); - let target_type_id = self_.tail_type_id(guard)?.or_fake_id(); + let target_type_id = self_.tail_type_id(world.clone())?.or_fake_id(); let args = vec![ScriptValue::Reference(self_), other]; let out = world.try_call_overloads(target_type_id, "eq", args, LUA_CALLER_CONTEXT)?; @@ -214,11 +202,10 @@ impl UserData for LuaReflectReference { m.add_meta_function( MetaMethod::Lt, |_, (self_, other): (LuaReflectReference, LuaScriptValue)| { - let world = ThreadWorldContainer.try_get_callback_world()?; - let guard = world.try_read()?; + let world = ThreadWorldContainer.try_get_world()?; let self_: ReflectReference = self_.into(); let other: ScriptValue = other.into(); - let target_type_id = self_.tail_type_id(guard)?.or_fake_id(); + let target_type_id = self_.tail_type_id(world.clone())?.or_fake_id(); let args = vec![ScriptValue::Reference(self_), other]; let out = world.try_call_overloads(target_type_id, "lt", args, LUA_CALLER_CONTEXT)?; @@ -253,7 +240,6 @@ impl UserData for LuaReflectReference { Ok(LuaScriptValue::from(iter_func.call( vec![ScriptValue::Reference(s.into())], - world, LUA_CALLER_CONTEXT, )?)) }); @@ -267,7 +253,6 @@ impl UserData for LuaReflectReference { .map_err(|f| InteropError::missing_function(TypeId::of::(), f))?; let out = func.call( vec![ScriptValue::Reference(reflect_reference)], - world, LUA_CALLER_CONTEXT, )?; Ok(LuaScriptValue(out)) diff --git a/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs b/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs index 6c08150810..6f464b0896 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/bindings/script_value.rs @@ -1,7 +1,6 @@ use super::reference::LuaReflectReference; use bevy_mod_scripting_core::bindings::{ - function::script_function::CallerContext, script_value::ScriptValue, ThreadWorldContainer, - WorldContainer, + function::script_function::FunctionCallContext, script_value::ScriptValue, }; use mlua::{FromLua, IntoLua, Value, Variadic}; use std::ops::{Deref, DerefMut}; @@ -79,7 +78,7 @@ impl FromLua for LuaScriptValue { } } -pub const LUA_CALLER_CONTEXT: CallerContext = CallerContext { +pub const LUA_CALLER_CONTEXT: FunctionCallContext = FunctionCallContext { convert_to_0_indexed: true, }; @@ -98,24 +97,16 @@ impl IntoLua for LuaScriptValue { ScriptValue::Error(script_error) => return Err(mlua::Error::external(script_error)), ScriptValue::Function(function) => lua .create_function(move |_lua, args: Variadic| { - let world = ThreadWorldContainer.try_get_world()?; - let out = function.call( - args.into_iter().map(Into::into), - world, - LUA_CALLER_CONTEXT, - )?; + let out = + function.call(args.into_iter().map(Into::into), LUA_CALLER_CONTEXT)?; Ok(LuaScriptValue::from(out)) })? .into_lua(lua)?, ScriptValue::FunctionMut(function) => lua .create_function(move |_lua, args: Variadic| { - let world = ThreadWorldContainer.try_get_world()?; - let out = function.call( - args.into_iter().map(Into::into), - world, - LUA_CALLER_CONTEXT, - )?; + let out = + function.call(args.into_iter().map(Into::into), LUA_CALLER_CONTEXT)?; Ok(LuaScriptValue::from(out)) })? diff --git a/crates/languages/bevy_mod_scripting_lua/src/lib.rs b/crates/languages/bevy_mod_scripting_lua/src/lib.rs index 40f6700bc1..3490d78177 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/lib.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/lib.rs @@ -6,7 +6,7 @@ use bevy_mod_scripting_core::{ asset::{AssetPathToLanguageMapper, Language}, bindings::{ function::namespace::Namespace, script_value::ScriptValue, ThreadWorldContainer, - WorldCallbackAccess, WorldContainer, + WorldContainer, }, context::{ContextBuilder, ContextInitializer, ContextPreHandlingInitializer}, error::ScriptError, @@ -151,7 +151,6 @@ pub fn lua_context_load( content: &[u8], initializers: &[ContextInitializer], pre_handling_initializers: &[ContextPreHandlingInitializer], - world: &mut World, _: &mut (), ) -> Result { #[cfg(feature = "unsafe_lua_modules")] @@ -159,21 +158,18 @@ pub fn lua_context_load( #[cfg(not(feature = "unsafe_lua_modules"))] let mut context = Lua::new(); - with_world(world, &mut context, |context| { - initializers - .iter() - .try_for_each(|init| init(script_id, context))?; + initializers + .iter() + .try_for_each(|init| init(script_id, &mut context))?; - pre_handling_initializers - .iter() - .try_for_each(|init| init(script_id, Entity::from_raw(0), context))?; + pre_handling_initializers + .iter() + .try_for_each(|init| init(script_id, Entity::from_raw(0), &mut context))?; - context - .load(content) - .exec() - .map_err(ScriptError::from_mlua_error)?; - Ok(()) - })?; + context + .load(content) + .exec() + .map_err(ScriptError::from_mlua_error)?; Ok(context) } @@ -184,7 +180,6 @@ pub fn lua_context_reload( old_ctxt: &mut Lua, initializers: &[ContextInitializer], pre_handling_initializers: &[ContextPreHandlingInitializer], - world: &mut World, _: &mut (), ) -> Result<(), ScriptError> { *old_ctxt = lua_context_load( @@ -192,7 +187,6 @@ pub fn lua_context_reload( content, initializers, pre_handling_initializers, - world, &mut (), )?; Ok(()) @@ -207,38 +201,23 @@ pub fn lua_handler( context: &mut Lua, pre_handling_initializers: &[ContextPreHandlingInitializer], _: &mut (), - world: &mut bevy::ecs::world::World, ) -> Result { - with_world(world, context, |context| { - pre_handling_initializers - .iter() - .try_for_each(|init| init(script_id, entity, context))?; - - let handler: Function = match context.globals().raw_get(callback_label.as_ref()) { - Ok(handler) => handler, - // not subscribed to this event type - Err(_) => return Ok(ScriptValue::Unit), - }; - - let input = MultiValue::from_vec( - args.into_iter() - .map(|arg| LuaScriptValue::from(arg).into_lua(context)) - .collect::>()?, - ); - - let out = handler.call::(input)?; - Ok(out.into()) - }) -} - -/// Safely scopes world access for a lua context to the given closure's scope -pub fn with_world Result>( - world: &mut World, - context: &mut Lua, - f: F, -) -> Result { - WorldCallbackAccess::with_callback_access(world, |guard| { - ThreadWorldContainer.set_world(guard.clone())?; - f(context) - }) + pre_handling_initializers + .iter() + .try_for_each(|init| init(script_id, entity, context))?; + + let handler: Function = match context.globals().raw_get(callback_label.as_ref()) { + Ok(handler) => handler, + // not subscribed to this event type + Err(_) => return Ok(ScriptValue::Unit), + }; + + let input = MultiValue::from_vec( + args.into_iter() + .map(|arg| LuaScriptValue::from(arg).into_lua(context)) + .collect::>()?, + ); + + let out = handler.call::(input)?; + Ok(out.into()) } diff --git a/crates/script_integration_test_harness/src/lib.rs b/crates/script_integration_test_harness/src/lib.rs index 97f6c562b2..b9dc3310d4 100644 --- a/crates/script_integration_test_harness/src/lib.rs +++ b/crates/script_integration_test_harness/src/lib.rs @@ -6,10 +6,8 @@ use bevy::{ reflect::TypeRegistry, }; use bevy_mod_scripting_core::{ - bindings::{ - pretty_print::DisplayWithWorld, script_value::ScriptValue, WorldAccessGuard, WorldGuard, - }, - context::ContextLoadingSettings, + bindings::{pretty_print::DisplayWithWorld, script_value::ScriptValue, WorldGuard}, + context::{ContextBuilder, ContextLoadingSettings}, event::{IntoCallbackLabel, OnScriptLoaded}, handler::CallbackSettings, runtime::RuntimeSettings, @@ -66,7 +64,8 @@ pub fn execute_integration_test< }); // load the context as normal - let mut loaded_context = (context_settings.loader.load)( + let mut loaded_context = (ContextBuilder::

::load)( + context_settings.loader.load, &(script_id.to_owned()).into(), code, &context_settings.context_initializers, @@ -76,12 +75,12 @@ pub fn execute_integration_test< ) .map_err(|e| { let world = app.world_mut(); - let world = WorldAccessGuard::new(world); e.display_with_world(WorldGuard::new(world)) })?; // call on_script_loaded as normal - let val = (callback_settings.callback_handler)( + let val = (CallbackSettings::

::call)( + callback_settings.callback_handler, vec![], Entity::from_raw(0), &(script_id.to_owned()).into(), @@ -91,14 +90,15 @@ pub fn execute_integration_test< &mut runtime, app.world_mut(), ) - .map_err(|e| e.display_with_world(WorldGuard::new(WorldAccessGuard::new(app.world_mut()))))?; + .map_err(|e| e.display_with_world(WorldGuard::new(app.world_mut())))?; if let ScriptValue::Error(e) = val { - return Err(e.display_with_world(WorldGuard::new(WorldAccessGuard::new(app.world_mut())))); + return Err(e.display_with_world(WorldGuard::new(app.world_mut()))); } // call on_test callback - let val = (callback_settings.callback_handler)( + let val = (CallbackSettings::

::call)( + callback_settings.callback_handler, vec![], Entity::from_raw(0), &(script_id.to_owned()).into(), @@ -108,10 +108,10 @@ pub fn execute_integration_test< &mut runtime, app.world_mut(), ) - .map_err(|e| e.display_with_world(WorldGuard::new(WorldAccessGuard::new(app.world_mut()))))?; + .map_err(|e| e.display_with_world(WorldGuard::new(app.world_mut())))?; if let ScriptValue::Error(e) = val { - return Err(e.display_with_world(WorldGuard::new(WorldAccessGuard::new(app.world_mut())))); + return Err(e.display_with_world(WorldGuard::new(app.world_mut()))); } Ok(()) diff --git a/crates/script_integration_test_harness/src/test_functions.rs b/crates/script_integration_test_harness/src/test_functions.rs index 7e6ead81dc..805fa2b833 100644 --- a/crates/script_integration_test_harness/src/test_functions.rs +++ b/crates/script_integration_test_harness/src/test_functions.rs @@ -10,11 +10,11 @@ use bevy_mod_scripting_core::{ bindings::{ function::{ namespace::{GlobalNamespace, NamespaceBuilder}, - script_function::{CallerContext, DynamicScriptFunctionMut}, + script_function::{DynamicScriptFunctionMut, FunctionCallContext}, }, pretty_print::DisplayWithWorld, ReflectReference, ScriptComponentRegistration, ScriptResourceRegistration, - ScriptTypeRegistration, WorldCallbackAccess, + ScriptTypeRegistration, }, error::InteropError, }; @@ -23,8 +23,8 @@ use test_utils::test_data::EnumerateTestComponents; pub fn register_test_functions(world: &mut App) { let world = world.world_mut(); NamespaceBuilder::::new_unregistered(world) - .register("_get_mock_type", |s: WorldCallbackAccess| { - let world = s.try_read().unwrap(); + .register("_get_mock_type", |s: FunctionCallContext| { + let world = s.world().unwrap(); #[derive(Reflect)] struct Dummy; let reg = ScriptTypeRegistration::new(Arc::new(TypeRegistration::of::())); @@ -32,8 +32,8 @@ pub fn register_test_functions(world: &mut App) { let mut allocator = allocator.write(); ReflectReference::new_allocated(reg, &mut allocator) }) - .register("_get_mock_component_type", |s: WorldCallbackAccess| { - let world = s.try_read().unwrap(); + .register("_get_mock_component_type", |s: FunctionCallContext| { + let world = s.world().unwrap(); #[derive(Reflect)] struct Dummy; let reg = ScriptTypeRegistration::new(Arc::new(TypeRegistration::of::())); @@ -42,8 +42,8 @@ pub fn register_test_functions(world: &mut App) { let mut allocator = allocator.write(); ReflectReference::new_allocated(comp, &mut allocator) }) - .register("_get_mock_resource_type", |s: WorldCallbackAccess| { - let world = s.try_read().unwrap(); + .register("_get_mock_resource_type", |s: FunctionCallContext| { + let world = s.world().unwrap(); #[derive(Reflect)] struct Dummy; let reg = ScriptTypeRegistration::new(Arc::new(TypeRegistration::of::())); @@ -54,8 +54,8 @@ pub fn register_test_functions(world: &mut App) { }) .register( "_get_entity_with_test_component", - |s: WorldCallbackAccess, name: String| { - let world = s.try_read().unwrap(); + |s: FunctionCallContext, name: String| { + let world = s.world().unwrap(); World::enumerate_test_components() .iter() .find(|(n, _, _)| n.contains(&name)) @@ -72,10 +72,10 @@ pub fn register_test_functions(world: &mut App) { ) .register( "_assert_throws", - |s: WorldCallbackAccess, f: DynamicScriptFunctionMut, reg: String| { - let world = s.try_read().unwrap(); + |s: FunctionCallContext, f: DynamicScriptFunctionMut, reg: String| { + let world = s.world().unwrap(); - let result = f.call(vec![], world.clone(), CallerContext::default()); + let result = f.call(vec![], FunctionCallContext::default()); let err = match result { Ok(_) => { return Err(InteropError::external_error(