bevy_mod_scripting_bindings/
world.rs

1//! # Motivation
2//!
3//! Traits and structs needed to support the creation of bindings for scripting languages.
4//! reflection gives us access to `dyn PartialReflect` objects via their type name,
5//! Scripting languages only really support `Clone` objects so if we want to support references,
6//! we need wrapper types which have owned and ref variants.
7
8use 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
66/// Prefer to directly using [`WorldAccessGuard`]. If the underlying type changes, this alias will be updated.
67pub type WorldGuard<'w> = WorldAccessGuard<'w>;
68/// Similar to [`WorldGuard`], but without the arc, use for when you don't need the outer Arc.
69pub type WorldGuardRef<'w> = &'w WorldAccessGuard<'w>;
70
71/// Provides safe access to the world via [`WorldAccess`] permissions, which enforce aliasing rules at runtime in multi-thread environments
72#[derive(Clone, Debug)]
73pub struct WorldAccessGuard<'w> {
74    /// The guard this guard pointer represents
75    pub(crate) inner: Rc<WorldAccessGuardInner<'w>>,
76    /// if true the guard is invalid and cannot be used, stored as a second pointer so that this validity can be
77    /// stored separate from the contents of the guard
78    invalid: Rc<AtomicBool>,
79}
80impl WorldAccessGuard<'_> {
81    /// Returns the id of the world this guard provides access to
82    pub fn id(&self) -> WorldId {
83        self.inner.cell.id()
84    }
85}
86
87/// Used to decrease the stack size of [`WorldAccessGuard`]
88pub(crate) struct WorldAccessGuardInner<'w> {
89    /// Safety: cannot be used unless the scope depth is less than the max valid scope
90    cell: UnsafeWorldCell<'w>,
91    // TODO: this is fairly hefty, explore sparse sets, bit fields etc
92    pub(crate) accesses: AnyAccessMap,
93    /// Cached for convenience, since we need it for most operations, means we don't need to lock the type registry every time
94    type_registry: TypeRegistryArc,
95    /// The script allocator for the world
96    allocator: AppReflectAllocator,
97    /// The function registry for the world
98    function_registry: AppScriptFunctionRegistry,
99    /// The schedule registry for the world
100    schedule_registry: AppScheduleRegistry,
101    /// The registry of script registered components
102    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    /// Shortens the lifetime of the guard to the given lifetime.
114    pub(crate) fn shorten_lifetime<'w>(self) -> WorldGuard<'w> {
115        // Safety: todo
116        unsafe { std::mem::transmute(self) }
117    }
118}
119#[profiling::all_functions]
120impl<'w> WorldAccessGuard<'w> {
121    /// creates a new guard derived from this one, which if invalidated, will not invalidate the original
122    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    /// Returns true if the guard is valid, false if it is invalid
134    fn is_valid(&self) -> bool {
135        !self.invalid.load(std::sync::atomic::Ordering::Relaxed)
136    }
137
138    /// Invalidates the world access guard, making it and any guards derived from this one unusable.
139    pub fn invalidate(&self) {
140        self.invalid
141            .store(true, std::sync::atomic::Ordering::Relaxed);
142    }
143
144    /// Safely allows access to the world for the duration of the closure via a static [`WorldAccessGuard`].
145    ///
146    /// The guard is invalidated at the end of the closure, meaning the world cannot be accessed at all after the closure ends.
147    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        // safety: we invalidate the guard after the closure is called, meaning the world cannot be accessed at all after the 'w lifetime ends
153        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    /// Safely allows access to the world for the duration of the closure via a static [`WorldAccessGuard`] using a previously lifetimed world guard.
161    /// Will invalidate the static guard at the end but not the original.
162    pub fn with_existing_static_guard<O>(
163        guard: WorldAccessGuard<'w>,
164        f: impl FnOnce(WorldGuard<'static>) -> O,
165    ) -> O {
166        // safety: we invalidate the guard after the closure is called, meaning the world cannot be accessed at all after the 'w lifetime ends, from the static guard
167        // i.e. even if somebody squirells it away, it will be useless.
168        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    /// Creates a new [`WorldAccessGuard`] from a possibly non-exclusive access to the world.
175    ///
176    /// It requires specyfing the exact accesses that are allowed to be given out by the guard.
177    /// Those accesses need to be safe to be given out to the script, as the guard will assume that it is safe to give them out in any way.
178    ///
179    /// # Safety
180    /// - The caller must ensure that the accesses in subset are not aliased by any other access
181    /// - If an access is allowed in this subset, but alised by someone else,
182    /// either by being converted to mutable or non mutable reference, this guard will be unsafe.
183    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                    // allocations live beyond the world, and can be safely accessed
198                    |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    /// Creates a new [`WorldAccessGuard`] for the given mutable borrow of the world.
211    ///
212    /// Creating a guard requires that some resources exist in the world, namely:
213    /// - [`AppTypeRegistry`]
214    /// - [`AppReflectAllocator`]
215    /// - [`AppScriptFunctionRegistry`]
216    ///
217    /// If these resources do not exist, they will be initialized.
218    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    /// Queues a command to the world, which will be executed later.
247    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    /// Runs a closure within an isolated access scope, releasing leftover accesses, should only be used in a single-threaded context.
254    ///
255    /// Safety:
256    /// - The caller must ensure it's safe to release any potentially locked accesses.
257    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    /// Purely debugging utility to list all accesses currently held.
265    pub fn list_accesses(&self) -> Vec<(ReflectAccessId, AccessCount)> {
266        self.inner.accesses.list_accesses()
267    }
268
269    /// Should only really be used for testing purposes
270    pub unsafe fn release_all_accesses(&self) {
271        self.inner.accesses.release_all_accesses();
272    }
273
274    /// Returns the number of accesses currently held.
275    pub fn access_len(&self) -> usize {
276        self.inner.accesses.count_accesses()
277    }
278
279    /// Retrieves the underlying unsafe world cell, with no additional guarantees of safety
280    /// proceed with caution and only use this if you understand what you're doing
281    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    /// Retrieves the underlying read only unsafe world cell, with no additional guarantees of safety
290    /// proceed with caution and only use this if you understand what you're doing
291    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    /// Gets the component id of the given component or resource
300    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    /// Gets the resource id of the given component or resource
308    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    /// A utility for running a closure with scoped read access to the given id
316    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            // Safety: just claimed this access
325            unsafe { self.release_access(id) };
326            out
327        } else {
328            Err(())
329        }
330    }
331
332    /// A utility for running a closure with scoped write access to the given id
333    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            // Safety: just claimed this access
342            unsafe { self.release_access(id) };
343            out
344        } else {
345            Err(())
346        }
347    }
348
349    /// Get the location of the given access
350    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    /// Claims read access to the given type.
359    pub fn claim_read_access(&self, raid: ReflectAccessId) -> bool {
360        self.inner.accesses.claim_read_access(raid)
361    }
362
363    #[track_caller]
364    /// Claims write access to the given type.
365    pub fn claim_write_access(&self, raid: ReflectAccessId) -> bool {
366        self.inner.accesses.claim_write_access(raid)
367    }
368
369    /// Releases read or write access to the given type.
370    ///
371    /// # Safety
372    /// - This can only be called safely after all references to the type created using the access have been dropped
373    /// - You can only call this if you previously called one of: [`WorldAccessGuard::claim_read_access`] or [`WorldAccessGuard::claim_write_access`]
374    /// - The number of claim and release calls for the same id must always match
375    pub unsafe fn release_access(&self, raid: ReflectAccessId) {
376        self.inner.accesses.release_access(raid)
377    }
378
379    /// Claims global access to the world
380    pub fn claim_global_access(&self) -> bool {
381        self.inner.accesses.claim_global_access()
382    }
383
384    /// Releases global access to the world
385    ///
386    /// # Safety
387    /// - This can only be called safely after all references created using the access have been dropped
388    pub unsafe fn release_global_access(&self) {
389        self.inner.accesses.release_global_access()
390    }
391
392    /// Returns the type registry for the world
393    pub fn type_registry(&self) -> TypeRegistryArc {
394        self.inner.type_registry.clone()
395    }
396
397    /// Returns the schedule registry for the world
398    pub fn schedule_registry(&self) -> AppScheduleRegistry {
399        self.inner.schedule_registry.clone()
400    }
401
402    /// Returns the component registry for the world
403    pub fn component_registry(&self) -> AppScriptComponentRegistry {
404        self.inner.script_component_registry.clone()
405    }
406
407    /// Returns the script allocator for the world
408    pub fn allocator(&self) -> AppReflectAllocator {
409        self.inner.allocator.clone()
410    }
411
412    /// Returns the function registry for the world
413    pub fn script_function_registry(&self) -> AppScriptFunctionRegistry {
414        self.inner.function_registry.clone()
415    }
416
417    /// Claims access to the world for the duration of the closure, allowing for global access to the world.
418    #[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                // safety: we have global access for the duration of the closure
428                let world = unsafe { self.as_unsafe_world_cell()?.world_mut() };
429                Ok(f(world))
430            }
431        )?
432    }
433
434    /// Safely accesses the resource by claiming and releasing access to it.
435    ///
436    /// # Panics
437    /// - if the resource does not exist
438    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                // Safety: we have acquired access for the duration of the closure
452                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    /// Safely accesses the resource by claiming and releasing access to it.
464    ///
465    /// # Panics
466    /// - if the resource does not exist
467    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                // Safety: we have acquired access for the duration of the closure
480                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    /// Safely accesses the component by claiming and releasing access to it.
492    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                // Safety: we have acquired access for the duration of the closure
505                f(unsafe { cell.get_entity(entity).map(|e| e.get::<T>()) }
506                    .ok()
507                    .unwrap_or(None))
508            }
509        )
510    }
511
512    /// Safely accesses the component by claiming and releasing access to it.
513    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                // Safety: we have acquired access for the duration of the closure
527                f(unsafe { cell.get_entity(entity).map(|e| e.get_mut::<T>()) }
528                    .ok()
529                    .unwrap_or(None))
530            }
531        )
532    }
533
534    /// Safey modify or insert a component by claiming and releasing global access.
535    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    /// Try to lookup a function with the given name on the given type id's namespaces.
557    ///
558    /// Returns the function if found, otherwise returns the name of the function that was not found.
559    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    /// Iterates over all available functions on the type id's namespace + those available on any reference if any exist.
579    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    /// checks if a given entity exists and is valid
597    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    /// 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.
603    /// Currently does this by repeatedly trying each overload until one succeeds or all fail.
604    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/// Impl block for higher level world methods
638#[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        // if the value is missing, try to construct a default and return it
647        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        // otherwise we need to use from_script_ref
669        <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            // correct for indexing
699            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            // correct for indexing
723            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    /// An arbitrary type constructor utility.
741    ///
742    /// Allows the construction of arbitrary types (within limits dictated by the API) from the script directly
743    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        // figure out the kind of type we're building
750        let type_info = type_.registration.type_info();
751        // we just need to a) extract fields, if enum we need a "variant" field specifying the variant
752        // then build the corresponding dynamic structure, whatever it may be
753
754        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                // extract variant from "variant"
817                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                        // same as above struct variant
842                        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                        // same as tuple variant
865                        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        // try to construct type from reflect
898        // TODO: it would be nice to have a <dyn PartialReflect>::from_reflect_with_fallback equivalent, that does exactly that
899        // only using this as it's already there and convenient, the clone variant hitting will be confusing to end users
900        <dyn PartialReflect>::from_reflect_or_clone(dynamic.as_ref(), self.clone())
901    }
902
903    /// Loads a script from the given asset path with default settings.
904    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    /// Checks the load state of a script asset.
909    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    // /// Attaches a script
917    // pub fn attach_script(&self, attachment: ScriptAttachment) -> Result<(), InteropError> {
918    //     match attachment {
919    //         ScriptAttachment::EntityScript(entity, handle) => {
920    //             // find existing script components on the entity
921    //             self.with_or_insert_component_mut(entity, |c: &mut ScriptComponent| {
922    //                 c.0.push(handle.clone())
923    //             })?;
924    //         }
925    //         ScriptAttachment::StaticScript(handle) => {
926    //             self.queue(AddStaticScript::new(handle))?;
927    //         }
928    //     };
929
930    //     Ok(())
931    // }
932
933    /// Spawns a new entity in the world
934    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    /// get a type registration for the type, without checking if it's a component or resource
945    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    /// get a type erased type registration for the type including information about whether it's a component or resource
955    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    /// Similar to [`Self::get_type_by_name`] but returns a type erased [`ScriptTypeRegistration`], [`ScriptComponentRegistration`] or [`ScriptResourceRegistration`]
983    /// depending on the underlying type and state of the world.
984    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                // try the component registry
1001                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    /// get a schedule by name
1011    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    /// get a component type registration for the type
1021    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    /// get a resource type registration for the type
1032    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    /// add a default component to an entity
1043    pub fn add_default_component(
1044        &self,
1045        entity: Entity,
1046        registration: ScriptComponentRegistration,
1047    ) -> Result<(), InteropError> {
1048        // we look for ReflectDefault or ReflectFromWorld data then a ReflectComponent data
1049        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    /// insert the component into the entity
1072    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    /// get the component from the entity
1095    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    /// check if the entity has the component
1122    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    /// remove the component from the entity
1136    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    /// get the given resource
1145    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    /// remove the given resource
1176    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        //  TODO: this shouldn't need entire world access it feels
1192        self.with_global_access(|world| component_data.remove(world))
1193    }
1194
1195    /// check if the entity has the resource
1196    pub fn has_resource(&self, resource_id: ComponentId) -> Result<bool, InteropError> {
1197        let cell = self.as_unsafe_world_cell()?;
1198        // Safety: we are not reading the value at all
1199        let res_ptr = unsafe { cell.get_resource_by_id(resource_id) };
1200        Ok(res_ptr.is_some())
1201    }
1202
1203    /// check the given entity exists
1204    pub fn has_entity(&self, entity: Entity) -> Result<bool, InteropError> {
1205        self.is_valid_entity(entity)
1206    }
1207
1208    /// get the children of the given entity
1209    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    /// get the parent of the given entity
1220    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    /// insert children into the given entity
1229    pub fn push_children(&self, parent: Entity, children: &[Entity]) -> Result<(), InteropError> {
1230        // verify entities exist
1231        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    /// remove children from the given entity
1248    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    /// insert children into the given entity at the given index
1267    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    /// despawn this and all children of the given entity recursively
1292    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    /// despawn the given entity
1305    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    /// despawn all children of the given entity recursively
1319    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    /// Sends AppExit event to the world with success status
1333    pub fn exit(&self) -> Result<(), InteropError> {
1334        self.with_global_access(|world| {
1335            world.send_event(AppExit::Success);
1336        })
1337    }
1338}
1339
1340/// Utility type for accessing the world in a callback
1341pub trait WorldContainer {
1342    /// The error type for the container
1343    type Error: Debug;
1344    /// Sets the world to the given value in the container
1345    fn set_world(&mut self, world: WorldGuard<'static>) -> Result<(), Self::Error>;
1346
1347    /// Tries to get the world from the container
1348    fn try_get_world<'l>(&self) -> Result<WorldGuard<'l>, Self::Error>;
1349}
1350
1351/// A world container that stores the world in a thread local
1352pub 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    /// # Safety
1438    /// - TODO: should generaly be safe as the guard is invalidated once the world is out of scope
1439    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        // zero indexed
1485        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        // one indexed
1493        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            // TODO: does this ever get registered on normal types? I don't think so: https://github.com/bevyengine/bevy/issues/17981
1507            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        // zero indexed
1523        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        // one indexed
1533        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        // struct version
1555        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        // tuple struct version
1567        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        // unit version
1582        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        // can use scoped & normal worlds
1599        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        // can only use normal world
1607        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        // within the access scope, no extra accesses are claimed
1636        let result = unsafe { guard.with_access_scope(|| 100) };
1637        assert_eq!(result.unwrap(), 100);
1638    }
1639}