Skip to content

Commit 9169590

Browse files
committed
Merge branch 'archetype_event' into query_entities
2 parents f25aa9c + 7b530c9 commit 9169590

File tree

22 files changed

+852
-360
lines changed

22 files changed

+852
-360
lines changed

crates/bevy_asset/src/io/wasm.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ impl HttpWasmAssetReader {
8181
let reader = VecReader::new(bytes);
8282
Ok(reader)
8383
}
84-
404 => Err(AssetReaderError::NotFound(path)),
84+
// Some web servers, including itch.io's CDN, return 403 when a requested file isn't present.
85+
// TODO: remove handling of 403 as not found when it's easier to configure
86+
// see https://github.com/bevyengine/bevy/pull/19268#pullrequestreview-2882410105
87+
403 | 404 => Err(AssetReaderError::NotFound(path)),
8588
status => Err(AssetReaderError::HttpError(status)),
8689
}
8790
}

crates/bevy_ecs/src/archetype.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ use core::{
3636
use nonmax::NonMaxU32;
3737

3838
#[derive(Event)]
39-
#[allow(dead_code)]
40-
39+
#[expect(dead_code, reason = "Prepare for the upcoming Query as Entities")]
4140
pub(crate) struct ArchetypeCreated(pub ArchetypeId);
4241

4342
/// An opaque location within a [`Archetype`].

crates/bevy_ecs/src/bundle.rs

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ impl<'w> BundleInserter<'w> {
10351035
change_tick: Tick,
10361036
) -> Self {
10371037
// SAFETY: We will not make any accesses to the command queue, component or resource data of this world
1038-
let mut bundle_info = world.bundles.get_unchecked(bundle_id);
1038+
let bundle_info = world.bundles.get_unchecked(bundle_id);
10391039
let bundle_id = bundle_info.id();
10401040
let (new_archetype_id, is_new_created) = bundle_info.insert_bundle_into_archetype(
10411041
&mut world.archetypes,
@@ -1045,12 +1045,7 @@ impl<'w> BundleInserter<'w> {
10451045
archetype_id,
10461046
);
10471047

1048-
if is_new_created {
1049-
world.trigger(ArchetypeCreated(new_archetype_id));
1050-
bundle_info = world.bundles.get_unchecked(bundle_id);
1051-
}
1052-
1053-
if new_archetype_id == archetype_id {
1048+
let inserter = if new_archetype_id == archetype_id {
10541049
let archetype = &mut world.archetypes[archetype_id];
10551050
// SAFETY: The edge is assured to be initialized when we called insert_bundle_into_archetype
10561051
let archetype_after_insert = unsafe {
@@ -1110,7 +1105,15 @@ impl<'w> BundleInserter<'w> {
11101105
world: world.as_unsafe_world_cell(),
11111106
}
11121107
}
1108+
};
1109+
1110+
if is_new_created {
1111+
inserter
1112+
.world
1113+
.into_deferred()
1114+
.trigger(ArchetypeCreated(new_archetype_id));
11131115
}
1116+
inserter
11141117
}
11151118

11161119
/// # Safety
@@ -1200,16 +1203,16 @@ impl<'w> BundleInserter<'w> {
12001203
unsafe { entities.get(swapped_entity).debug_checked_unwrap() };
12011204
entities.set(
12021205
swapped_entity.index(),
1203-
EntityLocation {
1206+
Some(EntityLocation {
12041207
archetype_id: swapped_location.archetype_id,
12051208
archetype_row: location.archetype_row,
12061209
table_id: swapped_location.table_id,
12071210
table_row: swapped_location.table_row,
1208-
},
1211+
}),
12091212
);
12101213
}
12111214
let new_location = new_archetype.allocate(entity, result.table_row);
1212-
entities.set(entity.index(), new_location);
1215+
entities.set(entity.index(), Some(new_location));
12131216
let after_effect = bundle_info.write_components(
12141217
table,
12151218
sparse_sets,
@@ -1249,19 +1252,19 @@ impl<'w> BundleInserter<'w> {
12491252
unsafe { entities.get(swapped_entity).debug_checked_unwrap() };
12501253
entities.set(
12511254
swapped_entity.index(),
1252-
EntityLocation {
1255+
Some(EntityLocation {
12531256
archetype_id: swapped_location.archetype_id,
12541257
archetype_row: location.archetype_row,
12551258
table_id: swapped_location.table_id,
12561259
table_row: swapped_location.table_row,
1257-
},
1260+
}),
12581261
);
12591262
}
12601263
// PERF: store "non bundle" components in edge, then just move those to avoid
12611264
// redundant copies
12621265
let move_result = table.move_to_superset_unchecked(result.table_row, new_table);
12631266
let new_location = new_archetype.allocate(entity, move_result.new_row);
1264-
entities.set(entity.index(), new_location);
1267+
entities.set(entity.index(), Some(new_location));
12651268

12661269
// If an entity was moved into this entity's table spot, update its table row.
12671270
if let Some(swapped_entity) = move_result.swapped_entity {
@@ -1271,12 +1274,12 @@ impl<'w> BundleInserter<'w> {
12711274

12721275
entities.set(
12731276
swapped_entity.index(),
1274-
EntityLocation {
1277+
Some(EntityLocation {
12751278
archetype_id: swapped_location.archetype_id,
12761279
archetype_row: swapped_location.archetype_row,
12771280
table_id: swapped_location.table_id,
12781281
table_row: result.table_row,
1279-
},
1282+
}),
12801283
);
12811284

12821285
if archetype.id() == swapped_location.archetype_id {
@@ -1426,7 +1429,7 @@ impl<'w> BundleRemover<'w> {
14261429
bundle_id: BundleId,
14271430
require_all: bool,
14281431
) -> Option<Self> {
1429-
let mut bundle_info = world.bundles.get_unchecked(bundle_id);
1432+
let bundle_info = world.bundles.get_unchecked(bundle_id);
14301433
// SAFETY: Caller ensures archetype and bundle ids are correct.
14311434
let (new_archetype_id, is_new_created) = unsafe {
14321435
bundle_info.remove_bundle_from_archetype(
@@ -1444,11 +1447,6 @@ impl<'w> BundleRemover<'w> {
14441447
return None;
14451448
}
14461449

1447-
if is_new_created {
1448-
world.trigger(ArchetypeCreated(new_archetype_id));
1449-
bundle_info = world.bundles.get_unchecked(bundle_id);
1450-
}
1451-
14521450
let (old_archetype, new_archetype) =
14531451
world.archetypes.get_2_mut(archetype_id, new_archetype_id);
14541452

@@ -1462,13 +1460,20 @@ impl<'w> BundleRemover<'w> {
14621460
Some((old.into(), new.into()))
14631461
};
14641462

1465-
Some(Self {
1463+
let remover = Self {
14661464
bundle_info: bundle_info.into(),
14671465
new_archetype: new_archetype.into(),
14681466
old_archetype: old_archetype.into(),
14691467
old_and_new_table: tables,
14701468
world: world.as_unsafe_world_cell(),
1471-
})
1469+
};
1470+
if is_new_created {
1471+
remover
1472+
.world
1473+
.into_deferred()
1474+
.trigger(ArchetypeCreated(new_archetype_id));
1475+
}
1476+
Some(remover)
14721477
}
14731478

14741479
/// This can be passed to [`remove`](Self::remove) as the `pre_remove` function if you don't want to do anything before removing.
@@ -1588,12 +1593,12 @@ impl<'w> BundleRemover<'w> {
15881593

15891594
world.entities.set(
15901595
swapped_entity.index(),
1591-
EntityLocation {
1596+
Some(EntityLocation {
15921597
archetype_id: swapped_location.archetype_id,
15931598
archetype_row: location.archetype_row,
15941599
table_id: swapped_location.table_id,
15951600
table_row: swapped_location.table_row,
1596-
},
1601+
}),
15971602
);
15981603
}
15991604

@@ -1629,12 +1634,12 @@ impl<'w> BundleRemover<'w> {
16291634

16301635
world.entities.set(
16311636
swapped_entity.index(),
1632-
EntityLocation {
1637+
Some(EntityLocation {
16331638
archetype_id: swapped_location.archetype_id,
16341639
archetype_row: swapped_location.archetype_row,
16351640
table_id: swapped_location.table_id,
16361641
table_row: location.table_row,
1637-
},
1642+
}),
16381643
);
16391644
world.archetypes[swapped_location.archetype_id]
16401645
.set_entity_table_row(swapped_location.archetype_row, location.table_row);
@@ -1650,7 +1655,7 @@ impl<'w> BundleRemover<'w> {
16501655

16511656
// SAFETY: The entity is valid and has been moved to the new location already.
16521657
unsafe {
1653-
world.entities.set(entity.index(), new_location);
1658+
world.entities.set(entity.index(), Some(new_location));
16541659
}
16551660

16561661
(new_location, pre_remove_result)
@@ -1689,7 +1694,7 @@ impl<'w> BundleSpawner<'w> {
16891694
bundle_id: BundleId,
16901695
change_tick: Tick,
16911696
) -> Self {
1692-
let mut bundle_info = world.bundles.get_unchecked(bundle_id);
1697+
let bundle_info = world.bundles.get_unchecked(bundle_id);
16931698
let (new_archetype_id, is_new_created) = bundle_info.insert_bundle_into_archetype(
16941699
&mut world.archetypes,
16951700
&mut world.storages,
@@ -1698,20 +1703,22 @@ impl<'w> BundleSpawner<'w> {
16981703
ArchetypeId::EMPTY,
16991704
);
17001705

1701-
if is_new_created {
1702-
world.trigger(ArchetypeCreated(new_archetype_id));
1703-
bundle_info = world.bundles.get_unchecked(bundle_id);
1704-
}
1705-
17061706
let archetype = &mut world.archetypes[new_archetype_id];
17071707
let table = &mut world.storages.tables[archetype.table_id()];
1708-
Self {
1708+
let spawner = Self {
17091709
bundle_info: bundle_info.into(),
17101710
table: table.into(),
17111711
archetype: archetype.into(),
17121712
change_tick,
17131713
world: world.as_unsafe_world_cell(),
1714+
};
1715+
if is_new_created {
1716+
spawner
1717+
.world
1718+
.into_deferred()
1719+
.trigger(ArchetypeCreated(new_archetype_id));
17141720
}
1721+
spawner
17151722
}
17161723

17171724
#[inline]
@@ -1757,7 +1764,7 @@ impl<'w> BundleSpawner<'w> {
17571764
InsertMode::Replace,
17581765
caller,
17591766
);
1760-
entities.set(entity.index(), location);
1767+
entities.set(entity.index(), Some(location));
17611768
entities.mark_spawn_despawn(entity.index(), caller, self.change_tick);
17621769
(location, after_effect)
17631770
};

crates/bevy_ecs/src/entity/clone_entities.rs

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use bumpalo::Bump;
55
use core::any::TypeId;
66

77
use crate::{
8+
archetype::Archetype,
89
bundle::Bundle,
910
component::{Component, ComponentCloneBehavior, ComponentCloneFn, ComponentId, ComponentInfo},
1011
entity::{hash_map::EntityHashMap, Entities, Entity, EntityMapper},
@@ -340,6 +341,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> {
340341
pub struct EntityCloner {
341342
filter_allows_components: bool,
342343
filter: HashSet<ComponentId>,
344+
filter_required: HashSet<ComponentId>,
343345
clone_behavior_overrides: HashMap<ComponentId, ComponentCloneBehavior>,
344346
move_components: bool,
345347
linked_cloning: bool,
@@ -356,6 +358,7 @@ impl Default for EntityCloner {
356358
linked_cloning: false,
357359
default_clone_fn: ComponentCloneBehavior::global_default_fn(),
358360
filter: Default::default(),
361+
filter_required: Default::default(),
359362
clone_behavior_overrides: Default::default(),
360363
clone_queue: Default::default(),
361364
deferred_commands: Default::default(),
@@ -459,6 +462,12 @@ impl EntityCloner {
459462
{
460463
let world = world.as_unsafe_world_cell();
461464
let source_entity = world.get_entity(source).expect("Source entity must exist");
465+
let target_archetype = (!self.filter_required.is_empty()).then(|| {
466+
world
467+
.get_entity(target)
468+
.expect("Target entity must exist")
469+
.archetype()
470+
});
462471

463472
#[cfg(feature = "bevy_reflect")]
464473
// SAFETY: we have unique access to `world`, nothing else accesses the registry at this moment, and we clone
@@ -475,7 +484,7 @@ impl EntityCloner {
475484
bundle_scratch = BundleScratch::with_capacity(archetype.component_count());
476485

477486
for component in archetype.components() {
478-
if !self.is_cloning_allowed(&component) {
487+
if !self.is_cloning_allowed(&component, target_archetype) {
479488
continue;
480489
}
481490

@@ -599,9 +608,19 @@ impl EntityCloner {
599608
target
600609
}
601610

602-
fn is_cloning_allowed(&self, component: &ComponentId) -> bool {
603-
(self.filter_allows_components && self.filter.contains(component))
604-
|| (!self.filter_allows_components && !self.filter.contains(component))
611+
fn is_cloning_allowed(
612+
&self,
613+
component: &ComponentId,
614+
target_archetype: Option<&Archetype>,
615+
) -> bool {
616+
if self.filter_allows_components {
617+
self.filter.contains(component)
618+
|| target_archetype.is_some_and(|archetype| {
619+
!archetype.contains(*component) && self.filter_required.contains(component)
620+
})
621+
} else {
622+
!self.filter.contains(component) && !self.filter_required.contains(component)
623+
}
605624
}
606625
}
607626

@@ -803,9 +822,9 @@ impl<'w> EntityClonerBuilder<'w> {
803822
if let Some(info) = self.world.components().get_info(id) {
804823
for required_id in info.required_components().iter_ids() {
805824
if self.entity_cloner.filter_allows_components {
806-
self.entity_cloner.filter.insert(required_id);
825+
self.entity_cloner.filter_required.insert(required_id);
807826
} else {
808-
self.entity_cloner.filter.remove(&required_id);
827+
self.entity_cloner.filter_required.remove(&required_id);
809828
}
810829
}
811830
}
@@ -823,9 +842,9 @@ impl<'w> EntityClonerBuilder<'w> {
823842
if let Some(info) = self.world.components().get_info(id) {
824843
for required_id in info.required_components().iter_ids() {
825844
if self.entity_cloner.filter_allows_components {
826-
self.entity_cloner.filter.remove(&required_id);
845+
self.entity_cloner.filter_required.remove(&required_id);
827846
} else {
828-
self.entity_cloner.filter.insert(required_id);
847+
self.entity_cloner.filter_required.insert(required_id);
829848
}
830849
}
831850
}
@@ -1400,4 +1419,36 @@ mod tests {
14001419
);
14011420
assert!(world.resource::<FromWorldCalled>().0);
14021421
}
1422+
1423+
#[test]
1424+
fn cloning_with_required_components_preserves_existing() {
1425+
#[derive(Component, Clone, PartialEq, Debug, Default)]
1426+
#[require(B(5))]
1427+
struct A;
1428+
1429+
#[derive(Component, Clone, PartialEq, Debug)]
1430+
struct B(u32);
1431+
1432+
let mut world = World::default();
1433+
1434+
let e = world.spawn((A, B(0))).id();
1435+
let e_clone = world.spawn(B(1)).id();
1436+
1437+
EntityCloner::build(&mut world)
1438+
.deny_all()
1439+
.allow::<A>()
1440+
.clone_entity(e, e_clone);
1441+
1442+
assert_eq!(world.entity(e_clone).get::<A>(), Some(&A));
1443+
assert_eq!(world.entity(e_clone).get::<B>(), Some(&B(1)));
1444+
1445+
let e_clone2 = world.spawn(B(2)).id();
1446+
1447+
EntityCloner::build(&mut world)
1448+
.allow_all()
1449+
.deny::<A>()
1450+
.clone_entity(e, e_clone2);
1451+
1452+
assert_eq!(world.entity(e_clone2).get::<B>(), Some(&B(2)));
1453+
}
14031454
}

0 commit comments

Comments
 (0)