Skip to content

Commit

Permalink
Improve error messages when archetypes fail
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sixfold-origami committed Jul 2, 2022
1 parent 720b44f commit 0f17607
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 11 deletions.
17 changes: 11 additions & 6 deletions crates/bevy_ecs/src/archetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use crate::{
bundle::BundleId,
component::{ComponentId, StorageType},
component::{ComponentId, Components, StorageType},
entity::{Entity, EntityLocation},
storage::{Column, SparseArray, SparseSet, SparseSetIndex, TableId},
world::ArchetypeInvariants,
Expand Down Expand Up @@ -154,13 +154,14 @@ impl Archetype {
table_archetype_components: Vec<ArchetypeComponentId>,
sparse_set_archetype_components: Vec<ArchetypeComponentId>,
archetype_invariants: &ArchetypeInvariants,
components: &Components,
) -> Self {
let mut components =
let mut component_set =
SparseSet::with_capacity(table_components.len() + sparse_set_components.len());
for (component_id, archetype_component_id) in
table_components.iter().zip(table_archetype_components)
{
components.insert(
component_set.insert(
*component_id,
ArchetypeComponentInfo {
storage_type: StorageType::Table,
Expand All @@ -173,7 +174,7 @@ impl Archetype {
.iter()
.zip(sparse_set_archetype_components)
{
components.insert(
component_set.insert(
*component_id,
ArchetypeComponentInfo {
storage_type: StorageType::SparseSet,
Expand All @@ -182,15 +183,15 @@ impl Archetype {
);
}

archetype_invariants.test_archetype(components.indices());
archetype_invariants.test_archetype(component_set.indices(), components);

Self {
id,
table_info: TableInfo {
id: table_id,
entity_rows: Default::default(),
},
components,
components: component_set,
table_components,
sparse_set_components,
unique_components: SparseSet::new(),
Expand Down Expand Up @@ -401,6 +402,7 @@ impl Default for Archetypes {
Vec::new(),
Vec::new(),
&ArchetypeInvariants::default(),
&Components::default(),
);

// adds the resource archetype. it is "special" in that it is inaccessible via a "hash",
Expand All @@ -413,6 +415,7 @@ impl Default for Archetypes {
Vec::new(),
Vec::new(),
&ArchetypeInvariants::default(),
&Components::default(),
));
archetypes
}
Expand Down Expand Up @@ -500,6 +503,7 @@ impl Archetypes {
table_components: Vec<ComponentId>,
sparse_set_components: Vec<ComponentId>,
archetype_invariants: &ArchetypeInvariants,
components: &Components,
) -> ArchetypeId {
let table_components = table_components.into_boxed_slice();
let sparse_set_components = sparse_set_components.into_boxed_slice();
Expand Down Expand Up @@ -534,6 +538,7 @@ impl Archetypes {
table_archetype_components,
sparse_set_archetype_components,
archetype_invariants,
components,
));
id
})
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_ecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ impl BundleInfo {
table_components,
sparse_set_components,
archetype_invariants,
components,
);
// add an edge from the old archetype to the new archetype
archetypes[archetype_id].edges_mut().insert_add_bundle(
Expand Down
66 changes: 61 additions & 5 deletions crates/bevy_ecs/src/world/archetype_invariants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ use std::marker::PhantomData;

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

use crate::{component::ComponentId, prelude::Bundle, world::World};
use crate::{
component::{ComponentId, Components},
prelude::Bundle,
world::World,
};

/// A rule about which [`Component`](crate::component::Component)s can coexist on entities.
///
Expand Down Expand Up @@ -236,6 +240,15 @@ impl UntypedArchetypeInvariant {
);
}
}

/// Returns formatted string describing this archetype invariant
pub fn display(&self, components: &Components) -> String {
format!(
"{{Premise: {}, Consequence: {}}}",
self.premise.display(components),
self.consequence.display(components)
)
}
}

/// A type-erased version of [`ArchetypeStatement`].
Expand Down Expand Up @@ -270,6 +283,31 @@ impl UntypedArchetypeStatement {
}
}

/// Returns formatted string describing this archetype invariant
///
/// For Rust types, the names should match the type name.
/// If any [`ComponentId`]s in the invariant have not been registered in the world,
/// then the raw component id will be returned instead.
pub fn display(&self, components: &Components) -> String {
let component_names: String = self
.component_ids()
.iter()
.map(|id| match components.get_info(*id) {
Some(info) => info.name().to_owned(),
None => format!("{:?}", id),
})
.reduce(|acc, s| format!("{}, {}", acc, s))
.unwrap_or("".to_owned());

match self {
UntypedArchetypeStatement::AllOf(_) => format!("AllOf({component_names})"),
UntypedArchetypeStatement::AnyOf(_) => format!("AnyOf({component_names})"),
UntypedArchetypeStatement::AtMostOneOf(_) => format!("AtMostOneOf({component_names})"),
UntypedArchetypeStatement::NoneOf(_) => format!("NoneOf({component_names})"),
UntypedArchetypeStatement::Only(_) => format!("Only({component_names})"),
}
}

/// Test if this statement is true for the provided set of [`ComponentId`]s.
pub fn test(&self, component_ids: &HashSet<ComponentId>) -> bool {
match self {
Expand Down Expand Up @@ -342,7 +380,11 @@ impl ArchetypeInvariants {
/// # Panics
///
/// Panics if any archetype invariant is violated.
pub fn test_archetype(&self, component_ids_of_archetype: impl Iterator<Item = ComponentId>) {
pub fn test_archetype(
&self,
component_ids_of_archetype: impl Iterator<Item = ComponentId>,
components: &Components,
) {
let component_ids_of_archetype: HashSet<ComponentId> = component_ids_of_archetype.collect();

for invariant in &self.raw_list {
Expand All @@ -359,10 +401,24 @@ impl ArchetypeInvariants {
}
}

let archetype_component_names: Vec<String> = component_ids_of_archetype
.into_iter()
.map(|id| match components.get_info(id) {
Some(info) => info.name().to_owned(),
None => format!("{:?}", id),
})
.collect();

let failed_invariant_names: String = failed_invariants
.into_iter()
.map(|invariant| invariant.display(components))
.reduce(|acc, s| format!("{}\n{}", acc, s))
.unwrap();

panic!(
"Archetype invariant violated! The following invariants were violated for archetype {:?}:\n{:?}",
component_ids_of_archetype,
failed_invariants,
"Archetype invariant violated! The following invariants were violated for archetype {:?}:\n{}",
archetype_component_names,
failed_invariant_names,
)
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_ecs/src/world/entity_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,7 @@ unsafe fn remove_bundle_from_archetype(
next_table_components,
next_sparse_set_components,
archetype_invariants,
components,
);
Some(new_archetype_id)
};
Expand Down

0 comments on commit 0f17607

Please sign in to comment.