diff --git a/Cargo.toml b/Cargo.toml index 7a0eec40f6..5156594211 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ bevy_audio = ["bevy_internal/bevy_audio"] bevy_dynamic_plugin = ["bevy_internal/bevy_dynamic_plugin"] bevy_gilrs = ["bevy_internal/bevy_gilrs"] bevy_gltf = ["bevy_internal/bevy_gltf"] -bevy_wgpu = ["bevy_internal/bevy_wgpu"] +bevy_wgpu = ["bevy_internal/bevy_wgpu"] bevy_winit = ["bevy_internal/bevy_winit"] trace_chrome = ["bevy_internal/trace_chrome"] diff --git a/benches/benches/bevy_ecs/commands.rs b/benches/benches/bevy_ecs/commands.rs index 620b1915f9..c4d974238d 100644 --- a/benches/benches/bevy_ecs/commands.rs +++ b/benches/benches/bevy_ecs/commands.rs @@ -1,4 +1,5 @@ use bevy::ecs::{ + component::Component, entity::Entity, system::{Command, CommandQueue, Commands}, world::World, @@ -18,8 +19,11 @@ criterion_group!( ); criterion_main!(benches); +#[derive(Component)] struct A; +#[derive(Component)] struct B; +#[derive(Component)] struct C; fn empty_commands(criterion: &mut Criterion) { @@ -79,10 +83,10 @@ fn spawn_commands(criterion: &mut Criterion) { group.finish(); } -#[derive(Default)] +#[derive(Default, Component)] struct Matrix([[f32; 4]; 4]); -#[derive(Default)] +#[derive(Default, Component)] struct Vec3([f32; 3]); fn insert_commands(criterion: &mut Criterion) { @@ -95,14 +99,16 @@ fn insert_commands(criterion: &mut Criterion) { let mut world = World::default(); let mut command_queue = CommandQueue::default(); let mut entities = Vec::new(); - for i in 0..entity_count { + for _ in 0..entity_count { entities.push(world.spawn().id()); } bencher.iter(|| { let mut commands = Commands::new(&mut command_queue, &world); for entity in entities.iter() { - commands.entity(*entity).insert_bundle((Matrix::default(), Vec3::default())); + commands + .entity(*entity) + .insert_bundle((Matrix::default(), Vec3::default())); } drop(commands); command_queue.apply(&mut world); @@ -112,7 +118,7 @@ fn insert_commands(criterion: &mut Criterion) { let mut world = World::default(); let mut command_queue = CommandQueue::default(); let mut entities = Vec::new(); - for i in 0..entity_count { + for _ in 0..entity_count { entities.push(world.spawn().id()); } diff --git a/benches/benches/bevy_ecs/stages.rs b/benches/benches/bevy_ecs/stages.rs index e4fce66f0f..8e572acff1 100644 --- a/benches/benches/bevy_ecs/stages.rs +++ b/benches/benches/bevy_ecs/stages.rs @@ -1,6 +1,7 @@ use bevy::ecs::{ + component::Component, schedule::{Stage, SystemStage}, - system::{IntoSystem, Query}, + system::Query, world::World, }; use criterion::{criterion_group, criterion_main, Criterion}; @@ -12,10 +13,15 @@ fn run_stage(stage: &mut SystemStage, world: &mut World) { stage.run(world); } +#[derive(Component)] struct A(f32); +#[derive(Component)] struct B(f32); +#[derive(Component)] struct C(f32); +#[derive(Component)] struct D(f32); +#[derive(Component)] struct E(f32); const ENTITY_BUNCH: usize = 5000; diff --git a/benches/benches/bevy_ecs/world_get.rs b/benches/benches/bevy_ecs/world_get.rs index 21aae5a8f4..fc943ede47 100644 --- a/benches/benches/bevy_ecs/world_get.rs +++ b/benches/benches/bevy_ecs/world_get.rs @@ -1,8 +1,4 @@ -use bevy::ecs::{ - component::{ComponentDescriptor, StorageType}, - entity::Entity, - world::World, -}; +use bevy::ecs::{component::Component, entity::Entity, world::World}; use criterion::{black_box, criterion_group, criterion_main, Criterion}; criterion_group!( @@ -15,16 +11,18 @@ criterion_group!( ); criterion_main!(benches); -struct A(f32); +#[derive(Component, Default)] +#[component(storage = "Table")] +struct Table(f32); +#[derive(Component, Default)] +#[component(storage = "SparseSet")] +struct Sparse(f32); const RANGE: std::ops::Range = 5..6; -fn setup(entity_count: u32, storage: StorageType) -> World { +fn setup(entity_count: u32) -> World { let mut world = World::default(); - world - .register_component(ComponentDescriptor::new::(storage)) - .unwrap(); - world.spawn_batch((0..entity_count).map(|_| (A(0.0),))); + world.spawn_batch((0..entity_count).map(|_| (T::default(),))); world } @@ -35,7 +33,7 @@ fn world_entity(criterion: &mut Criterion) { for entity_count in RANGE.map(|i| i * 10_000) { group.bench_function(format!("{}_entities", entity_count), |bencher| { - let world = setup(entity_count, StorageType::Table); + let world = setup::(entity_count); bencher.iter(|| { for i in 0..entity_count { @@ -55,21 +53,26 @@ fn world_get(criterion: &mut Criterion) { group.measurement_time(std::time::Duration::from_secs(4)); for entity_count in RANGE.map(|i| i * 10_000) { - for storage in [StorageType::Table, StorageType::SparseSet] { - group.bench_function( - format!("{}_entities_{:?}", entity_count, storage), - |bencher| { - let world = setup(entity_count, storage); + group.bench_function(format!("{}_entities_table", entity_count), |bencher| { + let world = setup::
(entity_count); - bencher.iter(|| { - for i in 0..entity_count { - let entity = Entity::new(i); - assert!(world.get::(entity).is_some()); - } - }); - }, - ); - } + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::new(i); + assert!(world.get::
(entity).is_some()); + } + }); + }); + group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| { + let world = setup::(entity_count); + + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::new(i); + assert!(world.get::(entity).is_some()); + } + }); + }); } group.finish(); @@ -81,22 +84,28 @@ fn world_query_get(criterion: &mut Criterion) { group.measurement_time(std::time::Duration::from_secs(4)); for entity_count in RANGE.map(|i| i * 10_000) { - for storage in [StorageType::Table, StorageType::SparseSet] { - group.bench_function( - format!("{}_entities_{:?}", entity_count, storage), - |bencher| { - let mut world = setup(entity_count, storage); - let mut query = world.query::<&A>(); + group.bench_function(format!("{}_entities_table", entity_count), |bencher| { + let mut world = setup::
(entity_count); + let mut query = world.query::<&Table>(); - bencher.iter(|| { - for i in 0..entity_count { - let entity = Entity::new(i); - assert!(query.get(&world, entity).is_ok()); - } - }); - }, - ); - } + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::new(i); + assert!(query.get(&world, entity).is_ok()); + } + }); + }); + group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| { + let mut world = setup::(entity_count); + let mut query = world.query::<&Sparse>(); + + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::new(i); + assert!(query.get(&world, entity).is_ok()); + } + }); + }); } group.finish(); @@ -108,24 +117,32 @@ fn world_query_iter(criterion: &mut Criterion) { group.measurement_time(std::time::Duration::from_secs(4)); for entity_count in RANGE.map(|i| i * 10_000) { - for storage in [StorageType::Table, StorageType::SparseSet] { - group.bench_function( - format!("{}_entities_{:?}", entity_count, storage), - |bencher| { - let mut world = setup(entity_count, storage); - let mut query = world.query::<&A>(); + group.bench_function(format!("{}_entities_table", entity_count), |bencher| { + let mut world = setup::
(entity_count); + let mut query = world.query::<&Table>(); - bencher.iter(|| { - let mut count = 0; - for comp in query.iter(&world) { - black_box(comp); - count += 1; - } - assert_eq!(black_box(count), entity_count); - }); - }, - ); - } + bencher.iter(|| { + let mut count = 0; + for comp in query.iter(&world) { + black_box(comp); + count += 1; + } + assert_eq!(black_box(count), entity_count); + }); + }); + group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| { + let mut world = setup::(entity_count); + let mut query = world.query::<&Sparse>(); + + bencher.iter(|| { + let mut count = 0; + for comp in query.iter(&world) { + black_box(comp); + count += 1; + } + assert_eq!(black_box(count), entity_count); + }); + }); } group.finish(); @@ -137,24 +154,32 @@ fn world_query_for_each(criterion: &mut Criterion) { group.measurement_time(std::time::Duration::from_secs(4)); for entity_count in RANGE.map(|i| i * 10_000) { - for storage in [StorageType::Table, StorageType::SparseSet] { - group.bench_function( - format!("{}_entities_{:?}", entity_count, storage), - |bencher| { - let mut world = setup(entity_count, storage); - let mut query = world.query::<&A>(); + group.bench_function(format!("{}_entities_table", entity_count), |bencher| { + let mut world = setup::
(entity_count); + let mut query = world.query::<&Table>(); - bencher.iter(|| { - let mut count = 0; - query.for_each(&world, |comp| { - black_box(comp); - count += 1; - }); - assert_eq!(black_box(count), entity_count); - }); - }, - ); - } + bencher.iter(|| { + let mut count = 0; + query.for_each(&world, |comp| { + black_box(comp); + count += 1; + }); + assert_eq!(black_box(count), entity_count); + }); + }); + group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| { + let mut world = setup::(entity_count); + let mut query = world.query::<&Sparse>(); + + bencher.iter(|| { + let mut count = 0; + query.for_each(&world, |comp| { + black_box(comp); + count += 1; + }); + assert_eq!(black_box(count), entity_count); + }); + }); } group.finish(); diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 4146268905..4887bb5917 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1,14 +1,15 @@ use crate::{CoreStage, Events, Plugin, PluginGroup, PluginGroupBuilder, StartupStage}; use bevy_ecs::{ - component::{Component, ComponentDescriptor}, prelude::{FromWorld, IntoExclusiveSystem}, schedule::{ - IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, SystemSet, SystemStage, + IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, StateData, SystemSet, + SystemStage, }, + system::Resource, world::World, }; use bevy_utils::tracing::debug; -use std::{fmt::Debug, hash::Hash}; +use std::fmt::Debug; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; @@ -493,7 +494,7 @@ impl App { /// adding [State::get_driver] to additional stages you need it in. pub fn add_state(&mut self, initial: T) -> &mut Self where - T: Component + Debug + Clone + Eq + Hash, + T: StateData, { self.add_state_to_stage(CoreStage::Update, initial) } @@ -505,7 +506,7 @@ impl App { /// stages you need it in. pub fn add_state_to_stage(&mut self, stage: impl StageLabel, initial: T) -> &mut Self where - T: Component + Debug + Clone + Eq + Hash, + T: StateData, { self.insert_resource(State::new(initial)) .add_system_set_to_stage(stage, State::::get_driver()) @@ -582,7 +583,7 @@ impl App { /// ``` pub fn add_event(&mut self) -> &mut Self where - T: Component, + T: Resource, { self.init_resource::>() .add_system_to_stage(CoreStage::First, Events::::update_system) @@ -608,7 +609,7 @@ impl App { /// ``` pub fn insert_resource(&mut self, resource: T) -> &mut Self where - T: Component, + T: Resource, { self.world.insert_resource(resource); self @@ -810,18 +811,6 @@ impl App { self } - /// Registers a new component using the given [ComponentDescriptor]. - /// - /// Components do not need to be manually registered. This just provides a way to - /// override default configuration. Attempting to register a component with a type - /// that has already been used by [World] will result in an error. - /// - /// See [World::register_component] - pub fn register_component(&mut self, descriptor: ComponentDescriptor) -> &mut Self { - self.world.register_component(descriptor).unwrap(); - self - } - /// Adds the type `T` to the type registry resource. #[cfg(feature = "bevy_reflect")] pub fn register_type(&mut self) -> &mut Self { diff --git a/crates/bevy_asset/src/handle.rs b/crates/bevy_asset/src/handle.rs index ae2bb45c08..1a606c1a62 100644 --- a/crates/bevy_asset/src/handle.rs +++ b/crates/bevy_asset/src/handle.rs @@ -9,7 +9,7 @@ use crate::{ path::{AssetPath, AssetPathId}, Asset, Assets, }; -use bevy_ecs::reflect::ReflectComponent; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_reflect::{Reflect, ReflectDeserialize}; use bevy_utils::Uuid; use crossbeam_channel::{Receiver, Sender}; @@ -58,7 +58,7 @@ impl HandleId { /// /// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets) /// collection. -#[derive(Reflect)] +#[derive(Component, Reflect)] #[reflect(Component)] pub struct Handle where diff --git a/crates/bevy_asset/src/loader.rs b/crates/bevy_asset/src/loader.rs index 0ca85fa747..960208c77b 100644 --- a/crates/bevy_asset/src/loader.rs +++ b/crates/bevy_asset/src/loader.rs @@ -3,10 +3,7 @@ use crate::{ RefChangeChannel, }; use anyhow::Result; -use bevy_ecs::{ - component::Component, - system::{Res, ResMut}, -}; +use bevy_ecs::system::{Res, ResMut}; use bevy_reflect::{TypeUuid, TypeUuidDynamic}; use bevy_tasks::TaskPool; use bevy_utils::{BoxedFuture, HashMap}; @@ -146,7 +143,7 @@ impl<'a> LoadContext<'a> { /// The result of loading an asset of type `T` #[derive(Debug)] -pub struct AssetResult { +pub struct AssetResult { pub asset: Box, pub id: HandleId, pub version: usize, @@ -154,12 +151,12 @@ pub struct AssetResult { /// A channel to send and receive [AssetResult]s #[derive(Debug)] -pub struct AssetLifecycleChannel { +pub struct AssetLifecycleChannel { pub sender: Sender>, pub receiver: Receiver>, } -pub enum AssetLifecycleEvent { +pub enum AssetLifecycleEvent { Create(AssetResult), Free(HandleId), } @@ -193,7 +190,7 @@ impl AssetLifecycle for AssetLifecycleChannel { } } -impl Default for AssetLifecycleChannel { +impl Default for AssetLifecycleChannel { fn default() -> Self { let (sender, receiver) = crossbeam_channel::unbounded(); AssetLifecycleChannel { sender, receiver } diff --git a/crates/bevy_core/src/label.rs b/crates/bevy_core/src/label.rs index 6c99bc76d8..4b805641ef 100644 --- a/crates/bevy_core/src/label.rs +++ b/crates/bevy_core/src/label.rs @@ -1,4 +1,5 @@ use bevy_ecs::{ + component::Component, entity::Entity, query::Changed, reflect::ReflectComponent, @@ -13,7 +14,7 @@ use std::{ }; /// A collection of labels -#[derive(Default, Reflect)] +#[derive(Component, Default, Reflect)] #[reflect(Component)] pub struct Labels { labels: HashSet>, diff --git a/crates/bevy_core/src/name.rs b/crates/bevy_core/src/name.rs index 7390fd8b18..593c7ae202 100644 --- a/crates/bevy_core/src/name.rs +++ b/crates/bevy_core/src/name.rs @@ -1,4 +1,4 @@ -use bevy_ecs::reflect::ReflectComponent; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_reflect::Reflect; use bevy_utils::AHasher; use std::{ @@ -8,7 +8,7 @@ use std::{ }; /// Component used to identify an entity. Stores a hash for faster comparisons -#[derive(Debug, Clone, Reflect)] +#[derive(Component, Debug, Clone, Reflect)] #[reflect(Component)] pub struct Name { hash: u64, // TODO: Shouldn't be serialized diff --git a/crates/bevy_core/src/time/stopwatch.rs b/crates/bevy_core/src/time/stopwatch.rs index 8aa1e974c9..d03da20824 100644 --- a/crates/bevy_core/src/time/stopwatch.rs +++ b/crates/bevy_core/src/time/stopwatch.rs @@ -1,4 +1,4 @@ -use bevy_ecs::reflect::ReflectComponent; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_reflect::Reflect; use bevy_utils::Duration; @@ -23,7 +23,7 @@ use bevy_utils::Duration; /// assert!(stopwatch.paused()); /// assert_eq!(stopwatch.elapsed_secs(), 0.0); /// ``` -#[derive(Clone, Debug, Default, Reflect)] +#[derive(Component, Clone, Debug, Default, Reflect)] #[reflect(Component)] pub struct Stopwatch { elapsed: Duration, diff --git a/crates/bevy_core/src/time/timer.rs b/crates/bevy_core/src/time/timer.rs index f09f3f9504..b9bd0800de 100644 --- a/crates/bevy_core/src/time/timer.rs +++ b/crates/bevy_core/src/time/timer.rs @@ -1,5 +1,5 @@ use crate::Stopwatch; -use bevy_ecs::reflect::ReflectComponent; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_reflect::Reflect; use bevy_utils::Duration; @@ -10,7 +10,7 @@ use bevy_utils::Duration; /// exceeded, and can still be reset at any given point. /// /// Paused timers will not have elapsed time increased. -#[derive(Clone, Debug, Default, Reflect)] +#[derive(Component, Clone, Debug, Default, Reflect)] #[reflect(Component)] pub struct Timer { stopwatch: Stopwatch, diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index 8f01bc61db..9c39a46e62 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -34,10 +34,6 @@ rand = "0.8" name = "events" path = "examples/events.rs" -[[example]] -name = "component_storage" -path = "examples/component_storage.rs" - [[example]] name = "resources" path = "examples/resources.rs" diff --git a/crates/bevy_ecs/README.md b/crates/bevy_ecs/README.md index 478b23b915..8bdb28fa59 100644 --- a/crates/bevy_ecs/README.md +++ b/crates/bevy_ecs/README.md @@ -193,11 +193,15 @@ Components can be stored in: * **Tables**: Fast and cache friendly iteration, but slower adding and removing of components. This is the default storage type. * **Sparse Sets**: Fast adding and removing of components, but slower iteration. -Component storage types are configurable, and they default to table storage if the storage is not manually defined. The [`component_storage.rs`](examples/component_storage.rs) example shows how to configure the storage type for a component. +Component storage types are configurable, and they default to table storage if the storage is not manually defined. -```rust -// store Position components in Sparse Sets -world.register_component(ComponentDescriptor::new::(StorageType::SparseSet)); +```rs +#[derive(Component)] +struct TableStoredComponent; + +#[derive(Component)] +#[component(storage = "SparseSet")] +struct SparseStoredComponent; ``` ### Component Bundles diff --git a/crates/bevy_ecs/examples/change_detection.rs b/crates/bevy_ecs/examples/change_detection.rs index ec80dcb939..1a3e135b65 100644 --- a/crates/bevy_ecs/examples/change_detection.rs +++ b/crates/bevy_ecs/examples/change_detection.rs @@ -46,7 +46,7 @@ struct EntityCounter { } // This struct represents a Component and holds the age in frames of the entity it gets assigned to -#[derive(Default, Debug)] +#[derive(Component, Default, Debug)] struct Age { frames: i32, } diff --git a/crates/bevy_ecs/examples/component_storage.rs b/crates/bevy_ecs/examples/component_storage.rs deleted file mode 100644 index 018f7afd47..0000000000 --- a/crates/bevy_ecs/examples/component_storage.rs +++ /dev/null @@ -1,39 +0,0 @@ -use bevy_ecs::{ - component::{ComponentDescriptor, StorageType}, - prelude::*, -}; - -// This example shows how to configure the storage of Components. -// A system demonstrates that querying for components is independent of their storage type. -fn main() { - let mut world = World::new(); - - // Store components of type `i32` in a Sparse set - world - .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) - .expect("The component of type i32 is already in use"); - - // Components of type i32 will have the above configured Sparse set storage, - // while f64 components will have the default table storage - world.spawn().insert(1).insert(0.1); - world.spawn().insert(2); - world.spawn().insert(0.2); - - // Setup a schedule and stage to add a system querying for the just spawned entities - let mut schedule = Schedule::default(); - let mut update = SystemStage::parallel(); - update.add_system(query_entities); - schedule.add_stage("update", update); - - schedule.run(&mut world); -} - -// The storage type does not matter for how to query in systems -fn query_entities(entities_with_i32: Query<&i32>, entities_with_f64: Query<&f64>) { - for value in entities_with_i32.iter() { - println!("Got entity with i32: {}", value); - } - for value in entities_with_f64.iter() { - println!("Got entity with f64: {}", value); - } -} diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs new file mode 100644 index 0000000000..446210d066 --- /dev/null +++ b/crates/bevy_ecs/macros/src/component.rs @@ -0,0 +1,102 @@ +use bevy_macro_utils::{get_lit_str, Symbol}; +use proc_macro::TokenStream; +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::{quote, ToTokens}; +use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Ident, Path, Result}; + +pub fn derive_component(input: TokenStream) -> TokenStream { + let mut ast = parse_macro_input!(input as DeriveInput); + let bevy_ecs_path: Path = crate::bevy_ecs_path(); + + let attrs = match parse_component_attr(&ast) { + Ok(attrs) => attrs, + Err(e) => return e.into_compile_error().into(), + }; + + let storage = storage_path(bevy_ecs_path.clone(), attrs.storage); + + ast.generics + .make_where_clause() + .predicates + .push(parse_quote! { Self: Send + Sync + 'static }); + + let struct_name = &ast.ident; + let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + + TokenStream::from(quote! { + impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause { + type Storage = #storage; + } + }) +} + +pub const COMPONENT: Symbol = Symbol("component"); +pub const STORAGE: Symbol = Symbol("storage"); + +struct Attrs { + storage: StorageTy, +} + +#[derive(Clone, Copy)] +enum StorageTy { + Table, + SparseSet, +} + +fn parse_component_attr(ast: &DeriveInput) -> Result { + let meta_items = bevy_macro_utils::parse_attrs(ast, COMPONENT)?; + + let mut attrs = Attrs { + storage: StorageTy::Table, + }; + + for meta in meta_items { + use syn::{ + Meta::NameValue, + NestedMeta::{Lit, Meta}, + }; + match meta { + Meta(NameValue(m)) if m.path == STORAGE => { + attrs.storage = match get_lit_str(STORAGE, &m.lit)?.value().as_str() { + "Table" => StorageTy::Table, + "SparseSet" => StorageTy::SparseSet, + s => { + return Err(Error::new_spanned( + m.lit, + format!( + "Invalid storage type `{}`, expected 'table' or 'sparse'.", + s + ), + )) + } + }; + } + Meta(meta_item) => { + return Err(Error::new_spanned( + meta_item.path(), + format!( + "unknown component attribute `{}`", + meta_item.path().into_token_stream().to_string() + ), + )); + } + Lit(lit) => { + return Err(Error::new_spanned( + lit, + "unexpected literal in component attribute", + )) + } + } + } + + Ok(attrs) +} + +fn storage_path(bevy_ecs_path: Path, ty: StorageTy) -> TokenStream2 { + let typename = match ty { + StorageTy::Table => Ident::new("TableStorage", Span::call_site()), + StorageTy::SparseSet => Ident::new("SparseStorage", Span::call_site()), + }; + + quote! { #bevy_ecs_path::component::#typename } +} diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 8371de3e4b..e508c7ac2c 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -1,5 +1,7 @@ extern crate proc_macro; +mod component; + use bevy_macro_utils::BevyManifest; use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; @@ -118,7 +120,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { { if *is_bundle { field_component_ids.push(quote! { - component_ids.extend(<#field_type as #ecs_path::bundle::Bundle>::component_ids(components)); + component_ids.extend(<#field_type as #ecs_path::bundle::Bundle>::component_ids(components, storages)); }); field_get_components.push(quote! { self.#field.get_components(&mut func); @@ -128,7 +130,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { }); } else { field_component_ids.push(quote! { - component_ids.push(components.get_or_insert_id::<#field_type>()); + component_ids.push(components.init_component::<#field_type>(storages)); }); field_get_components.push(quote! { func((&mut self.#field as *mut #field_type).cast::()); @@ -145,10 +147,11 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { let struct_name = &ast.ident; TokenStream::from(quote! { - /// SAFE: TypeInfo is returned in field-definition-order. [from_components] and [get_components] use field-definition-order + /// SAFE: ComponentId is returned in field-definition-order. [from_components] and [get_components] use field-definition-order unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name#ty_generics #where_clause { fn component_ids( components: &mut #ecs_path::component::Components, + storages: &mut #ecs_path::storage::Storages, ) -> Vec<#ecs_path::component::ComponentId> { let mut component_ids = Vec::with_capacity(#field_len); #(#field_component_ids)* @@ -468,6 +471,11 @@ fn derive_label(input: DeriveInput, label_type: Ident) -> TokenStream2 { } } -fn bevy_ecs_path() -> syn::Path { +pub(crate) fn bevy_ecs_path() -> syn::Path { BevyManifest::default().get_path("bevy_ecs") } + +#[proc_macro_derive(Component, attributes(component))] +pub fn derive_component(input: TokenStream) -> TokenStream { + component::derive_component(input) +} diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index cfb9f96c10..463a0483cf 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -29,8 +29,11 @@ use std::{any::TypeId, collections::HashMap}; /// /// ``` /// # use bevy_ecs::prelude::*; +/// # #[derive(Component)] /// # struct ComponentA; +/// # #[derive(Component)] /// # struct ComponentB; +/// # #[derive(Component)] /// # struct ComponentC; /// # /// #[derive(Bundle)] @@ -43,19 +46,26 @@ use std::{any::TypeId, collections::HashMap}; /// /// You can nest bundles using the `#[bundle]` attribute: /// ``` -/// # use bevy_ecs::bundle::Bundle; +/// # use bevy_ecs::{component::Component, bundle::Bundle}; +/// +/// #[derive(Component)] +/// struct X(i32); +/// #[derive(Component)] +/// struct Y(u64); +/// #[derive(Component)] +/// struct Z(String); /// /// #[derive(Bundle)] /// struct A { -/// x: i32, -/// y: u64, +/// x: X, +/// y: Y, /// } /// /// #[derive(Bundle)] /// struct B { /// #[bundle] /// a: A, -/// z: String, +/// z: Z, /// } /// ``` /// @@ -67,7 +77,7 @@ use std::{any::TypeId, collections::HashMap}; /// [Bundle::component_ids]. pub unsafe trait Bundle: Send + Sync + 'static { /// Gets this [Bundle]'s component ids, in the order of this bundle's Components - fn component_ids(components: &mut Components) -> Vec; + fn component_ids(components: &mut Components, storages: &mut Storages) -> Vec; /// Calls `func`, which should return data for each component in the bundle, in the order of /// this bundle's Components @@ -87,11 +97,11 @@ pub unsafe trait Bundle: Send + Sync + 'static { macro_rules! tuple_impl { ($($name: ident),*) => { - /// SAFE: TypeInfo is returned in tuple-order. [Bundle::from_components] and [Bundle::get_components] use tuple-order + /// SAFE: Component is returned in tuple-order. [Bundle::from_components] and [Bundle::get_components] use tuple-order unsafe impl<$($name: Component),*> Bundle for ($($name,)*) { #[allow(unused_variables)] - fn component_ids(components: &mut Components) -> Vec { - vec![$(components.get_or_insert_id::<$name>()),*] + fn component_ids(components: &mut Components, storages: &mut Storages) -> Vec { + vec![$(components.init_component::<$name>(storages)),*] } #[allow(unused_variables, unused_mut)] @@ -318,10 +328,7 @@ impl BundleInfo { let component_info = unsafe { components.get_info_unchecked(component_id) }; match component_info.storage_type() { StorageType::Table => new_table_components.push(component_id), - StorageType::SparseSet => { - storages.sparse_sets.get_or_insert(component_info); - new_sparse_set_components.push(component_id) - } + StorageType::SparseSet => new_sparse_set_components.push(component_id), } } } @@ -585,10 +592,11 @@ impl Bundles { pub(crate) fn init_info<'a, T: Bundle>( &'a mut self, components: &mut Components, + storages: &mut Storages, ) -> &'a BundleInfo { let bundle_infos = &mut self.bundle_infos; let id = self.bundle_ids.entry(TypeId::of::()).or_insert_with(|| { - let component_ids = T::component_ids(components); + let component_ids = T::component_ids(components, storages); let id = BundleId(bundle_infos.len()); // SAFE: T::component_id ensures info was created let bundle_info = unsafe { diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index 3cf2fecc49..3b59303de4 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -1,6 +1,6 @@ //! Types that detect when their internal data mutate. -use crate::component::{Component, ComponentTicks}; +use crate::{component::ComponentTicks, system::Resource}; use bevy_reflect::Reflect; use std::ops::{Deref, DerefMut}; @@ -147,14 +147,14 @@ pub(crate) struct Ticks<'a> { /// Panics when used as a [`SystemParam`](crate::system::SystemParam) if the resource does not exist. /// /// Use `Option>` instead if the resource might not always exist. -pub struct ResMut<'a, T: Component> { +pub struct ResMut<'a, T: Resource> { pub(crate) value: &'a mut T, pub(crate) ticks: Ticks<'a>, } -change_detection_impl!(ResMut<'a, T>, T, Component); -impl_into_inner!(ResMut<'a, T>, T, Component); -impl_debug!(ResMut<'a, T>, Component); +change_detection_impl!(ResMut<'a, T>, T, Resource); +impl_into_inner!(ResMut<'a, T>, T, Resource); +impl_debug!(ResMut<'a, T>, Resource); /// Unique borrow of a non-[`Send`] resource. /// diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index a633ff620e..5ece927418 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -1,18 +1,28 @@ //! Types for declaring and storing [`Component`]s. -use crate::storage::SparseSetIndex; +use crate::{ + storage::{SparseSetIndex, Storages}, + system::Resource, +}; +pub use bevy_ecs_macros::Component; use std::{ alloc::Layout, any::{Any, TypeId}, - collections::hash_map::Entry, }; use thiserror::Error; /// A component is data associated with an [`Entity`](crate::entity::Entity). Each entity can have /// multiple different types of components, but only one of them per type. /// -/// Any type that is `Send + Sync + 'static` automatically implements `Component`. +/// Any type that is `Send + Sync + 'static` can implement `Component` using `#[derive(Component)]`. /// +/// In order to use foreign types as components, wrap them using a newtype pattern. +/// ``` +/// # use bevy_ecs::component::Component; +/// use std::time::Duration; +/// #[derive(Component)] +/// struct Cooldown(Duration); +/// ``` /// Components are added with new entities using [`Commands::spawn`](crate::system::Commands::spawn), /// or to existing entities with [`EntityCommands::insert`](crate::system::EntityCommands::insert), /// or their [`World`](crate::world::World) equivalents. @@ -21,21 +31,49 @@ use thiserror::Error; /// as one of the arguments. /// /// Components can be grouped together into a [`Bundle`](crate::bundle::Bundle). -pub trait Component: Send + Sync + 'static {} -impl Component for T {} +pub trait Component: Send + Sync + 'static { + type Storage: ComponentStorage; +} + +pub struct TableStorage; +pub struct SparseStorage; + +pub trait ComponentStorage: sealed::Sealed { + // because the trait is selaed, those items are private API. + const STORAGE_TYPE: StorageType; +} + +impl ComponentStorage for TableStorage { + const STORAGE_TYPE: StorageType = StorageType::Table; +} +impl ComponentStorage for SparseStorage { + const STORAGE_TYPE: StorageType = StorageType::SparseSet; +} + +mod sealed { + pub trait Sealed {} + impl Sealed for super::TableStorage {} + impl Sealed for super::SparseStorage {} +} + +// ECS dependencies cannot derive Component, so we must implement it manually for relevant structs. +impl Component for bevy_tasks::Task +where + Self: Send + Sync + 'static, +{ + type Storage = TableStorage; +} /// The storage used for a specific component type. /// /// # Examples -/// The [`StorageType`] for a component is normally configured via `World::register_component`. +/// The [`StorageType`] for a component is configured via the derive attribute /// /// ``` /// # use bevy_ecs::{prelude::*, component::*}; -/// +/// #[derive(Component)] +/// #[component(storage = "SparseSet")] /// struct A; -/// -/// let mut world = World::default(); -/// world.register_component(ComponentDescriptor::new::(StorageType::SparseSet)); /// ``` #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum StorageType { @@ -128,6 +166,8 @@ impl SparseSetIndex for ComponentId { #[derive(Debug)] pub struct ComponentDescriptor { name: String, + // SAFETY: This must remain private. It must match the statically known StorageType of the + // associated rust component type if one exists. storage_type: StorageType, // SAFETY: This must remain private. It must only be set to "true" if this component is // actually Send + Sync @@ -143,7 +183,18 @@ impl ComponentDescriptor { x.cast::().drop_in_place() } - pub fn new(storage_type: StorageType) -> Self { + pub fn new() -> Self { + Self { + name: std::any::type_name::().to_string(), + storage_type: T::Storage::STORAGE_TYPE, + is_send_and_sync: true, + type_id: Some(TypeId::of::()), + layout: Layout::new::(), + drop: Self::drop_ptr::, + } + } + + pub fn new_resource(storage_type: StorageType) -> Self { Self { name: std::any::type_name::().to_string(), storage_type, @@ -191,46 +242,29 @@ pub struct Components { #[derive(Debug, Error)] pub enum ComponentsError { #[error("A component of type {name:?} ({type_id:?}) already exists")] - ComponentAlreadyExists { type_id: TypeId, name: String }, + ComponentAlreadyExists { + type_id: TypeId, + name: String, + existing_id: ComponentId, + }, } impl Components { - pub(crate) fn add( - &mut self, - descriptor: ComponentDescriptor, - ) -> Result { - let index = self.components.len(); - if let Some(type_id) = descriptor.type_id { - let index_entry = self.indices.entry(type_id); - if let Entry::Occupied(_) = index_entry { - return Err(ComponentsError::ComponentAlreadyExists { - type_id, - name: descriptor.name, - }); + #[inline] + pub fn init_component(&mut self, storages: &mut Storages) -> ComponentId { + let type_id = TypeId::of::(); + let components = &mut self.components; + let index = self.indices.entry(type_id).or_insert_with(|| { + let index = components.len(); + let descriptor = ComponentDescriptor::new::(); + let info = ComponentInfo::new(ComponentId(index), descriptor); + if T::Storage::STORAGE_TYPE == StorageType::SparseSet { + storages.sparse_sets.get_or_insert(&info); } - self.indices.insert(type_id, index); - } - self.components - .push(ComponentInfo::new(ComponentId(index), descriptor)); - - Ok(ComponentId(index)) - } - - #[inline] - pub fn get_or_insert_id(&mut self) -> ComponentId { - // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] - unsafe { - self.get_or_insert_with(TypeId::of::(), || { - ComponentDescriptor::new::(StorageType::default()) - }) - } - } - - #[inline] - pub fn get_or_insert_info(&mut self) -> &ComponentInfo { - let id = self.get_or_insert_id::(); - // SAFE: component_info with the given `id` initialized above - unsafe { self.get_info_unchecked(id) } + components.push(info); + index + }); + ComponentId(*index) } #[inline] @@ -270,17 +304,17 @@ impl Components { } #[inline] - pub fn get_or_insert_resource_id(&mut self) -> ComponentId { + pub fn init_resource(&mut self) -> ComponentId { // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] unsafe { self.get_or_insert_resource_with(TypeId::of::(), || { - ComponentDescriptor::new::(StorageType::default()) + ComponentDescriptor::new_resource::(StorageType::default()) }) } } #[inline] - pub fn get_or_insert_non_send_resource_id(&mut self) -> ComponentId { + pub fn init_non_send(&mut self) -> ComponentId { // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] unsafe { self.get_or_insert_resource_with(TypeId::of::(), || { @@ -308,26 +342,6 @@ impl Components { ComponentId(*index) } - - /// # Safety - /// - /// The [`ComponentDescriptor`] must match the [`TypeId`] - #[inline] - pub(crate) unsafe fn get_or_insert_with( - &mut self, - type_id: TypeId, - func: impl FnOnce() -> ComponentDescriptor, - ) -> ComponentId { - let components = &mut self.components; - let index = self.indices.entry(type_id).or_insert_with(|| { - let descriptor = func(); - let index = components.len(); - components.push(ComponentInfo::new(ComponentId(index), descriptor)); - index - }); - - ComponentId(*index) - } } #[derive(Clone, Debug)] diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index d627c68e69..d28ce5ad3a 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -1,10 +1,7 @@ //! Event handling types. -use crate as bevy_ecs; -use crate::{ - component::Component, - system::{Local, Res, ResMut, SystemParam}, -}; +use crate::system::{Local, Res, ResMut, SystemParam}; +use crate::{self as bevy_ecs, system::Resource}; use bevy_utils::tracing::trace; use std::{ fmt::{self}, @@ -153,20 +150,20 @@ fn map_instance_event(event_instance: &EventInstance) -> &T { /// Reads events of type `T` in order and tracks which events have already been read. #[derive(SystemParam)] -pub struct EventReader<'w, 's, T: Component> { +pub struct EventReader<'w, 's, T: Resource> { last_event_count: Local<'s, (usize, PhantomData)>, events: Res<'w, Events>, } /// Sends events of type `T`. #[derive(SystemParam)] -pub struct EventWriter<'w, 's, T: Component> { +pub struct EventWriter<'w, 's, T: Resource> { events: ResMut<'w, Events>, #[system_param(ignore)] marker: PhantomData<&'s usize>, } -impl<'w, 's, T: Component> EventWriter<'w, 's, T> { +impl<'w, 's, T: Resource> EventWriter<'w, 's, T> { pub fn send(&mut self, event: T) { self.events.send(event); } @@ -256,7 +253,7 @@ fn internal_event_reader<'a, T>( } } -impl<'w, 's, T: Component> EventReader<'w, 's, T> { +impl<'w, 's, T: Resource> EventReader<'w, 's, T> { /// Iterates over the events this EventReader has not seen yet. This updates the EventReader's /// event counter, which means subsequent event reads will not include events that happened /// before now. @@ -273,7 +270,7 @@ impl<'w, 's, T: Component> EventReader<'w, 's, T> { } } -impl Events { +impl Events { /// "Sends" an `event` by writing it to the current event buffer. [EventReader]s can then read /// the event. pub fn send(&mut self, event: T) { diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index ffe8c06804..6fb347a2a7 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -21,6 +21,7 @@ pub mod prelude { pub use crate::{ bundle::Bundle, change_detection::DetectChanges, + component::Component, entity::Entity, event::{EventReader, EventWriter}, query::{Added, ChangeTrackers, Changed, Or, QueryState, With, Without}, @@ -42,7 +43,7 @@ mod tests { use crate as bevy_ecs; use crate::{ bundle::Bundle, - component::{Component, ComponentDescriptor, ComponentId, StorageType}, + component::{Component, ComponentId}, entity::Entity, query::{ Added, ChangeTrackers, Changed, FilterFetch, FilteredAccess, With, Without, WorldQuery, @@ -59,14 +60,14 @@ mod tests { }, }; - #[derive(Debug, PartialEq, Eq)] + #[derive(Component, Debug, PartialEq, Eq, Clone, Copy)] struct A(usize); - #[derive(Debug, PartialEq, Eq)] + #[derive(Component, Debug, PartialEq, Eq, Clone, Copy)] struct B(usize); - #[derive(Debug, PartialEq, Eq)] + #[derive(Component, Debug, PartialEq, Eq, Clone, Copy)] struct C; - #[derive(Clone, Debug)] + #[derive(Component, Clone, Debug)] struct DropCk(Arc); impl DropCk { fn new_pair() -> (Self, Arc) { @@ -81,26 +82,41 @@ mod tests { } } + #[derive(Component, Clone, Debug)] + #[component(storage = "SparseSet")] + struct DropCkSparse(DropCk); + + #[derive(Component, Copy, Clone, PartialEq, Eq, Debug)] + #[component(storage = "Table")] + struct TableStored(&'static str); + #[derive(Component, Copy, Clone, PartialEq, Eq, Debug)] + #[component(storage = "SparseSet")] + struct SparseStored(u32); + #[test] fn random_access() { let mut world = World::new(); - world - .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) - .unwrap(); - let e = world.spawn().insert_bundle(("abc", 123)).id(); - let f = world.spawn().insert_bundle(("def", 456, true)).id(); - assert_eq!(*world.get::<&str>(e).unwrap(), "abc"); - assert_eq!(*world.get::(e).unwrap(), 123); - assert_eq!(*world.get::<&str>(f).unwrap(), "def"); - assert_eq!(*world.get::(f).unwrap(), 456); + + let e = world + .spawn() + .insert_bundle((TableStored("abc"), SparseStored(123))) + .id(); + let f = world + .spawn() + .insert_bundle((TableStored("def"), SparseStored(456), A(1))) + .id(); + assert_eq!(world.get::(e).unwrap().0, "abc"); + assert_eq!(world.get::(e).unwrap().0, 123); + assert_eq!(world.get::(f).unwrap().0, "def"); + assert_eq!(world.get::(f).unwrap().0, 456); // test archetype get_mut() - *world.get_mut::<&'static str>(e).unwrap() = "xyz"; - assert_eq!(*world.get::<&'static str>(e).unwrap(), "xyz"); + world.get_mut::(e).unwrap().0 = "xyz"; + assert_eq!(world.get::(e).unwrap().0, "xyz"); // test sparse set get_mut() - *world.get_mut::(f).unwrap() = 42; - assert_eq!(*world.get::(f).unwrap(), 42); + world.get_mut::(f).unwrap().0 = 42; + assert_eq!(world.get::(f).unwrap().0, 42); } #[test] @@ -109,79 +125,93 @@ mod tests { #[derive(Bundle, PartialEq, Debug)] struct Foo { - x: &'static str, - y: i32, + x: TableStored, + y: SparseStored, } - world - .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) - .unwrap(); - assert_eq!( - ::component_ids(world.components_mut()), + ::component_ids(&mut world.components, &mut world.storages), vec![ - world.components_mut().get_or_insert_id::<&'static str>(), - world.components_mut().get_or_insert_id::(), + world.init_component::(), + world.init_component::(), ] ); - let e1 = world.spawn().insert_bundle(Foo { x: "abc", y: 123 }).id(); - let e2 = world.spawn().insert_bundle(("def", 456, true)).id(); - assert_eq!(*world.get::<&str>(e1).unwrap(), "abc"); - assert_eq!(*world.get::(e1).unwrap(), 123); - assert_eq!(*world.get::<&str>(e2).unwrap(), "def"); - assert_eq!(*world.get::(e2).unwrap(), 456); + let e1 = world + .spawn() + .insert_bundle(Foo { + x: TableStored("abc"), + y: SparseStored(123), + }) + .id(); + let e2 = world + .spawn() + .insert_bundle((TableStored("def"), SparseStored(456), A(1))) + .id(); + assert_eq!(world.get::(e1).unwrap().0, "abc"); + assert_eq!(world.get::(e1).unwrap().0, 123); + assert_eq!(world.get::(e2).unwrap().0, "def"); + assert_eq!(world.get::(e2).unwrap().0, 456); // test archetype get_mut() - *world.get_mut::<&'static str>(e1).unwrap() = "xyz"; - assert_eq!(*world.get::<&'static str>(e1).unwrap(), "xyz"); + world.get_mut::(e1).unwrap().0 = "xyz"; + assert_eq!(world.get::(e1).unwrap().0, "xyz"); // test sparse set get_mut() - *world.get_mut::(e2).unwrap() = 42; - assert_eq!(*world.get::(e2).unwrap(), 42); + world.get_mut::(e2).unwrap().0 = 42; + assert_eq!(world.get::(e2).unwrap().0, 42); assert_eq!( world.entity_mut(e1).remove_bundle::().unwrap(), - Foo { x: "xyz", y: 123 } + Foo { + x: TableStored("xyz"), + y: SparseStored(123), + } ); #[derive(Bundle, PartialEq, Debug)] struct Nested { - a: usize, + a: A, #[bundle] foo: Foo, - b: u8, + b: B, } assert_eq!( - ::component_ids(world.components_mut()), + ::component_ids(&mut world.components, &mut world.storages), vec![ - world.components_mut().get_or_insert_id::(), - world.components_mut().get_or_insert_id::<&'static str>(), - world.components_mut().get_or_insert_id::(), - world.components_mut().get_or_insert_id::(), + world.init_component::(), + world.init_component::(), + world.init_component::(), + world.init_component::(), ] ); let e3 = world .spawn() .insert_bundle(Nested { - a: 1, - foo: Foo { x: "ghi", y: 789 }, - b: 2, + a: A(1), + foo: Foo { + x: TableStored("ghi"), + y: SparseStored(789), + }, + b: B(2), }) .id(); - assert_eq!(*world.get::<&str>(e3).unwrap(), "ghi"); - assert_eq!(*world.get::(e3).unwrap(), 789); - assert_eq!(*world.get::(e3).unwrap(), 1); - assert_eq!(*world.get::(e3).unwrap(), 2); + assert_eq!(world.get::(e3).unwrap().0, "ghi"); + assert_eq!(world.get::(e3).unwrap().0, 789); + assert_eq!(world.get::(e3).unwrap().0, 1); + assert_eq!(world.get::(e3).unwrap().0, 2); assert_eq!( world.entity_mut(e3).remove_bundle::().unwrap(), Nested { - a: 1, - foo: Foo { x: "ghi", y: 789 }, - b: 2, + a: A(1), + foo: Foo { + x: TableStored("ghi"), + y: SparseStored(789), + }, + b: B(2), } ); } @@ -189,113 +219,167 @@ mod tests { #[test] fn despawn_table_storage() { let mut world = World::new(); - let e = world.spawn().insert_bundle(("abc", 123)).id(); - let f = world.spawn().insert_bundle(("def", 456)).id(); + let e = world + .spawn() + .insert_bundle((TableStored("abc"), A(123))) + .id(); + let f = world + .spawn() + .insert_bundle((TableStored("def"), A(456))) + .id(); assert_eq!(world.entities.len(), 2); assert!(world.despawn(e)); assert_eq!(world.entities.len(), 1); - assert!(world.get::<&str>(e).is_none()); - assert!(world.get::(e).is_none()); - assert_eq!(*world.get::<&str>(f).unwrap(), "def"); - assert_eq!(*world.get::(f).unwrap(), 456); + assert!(world.get::(e).is_none()); + assert!(world.get::(e).is_none()); + assert_eq!(world.get::(f).unwrap().0, "def"); + assert_eq!(world.get::(f).unwrap().0, 456); } #[test] fn despawn_mixed_storage() { let mut world = World::new(); - world - .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) - .unwrap(); - let e = world.spawn().insert_bundle(("abc", 123)).id(); - let f = world.spawn().insert_bundle(("def", 456)).id(); + + let e = world + .spawn() + .insert_bundle((TableStored("abc"), SparseStored(123))) + .id(); + let f = world + .spawn() + .insert_bundle((TableStored("def"), SparseStored(456))) + .id(); assert_eq!(world.entities.len(), 2); assert!(world.despawn(e)); assert_eq!(world.entities.len(), 1); - assert!(world.get::<&str>(e).is_none()); - assert!(world.get::(e).is_none()); - assert_eq!(*world.get::<&str>(f).unwrap(), "def"); - assert_eq!(*world.get::(f).unwrap(), 456); + assert!(world.get::(e).is_none()); + assert!(world.get::(e).is_none()); + assert_eq!(world.get::(f).unwrap().0, "def"); + assert_eq!(world.get::(f).unwrap().0, 456); } #[test] fn query_all() { let mut world = World::new(); - let e = world.spawn().insert_bundle(("abc", 123)).id(); - let f = world.spawn().insert_bundle(("def", 456)).id(); + let e = world + .spawn() + .insert_bundle((TableStored("abc"), A(123))) + .id(); + let f = world + .spawn() + .insert_bundle((TableStored("def"), A(456))) + .id(); let ents = world - .query::<(Entity, &i32, &&str)>() + .query::<(Entity, &A, &TableStored)>() .iter(&world) .map(|(e, &i, &s)| (e, i, s)) .collect::>(); - assert_eq!(ents, &[(e, 123, "abc"), (f, 456, "def")]); + assert_eq!( + ents, + &[ + (e, A(123), TableStored("abc")), + (f, A(456), TableStored("def")) + ] + ); } #[test] fn query_all_for_each() { let mut world = World::new(); - let e = world.spawn().insert_bundle(("abc", 123)).id(); - let f = world.spawn().insert_bundle(("def", 456)).id(); + let e = world + .spawn() + .insert_bundle((TableStored("abc"), A(123))) + .id(); + let f = world + .spawn() + .insert_bundle((TableStored("def"), A(456))) + .id(); let mut results = Vec::new(); world - .query::<(Entity, &i32, &&str)>() + .query::<(Entity, &A, &TableStored)>() .for_each(&world, |(e, &i, &s)| results.push((e, i, s))); - assert_eq!(results, &[(e, 123, "abc"), (f, 456, "def")]); + assert_eq!( + results, + &[ + (e, A(123), TableStored("abc")), + (f, A(456), TableStored("def")) + ] + ); } #[test] fn query_single_component() { let mut world = World::new(); - let e = world.spawn().insert_bundle(("abc", 123)).id(); - let f = world.spawn().insert_bundle(("def", 456, true)).id(); + let e = world + .spawn() + .insert_bundle((TableStored("abc"), A(123))) + .id(); + let f = world + .spawn() + .insert_bundle((TableStored("def"), A(456), B(1))) + .id(); let ents = world - .query::<(Entity, &i32)>() + .query::<(Entity, &A)>() .iter(&world) .map(|(e, &i)| (e, i)) .collect::>(); - assert_eq!(ents, &[(e, 123), (f, 456)]); + assert_eq!(ents, &[(e, A(123)), (f, A(456))]); } #[test] fn stateful_query_handles_new_archetype() { let mut world = World::new(); - let e = world.spawn().insert_bundle(("abc", 123)).id(); - let mut query = world.query::<(Entity, &i32)>(); + let e = world + .spawn() + .insert_bundle((TableStored("abc"), A(123))) + .id(); + let mut query = world.query::<(Entity, &A)>(); let ents = query.iter(&world).map(|(e, &i)| (e, i)).collect::>(); - assert_eq!(ents, &[(e, 123)]); + assert_eq!(ents, &[(e, A(123))]); - let f = world.spawn().insert_bundle(("def", 456, true)).id(); + let f = world + .spawn() + .insert_bundle((TableStored("def"), A(456), B(1))) + .id(); let ents = query.iter(&world).map(|(e, &i)| (e, i)).collect::>(); - assert_eq!(ents, &[(e, 123), (f, 456)]); + assert_eq!(ents, &[(e, A(123)), (f, A(456))]); } #[test] fn query_single_component_for_each() { let mut world = World::new(); - let e = world.spawn().insert_bundle(("abc", 123)).id(); - let f = world.spawn().insert_bundle(("def", 456, true)).id(); + let e = world + .spawn() + .insert_bundle((TableStored("abc"), A(123))) + .id(); + let f = world + .spawn() + .insert_bundle((TableStored("def"), A(456), B(1))) + .id(); let mut results = Vec::new(); world - .query::<(Entity, &i32)>() + .query::<(Entity, &A)>() .for_each(&world, |(e, &i)| results.push((e, i))); - assert_eq!(results, &[(e, 123), (f, 456)]); + assert_eq!(results, &[(e, A(123)), (f, A(456))]); } #[test] fn par_for_each_dense() { let mut world = World::new(); let task_pool = TaskPool::default(); - let e1 = world.spawn().insert(1).id(); - let e2 = world.spawn().insert(2).id(); - let e3 = world.spawn().insert(3).id(); - let e4 = world.spawn().insert_bundle((4, true)).id(); - let e5 = world.spawn().insert_bundle((5, true)).id(); + let e1 = world.spawn().insert(A(1)).id(); + let e2 = world.spawn().insert(A(2)).id(); + let e3 = world.spawn().insert(A(3)).id(); + let e4 = world.spawn().insert_bundle((A(4), B(1))).id(); + let e5 = world.spawn().insert_bundle((A(5), B(1))).id(); let results = Arc::new(Mutex::new(Vec::new())); world - .query::<(Entity, &i32)>() - .par_for_each(&world, &task_pool, 2, |(e, &i)| results.lock().push((e, i))); + .query::<(Entity, &A)>() + .par_for_each(&world, &task_pool, 2, |(e, &A(i))| { + results.lock().push((e, i)) + }); results.lock().sort(); assert_eq!( &*results.lock(), @@ -306,19 +390,20 @@ mod tests { #[test] fn par_for_each_sparse() { let mut world = World::new(); - world - .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) - .unwrap(); + let task_pool = TaskPool::default(); - let e1 = world.spawn().insert(1).id(); - let e2 = world.spawn().insert(2).id(); - let e3 = world.spawn().insert(3).id(); - let e4 = world.spawn().insert_bundle((4, true)).id(); - let e5 = world.spawn().insert_bundle((5, true)).id(); + let e1 = world.spawn().insert(SparseStored(1)).id(); + let e2 = world.spawn().insert(SparseStored(2)).id(); + let e3 = world.spawn().insert(SparseStored(3)).id(); + let e4 = world.spawn().insert_bundle((SparseStored(4), A(1))).id(); + let e5 = world.spawn().insert_bundle((SparseStored(5), A(1))).id(); let results = Arc::new(Mutex::new(Vec::new())); - world - .query::<(Entity, &i32)>() - .par_for_each(&world, &task_pool, 2, |(e, &i)| results.lock().push((e, i))); + world.query::<(Entity, &SparseStored)>().par_for_each( + &world, + &task_pool, + 2, + |(e, &SparseStored(i))| results.lock().push((e, i)), + ); results.lock().sort(); assert_eq!( &*results.lock(), @@ -329,194 +414,218 @@ mod tests { #[test] fn query_missing_component() { let mut world = World::new(); - world.spawn().insert_bundle(("abc", 123)); - world.spawn().insert_bundle(("def", 456)); - assert!(world.query::<(&bool, &i32)>().iter(&world).next().is_none()); + world.spawn().insert_bundle((TableStored("abc"), A(123))); + world.spawn().insert_bundle((TableStored("def"), A(456))); + assert!(world.query::<(&B, &A)>().iter(&world).next().is_none()); } #[test] fn query_sparse_component() { let mut world = World::new(); - world.spawn().insert_bundle(("abc", 123)); - let f = world.spawn().insert_bundle(("def", 456, true)).id(); + world.spawn().insert_bundle((TableStored("abc"), A(123))); + let f = world + .spawn() + .insert_bundle((TableStored("def"), A(456), B(1))) + .id(); let ents = world - .query::<(Entity, &bool)>() + .query::<(Entity, &B)>() .iter(&world) .map(|(e, &b)| (e, b)) .collect::>(); - assert_eq!(ents, &[(f, true)]); + assert_eq!(ents, &[(f, B(1))]); } #[test] fn query_filter_with() { let mut world = World::new(); - world.spawn().insert_bundle((123u32, 1.0f32)); - world.spawn().insert(456u32); + world.spawn().insert_bundle((A(123), B(1))); + world.spawn().insert(A(456)); let result = world - .query_filtered::<&u32, With>() + .query_filtered::<&A, With>() .iter(&world) .cloned() .collect::>(); - assert_eq!(result, vec![123]); + assert_eq!(result, vec![A(123)]); } #[test] fn query_filter_with_for_each() { let mut world = World::new(); - world.spawn().insert_bundle((123u32, 1.0f32)); - world.spawn().insert(456u32); + world.spawn().insert_bundle((A(123), B(1))); + world.spawn().insert(A(456)); let mut results = Vec::new(); world - .query_filtered::<&u32, With>() + .query_filtered::<&A, With>() .for_each(&world, |i| results.push(*i)); - assert_eq!(results, vec![123]); + assert_eq!(results, vec![A(123)]); } #[test] fn query_filter_with_sparse() { let mut world = World::new(); - world - .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) - .unwrap(); - world.spawn().insert_bundle((123u32, 1.0f32)); - world.spawn().insert(456u32); + + world.spawn().insert_bundle((A(123), SparseStored(321))); + world.spawn().insert(A(456)); let result = world - .query_filtered::<&u32, With>() + .query_filtered::<&A, With>() .iter(&world) .cloned() .collect::>(); - assert_eq!(result, vec![123]); + assert_eq!(result, vec![A(123)]); } #[test] fn query_filter_with_sparse_for_each() { let mut world = World::new(); - world - .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) - .unwrap(); - world.spawn().insert_bundle((123u32, 1.0f32)); - world.spawn().insert(456u32); + + world.spawn().insert_bundle((A(123), SparseStored(321))); + world.spawn().insert(A(456)); let mut results = Vec::new(); world - .query_filtered::<&u32, With>() + .query_filtered::<&A, With>() .for_each(&world, |i| results.push(*i)); - assert_eq!(results, vec![123]); + assert_eq!(results, vec![A(123)]); } #[test] fn query_filter_without() { let mut world = World::new(); - world.spawn().insert_bundle((123u32, 1.0f32)); - world.spawn().insert(456u32); + world.spawn().insert_bundle((A(123), B(321))); + world.spawn().insert(A(456)); let result = world - .query_filtered::<&u32, Without>() + .query_filtered::<&A, Without>() .iter(&world) .cloned() .collect::>(); - assert_eq!(result, vec![456]); + assert_eq!(result, vec![A(456)]); } #[test] fn query_optional_component_table() { let mut world = World::new(); - let e = world.spawn().insert_bundle(("abc", 123)).id(); - let f = world.spawn().insert_bundle(("def", 456, true)).id(); + let e = world + .spawn() + .insert_bundle((TableStored("abc"), A(123))) + .id(); + let f = world + .spawn() + .insert_bundle((TableStored("def"), A(456), B(1))) + .id(); // this should be skipped - world.spawn().insert("abc"); + world.spawn().insert(TableStored("abc")); let ents = world - .query::<(Entity, Option<&bool>, &i32)>() + .query::<(Entity, Option<&B>, &A)>() .iter(&world) .map(|(e, b, &i)| (e, b.copied(), i)) .collect::>(); - assert_eq!(ents, &[(e, None, 123), (f, Some(true), 456)]); + assert_eq!(ents, &[(e, None, A(123)), (f, Some(B(1)), A(456))]); } #[test] fn query_optional_component_sparse() { let mut world = World::new(); - world - .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) - .unwrap(); - let e = world.spawn().insert_bundle(("abc", 123)).id(); - let f = world.spawn().insert_bundle(("def", 456, true)).id(); + + let e = world + .spawn() + .insert_bundle((TableStored("abc"), A(123))) + .id(); + let f = world + .spawn() + .insert_bundle((TableStored("def"), A(456), SparseStored(1))) + .id(); // // this should be skipped - // world.spawn().insert("abc"); + // SparseStored(1).spawn().insert("abc"); let ents = world - .query::<(Entity, Option<&bool>, &i32)>() + .query::<(Entity, Option<&SparseStored>, &A)>() .iter(&world) .map(|(e, b, &i)| (e, b.copied(), i)) .collect::>(); - assert_eq!(ents, &[(e, None, 123), (f, Some(true), 456)]); + assert_eq!( + ents, + &[(e, None, A(123)), (f, Some(SparseStored(1)), A(456))] + ); } #[test] fn query_optional_component_sparse_no_match() { let mut world = World::new(); - world - .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) - .unwrap(); - let e = world.spawn().insert_bundle(("abc", 123)).id(); - let f = world.spawn().insert_bundle(("def", 456)).id(); + + let e = world + .spawn() + .insert_bundle((TableStored("abc"), A(123))) + .id(); + let f = world + .spawn() + .insert_bundle((TableStored("def"), A(456))) + .id(); // // this should be skipped - world.spawn().insert("abc"); + world.spawn().insert(TableStored("abc")); let ents = world - .query::<(Entity, Option<&bool>, &i32)>() + .query::<(Entity, Option<&SparseStored>, &A)>() .iter(&world) .map(|(e, b, &i)| (e, b.copied(), i)) .collect::>(); - assert_eq!(ents, &[(e, None, 123), (f, None, 456)]); + assert_eq!(ents, &[(e, None, A(123)), (f, None, A(456))]); } #[test] fn add_remove_components() { let mut world = World::new(); - let e1 = world.spawn().insert(42).insert_bundle((true, "abc")).id(); - let e2 = world.spawn().insert(0).insert_bundle((false, "xyz")).id(); + let e1 = world + .spawn() + .insert(A(1)) + .insert_bundle((B(3), TableStored("abc"))) + .id(); + let e2 = world + .spawn() + .insert(A(2)) + .insert_bundle((B(4), TableStored("xyz"))) + .id(); assert_eq!( world - .query::<(Entity, &i32, &bool)>() + .query::<(Entity, &A, &B)>() .iter(&world) .map(|(e, &i, &b)| (e, i, b)) .collect::>(), - &[(e1, 42, true), (e2, 0, false)] + &[(e1, A(1), B(3)), (e2, A(2), B(4))] ); - assert_eq!(world.entity_mut(e1).remove::(), Some(42)); + assert_eq!(world.entity_mut(e1).remove::(), Some(A(1))); assert_eq!( world - .query::<(Entity, &i32, &bool)>() + .query::<(Entity, &A, &B)>() .iter(&world) .map(|(e, &i, &b)| (e, i, b)) .collect::>(), - &[(e2, 0, false)] + &[(e2, A(2), B(4))] ); assert_eq!( world - .query::<(Entity, &bool, &&str)>() + .query::<(Entity, &B, &TableStored)>() .iter(&world) - .map(|(e, &b, &s)| (e, b, s)) + .map(|(e, &B(b), &TableStored(s))| (e, b, s)) .collect::>(), - &[(e2, false, "xyz"), (e1, true, "abc")] + &[(e2, 4, "xyz"), (e1, 3, "abc")] ); - world.entity_mut(e1).insert(43); + world.entity_mut(e1).insert(A(43)); assert_eq!( world - .query::<(Entity, &i32, &bool)>() + .query::<(Entity, &A, &B)>() .iter(&world) .map(|(e, &i, &b)| (e, i, b)) .collect::>(), - &[(e2, 0, false), (e1, 43, true)] + &[(e2, A(2), B(4)), (e1, A(43), B(3))] ); - world.entity_mut(e1).insert(1.0f32); + world.entity_mut(e1).insert(C); assert_eq!( world - .query::<(Entity, &f32)>() + .query::<(Entity, &C)>() .iter(&world) .map(|(e, &f)| (e, f)) .collect::>(), - &[(e1, 1.0)] + &[(e1, C)] ); } @@ -525,53 +634,57 @@ mod tests { let mut world = World::default(); let mut entities = Vec::with_capacity(10_000); for _ in 0..1000 { - entities.push(world.spawn().insert(0.0f32).id()); + entities.push(world.spawn().insert(B(0)).id()); } for (i, entity) in entities.iter().cloned().enumerate() { - world.entity_mut(entity).insert(i); + world.entity_mut(entity).insert(A(i)); } for (i, entity) in entities.iter().cloned().enumerate() { - assert_eq!(world.entity_mut(entity).remove::(), Some(i)); + assert_eq!(world.entity_mut(entity).remove::(), Some(A(i))); } } #[test] fn sparse_set_add_remove_many() { let mut world = World::default(); - world - .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) - .unwrap(); + let mut entities = Vec::with_capacity(1000); for _ in 0..4 { - entities.push(world.spawn().insert(0.0f32).id()); + entities.push(world.spawn().insert(A(2)).id()); } for (i, entity) in entities.iter().cloned().enumerate() { - world.entity_mut(entity).insert(i); + world.entity_mut(entity).insert(SparseStored(i as u32)); } for (i, entity) in entities.iter().cloned().enumerate() { - assert_eq!(world.entity_mut(entity).remove::(), Some(i)); + assert_eq!( + world.entity_mut(entity).remove::(), + Some(SparseStored(i as u32)) + ); } } #[test] fn remove_missing() { let mut world = World::new(); - let e = world.spawn().insert_bundle(("abc", 123)).id(); - assert!(world.entity_mut(e).remove::().is_none()); + let e = world + .spawn() + .insert_bundle((TableStored("abc"), A(123))) + .id(); + assert!(world.entity_mut(e).remove::().is_none()); } #[test] fn spawn_batch() { let mut world = World::new(); - world.spawn_batch((0..100).map(|x| (x, "abc"))); + world.spawn_batch((0..100).map(|x| (A(x), TableStored("abc")))); let values = world - .query::<&i32>() + .query::<&A>() .iter(&world) - .copied() + .map(|v| v.0) .collect::>(); let expected = (0..100).collect::>(); assert_eq!(values, expected); @@ -580,17 +693,26 @@ mod tests { #[test] fn query_get() { let mut world = World::new(); - let a = world.spawn().insert_bundle(("abc", 123)).id(); - let b = world.spawn().insert_bundle(("def", 456)).id(); - let c = world.spawn().insert_bundle(("ghi", 789, true)).id(); + let a = world + .spawn() + .insert_bundle((TableStored("abc"), A(123))) + .id(); + let b = world + .spawn() + .insert_bundle((TableStored("def"), A(456))) + .id(); + let c = world + .spawn() + .insert_bundle((TableStored("ghi"), A(789), B(1))) + .id(); - let mut i32_query = world.query::<&i32>(); - assert_eq!(i32_query.get(&world, a).unwrap(), &123); - assert_eq!(i32_query.get(&world, b).unwrap(), &456); + let mut i32_query = world.query::<&A>(); + assert_eq!(i32_query.get(&world, a).unwrap().0, 123); + assert_eq!(i32_query.get(&world, b).unwrap().0, 456); - let mut i32_bool_query = world.query::<(&i32, &bool)>(); + let mut i32_bool_query = world.query::<(&A, &B)>(); assert!(i32_bool_query.get(&world, a).is_err()); - assert_eq!(i32_bool_query.get(&world, c).unwrap(), (&789, &true)); + assert_eq!(i32_bool_query.get(&world, c).unwrap(), (&A(789), &B(1))); assert!(world.despawn(a)); assert!(i32_query.get(&world, a).is_err()); } @@ -598,53 +720,49 @@ mod tests { #[test] fn remove_tracking() { let mut world = World::new(); - world - .register_component(ComponentDescriptor::new::<&'static str>( - StorageType::SparseSet, - )) - .unwrap(); - let a = world.spawn().insert_bundle(("abc", 123)).id(); - let b = world.spawn().insert_bundle(("abc", 123)).id(); + + let a = world.spawn().insert_bundle((SparseStored(0), A(123))).id(); + let b = world.spawn().insert_bundle((SparseStored(1), A(123))).id(); world.entity_mut(a).despawn(); assert_eq!( - world.removed::().collect::>(), + world.removed::().collect::>(), &[a], "despawning results in 'removed component' state for table components" ); assert_eq!( - world.removed::<&'static str>().collect::>(), + world.removed::().collect::>(), &[a], "despawning results in 'removed component' state for sparse set components" ); - world.entity_mut(b).insert(10.0); + world.entity_mut(b).insert(B(1)); assert_eq!( - world.removed::().collect::>(), + world.removed::().collect::>(), &[a], "archetype moves does not result in 'removed component' state" ); - world.entity_mut(b).remove::(); + world.entity_mut(b).remove::(); assert_eq!( - world.removed::().collect::>(), + world.removed::().collect::>(), &[a, b], "removing a component results in a 'removed component' state" ); world.clear_trackers(); assert_eq!( - world.removed::().collect::>(), + world.removed::().collect::>(), &[], "clearning trackers clears removals" ); assert_eq!( - world.removed::<&'static str>().collect::>(), + world.removed::().collect::>(), &[], "clearning trackers clears removals" ); assert_eq!( - world.removed::().collect::>(), + world.removed::().collect::>(), &[], "clearning trackers clears removals" ); @@ -673,61 +791,49 @@ mod tests { #[test] fn added_tracking() { let mut world = World::new(); - let a = world.spawn().insert(123i32).id(); + let a = world.spawn().insert(A(123)).id(); - assert_eq!(world.query::<&i32>().iter(&world).count(), 1); + assert_eq!(world.query::<&A>().iter(&world).count(), 1); assert_eq!( - world - .query_filtered::<(), Added>() - .iter(&world) - .count(), + world.query_filtered::<(), Added>().iter(&world).count(), 1 ); - assert_eq!(world.query::<&i32>().iter(&world).count(), 1); + assert_eq!(world.query::<&A>().iter(&world).count(), 1); assert_eq!( - world - .query_filtered::<(), Added>() - .iter(&world) - .count(), + world.query_filtered::<(), Added>().iter(&world).count(), 1 ); - assert!(world.query::<&i32>().get(&world, a).is_ok()); + assert!(world.query::<&A>().get(&world, a).is_ok()); assert!(world - .query_filtered::<(), Added>() + .query_filtered::<(), Added>() .get(&world, a) .is_ok()); - assert!(world.query::<&i32>().get(&world, a).is_ok()); + assert!(world.query::<&A>().get(&world, a).is_ok()); assert!(world - .query_filtered::<(), Added>() + .query_filtered::<(), Added>() .get(&world, a) .is_ok()); world.clear_trackers(); - assert_eq!(world.query::<&i32>().iter(&world).count(), 1); + assert_eq!(world.query::<&A>().iter(&world).count(), 1); assert_eq!( - world - .query_filtered::<(), Added>() - .iter(&world) - .count(), + world.query_filtered::<(), Added>().iter(&world).count(), 0 ); - assert_eq!(world.query::<&i32>().iter(&world).count(), 1); + assert_eq!(world.query::<&A>().iter(&world).count(), 1); assert_eq!( - world - .query_filtered::<(), Added>() - .iter(&world) - .count(), + world.query_filtered::<(), Added>().iter(&world).count(), 0 ); - assert!(world.query::<&i32>().get(&world, a).is_ok()); + assert!(world.query::<&A>().get(&world, a).is_ok()); assert!(world - .query_filtered::<(), Added>() + .query_filtered::<(), Added>() .get(&world, a) .is_err()); - assert!(world.query::<&i32>().get(&world, a).is_ok()); + assert!(world.query::<&A>().get(&world, a).is_ok()); assert!(world - .query_filtered::<(), Added>() + .query_filtered::<(), Added>() .get(&world, a) .is_err()); } @@ -768,7 +874,7 @@ mod tests { let e1 = world.spawn().insert_bundle((A(0), B(0))).id(); let e2 = world.spawn().insert_bundle((A(0), B(0))).id(); let e3 = world.spawn().insert_bundle((A(0), B(0))).id(); - world.spawn().insert_bundle((A(0), B)); + world.spawn().insert_bundle((A(0), B(0))); world.clear_trackers(); @@ -796,7 +902,7 @@ mod tests { assert_eq!(get_filtered::>(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)"); // spawning a new A entity should not change existing changed state - world.entity_mut(e1).insert_bundle((A(0), B)); + world.entity_mut(e1).insert_bundle((A(0), B(0))); assert_eq!( get_filtered::>(&mut world), vec![e3, e1], @@ -979,36 +1085,39 @@ mod tests { #[test] fn remove_intersection() { let mut world = World::default(); - let e1 = world.spawn().insert_bundle((1, 1.0, "a")).id(); + let e1 = world + .spawn() + .insert_bundle((A(1), B(1), TableStored("a"))) + .id(); let mut e = world.entity_mut(e1); - assert_eq!(e.get::<&'static str>(), Some(&"a")); - assert_eq!(e.get::(), Some(&1)); - assert_eq!(e.get::(), Some(&1.0)); + assert_eq!(e.get::(), Some(&TableStored("a"))); + assert_eq!(e.get::(), Some(&A(1))); + assert_eq!(e.get::(), Some(&B(1))); assert_eq!( - e.get::(), + e.get::(), None, - "usize is not in the entity, so it should not exist" + "C is not in the entity, so it should not exist" ); - e.remove_bundle_intersection::<(i32, f64, usize)>(); + e.remove_bundle_intersection::<(A, B, C)>(); assert_eq!( - e.get::<&'static str>(), - Some(&"a"), - "&'static str is not in the removed bundle, so it should exist" + e.get::(), + Some(&TableStored("a")), + "TableStored is not in the removed bundle, so it should exist" ); assert_eq!( - e.get::(), + e.get::(), None, "i32 is in the removed bundle, so it should not exist" ); assert_eq!( - e.get::(), + e.get::(), None, "f64 is in the removed bundle, so it should not exist" ); assert_eq!( - e.get::(), + e.get::(), None, "usize is in the removed bundle, so it should not exist" ); @@ -1017,48 +1126,57 @@ mod tests { #[test] fn remove_bundle() { let mut world = World::default(); - world.spawn().insert_bundle((1, 1.0, 1usize)).id(); - let e2 = world.spawn().insert_bundle((2, 2.0, 2usize)).id(); - world.spawn().insert_bundle((3, 3.0, 3usize)).id(); + world + .spawn() + .insert_bundle((A(1), B(1), TableStored("1"))) + .id(); + let e2 = world + .spawn() + .insert_bundle((A(2), B(2), TableStored("2"))) + .id(); + world + .spawn() + .insert_bundle((A(3), B(3), TableStored("3"))) + .id(); - let mut query = world.query::<(&f64, &usize)>(); + let mut query = world.query::<(&B, &TableStored)>(); let results = query .iter(&world) - .map(|(a, b)| (*a, *b)) + .map(|(a, b)| (a.0, b.0)) .collect::>(); - assert_eq!(results, vec![(1.0, 1usize), (2.0, 2usize), (3.0, 3usize),]); + assert_eq!(results, vec![(1, "1"), (2, "2"), (3, "3"),]); let removed_bundle = world .entity_mut(e2) - .remove_bundle::<(f64, usize)>() + .remove_bundle::<(B, TableStored)>() .unwrap(); - assert_eq!(removed_bundle, (2.0, 2usize)); + assert_eq!(removed_bundle, (B(2), TableStored("2"))); let results = query .iter(&world) - .map(|(a, b)| (*a, *b)) + .map(|(a, b)| (a.0, b.0)) .collect::>(); - assert_eq!(results, vec![(1.0, 1usize), (3.0, 3usize),]); + assert_eq!(results, vec![(1, "1"), (3, "3"),]); - let mut i32_query = world.query::<&i32>(); - let results = i32_query.iter(&world).cloned().collect::>(); + let mut a_query = world.query::<&A>(); + let results = a_query.iter(&world).map(|a| a.0).collect::>(); assert_eq!(results, vec![1, 3, 2]); let entity_ref = world.entity(e2); assert_eq!( - entity_ref.get::(), - Some(&2), - "i32 is not in the removed bundle, so it should exist" + entity_ref.get::(), + Some(&A(2)), + "A is not in the removed bundle, so it should exist" ); assert_eq!( - entity_ref.get::(), + entity_ref.get::(), None, - "f64 is in the removed bundle, so it should not exist" + "B is in the removed bundle, so it should not exist" ); assert_eq!( - entity_ref.get::(), + entity_ref.get::(), None, - "usize is in the removed bundle, so it should not exist" + "TableStored is in the removed bundle, so it should not exist" ); } @@ -1123,7 +1241,7 @@ mod tests { #[should_panic] fn duplicate_components_panic() { let mut world = World::new(); - world.spawn().insert_bundle((1, 2)); + world.spawn().insert_bundle((A(1), A(2))); } #[test] @@ -1152,7 +1270,7 @@ mod tests { fn multiple_worlds_same_query_iter() { let mut world_a = World::new(); let world_b = World::new(); - let mut query = world_a.query::<&i32>(); + let mut query = world_a.query::<&A>(); query.iter(&world_a); query.iter(&world_b); } @@ -1160,19 +1278,19 @@ mod tests { #[test] fn query_filters_dont_collide_with_fetches() { let mut world = World::new(); - world.query_filtered::<&mut i32, Changed>(); + world.query_filtered::<&mut A, Changed>(); } #[test] fn filtered_query_access() { let mut world = World::new(); - let query = world.query_filtered::<&mut i32, Changed>(); + let query = world.query_filtered::<&mut A, Changed>(); let mut expected = FilteredAccess::::default(); - let i32_id = world.components.get_id(TypeId::of::()).unwrap(); - let f64_id = world.components.get_id(TypeId::of::()).unwrap(); - expected.add_write(i32_id); - expected.add_read(f64_id); + let a_id = world.components.get_id(TypeId::of::()).unwrap(); + let b_id = world.components.get_id(TypeId::of::()).unwrap(); + expected.add_write(a_id); + expected.add_read(b_id); assert!( query.component_access.eq(&expected), "ComponentId access from query fetch and query filter should be combined" @@ -1184,7 +1302,7 @@ mod tests { fn multiple_worlds_same_query_get() { let mut world_a = World::new(); let world_b = World::new(); - let mut query = world_a.query::<&i32>(); + let mut query = world_a.query::<&A>(); let _ = query.get(&world_a, Entity::new(0)); let _ = query.get(&world_b, Entity::new(0)); } @@ -1194,7 +1312,7 @@ mod tests { fn multiple_worlds_same_query_for_each() { let mut world_a = World::new(); let world_b = World::new(); - let mut query = world_a.query::<&i32>(); + let mut query = world_a.query::<&A>(); query.for_each(&world_a, |_| {}); query.for_each(&world_b, |_| {}); } @@ -1228,10 +1346,11 @@ mod tests { let (dropck1, dropped1) = DropCk::new_pair(); let (dropck2, dropped2) = DropCk::new_pair(); let mut world = World::default(); + world - .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) - .unwrap(); - world.spawn().insert(dropck1).insert(dropck2); + .spawn() + .insert(DropCkSparse(dropck1)) + .insert(DropCkSparse(dropck2)); assert_eq!(dropped1.load(Ordering::Relaxed), 1); assert_eq!(dropped2.load(Ordering::Relaxed), 0); drop(world); @@ -1242,15 +1361,13 @@ mod tests { #[test] fn clear_entities() { let mut world = World::default(); - world - .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) - .unwrap(); - world.insert_resource::(0); - world.spawn().insert(1u32); - world.spawn().insert(1.0f32); - let mut q1 = world.query::<&u32>(); - let mut q2 = world.query::<&f32>(); + world.insert_resource::(0); + world.spawn().insert(A(1)); + world.spawn().insert(SparseStored(1)); + + let mut q1 = world.query::<&A>(); + let mut q2 = world.query::<&SparseStored>(); assert_eq!(q1.iter(&world).len(), 1); assert_eq!(q2.iter(&world).len(), 1); @@ -1285,8 +1402,8 @@ mod tests { let mut world_a = World::default(); let mut world_b = World::default(); - let e1 = world_a.spawn().insert(1u32).id(); - let e2 = world_a.spawn().insert(2u32).id(); + let e1 = world_a.spawn().insert(A(1)).id(); + let e2 = world_a.spawn().insert(A(2)).id(); let e3 = world_a.entities().reserve_entity(); world_a.flush(); @@ -1296,7 +1413,7 @@ mod tests { .reserve_entities(world_a_max_entities as u32); world_b.entities.flush_as_invalid(); - let e4 = world_b.spawn().insert(4u32).id(); + let e4 = world_b.spawn().insert(A(4)).id(); assert_eq!( e4, Entity { @@ -1305,31 +1422,31 @@ mod tests { }, "new entity is created immediately after world_a's max entity" ); - assert!(world_b.get::(e1).is_none()); + assert!(world_b.get::(e1).is_none()); assert!(world_b.get_entity(e1).is_none()); - assert!(world_b.get::(e2).is_none()); + assert!(world_b.get::(e2).is_none()); assert!(world_b.get_entity(e2).is_none()); - assert!(world_b.get::(e3).is_none()); + assert!(world_b.get::(e3).is_none()); assert!(world_b.get_entity(e3).is_none()); - world_b.get_or_spawn(e1).unwrap().insert(1.0f32); + world_b.get_or_spawn(e1).unwrap().insert(B(1)); assert_eq!( - world_b.get::(e1), - Some(&1.0f32), + world_b.get::(e1), + Some(&B(1)), "spawning into 'world_a' entities works" ); - world_b.get_or_spawn(e4).unwrap().insert(4.0f32); + world_b.get_or_spawn(e4).unwrap().insert(B(4)); assert_eq!( - world_b.get::(e4), - Some(&4.0f32), + world_b.get::(e4), + Some(&B(4)), "spawning into existing `world_b` entities works" ); assert_eq!( - world_b.get::(e4), - Some(&4u32), + world_b.get::(e4), + Some(&A(4)), "spawning into existing `world_b` entities works" ); @@ -1342,13 +1459,13 @@ mod tests { "attempting to spawn on top of an entity with a mismatched entity generation fails" ); assert_eq!( - world_b.get::(e4), - Some(&4.0f32), + world_b.get::(e4), + Some(&B(4)), "failed mismatched spawn doesn't change existing entity" ); assert_eq!( - world_b.get::(e4), - Some(&4u32), + world_b.get::(e4), + Some(&A(4)), "failed mismatched spawn doesn't change existing entity" ); @@ -1359,10 +1476,10 @@ mod tests { world_b .get_or_spawn(high_non_existent_entity) .unwrap() - .insert(10.0f32); + .insert(B(10)); assert_eq!( - world_b.get::(high_non_existent_entity), - Some(&10.0f32), + world_b.get::(high_non_existent_entity), + Some(&B(10)), "inserting into newly allocated high / non-continous entity id works" ); diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 9e20c20876..9572a797f1 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -1,7 +1,7 @@ use crate::{ archetype::{Archetype, ArchetypeComponentId}, change_detection::Ticks, - component::{Component, ComponentId, ComponentTicks, StorageType}, + component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType}, entity::Entity, query::{Access, FilteredAccess}, storage::{ComponentSparseSet, Table, Tables}, @@ -67,7 +67,7 @@ pub trait Fetch<'world, 'state>: Sized { /// for "dense" queries. If this returns true, [`Fetch::set_table`] and [`Fetch::table_fetch`] /// will be called for iterators. If this returns false, [`Fetch::set_archetype`] and /// [`Fetch::archetype_fetch`] will be called for iterators. - fn is_dense(&self) -> bool; + const IS_DENSE: bool; /// Adjusts internal state to account for the next [`Archetype`]. This will always be called on /// archetypes that match this [`Fetch`]. @@ -178,10 +178,7 @@ impl<'w, 's> Fetch<'w, 's> for EntityFetch { type Item = Entity; type State = EntityState; - #[inline] - fn is_dense(&self) -> bool { - true - } + const IS_DENSE: bool = true; unsafe fn init( _world: &World, @@ -228,7 +225,6 @@ impl WorldQuery for &T { /// The [`FetchState`] of `&T`. pub struct ReadState { component_id: ComponentId, - storage_type: StorageType, marker: PhantomData, } @@ -236,10 +232,9 @@ pub struct ReadState { // read unsafe impl FetchState for ReadState { fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_id = world.init_component::(); ReadState { - component_id: component_info.id(), - storage_type: component_info.storage_type(), + component_id, marker: PhantomData, } } @@ -275,7 +270,6 @@ unsafe impl FetchState for ReadState { /// The [`Fetch`] of `&T`. pub struct ReadFetch { - storage_type: StorageType, table_components: NonNull, entity_table_rows: *const usize, entities: *const Entity, @@ -285,7 +279,6 @@ pub struct ReadFetch { impl Clone for ReadFetch { fn clone(&self) -> Self { Self { - storage_type: self.storage_type, table_components: self.table_components, entity_table_rows: self.entity_table_rows, entities: self.entities, @@ -301,13 +294,12 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch { type Item = &'w T; type State = ReadState; - #[inline] - fn is_dense(&self) -> bool { - match self.storage_type { + const IS_DENSE: bool = { + match T::Storage::STORAGE_TYPE { StorageType::Table => true, StorageType::SparseSet => false, } - } + }; unsafe fn init( world: &World, @@ -316,13 +308,12 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch { _change_tick: u32, ) -> Self { let mut value = Self { - storage_type: state.storage_type, table_components: NonNull::dangling(), entities: ptr::null::(), entity_table_rows: ptr::null::(), sparse_set: ptr::null::(), }; - if state.storage_type == StorageType::SparseSet { + if T::Storage::STORAGE_TYPE == StorageType::SparseSet { value.sparse_set = world .storages() .sparse_sets @@ -339,7 +330,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch { archetype: &Archetype, tables: &Tables, ) { - match state.storage_type { + match T::Storage::STORAGE_TYPE { StorageType::Table => { self.entity_table_rows = archetype.entity_table_rows().as_ptr(); let column = tables[archetype.table_id()] @@ -362,7 +353,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch { #[inline] unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { - match self.storage_type { + match T::Storage::STORAGE_TYPE { StorageType::Table => { let table_row = *self.entity_table_rows.add(archetype_index); &*self.table_components.as_ptr().add(table_row) @@ -387,7 +378,6 @@ impl WorldQuery for &mut T { /// The [`Fetch`] of `&mut T`. pub struct WriteFetch { - storage_type: StorageType, table_components: NonNull, table_ticks: *const UnsafeCell, entities: *const Entity, @@ -400,7 +390,6 @@ pub struct WriteFetch { impl Clone for WriteFetch { fn clone(&self) -> Self { Self { - storage_type: self.storage_type, table_components: self.table_components, table_ticks: self.table_ticks, entities: self.entities, @@ -415,7 +404,6 @@ impl Clone for WriteFetch { /// The [`FetchState`] of `&mut T`. pub struct WriteState { component_id: ComponentId, - storage_type: StorageType, marker: PhantomData, } @@ -423,10 +411,9 @@ pub struct WriteState { // written unsafe impl FetchState for WriteState { fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_id = world.init_component::(); WriteState { - component_id: component_info.id(), - storage_type: component_info.storage_type(), + component_id, marker: PhantomData, } } @@ -464,13 +451,12 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch { type Item = Mut<'w, T>; type State = WriteState; - #[inline] - fn is_dense(&self) -> bool { - match self.storage_type { + const IS_DENSE: bool = { + match T::Storage::STORAGE_TYPE { StorageType::Table => true, StorageType::SparseSet => false, } - } + }; unsafe fn init( world: &World, @@ -479,7 +465,6 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch { change_tick: u32, ) -> Self { let mut value = Self { - storage_type: state.storage_type, table_components: NonNull::dangling(), entities: ptr::null::(), entity_table_rows: ptr::null::(), @@ -488,7 +473,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch { last_change_tick, change_tick, }; - if state.storage_type == StorageType::SparseSet { + if T::Storage::STORAGE_TYPE == StorageType::SparseSet { value.sparse_set = world .storages() .sparse_sets @@ -505,7 +490,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch { archetype: &Archetype, tables: &Tables, ) { - match state.storage_type { + match T::Storage::STORAGE_TYPE { StorageType::Table => { self.entity_table_rows = archetype.entity_table_rows().as_ptr(); let column = tables[archetype.table_id()] @@ -527,7 +512,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch { #[inline] unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { - match self.storage_type { + match T::Storage::STORAGE_TYPE { StorageType::Table => { let table_row = *self.entity_table_rows.add(archetype_index); Mut { @@ -625,10 +610,7 @@ impl<'w, 's, T: Fetch<'w, 's>> Fetch<'w, 's> for OptionFetch { type Item = Option; type State = OptionState; - #[inline] - fn is_dense(&self) -> bool { - self.fetch.is_dense() - } + const IS_DENSE: bool = T::IS_DENSE; unsafe fn init( world: &World, @@ -693,12 +675,14 @@ impl<'w, 's, T: Fetch<'w, 's>> Fetch<'w, 's> for OptionFetch { /// # Examples /// /// ``` -/// # use bevy_ecs::system::Query; +/// # use bevy_ecs::component::Component; /// # use bevy_ecs::query::ChangeTrackers; /// # use bevy_ecs::system::IntoSystem; +/// # use bevy_ecs::system::Query; /// # -/// # #[derive(Debug)] +/// # #[derive(Component, Debug)] /// # struct Name {}; +/// # #[derive(Component)] /// # struct Transform {}; /// # /// fn print_moving_objects_system(query: Query<(&Name, ChangeTrackers)>) { @@ -752,7 +736,6 @@ impl WorldQuery for ChangeTrackers { /// The [`FetchState`] of [`ChangeTrackers`]. pub struct ChangeTrackersState { component_id: ComponentId, - storage_type: StorageType, marker: PhantomData, } @@ -760,10 +743,9 @@ pub struct ChangeTrackersState { // read unsafe impl FetchState for ChangeTrackersState { fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_id = world.init_component::(); Self { - component_id: component_info.id(), - storage_type: component_info.storage_type(), + component_id, marker: PhantomData, } } @@ -799,7 +781,6 @@ unsafe impl FetchState for ChangeTrackersState { /// The [`Fetch`] of [`ChangeTrackers`]. pub struct ChangeTrackersFetch { - storage_type: StorageType, table_ticks: *const ComponentTicks, entity_table_rows: *const usize, entities: *const Entity, @@ -812,7 +793,6 @@ pub struct ChangeTrackersFetch { impl Clone for ChangeTrackersFetch { fn clone(&self) -> Self { Self { - storage_type: self.storage_type, table_ticks: self.table_ticks, entity_table_rows: self.entity_table_rows, entities: self.entities, @@ -831,13 +811,12 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch { type Item = ChangeTrackers; type State = ChangeTrackersState; - #[inline] - fn is_dense(&self) -> bool { - match self.storage_type { + const IS_DENSE: bool = { + match T::Storage::STORAGE_TYPE { StorageType::Table => true, StorageType::SparseSet => false, } - } + }; unsafe fn init( world: &World, @@ -846,7 +825,6 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch { change_tick: u32, ) -> Self { let mut value = Self { - storage_type: state.storage_type, table_ticks: ptr::null::(), entities: ptr::null::(), entity_table_rows: ptr::null::(), @@ -855,7 +833,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch { last_change_tick, change_tick, }; - if state.storage_type == StorageType::SparseSet { + if T::Storage::STORAGE_TYPE == StorageType::SparseSet { value.sparse_set = world .storages() .sparse_sets @@ -872,7 +850,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch { archetype: &Archetype, tables: &Tables, ) { - match state.storage_type { + match T::Storage::STORAGE_TYPE { StorageType::Table => { self.entity_table_rows = archetype.entity_table_rows().as_ptr(); let column = tables[archetype.table_id()] @@ -894,7 +872,7 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch { #[inline] unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { - match self.storage_type { + match T::Storage::STORAGE_TYPE { StorageType::Table => { let table_row = *self.entity_table_rows.add(archetype_index); ChangeTrackers { @@ -940,12 +918,7 @@ macro_rules! impl_tuple_fetch { ($($name::init(_world, $name, _last_change_tick, _change_tick),)*) } - - #[inline] - fn is_dense(&self) -> bool { - let ($($name,)*) = self; - true $(&& $name.is_dense())* - } + const IS_DENSE: bool = true $(&& $name::IS_DENSE)*; #[inline] unsafe fn set_archetype(&mut self, _state: &Self::State, _archetype: &Archetype, _tables: &Tables) { diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 69dd298cd3..bbd8ec567a 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -1,6 +1,6 @@ use crate::{ archetype::{Archetype, ArchetypeComponentId}, - component::{Component, ComponentId, ComponentTicks, StorageType}, + component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType}, entity::Entity, query::{Access, Fetch, FetchState, FilteredAccess, WorldQuery}, storage::{ComponentSparseSet, Table, Tables}, @@ -50,12 +50,14 @@ where /// # Examples /// /// ``` -/// # use bevy_ecs::system::Query; +/// # use bevy_ecs::component::Component; /// # use bevy_ecs::query::With; /// # use bevy_ecs::system::IntoSystem; +/// # use bevy_ecs::system::Query; /// # -/// # #[derive(Debug)] -/// # struct IsBeautiful {}; +/// # #[derive(Component)] +/// # struct IsBeautiful; +/// # #[derive(Component)] /// # struct Name { name: &'static str }; /// # /// fn compliment_entity_system(query: Query<&Name, With>) { @@ -74,24 +76,21 @@ impl WorldQuery for With { /// The [`Fetch`] of [`With`]. pub struct WithFetch { - storage_type: StorageType, marker: PhantomData, } /// The [`FetchState`] of [`With`]. pub struct WithState { component_id: ComponentId, - storage_type: StorageType, marker: PhantomData, } // SAFETY: no component access or archetype component access unsafe impl FetchState for WithState { fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_id = world.init_component::(); Self { - component_id: component_info.id(), - storage_type: component_info.storage_type(), + component_id, marker: PhantomData, } } @@ -124,20 +123,21 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithFetch { unsafe fn init( _world: &World, - state: &Self::State, + _state: &Self::State, _last_change_tick: u32, _change_tick: u32, ) -> Self { Self { - storage_type: state.storage_type, marker: PhantomData, } } - #[inline] - fn is_dense(&self) -> bool { - self.storage_type == StorageType::Table - } + const IS_DENSE: bool = { + match T::Storage::STORAGE_TYPE { + StorageType::Table => true, + StorageType::SparseSet => false, + } + }; #[inline] unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {} @@ -169,12 +169,14 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithFetch { /// # Examples /// /// ``` -/// # use bevy_ecs::system::Query; +/// # use bevy_ecs::component::Component; /// # use bevy_ecs::query::Without; /// # use bevy_ecs::system::IntoSystem; +/// # use bevy_ecs::system::Query; /// # -/// # #[derive(Debug)] +/// # #[derive(Component)] /// # struct Permit; +/// # #[derive(Component)] /// # struct Name { name: &'static str }; /// # /// fn no_permit_system(query: Query<&Name, Without>) { @@ -193,24 +195,21 @@ impl WorldQuery for Without { /// The [`Fetch`] of [`Without`]. pub struct WithoutFetch { - storage_type: StorageType, marker: PhantomData, } /// The [`FetchState`] of [`Without`]. pub struct WithoutState { component_id: ComponentId, - storage_type: StorageType, marker: PhantomData, } // SAFETY: no component access or archetype component access unsafe impl FetchState for WithoutState { fn init(world: &mut World) -> Self { - let component_info = world.components.get_or_insert_info::(); + let component_id = world.init_component::(); Self { - component_id: component_info.id(), - storage_type: component_info.storage_type(), + component_id, marker: PhantomData, } } @@ -243,20 +242,21 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutFetch { unsafe fn init( _world: &World, - state: &Self::State, + _state: &Self::State, _last_change_tick: u32, _change_tick: u32, ) -> Self { Self { - storage_type: state.storage_type, marker: PhantomData, } } - #[inline] - fn is_dense(&self) -> bool { - self.storage_type == StorageType::Table - } + const IS_DENSE: bool = { + match T::Storage::STORAGE_TYPE { + StorageType::Table => true, + StorageType::SparseSet => false, + } + }; #[inline] unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {} @@ -292,14 +292,16 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutFetch { /// # Examples /// /// ``` +/// # use bevy_ecs::component::Component; /// # use bevy_ecs::entity::Entity; -/// # use bevy_ecs::system::Query; -/// # use bevy_ecs::system::IntoSystem; /// # use bevy_ecs::query::Changed; /// # use bevy_ecs::query::Or; +/// # use bevy_ecs::system::IntoSystem; +/// # use bevy_ecs::system::Query; /// # -/// # #[derive(Debug)] +/// # #[derive(Component, Debug)] /// # struct Color {}; +/// # #[derive(Component)] /// # struct Style {}; /// # /// fn print_cool_entity_system(query: Query, Changed