1use super::{
9 AppReflectAllocator, AppScriptComponentRegistry, ReflectBase, ReflectBaseType,
10 ReflectReference, ScriptComponentRegistration, ScriptResourceRegistration,
11 ScriptTypeRegistration, Union,
12 access_map::{
13 AccessCount, AccessMapKey, AnyAccessMap, DynamicSystemMeta, ReflectAccessId,
14 ReflectAccessKind, SubsetAccessMap,
15 },
16 function::{
17 namespace::Namespace,
18 script_function::{AppScriptFunctionRegistry, DynamicScriptFunction, FunctionCallContext},
19 },
20 schedule::AppScheduleRegistry,
21 script_value::ScriptValue,
22 with_global_access,
23};
24use crate::{
25 error::InteropError,
26 function::{from::FromScript, from_ref::FromScriptRef},
27 reflection_extensions::PartialReflectExt,
28 with_access_read, with_access_write,
29};
30use ::{
31 bevy_app::AppExit,
32 bevy_asset::{AssetServer, Handle, LoadState},
33 bevy_ecs::{
34 component::{Component, ComponentId},
35 entity::Entity,
36 prelude::Resource,
37 reflect::{AppTypeRegistry, ReflectFromWorld, ReflectResource},
38 system::Commands,
39 world::{CommandQueue, Mut, World, unsafe_world_cell::UnsafeWorldCell},
40 },
41 bevy_reflect::{
42 DynamicEnum, DynamicStruct, DynamicTuple, DynamicTupleStruct, DynamicVariant, ParsedPath,
43 PartialReflect, TypeRegistryArc, std_traits::ReflectDefault,
44 },
45};
46use bevy_ecs::{
47 component::Mutable,
48 hierarchy::{ChildOf, Children},
49 system::Command,
50 world::WorldId,
51};
52use bevy_mod_scripting_asset::ScriptAsset;
53use bevy_mod_scripting_display::GetTypeInfo;
54use bevy_platform::collections::HashMap;
55use bevy_reflect::{TypeInfo, VariantInfo};
56use bevy_system_reflection::ReflectSchedule;
57use std::{
58 any::{Any, TypeId},
59 borrow::Cow,
60 cell::RefCell,
61 fmt::Debug,
62 rc::Rc,
63 sync::{Arc, atomic::AtomicBool},
64};
65
66pub type WorldGuard<'w> = WorldAccessGuard<'w>;
68pub type WorldGuardRef<'w> = &'w WorldAccessGuard<'w>;
70
71#[derive(Clone, Debug)]
73pub struct WorldAccessGuard<'w> {
74 pub(crate) inner: Rc<WorldAccessGuardInner<'w>>,
76 invalid: Rc<AtomicBool>,
79}
80impl WorldAccessGuard<'_> {
81 pub fn id(&self) -> WorldId {
83 self.inner.cell.id()
84 }
85}
86
87pub(crate) struct WorldAccessGuardInner<'w> {
89 cell: UnsafeWorldCell<'w>,
91 pub(crate) accesses: AnyAccessMap,
93 type_registry: TypeRegistryArc,
95 allocator: AppReflectAllocator,
97 function_registry: AppScriptFunctionRegistry,
99 schedule_registry: AppScheduleRegistry,
101 script_component_registry: AppScriptComponentRegistry,
103}
104
105impl std::fmt::Debug for WorldAccessGuardInner<'_> {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 f.debug_struct("WorldAccessGuardInner").finish()
108 }
109}
110
111#[profiling::all_functions]
112impl WorldAccessGuard<'static> {
113 pub(crate) fn shorten_lifetime<'w>(self) -> WorldGuard<'w> {
115 unsafe { std::mem::transmute(self) }
117 }
118}
119#[profiling::all_functions]
120impl<'w> WorldAccessGuard<'w> {
121 fn scope(&self) -> Self {
123 let mut new_guard = self.clone();
124 new_guard.invalid = Rc::new(
125 new_guard
126 .invalid
127 .load(std::sync::atomic::Ordering::Relaxed)
128 .into(),
129 );
130 new_guard
131 }
132
133 fn is_valid(&self) -> bool {
135 !self.invalid.load(std::sync::atomic::Ordering::Relaxed)
136 }
137
138 pub fn invalidate(&self) {
140 self.invalid
141 .store(true, std::sync::atomic::Ordering::Relaxed);
142 }
143
144 pub fn with_static_guard<O>(
148 world: &'w mut World,
149 f: impl FnOnce(WorldGuard<'static>) -> O,
150 ) -> O {
151 let guard = WorldAccessGuard::new_exclusive(world);
152 let static_guard: WorldAccessGuard<'static> = unsafe { std::mem::transmute(guard) };
154 let o = f(static_guard.clone());
155
156 static_guard.invalidate();
157 o
158 }
159
160 pub fn with_existing_static_guard<O>(
163 guard: WorldAccessGuard<'w>,
164 f: impl FnOnce(WorldGuard<'static>) -> O,
165 ) -> O {
166 let static_guard: WorldAccessGuard<'static> = unsafe { std::mem::transmute(guard.scope()) };
169 let o = f(static_guard.clone());
170 static_guard.invalidate();
171 o
172 }
173
174 pub unsafe fn new_non_exclusive(
184 world: UnsafeWorldCell<'w>,
185 subset: impl IntoIterator<Item = ReflectAccessId>,
186 type_registry: AppTypeRegistry,
187 allocator: AppReflectAllocator,
188 function_registry: AppScriptFunctionRegistry,
189 schedule_registry: AppScheduleRegistry,
190 script_component_registry: AppScriptComponentRegistry,
191 ) -> Self {
192 Self {
193 inner: Rc::new(WorldAccessGuardInner {
194 cell: world,
195 accesses: AnyAccessMap::SubsetAccessMap(SubsetAccessMap::new(
196 subset,
197 |id| ReflectAccessId::from_index(id).kind == ReflectAccessKind::Allocation,
199 )),
200 type_registry: type_registry.0,
201 allocator,
202 function_registry,
203 schedule_registry,
204 script_component_registry,
205 }),
206 invalid: Rc::new(false.into()),
207 }
208 }
209
210 pub fn new_exclusive(world: &'w mut World) -> Self {
219 let type_registry = world.get_resource_or_init::<AppTypeRegistry>().0.clone();
220
221 let allocator = world.get_resource_or_init::<AppReflectAllocator>().clone();
222
223 let function_registry = world
224 .get_resource_or_init::<AppScriptFunctionRegistry>()
225 .clone();
226
227 let script_component_registry = world
228 .get_resource_or_init::<AppScriptComponentRegistry>()
229 .clone();
230
231 let schedule_registry = world.get_resource_or_init::<AppScheduleRegistry>().clone();
232 Self {
233 inner: Rc::new(WorldAccessGuardInner {
234 cell: world.as_unsafe_world_cell(),
235 accesses: AnyAccessMap::UnlimitedAccessMap(Default::default()),
236 allocator,
237 type_registry,
238 function_registry,
239 schedule_registry,
240 script_component_registry,
241 }),
242 invalid: Rc::new(false.into()),
243 }
244 }
245
246 pub(crate) fn queue(&self, command: impl Command) -> Result<(), InteropError> {
248 self.with_global_access(|w| {
249 w.commands().queue(command);
250 })
251 }
252
253 pub(crate) unsafe fn with_access_scope<O, F: FnOnce() -> O>(
258 &self,
259 f: F,
260 ) -> Result<O, InteropError> {
261 Ok(self.inner.accesses.with_scope(f))
262 }
263
264 pub fn list_accesses(&self) -> Vec<(ReflectAccessId, AccessCount)> {
266 self.inner.accesses.list_accesses()
267 }
268
269 pub unsafe fn release_all_accesses(&self) {
271 self.inner.accesses.release_all_accesses();
272 }
273
274 pub fn access_len(&self) -> usize {
276 self.inner.accesses.count_accesses()
277 }
278
279 pub fn as_unsafe_world_cell(&self) -> Result<UnsafeWorldCell<'w>, InteropError> {
282 if !self.is_valid() {
283 return Err(InteropError::missing_world());
284 }
285
286 Ok(self.inner.cell)
287 }
288
289 pub fn as_unsafe_world_cell_readonly(&self) -> Result<UnsafeWorldCell<'w>, InteropError> {
292 if !self.is_valid() {
293 return Err(InteropError::missing_world());
294 }
295
296 Ok(self.inner.cell)
297 }
298
299 pub fn get_component_id(&self, id: TypeId) -> Result<Option<ComponentId>, InteropError> {
301 Ok(self
302 .as_unsafe_world_cell_readonly()?
303 .components()
304 .get_id(id))
305 }
306
307 pub fn get_resource_id(&self, id: TypeId) -> Result<Option<ComponentId>, InteropError> {
309 Ok(self
310 .as_unsafe_world_cell_readonly()?
311 .components()
312 .get_resource_id(id))
313 }
314
315 pub fn with_read_access<T: Into<ReflectAccessId>, O, F: FnOnce(&Self) -> O>(
317 &self,
318 id: T,
319 closure: F,
320 ) -> Result<O, ()> {
321 let id = id.into();
322 if self.claim_read_access(id) {
323 let out = Ok(closure(self));
324 unsafe { self.release_access(id) };
326 out
327 } else {
328 Err(())
329 }
330 }
331
332 pub fn with_write_access<T: Into<ReflectAccessId>, O, F: FnOnce(&Self) -> O>(
334 &self,
335 id: T,
336 closure: F,
337 ) -> Result<O, ()> {
338 let id = id.into();
339 if self.claim_write_access(id) {
340 let out = Ok(closure(self));
341 unsafe { self.release_access(id) };
343 out
344 } else {
345 Err(())
346 }
347 }
348
349 pub fn get_access_location(
351 &self,
352 raid: ReflectAccessId,
353 ) -> Option<std::panic::Location<'static>> {
354 self.inner.accesses.access_location(raid)
355 }
356
357 #[track_caller]
358 pub fn claim_read_access(&self, raid: ReflectAccessId) -> bool {
360 self.inner.accesses.claim_read_access(raid)
361 }
362
363 #[track_caller]
364 pub fn claim_write_access(&self, raid: ReflectAccessId) -> bool {
366 self.inner.accesses.claim_write_access(raid)
367 }
368
369 pub unsafe fn release_access(&self, raid: ReflectAccessId) {
376 self.inner.accesses.release_access(raid)
377 }
378
379 pub fn claim_global_access(&self) -> bool {
381 self.inner.accesses.claim_global_access()
382 }
383
384 pub unsafe fn release_global_access(&self) {
389 self.inner.accesses.release_global_access()
390 }
391
392 pub fn type_registry(&self) -> TypeRegistryArc {
394 self.inner.type_registry.clone()
395 }
396
397 pub fn schedule_registry(&self) -> AppScheduleRegistry {
399 self.inner.schedule_registry.clone()
400 }
401
402 pub fn component_registry(&self) -> AppScriptComponentRegistry {
404 self.inner.script_component_registry.clone()
405 }
406
407 pub fn allocator(&self) -> AppReflectAllocator {
409 self.inner.allocator.clone()
410 }
411
412 pub fn script_function_registry(&self) -> AppScriptFunctionRegistry {
414 self.inner.function_registry.clone()
415 }
416
417 #[track_caller]
419 pub fn with_global_access<F: FnOnce(&mut World) -> O, O>(
420 &self,
421 f: F,
422 ) -> Result<O, InteropError> {
423 with_global_access!(
424 &self.inner.accesses,
425 "Could not claim exclusive world access",
426 {
427 let world = unsafe { self.as_unsafe_world_cell()?.world_mut() };
429 Ok(f(world))
430 }
431 )?
432 }
433
434 pub fn with_resource<F, R, O>(&self, f: F) -> Result<O, InteropError>
439 where
440 R: Resource,
441 F: FnOnce(&R) -> O,
442 {
443 let cell = self.as_unsafe_world_cell()?;
444 let access_id = ReflectAccessId::for_resource::<R>(&cell)?;
445
446 with_access_read!(
447 &self.inner.accesses,
448 access_id,
449 format!("Could not access resource: {}", std::any::type_name::<R>()),
450 {
451 f(unsafe {
453 cell.get_resource::<R>().ok_or_else(|| {
454 InteropError::unregistered_component_or_resource_type(
455 std::any::type_name::<R>(),
456 )
457 })?
458 })
459 }
460 )
461 }
462
463 pub fn with_resource_mut<F, R, O>(&self, f: F) -> Result<O, InteropError>
468 where
469 R: Resource,
470 F: FnOnce(Mut<R>) -> O,
471 {
472 let cell = self.as_unsafe_world_cell()?;
473 let access_id = ReflectAccessId::for_resource::<R>(&cell)?;
474 with_access_write!(
475 &self.inner.accesses,
476 access_id,
477 format!("Could not access resource: {}", std::any::type_name::<R>()),
478 {
479 f(unsafe {
481 cell.get_resource_mut::<R>().ok_or_else(|| {
482 InteropError::unregistered_component_or_resource_type(
483 std::any::type_name::<R>(),
484 )
485 })?
486 })
487 }
488 )
489 }
490
491 pub fn with_component<F, T, O>(&self, entity: Entity, f: F) -> Result<O, InteropError>
493 where
494 T: Component,
495 F: FnOnce(Option<&T>) -> O,
496 {
497 let cell = self.as_unsafe_world_cell()?;
498 let access_id = ReflectAccessId::for_component::<T>(&cell)?;
499 with_access_read!(
500 &self.inner.accesses,
501 access_id,
502 format!("Could not access component: {}", std::any::type_name::<T>()),
503 {
504 f(unsafe { cell.get_entity(entity).map(|e| e.get::<T>()) }
506 .ok()
507 .unwrap_or(None))
508 }
509 )
510 }
511
512 pub fn with_component_mut<F, T, O>(&self, entity: Entity, f: F) -> Result<O, InteropError>
514 where
515 T: Component<Mutability = Mutable>,
516 F: FnOnce(Option<Mut<T>>) -> O,
517 {
518 let cell = self.as_unsafe_world_cell()?;
519 let access_id = ReflectAccessId::for_component::<T>(&cell)?;
520
521 with_access_write!(
522 &self.inner.accesses,
523 access_id,
524 format!("Could not access component: {}", std::any::type_name::<T>()),
525 {
526 f(unsafe { cell.get_entity(entity).map(|e| e.get_mut::<T>()) }
528 .ok()
529 .unwrap_or(None))
530 }
531 )
532 }
533
534 pub fn with_or_insert_component_mut<F, T, O>(
536 &self,
537 entity: Entity,
538 f: F,
539 ) -> Result<O, InteropError>
540 where
541 T: Component<Mutability = Mutable> + Default,
542 F: FnOnce(&mut T) -> O,
543 {
544 self.with_global_access(|world| match world.get_mut::<T>(entity) {
545 Some(mut component) => f(&mut component),
546 None => {
547 let mut component = T::default();
548 let mut commands = world.commands();
549 let result = f(&mut component);
550 commands.entity(entity).insert(component);
551 result
552 }
553 })
554 }
555
556 pub fn lookup_function(
560 &self,
561 type_ids: impl IntoIterator<Item = TypeId>,
562 name: impl Into<Cow<'static, str>>,
563 ) -> Result<DynamicScriptFunction, Cow<'static, str>> {
564 let registry = self.script_function_registry();
565 let registry = registry.read();
566
567 let mut name = name.into();
568 for type_id in type_ids {
569 name = match registry.get_function(Namespace::OnType(type_id), name) {
570 Ok(func) => return Ok(func.clone()),
571 Err(name) => name,
572 };
573 }
574
575 Err(name)
576 }
577
578 pub fn get_functions_on_type(
580 &self,
581 type_id: TypeId,
582 ) -> Vec<(Cow<'static, str>, DynamicScriptFunction)> {
583 let registry = self.script_function_registry();
584 let registry = registry.read();
585
586 registry
587 .iter_namespace(Namespace::OnType(type_id))
588 .chain(
589 registry
590 .iter_namespace(Namespace::OnType(std::any::TypeId::of::<ReflectReference>())),
591 )
592 .map(|(key, func)| (key.name.clone(), func.clone()))
593 .collect()
594 }
595
596 pub fn is_valid_entity(&self, entity: Entity) -> Result<bool, InteropError> {
598 let cell = self.as_unsafe_world_cell()?;
599 Ok(cell.get_entity(entity).is_ok() && entity.index() != 0)
600 }
601
602 pub fn try_call_overloads(
605 &self,
606 type_id: TypeId,
607 name: impl Into<Cow<'static, str>>,
608 args: Vec<ScriptValue>,
609 context: FunctionCallContext,
610 ) -> Result<ScriptValue, InteropError> {
611 let registry = self.script_function_registry();
612 let registry = registry.read();
613
614 let name = name.into();
615 let overload_iter = match registry.iter_overloads(Namespace::OnType(type_id), name) {
616 Ok(iter) => iter,
617 Err(name) => {
618 return Err(InteropError::missing_function(
619 name.to_string(),
620 Namespace::OnType(type_id),
621 ));
622 }
623 };
624
625 let mut last_error = None;
626 for overload in overload_iter {
627 match overload.call(args.clone(), context.clone()) {
628 Ok(out) => return Ok(out),
629 Err(e) => last_error = Some(e),
630 }
631 }
632
633 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"))?)
634 }
635}
636
637#[profiling::all_functions]
639impl WorldAccessGuard<'_> {
640 fn construct_from_script_value(
641 &self,
642 descriptor: impl Into<Cow<'static, str>>,
643 type_id: TypeId,
644 value: Option<ScriptValue>,
645 ) -> Result<Box<dyn PartialReflect>, InteropError> {
646 let value = match value {
648 Some(value) => value,
649 None => {
650 let type_registry = self.type_registry();
651 let type_registry = type_registry.read();
652 let default_data = type_registry
653 .get_type_data::<ReflectDefault>(type_id)
654 .ok_or_else(|| {
655 InteropError::function_interop_error(
656 "construct",
657 Namespace::OnType(TypeId::of::<World>()),
658 InteropError::string(format!(
659 "field missing and no default provided: '{}'",
660 descriptor.into()
661 )),
662 )
663 })?;
664 return Ok(default_data.default().into_partial_reflect());
665 }
666 };
667
668 <Box<dyn PartialReflect>>::from_script_ref(type_id, value, self.clone())
670 }
671
672 fn construct_dynamic_struct(
673 &self,
674 payload: &mut HashMap<String, ScriptValue>,
675 fields: Vec<(&'static str, TypeId)>,
676 ) -> Result<DynamicStruct, InteropError> {
677 let mut dynamic = DynamicStruct::default();
678 for (field_name, field_type_id) in fields {
679 let constructed = self.construct_from_script_value(
680 field_name,
681 field_type_id,
682 payload.remove(field_name),
683 )?;
684
685 dynamic.insert_boxed(field_name, constructed);
686 }
687 Ok(dynamic)
688 }
689
690 fn construct_dynamic_tuple_struct(
691 &self,
692 payload: &mut HashMap<String, ScriptValue>,
693 fields: Vec<TypeId>,
694 one_indexed: bool,
695 ) -> Result<DynamicTupleStruct, InteropError> {
696 let mut dynamic = DynamicTupleStruct::default();
697 for (field_idx, field_type_id) in fields.into_iter().enumerate() {
698 let script_idx = if one_indexed {
700 field_idx + 1
701 } else {
702 field_idx
703 };
704 let field_string = format!("_{script_idx}");
705 dynamic.insert_boxed(self.construct_from_script_value(
706 field_string.clone(),
707 field_type_id,
708 payload.remove(&field_string),
709 )?);
710 }
711 Ok(dynamic)
712 }
713
714 fn construct_dynamic_tuple(
715 &self,
716 payload: &mut HashMap<String, ScriptValue>,
717 fields: Vec<TypeId>,
718 one_indexed: bool,
719 ) -> Result<DynamicTuple, InteropError> {
720 let mut dynamic = DynamicTuple::default();
721 for (field_idx, field_type_id) in fields.into_iter().enumerate() {
722 let script_idx = if one_indexed {
724 field_idx + 1
725 } else {
726 field_idx
727 };
728
729 let field_string = format!("_{script_idx}");
730
731 dynamic.insert_boxed(self.construct_from_script_value(
732 field_string.clone(),
733 field_type_id,
734 payload.remove(&field_string),
735 )?);
736 }
737 Ok(dynamic)
738 }
739
740 pub fn construct(
744 &self,
745 type_: ScriptTypeRegistration,
746 mut payload: HashMap<String, ScriptValue>,
747 one_indexed: bool,
748 ) -> Result<Box<dyn PartialReflect>, InteropError> {
749 let type_info = type_.registration.type_info();
751 let dynamic: Box<dyn PartialReflect> = match type_info {
755 TypeInfo::Struct(struct_info) => {
756 let fields_iter = struct_info
757 .field_names()
758 .iter()
759 .map(|f| {
760 Ok((
761 *f,
762 struct_info
763 .field(f)
764 .ok_or_else(|| {
765 InteropError::invariant(
766 "field in field_names should have reflection information",
767 )
768 })?
769 .type_id(),
770 ))
771 })
772 .collect::<Result<Vec<_>, InteropError>>()?;
773 let mut dynamic = self.construct_dynamic_struct(&mut payload, fields_iter)?;
774 dynamic.set_represented_type(Some(type_info));
775 Box::new(dynamic)
776 }
777 TypeInfo::TupleStruct(tuple_struct_info) => {
778 let fields_iter = (0..tuple_struct_info.field_len())
779 .map(|f| {
780 Ok(tuple_struct_info
781 .field_at(f)
782 .ok_or_else(|| {
783 InteropError::invariant(
784 "field in field_names should have reflection information",
785 )
786 })?
787 .type_id())
788 })
789 .collect::<Result<Vec<_>, InteropError>>()?;
790
791 let mut dynamic =
792 self.construct_dynamic_tuple_struct(&mut payload, fields_iter, one_indexed)?;
793 dynamic.set_represented_type(Some(type_info));
794 Box::new(dynamic)
795 }
796 TypeInfo::Tuple(tuple_info) => {
797 let fields_iter = (0..tuple_info.field_len())
798 .map(|f| {
799 Ok(tuple_info
800 .field_at(f)
801 .ok_or_else(|| {
802 InteropError::invariant(
803 "field in field_names should have reflection information",
804 )
805 })?
806 .type_id())
807 })
808 .collect::<Result<Vec<_>, InteropError>>()?;
809
810 let mut dynamic =
811 self.construct_dynamic_tuple(&mut payload, fields_iter, one_indexed)?;
812 dynamic.set_represented_type(Some(type_info));
813 Box::new(dynamic)
814 }
815 TypeInfo::Enum(enum_info) => {
816 let variant = payload.remove("variant").ok_or_else(|| {
818 InteropError::function_interop_error(
819 "construct",
820 Namespace::OnType(TypeId::of::<World>()),
821 InteropError::str("missing 'variant' field in enum constructor payload"),
822 )
823 })?;
824
825 let variant_name = String::from_script(variant, self.clone())?;
826
827 let variant = enum_info.variant(&variant_name).ok_or_else(|| {
828 InteropError::function_interop_error(
829 "construct",
830 Namespace::OnType(TypeId::of::<World>()),
831 InteropError::string(format!(
832 "invalid variant name '{}' for enum '{}'",
833 variant_name,
834 enum_info.type_path()
835 )),
836 )
837 })?;
838
839 let variant = match variant {
840 VariantInfo::Struct(struct_variant_info) => {
841 let fields_iter = struct_variant_info
843 .field_names()
844 .iter()
845 .map(|f| {
846 Ok((
847 *f,
848 struct_variant_info
849 .field(f)
850 .ok_or_else(|| {
851 InteropError::invariant(
852 "field in field_names should have reflection information",
853 )
854 })?
855 .type_id(),
856 ))
857 })
858 .collect::<Result<Vec<_>, InteropError>>()?;
859
860 let dynamic = self.construct_dynamic_struct(&mut payload, fields_iter)?;
861 DynamicVariant::Struct(dynamic)
862 }
863 VariantInfo::Tuple(tuple_variant_info) => {
864 let fields_iter = (0..tuple_variant_info.field_len())
866 .map(|f| {
867 Ok(tuple_variant_info
868 .field_at(f)
869 .ok_or_else(|| {
870 InteropError::invariant(
871 "field in field_names should have reflection information",
872 )
873 })?
874 .type_id())
875 })
876 .collect::<Result<Vec<_>, InteropError>>()?;
877
878 let dynamic =
879 self.construct_dynamic_tuple(&mut payload, fields_iter, one_indexed)?;
880 DynamicVariant::Tuple(dynamic)
881 }
882 VariantInfo::Unit(_) => DynamicVariant::Unit,
883 };
884 let mut dynamic = DynamicEnum::new(variant_name, variant);
885 dynamic.set_represented_type(Some(type_info));
886 Box::new(dynamic)
887 }
888 _ => {
889 return Err(InteropError::unsupported_operation(
890 Some(type_info.type_id()),
891 Some(Box::new(payload)),
892 "Type constructor not supported",
893 ));
894 }
895 };
896
897 <dyn PartialReflect>::from_reflect_or_clone(dynamic.as_ref(), self.clone())
901 }
902
903 pub fn load_script_asset(&self, asset_path: &str) -> Result<Handle<ScriptAsset>, InteropError> {
905 self.with_resource(|r: &AssetServer| r.load(asset_path))
906 }
907
908 pub fn get_script_asset_load_state(
910 &self,
911 script: Handle<ScriptAsset>,
912 ) -> Result<LoadState, InteropError> {
913 self.with_resource(|r: &AssetServer| r.load_state(script.id()))
914 }
915
916 pub fn spawn(&self) -> Result<Entity, InteropError> {
935 self.with_global_access(|world| {
936 let mut command_queue = CommandQueue::default();
937 let mut commands = Commands::new(&mut command_queue, world);
938 let id = commands.spawn_empty().id();
939 command_queue.apply(world);
940 id
941 })
942 }
943
944 pub fn get_type_by_name(&self, type_name: &str) -> Option<ScriptTypeRegistration> {
946 let type_registry = self.type_registry();
947 let type_registry = type_registry.read();
948 type_registry
949 .get_with_short_type_path(type_name)
950 .or_else(|| type_registry.get_with_type_path(type_name))
951 .map(|registration| ScriptTypeRegistration::new(Arc::new(registration.clone())))
952 }
953
954 pub(crate) fn get_type_registration(
956 &self,
957 registration: ScriptTypeRegistration,
958 ) -> Result<
959 Union<
960 ScriptTypeRegistration,
961 Union<ScriptComponentRegistration, ScriptResourceRegistration>,
962 >,
963 InteropError,
964 > {
965 let registration = match self.get_resource_type(registration)? {
966 Ok(res) => {
967 return Ok(Union::new_right(Union::new_right(res)));
968 }
969 Err(registration) => registration,
970 };
971
972 let registration = match self.get_component_type(registration)? {
973 Ok(comp) => {
974 return Ok(Union::new_right(Union::new_left(comp)));
975 }
976 Err(registration) => registration,
977 };
978
979 Ok(Union::new_left(registration))
980 }
981
982 pub fn get_type_registration_by_name(
985 &self,
986 type_name: String,
987 ) -> Result<
988 Option<
989 Union<
990 ScriptTypeRegistration,
991 Union<ScriptComponentRegistration, ScriptResourceRegistration>,
992 >,
993 >,
994 InteropError,
995 > {
996 let val = self.get_type_by_name(&type_name);
997 Ok(match val {
998 Some(registration) => Some(self.get_type_registration(registration)?),
999 None => {
1000 let components = self.component_registry();
1002 let components = components.read();
1003 components
1004 .get(&type_name)
1005 .map(|c| Union::new_right(Union::new_left(c.registration.clone())))
1006 }
1007 })
1008 }
1009
1010 pub fn get_schedule_by_name(&self, schedule_name: String) -> Option<ReflectSchedule> {
1012 let schedule_registry = self.schedule_registry();
1013 let schedule_registry = schedule_registry.read();
1014
1015 schedule_registry
1016 .get_schedule_by_name(&schedule_name)
1017 .cloned()
1018 }
1019
1020 pub fn get_component_type(
1022 &self,
1023 registration: ScriptTypeRegistration,
1024 ) -> Result<Result<ScriptComponentRegistration, ScriptTypeRegistration>, InteropError> {
1025 Ok(match self.get_component_id(registration.type_id())? {
1026 Some(comp_id) => Ok(ScriptComponentRegistration::new(registration, comp_id)),
1027 None => Err(registration),
1028 })
1029 }
1030
1031 pub fn get_resource_type(
1033 &self,
1034 registration: ScriptTypeRegistration,
1035 ) -> Result<Result<ScriptResourceRegistration, ScriptTypeRegistration>, InteropError> {
1036 Ok(match self.get_resource_id(registration.type_id())? {
1037 Some(resource_id) => Ok(ScriptResourceRegistration::new(registration, resource_id)),
1038 None => Err(registration),
1039 })
1040 }
1041
1042 pub fn add_default_component(
1044 &self,
1045 entity: Entity,
1046 registration: ScriptComponentRegistration,
1047 ) -> Result<(), InteropError> {
1048 let instance = if let Some(default_td) = registration
1050 .type_registration()
1051 .type_registration()
1052 .data::<ReflectDefault>()
1053 {
1054 default_td.default()
1055 } else if let Some(from_world_td) = registration
1056 .type_registration()
1057 .type_registration()
1058 .data::<ReflectFromWorld>()
1059 {
1060 self.with_global_access(|world| from_world_td.from_world(world))?
1061 } else {
1062 return Err(InteropError::missing_type_data(
1063 registration.registration.type_id(),
1064 "ReflectDefault or ReflectFromWorld".to_owned(),
1065 ));
1066 };
1067
1068 registration.insert_into_entity(self.clone(), entity, instance)
1069 }
1070
1071 pub fn insert_component(
1073 &self,
1074 entity: Entity,
1075 registration: ScriptComponentRegistration,
1076 value: ReflectReference,
1077 ) -> Result<(), InteropError> {
1078 let instance = <Box<dyn PartialReflect>>::from_script_ref(
1079 registration.type_registration().type_id(),
1080 ScriptValue::Reference(value),
1081 self.clone(),
1082 )?;
1083
1084 let reflect = instance.try_into_reflect().map_err(|v| {
1085 InteropError::failed_from_reflect(
1086 Some(registration.type_registration().type_id()),
1087 format!("instance produced by conversion to target type when inserting component is not a full reflect type: {v:?}"),
1088 )
1089 })?;
1090
1091 registration.insert_into_entity(self.clone(), entity, reflect)
1092 }
1093
1094 pub fn get_component(
1096 &self,
1097 entity: Entity,
1098 component_registration: ScriptComponentRegistration,
1099 ) -> Result<Option<ReflectReference>, InteropError> {
1100 let cell = self.as_unsafe_world_cell()?;
1101 let entity = cell
1102 .get_entity(entity)
1103 .map_err(|_| InteropError::missing_entity(entity))?;
1104
1105 if entity.contains_id(component_registration.component_id) {
1106 Ok(Some(ReflectReference {
1107 base: ReflectBaseType {
1108 type_id: component_registration.type_registration().type_id(),
1109 base_id: ReflectBase::Component(
1110 entity.id(),
1111 component_registration.component_id,
1112 ),
1113 },
1114 reflect_path: ParsedPath(vec![]),
1115 }))
1116 } else {
1117 Ok(None)
1118 }
1119 }
1120
1121 pub fn has_component(
1123 &self,
1124 entity: Entity,
1125 component_id: ComponentId,
1126 ) -> Result<bool, InteropError> {
1127 let cell = self.as_unsafe_world_cell()?;
1128 let entity = cell
1129 .get_entity(entity)
1130 .map_err(|_| InteropError::missing_entity(entity))?;
1131
1132 Ok(entity.contains_id(component_id))
1133 }
1134
1135 pub fn remove_component(
1137 &self,
1138 entity: Entity,
1139 registration: ScriptComponentRegistration,
1140 ) -> Result<(), InteropError> {
1141 registration.remove_from_entity(self.clone(), entity)
1142 }
1143
1144 pub fn get_resource(
1146 &self,
1147 resource_id: ComponentId,
1148 ) -> Result<Option<ReflectReference>, InteropError> {
1149 let cell = self.as_unsafe_world_cell()?;
1150 let component_info = match cell.components().get_info(resource_id) {
1151 Some(info) => info,
1152 None => return Ok(None),
1153 };
1154
1155 Ok(Some(ReflectReference {
1156 base: ReflectBaseType {
1157 type_id: component_info
1158 .type_id()
1159 .ok_or_else(|| {
1160 InteropError::unsupported_operation(
1161 None,
1162 None,
1163 format!(
1164 "Resource {} does not have a type id. Such resources are not supported by BMS.",
1165 component_info.name()
1166 ),
1167 )
1168 })?,
1169 base_id: ReflectBase::Resource(resource_id),
1170 },
1171 reflect_path: ParsedPath(vec![]),
1172 }))
1173 }
1174
1175 pub fn remove_resource(
1177 &self,
1178 registration: ScriptResourceRegistration,
1179 ) -> Result<(), InteropError> {
1180 let component_data = registration
1181 .type_registration()
1182 .type_registration()
1183 .data::<ReflectResource>()
1184 .ok_or_else(|| {
1185 InteropError::missing_type_data(
1186 registration.registration.type_id(),
1187 "ReflectResource".to_owned(),
1188 )
1189 })?;
1190
1191 self.with_global_access(|world| component_data.remove(world))
1193 }
1194
1195 pub fn has_resource(&self, resource_id: ComponentId) -> Result<bool, InteropError> {
1197 let cell = self.as_unsafe_world_cell()?;
1198 let res_ptr = unsafe { cell.get_resource_by_id(resource_id) };
1200 Ok(res_ptr.is_some())
1201 }
1202
1203 pub fn has_entity(&self, entity: Entity) -> Result<bool, InteropError> {
1205 self.is_valid_entity(entity)
1206 }
1207
1208 pub fn get_children(&self, entity: Entity) -> Result<Vec<Entity>, InteropError> {
1210 if !self.is_valid_entity(entity)? {
1211 return Err(InteropError::missing_entity(entity));
1212 }
1213
1214 self.with_component(entity, |c: Option<&Children>| {
1215 c.map(|c| c.to_vec()).unwrap_or_default()
1216 })
1217 }
1218
1219 pub fn get_parent(&self, entity: Entity) -> Result<Option<Entity>, InteropError> {
1221 if !self.is_valid_entity(entity)? {
1222 return Err(InteropError::missing_entity(entity));
1223 }
1224
1225 self.with_component(entity, |c: Option<&ChildOf>| c.map(|c| c.parent()))
1226 }
1227
1228 pub fn push_children(&self, parent: Entity, children: &[Entity]) -> Result<(), InteropError> {
1230 if !self.is_valid_entity(parent)? {
1232 return Err(InteropError::missing_entity(parent));
1233 }
1234 for c in children {
1235 if !self.is_valid_entity(*c)? {
1236 return Err(InteropError::missing_entity(*c));
1237 }
1238 }
1239 self.with_global_access(|world| {
1240 let mut queue = CommandQueue::default();
1241 let mut commands = Commands::new(&mut queue, world);
1242 commands.entity(parent).add_children(children);
1243 queue.apply(world);
1244 })
1245 }
1246
1247 pub fn remove_children(&self, parent: Entity, children: &[Entity]) -> Result<(), InteropError> {
1249 if !self.is_valid_entity(parent)? {
1250 return Err(InteropError::missing_entity(parent));
1251 }
1252
1253 for c in children {
1254 if !self.is_valid_entity(*c)? {
1255 return Err(InteropError::missing_entity(*c));
1256 }
1257 }
1258 self.with_global_access(|world| {
1259 let mut queue = CommandQueue::default();
1260 let mut commands = Commands::new(&mut queue, world);
1261 commands.entity(parent).remove_children(children);
1262 queue.apply(world);
1263 })
1264 }
1265
1266 pub fn insert_children(
1268 &self,
1269 parent: Entity,
1270 index: usize,
1271 children: &[Entity],
1272 ) -> Result<(), InteropError> {
1273 if !self.is_valid_entity(parent)? {
1274 return Err(InteropError::missing_entity(parent));
1275 }
1276
1277 for c in children {
1278 if !self.is_valid_entity(*c)? {
1279 return Err(InteropError::missing_entity(*c));
1280 }
1281 }
1282
1283 self.with_global_access(|world| {
1284 let mut queue = CommandQueue::default();
1285 let mut commands = Commands::new(&mut queue, world);
1286 commands.entity(parent).insert_children(index, children);
1287 queue.apply(world);
1288 })
1289 }
1290
1291 pub fn despawn_recursive(&self, parent: Entity) -> Result<(), InteropError> {
1293 if !self.is_valid_entity(parent)? {
1294 return Err(InteropError::missing_entity(parent));
1295 }
1296 self.with_global_access(|world| {
1297 let mut queue = CommandQueue::default();
1298 let mut commands = Commands::new(&mut queue, world);
1299 commands.entity(parent).despawn();
1300 queue.apply(world);
1301 })
1302 }
1303
1304 pub fn despawn(&self, entity: Entity) -> Result<(), InteropError> {
1306 if !self.is_valid_entity(entity)? {
1307 return Err(InteropError::missing_entity(entity));
1308 }
1309
1310 self.with_global_access(|world| {
1311 let mut queue = CommandQueue::default();
1312 let mut commands = Commands::new(&mut queue, world);
1313 commands.entity(entity).remove::<Children>().despawn();
1314 queue.apply(world);
1315 })
1316 }
1317
1318 pub fn despawn_descendants(&self, parent: Entity) -> Result<(), InteropError> {
1320 if !self.is_valid_entity(parent)? {
1321 return Err(InteropError::missing_entity(parent));
1322 }
1323
1324 self.with_global_access(|world| {
1325 let mut queue = CommandQueue::default();
1326 let mut commands = Commands::new(&mut queue, world);
1327 commands.entity(parent).despawn_related::<Children>();
1328 queue.apply(world);
1329 })
1330 }
1331
1332 pub fn exit(&self) -> Result<(), InteropError> {
1334 self.with_global_access(|world| {
1335 world.send_event(AppExit::Success);
1336 })
1337 }
1338}
1339
1340pub trait WorldContainer {
1342 type Error: Debug;
1344 fn set_world(&mut self, world: WorldGuard<'static>) -> Result<(), Self::Error>;
1346
1347 fn try_get_world<'l>(&self) -> Result<WorldGuard<'l>, Self::Error>;
1349}
1350
1351pub struct ThreadWorldContainer;
1353
1354thread_local! {
1355 static WORLD_CALLBACK_ACCESS: RefCell<Option<WorldGuard<'static>>> = const { RefCell::new(None) };
1356}
1357#[profiling::all_functions]
1358impl WorldContainer for ThreadWorldContainer {
1359 type Error = InteropError;
1360
1361 fn set_world(&mut self, world: WorldGuard<'static>) -> Result<(), Self::Error> {
1362 WORLD_CALLBACK_ACCESS.with(|w| {
1363 w.replace(Some(world));
1364 });
1365 Ok(())
1366 }
1367
1368 fn try_get_world<'l>(&self) -> Result<WorldGuard<'l>, Self::Error> {
1369 WORLD_CALLBACK_ACCESS
1370 .with(|w| w.borrow().clone().ok_or_else(InteropError::missing_world))
1371 .map(|v| v.shorten_lifetime())
1372 }
1373}
1374
1375impl GetTypeInfo for ThreadWorldContainer {
1376 fn get_type_info(&self, type_id: TypeId) -> Option<&TypeInfo> {
1377 let world = self.try_get_world().ok()?;
1378 let registry = world.type_registry();
1379 let registry = registry.read();
1380 registry.get(type_id).map(|r| r.type_info())
1381 }
1382
1383 fn query_type_registration(
1384 &self,
1385 type_id: TypeId,
1386 type_data_id: TypeId,
1387 ) -> Option<Box<dyn bevy_reflect::TypeData>> {
1388 let world = self.try_get_world().ok()?;
1389 let registry = world.type_registry();
1390 let registry = registry.read();
1391 registry
1392 .get(type_id)
1393 .and_then(|r| r.data_by_id(type_data_id).map(|t| t.clone_type_data()))
1394 }
1395
1396 fn get_component_info(
1397 &self,
1398 component_id: ComponentId,
1399 ) -> Option<&bevy_ecs::component::ComponentInfo> {
1400 let world = self.try_get_world().ok()?;
1401 let cell = world.as_unsafe_world_cell().ok()?;
1402 cell.components().get_info(component_id)
1403 }
1404
1405 unsafe fn as_any_static(&self) -> &dyn Any {
1406 self
1407 }
1408}
1409
1410impl GetTypeInfo for WorldGuard<'_> {
1411 fn get_type_info(&self, type_id: TypeId) -> Option<&TypeInfo> {
1412 let registry = self.type_registry();
1413 let registry = registry.read();
1414 registry.get(type_id).map(|r| r.type_info())
1415 }
1416
1417 fn query_type_registration(
1418 &self,
1419 type_id: TypeId,
1420 type_data_id: TypeId,
1421 ) -> Option<Box<dyn bevy_reflect::TypeData>> {
1422 let registry = self.type_registry();
1423 let registry = registry.read();
1424 registry
1425 .get(type_id)
1426 .and_then(|r| r.data_by_id(type_data_id).map(|t| t.clone_type_data()))
1427 }
1428
1429 fn get_component_info(
1430 &self,
1431 component_id: ComponentId,
1432 ) -> Option<&bevy_ecs::component::ComponentInfo> {
1433 let cell = self.as_unsafe_world_cell().ok()?;
1434 cell.components().get_info(component_id)
1435 }
1436
1437 unsafe fn as_any_static(&self) -> &dyn Any {
1440 let static_self: &WorldGuard<'static> = unsafe { std::mem::transmute(self) };
1441 static_self as &dyn Any
1442 }
1443}
1444
1445#[cfg(test)]
1446mod test {
1447 use super::*;
1448 use bevy_reflect::{GetTypeRegistration, ReflectFromReflect};
1449 use test_utils::test_data::{SimpleEnum, SimpleStruct, SimpleTupleStruct, setup_world};
1450
1451 #[test]
1452 fn test_construct_struct() {
1453 let mut world = setup_world(|_, _| {});
1454 let world = WorldAccessGuard::new_exclusive(&mut world);
1455
1456 let registry = world.type_registry();
1457 let registry = registry.read();
1458
1459 let registration = registry.get(TypeId::of::<SimpleStruct>()).unwrap().clone();
1460 let type_registration = ScriptTypeRegistration::new(Arc::new(registration));
1461
1462 let payload = HashMap::from_iter(vec![("foo".to_owned(), ScriptValue::Integer(1))]);
1463
1464 let result = world.construct(type_registration, payload, false);
1465 let expected =
1466 Ok::<_, InteropError>(Box::new(SimpleStruct { foo: 1 }) as Box<dyn PartialReflect>);
1467 pretty_assertions::assert_str_eq!(format!("{result:#?}"), format!("{expected:#?}"));
1468 }
1469
1470 #[test]
1471 fn test_construct_tuple_struct() {
1472 let mut world = setup_world(|_, _| {});
1473 let world = WorldAccessGuard::new_exclusive(&mut world);
1474
1475 let registry = world.type_registry();
1476 let registry = registry.read();
1477
1478 let registration = registry
1479 .get(TypeId::of::<SimpleTupleStruct>())
1480 .unwrap()
1481 .clone();
1482 let type_registration = ScriptTypeRegistration::new(Arc::new(registration));
1483
1484 let payload = HashMap::from_iter(vec![("_0".to_owned(), ScriptValue::Integer(1))]);
1486
1487 let result = world.construct(type_registration.clone(), payload, false);
1488 let expected =
1489 Ok::<_, InteropError>(Box::new(SimpleTupleStruct(1)) as Box<dyn PartialReflect>);
1490 pretty_assertions::assert_str_eq!(format!("{result:#?}"), format!("{expected:#?}"));
1491
1492 let payload = HashMap::from_iter(vec![("_1".to_owned(), ScriptValue::Integer(1))]);
1494
1495 let result = world.construct(type_registration, payload, true);
1496 let expected =
1497 Ok::<_, InteropError>(Box::new(SimpleTupleStruct(1)) as Box<dyn PartialReflect>);
1498
1499 pretty_assertions::assert_str_eq!(format!("{result:#?}"), format!("{expected:#?}"));
1500 }
1501
1502 #[test]
1503 fn test_construct_tuple() {
1504 let mut world = setup_world(|_, registry| {
1505 registry.register::<(usize, usize)>();
1506 registry.register_type_data::<(usize, usize), ReflectFromReflect>();
1508 });
1509
1510 <usize as GetTypeRegistration>::get_type_registration();
1511 let world = WorldAccessGuard::new_exclusive(&mut world);
1512
1513 let registry = world.type_registry();
1514 let registry = registry.read();
1515
1516 let registration = registry
1517 .get(TypeId::of::<(usize, usize)>())
1518 .unwrap()
1519 .clone();
1520 let type_registration = ScriptTypeRegistration::new(Arc::new(registration));
1521
1522 let payload = HashMap::from_iter(vec![
1524 ("_0".to_owned(), ScriptValue::Integer(1)),
1525 ("_1".to_owned(), ScriptValue::Integer(2)),
1526 ]);
1527
1528 let result = world.construct(type_registration.clone(), payload, false);
1529 let expected = Ok::<_, InteropError>(Box::new((1, 2)) as Box<dyn PartialReflect>);
1530 pretty_assertions::assert_str_eq!(format!("{result:#?}"), format!("{expected:#?}"));
1531
1532 let payload = HashMap::from_iter(vec![
1534 ("_1".to_owned(), ScriptValue::Integer(1)),
1535 ("_2".to_owned(), ScriptValue::Integer(2)),
1536 ]);
1537
1538 let result = world.construct(type_registration.clone(), payload, true);
1539 let expected = Ok::<_, InteropError>(Box::new((1, 2)) as Box<dyn PartialReflect>);
1540 pretty_assertions::assert_str_eq!(format!("{result:#?}"), format!("{expected:#?}"));
1541 }
1542
1543 #[test]
1544 fn test_construct_enum() {
1545 let mut world = setup_world(|_, _| {});
1546 let world = WorldAccessGuard::new_exclusive(&mut world);
1547
1548 let registry = world.type_registry();
1549 let registry = registry.read();
1550
1551 let registration = registry.get(TypeId::of::<SimpleEnum>()).unwrap().clone();
1552 let type_registration = ScriptTypeRegistration::new(Arc::new(registration));
1553
1554 let payload = HashMap::from_iter(vec![
1556 ("foo".to_owned(), ScriptValue::Integer(1)),
1557 ("variant".to_owned(), ScriptValue::String("Struct".into())),
1558 ]);
1559
1560 let result = world.construct(type_registration.clone(), payload, false);
1561 let expected = Ok::<_, InteropError>(
1562 Box::new(SimpleEnum::Struct { foo: 1 }) as Box<dyn PartialReflect>
1563 );
1564 pretty_assertions::assert_str_eq!(format!("{result:#?}"), format!("{expected:#?}"));
1565
1566 let payload = HashMap::from_iter(vec![
1568 ("_0".to_owned(), ScriptValue::Integer(1)),
1569 (
1570 "variant".to_owned(),
1571 ScriptValue::String("TupleStruct".into()),
1572 ),
1573 ]);
1574
1575 let result = world.construct(type_registration.clone(), payload, false);
1576 let expected =
1577 Ok::<_, InteropError>(Box::new(SimpleEnum::TupleStruct(1)) as Box<dyn PartialReflect>);
1578
1579 pretty_assertions::assert_str_eq!(format!("{result:#?}"), format!("{expected:#?}"));
1580
1581 let payload = HashMap::from_iter(vec![(
1583 "variant".to_owned(),
1584 ScriptValue::String("Unit".into()),
1585 )]);
1586
1587 let result = world.construct(type_registration, payload, false);
1588 let expected = Ok::<_, InteropError>(Box::new(SimpleEnum::Unit) as Box<dyn PartialReflect>);
1589 pretty_assertions::assert_str_eq!(format!("{result:#?}"), format!("{expected:#?}"));
1590 }
1591
1592 #[test]
1593 fn test_scoped_handle_invalidate_doesnt_invalidate_parent() {
1594 let mut world = setup_world(|_, _| {});
1595 let world = WorldAccessGuard::new_exclusive(&mut world);
1596 let scoped_world = world.scope();
1597
1598 scoped_world.spawn().unwrap();
1600 world.spawn().unwrap();
1601 pretty_assertions::assert_eq!(scoped_world.is_valid(), true);
1602 pretty_assertions::assert_eq!(world.is_valid(), true);
1603
1604 scoped_world.invalidate();
1605
1606 pretty_assertions::assert_eq!(scoped_world.is_valid(), false);
1608 pretty_assertions::assert_eq!(world.is_valid(), true);
1609 world.spawn().unwrap();
1610 }
1611
1612 #[test]
1613 fn with_existing_static_guard_does_not_invalidate_original() {
1614 let mut world = setup_world(|_, _| {});
1615 let world = WorldAccessGuard::new_exclusive(&mut world);
1616
1617 let mut sneaky_clone = None;
1618 WorldAccessGuard::with_existing_static_guard(world.clone(), |g| {
1619 pretty_assertions::assert_eq!(g.is_valid(), true);
1620 sneaky_clone = Some(g.clone());
1621 });
1622 pretty_assertions::assert_eq!(world.is_valid(), true, "original world was invalidated");
1623 pretty_assertions::assert_eq!(
1624 sneaky_clone.map(|c| c.is_valid()),
1625 Some(false),
1626 "scoped world was not invalidated"
1627 );
1628 }
1629
1630 #[test]
1631 fn test_with_access_scope_success() {
1632 let mut world = setup_world(|_, _| {});
1633 let guard = WorldAccessGuard::new_exclusive(&mut world);
1634
1635 let result = unsafe { guard.with_access_scope(|| 100) };
1637 assert_eq!(result.unwrap(), 100);
1638 }
1639}