bevy/release-content/migration-guides/relationship_set_risky.md
urben1680 c6ae964709
EntityWorldMut methods do not automatically overwrite Relationship components (#19601)
# Objective

Some methods and commands carelessly overwrite `Relationship`
components. This may overwrite additional data stored at them which is
undesired.

Part of #19589

## Solution

A new private method will be used instead of insert:
`modify_or_insert_relation_with_relationship_hook_mode`.

This method behaves different to `insert` if `Relationship` is a larger
type than `Entity` and already contains this component. It will then use
the `modify_component` API and a new `Relationship::set_risky` method to
set the related entity, keeping all other data untouched.

For the `replace_related`(`_with_difference`) methods this also required
a `InsertHookMode` parameter for efficient modifications of multiple
children. The changes here are limited to the non-public methods.

I would appreciate feedback if this is all good.

# Testing

Added tests of all methods that previously could reset `Relationship`
data.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-22 00:22:05 +00:00

1.7 KiB

title pull_requests
Relationship method set_risky
19601

The trait Relationship received a new method, set_risky. It is used to alter the entity ID of the entity that contains its RelationshipTarget counterpart. This is needed to leave other data you can store in these components unchanged at operations that reassign the relationship target, for example EntityCommands::add_related. Previously this could have caused the data to be reset to its default value which may not be what you wanted to happen.

Manually overwriting the component is still possible everywhere the full component is inserted:

#[derive(Component)]
#[relationship(relationship_target = CarOwner)]
struct OwnedCar {
    #[relationship]
    owner: Entity,
    first_owner: Option<Entity>, // None if `owner` is the first one
}

#[derive(Component)]
#[relationship_target(relationship = OwnedCar)]
struct CarOwner(Vec<Entity>);

let mut me_entity_mut = world.entity_mut(me_entity);

// if `car_entity` already contains `OwnedCar`, then the first owner remains unchanged
me_entity_mut.add_one_related::<OwnedCar>(car_entity);

// if `car_entity` already contains `OwnedCar`, then the first owner is overwritten with None here
car_entity_mut.insert(OwnedCar {
    owner: me_entity,
    first_owner: None // I swear it is not stolen officer!
});

The new method should not be called by user code as that can invalidate the relationship it had or will have.

If you implement Relationship manually (which is strongly discouraged) then this method needs to overwrite the Entity used for the relationship.