Skip to content

Commit

Permalink
feat: Add component upsert function (#218)
Browse files Browse the repository at this point in the history
feat: add insert_component script function
  • Loading branch information
makspll authored Jan 20, 2025
1 parent 6c92a68 commit 9b9dd57
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 6 deletions.
6 changes: 0 additions & 6 deletions crates/bevy_mod_scripting_core/src/bindings/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, F: FnOnce(&dyn PartialReflect) -> O>(
&self,
Expand All @@ -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, F: FnOnce(&mut dyn PartialReflect) -> O>(
&self,
Expand Down
43 changes: 43 additions & 0 deletions crates/bevy_mod_scripting_core/src/bindings/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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::<ReflectComponent>()
.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,
Expand Down
7 changes: 7 additions & 0 deletions crates/bevy_mod_scripting_functions/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Entity>,
r: Val<ScriptComponentRegistration>,
v: ReflectReference| { w.insert_component(*e, r.into_inner(), v) },
)
.register("spawn", |s: WorldCallbackAccess| Ok(Val(s.spawn()?)))
.register(
"insert_children",
Expand Down
Original file line number Diff line number Diff line change
@@ -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')
Original file line number Diff line number Diff line change
@@ -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.*")
18 changes: 18 additions & 0 deletions docs/src/ScriptingReference/world.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 9b9dd57

Please sign in to comment.