Skip to main content

bevy_mod_scripting_bindings/
script_component.rs

1//! Everything necessary to support scripts registering their own components
2
3use super::{ScriptComponentRegistration, ScriptValue};
4use ::{
5    bevy_app::{App, Plugin},
6    bevy_ecs::component::{Component, Mutable, StorageType},
7    bevy_reflect::Reflect,
8};
9use bevy_ecs::resource::Resource;
10use bevy_platform::collections::HashMap;
11use bevy_reflect::std_traits::ReflectDefault;
12use parking_lot::RwLock;
13use std::sync::Arc;
14/// A dynamic script component
15#[derive(Reflect, Clone, Default)]
16#[reflect(Default)]
17pub struct DynamicComponent {
18    data: ScriptValue,
19}
20
21/// Some metadata about dynamic script components
22pub struct DynamicComponentInfo {
23    /// The name of the component
24    pub name: String,
25    /// The type registration for the component
26    pub registration: ScriptComponentRegistration,
27}
28
29impl Component for DynamicComponent {
30    const STORAGE_TYPE: StorageType = StorageType::Table;
31    type Mutability = Mutable;
32}
33
34/// A registry of dynamically registered script components
35#[derive(Clone, Resource, Default)]
36pub struct AppScriptComponentRegistry(pub Arc<RwLock<ScriptComponentRegistry>>);
37
38#[profiling::all_functions]
39impl AppScriptComponentRegistry {
40    /// Reads the underlying registry
41    pub fn read(&self) -> parking_lot::RwLockReadGuard<'_, ScriptComponentRegistry> {
42        self.0.read()
43    }
44
45    /// Writes to the underlying registry
46    pub fn write(&self) -> parking_lot::RwLockWriteGuard<'_, ScriptComponentRegistry> {
47        self.0.write()
48    }
49}
50
51#[derive(Default)]
52/// A registry of dynamically registered script components
53pub struct ScriptComponentRegistry {
54    components: HashMap<String, DynamicComponentInfo>,
55}
56
57#[profiling::all_functions]
58impl ScriptComponentRegistry {
59    /// Registers a dynamic script component, possibly overwriting an existing one
60    pub fn register(&mut self, info: DynamicComponentInfo) {
61        self.components.insert(info.name.clone(), info);
62    }
63
64    /// Gets a dynamic script component by name
65    pub fn get(&self, name: &str) -> Option<&DynamicComponentInfo> {
66        self.components.get(name)
67    }
68}
69
70/// A plugin to support dynamic script components
71pub struct DynamicScriptComponentPlugin;
72
73impl Plugin for DynamicScriptComponentPlugin {
74    fn build(&self, app: &mut App) {
75        app.init_resource::<AppScriptComponentRegistry>()
76            .register_type::<DynamicComponent>();
77    }
78}
79
80#[cfg(test)]
81mod test {
82    use bevy_ecs::world::World;
83    use bevy_mod_scripting_world::{WorldAccessGuard, WorldGuard};
84
85    use crate::{CurrentScriptAttachment, WorldExtensions};
86
87    use super::*;
88
89    #[test]
90    fn test_script_component() {
91        let mut world = World::new();
92        world.init_resource::<AppScriptComponentRegistry>();
93        let cache = WorldGuard::setup_cache(&world, CurrentScriptAttachment::default());
94        let registration = {
95            let guard = WorldAccessGuard::new_exclusive(&mut world, cache);
96
97            guard
98                .register_script_component("ScriptTest".to_string())
99                .unwrap()
100        };
101
102        let registry = world.get_resource::<AppScriptComponentRegistry>().unwrap();
103
104        let registry = registry.read();
105        let info = registry.get("ScriptTest").unwrap();
106        assert_eq!(info.registration.component_id, registration.component_id);
107        assert_eq!(info.name, "ScriptTest");
108
109        // can get the component through the world
110        let component = world
111            .components()
112            .get_info(info.registration.component_id)
113            .unwrap();
114
115        assert_eq!(component.name(), "ScriptTest".into());
116    }
117}