Skip to content

Commit ed7b366

Browse files
ElliottjPierceJaySprucealice-i-cecile
authored
Deprecate insert_or_spawn function family (#18147)
# Objective Based on #18054, this PR builds on #18035 to deprecate: - `Commands::insert_or_spawn_batch` - `Entities::alloc_at_without_replacement` - `Entities::alloc_at` - `World::insert_or_spawn_batch` - `World::insert_or_spawn_batch_with_caller` ## Testing Just deprecation, so no new tests. Note that as of writing #18035 is still under testing and review. ## Open Questions - [x] Should `entity::AllocAtWithoutReplacement` be deprecated? It is internal and only used in `Entities::alloc_at_without_replacement`. **EDIT:** Now deprecated. ## Migration Guide The following functions have been deprecated: - `Commands::insert_or_spawn_batch` - `World::insert_or_spawn_batch` - `World::insert_or_spawn_batch_with_caller` These functions, when used incorrectly, can cause major performance problems and are generally viewed as anti-patterns and foot guns. These are planned to be removed altogether in 0.17. Instead of these functions consider doing one of the following: Option A) Instead of despawing entities and re-spawning them at a particular id, insert the new `Disabled` component without despawning the entity, and use `try_insert_batch` or `insert_batch` and remove `Disabled` instead of re-spawning it. Option B) Instead of giving special meaning to an entity id, simply use `spawn_batch` and ensure entity references are valid when despawning. --------- Co-authored-by: JaySpruce <[email protected]> Co-authored-by: Alice Cecile <[email protected]>
1 parent 47509ef commit ed7b366

File tree

5 files changed

+51
-0
lines changed

5 files changed

+51
-0
lines changed

benches/benches/bevy_ecs/world/commands.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ pub fn insert_commands(criterion: &mut Criterion) {
106106
for entity in &entities {
107107
values.push((*entity, (Matrix::default(), Vec3::default())));
108108
}
109+
#[expect(
110+
deprecated,
111+
reason = "This needs to be supported for now, and therefore still needs the benchmark."
112+
)]
109113
commands.insert_or_spawn_batch(values);
110114
command_queue.apply(&mut world);
111115
});

crates/bevy_ecs/src/entity/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ impl Hash for Entity {
246246
}
247247
}
248248

249+
#[deprecated(
250+
note = "This is exclusively used with the now deprecated `Entities::alloc_at_without_replacement`."
251+
)]
249252
pub(crate) enum AllocAtWithoutReplacement {
250253
Exists(EntityLocation),
251254
DidNotExist,
@@ -697,6 +700,9 @@ impl Entities {
697700
///
698701
/// Returns the location of the entity currently using the given ID, if any. Location should be
699702
/// written immediately.
703+
#[deprecated(
704+
note = "This can cause extreme performance problems when used after freeing a large number of entities and requesting an arbitrary entity. See #18054 on GitHub."
705+
)]
700706
pub fn alloc_at(&mut self, entity: Entity) -> Option<EntityLocation> {
701707
self.verify_flushed();
702708

@@ -730,6 +736,13 @@ impl Entities {
730736
/// Allocate a specific entity ID, overwriting its generation.
731737
///
732738
/// Returns the location of the entity currently using the given ID, if any.
739+
#[deprecated(
740+
note = "This can cause extreme performance problems when used after freeing a large number of entities and requesting an arbitrary entity. See #18054 on GitHub."
741+
)]
742+
#[expect(
743+
deprecated,
744+
reason = "We need to support `AllocAtWithoutReplacement` for now."
745+
)]
733746
pub(crate) fn alloc_at_without_replacement(
734747
&mut self,
735748
entity: Entity,

crates/bevy_ecs/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,10 @@ mod tests {
17021702

17031703
let values = vec![(e0, (B(0), C)), (e1, (B(1), C))];
17041704

1705+
#[expect(
1706+
deprecated,
1707+
reason = "This needs to be supported for now, and therefore still needs the test."
1708+
)]
17051709
world.insert_or_spawn_batch(values).unwrap();
17061710

