Skip to content

Commit 0f17607

Browse files
Improve error messages when archetypes fail
Now, the Rust type names are fetched and nicely formatted, where possible. It would be good to shorten the type names, but there is a separate PR for this.
1 parent 720b44f commit 0f17607

File tree

4 files changed

+74
-11
lines changed

4 files changed

+74
-11
lines changed

crates/bevy_ecs/src/archetype.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
use crate::{
55
bundle::BundleId,
6-
component::{ComponentId, StorageType},
6+
component::{ComponentId, Components, StorageType},
77
entity::{Entity, EntityLocation},
88
storage::{Column, SparseArray, SparseSet, SparseSetIndex, TableId},
99
world::ArchetypeInvariants,
@@ -154,13 +154,14 @@ impl Archetype {
154154
table_archetype_components: Vec<ArchetypeComponentId>,
155155
sparse_set_archetype_components: Vec<ArchetypeComponentId>,
156156
archetype_invariants: &ArchetypeInvariants,
157+
components: &Components,
157158
) -> Self {
158-
let mut components =
159+
let mut component_set =
159160
SparseSet::with_capacity(table_components.len() + sparse_set_components.len());
160161
for (component_id, archetype_component_id) in
161162
table_components.iter().zip(table_archetype_components)
162163
{
163-
components.insert(
164+
component_set.insert(
164165
*component_id,
165166
ArchetypeComponentInfo {
166167
storage_type: StorageType::Table,
@@ -173,7 +174,7 @@ impl Archetype {
173174
.iter()
174175
.zip(sparse_set_archetype_components)
175176
{
176-
components.insert(
177+
component_set.insert(
177178
*component_id,
178179
ArchetypeComponentInfo {
179180
storage_type: StorageType::SparseSet,
@@ -182,15 +183,15 @@ impl Archetype {
182183
);
183184
}
184185

185-
archetype_invariants.test_archetype(components.indices());
186+
archetype_invariants.test_archetype(component_set.indices(), components);
186187

187188
Self {
188189
id,
189190
table_info: TableInfo {
190191
id: table_id,
191192
entity_rows: Default::default(),
192193
},
193-
components,
194+
components: component_set,
194195
table_components,
195196
sparse_set_components,
196197
unique_components: SparseSet::new(),
@@ -401,6 +402,7 @@ impl Default for Archetypes {
401402
Vec::new(),
402403
Vec::new(),
403404
&ArchetypeInvariants::default(),
405+
&Components::default(),
404406
);
405407

406408
// adds the resource archetype. it is "special" in that it is inaccessible via a "hash",
@@ -413,6 +415,7 @@ impl Default for Archetypes {
413415
Vec::new(),
414416
Vec::new(),
415417
&ArchetypeInvariants::default(),
418+
&Components::default(),
416419
));
417420
archetypes
418421
}
@@ -500,6 +503,7 @@ impl Archetypes {
500503
table_components: Vec<ComponentId>,
501504
sparse_set_components: Vec<ComponentId>,
502505
archetype_invariants: &ArchetypeInvariants,
506+
components: &Components,
503507
) -> ArchetypeId {
504508
let table_components = table_components.into_boxed_slice();
505509
let sparse_set_components = sparse_set_components.into_boxed_slice();
@@ -534,6 +538,7 @@ impl Archetypes {
534538
table_archetype_components,
535539
sparse_set_archetype_components,
536540
archetype_invariants,
541+
components,
537542
));
538543
id
539544
})

crates/bevy_ecs/src/bundle.rs

+1
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ impl BundleInfo {
393393
table_components,
394394
sparse_set_components,
395395
archetype_invariants,
396+
components,
396397
);
397398
// add an edge from the old archetype to the new archetype
398399
archetypes[archetype_id].edges_mut().insert_add_bundle(

crates/bevy_ecs/src/world/archetype_invariants.rs

+61-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ use std::marker::PhantomData;
22

33
use bevy_utils::{tracing::warn, HashSet};
44

5-
use crate::{component::ComponentId, prelude::Bundle, world::World};
5+
use crate::{
6+
component::{ComponentId, Components},
7+
prelude::Bundle,
8+
world::World,
9+
};
610

711
/// A rule about which [`Component`](crate::component::Component)s can coexist on entities.
812
///
@@ -236,6 +240,15 @@ impl UntypedArchetypeInvariant {
236240
);
237241
}
238242
}
243+
244+
/// Returns formatted string describing this archetype invariant
245+
pub fn display(&self, components: &Components) -> String {
246+
format!(
247+
"{{Premise: {}, Consequence: {}}}",
248+
self.premise.display(components),
249+
self.consequence.display(components)
250+
)
251+
}
239252
}
240253

241254
/// A type-erased version of [`ArchetypeStatement`].
@@ -270,6 +283,31 @@ impl UntypedArchetypeStatement {
270283
}
271284
}
272285

286+
/// Returns formatted string describing this archetype invariant
287+
///
288+
/// For Rust types, the names should match the type name.
289+
/// If any [`ComponentId`]s in the invariant have not been registered in the world,
290+
/// then the raw component id will be returned instead.
291+
pub fn display(&self, components: &Components) -> String {
292+
let component_names: String = self
293+
.component_ids()
294+
.iter()
295+
.map(|id| match components.get_info(*id) {
296+
Some(info) => info.name().to_owned(),
297+
None => format!("{:?}", id),
298+
})
299+
.reduce(|acc, s| format!("{}, {}", acc, s))
300+
.unwrap_or("".to_owned());
301+
302+
match self {
303+
UntypedArchetypeStatement::AllOf(_) => format!("AllOf({component_names})"),
304+
UntypedArchetypeStatement::AnyOf(_) => format!("AnyOf({component_names})"),
305+
UntypedArchetypeStatement::AtMostOneOf(_) => format!("AtMostOneOf({component_names})"),
306+
UntypedArchetypeStatement::NoneOf(_) => format!("NoneOf({component_names})"),
307+
UntypedArchetypeStatement::Only(_) => format!("Only({component_names})"),
308+
}
309+
}
310+
273311
/// Test if this statement is true for the provided set of [`ComponentId`]s.
274312
pub fn test(&self, component_ids: &HashSet<ComponentId>) -> bool {
275313
match self {
@@ -342,7 +380,11 @@ impl ArchetypeInvariants {
342380
/// # Panics
343381
///
344382
/// Panics if any archetype invariant is violated.
345-
pub fn test_archetype(&self, component_ids_of_archetype: impl Iterator<Item = ComponentId>) {
383+
pub fn test_archetype(
384+
&self,
385+
component_ids_of_archetype: impl Iterator<Item = ComponentId>,
386+
components: &Components,
387+
) {
346388
let component_ids_of_archetype: HashSet<ComponentId> = component_ids_of_archetype.collect();
347389

348390
for invariant in &self.raw_list {
@@ -359,10 +401,24 @@ impl ArchetypeInvariants {
359401
}
360402
}
361403

404+
let archetype_component_names: Vec<String> = component_ids_of_archetype
405+
.into_iter()
406+
.map(|id| match components.get_info(id) {
407+
Some(info) => info.name().to_owned(),
408+
None => format!("{:?}", id),
409+
})
410+
.collect();
411+
412+
let failed_invariant_names: String = failed_invariants
413+
.into_iter()
414+
.map(|invariant| invariant.display(components))
415+
.reduce(|acc, s| format!("{}\n{}", acc, s))
416+
.unwrap();
417+
362418
panic!(
363-
"Archetype invariant violated! The following invariants were violated for archetype {:?}:\n{:?}",
364-
component_ids_of_archetype,
365-
failed_invariants,
419+
"Archetype invariant violated! The following invariants were violated for archetype {:?}:\n{}",
420+
archetype_component_names,
421+
failed_invariant_names,
366422
)
367423
}
368424
}

crates/bevy_ecs/src/world/entity_ref.rs

+1
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,7 @@ unsafe fn remove_bundle_from_archetype(
820820
next_table_components,
821821
next_sparse_set_components,
822822
archetype_invariants,
823+
components,
823824
);
824825
Some(new_archetype_id)
825826
};

0 commit comments

Comments
 (0)