Change Commands::get_entity
to return Result
and remove panic from Commands::entity
(#18043)
## Objective Alternative to #18001. - Now that systems can handle the `?` operator, `get_entity` returning `Result` would be more useful than `Option`. - With `get_entity` being more flexible, combined with entity commands now checking the entity's existence automatically, the panic in `entity` isn't really necessary. ## Solution - Changed `Commands::get_entity` to return `Result<EntityCommands, EntityDoesNotExistError>`. - Removed panic from `Commands::entity`.
This commit is contained in:
parent
ccb7069e7f
commit
058497e0bb
@ -128,7 +128,7 @@ pub trait Relationship: Component + Sized {
|
|||||||
{
|
{
|
||||||
relationship_target.collection_mut_risky().remove(entity);
|
relationship_target.collection_mut_risky().remove(entity);
|
||||||
if relationship_target.len() == 0 {
|
if relationship_target.len() == 0 {
|
||||||
if let Some(mut entity) = world.commands().get_entity(target_entity) {
|
if let Ok(mut entity) = world.commands().get_entity(target_entity) {
|
||||||
// this "remove" operation must check emptiness because in the event that an identical
|
// this "remove" operation must check emptiness because in the event that an identical
|
||||||
// relationship is inserted on top, this despawn would result in the removal of that identical
|
// relationship is inserted on top, this despawn would result in the removal of that identical
|
||||||
// relationship ... not what we want!
|
// relationship ... not what we want!
|
||||||
|
@ -20,7 +20,7 @@ use crate::{
|
|||||||
bundle::{Bundle, InsertMode, NoBundleEffect},
|
bundle::{Bundle, InsertMode, NoBundleEffect},
|
||||||
change_detection::{MaybeLocation, Mut},
|
change_detection::{MaybeLocation, Mut},
|
||||||
component::{Component, ComponentId, Mutable},
|
component::{Component, ComponentId, Mutable},
|
||||||
entity::{Entities, Entity, EntityClonerBuilder},
|
entity::{Entities, Entity, EntityClonerBuilder, EntityDoesNotExistError},
|
||||||
event::Event,
|
event::Event,
|
||||||
observer::{Observer, TriggerTargets},
|
observer::{Observer, TriggerTargets},
|
||||||
resource::Resource,
|
resource::Resource,
|
||||||
@ -404,13 +404,9 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
|
|
||||||
/// Returns the [`EntityCommands`] for the requested [`Entity`].
|
/// Returns the [`EntityCommands`] for the requested [`Entity`].
|
||||||
///
|
///
|
||||||
/// This method does not guarantee that commands queued by the `EntityCommands`
|
/// This method does not guarantee that commands queued by the returned `EntityCommands`
|
||||||
/// will be successful, since the entity could be despawned before they are executed.
|
/// will be successful, since the entity could be despawned before they are executed.
|
||||||
///
|
///
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// This method panics if the requested entity does not exist.
|
|
||||||
///
|
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -442,33 +438,21 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn entity(&mut self, entity: Entity) -> EntityCommands {
|
pub fn entity(&mut self, entity: Entity) -> EntityCommands {
|
||||||
#[inline(never)]
|
EntityCommands {
|
||||||
#[cold]
|
entity,
|
||||||
#[track_caller]
|
commands: self.reborrow(),
|
||||||
fn panic_no_entity(entities: &Entities, entity: Entity) -> ! {
|
|
||||||
panic!(
|
|
||||||
"Attempting to create an EntityCommands for entity {entity}, which {}",
|
|
||||||
entities.entity_does_not_exist_error_details(entity)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.get_entity(entity).is_some() {
|
|
||||||
EntityCommands {
|
|
||||||
entity,
|
|
||||||
commands: self.reborrow(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic_no_entity(self.entities, entity)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the [`EntityCommands`] for the requested [`Entity`], if it exists.
|
/// Returns the [`EntityCommands`] for the requested [`Entity`], if it exists.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the entity does not exist.
|
/// This method does not guarantee that commands queued by the returned `EntityCommands`
|
||||||
///
|
|
||||||
/// This method does not guarantee that commands queued by the `EntityCommands`
|
|
||||||
/// will be successful, since the entity could be despawned before they are executed.
|
/// will be successful, since the entity could be despawned before they are executed.
|
||||||
///
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`EntityDoesNotExistError`] if the requested entity does not exist.
|
||||||
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -476,29 +460,41 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
///
|
///
|
||||||
/// #[derive(Component)]
|
/// #[derive(Component)]
|
||||||
/// struct Label(&'static str);
|
/// struct Label(&'static str);
|
||||||
/// fn example_system(mut commands: Commands) {
|
/// fn example_system(mut commands: Commands) -> Result {
|
||||||
/// // Create a new, empty entity
|
/// // Create a new, empty entity.
|
||||||
/// let entity = commands.spawn_empty().id();
|
/// let entity = commands.spawn_empty().id();
|
||||||
///
|
///
|
||||||
/// // Get the entity if it still exists, which it will in this case
|
/// // Get the entity if it still exists, which it will in this case.
|
||||||
/// if let Some(mut entity_commands) = commands.get_entity(entity) {
|
/// // If it didn't, the `?` operator would propagate the returned error
|
||||||
/// // adds a single component to the entity
|
/// // to the system, and the system would pass it to an error handler.
|
||||||
/// entity_commands.insert(Label("hello world"));
|
/// let mut entity_commands = commands.get_entity(entity)?;
|
||||||
/// }
|
///
|
||||||
|
/// // Add a single component to the entity.
|
||||||
|
/// entity_commands.insert(Label("hello world"));
|
||||||
|
///
|
||||||
|
/// // Return from the system with a success.
|
||||||
|
/// Ok(())
|
||||||
/// }
|
/// }
|
||||||
/// # bevy_ecs::system::assert_is_system(example_system);
|
/// # bevy_ecs::system::assert_is_system(example_system);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// # See also
|
/// # See also
|
||||||
///
|
///
|
||||||
/// - [`entity`](Self::entity) for the panicking version.
|
/// - [`entity`](Self::entity) for the infallible version.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn get_entity(&mut self, entity: Entity) -> Option<EntityCommands> {
|
pub fn get_entity(
|
||||||
self.entities.contains(entity).then_some(EntityCommands {
|
&mut self,
|
||||||
entity,
|
entity: Entity,
|
||||||
commands: self.reborrow(),
|
) -> Result<EntityCommands, EntityDoesNotExistError> {
|
||||||
})
|
if self.entities.contains(entity) {
|
||||||
|
Ok(EntityCommands {
|
||||||
|
entity,
|
||||||
|
commands: self.reborrow(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(EntityDoesNotExistError::new(entity, self.entities))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes a [`Command`] to the queue for creating entities with a particular [`Bundle`] type.
|
/// Pushes a [`Command`] to the queue for creating entities with a particular [`Bundle`] type.
|
||||||
|
@ -1460,7 +1460,7 @@ pub fn gamepad_connection_system(
|
|||||||
vendor_id,
|
vendor_id,
|
||||||
product_id,
|
product_id,
|
||||||
} => {
|
} => {
|
||||||
let Some(mut gamepad) = commands.get_entity(id) else {
|
let Ok(mut gamepad) = commands.get_entity(id) else {
|
||||||
warn!("Gamepad {} removed before handling connection event.", id);
|
warn!("Gamepad {} removed before handling connection event.", id);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@ -1475,7 +1475,7 @@ pub fn gamepad_connection_system(
|
|||||||
info!("Gamepad {} connected.", id);
|
info!("Gamepad {} connected.", id);
|
||||||
}
|
}
|
||||||
GamepadConnection::Disconnected => {
|
GamepadConnection::Disconnected => {
|
||||||
let Some(mut gamepad) = commands.get_entity(id) else {
|
let Ok(mut gamepad) = commands.get_entity(id) else {
|
||||||
warn!("Gamepad {} removed before handling disconnection event. You can ignore this if you manually removed it.", id);
|
warn!("Gamepad {} removed before handling disconnection event. You can ignore this if you manually removed it.", id);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
@ -525,7 +525,7 @@ pub(crate) fn add_light_view_entities(
|
|||||||
trigger: Trigger<OnAdd, (ExtractedDirectionalLight, ExtractedPointLight)>,
|
trigger: Trigger<OnAdd, (ExtractedDirectionalLight, ExtractedPointLight)>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
if let Some(mut v) = commands.get_entity(trigger.target()) {
|
if let Ok(mut v) = commands.get_entity(trigger.target()) {
|
||||||
v.insert(LightViewEntities::default());
|
v.insert(LightViewEntities::default());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -535,7 +535,7 @@ pub(crate) fn extracted_light_removed(
|
|||||||
trigger: Trigger<OnRemove, (ExtractedDirectionalLight, ExtractedPointLight)>,
|
trigger: Trigger<OnRemove, (ExtractedDirectionalLight, ExtractedPointLight)>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
if let Some(mut v) = commands.get_entity(trigger.target()) {
|
if let Ok(mut v) = commands.get_entity(trigger.target()) {
|
||||||
v.try_remove::<LightViewEntities>();
|
v.try_remove::<LightViewEntities>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -548,7 +548,7 @@ pub(crate) fn remove_light_view_entities(
|
|||||||
if let Ok(entities) = query.get(trigger.target()) {
|
if let Ok(entities) = query.get(trigger.target()) {
|
||||||
for v in entities.0.values() {
|
for v in entities.0.values() {
|
||||||
for e in v.iter().copied() {
|
for e in v.iter().copied() {
|
||||||
if let Some(mut v) = commands.get_entity(e) {
|
if let Ok(mut v) = commands.get_entity(e) {
|
||||||
v.despawn();
|
v.despawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ fn apply_wireframe_material(
|
|||||||
global_material: Res<GlobalWireframeMaterial>,
|
global_material: Res<GlobalWireframeMaterial>,
|
||||||
) {
|
) {
|
||||||
for e in removed_wireframes.read().chain(no_wireframes.iter()) {
|
for e in removed_wireframes.read().chain(no_wireframes.iter()) {
|
||||||
if let Some(mut commands) = commands.get_entity(e) {
|
if let Ok(mut commands) = commands.get_entity(e) {
|
||||||
commands.remove::<MeshMaterial3d<WireframeMaterial>>();
|
commands.remove::<MeshMaterial3d<WireframeMaterial>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,7 @@ pub fn update_interactions(
|
|||||||
for (hovered_entity, new_interaction) in new_interaction_state.drain() {
|
for (hovered_entity, new_interaction) in new_interaction_state.drain() {
|
||||||
if let Ok(mut interaction) = interact.get_mut(hovered_entity) {
|
if let Ok(mut interaction) = interact.get_mut(hovered_entity) {
|
||||||
*interaction = new_interaction;
|
*interaction = new_interaction;
|
||||||
} else if let Some(mut entity_commands) = commands.get_entity(hovered_entity) {
|
} else if let Ok(mut entity_commands) = commands.get_entity(hovered_entity) {
|
||||||
entity_commands.try_insert(new_interaction);
|
entity_commands.try_insert(new_interaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,7 @@ fn apply_wireframe_material(
|
|||||||
global_material: Res<GlobalWireframe2dMaterial>,
|
global_material: Res<GlobalWireframe2dMaterial>,
|
||||||
) {
|
) {
|
||||||
for e in removed_wireframes.read().chain(no_wireframes.iter()) {
|
for e in removed_wireframes.read().chain(no_wireframes.iter()) {
|
||||||
if let Some(mut commands) = commands.get_entity(e) {
|
if let Ok(mut commands) = commands.get_entity(e) {
|
||||||
commands.remove::<MeshMaterial2d<Wireframe2dMaterial>>();
|
commands.remove::<MeshMaterial2d<Wireframe2dMaterial>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ fn on_remove_mine(
|
|||||||
fn explode_mine(trigger: Trigger<Explode>, query: Query<&Mine>, mut commands: Commands) {
|
fn explode_mine(trigger: Trigger<Explode>, query: Query<&Mine>, mut commands: Commands) {
|
||||||
// If a triggered event is targeting a specific entity you can access it with `.target()`
|
// If a triggered event is targeting a specific entity you can access it with `.target()`
|
||||||
let id = trigger.target();
|
let id = trigger.target();
|
||||||
let Some(mut entity) = commands.get_entity(id) else {
|
let Ok(mut entity) = commands.get_entity(id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
info!("Boom! {} exploded.", id.index());
|
info!("Boom! {} exploded.", id.index());
|
||||||
|
Loading…
Reference in New Issue
Block a user