|
1 | 1 | mod parallel_scope;
|
2 | 2 |
|
3 | 3 | use core::panic::Location;
|
| 4 | +use std::marker::PhantomData; |
4 | 5 |
|
5 | 6 | use super::{Deferred, IntoObserverSystem, IntoSystem, RegisterSystem, Resource};
|
6 | 7 | use crate::{
|
7 | 8 | self as bevy_ecs,
|
8 | 9 | bundle::{Bundle, InsertMode},
|
9 |
| - component::{ComponentId, ComponentInfo}, |
| 10 | + change_detection::Mut, |
| 11 | + component::{Component, ComponentId, ComponentInfo}, |
10 | 12 | entity::{Entities, Entity},
|
11 | 13 | event::{Event, SendEvent},
|
12 | 14 | observer::{Observer, TriggerEvent, TriggerTargets},
|
@@ -906,6 +908,38 @@ impl EntityCommands<'_> {
|
906 | 908 | }
|
907 | 909 | }
|
908 | 910 |
|
| 911 | + /// Get an [`EntityEntryCommands`] for the [`Component`] `T`, |
| 912 | + /// allowing you to modify it or insert it if it isn't already present. |
| 913 | + /// |
| 914 | + /// See also [`insert_if_new`](Self::insert_if_new), which lets you insert a [`Bundle`] without overwriting it. |
| 915 | + /// |
| 916 | + /// # Example |
| 917 | + /// |
| 918 | + /// ``` |
| 919 | + /// # use bevy_ecs::prelude::*; |
| 920 | + /// # #[derive(Resource)] |
| 921 | + /// # struct PlayerEntity { entity: Entity } |
| 922 | + /// #[derive(Component)] |
| 923 | + /// struct Level(u32); |
| 924 | + /// |
| 925 | + /// fn level_up_system(mut commands: Commands, player: Res<PlayerEntity>) { |
| 926 | + /// commands |
| 927 | + /// .entity(player.entity) |
| 928 | + /// .entry::<Level>() |
| 929 | + /// // Modify the component if it exists |
| 930 | + /// .and_modify(|mut lvl| lvl.0 += 1) |
| 931 | + /// // Otherwise insert a default value |
| 932 | + /// .or_insert(Level(0)); |
| 933 | + /// } |
| 934 | + /// # bevy_ecs::system::assert_is_system(level_up_system); |
| 935 | + /// ``` |
| 936 | + pub fn entry<T: Component>(&mut self) -> EntityEntryCommands<T> { |
| 937 | + EntityEntryCommands { |
| 938 | + entity_commands: self.reborrow(), |
| 939 | + marker: PhantomData, |
| 940 | + } |
| 941 | + } |
| 942 | + |
909 | 943 | /// Adds a [`Bundle`] of components to the entity.
|
910 | 944 | ///
|
911 | 945 | /// This will overwrite any previous value(s) of the same component type.
|
@@ -1010,6 +1044,9 @@ impl EntityCommands<'_> {
|
1010 | 1044 | /// components will leave the old values instead of replacing them with new
|
1011 | 1045 | /// ones.
|
1012 | 1046 | ///
|
| 1047 | + /// See also [`entry`](Self::entry), which lets you modify a [`Component`] if it's present, |
| 1048 | + /// as well as initialize it with a default value. |
| 1049 | + /// |
1013 | 1050 | /// # Panics
|
1014 | 1051 | ///
|
1015 | 1052 | /// The command will panic when applied if the associated entity does not exist.
|
@@ -1417,6 +1454,113 @@ impl EntityCommands<'_> {
|
1417 | 1454 | }
|
1418 | 1455 | }
|
1419 | 1456 |
|
| 1457 | +/// A wrapper around [`EntityCommands`] with convenience methods for working with a specified component type. |
| 1458 | +pub struct EntityEntryCommands<'a, T> { |
| 1459 | + entity_commands: EntityCommands<'a>, |
| 1460 | + marker: PhantomData<T>, |
| 1461 | +} |
| 1462 | + |
| 1463 | +impl<'a, T: Component> EntityEntryCommands<'a, T> { |
| 1464 | + /// Modify the component `T` if it exists, using the the function `modify`. |
| 1465 | + pub fn and_modify(mut self, modify: impl FnOnce(Mut<T>) + Send + Sync + 'static) -> Self { |
| 1466 | + self.entity_commands = self |
| 1467 | + .entity_commands |
| 1468 | + .queue(move |mut entity: EntityWorldMut| { |
| 1469 | + if let Some(value) = entity.get_mut() { |
| 1470 | + modify(value); |
| 1471 | + } |
| 1472 | + }); |
| 1473 | + self |
| 1474 | + } |
| 1475 | + |
| 1476 | + /// [Insert](EntityCommands::insert) `default` into this entity, if `T` is not already present. |
| 1477 | + /// |
| 1478 | + /// See also [`or_insert_with`](Self::or_insert_with). |
| 1479 | + /// |
| 1480 | + /// # Panics |
| 1481 | + /// |
| 1482 | + /// Panics if the entity does not exist. |
| 1483 | + /// See [`or_try_insert`](Self::or_try_insert) for a non-panicking version. |
| 1484 | + #[track_caller] |
| 1485 | + pub fn or_insert(mut self, default: T) -> Self { |
| 1486 | + self.entity_commands = self |
| 1487 | + .entity_commands |
| 1488 | + .queue(insert(default, InsertMode::Keep)); |
| 1489 | + self |
| 1490 | + } |
| 1491 | + |
| 1492 | + /// [Insert](EntityCommands::insert) `default` into this entity, if `T` is not already present. |
| 1493 | + /// |
| 1494 | + /// Unlike [`or_insert`](Self::or_insert), this will not panic if the entity does not exist. |
| 1495 | + /// |
| 1496 | + /// See also [`or_insert_with`](Self::or_insert_with). |
| 1497 | + #[track_caller] |
| 1498 | + pub fn or_try_insert(mut self, default: T) -> Self { |
| 1499 | + self.entity_commands = self |
| 1500 | + .entity_commands |
| 1501 | + .queue(try_insert(default, InsertMode::Keep)); |
| 1502 | + self |
| 1503 | + } |
| 1504 | + |
| 1505 | + /// [Insert](EntityCommands::insert) the value returned from `default` into this entity, if `T` is not already present. |
| 1506 | + /// |
| 1507 | + /// See also [`or_insert`](Self::or_insert) and [`or_try_insert`](Self::or_try_insert). |
| 1508 | + /// |
| 1509 | + /// # Panics |
| 1510 | + /// |
| 1511 | + /// Panics if the entity does not exist. |
| 1512 | + /// See [`or_try_insert_with`](Self::or_try_insert_with) for a non-panicking version. |
| 1513 | + #[track_caller] |
| 1514 | + pub fn or_insert_with(self, default: impl Fn() -> T) -> Self { |
| 1515 | + self.or_insert(default()) |
| 1516 | + } |
| 1517 | + |
| 1518 | + /// [Insert](EntityCommands::insert) the value returned from `default` into this entity, if `T` is not already present. |
| 1519 | + /// |
| 1520 | + /// Unlike [`or_insert_with`](Self::or_insert_with), this will not panic if the entity does not exist. |
| 1521 | + /// |
| 1522 | + /// See also [`or_insert`](Self::or_insert) and [`or_try_insert`](Self::or_try_insert). |
| 1523 | + #[track_caller] |
| 1524 | + pub fn or_try_insert_with(self, default: impl Fn() -> T) -> Self { |
| 1525 | + self.or_try_insert(default()) |
| 1526 | + } |
| 1527 | + |
| 1528 | + /// [Insert](EntityCommands::insert) `T::default` into this entity, if `T` is not already present. |
| 1529 | + /// |
| 1530 | + /// See also [`or_insert`](Self::or_insert) and [`or_from_world`](Self::or_from_world). |
| 1531 | + /// |
| 1532 | + /// # Panics |
| 1533 | + /// |
| 1534 | + /// Panics if the entity does not exist. |
| 1535 | + #[track_caller] |
| 1536 | + pub fn or_default(self) -> Self |
| 1537 | + where |
| 1538 | + T: Default, |
| 1539 | + { |
| 1540 | + #[allow(clippy::unwrap_or_default)] |
| 1541 | + // FIXME: use `expect` once stable |
| 1542 | + self.or_insert(T::default()) |
| 1543 | + } |
| 1544 | + |
| 1545 | + /// [Insert](EntityCommands::insert) `T::from_world` into this entity, if `T` is not already present. |
| 1546 | + /// |
| 1547 | + /// See also [`or_insert`](Self::or_insert) and [`or_default`](Self::or_default). |
| 1548 | + /// |
| 1549 | + /// # Panics |
| 1550 | + /// |
| 1551 | + /// Panics if the entity does not exist. |
| 1552 | + #[track_caller] |
| 1553 | + pub fn or_from_world(mut self) -> Self |
| 1554 | + where |
| 1555 | + T: FromWorld, |
| 1556 | + { |
| 1557 | + self.entity_commands = self |
| 1558 | + .entity_commands |
| 1559 | + .queue(insert_from_world::<T>(InsertMode::Keep)); |
| 1560 | + self |
| 1561 | + } |
| 1562 | +} |
| 1563 | + |
1420 | 1564 | impl<F> Command for F
|
1421 | 1565 | where
|
1422 | 1566 | F: FnOnce(&mut World) + Send + 'static,
|
@@ -1525,6 +1669,25 @@ fn insert<T: Bundle>(bundle: T, mode: InsertMode) -> impl EntityCommand {
|
1525 | 1669 | }
|
1526 | 1670 | }
|
1527 | 1671 |
|
| 1672 | +/// An [`EntityCommand`] that adds the component using its `FromWorld` implementation. |
| 1673 | +#[track_caller] |
| 1674 | +fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand { |
| 1675 | + let caller = Location::caller(); |
| 1676 | + move |entity: Entity, world: &mut World| { |
| 1677 | + let value = T::from_world(world); |
| 1678 | + if let Some(mut entity) = world.get_entity_mut(entity) { |
| 1679 | + entity.insert_with_caller( |
| 1680 | + value, |
| 1681 | + mode, |
| 1682 | + #[cfg(feature = "track_change_detection")] |
| 1683 | + caller, |
| 1684 | + ); |
| 1685 | + } else { |
| 1686 | + panic!("error[B0003]: {caller}: Could not insert a bundle (of type `{}`) for entity {:?} because it doesn't exist in this World. See: https://bevyengine.org/learn/errors/b0003", std::any::type_name::<T>(), entity); |
| 1687 | + } |
| 1688 | + } |
| 1689 | +} |
| 1690 | + |
1528 | 1691 | /// An [`EntityCommand`] that attempts to add the components in a [`Bundle`] to an entity.
|
1529 | 1692 | /// Does nothing if the entity does not exist.
|
1530 | 1693 | #[track_caller]
|
@@ -1660,7 +1823,7 @@ mod tests {
|
1660 | 1823 | self as bevy_ecs,
|
1661 | 1824 | component::Component,
|
1662 | 1825 | system::{Commands, Resource},
|
1663 |
| - world::{CommandQueue, World}, |
| 1826 | + world::{CommandQueue, FromWorld, World}, |
1664 | 1827 | };
|
1665 | 1828 | use std::{
|
1666 | 1829 | any::TypeId,
|
@@ -1697,6 +1860,50 @@ mod tests {
|
1697 | 1860 | world.spawn((W(0u32), W(42u64)));
|
1698 | 1861 | }
|
1699 | 1862 |
|
| 1863 | + impl FromWorld for W<String> { |
| 1864 | + fn from_world(world: &mut World) -> Self { |
| 1865 | + let v = world.resource::<W<usize>>(); |
| 1866 | + Self("*".repeat(v.0)) |
| 1867 | + } |
| 1868 | + } |
| 1869 | + |
| 1870 | + #[test] |
| 1871 | + fn entity_commands_entry() { |
| 1872 | + let mut world = World::default(); |
| 1873 | + let mut queue = CommandQueue::default(); |
| 1874 | + let mut commands = Commands::new(&mut queue, &world); |
| 1875 | + let entity = commands.spawn_empty().id(); |
| 1876 | + commands |
| 1877 | + .entity(entity) |
| 1878 | + .entry::<W<u32>>() |
| 1879 | + .and_modify(|_| unreachable!()); |
| 1880 | + queue.apply(&mut world); |
| 1881 | + assert!(!world.entity(entity).contains::<W<u32>>()); |
| 1882 | + let mut commands = Commands::new(&mut queue, &world); |
| 1883 | + commands |
| 1884 | + .entity(entity) |
| 1885 | + .entry::<W<u32>>() |
| 1886 | + .or_insert(W(0)) |
| 1887 | + .and_modify(|mut val| { |
| 1888 | + val.0 = 21; |
| 1889 | + }); |
| 1890 | + queue.apply(&mut world); |
| 1891 | + assert_eq!(21, world.get::<W<u32>>(entity).unwrap().0); |
| 1892 | + let mut commands = Commands::new(&mut queue, &world); |
| 1893 | + commands |
| 1894 | + .entity(entity) |
| 1895 | + .entry::<W<u64>>() |
| 1896 | + .and_modify(|_| unreachable!()) |
| 1897 | + .or_insert(W(42)); |
| 1898 | + queue.apply(&mut world); |
| 1899 | + assert_eq!(42, world.get::<W<u64>>(entity).unwrap().0); |
| 1900 | + world.insert_resource(W(5_usize)); |
| 1901 | + let mut commands = Commands::new(&mut queue, &world); |
| 1902 | + commands.entity(entity).entry::<W<String>>().or_from_world(); |
| 1903 | + queue.apply(&mut world); |
| 1904 | + assert_eq!("*****", &world.get::<W<String>>(entity).unwrap().0); |
| 1905 | + } |
| 1906 | + |
1700 | 1907 | #[test]
|
1701 | 1908 | fn commands() {
|
1702 | 1909 | let mut world = World::default();
|
|
0 commit comments