1#](https://github.com/bevyengine/bevy#license)\n[](https://crates.io/crates/bevy_ecs)\n[](https://crates.io/crates/bevy_ecs)\n[](https://docs.rs/bevy_ecs/latest/bevy_ecs/)\n[](https://discord.gg/bevy)\n\n## What is Bevy ECS?\n\nBevy ECS is an Entity Component System custom-built for the [Bevy][bevy] game engine.\nIt aims to be simple to use, ergonomic, fast, massively parallel, opinionated, and featureful.\nIt was created specifically for Bevy\'s needs, but it can easily be used as a standalone crate in other projects.\n\n## ECS\n\nAll app logic in Bevy uses the Entity Component System paradigm, which is often shortened to ECS. ECS is a software pattern that involves breaking your program up into Entities, Components, and Systems. Entities are unique \"things\" that are assigned groups of Components, which are then processed using Systems.\n\nFor example, one entity might have a `Position` and `Velocity` component, whereas another entity might have a `Position` and `UI` component. You might have a movement system that runs on all entities with a Position and Velocity component.\n\nThe ECS pattern encourages clean, decoupled designs by forcing you to break up your app data and logic into its core components. It also helps make your code faster by optimizing memory access patterns and making parallelism easier.\n\n## Concepts\n\nBevy ECS is Bevy\'s implementation of the ECS pattern. Unlike other Rust ECS implementations, which often require complex lifetimes, traits, builder patterns, or macros, Bevy ECS uses normal Rust data types for all of these concepts:\n\n### Components\n\nComponents are normal Rust structs. They are data stored in a `World` and specific instances of Components correlate to Entities.\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct Position { x: f32, y: f32 }\n```\n\n### Worlds\n\nEntities, Components, and Resources are stored in a `World`. Worlds, much like `std::collections`\'s `HashSet` and `Vec`, expose operations to insert, read, write, and remove the data they store.\n\n```rust\nuse bevy_ecs::world::World;\n\nlet world = World::default();\n```\n\n### Entities\n\nEntities are unique identifiers that correlate to zero or more Components.\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct Position { x: f32, y: f32 }\n#[derive(Component)]\nstruct Velocity { x: f32, y: f32 }\n\nlet mut world = World::new();\n\nlet entity = world\n .spawn((Position { x: 0.0, y: 0.0 }, Velocity { x: 1.0, y: 0.0 }))\n .id();\n\nlet entity_ref = world.entity(entity);\nlet position = entity_ref.get::<Position>().unwrap();\nlet velocity = entity_ref.get::<Velocity>().unwrap();\n```\n\n### Systems\n\nSystems are normal Rust functions. Thanks to the Rust type system, Bevy ECS can use function parameter types to determine what data needs to be sent to the system. It also uses this \"data access\" information to determine what Systems can run in parallel with each other.\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct Position { x: f32, y: f32 }\n\nfn print_position(query: Query<(Entity, &Position)>) {\n for (entity, position) in &query {\n println!(\"Entity {} is at position: x {}, y {}\", entity, position.x, position.y);\n }\n}\n```\n\n### Resources\n\nApps often require unique resources, such as asset collections, renderers, audio servers, time, etc. Bevy ECS makes this pattern a first class citizen. `Resource` is a special kind of component that does not belong to any entity. Instead, it is identified uniquely by its type:\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Resource, Default)]\nstruct Time {\n seconds: f32,\n}\n\nlet mut world = World::new();\n\nworld.insert_resource(Time::default());\n\nlet time = world.get_resource::<Time>().unwrap();\n\n// You can also access resources from Systems\nfn print_time(time: Res<Time>) {\n println!(\"{}\", time.seconds);\n}\n```\n\n### Schedules\n\nSchedules run a set of Systems according to some execution strategy.\nSystems can be added to any number of System Sets, which are used to control their scheduling metadata.\n\nThe built in \"parallel executor\" considers dependencies between systems and (by default) run as many of them in parallel as possible. This maximizes performance, while keeping the system execution safe. To control the system ordering, define explicit dependencies between systems and their sets.\n\n## Using Bevy ECS\n\nBevy ECS should feel very natural for those familiar with Rust syntax:\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct Position { x: f32, y: f32 }\n#[derive(Component)]\nstruct Velocity { x: f32, y: f32 }\n\n// This system moves each entity with a Position and Velocity component\nfn movement(mut query: Query<(&mut Position, &Velocity)>) {\n for (mut position, velocity) in &mut query {\n position.x += velocity.x;\n position.y += velocity.y;\n }\n}\n\nfn main() {\n // Create a new empty World to hold our Entities and Components\n let mut world = World::new();\n\n // Spawn an entity with Position and Velocity components\n world.spawn((\n Position { x: 0.0, y: 0.0 },\n Velocity { x: 1.0, y: 0.0 },\n ));\n\n // Create a new Schedule, which defines an execution strategy for Systems\n let mut schedule = Schedule::default();\n\n // Add our system to the schedule\n schedule.add_systems(movement);\n\n // Run the schedule once. If your app has a \"loop\", you would run this once per loop\n schedule.run(&mut world);\n}\n```\n\n## Features\n\n### Query Filters\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct Position { x: f32, y: f32 }\n#[derive(Component)]\nstruct Player;\n#[derive(Component)]\nstruct Alive;\n\n// Gets the Position component of all Entities with Player component and without the Alive\n// component.\nfn system(query: Query<&Position, (With<Player>, Without<Alive>)>) {\n for position in &query {\n }\n}\n```\n\n### Change Detection\n\nBevy ECS tracks _all_ changes to Components and Resources.\n\nQueries can filter for changed Components:\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct Position { x: f32, y: f32 }\n#[derive(Component)]\nstruct Velocity { x: f32, y: f32 }\n\n// Gets the Position component of all Entities whose Velocity has changed since the last run of the System\nfn system_changed(query: Query<&Position, Changed<Velocity>>) {\n for position in &query {\n }\n}\n\n// Gets the Position component of all Entities that had a Velocity component added since the last run of the System\nfn system_added(query: Query<&Position, Added<Velocity>>) {\n for position in &query {\n }\n}\n```\n\nResources also expose change state:\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Resource)]\nstruct Time(f32);\n\n// Prints \"time changed!\" if the Time resource has changed since the last run of the System\nfn system(time: Res<Time>) {\n if time.is_changed() {\n println!(\"time changed!\");\n }\n}\n```\n\n### Component Storage\n\nBevy ECS supports multiple component storage types.\n\nComponents can be stored in:\n\n* **Tables**: Fast and cache friendly iteration, but slower adding and removing of components. This is the default storage type.\n* **Sparse Sets**: Fast adding and removing of components, but slower iteration.\n\nComponent storage types are configurable, and they default to table storage if the storage is not manually defined.\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct TableStoredComponent;\n\n#[derive(Component)]\n#[component(storage = \"SparseSet\")]\nstruct SparseStoredComponent;\n```\n\n### Component Bundles\n\nDefine sets of Components that should be added together.\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Default, Component)]\nstruct Player;\n#[derive(Default, Component)]\nstruct Position { x: f32, y: f32 }\n#[derive(Default, Component)]\nstruct Velocity { x: f32, y: f32 }\n\n#[derive(Bundle, Default)]\nstruct PlayerBundle {\n player: Player,\n position: Position,\n velocity: Velocity,\n}\n\nlet mut world = World::new();\n\n// Spawn a new entity and insert the default PlayerBundle\nworld.spawn(PlayerBundle::default());\n\n// Bundles play well with Rust\'s struct update syntax\nworld.spawn(PlayerBundle {\n position: Position { x: 1.0, y: 1.0 },\n ..Default::default()\n});\n```\n\n### Messages\n\nMessages offer a communication channel between one or more systems.\nThey can be sent using the `MessageWriter` system parameter and received with `MessageReader`.\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Message)]\nstruct Message(String);\n\nfn writer(mut writer: MessageWriter<Message>) {\n writer.write(Message(\"Hello!\".to_string()));\n}\n\nfn reader(mut reader: MessageReader<Message>) {\n for Message(message) in reader.read() {\n println!(\"{}\", message);\n }\n}\n```\n\n### Observers\n\nObservers are systems that watch for a \"trigger\" of a specific `Event`:\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Event)]\nstruct Speak {\n message: String\n}\n\nlet mut world = World::new();\n\nworld.add_observer(|event: On<Speak>| {\n println!(\"{}\", event.message);\n});\n\nworld.trigger(Speak {\n message: \"Hello!\".to_string(),\n});\n```\n\nThese differ from `MessageReader` and `MessageWriter` in that they are \"reactive\".\nRather than happening at a specific point in a schedule, they happen _immediately_ whenever a trigger happens.\nTriggers can trigger other triggers, and they all will be evaluated at the same time!\n\nIf the event is an `EntityEvent`, it can also be triggered to target specific entities:\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(EntityEvent)]\nstruct Explode {\n entity: Entity,\n}\n\nlet mut world = World::new();\nlet entity = world.spawn_empty().id();\n\nworld.add_observer(|explode: On<Explode>, mut commands: Commands| {\n println!(\"Entity {} goes BOOM!\", explode.entity);\n commands.entity(explode.entity).despawn();\n});\n\nworld.trigger(Explode { entity });\n```\n\n[bevy]: https://bevy.org/\n"include_str!("../README.md")]
2#![cfg_attr(
3 any(docsrs, docsrs_dep),
4 expect(
5 internal_features,
6 reason = "rustdoc_internals is needed for fake_variadic"
7 )
8)]
9#![cfg_attr(any(docsrs, docsrs_dep), feature(rustdoc_internals))]
10#![cfg_attr(docsrs, feature(doc_cfg))]
11#![expect(unsafe_code, reason = "Unsafe code is used to improve performance.")]
12#![doc(
13 html_logo_url = "https://bevy.org/assets/icon.png",
14 html_favicon_url = "https://bevy.org/assets/icon.png"
15)]
16#![no_std]
17
18#[cfg(feature = "std")]
19extern crate std;
20
21#[cfg(target_pointer_width = "16")]
22compile_error!("bevy_ecs cannot safely compile for a 16-bit platform.");
23
24extern crate alloc;
25
26extern crate self as bevy_ecs;
28
29pub mod archetype;
30pub mod batching;
31pub mod bundle;
32pub mod change_detection;
33pub mod component;
34pub mod entity;
35pub mod entity_disabling;
36pub mod error;
37pub mod event;
38pub mod hierarchy;
39pub mod intern;
40pub mod label;
41pub mod lifecycle;
42pub mod message;
43pub mod name;
44pub mod never;
45pub mod observer;
46pub mod query;
47#[cfg(feature = "bevy_reflect")]
48pub mod reflect;
49pub mod relationship;
50pub mod resource;
51pub mod schedule;
52pub mod spawn;
53pub mod storage;
54pub mod system;
55pub mod template;
56pub mod traversal;
57pub mod world;
58
59pub use bevy_ptr as ptr;
60pub use bevy_utils as utils;
61
62#[cfg(feature = "hotpatching")]
63use message::Message;
64
65pub mod prelude {
69 #[doc(hidden)]
70 pub use crate::{
71 bundle::Bundle,
72 change_detection::{
73 ContiguousMut, ContiguousRef, DetectChanges, DetectChangesMut, Mut, Ref,
74 },
75 children,
76 component::Component,
77 entity::{ContainsEntity, Entity, EntityMapper},
78 error::{BevyError, ContextExt, Result, ResultSeverityExt, Severity},
79 event::{EntityEvent, Event, EventPattern},
80 hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children},
81 lifecycle::{Add, Despawn, Discard, Insert, Remove, RemovedComponents},
82 message::{
83 Message, MessageMutator, MessageReader, MessageWriter, Messages, PopulatedMessageReader,
84 },
85 name::{Name, NameOrEntity},
86 observer::{Observer, ObserverSystemExt, On},
87 query::{Added, Allow, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without},
88 related,
89 relationship::RelationshipTarget,
90 resource::Resource,
91 schedule::{
92 common_conditions::*, ApplyDeferred, IntoScheduleConfigs, IntoSystemSet, Schedule,
93 Schedules, SystemCondition, SystemSet,
94 },
95 spawn::{Spawn, SpawnIter, SpawnRelated, SpawnWith, WithOneRelated, WithRelated},
96 system::{
97 Command, Commands, Deferred, EntityCommand, EntityCommands, If, In, InMut, InRef,
98 IntoSystem, Local, NonSend, NonSendMut, ParamSet, Populated, Query, ReadOnlySystem,
99 Res, ResMut, Single, System, SystemIn, SystemInput, SystemParamBuilder,
100 SystemParamFunction,
101 },
102 template::{template, FromTemplate, Template},
103 world::{EntityMut, EntityRef, EntityWorldMut, FromWorld, World},
104 };
105
106 #[expect(deprecated, reason = "`FilteredResources` will be removed.")]
107 pub use crate::world::{FilteredResources, FilteredResourcesMut};
108
109 #[doc(hidden)]
110 #[cfg(feature = "std")]
111 pub use crate::system::ParallelCommands;
112
113 #[doc(hidden)]
114 #[cfg(feature = "bevy_reflect")]
115 pub use crate::reflect::{
116 AppTypeRegistry, ReflectComponent, ReflectEvent, ReflectFromWorld, ReflectMessage,
117 ReflectResource,
118 };
119
120 #[doc(hidden)]
121 #[cfg(feature = "reflect_functions")]
122 pub use crate::reflect::AppFunctionRegistry;
123}
124
125#[doc(hidden)]
129pub mod __macro_exports {
130 pub use crate::query::DebugCheckedUnwrap;
134 pub use alloc::format;
135 pub use alloc::vec::Vec;
136}
137
138#[cfg(feature = "hotpatching")]
142#[derive(impl bevy_ecs::message::Message for HotPatched where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Message, #[automatically_derived]impl ::core::default::Default for HotPatched {
#[inline]
fn default() -> Self { HotPatched {} }
}Default)]
143pub struct HotPatched;
144
145#[cfg(feature = "hotpatching")]
154#[derive(impl bevy_ecs::component::Component for HotPatchChanges where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::SparseSet;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {
let resource_component_id =
if let ::core::option::Option::Some(id) =
required_components.components_registrator().component_id::<HotPatchChanges>()
{
id
} else {
required_components.components_registrator().register_component::<HotPatchChanges>()
};
required_components.register_required::<bevy_ecs::resource::IsResource>(move
||
bevy_ecs::resource::IsResource::new(resource_component_id));
}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}
impl bevy_ecs::resource::Resource for HotPatchChanges where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {}resource::Resource, #[automatically_derived]impl ::core::default::Default for HotPatchChanges {
#[inline]
fn default() -> Self { HotPatchChanges {} }
}Default)]
155pub struct HotPatchChanges;
156
157#[cfg(test)]
158mod tests {
159 use crate::{
160 bundle::Bundle,
161 change_detection::Ref,
162 component::Component,
163 entity::{Entity, EntityHashSet, EntityMapper, EntityNotSpawnedError},
164 entity_disabling::DefaultQueryFilters,
165 prelude::Or,
166 query::{Added, Changed, FilteredAccess, QueryFilter, With, Without},
167 resource::Resource,
168 world::{error::EntityDespawnError, EntityMut, EntityRef, Mut, World},
169 };
170 use alloc::{string::String, sync::Arc, vec, vec::Vec};
171 use bevy_platform::collections::HashSet;
172 use bevy_tasks::{ComputeTaskPool, TaskPool};
173 use core::{
174 any::TypeId,
175 sync::atomic::{AtomicUsize, Ordering},
176 };
177 use std::sync::Mutex;
178
179 #[derive(Component, Debug, PartialEq, Eq, Hash, Clone, Copy)]
180 struct A(usize);
181 #[derive(Resource, Debug, PartialEq, Eq)]
182 struct ResA(usize);
183 #[derive(Component, Debug, PartialEq, Eq, Hash, Clone, Copy)]
184 struct B(usize);
185 #[derive(Component, Debug, PartialEq, Eq, Clone, Copy)]
186 struct C;
187
188 #[derive(Component, Clone, Debug)]
189 struct DropCk(Arc<AtomicUsize>);
190 impl DropCk {
191 fn new_pair() -> (Self, Arc<AtomicUsize>) {
192 let atomic = Arc::new(AtomicUsize::new(0));
193 (DropCk(atomic.clone()), atomic)
194 }
195 }
196
197 impl Drop for DropCk {
198 fn drop(&mut self) {
199 self.0.as_ref().fetch_add(1, Ordering::Relaxed);
200 }
201 }
202
203 #[expect(
204 dead_code,
205 reason = "This struct is used to test how `Drop` behavior works in regards to SparseSet storage, and as such is solely a wrapper around `DropCk` to make it use the SparseSet storage. Because of this, the inner field is intentionally never read."
206 )]
207 #[derive(Component, Clone, Debug)]
208 #[component(storage = "SparseSet")]
209 struct DropCkSparse(DropCk);
210
211 #[derive(Component, Copy, Clone, PartialEq, Eq, Debug)]
212 #[component(storage = "Table")]
213 struct TableStored(&'static str);
214 #[derive(Component, Copy, Clone, PartialEq, Eq, Hash, Debug)]
215 #[component(storage = "SparseSet")]
216 struct SparseStored(u32);
217
218 #[test]
219 fn random_access() {
220 let mut world = World::new();
221
222 let e = world.spawn((TableStored("abc"), SparseStored(123))).id();
223 let f = world
224 .spawn((TableStored("def"), SparseStored(456), A(1)))
225 .id();
226 assert_eq!(world.get::<TableStored>(e).unwrap().0, "abc");
227 assert_eq!(world.get::<SparseStored>(e).unwrap().0, 123);
228 assert_eq!(world.get::<TableStored>(f).unwrap().0, "def");
229 assert_eq!(world.get::<SparseStored>(f).unwrap().0, 456);
230
231 world.get_mut::<TableStored>(e).unwrap().0 = "xyz";
233 assert_eq!(world.get::<TableStored>(e).unwrap().0, "xyz");
234
235 world.get_mut::<SparseStored>(f).unwrap().0 = 42;
237 assert_eq!(world.get::<SparseStored>(f).unwrap().0, 42);
238 }
239
240 #[test]
241 fn bundle_derive() {
242 let mut world = World::new();
243
244 #[derive(Bundle, PartialEq, Debug)]
245 struct FooBundle {
246 x: TableStored,
247 y: SparseStored,
248 }
249 let ids: Vec<_> =
250 <FooBundle as Bundle>::component_ids(&mut world.components_registrator()).collect();
251
252 assert_eq!(
253 ids,
254 &[
255 world.register_component::<TableStored>(),
256 world.register_component::<SparseStored>(),
257 ]
258 );
259
260 let e1 = world
261 .spawn(FooBundle {
262 x: TableStored("abc"),
263 y: SparseStored(123),
264 })
265 .id();
266 let e2 = world
267 .spawn((TableStored("def"), SparseStored(456), A(1)))
268 .id();
269 assert_eq!(world.get::<TableStored>(e1).unwrap().0, "abc");
270 assert_eq!(world.get::<SparseStored>(e1).unwrap().0, 123);
271 assert_eq!(world.get::<TableStored>(e2).unwrap().0, "def");
272 assert_eq!(world.get::<SparseStored>(e2).unwrap().0, 456);
273
274 world.get_mut::<TableStored>(e1).unwrap().0 = "xyz";
276 assert_eq!(world.get::<TableStored>(e1).unwrap().0, "xyz");
277
278 world.get_mut::<SparseStored>(e2).unwrap().0 = 42;
280 assert_eq!(world.get::<SparseStored>(e2).unwrap().0, 42);
281
282 assert_eq!(
283 world.entity_mut(e1).take::<FooBundle>().unwrap(),
284 FooBundle {
285 x: TableStored("xyz"),
286 y: SparseStored(123),
287 }
288 );
289
290 #[derive(Bundle, PartialEq, Debug)]
291 struct NestedBundle {
292 a: A,
293 foo: FooBundle,
294 b: B,
295 }
296
297 let ids: Vec<_> =
298 <NestedBundle as Bundle>::component_ids(&mut world.components_registrator()).collect();
299
300 assert_eq!(
301 ids,
302 &[
303 world.register_component::<A>(),
304 world.register_component::<TableStored>(),
305 world.register_component::<SparseStored>(),
306 world.register_component::<B>(),
307 ]
308 );
309
310 let e3 = world
311 .spawn(NestedBundle {
312 a: A(1),
313 foo: FooBundle {
314 x: TableStored("ghi"),
315 y: SparseStored(789),
316 },
317 b: B(2),
318 })
319 .id();
320
321 assert_eq!(world.get::<TableStored>(e3).unwrap().0, "ghi");
322 assert_eq!(world.get::<SparseStored>(e3).unwrap().0, 789);
323 assert_eq!(world.get::<A>(e3).unwrap().0, 1);
324 assert_eq!(world.get::<B>(e3).unwrap().0, 2);
325 assert_eq!(
326 world.entity_mut(e3).take::<NestedBundle>().unwrap(),
327 NestedBundle {
328 a: A(1),
329 foo: FooBundle {
330 x: TableStored("ghi"),
331 y: SparseStored(789),
332 },
333 b: B(2),
334 }
335 );
336
337 #[derive(Default, Component, PartialEq, Debug)]
338 struct Ignored;
339
340 #[derive(Bundle, PartialEq, Debug)]
341 struct BundleWithIgnored {
342 c: C,
343 #[bundle(ignore)]
344 ignored: Ignored,
345 }
346
347 let ids: Vec<_> =
348 <BundleWithIgnored as Bundle>::component_ids(&mut world.components_registrator())
349 .collect();
350
351 assert_eq!(ids, &[world.register_component::<C>(),]);
352
353 let e4 = world
354 .spawn(BundleWithIgnored {
355 c: C,
356 ignored: Ignored,
357 })
358 .id();
359
360 assert_eq!(world.get::<C>(e4).unwrap(), &C);
361 assert_eq!(world.get::<Ignored>(e4), None);
362
363 assert_eq!(
364 world.entity_mut(e4).take::<BundleWithIgnored>().unwrap(),
365 BundleWithIgnored {
366 c: C,
367 ignored: Ignored,
368 }
369 );
370 }
371
372 #[test]
373 fn spawning_with_manual_entity_allocation() {
374 let mut world = World::new();
375 let start = world.entities().count_spawned();
376 let e1 = world.entity_allocator_mut().alloc();
377 world.spawn_at(e1, (TableStored("abc"), A(123))).unwrap();
378
379 let e2 = world.entity_allocator_mut().alloc();
380 assert!(matches!(
381 world.try_despawn_no_free(e2),
382 Err(EntityDespawnError(
383 EntityNotSpawnedError::ValidButNotSpawned(_)
384 ))
385 ));
386 assert!(!world.despawn(e2));
387 world.entity_allocator_mut().free(e2);
388
389 let e3 = world.entity_allocator_mut().alloc();
390 let e3 = world
391 .spawn_at(e3, (TableStored("junk"), A(0)))
392 .unwrap()
393 .despawn_no_free();
394 world.spawn_at(e3, (TableStored("def"), A(456))).unwrap();
395
396 assert_eq!(world.entities.count_spawned(), start + 2);
397 assert!(world.despawn(e1));
398 assert_eq!(world.entities.count_spawned(), start + 1);
399 assert!(world.get::<TableStored>(e1).is_none());
400 assert!(world.get::<A>(e1).is_none());
401 assert_eq!(world.get::<TableStored>(e3).unwrap().0, "def");
402 assert_eq!(world.get::<A>(e3).unwrap().0, 456);
403 }
404
405 #[test]
406 fn despawn_table_storage() {
407 let mut world = World::new();
408 let e = world.spawn((TableStored("abc"), A(123))).id();
409 let f = world.spawn((TableStored("def"), A(456))).id();
410 assert_eq!(world.query::<&TableStored>().query(&world).count(), 2);
411 assert!(world.despawn(e));
412 assert_eq!(world.query::<&TableStored>().query(&world).count(), 1);
413 assert!(world.get::<TableStored>(e).is_none());
414 assert!(world.get::<A>(e).is_none());
415 assert_eq!(world.get::<TableStored>(f).unwrap().0, "def");
416 assert_eq!(world.get::<A>(f).unwrap().0, 456);
417 }
418
419 #[test]
420 fn despawn_mixed_storage() {
421 let mut world = World::new();
422
423 let e = world.spawn((TableStored("abc"), SparseStored(123))).id();
424 let f = world.spawn((TableStored("def"), SparseStored(456))).id();
425 assert_eq!(world.query::<&TableStored>().query(&world).count(), 2);
426 assert!(world.despawn(e));
427 assert_eq!(world.query::<&TableStored>().query(&world).count(), 1);
428 assert!(world.get::<TableStored>(e).is_none());
429 assert!(world.get::<SparseStored>(e).is_none());
430 assert_eq!(world.get::<TableStored>(f).unwrap().0, "def");
431 assert_eq!(world.get::<SparseStored>(f).unwrap().0, 456);
432 }
433
434 #[test]
435 fn query_all() {
436 let mut world = World::new();
437 let e = world.spawn((TableStored("abc"), A(123))).id();
438 let f = world.spawn((TableStored("def"), A(456))).id();
439
440 let ents = world
441 .query::<(Entity, &A, &TableStored)>()
442 .iter(&world)
443 .map(|(e, &i, &s)| (e, i, s))
444 .collect::<Vec<_>>();
445 assert_eq!(
446 ents,
447 &[
448 (e, A(123), TableStored("abc")),
449 (f, A(456), TableStored("def"))
450 ]
451 );
452 }
453
454 #[test]
455 fn query_all_for_each() {
456 let mut world = World::new();
457 let e = world.spawn((TableStored("abc"), A(123))).id();
458 let f = world.spawn((TableStored("def"), A(456))).id();
459
460 let mut results = Vec::new();
461 world
462 .query::<(Entity, &A, &TableStored)>()
463 .iter(&world)
464 .for_each(|(e, &i, &s)| results.push((e, i, s)));
465 assert_eq!(
466 results,
467 &[
468 (e, A(123), TableStored("abc")),
469 (f, A(456), TableStored("def"))
470 ]
471 );
472 }
473
474 #[test]
475 fn query_single_component() {
476 let mut world = World::new();
477 let e = world.spawn((TableStored("abc"), A(123))).id();
478 let f = world.spawn((TableStored("def"), A(456), B(1))).id();
479 let ents = world
480 .query::<(Entity, &A)>()
481 .iter(&world)
482 .map(|(e, &i)| (e, i))
483 .collect::<HashSet<_>>();
484 assert!(ents.contains(&(e, A(123))));
485 assert!(ents.contains(&(f, A(456))));
486 }
487
488 #[test]
489 fn stateful_query_handles_new_archetype() {
490 let mut world = World::new();
491 let e = world.spawn((TableStored("abc"), A(123))).id();
492 let mut query = world.query::<(Entity, &A)>();
493
494 let ents = query.iter(&world).map(|(e, &i)| (e, i)).collect::<Vec<_>>();
495 assert_eq!(ents, &[(e, A(123))]);
496
497 let f = world.spawn((TableStored("def"), A(456), B(1))).id();
498 let ents = query.iter(&world).map(|(e, &i)| (e, i)).collect::<Vec<_>>();
499 assert_eq!(ents, &[(e, A(123)), (f, A(456))]);
500 }
501
502 #[test]
503 fn query_single_component_for_each() {
504 let mut world = World::new();
505 let e = world.spawn((TableStored("abc"), A(123))).id();
506 let f = world.spawn((TableStored("def"), A(456), B(1))).id();
507 let mut results = <HashSet<_>>::default();
508 world
509 .query::<(Entity, &A)>()
510 .iter(&world)
511 .for_each(|(e, &i)| {
512 results.insert((e, i));
513 });
514 assert!(results.contains(&(e, A(123))));
515 assert!(results.contains(&(f, A(456))));
516 }
517
518 #[test]
519 fn par_for_each_dense() {
520 ComputeTaskPool::get_or_init(TaskPool::default);
521 let mut world = World::new();
522 let e1 = world.spawn(A(1)).id();
523 let e2 = world.spawn(A(2)).id();
524 let e3 = world.spawn(A(3)).id();
525 let e4 = world.spawn((A(4), B(1))).id();
526 let e5 = world.spawn((A(5), B(1))).id();
527 let results = Arc::new(Mutex::new(Vec::new()));
528 world
529 .query::<(Entity, &A)>()
530 .par_iter(&world)
531 .for_each(|(e, &A(i))| {
532 results.lock().unwrap().push((e, i));
533 });
534 results.lock().unwrap().sort();
535 let mut expected = [(e1, 1), (e2, 2), (e3, 3), (e4, 4), (e5, 5)];
536 expected.sort();
537 assert_eq!(&*results.lock().unwrap(), &expected);
538 }
539
540 #[test]
541 fn par_for_each_sparse() {
542 ComputeTaskPool::get_or_init(TaskPool::default);
543 let mut world = World::new();
544 let e1 = world.spawn(SparseStored(1)).id();
545 let e2 = world.spawn(SparseStored(2)).id();
546 let e3 = world.spawn(SparseStored(3)).id();
547 let e4 = world.spawn((SparseStored(4), A(1))).id();
548 let e5 = world.spawn((SparseStored(5), A(1))).id();
549 let results = Arc::new(Mutex::new(Vec::new()));
550 world
551 .query::<(Entity, &SparseStored)>()
552 .par_iter(&world)
553 .for_each(|(e, &SparseStored(i))| results.lock().unwrap().push((e, i)));
554 results.lock().unwrap().sort();
555 let mut expected = [(e1, 1), (e2, 2), (e3, 3), (e4, 4), (e5, 5)];
556 expected.sort();
557 assert_eq!(&*results.lock().unwrap(), &expected);
558 }
559
560 #[test]
561 fn query_missing_component() {
562 let mut world = World::new();
563 world.spawn((TableStored("abc"), A(123)));
564 world.spawn((TableStored("def"), A(456)));
565 assert!(world.query::<(&B, &A)>().iter(&world).next().is_none());
566 }
567
568 #[test]
569 fn query_sparse_component() {
570 let mut world = World::new();
571 world.spawn((TableStored("abc"), A(123)));
572 let f = world.spawn((TableStored("def"), A(456), B(1))).id();
573 let ents = world
574 .query::<(Entity, &B)>()
575 .iter(&world)
576 .map(|(e, &b)| (e, b))
577 .collect::<Vec<_>>();
578 assert_eq!(ents, &[(f, B(1))]);
579 }
580
581 #[test]
582 fn query_filter_with() {
583 let mut world = World::new();
584 world.spawn((A(123), B(1)));
585 world.spawn(A(456));
586 let result = world
587 .query_filtered::<&A, With<B>>()
588 .iter(&world)
589 .cloned()
590 .collect::<Vec<_>>();
591 assert_eq!(result, vec![A(123)]);
592 }
593
594 #[test]
595 fn query_filter_with_for_each() {
596 let mut world = World::new();
597 world.spawn((A(123), B(1)));
598 world.spawn(A(456));
599
600 let mut results = Vec::new();
601 world
602 .query_filtered::<&A, With<B>>()
603 .iter(&world)
604 .for_each(|i| results.push(*i));
605 assert_eq!(results, vec![A(123)]);
606 }
607
608 #[test]
609 fn query_filter_with_sparse() {
610 let mut world = World::new();
611
612 world.spawn((A(123), SparseStored(321)));
613 world.spawn(A(456));
614 let result = world
615 .query_filtered::<&A, With<SparseStored>>()
616 .iter(&world)
617 .cloned()
618 .collect::<Vec<_>>();
619 assert_eq!(result, vec![A(123)]);
620 }
621
622 #[test]
623 fn query_filter_with_sparse_for_each() {
624 let mut world = World::new();
625
626 world.spawn((A(123), SparseStored(321)));
627 world.spawn(A(456));
628 let mut results = Vec::new();
629 world
630 .query_filtered::<&A, With<SparseStored>>()
631 .iter(&world)
632 .for_each(|i| results.push(*i));
633 assert_eq!(results, vec![A(123)]);
634 }
635
636 #[test]
637 fn query_filter_without() {
638 let mut world = World::new();
639 world.spawn((A(123), B(321)));
640 world.spawn(A(456));
641 let result = world
642 .query_filtered::<&A, Without<B>>()
643 .iter(&world)
644 .cloned()
645 .collect::<Vec<_>>();
646 assert_eq!(result, vec![A(456)]);
647 }
648
649 #[test]
650 fn query_optional_component_table() {
651 let mut world = World::new();
652 let e = world.spawn((TableStored("abc"), A(123))).id();
653 let f = world.spawn((TableStored("def"), A(456), B(1))).id();
654 world.spawn(TableStored("abc"));
656 let ents = world
657 .query::<(Entity, Option<&B>, &A)>()
658 .iter(&world)
659 .map(|(e, b, &i)| (e, b.copied(), i))
660 .collect::<HashSet<_>>();
661 assert!(ents.contains(&(e, None, A(123))));
662 assert!(ents.contains(&(f, Some(B(1)), A(456))));
663 }
664
665 #[test]
666 fn query_optional_component_sparse() {
667 let mut world = World::new();
668
669 let e = world.spawn((TableStored("abc"), A(123))).id();
670 let f = world
671 .spawn((TableStored("def"), A(456), SparseStored(1)))
672 .id();
673 let ents = world
676 .query::<(Entity, Option<&SparseStored>, &A)>()
677 .iter(&world)
678 .map(|(e, b, &i)| (e, b.copied(), i))
679 .collect::<HashSet<_>>();
680 assert_eq!(
681 ents,
682 [(e, None, A(123)), (f, Some(SparseStored(1)), A(456))]
683 .into_iter()
684 .collect::<HashSet<_>>()
685 );
686 }
687
688 #[test]
689 fn query_optional_component_sparse_no_match() {
690 let mut world = World::new();
691
692 let e = world.spawn((TableStored("abc"), A(123))).id();
693 let f = world.spawn((TableStored("def"), A(456))).id();
694 world.spawn(TableStored("abc"));
696 let ents = world
697 .query::<(Entity, Option<&SparseStored>, &A)>()
698 .iter(&world)
699 .map(|(e, b, &i)| (e, b.copied(), i))
700 .collect::<Vec<_>>();
701 assert_eq!(ents, &[(e, None, A(123)), (f, None, A(456))]);
702 }
703
704 #[test]
705 fn add_remove_components() {
706 let mut world = World::new();
707 let e1 = world.spawn((A(1), B(3), TableStored("abc"))).id();
708 let e2 = world.spawn((A(2), B(4), TableStored("xyz"))).id();
709
710 assert_eq!(
711 world
712 .query::<(Entity, &A, &B)>()
713 .iter(&world)
714 .map(|(e, &i, &b)| (e, i, b))
715 .collect::<HashSet<_>>(),
716 [(e1, A(1), B(3)), (e2, A(2), B(4))]
717 .into_iter()
718 .collect::<HashSet<_>>()
719 );
720 assert_eq!(world.entity_mut(e1).take::<A>(), Some(A(1)));
721 assert_eq!(
722 world
723 .query::<(Entity, &A, &B)>()
724 .iter(&world)
725 .map(|(e, &i, &b)| (e, i, b))
726 .collect::<Vec<_>>(),
727 &[(e2, A(2), B(4))]
728 );
729 assert_eq!(
730 world
731 .query::<(Entity, &B, &TableStored)>()
732 .iter(&world)
733 .map(|(e, &B(b), &TableStored(s))| (e, b, s))
734 .collect::<HashSet<_>>(),
735 [(e2, 4, "xyz"), (e1, 3, "abc")]
736 .into_iter()
737 .collect::<HashSet<_>>()
738 );
739 world.entity_mut(e1).insert(A(43));
740 assert_eq!(
741 world
742 .query::<(Entity, &A, &B)>()
743 .iter(&world)
744 .map(|(e, &i, &b)| (e, i, b))
745 .collect::<HashSet<_>>(),
746 [(e2, A(2), B(4)), (e1, A(43), B(3))]
747 .into_iter()
748 .collect::<HashSet<_>>()
749 );
750 world.entity_mut(e1).insert(C);
751 assert_eq!(
752 world
753 .query::<(Entity, &C)>()
754 .iter(&world)
755 .map(|(e, &f)| (e, f))
756 .collect::<Vec<_>>(),
757 &[(e1, C)]
758 );
759 }
760
761 #[test]
762 fn table_add_remove_many() {
763 let mut world = World::default();
764 #[cfg(miri)]
765 let (mut entities, to) = {
766 let to = 10;
767 (Vec::with_capacity(to), to)
768 };
769 #[cfg(not(miri))]
770 let (mut entities, to) = {
771 let to = 10_000;
772 (Vec::with_capacity(to), to)
773 };
774
775 for _ in 0..to {
776 entities.push(world.spawn(B(0)).id());
777 }
778
779 for (i, entity) in entities.iter().cloned().enumerate() {
780 world.entity_mut(entity).insert(A(i));
781 }
782
783 for (i, entity) in entities.iter().cloned().enumerate() {
784 assert_eq!(world.entity_mut(entity).take::<A>(), Some(A(i)));
785 }
786 }
787
788 #[test]
789 fn sparse_set_add_remove_many() {
790 let mut world = World::default();
791
792 let mut entities = Vec::with_capacity(1000);
793 for _ in 0..4 {
794 entities.push(world.spawn(A(2)).id());
795 }
796
797 for (i, entity) in entities.iter().cloned().enumerate() {
798 world.entity_mut(entity).insert(SparseStored(i as u32));
799 }
800
801 for (i, entity) in entities.iter().cloned().enumerate() {
802 assert_eq!(
803 world.entity_mut(entity).take::<SparseStored>(),
804 Some(SparseStored(i as u32))
805 );
806 }
807 }
808
809 #[test]
810 fn remove_missing() {
811 let mut world = World::new();
812 let e = world.spawn((TableStored("abc"), A(123))).id();
813 assert!(world.entity_mut(e).take::<B>().is_none());
814 }
815
816 #[test]
817 fn spawn_batch() {
818 let mut world = World::new();
819 world.spawn_batch((0..100).map(|x| (A(x), TableStored("abc"))));
820 let values = world
821 .query::<&A>()
822 .iter(&world)
823 .map(|v| v.0)
824 .collect::<Vec<_>>();
825 let expected = (0..100).collect::<Vec<_>>();
826 assert_eq!(values, expected);
827 }
828
829 #[test]
830 fn query_get() {
831 let mut world = World::new();
832 let a = world.spawn((TableStored("abc"), A(123))).id();
833 let b = world.spawn((TableStored("def"), A(456))).id();
834 let c = world.spawn((TableStored("ghi"), A(789), B(1))).id();
835
836 let mut i32_query = world.query::<&A>();
837 assert_eq!(i32_query.get(&world, a).unwrap().0, 123);
838 assert_eq!(i32_query.get(&world, b).unwrap().0, 456);
839
840 let mut i32_bool_query = world.query::<(&A, &B)>();
841 assert!(i32_bool_query.get(&world, a).is_err());
842 assert_eq!(i32_bool_query.get(&world, c).unwrap(), (&A(789), &B(1)));
843 assert!(world.despawn(a));
844 assert!(i32_query.get(&world, a).is_err());
845 }
846
847 #[test]
848 fn query_get_works_across_sparse_removal() {
849 let mut world = World::new();
851 let a = world.spawn((TableStored("abc"), SparseStored(123))).id();
852 let b = world.spawn((TableStored("def"), SparseStored(456))).id();
853 let c = world
854 .spawn((TableStored("ghi"), SparseStored(789), B(1)))
855 .id();
856
857 let mut query = world.query::<&TableStored>();
858 assert_eq!(query.get(&world, a).unwrap(), &TableStored("abc"));
859 assert_eq!(query.get(&world, b).unwrap(), &TableStored("def"));
860 assert_eq!(query.get(&world, c).unwrap(), &TableStored("ghi"));
861
862 world.entity_mut(b).remove::<SparseStored>();
863 world.entity_mut(c).remove::<SparseStored>();
864
865 assert_eq!(query.get(&world, a).unwrap(), &TableStored("abc"));
866 assert_eq!(query.get(&world, b).unwrap(), &TableStored("def"));
867 assert_eq!(query.get(&world, c).unwrap(), &TableStored("ghi"));
868 }
869
870 #[test]
871 fn remove_tracking() {
872 let mut world = World::new();
873
874 let a = world.spawn((SparseStored(0), A(123))).id();
875 let b = world.spawn((SparseStored(1), A(123))).id();
876
877 world.entity_mut(a).despawn();
878 assert_eq!(
879 world.removed::<A>().collect::<Vec<_>>(),
880 &[a],
881 "despawning results in 'removed component' state for table components"
882 );
883 assert_eq!(
884 world.removed::<SparseStored>().collect::<Vec<_>>(),
885 &[a],
886 "despawning results in 'removed component' state for sparse set components"
887 );
888
889 world.entity_mut(b).insert(B(1));
890 assert_eq!(
891 world.removed::<A>().collect::<Vec<_>>(),
892 &[a],
893 "archetype moves does not result in 'removed component' state"
894 );
895
896 world.entity_mut(b).remove::<A>();
897 assert_eq!(
898 world.removed::<A>().collect::<Vec<_>>(),
899 &[a, b],
900 "removing a component results in a 'removed component' state"
901 );
902
903 world.clear_trackers();
904 assert_eq!(
905 world.removed::<A>().collect::<Vec<_>>(),
906 &[],
907 "clearing trackers clears removals"
908 );
909 assert_eq!(
910 world.removed::<SparseStored>().collect::<Vec<_>>(),
911 &[],
912 "clearing trackers clears removals"
913 );
914 assert_eq!(
915 world.removed::<B>().collect::<Vec<_>>(),
916 &[],
917 "clearing trackers clears removals"
918 );
919
920 }
940
941 #[test]
942 fn added_tracking() {
943 let mut world = World::new();
944 let a = world.spawn(A(123)).id();
945
946 assert_eq!(world.query::<&A>().iter(&world).count(), 1);
947 assert_eq!(
948 world.query_filtered::<(), Added<A>>().iter(&world).count(),
949 1
950 );
951 assert_eq!(world.query::<&A>().iter(&world).count(), 1);
952 assert_eq!(
953 world.query_filtered::<(), Added<A>>().iter(&world).count(),
954 1
955 );
956 assert!(world.query::<&A>().get(&world, a).is_ok());
957 assert!(world
958 .query_filtered::<(), Added<A>>()
959 .get(&world, a)
960 .is_ok());
961 assert!(world.query::<&A>().get(&world, a).is_ok());
962 assert!(world
963 .query_filtered::<(), Added<A>>()
964 .get(&world, a)
965 .is_ok());
966
967 world.clear_trackers();
968
969 assert_eq!(world.query::<&A>().iter(&world).count(), 1);
970 assert_eq!(
971 world.query_filtered::<(), Added<A>>().iter(&world).count(),
972 0
973 );
974 assert_eq!(world.query::<&A>().iter(&world).count(), 1);
975 assert_eq!(
976 world.query_filtered::<(), Added<A>>().iter(&world).count(),
977 0
978 );
979 assert!(world.query::<&A>().get(&world, a).is_ok());
980 assert!(world
981 .query_filtered::<(), Added<A>>()
982 .get(&world, a)
983 .is_err());
984 assert!(world.query::<&A>().get(&world, a).is_ok());
985 assert!(world
986 .query_filtered::<(), Added<A>>()
987 .get(&world, a)
988 .is_err());
989 }
990
991 #[test]
992 fn added_queries() {
993 let mut world = World::default();
994 let e1 = world.spawn(A(0)).id();
995
996 fn get_added<Com: Component>(world: &mut World) -> Vec<Entity> {
997 world
998 .query_filtered::<Entity, Added<Com>>()
999 .iter(world)
1000 .collect::<Vec<Entity>>()
1001 }
1002
1003 assert_eq!(get_added::<A>(&mut world), vec![e1]);
1004 world.entity_mut(e1).insert(B(0));
1005 assert_eq!(get_added::<A>(&mut world), vec![e1]);
1006 assert_eq!(get_added::<B>(&mut world), vec![e1]);
1007
1008 world.clear_trackers();
1009 assert!(get_added::<A>(&mut world).is_empty());
1010 let e2 = world.spawn((A(1), B(1))).id();
1011 assert_eq!(get_added::<A>(&mut world), vec![e2]);
1012 assert_eq!(get_added::<B>(&mut world), vec![e2]);
1013
1014 let added = world
1015 .query_filtered::<Entity, (Added<A>, Added<B>)>()
1016 .iter(&world)
1017 .collect::<Vec<Entity>>();
1018 assert_eq!(added, vec![e2]);
1019 }
1020
1021 #[test]
1022 fn changed_trackers() {
1023 let mut world = World::default();
1024 let e1 = world.spawn((A(0), B(0))).id();
1025 let e2 = world.spawn((A(0), B(0))).id();
1026 let e3 = world.spawn((A(0), B(0))).id();
1027 world.spawn((A(0), B(0)));
1028
1029 world.clear_trackers();
1030
1031 for (i, mut a) in world.query::<&mut A>().iter_mut(&mut world).enumerate() {
1032 if i % 2 == 0 {
1033 a.0 += 1;
1034 }
1035 }
1036
1037 fn get_filtered<F: QueryFilter>(world: &mut World) -> EntityHashSet {
1038 world
1039 .query_filtered::<Entity, F>()
1040 .iter(world)
1041 .collect::<EntityHashSet>()
1042 }
1043
1044 assert_eq!(
1045 get_filtered::<Changed<A>>(&mut world),
1046 [e1, e3].into_iter().collect::<EntityHashSet>()
1047 );
1048
1049 world.entity_mut(e1).insert(C);
1051
1052 assert_eq!(
1053 get_filtered::<Changed<A>>(&mut world),
1054 [e3, e1].into_iter().collect::<EntityHashSet>(),
1055 "changed entities list should not change"
1056 );
1057
1058 world.entity_mut(e1).insert((A(0), B(0)));
1060
1061 assert_eq!(
1062 get_filtered::<Changed<A>>(&mut world),
1063 [e3, e1].into_iter().collect::<EntityHashSet>(),
1064 "changed entities list should not change"
1065 );
1066
1067 assert!(world.despawn(e2));
1069 assert_eq!(
1070 get_filtered::<Changed<A>>(&mut world),
1071 [e3, e1].into_iter().collect::<EntityHashSet>(),
1072 "changed entities list should not change"
1073 );
1074
1075 assert!(world.despawn(e1));
1077 assert_eq!(
1078 get_filtered::<Changed<A>>(&mut world),
1079 [e3].into_iter().collect::<EntityHashSet>(),
1080 "e1 should no longer be returned"
1081 );
1082
1083 world.clear_trackers();
1084
1085 assert!(get_filtered::<Changed<A>>(&mut world).is_empty());
1086
1087 let e4 = world.spawn_empty().id();
1088
1089 world.entity_mut(e4).insert(A(0));
1090 assert_eq!(
1091 get_filtered::<Changed<A>>(&mut world),
1092 [e4].into_iter().collect::<EntityHashSet>()
1093 );
1094 assert_eq!(
1095 get_filtered::<Added<A>>(&mut world),
1096 [e4].into_iter().collect::<EntityHashSet>()
1097 );
1098
1099 world.entity_mut(e4).insert(A(1));
1100 assert_eq!(
1101 get_filtered::<Changed<A>>(&mut world),
1102 [e4].into_iter().collect::<EntityHashSet>()
1103 );
1104
1105 world.clear_trackers();
1106
1107 world.entity_mut(e4).insert((A(0), B(0)));
1110
1111 assert!(get_filtered::<Added<A>>(&mut world).is_empty());
1112 assert_eq!(
1113 get_filtered::<Changed<A>>(&mut world),
1114 [e4].into_iter().collect::<EntityHashSet>()
1115 );
1116 assert_eq!(
1117 get_filtered::<Added<B>>(&mut world),
1118 [e4].into_iter().collect::<EntityHashSet>()
1119 );
1120 assert_eq!(
1121 get_filtered::<Changed<B>>(&mut world),
1122 [e4].into_iter().collect::<EntityHashSet>()
1123 );
1124 }
1125
1126 #[test]
1127 fn changed_trackers_sparse() {
1128 let mut world = World::default();
1129 let e1 = world.spawn(SparseStored(0)).id();
1130 let e2 = world.spawn(SparseStored(0)).id();
1131 let e3 = world.spawn(SparseStored(0)).id();
1132 world.spawn(SparseStored(0));
1133
1134 world.clear_trackers();
1135
1136 for (i, mut a) in world
1137 .query::<&mut SparseStored>()
1138 .iter_mut(&mut world)
1139 .enumerate()
1140 {
1141 if i % 2 == 0 {
1142 a.0 += 1;
1143 }
1144 }
1145
1146 fn get_filtered<F: QueryFilter>(world: &mut World) -> EntityHashSet {
1147 world
1148 .query_filtered::<Entity, F>()
1149 .iter(world)
1150 .collect::<EntityHashSet>()
1151 }
1152
1153 assert_eq!(
1154 get_filtered::<Changed<SparseStored>>(&mut world),
1155 [e1, e3].into_iter().collect::<EntityHashSet>()
1156 );
1157
1158 world.entity_mut(e1).insert(C);
1160
1161 assert_eq!(get_filtered::<Changed<SparseStored>>(&mut world), [e3, e1].into_iter().collect::<EntityHashSet>(), "changed entities list should not change (although the order will due to archetype moves)");
1162
1163 world.entity_mut(e1).insert(SparseStored(0));
1165 assert_eq!(
1166 get_filtered::<Changed<SparseStored>>(&mut world),
1167 [e3, e1].into_iter().collect::<EntityHashSet>(),
1168 "changed entities list should not change"
1169 );
1170
1171 assert!(world.despawn(e2));
1173 assert_eq!(
1174 get_filtered::<Changed<SparseStored>>(&mut world),
1175 [e3, e1].into_iter().collect::<EntityHashSet>(),
1176 "changed entities list should not change"
1177 );
1178
1179 assert!(world.despawn(e1));
1181 assert_eq!(
1182 get_filtered::<Changed<SparseStored>>(&mut world),
1183 [e3].into_iter().collect::<EntityHashSet>(),
1184 "e1 should no longer be returned"
1185 );
1186
1187 world.clear_trackers();
1188
1189 assert!(get_filtered::<Changed<SparseStored>>(&mut world).is_empty());
1190
1191 let e4 = world.spawn_empty().id();
1192
1193 world.entity_mut(e4).insert(SparseStored(0));
1194 assert_eq!(
1195 get_filtered::<Changed<SparseStored>>(&mut world),
1196 [e4].into_iter().collect::<EntityHashSet>()
1197 );
1198 assert_eq!(
1199 get_filtered::<Added<SparseStored>>(&mut world),
1200 [e4].into_iter().collect::<EntityHashSet>()
1201 );
1202
1203 world.entity_mut(e4).insert(A(1));
1204 assert_eq!(
1205 get_filtered::<Changed<SparseStored>>(&mut world),
1206 [e4].into_iter().collect::<EntityHashSet>()
1207 );
1208
1209 world.clear_trackers();
1210
1211 world.entity_mut(e4).insert(SparseStored(0));
1214
1215 assert!(get_filtered::<Added<SparseStored>>(&mut world).is_empty());
1216 assert_eq!(
1217 get_filtered::<Changed<SparseStored>>(&mut world),
1218 [e4].into_iter().collect::<EntityHashSet>()
1219 );
1220 }
1221
1222 #[test]
1223 fn empty_spawn() {
1224 let mut world = World::default();
1225 let e = world.spawn_empty().id();
1226 let mut e_mut = world.entity_mut(e);
1227 e_mut.insert(A(0));
1228 assert_eq!(e_mut.get::<A>().unwrap(), &A(0));
1229 }
1230
1231 #[test]
1232 fn changed_query() {
1233 let mut world = World::default();
1234 let e1 = world.spawn((A(0), B(0))).id();
1235
1236 fn get_changed(world: &mut World) -> Vec<Entity> {
1237 world
1238 .query_filtered::<Entity, Changed<A>>()
1239 .iter(world)
1240 .collect::<Vec<Entity>>()
1241 }
1242 assert_eq!(get_changed(&mut world), vec![e1]);
1243 world.clear_trackers();
1244 assert_eq!(get_changed(&mut world), vec![]);
1245 *world.get_mut(e1).unwrap() = A(1);
1246 assert_eq!(get_changed(&mut world), vec![e1]);
1247 }
1248
1249 #[test]
1250 fn resource() {
1251 use crate::resource::Resource;
1252
1253 #[derive(Resource, PartialEq, Debug)]
1254 struct Num(i32);
1255
1256 #[derive(Resource, PartialEq, Debug)]
1257 struct BigNum(u64);
1258 let mut world = World::default();
1259 assert!(world.get_resource::<Num>().is_none());
1260 assert!(!world.contains_resource::<Num>());
1261 assert!(!world.is_resource_added::<Num>());
1262 assert!(!world.is_resource_changed::<Num>());
1263
1264 world.insert_resource(Num(123));
1265 let resource_id = world.components().get_id(TypeId::of::<Num>()).unwrap();
1266
1267 assert_eq!(world.resource::<Num>().0, 123);
1268 assert!(world.contains_resource::<Num>());
1269 assert!(world.is_resource_added::<Num>());
1270 assert!(world.is_resource_changed::<Num>());
1271
1272 world.insert_resource(BigNum(456));
1273 assert_eq!(world.resource::<BigNum>().0, 456u64);
1274
1275 world.insert_resource(BigNum(789));
1276 assert_eq!(world.resource::<BigNum>().0, 789);
1277
1278 {
1279 let mut value = world.resource_mut::<BigNum>();
1280 assert_eq!(value.0, 789);
1281 value.0 = 10;
1282 }
1283
1284 assert_eq!(
1285 world.resource::<BigNum>().0,
1286 10,
1287 "resource changes are preserved"
1288 );
1289
1290 assert_eq!(
1291 world.remove_resource::<BigNum>(),
1292 Some(BigNum(10)),
1293 "removed resource has the correct value"
1294 );
1295 assert_eq!(
1296 world.get_resource::<BigNum>(),
1297 None,
1298 "removed resource no longer exists"
1299 );
1300 assert_eq!(
1301 world.remove_resource::<BigNum>(),
1302 None,
1303 "double remove returns nothing"
1304 );
1305
1306 world.insert_resource(BigNum(1));
1307 assert_eq!(
1308 world.get_resource::<BigNum>(),
1309 Some(&BigNum(1)),
1310 "re-inserting resources works"
1311 );
1312
1313 assert_eq!(
1314 world.get_resource::<Num>(),
1315 Some(&Num(123)),
1316 "other resources are unaffected"
1317 );
1318
1319 let current_resource_id = world.components().get_id(TypeId::of::<Num>()).unwrap();
1320 assert_eq!(
1321 resource_id, current_resource_id,
1322 "resource id does not change after removing / re-adding"
1323 );
1324 }
1325
1326 #[test]
1327 fn remove() {
1328 let mut world = World::default();
1329 let e1 = world.spawn((A(1), B(1), TableStored("a"))).id();
1330
1331 let mut e = world.entity_mut(e1);
1332 assert_eq!(e.get::<TableStored>(), Some(&TableStored("a")));
1333 assert_eq!(e.get::<A>(), Some(&A(1)));
1334 assert_eq!(e.get::<B>(), Some(&B(1)));
1335 assert_eq!(
1336 e.get::<C>(),
1337 None,
1338 "C is not in the entity, so it should not exist"
1339 );
1340
1341 e.remove::<(A, B, C)>();
1342 assert_eq!(
1343 e.get::<TableStored>(),
1344 Some(&TableStored("a")),
1345 "TableStored is not in the removed bundle, so it should exist"
1346 );
1347 assert_eq!(
1348 e.get::<A>(),
1349 None,
1350 "Num is in the removed bundle, so it should not exist"
1351 );
1352 assert_eq!(
1353 e.get::<B>(),
1354 None,
1355 "f64 is in the removed bundle, so it should not exist"
1356 );
1357 assert_eq!(
1358 e.get::<C>(),
1359 None,
1360 "usize is in the removed bundle, so it should not exist"
1361 );
1362 }
1363
1364 #[test]
1365 fn take() {
1366 let mut world = World::default();
1367 world.spawn((A(1), B(1), TableStored("1")));
1368 let e2 = world.spawn((A(2), B(2), TableStored("2"))).id();
1369 world.spawn((A(3), B(3), TableStored("3")));
1370
1371 let mut query = world.query::<(&B, &TableStored)>();
1372 let results = query
1373 .iter(&world)
1374 .map(|(a, b)| (a.0, b.0))
1375 .collect::<HashSet<_>>();
1376 assert_eq!(
1377 results,
1378 [(1, "1"), (2, "2"), (3, "3"),]
1379 .into_iter()
1380 .collect::<HashSet<_>>()
1381 );
1382
1383 let removed_bundle = world.entity_mut(e2).take::<(B, TableStored)>().unwrap();
1384 assert_eq!(removed_bundle, (B(2), TableStored("2")));
1385
1386 let results = query
1387 .iter(&world)
1388 .map(|(a, b)| (a.0, b.0))
1389 .collect::<HashSet<_>>();
1390 assert_eq!(
1391 results,
1392 [(1, "1"), (3, "3"),].into_iter().collect::<HashSet<_>>()
1393 );
1394
1395 let mut a_query = world.query::<&A>();
1396 let results = a_query.iter(&world).map(|a| a.0).collect::<HashSet<_>>();
1397 assert_eq!(results, [1, 3, 2].into_iter().collect::<HashSet<_>>());
1398
1399 let entity_ref = world.entity(e2);
1400 assert_eq!(
1401 entity_ref.get::<A>(),
1402 Some(&A(2)),
1403 "A is not in the removed bundle, so it should exist"
1404 );
1405 assert_eq!(
1406 entity_ref.get::<B>(),
1407 None,
1408 "B is in the removed bundle, so it should not exist"
1409 );
1410 assert_eq!(
1411 entity_ref.get::<TableStored>(),
1412 None,
1413 "TableStored is in the removed bundle, so it should not exist"
1414 );
1415 }
1416
1417 #[test]
1418 fn non_send() {
1419 let mut world = World::default();
1420 world.insert_non_send(123i32);
1421 world.insert_non_send(456i64);
1422 assert_eq!(*world.non_send::<i32>(), 123);
1423 assert_eq!(*world.non_send_mut::<i64>(), 456);
1424 }
1425
1426 #[test]
1427 fn non_send_points_to_distinct_data() {
1428 let mut world = World::default();
1429 world.insert_resource(ResA(123));
1430 world.insert_non_send(ResA(456));
1431 assert_eq!(*world.resource::<ResA>(), ResA(123));
1432 assert_eq!(*world.non_send::<ResA>(), ResA(456));
1433 }
1434
1435 #[test]
1436 #[should_panic]
1437 fn non_send_panic() {
1438 let mut world = World::default();
1439 world.insert_non_send(0i32);
1440 std::thread::spawn(move || {
1441 let _ = world.non_send_mut::<i32>();
1442 })
1443 .join()
1444 .unwrap();
1445 }
1446
1447 #[test]
1448 fn exact_size_query() {
1449 let mut world = World::default();
1450 world.spawn((A(0), B(0)));
1451 world.spawn((A(0), B(0)));
1452 world.spawn((A(0), B(0), C));
1453 world.spawn(C);
1454
1455 let mut query = world.query::<(&A, &B)>();
1456 assert_eq!(query.iter(&world).len(), 3);
1457 }
1458
1459 #[test]
1460 #[should_panic]
1461 fn duplicate_components_panic() {
1462 let mut world = World::new();
1463 world.spawn((A(1), A(2)));
1464 }
1465
1466 #[test]
1467 #[should_panic]
1468 fn ref_and_mut_query_panic() {
1469 let mut world = World::new();
1470 world.query::<(&A, &mut A)>();
1471 }
1472
1473 #[test]
1474 #[should_panic]
1475 fn entity_ref_and_mut_query_panic() {
1476 let mut world = World::new();
1477 world.query::<(EntityRef, &mut A)>();
1478 }
1479
1480 #[test]
1481 #[should_panic]
1482 fn mut_and_ref_query_panic() {
1483 let mut world = World::new();
1484 world.query::<(&mut A, &A)>();
1485 }
1486
1487 #[test]
1488 #[should_panic]
1489 fn mut_and_entity_ref_query_panic() {
1490 let mut world = World::new();
1491 world.query::<(&mut A, EntityRef)>();
1492 }
1493
1494 #[test]
1495 #[should_panic]
1496 fn entity_ref_and_entity_mut_query_panic() {
1497 let mut world = World::new();
1498 world.query::<(EntityRef, EntityMut)>();
1499 }
1500
1501 #[test]
1502 #[should_panic]
1503 fn entity_mut_and_entity_mut_query_panic() {
1504 let mut world = World::new();
1505 world.query::<(EntityMut, EntityMut)>();
1506 }
1507
1508 #[test]
1509 fn entity_ref_and_entity_ref_query_no_panic() {
1510 let mut world = World::new();
1511 world.query::<(EntityRef, EntityRef)>();
1512 }
1513
1514 #[test]
1515 #[should_panic]
1516 fn mut_and_mut_query_panic() {
1517 let mut world = World::new();
1518 world.query::<(&mut A, &mut A)>();
1519 }
1520
1521 #[test]
1522 #[should_panic]
1523 fn multiple_worlds_same_query_iter() {
1524 let mut world_a = World::new();
1525 let world_b = World::new();
1526 let mut query = world_a.query::<&A>();
1527 query.iter(&world_a);
1528 query.iter(&world_b);
1529 }
1530
1531 #[test]
1532 fn query_filters_dont_collide_with_fetches() {
1533 let mut world = World::new();
1534 world.query_filtered::<&mut A, Changed<A>>();
1535 }
1536
1537 #[test]
1538 fn filtered_query_access() {
1539 let mut world = World::new();
1540 world.remove_resource::<DefaultQueryFilters>();
1542 let query = world.query_filtered::<&mut A, Changed<B>>();
1543
1544 let mut expected = FilteredAccess::default();
1545 let a_id = world.components.get_id(TypeId::of::<A>()).unwrap();
1546 let b_id = world.components.get_id(TypeId::of::<B>()).unwrap();
1547 expected.add_write(a_id);
1548 expected.add_read(b_id);
1549 assert!(
1550 query.component_access.eq(&expected),
1551 "ComponentId access from query fetch and query filter should be combined"
1552 );
1553 }
1554
1555 #[test]
1556 #[should_panic]
1557 fn multiple_worlds_same_query_get() {
1558 let mut world_a = World::new();
1559 let world_b = World::new();
1560 let mut query = world_a.query::<&A>();
1561 let _ = query.get(&world_a, Entity::from_raw_u32(10_000).unwrap());
1562 let _ = query.get(&world_b, Entity::from_raw_u32(10_000).unwrap());
1563 }
1564
1565 #[test]
1566 #[should_panic]
1567 fn multiple_worlds_same_query_for_each() {
1568 let mut world_a = World::new();
1569 let world_b = World::new();
1570 let mut query = world_a.query::<&A>();
1571 query.iter(&world_a).for_each(|_| {});
1572 query.iter(&world_b).for_each(|_| {});
1573 }
1574
1575 #[test]
1576 fn resource_scope() {
1577 let mut world = World::default();
1578 assert!(world.try_resource_scope::<ResA, _>(|_, _| {}).is_none());
1579 world.insert_resource(ResA(0));
1580 world.resource_scope(|world: &mut World, mut value: Mut<ResA>| {
1581 value.0 += 1;
1582 assert!(!world.contains_resource::<ResA>());
1583 });
1584 assert_eq!(world.resource::<ResA>().0, 1);
1585 }
1586
1587 #[cfg(feature = "std")]
1588 #[test]
1589 fn resource_scope_unwind() {
1590 #[derive(Debug, PartialEq)]
1591 struct Panic;
1592
1593 let mut world = World::default();
1594 assert!(world.try_resource_scope::<ResA, _>(|_, _| {}).is_none());
1595 world.insert_resource(ResA(0));
1596 let panic = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
1597 world.resource_scope(|world: &mut World, _value: Mut<ResA>| {
1598 assert!(!world.contains_resource::<ResA>());
1599 std::panic::panic_any(Panic);
1600 });
1601 unreachable!();
1602 }));
1603 assert_eq!(panic.unwrap_err().downcast_ref::<Panic>(), Some(&Panic));
1604 assert!(world.contains_resource::<ResA>());
1605 }
1606
1607 #[test]
1608 fn resource_scope_resources_cleared() {
1609 let mut world = World::default();
1610 assert!(world.try_resource_scope::<ResA, _>(|_, _| {}).is_none());
1611 world.insert_resource(ResA(0));
1612 let r = world.try_resource_scope(|world: &mut World, _value: Mut<ResA>| {
1613 assert!(!world.contains_resource::<ResA>());
1614 world.clear_resources();
1615 });
1616 assert_eq!(r, Some(()));
1617 assert!(world.contains_resource::<ResA>());
1618 }
1619
1620 #[test]
1621 fn non_send_drop_from_different_thread() {
1622 struct MustNotDrop;
1623 impl Drop for MustNotDrop {
1624 fn drop(&mut self) {
1625 panic!("Must not be dropped");
1626 }
1627 }
1628
1629 let mut world = World::default();
1630 world.insert_non_send(MustNotDrop);
1631
1632 let thread = std::thread::spawn(move || {
1633 drop(world);
1636 });
1637
1638 if let Err(err) = thread.join() {
1639 std::panic::resume_unwind(err);
1640 }
1641 }
1642
1643 #[test]
1644 fn non_send_drop_from_same_thread() {
1645 let mut world = World::default();
1646 let (dropck, dropped) = DropCk::new_pair();
1647 world.insert_non_send(dropck);
1648 drop(world);
1649 assert_eq!(1, dropped.load(Ordering::Relaxed));
1651
1652 let mut world = World::default();
1653 let (dropck, dropped) = DropCk::new_pair();
1654 world.insert_non_send(dropck);
1655 let _removed = world.remove_non_send::<DropCk>();
1656 drop(world);
1657 assert_eq!(0, dropped.load(Ordering::Relaxed));
1659 }
1660
1661 #[test]
1662 fn insert_overwrite_drop() {
1663 let (dropck1, dropped1) = DropCk::new_pair();
1664 let (dropck2, dropped2) = DropCk::new_pair();
1665 let mut world = World::default();
1666 world.spawn(dropck1).insert(dropck2);
1667 assert_eq!(dropped1.load(Ordering::Relaxed), 1);
1668 assert_eq!(dropped2.load(Ordering::Relaxed), 0);
1669 drop(world);
1670 assert_eq!(dropped1.load(Ordering::Relaxed), 1);
1671 assert_eq!(dropped2.load(Ordering::Relaxed), 1);
1672 }
1673
1674 #[test]
1675 fn insert_overwrite_drop_sparse() {
1676 let (dropck1, dropped1) = DropCk::new_pair();
1677 let (dropck2, dropped2) = DropCk::new_pair();
1678 let mut world = World::default();
1679
1680 world
1681 .spawn(DropCkSparse(dropck1))
1682 .insert(DropCkSparse(dropck2));
1683 assert_eq!(dropped1.load(Ordering::Relaxed), 1);
1684 assert_eq!(dropped2.load(Ordering::Relaxed), 0);
1685 drop(world);
1686 assert_eq!(dropped1.load(Ordering::Relaxed), 1);
1687 assert_eq!(dropped2.load(Ordering::Relaxed), 1);
1688 }
1689
1690 #[test]
1691 fn clear_entities() {
1692 let mut world = World::default();
1693
1694 world.insert_resource(ResA(0));
1695 world.spawn(A(1));
1696 world.spawn(SparseStored(1));
1697
1698 let mut q1 = world.query::<&A>();
1699 let mut q2 = world.query::<&SparseStored>();
1700
1701 assert_eq!(q1.query(&world).count(), 1);
1702 assert_eq!(q2.query(&world).count(), 1);
1703
1704 world.clear_entities();
1705
1706 assert_eq!(
1707 q1.query(&world).count(),
1708 0,
1709 "world should not contain table components"
1710 );
1711 assert_eq!(
1712 q2.query(&world).count(),
1713 0,
1714 "world should not contain sparse set components"
1715 );
1716 assert_eq!(
1717 world.get_resource::<ResA>(),
1718 None,
1719 "world should not contain resources"
1720 );
1721 }
1722
1723 #[test]
1724 fn test_is_archetypal_size_hints() {
1725 let mut world = World::default();
1726 macro_rules! query_min_size {
1727 ($query:ty, $filter:ty) => {
1728 world
1729 .query_filtered::<$query, $filter>()
1730 .iter(&world)
1731 .size_hint()
1732 .0
1733 };
1734 }
1735
1736 world.spawn((A(1), B(1), C));
1737 world.spawn((A(1), C));
1738 world.spawn((A(1), B(1)));
1739 world.spawn((B(1), C));
1740 world.spawn(A(1));
1741 world.spawn(C);
1742 assert_eq!(2, query_min_size![(), (With<A>, Without<B>)]);
1743 assert_eq!(3, query_min_size![&B, Or<(With<A>, With<C>)>]);
1744 assert_eq!(1, query_min_size![&B, (With<A>, With<C>)]);
1745 assert_eq!(1, query_min_size![(&A, &B), With<C>]);
1746 assert_eq!(4, query_min_size![&A, ()], "Simple Archetypal");
1747 assert_eq!(4, query_min_size![Ref<A>, ()]);
1748 assert_eq!(0, query_min_size![(), Added<A>], "Simple Added");
1751 assert_eq!(0, query_min_size![(), Changed<A>], "Simple Changed");
1752 assert_eq!(0, query_min_size![(&A, &B), Changed<A>]);
1753 assert_eq!(0, query_min_size![&A, (Changed<A>, With<B>)]);
1754 assert_eq!(0, query_min_size![(&A, &B), Or<(Changed<A>, Changed<B>)>]);
1755 }
1756
1757 #[test]
1758 fn insert_batch() {
1759 let mut world = World::default();
1760 let e0 = world.spawn(A(0)).id();
1761 let e1 = world.spawn(B(0)).id();
1762
1763 let values = vec![(e0, (A(1), B(0))), (e1, (A(0), B(1)))];
1764
1765 world.insert_batch(values);
1766
1767 assert_eq!(
1768 world.get::<A>(e0),
1769 Some(&A(1)),
1770 "first entity's A component should have been replaced"
1771 );
1772 assert_eq!(
1773 world.get::<B>(e0),
1774 Some(&B(0)),
1775 "first entity should have received B component"
1776 );
1777 assert_eq!(
1778 world.get::<A>(e1),
1779 Some(&A(0)),
1780 "second entity should have received A component"
1781 );
1782 assert_eq!(
1783 world.get::<B>(e1),
1784 Some(&B(1)),
1785 "second entity's B component should have been replaced"
1786 );
1787 }
1788
1789 #[test]
1790 fn insert_batch_same_archetype() {
1791 let mut world = World::default();
1792 let e0 = world.spawn((A(0), B(0))).id();
1793 let e1 = world.spawn((A(0), B(0))).id();
1794 let e2 = world.spawn(B(0)).id();
1795
1796 let values = vec![(e0, (B(1), C)), (e1, (B(2), C)), (e2, (B(3), C))];
1797
1798 world.insert_batch(values);
1799 let mut query = world.query::<(Option<&A>, &B, &C)>();
1800 let component_values = query.get_many(&world, [e0, e1, e2]).unwrap();
1801
1802 assert_eq!(
1803 component_values,
1804 [(Some(&A(0)), &B(1), &C), (Some(&A(0)), &B(2), &C), (None, &B(3), &C)],
1805 "all entities should have had their B component replaced, received C component, and had their A component (or lack thereof) unchanged"
1806 );
1807 }
1808
1809 #[test]
1810 fn insert_batch_if_new() {
1811 let mut world = World::default();
1812 let e0 = world.spawn(A(0)).id();
1813 let e1 = world.spawn(B(0)).id();
1814
1815 let values = vec![(e0, (A(1), B(0))), (e1, (A(0), B(1)))];
1816
1817 world.insert_batch_if_new(values);
1818
1819 assert_eq!(
1820 world.get::<A>(e0),
1821 Some(&A(0)),
1822 "first entity's A component should not have been replaced"
1823 );
1824 assert_eq!(
1825 world.get::<B>(e0),
1826 Some(&B(0)),
1827 "first entity should have received B component"
1828 );
1829 assert_eq!(
1830 world.get::<A>(e1),
1831 Some(&A(0)),
1832 "second entity should have received A component"
1833 );
1834 assert_eq!(
1835 world.get::<B>(e1),
1836 Some(&B(0)),
1837 "second entity's B component should not have been replaced"
1838 );
1839 }
1840
1841 #[test]
1842 fn try_insert_batch() {
1843 let mut world = World::default();
1844 let e0 = world.spawn(A(0)).id();
1845 let e1 = Entity::from_raw_u32(10_000).unwrap();
1846
1847 let values = vec![(e0, (A(1), B(0))), (e1, (A(0), B(1)))];
1848
1849 let error = world.try_insert_batch(values).unwrap_err();
1850
1851 assert_eq!(e1, error.entities[0]);
1852
1853 assert_eq!(
1854 world.get::<A>(e0),
1855 Some(&A(1)),
1856 "first entity's A component should have been replaced"
1857 );
1858 assert_eq!(
1859 world.get::<B>(e0),
1860 Some(&B(0)),
1861 "first entity should have received B component"
1862 );
1863 }
1864
1865 #[test]
1866 fn try_insert_batch_if_new() {
1867 let mut world = World::default();
1868 let e0 = world.spawn(A(0)).id();
1869 let e1 = Entity::from_raw_u32(10_000).unwrap();
1870
1871 let values = vec![(e0, (A(1), B(0))), (e1, (A(0), B(1)))];
1872
1873 let error = world.try_insert_batch_if_new(values).unwrap_err();
1874
1875 assert_eq!(e1, error.entities[0]);
1876
1877 assert_eq!(
1878 world.get::<A>(e0),
1879 Some(&A(0)),
1880 "first entity's A component should not have been replaced"
1881 );
1882 assert_eq!(
1883 world.get::<B>(e0),
1884 Some(&B(0)),
1885 "first entity should have received B component"
1886 );
1887 }
1888
1889 #[derive(Default)]
1890 struct CaptureMapper(Vec<Entity>);
1891 impl EntityMapper for CaptureMapper {
1892 fn get_mapped(&mut self, source: Entity) -> Entity {
1893 self.0.push(source);
1894 source
1895 }
1896
1897 fn set_mapped(&mut self, _source: Entity, _target: Entity) {}
1898 }
1899
1900 #[test]
1901 fn map_struct_entities() {
1902 #[derive(Component)]
1903 #[expect(
1904 unused,
1905 reason = "extra fields are used to ensure the derive works properly"
1906 )]
1907 struct Foo(usize, #[entities] Entity);
1908
1909 #[derive(Component)]
1910 #[expect(
1911 unused,
1912 reason = "extra fields are used to ensure the derive works properly"
1913 )]
1914 struct Bar {
1915 #[entities]
1916 a: Entity,
1917 b: usize,
1918 #[entities]
1919 c: Vec<Entity>,
1920 }
1921
1922 let mut world = World::new();
1923 let e1 = world.spawn_empty().id();
1924 let e2 = world.spawn_empty().id();
1925 let e3 = world.spawn_empty().id();
1926
1927 let mut foo = Foo(1, e1);
1928 let mut mapper = CaptureMapper::default();
1929 Component::map_entities(&mut foo, &mut mapper);
1930 assert_eq!(&mapper.0, &[e1]);
1931
1932 let mut bar = Bar {
1933 a: e1,
1934 b: 1,
1935 c: vec![e2, e3],
1936 };
1937 let mut mapper = CaptureMapper::default();
1938 Component::map_entities(&mut bar, &mut mapper);
1939 assert_eq!(&mapper.0, &[e1, e2, e3]);
1940 }
1941
1942 #[test]
1943 fn map_enum_entities() {
1944 #[derive(Component)]
1945 #[expect(
1946 unused,
1947 reason = "extra fields are used to ensure the derive works properly"
1948 )]
1949 enum Foo {
1950 Bar(usize, #[entities] Entity),
1951 Baz {
1952 #[entities]
1953 a: Entity,
1954 b: usize,
1955 #[entities]
1956 c: Vec<Entity>,
1957 },
1958 }
1959
1960 let mut world = World::new();
1961 let e1 = world.spawn_empty().id();
1962 let e2 = world.spawn_empty().id();
1963 let e3 = world.spawn_empty().id();
1964
1965 let mut foo = Foo::Bar(1, e1);
1966 let mut mapper = CaptureMapper::default();
1967 Component::map_entities(&mut foo, &mut mapper);
1968 assert_eq!(&mapper.0, &[e1]);
1969
1970 let mut foo = Foo::Baz {
1971 a: e1,
1972 b: 1,
1973 c: vec![e2, e3],
1974 };
1975 let mut mapper = CaptureMapper::default();
1976 Component::map_entities(&mut foo, &mut mapper);
1977 assert_eq!(&mapper.0, &[e1, e2, e3]);
1978 }
1979
1980 #[expect(
1981 dead_code,
1982 reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
1983 )]
1984 #[derive(Component)]
1985 struct ComponentA(u32);
1986
1987 #[expect(
1988 dead_code,
1989 reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
1990 )]
1991 #[derive(Component)]
1992 struct ComponentB(u32);
1993
1994 #[derive(Bundle)]
1995 struct Simple(ComponentA);
1996
1997 #[expect(
1998 dead_code,
1999 reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
2000 )]
2001 #[derive(Bundle)]
2002 struct Tuple(Simple, ComponentB);
2003
2004 #[expect(
2005 dead_code,
2006 reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
2007 )]
2008 #[derive(Bundle)]
2009 struct Record {
2010 field0: Simple,
2011 field1: ComponentB,
2012 }
2013
2014 #[expect(
2015 dead_code,
2016 reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
2017 )]
2018 #[derive(Component)]
2019 struct MyEntities {
2020 #[entities]
2021 entities: Vec<Entity>,
2022 #[entities]
2023 another_one: Entity,
2024 #[entities]
2025 maybe_entity: Option<Entity>,
2026 something_else: String,
2027 }
2028
2029 #[expect(
2030 dead_code,
2031 reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
2032 )]
2033 #[derive(Component)]
2034 struct MyEntitiesTuple(#[entities] Vec<Entity>, #[entities] Entity, usize);
2035
2036 #[test]
2037 fn clone_entities() {
2038 use crate::entity::{ComponentCloneCtx, SourceComponent};
2039
2040 #[expect(
2041 dead_code,
2042 reason = "This struct is used as a compilation test to test the derive macros, and as such this field is intentionally never used."
2043 )]
2044 #[derive(Component)]
2045 #[component(clone_behavior = Ignore)]
2046 struct IgnoreClone;
2047
2048 #[expect(
2049 dead_code,
2050 reason = "This struct is used as a compilation test to test the derive macros, and as such this field is intentionally never used."
2051 )]
2052 #[derive(Component)]
2053 #[component(clone_behavior = Default)]
2054 struct DefaultClone;
2055
2056 #[expect(
2057 dead_code,
2058 reason = "This struct is used as a compilation test to test the derive macros, and as such this field is intentionally never used."
2059 )]
2060 #[derive(Component)]
2061 #[component(clone_behavior = Custom(custom_clone))]
2062 struct CustomClone;
2063
2064 #[expect(
2065 dead_code,
2066 reason = "This struct is used as a compilation test to test the derive macros, and as such this field is intentionally never used."
2067 )]
2068 #[derive(Component, Clone)]
2069 #[component(clone_behavior = clone::<Self>())]
2070 struct CloneFunction;
2071
2072 #[expect(
2073 dead_code,
2074 reason = "This struct is used as a compilation test to test the derive macros, and as such this field is intentionally never used."
2075 )]
2076 fn custom_clone(_source: &SourceComponent, _ctx: &mut ComponentCloneCtx) {}
2077 }
2078
2079 #[test]
2080 #[cfg_attr(miri, ignore = "This test takes ~460s on CI")]
2081 fn queue_register_component_toctou() {
2082 for _ in 0..1000 {
2083 let w = World::new();
2084
2085 std::thread::scope(|s| {
2086 let c1 = s.spawn(|| w.components_queue().queue_register_component::<A>());
2087 let c2 = s.spawn(|| w.components_queue().queue_register_component::<A>());
2088 assert_eq!(c1.join().unwrap(), c2.join().unwrap());
2089 });
2090 }
2091 }
2092}