From 4b3c1338b8c389a68ec6b1f6646a3c25687d179e Mon Sep 17 00:00:00 2001 From: makspll Date: Mon, 20 Jan 2025 12:06:26 +0000 Subject: [PATCH] feat: add insert_component script function --- .../src/bindings/reference.rs | 6 --- .../src/bindings/world.rs | 43 +++++++++++++++++++ .../bevy_mod_scripting_functions/src/core.rs | 7 +++ ..._no_default_or_from_world_data_inserts.lua | 8 ++++ ..._with_default_no_component_data_errors.lua | 8 ++++ docs/src/ScriptingReference/world.md | 18 ++++++++ 6 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 crates/languages/bevy_mod_scripting_lua/tests/data/insert_component/component_no_default_or_from_world_data_inserts.lua create mode 100644 crates/languages/bevy_mod_scripting_lua/tests/data/insert_component/component_with_default_no_component_data_errors.lua diff --git a/crates/bevy_mod_scripting_core/src/bindings/reference.rs b/crates/bevy_mod_scripting_core/src/bindings/reference.rs index fe24ed6e96..10ca53a317 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/reference.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/reference.rs @@ -221,9 +221,6 @@ impl ReflectReference { /// The way to access the value of the reference, that is the pointed-to value. /// This method is safe to use as it ensures no-one else has aliasing access to the value at the same time. - /// - /// # Panics - /// - if the value is aliased and the access is not allowed #[track_caller] pub fn with_reflect O>( &self, @@ -241,9 +238,6 @@ impl ReflectReference { /// The way to access the value of the reference, that is the pointed-to value. /// This method is safe to use as it ensures no-one else has aliasing access to the value at the same time. - /// - /// # Panics - /// - if the value is aliased and the access is not allowed #[track_caller] pub fn with_reflect_mut O>( &self, diff --git a/crates/bevy_mod_scripting_core/src/bindings/world.rs b/crates/bevy_mod_scripting_core/src/bindings/world.rs index 4a2377dc75..ffebe007b2 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/world.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/world.rs @@ -138,6 +138,16 @@ impl WorldCallbackAccess { world.add_default_component(entity, registration) } + pub fn insert_component( + &self, + entity: Entity, + registration: ScriptComponentRegistration, + value: ReflectReference, + ) -> Result<(), InteropError> { + let world = self.try_read()?; + world.insert_component(entity, registration, value) + } + pub fn get_component( &self, entity: Entity, @@ -639,6 +649,39 @@ impl WorldAccessGuard<'_> { }) } + pub fn insert_component( + &self, + entity: Entity, + registration: ScriptComponentRegistration, + value: ReflectReference, + ) -> Result<(), InteropError> { + let component_data = registration + .type_registration() + .type_registration() + .data::() + .ok_or_else(|| { + InteropError::missing_type_data( + registration.registration.type_id(), + "ReflectComponent".to_owned(), + ) + })?; + + with_global_access!(self.0.accesses, "Could not insert element", { + let type_registry = self.type_registry(); + let type_registry = type_registry.read(); + let world_mut = unsafe { self.0.cell.world_mut() }; + let mut entity = world_mut + .get_entity_mut(entity) + .map_err(|_| InteropError::missing_entity(entity))?; + // TODO: is this fine? creating a new arc here? + // Safety: we have global access, we are only accessing the entity and component + let ref_ = unsafe { value.reflect_unsafe(Arc::new(self.clone()))? }; + component_data.apply_or_insert(&mut entity, ref_, &type_registry); + + Ok(()) + }) + } + pub fn get_component( &self, entity: Entity, diff --git a/crates/bevy_mod_scripting_functions/src/core.rs b/crates/bevy_mod_scripting_functions/src/core.rs index d988bbcecc..241657b07f 100644 --- a/crates/bevy_mod_scripting_functions/src/core.rs +++ b/crates/bevy_mod_scripting_functions/src/core.rs @@ -116,6 +116,13 @@ pub fn register_world_functions(reg: &mut World) -> Result<(), FunctionRegistrat w.add_default_component(*e, r.clone()) }, ) + .register( + "insert_component", + |w: WorldCallbackAccess, + e: Val, + r: Val, + v: ReflectReference| { w.insert_component(*e, r.into_inner(), v) }, + ) .register("spawn", |s: WorldCallbackAccess| Ok(Val(s.spawn()?))) .register( "insert_children", diff --git a/crates/languages/bevy_mod_scripting_lua/tests/data/insert_component/component_no_default_or_from_world_data_inserts.lua b/crates/languages/bevy_mod_scripting_lua/tests/data/insert_component/component_no_default_or_from_world_data_inserts.lua new file mode 100644 index 0000000000..c797fbd339 --- /dev/null +++ b/crates/languages/bevy_mod_scripting_lua/tests/data/insert_component/component_no_default_or_from_world_data_inserts.lua @@ -0,0 +1,8 @@ +local entity = world.spawn() +local type = world.get_type_by_name('TestComponent') +local entity_with_component = world._get_entity_with_test_component('TestComponent') +local existing_component = world.get_component(entity_with_component, type) + +assert(world.has_component(entity, type) == false, 'Expected entity to not have component before adding, test invalid') +world.insert_component(entity, type, existing_component) +assert(world.has_component(entity, type) == true, 'Expected entity to have component after adding') diff --git a/crates/languages/bevy_mod_scripting_lua/tests/data/insert_component/component_with_default_no_component_data_errors.lua b/crates/languages/bevy_mod_scripting_lua/tests/data/insert_component/component_with_default_no_component_data_errors.lua new file mode 100644 index 0000000000..b10f9154e0 --- /dev/null +++ b/crates/languages/bevy_mod_scripting_lua/tests/data/insert_component/component_with_default_no_component_data_errors.lua @@ -0,0 +1,8 @@ +local entity = world.spawn() +local _type = world.get_type_by_name('CompWithDefault') +local entity_with_component = world._get_entity_with_test_component('CompWithDefault') +local existing_component = world.get_component(entity_with_component, _type) + +assert_throws(function() + world.insert_component(entity, _type, existing_component) +end, "Missing type data ReflectComponent for type: .*CompWithDefault.*") diff --git a/docs/src/ScriptingReference/world.md b/docs/src/ScriptingReference/world.md index 5fc9ff52dd..ef421c60c8 100644 --- a/docs/src/ScriptingReference/world.md +++ b/docs/src/ScriptingReference/world.md @@ -145,6 +145,24 @@ Arguments: world.add_default_component(entity, MyType) ``` +### insert_component + +Inserts or applies the given value to the component of the entity. If the component does not exist it will be added. + +Arguments: + +| Argument | Type | Description | +| --- | --- | --- | +| `entity` | `Entity` | The entity to add the component to | +| `registration` | `ScriptTypeRegistration` | The type registration as returned by `get_type_by_name` of the component | +| `component` | `ReflectReference` | A reference to an existing component value to be inserted | + +```lua +local existingComponent = world.get_component(otherEntity, MyType) +world.insert_component(entity, MyType, existingComponent) +``` + + ### spawn Returns: