Skip to content

Commit 9b9dd57

Browse files
authored
feat: Add component upsert function (#218)
feat: add insert_component script function
1 parent 6c92a68 commit 9b9dd57

File tree

6 files changed

+84
-6
lines changed

6 files changed

+84
-6
lines changed

crates/bevy_mod_scripting_core/src/bindings/reference.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,6 @@ impl ReflectReference {
221221

222222
/// The way to access the value of the reference, that is the pointed-to value.
223223
/// This method is safe to use as it ensures no-one else has aliasing access to the value at the same time.
224-
///
225-
/// # Panics
226-
/// - if the value is aliased and the access is not allowed
227224
#[track_caller]
228225
pub fn with_reflect<O, F: FnOnce(&dyn PartialReflect) -> O>(
229226
&self,
@@ -241,9 +238,6 @@ impl ReflectReference {
241238

242239
/// The way to access the value of the reference, that is the pointed-to value.
243240
/// This method is safe to use as it ensures no-one else has aliasing access to the value at the same time.
244-
///
245-
/// # Panics
246-
/// - if the value is aliased and the access is not allowed
247241
#[track_caller]
248242
pub fn with_reflect_mut<O, F: FnOnce(&mut dyn PartialReflect) -> O>(
249243
&self,

crates/bevy_mod_scripting_core/src/bindings/world.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ impl WorldCallbackAccess {
138138
world.add_default_component(entity, registration)
139139
}
140140

141+
pub fn insert_component(
142+
&self,
143+
entity: Entity,
144+
registration: ScriptComponentRegistration,
145+
value: ReflectReference,
146+
) -> Result<(), InteropError> {
147+
let world = self.try_read()?;
148+
world.insert_component(entity, registration, value)
149+
}
150+
141151
pub fn get_component(
142152
&self,
143153
entity: Entity,
@@ -639,6 +649,39 @@ impl WorldAccessGuard<'_> {
639649
})
640650
}
641651

652+
pub fn insert_component(
653+
&self,
654+
entity: Entity,
655+
registration: ScriptComponentRegistration,
656+
value: ReflectReference,
657+
) -> Result<(), InteropError> {
658+
let component_data = registration
659+
.type_registration()
660+
.type_registration()
661+
.data::<ReflectComponent>()
662+
.ok_or_else(|| {
663+
InteropError::missing_type_data(
664+
registration.registration.type_id(),
665+
"ReflectComponent".to_owned(),
666+
)
667+
})?;
668+
669+
with_global_access!(self.0.accesses, "Could not insert element", {
670+
let type_registry = self.type_registry();
671+
let type_registry = type_registry.read();
672+
let world_mut = unsafe { self.0.cell.world_mut() };
673+
let mut entity = world_mut
674+
.get_entity_mut(entity)
675+
.map_err(|_| InteropError::missing_entity(entity))?;
676+
// TODO: is this fine? creating a new arc here?
677+
// Safety: we have global access, we are only accessing the entity and component
678+
let ref_ = unsafe { value.reflect_unsafe(Arc::new(self.clone()))? };
679+
component_data.apply_or_insert(&mut entity, ref_, &type_registry);
680+
681+
Ok(())
682+
})
683+
}
684+
642685
pub fn get_component(
643686
&self,
644687
entity: Entity,

crates/bevy_mod_scripting_functions/src/core.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ pub fn register_world_functions(reg: &mut World) -> Result<(), FunctionRegistrat
116116
w.add_default_component(*e, r.clone())
117117
},
118118
)
119+
.register(
120+
"insert_component",
121+
|w: WorldCallbackAccess,
122+
e: Val<Entity>,
123+
r: Val<ScriptComponentRegistration>,
124+
v: ReflectReference| { w.insert_component(*e, r.into_inner(), v) },
125+
)
119126
.register("spawn", |s: WorldCallbackAccess| Ok(Val(s.spawn()?)))
120127
.register(
121128
"insert_children",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
local entity = world.spawn()
2+
local type = world.get_type_by_name('TestComponent')
3+
local entity_with_component = world._get_entity_with_test_component('TestComponent')
4+
local existing_component = world.get_component(entity_with_component, type)
5+
6+
assert(world.has_component(entity, type) == false, 'Expected entity to not have component before adding, test invalid')
7+
world.insert_component(entity, type, existing_component)
8+
assert(world.has_component(entity, type) == true, 'Expected entity to have component after adding')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
local entity = world.spawn()
2+
local _type = world.get_type_by_name('CompWithDefault')
3+
local entity_with_component = world._get_entity_with_test_component('CompWithDefault')
4+
local existing_component = world.get_component(entity_with_component, _type)
5+
6+
assert_throws(function()
7+
world.insert_component(entity, _type, existing_component)
8+
end, "Missing type data ReflectComponent for type: .*CompWithDefault.*")

docs/src/ScriptingReference/world.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,24 @@ Arguments:
145145
world.add_default_component(entity, MyType)
146146
```
147147

148+
### insert_component
149+
150+
Inserts or applies the given value to the component of the entity. If the component does not exist it will be added.
151+
152+
Arguments:
153+
154+
| Argument | Type | Description |
155+
| --- | --- | --- |
156+
| `entity` | `Entity` | The entity to add the component to |
157+
| `registration` | `ScriptTypeRegistration` | The type registration as returned by `get_type_by_name` of the component |
158+
| `component` | `ReflectReference` | A reference to an existing component value to be inserted |
159+
160+
```lua
161+
local existingComponent = world.get_component(otherEntity, MyType)
162+
world.insert_component(entity, MyType, existingComponent)
163+
```
164+
165+
148166
### spawn
149167

150168
Returns:

0 commit comments

Comments
 (0)