17071711
assert_eq!(
@@ -1742,6 +1746,10 @@ mod tests {
17421746

17431747
let values = vec![(e0, (B(0), C)), (e1, (B(1), C)), (invalid_e2, (B(2), C))];
17441748

1749+
#[expect(
1750+
deprecated,
1751+
reason = "This needs to be supported for now, and therefore still needs the test."
1752+
)]
17451753
let result = world.insert_or_spawn_batch(values);
17461754

17471755
assert_eq!(

crates/bevy_ecs/src/system/commands/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,13 +681,21 @@ impl<'w, 's> Commands<'w, 's> {
681681
/// This method should generally only be used for sharing entities across apps, and only when they have a scheme
682682
/// worked out to share an ID space (which doesn't happen by default).
683683
#[track_caller]
684+
#[deprecated(
685+
note = "This can cause extreme performance problems when used with lots of arbitrary free entities. See #18054 on GitHub."
686+
)]
684687
pub fn insert_or_spawn_batch<I, B>(&mut self, bundles_iter: I)
685688
where
686689
I: IntoIterator<Item = (Entity, B)> + Send + Sync + 'static,
687690
B: Bundle<Effect: NoBundleEffect>,
688691
{
689692
let caller = MaybeLocation::caller();
690693
self.queue(move |world: &mut World| {
694+
695+
#[expect(
696+
deprecated,
697+
reason = "This needs to be supported for now, and the outer item is deprecated too."
698+
)]
691699
if let Err(invalid_entities) = world.insert_or_spawn_batch_with_caller(
692700
bundles_iter,
693701
caller,

crates/bevy_ecs/src/world/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ pub use filtered_resource::*;
3030
pub use identifier::WorldId;
3131
pub use spawn_batch::*;
3232

33+
#[expect(
34+
deprecated,
35+
reason = "We need to support `AllocAtWithoutReplacement` for now."
36+
)]
3337
use crate::{
3438
archetype::{ArchetypeId, ArchetypeRow, Archetypes},
3539
bundle::{
@@ -2154,18 +2158,28 @@ impl World {
21542158
/// assert_eq!(world.get::<B>(e0), Some(&B(0.0)));
21552159
/// ```
21562160
#[track_caller]
2161+
#[deprecated(
2162+
note = "This can cause extreme performance problems when used with lots of arbitrary free entities. See #18054 on GitHub."
2163+
)]
21572164
pub fn insert_or_spawn_batch<I, B>(&mut self, iter: I) -> Result<(), Vec<Entity>>
21582165
where
21592166
I: IntoIterator,
21602167
I::IntoIter: Iterator<Item = (Entity, B)>,
21612168
B: Bundle<Effect: NoBundleEffect>,
21622169
{
2170+
#[expect(
2171+
deprecated,
2172+
reason = "This needs to be supported for now, and the outer function is deprecated too."
2173+
)]
21632174
self.insert_or_spawn_batch_with_caller(iter, MaybeLocation::caller())
21642175
}
21652176

21662177
/// Split into a new function so we can pass the calling location into the function when using
21672178
/// as a command.
21682179
#[inline]
2180+
#[deprecated(
2181+
note = "This can cause extreme performance problems when used with lots of arbitrary free entities. See #18054 on GitHub."
2182+
)]
21692183
pub(crate) fn insert_or_spawn_batch_with_caller<I, B>(
21702184
&mut self,
21712185
iter: I,
@@ -2203,6 +2217,10 @@ impl World {
22032217

22042218
let mut invalid_entities = Vec::new();
22052219
for (entity, bundle) in iter {
2220+
#[expect(
2221+
deprecated,
2222+
reason = "This needs to be supported for now, and the outer function is deprecated too."
2223+
)]
22062224
match spawn_or_insert
22072225
.entities()
22082226
.alloc_at_without_replacement(entity)

0 commit comments

Comments
 (0)