bevy/crates/bevy_ecs/src/world/error.rs
SpecificProtagonist 21195a75e6
track_change_detection: Also track spawns/despawns (#16047)
# Objective

Expand `track_change_detection` feature to also track entity spawns and
despawns. Use this to create better error messages.

# Solution

Adds `Entities::entity_get_spawned_or_despawned_by` as well as `{all
entity reference types}::spawned_by`.

This also removes the deprecated `get_many_entities_mut` & co (and
therefore can't land in 0.15) because we don't yet have no Polonius.

## Testing

Added a test that checks that the locations get updated and these
updates are ordered correctly vs hooks & observers.

---

## Showcase

Access location:
```rust
let mut world = World::new();
let entity = world.spawn_empty().id();
println!("spawned by: {}", world.entity(entity).spawned_by());
```
```
spawned by: src/main.rs:5:24
```
Error message (with `track_change_detection`):
```rust
world.despawn(entity);
world.entity(entity);
```
```
thread 'main' panicked at src/main.rs:11:11:
Entity 0v1#4294967296 was despawned by src/main.rs:10:11
```
and without:
```
thread 'main' panicked at src/main.rs:11:11:
Entity 0v1#4294967296 does not exist (enable `track_change_detection` feature for more details)
```
Similar error messages now also exists for `Query::get`,
`World::entity_mut`, `EntityCommands` creation and everything that
causes `B0003`, e.g.
```
error[B0003]: Could not insert a bundle (of type `MaterialMeshBundle<StandardMaterial>`) for entity Entity { index: 7, generation: 1 }, which was despawned by src/main.rs:10:11. See: https://bevyengine.org/learn/errors/#b0003
```

---------

Co-authored-by: kurk070ff <108901106+kurk070ff@users.noreply.github.com>
Co-authored-by: Freya Pines <freya@MacBookAir.lan>
Co-authored-by: Freya Pines <freya@Freyas-MacBook-Air.local>
Co-authored-by: Matty Weatherley <weatherleymatthew@gmail.com>
2024-12-17 04:46:31 +00:00

85 lines
3.0 KiB
Rust

//! Contains error types returned by bevy's schedule.
use thiserror::Error;
use crate::{component::ComponentId, entity::Entity, schedule::InternedScheduleLabel};
use super::unsafe_world_cell::UnsafeWorldCell;
/// The error type returned by [`World::try_run_schedule`] if the provided schedule does not exist.
///
/// [`World::try_run_schedule`]: crate::world::World::try_run_schedule
#[derive(Error, Debug)]
#[error("The schedule with the label {0:?} was not found.")]
pub struct TryRunScheduleError(pub InternedScheduleLabel);
/// An error that occurs when dynamically retrieving components from an entity.
#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntityComponentError {
/// The component with the given [`ComponentId`] does not exist on the entity.
#[error("The component with ID {0:?} does not exist on the entity.")]
MissingComponent(ComponentId),
/// The component with the given [`ComponentId`] was requested mutably more than once.
#[error("The component with ID {0:?} was requested mutably more than once.")]
AliasedMutability(ComponentId),
}
/// An error that occurs when fetching entities mutably from a world.
#[derive(Clone, Copy)]
pub enum EntityFetchError<'w> {
/// The entity with the given ID does not exist.
NoSuchEntity(Entity, UnsafeWorldCell<'w>),
/// The entity with the given ID was requested mutably more than once.
AliasedMutability(Entity),
}
impl<'w> core::error::Error for EntityFetchError<'w> {}
impl<'w> core::fmt::Display for EntityFetchError<'w> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match *self {
Self::NoSuchEntity(entity, world) => {
write!(
f,
"Entity {entity} {}",
world
.entities()
.entity_does_not_exist_error_details_message(entity)
)
}
Self::AliasedMutability(entity) => {
write!(f, "Entity {entity} was requested mutably more than once")
}
}
}
}
impl<'w> core::fmt::Debug for EntityFetchError<'w> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match *self {
Self::NoSuchEntity(entity, world) => {
write!(
f,
"NoSuchEntity({entity} {})",
world
.entities()
.entity_does_not_exist_error_details_message(entity)
)
}
Self::AliasedMutability(entity) => write!(f, "AliasedMutability({entity})"),
}
}
}
impl<'w> PartialEq for EntityFetchError<'w> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::NoSuchEntity(e1, _), Self::NoSuchEntity(e2, _)) if e1 == e2 => true,
(Self::AliasedMutability(e1), Self::AliasedMutability(e2)) if e1 == e2 => true,
_ => false,
}
}
}
impl<'w> Eq for EntityFetchError<'w> {}