···11use crate::EffectMode;
22-use bevy_ecs::component::ComponentId;
33-use bevy_ecs::prelude::{Bundle, Entity, EntityRef, Name, Resource, World};
22+use bevy_ecs::component::{ComponentCloneBehavior, ComponentId};
33+use bevy_ecs::prelude::{
44+ AppTypeRegistry, Bundle, Entity, EntityRef, Name, ReflectComponent, Resource, World,
55+};
46use bevy_ecs::ptr::OwningPtr;
57use bevy_ecs::relationship::RelationshipHookMode;
68use bevy_utils::prelude::DebugName;
···3234 ///
3335 /// Should be [cleared](Self::clear) when finished.
3436 pub fn stash_bundle<B: Bundle>(&mut self, bundle: B) -> &mut Self {
3737+ self.clear();
3838+3539 self.world
3640 .entity_mut(self.scratch_entity)
3741 .insert_with_relationship_hook_mode(bundle, RelationshipHookMode::Skip);
···97101 type_id: TypeId,
98102 src_component_id: ComponentId,
99103 ) -> Result<&Self, MultiWorldCopyError> {
100100- let Some(existing_component_id) = dst_world.components().get_id(type_id) else {
104104+ let Some(dst_component_id) = dst_world.components().get_id(type_id) else {
101105 return Err(MultiWorldCopyError::Unregistered(type_id));
102106 };
103107104104- let component_info = dst_world
105105- .components()
106106- .get_info(existing_component_id)
107107- .unwrap(); // Already checked that component is registered.
108108+ let component_info = dst_world.components().get_info(dst_component_id).unwrap(); // Already checked that component is registered.
108109109109- if component_info.drop().is_some() {
110110- return Err(MultiWorldCopyError::UnCopyable(component_info.name()));
110110+ match component_info.clone_behavior() {
111111+ ComponentCloneBehavior::Default | ComponentCloneBehavior::Custom(_) => {}
112112+ ComponentCloneBehavior::Ignore => {
113113+ return Err(MultiWorldCopyError::Uncloneable(component_info.name()));
114114+ }
111115 }
112116113117 let Some(src) = self.world.get_by_id(self.scratch_entity, src_component_id) else {
114118 return Err(MultiWorldCopyError::MissingSrcComponent(
115119 component_info.name(),
120120+ self.scratch_entity,
116121 ));
117122 };
118123119119- unsafe {
120120- // SAFETY: Contract is required to be upheld by the world.
121121- let dst = alloc(component_info.layout());
124124+ if component_info.drop().is_none() {
125125+ unsafe {
126126+ // SAFETY: Contract is required to be upheld by the world.
127127+ let dst = alloc(component_info.layout());
122128123123- // SAFETY: `dst` is allocated from the component's layout.
124124- // Both IDs provided by the caller must match, and `src` and `dst` obtained using the IDs.
125125- // `src` and `dst` are from different worlds, so cannot overlap.
126126- copy_nonoverlapping(src.as_ptr(), dst, component_info.layout().size());
129129+ // SAFETY: `dst` is allocated from the component's layout.
130130+ // Both IDs provided by the caller must match, and `src` and `dst` obtained using the IDs.
131131+ // `src` and `dst` are from different worlds, so cannot overlap.
132132+ copy_nonoverlapping(src.as_ptr(), dst, component_info.layout().size());
127133128128- let owning = OwningPtr::new(NonNull::new(dst).unwrap());
134134+ let owning = OwningPtr::new(NonNull::new(dst).unwrap());
129135130130- // SAFETY: `existing_component_id` is extracted from `dst_world`.
131131- // Both IDs provided by the caller must match, `owning` was obtained using `src_component_id`.
132132- dst_world
133133- .get_entity_mut(dst_entity)
134134- .map_err(|_| MultiWorldCopyError::MissingDstEntity(dst_entity))?
135135- .insert_by_id(existing_component_id, owning);
136136+ // SAFETY: `existing_component_id` is extracted from `dst_world`.
137137+ // Both IDs provided by the caller must match, `owning` was obtained using `src_component_id`.
138138+ dst_world
139139+ .get_entity_mut(dst_entity)
140140+ .map_err(|_| MultiWorldCopyError::MissingDstEntity(dst_entity))?
141141+ .insert_by_id(dst_component_id, owning);
142142+ }
143143+ } else {
144144+ let registry = dst_world.resource::<AppTypeRegistry>().clone();
145145+ let registry = registry.read();
146146+147147+ let reflect_component = registry
148148+ .get_type_data::<ReflectComponent>(type_id)
149149+ .ok_or(MultiWorldCopyError::Uncloneable(component_info.name()))?;
150150+151151+ reflect_component.copy(
152152+ &self.world,
153153+ dst_world,
154154+ self.scratch_entity,
155155+ dst_entity,
156156+ ®istry,
157157+ );
136158 }
137159138160 Ok(self)
···142164#[derive(Debug, Eq, PartialEq, Clone)]
143165pub enum MultiWorldCopyError {
144166 Unregistered(TypeId),
145145- UnCopyable(DebugName),
167167+ Uncloneable(DebugName),
146168 MissingDstEntity(Entity),
147147- MissingSrcComponent(DebugName),
169169+ MissingSrcComponent(DebugName, Entity),
148170}
149171150172impl std::fmt::Display for MultiWorldCopyError {
···152174 match self {
153175 MultiWorldCopyError::Unregistered(type_id) => write!(
154176 f,
155155- "Component with type ID {type_id:?} has not been registered in the inspector world, and therefor cannot be inserted using merge mode."
177177+ "Component with {type_id:?} has not been registered in the destination world, and therefor cannot be cloned."
156178 ),
157157- MultiWorldCopyError::UnCopyable(name) => write!(
179179+ MultiWorldCopyError::Uncloneable(name) => write!(
158180 f,
159159- "Component {name} cannot be copied, and therefor cannot be inserted using merge mode.",
181181+ "Component {name} cannot be cloned, and therefor cannot be inserted using merge mode.",
160182 ),
161183 MultiWorldCopyError::MissingDstEntity(entity) => write!(
162184 f,
163185 "Entity {entity} does not exist in the destination world."
164186 ),
165165- MultiWorldCopyError::MissingSrcComponent(name) => write!(
187187+ MultiWorldCopyError::MissingSrcComponent(name, entity) => write!(
166188 f,
167167- "Component {name} does not exist in inspector world, and therefor cannot be inserted using merge mode.",
189189+ "Component {name} does not exist on the scratch entity {entity}, and therefor cannot be cloned.",
168190 ),
169191 }
170192 }
+10-8
src/command.rs
···3333 let type_id = inspector.get_type_id(*incoming_component_id).unwrap();
34343535 if let Some(merge) = registry.merges.get(&type_id) {
3636- merge(&mut world.entity_mut(existing_entity), &inspector.get_ref());
3737- continue;
3636+ let entity_mut = world.entity_mut(existing_entity);
3737+3838+ if entity_mut.contains_type_id(type_id) {
3939+ merge(entity_mut, inspector.get_ref());
4040+ continue;
4141+ }
3842 }
39434044 unsafe {
···4650 }
4751 }
4852 })
4949- .or_else(|| {
5050- warn_once!("No `EffectMergeRegistry` found. Did you forget to add the `AlchemyPlugin`?");
5151- None
5252- });
5353+ .or_else(|| {
5454+ warn_once!("No `EffectMergeRegistry` found. Did you forget to add the `AlchemyPlugin`?");
5555+ None
5656+ });
5357 })
5458 .or_else(|| {
5559 warn_once!("No `BundleInspector` found. Did you forget to add the `AlchemyPlugin`?");
···107111 }
108112 EffectMode::Merge => self.merge(world, old_entity),
109113 }
110110-111111- world.resource_mut::<BundleInspector>().clear();
112114 }
113115}
114116
+1-1
src/component/stack.rs
···8282}
83838484/// A [merge function](crate::EffectMergeFn) for the [`EffectStacks`] component.
8585-pub fn merge_effect_stacks(existing: &mut EntityWorldMut, incoming: &EntityRef) {
8585+pub fn merge_effect_stacks(mut existing: EntityWorldMut, incoming: EntityRef) {
8686 let incoming = incoming.get::<EffectStacks>().unwrap();
8787 *existing.get_mut::<EffectStacks>().unwrap() += incoming.0;
8888}