Skip to content

feat: Add component upsert function #218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading