mirror of
https://github.com/bevyengine/bevy
synced 2024-11-24 13:43:04 +00:00
Add upstream bevy_ecs and prepare for custom-shaders merge (#2815)
This updates the `pipelined-rendering` branch to use the latest `bevy_ecs` from `main`. This accomplishes a couple of goals: 1. prepares for upcoming `custom-shaders` branch changes, which were what drove many of the recent bevy_ecs changes on `main` 2. prepares for the soon-to-happen merge of `pipelined-rendering` into `main`. By including bevy_ecs changes now, we make that merge simpler / easier to review. I split this up into 3 commits: 1. **add upstream bevy_ecs**: please don't bother reviewing this content. it has already received thorough review on `main` and is a literal copy/paste of the relevant folders (the old folders were deleted so the directories are literally exactly the same as `main`). 2. **support manual buffer application in stages**: this is used to enable the Extract step. we've already reviewed this once on the `pipelined-rendering` branch, but its worth looking at one more time in the new context of (1). 3. **support manual archetype updates in QueryState**: same situation as (2).
This commit is contained in:
parent
59bfbd3295
commit
11b41206eb
147 changed files with 2321 additions and 1767 deletions
|
@ -365,10 +365,6 @@ path = "examples/ecs/system_sets.rs"
|
|||
name = "timers"
|
||||
path = "examples/ecs/timers.rs"
|
||||
|
||||
[[example]]
|
||||
name = "query_bundle"
|
||||
path = "examples/ecs/query_bundle.rs"
|
||||
|
||||
# Games
|
||||
[[example]]
|
||||
name = "alien_cake_addict"
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
name = "bevy_ecs"
|
||||
version = "0.5.0"
|
||||
edition = "2018"
|
||||
authors = [
|
||||
"Bevy Contributors <bevyengine@gmail.com>",
|
||||
"Carter Anderson <mcanders1@gmail.com>",
|
||||
]
|
||||
description = "Bevy Engine's entity component system"
|
||||
homepage = "https://bevyengine.org"
|
||||
repository = "https://github.com/bevyengine/bevy"
|
||||
|
|
|
@ -131,7 +131,7 @@ fn main() {
|
|||
// Add a Stage to our schedule. Each Stage in a schedule runs all of its systems
|
||||
// before moving on to the next Stage
|
||||
schedule.add_stage("update", SystemStage::parallel()
|
||||
.with_system(movement.system())
|
||||
.with_system(movement)
|
||||
);
|
||||
|
||||
// Run the schedule once. If your app has a "loop", you would run this once per loop
|
||||
|
|
|
@ -24,15 +24,11 @@ fn main() {
|
|||
|
||||
// Add systems to the Stage to execute our app logic
|
||||
// We can label our systems to force a specific run-order between some of them
|
||||
update.add_system(spawn_entities.system().label(SimulationSystem::Spawn));
|
||||
update.add_system(
|
||||
print_counter_when_changed
|
||||
.system()
|
||||
.after(SimulationSystem::Spawn),
|
||||
);
|
||||
update.add_system(age_all_entities.system().label(SimulationSystem::Age));
|
||||
update.add_system(remove_old_entities.system().after(SimulationSystem::Age));
|
||||
update.add_system(print_changed_entities.system().after(SimulationSystem::Age));
|
||||
update.add_system(spawn_entities.label(SimulationSystem::Spawn));
|
||||
update.add_system(print_counter_when_changed.after(SimulationSystem::Spawn));
|
||||
update.add_system(age_all_entities.label(SimulationSystem::Age));
|
||||
update.add_system(remove_old_entities.after(SimulationSystem::Age));
|
||||
update.add_system(print_changed_entities.after(SimulationSystem::Age));
|
||||
// Add the Stage with our systems to the Schedule
|
||||
schedule.add_stage("update", update);
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ fn main() {
|
|||
// 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.system());
|
||||
update.add_system(query_entities);
|
||||
schedule.add_stage("update", update);
|
||||
|
||||
schedule.run(&mut world);
|
||||
|
|
|
@ -16,13 +16,13 @@ fn main() {
|
|||
// called "second". In "first" we update the events and in "second" we run our systems
|
||||
// sending and receiving events.
|
||||
let mut first = SystemStage::parallel();
|
||||
first.add_system(Events::<MyEvent>::update_system.system());
|
||||
first.add_system(Events::<MyEvent>::update_system);
|
||||
schedule.add_stage("first", first);
|
||||
|
||||
// Add systems sending and receiving events to a "second" Stage
|
||||
let mut second = SystemStage::parallel();
|
||||
second.add_system(sending_system.system().label(EventSystem::Sending));
|
||||
second.add_system(receiving_system.system().after(EventSystem::Sending));
|
||||
second.add_system(sending_system.label(EventSystem::Sending));
|
||||
second.add_system(receiving_system.after(EventSystem::Sending));
|
||||
|
||||
// Run the "second" Stage after the "first" Stage, so our Events always get updated before we use them
|
||||
schedule.add_stage_after("first", "second", second);
|
||||
|
@ -41,7 +41,6 @@ enum EventSystem {
|
|||
}
|
||||
|
||||
// This is our event that we will send and receive in systems
|
||||
#[derive(Debug)]
|
||||
struct MyEvent {
|
||||
pub message: String,
|
||||
pub random_value: f32,
|
||||
|
@ -62,6 +61,9 @@ fn sending_system(mut event_writer: EventWriter<MyEvent>) {
|
|||
// If an event is received it will be printed to the console
|
||||
fn receiving_system(mut event_reader: EventReader<MyEvent>) {
|
||||
for my_event in event_reader.iter() {
|
||||
println!(" Received: {:?}", *my_event);
|
||||
println!(
|
||||
" Received message {:?}, with random value of {}",
|
||||
my_event.message, my_event.random_value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ fn main() {
|
|||
let mut update = SystemStage::parallel();
|
||||
|
||||
// Add systems to increase the counter and to print out the current value
|
||||
update.add_system(increase_counter.system().label(CounterSystem::Increase));
|
||||
update.add_system(print_counter.system().after(CounterSystem::Increase));
|
||||
update.add_system(increase_counter.label(CounterSystem::Increase));
|
||||
update.add_system(print_counter.after(CounterSystem::Increase));
|
||||
schedule.add_stage("update", update);
|
||||
|
||||
for iteration in 1..=10 {
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use bevy_macro_utils::{derive_label, BevyManifest};
|
||||
use bevy_macro_utils::BevyManifest;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
parse_macro_input,
|
||||
punctuated::Punctuated,
|
||||
token::Comma,
|
||||
Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, Lifetime, LitInt,
|
||||
Result, Token,
|
||||
Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, LitInt, Path, Result,
|
||||
Token,
|
||||
};
|
||||
|
||||
struct AllTuples {
|
||||
|
@ -110,15 +110,15 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
|||
.map(|field| &field.ty)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut field_type_infos = Vec::new();
|
||||
let mut field_component_ids = Vec::new();
|
||||
let mut field_get_components = Vec::new();
|
||||
let mut field_from_components = Vec::new();
|
||||
for ((field_type, is_bundle), field) in
|
||||
field_type.iter().zip(is_bundle.iter()).zip(field.iter())
|
||||
{
|
||||
if *is_bundle {
|
||||
field_type_infos.push(quote! {
|
||||
type_info.extend(<#field_type as #ecs_path::bundle::Bundle>::type_info());
|
||||
field_component_ids.push(quote! {
|
||||
component_ids.extend(<#field_type as #ecs_path::bundle::Bundle>::component_ids(components));
|
||||
});
|
||||
field_get_components.push(quote! {
|
||||
self.#field.get_components(&mut func);
|
||||
|
@ -127,8 +127,8 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
|||
#field: <#field_type as #ecs_path::bundle::Bundle>::from_components(&mut func),
|
||||
});
|
||||
} else {
|
||||
field_type_infos.push(quote! {
|
||||
type_info.push(#ecs_path::component::TypeInfo::of::<#field_type>());
|
||||
field_component_ids.push(quote! {
|
||||
component_ids.push(components.get_or_insert_id::<#field_type>());
|
||||
});
|
||||
field_get_components.push(quote! {
|
||||
func((&mut self.#field as *mut #field_type).cast::<u8>());
|
||||
|
@ -147,10 +147,12 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
|||
TokenStream::from(quote! {
|
||||
/// SAFE: TypeInfo 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 type_info() -> Vec<#ecs_path::component::TypeInfo> {
|
||||
let mut type_info = Vec::with_capacity(#field_len);
|
||||
#(#field_type_infos)*
|
||||
type_info
|
||||
fn component_ids(
|
||||
components: &mut #ecs_path::component::Components,
|
||||
) -> Vec<#ecs_path::component::ComponentId> {
|
||||
let mut component_ids = Vec::with_capacity(#field_len);
|
||||
#(#field_component_ids)*
|
||||
component_ids
|
||||
}
|
||||
|
||||
#[allow(unused_variables, unused_mut, non_snake_case)]
|
||||
|
@ -174,38 +176,26 @@ fn get_idents(fmt_string: fn(usize) -> String, count: usize) -> Vec<Ident> {
|
|||
.collect::<Vec<Ident>>()
|
||||
}
|
||||
|
||||
fn get_lifetimes(fmt_string: fn(usize) -> String, count: usize) -> Vec<Lifetime> {
|
||||
(0..count)
|
||||
.map(|i| Lifetime::new(&fmt_string(i), Span::call_site()))
|
||||
.collect::<Vec<Lifetime>>()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn impl_query_set(_input: TokenStream) -> TokenStream {
|
||||
let mut tokens = TokenStream::new();
|
||||
let max_queries = 4;
|
||||
let queries = get_idents(|i| format!("Q{}", i), max_queries);
|
||||
let filters = get_idents(|i| format!("F{}", i), max_queries);
|
||||
let lifetimes = get_lifetimes(|i| format!("'q{}", i), max_queries);
|
||||
let state_lifetimes = get_lifetimes(|i| format!("'qs{}", i), max_queries);
|
||||
let mut query_fns = Vec::new();
|
||||
let mut query_fn_muts = Vec::new();
|
||||
for i in 0..max_queries {
|
||||
let query = &queries[i];
|
||||
let filter = &filters[i];
|
||||
let lifetime = &lifetimes[i];
|
||||
let state_lifetime = &state_lifetimes[i];
|
||||
let fn_name = Ident::new(&format!("q{}", i), Span::call_site());
|
||||
let fn_name_mut = Ident::new(&format!("q{}_mut", i), Span::call_site());
|
||||
let index = Index::from(i);
|
||||
query_fns.push(quote! {
|
||||
pub fn #fn_name(&self) -> &Query<#lifetime, #state_lifetime, #query, #filter> {
|
||||
&self.0.#index
|
||||
}
|
||||
});
|
||||
query_fn_muts.push(quote! {
|
||||
pub fn #fn_name_mut(&mut self) -> &mut Query<#lifetime, #state_lifetime, #query, #filter> {
|
||||
&mut self.0.#index
|
||||
pub fn #fn_name(&mut self) -> Query<'_, '_, #query, #filter> {
|
||||
// SAFE: systems run without conflicts with other systems.
|
||||
// Conflicting queries in QuerySet are not accessible at the same time
|
||||
// QuerySets are guaranteed to not conflict with other SystemParams
|
||||
unsafe {
|
||||
Query::new(self.world, &self.query_states.#index, self.last_change_tick, self.change_tick)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -213,12 +203,9 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
|
|||
for query_count in 1..=max_queries {
|
||||
let query = &queries[0..query_count];
|
||||
let filter = &filters[0..query_count];
|
||||
let lifetime = &lifetimes[0..query_count];
|
||||
let state_lifetime = &state_lifetimes[0..query_count];
|
||||
let query_fn = &query_fns[0..query_count];
|
||||
let query_fn_mut = &query_fn_muts[0..query_count];
|
||||
tokens.extend(TokenStream::from(quote! {
|
||||
impl<'s, #(#lifetime,)* #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParam for QuerySet<(#(Query<#lifetime, 's, #query, #filter>,)*)>
|
||||
impl<'w, 's, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParam for QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)>
|
||||
where #(#filter::Fetch: FilterFetch,)*
|
||||
{
|
||||
type Fetch = QuerySetState<(#(QueryState<#query, #filter>,)*)>;
|
||||
|
@ -271,10 +258,10 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamFetch<'s, 'w> for QuerySetState<(#(QueryState<#query, #filter>,)*)>
|
||||
impl<'w, 's, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamFetch<'w, 's> for QuerySetState<(#(QueryState<#query, #filter>,)*)>
|
||||
where #(#filter::Fetch: FilterFetch,)*
|
||||
{
|
||||
type Item = QuerySet<(#(Query<'w, 's, #query, #filter>,)*)>;
|
||||
type Item = QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_param(
|
||||
|
@ -283,15 +270,18 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
|
|||
world: &'w World,
|
||||
change_tick: u32,
|
||||
) -> Self::Item {
|
||||
let (#(#query,)*) = &state.0;
|
||||
QuerySet((#(Query::new(world, #query, system_meta.last_change_tick, change_tick),)*))
|
||||
QuerySet {
|
||||
query_states: &state.0,
|
||||
world,
|
||||
last_change_tick: system_meta.last_change_tick,
|
||||
change_tick,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<#(#state_lifetime,)* #(#lifetime,)* #(#query: WorldQuery,)* #(#filter: WorldQuery,)*> QuerySet<(#(Query<#lifetime, #state_lifetime, #query, #filter>,)*)>
|
||||
impl<'w, 's, #(#query: WorldQuery,)* #(#filter: WorldQuery,)*> QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)>
|
||||
where #(#filter::Fetch: FilterFetch,)*
|
||||
{
|
||||
#(#query_fn)*
|
||||
#(#query_fn_mut)*
|
||||
}
|
||||
}));
|
||||
|
@ -385,7 +375,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
|
|||
|
||||
TokenStream::from(quote! {
|
||||
impl #impl_generics #path::system::SystemParam for #struct_name#ty_generics #where_clause {
|
||||
type Fetch = #fetch_struct_name <(#(<#field_types as SystemParam>::Fetch,)*), #punctuated_generic_idents>;
|
||||
type Fetch = #fetch_struct_name <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -416,7 +406,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
impl #impl_generics #path::system::SystemParamFetch<'s, 'w> for #fetch_struct_name <(#(<#field_types as SystemParam>::Fetch,)*), #punctuated_generic_idents> {
|
||||
impl #impl_generics #path::system::SystemParamFetch<'w, 's> for #fetch_struct_name <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents> {
|
||||
type Item = #struct_name#ty_generics;
|
||||
unsafe fn get_param(
|
||||
state: &'s mut Self,
|
||||
|
@ -425,7 +415,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
|
|||
change_tick: u32,
|
||||
) -> Self::Item {
|
||||
#struct_name {
|
||||
#(#fields: <<#field_types as SystemParam>::Fetch as #path::system::SystemParamFetch>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)*
|
||||
#(#fields: <<#field_types as #path::system::SystemParam>::Fetch as #path::system::SystemParamFetch>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)*
|
||||
#(#ignored_fields: <#ignored_field_types>::default(),)*
|
||||
}
|
||||
}
|
||||
|
@ -436,43 +426,46 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
|
|||
#[proc_macro_derive(SystemLabel)]
|
||||
pub fn derive_system_label(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let mut trait_path = bevy_ecs_path();
|
||||
trait_path.segments.push(format_ident!("schedule").into());
|
||||
trait_path
|
||||
.segments
|
||||
.push(format_ident!("SystemLabel").into());
|
||||
derive_label(input, trait_path)
|
||||
|
||||
derive_label(input, Ident::new("SystemLabel", Span::call_site())).into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(StageLabel)]
|
||||
pub fn derive_stage_label(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let mut trait_path = bevy_ecs_path();
|
||||
trait_path.segments.push(format_ident!("schedule").into());
|
||||
trait_path.segments.push(format_ident!("StageLabel").into());
|
||||
derive_label(input, trait_path)
|
||||
derive_label(input, Ident::new("StageLabel", Span::call_site())).into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(AmbiguitySetLabel)]
|
||||
pub fn derive_ambiguity_set_label(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let mut trait_path = bevy_ecs_path();
|
||||
trait_path.segments.push(format_ident!("schedule").into());
|
||||
trait_path
|
||||
.segments
|
||||
.push(format_ident!("AmbiguitySetLabel").into());
|
||||
derive_label(input, trait_path)
|
||||
derive_label(input, Ident::new("AmbiguitySetLabel", Span::call_site())).into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(RunCriteriaLabel)]
|
||||
pub fn derive_run_criteria_label(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let mut trait_path = bevy_ecs_path();
|
||||
trait_path.segments.push(format_ident!("schedule").into());
|
||||
trait_path
|
||||
.segments
|
||||
.push(format_ident!("RunCriteriaLabel").into());
|
||||
derive_label(input, trait_path)
|
||||
derive_label(input, Ident::new("RunCriteriaLabel", Span::call_site())).into()
|
||||
}
|
||||
|
||||
fn derive_label(input: DeriveInput, label_type: Ident) -> TokenStream2 {
|
||||
let ident = input.ident;
|
||||
let ecs_path: Path = bevy_ecs_path();
|
||||
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
|
||||
where_token: Default::default(),
|
||||
predicates: Default::default(),
|
||||
});
|
||||
where_clause.predicates.push(syn::parse2(quote! { Self: Eq + ::std::fmt::Debug + ::std::hash::Hash + Clone + Send + Sync + 'static }).unwrap());
|
||||
|
||||
quote! {
|
||||
impl #impl_generics #ecs_path::schedule::#label_type for #ident #ty_generics #where_clause {
|
||||
fn dyn_clone(&self) -> Box<dyn #ecs_path::schedule::#label_type> {
|
||||
Box::new(Clone::clone(self))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bevy_ecs_path() -> syn::Path {
|
||||
|
|
|
@ -15,26 +15,15 @@ use std::{
|
|||
pub struct ArchetypeId(usize);
|
||||
|
||||
impl ArchetypeId {
|
||||
pub const EMPTY: ArchetypeId = ArchetypeId(0);
|
||||
pub const RESOURCE: ArchetypeId = ArchetypeId(1);
|
||||
pub const INVALID: ArchetypeId = ArchetypeId(usize::MAX);
|
||||
|
||||
#[inline]
|
||||
pub const fn new(index: usize) -> Self {
|
||||
ArchetypeId(index)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn empty() -> ArchetypeId {
|
||||
ArchetypeId(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn invalid() -> ArchetypeId {
|
||||
ArchetypeId(usize::MAX)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn resource() -> ArchetypeId {
|
||||
ArchetypeId(1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn index(self) -> usize {
|
||||
self.0
|
||||
|
@ -65,7 +54,7 @@ impl Edges {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_add_bundle(
|
||||
pub fn insert_add_bundle(
|
||||
&mut self,
|
||||
bundle_id: BundleId,
|
||||
archetype_id: ArchetypeId,
|
||||
|
@ -86,7 +75,7 @@ impl Edges {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_remove_bundle(&mut self, bundle_id: BundleId, archetype_id: Option<ArchetypeId>) {
|
||||
pub fn insert_remove_bundle(&mut self, bundle_id: BundleId, archetype_id: Option<ArchetypeId>) {
|
||||
self.remove_bundle.insert(bundle_id, archetype_id);
|
||||
}
|
||||
|
||||
|
@ -99,7 +88,7 @@ impl Edges {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_remove_bundle_intersection(
|
||||
pub fn insert_remove_bundle_intersection(
|
||||
&mut self,
|
||||
bundle_id: BundleId,
|
||||
archetype_id: Option<ArchetypeId>,
|
||||
|
@ -115,8 +104,8 @@ struct TableInfo {
|
|||
}
|
||||
|
||||
pub(crate) struct ArchetypeSwapRemoveResult {
|
||||
pub swapped_entity: Option<Entity>,
|
||||
pub table_row: usize,
|
||||
pub(crate) swapped_entity: Option<Entity>,
|
||||
pub(crate) table_row: usize,
|
||||
}
|
||||
|
||||
pub(crate) struct ArchetypeComponentInfo {
|
||||
|
@ -387,7 +376,7 @@ impl Default for Archetypes {
|
|||
// adds the resource archetype. it is "special" in that it is inaccessible via a "hash",
|
||||
// which prevents entities from being added to it
|
||||
archetypes.archetypes.push(Archetype::new(
|
||||
ArchetypeId::resource(),
|
||||
ArchetypeId::RESOURCE,
|
||||
TableId::empty(),
|
||||
Cow::Owned(Vec::new()),
|
||||
Cow::Owned(Vec::new()),
|
||||
|
@ -412,7 +401,7 @@ impl Archetypes {
|
|||
#[inline]
|
||||
pub fn empty(&self) -> &Archetype {
|
||||
// SAFE: empty archetype always exists
|
||||
unsafe { self.archetypes.get_unchecked(ArchetypeId::empty().index()) }
|
||||
unsafe { self.archetypes.get_unchecked(ArchetypeId::EMPTY.index()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -420,17 +409,14 @@ impl Archetypes {
|
|||
// SAFE: empty archetype always exists
|
||||
unsafe {
|
||||
self.archetypes
|
||||
.get_unchecked_mut(ArchetypeId::empty().index())
|
||||
.get_unchecked_mut(ArchetypeId::EMPTY.index())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn resource(&self) -> &Archetype {
|
||||
// SAFE: resource archetype always exists
|
||||
unsafe {
|
||||
self.archetypes
|
||||
.get_unchecked(ArchetypeId::resource().index())
|
||||
}
|
||||
unsafe { self.archetypes.get_unchecked(ArchetypeId::RESOURCE.index()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -438,7 +424,7 @@ impl Archetypes {
|
|||
// SAFE: resource archetype always exists
|
||||
unsafe {
|
||||
self.archetypes
|
||||
.get_unchecked_mut(ArchetypeId::resource().index())
|
||||
.get_unchecked_mut(ArchetypeId::RESOURCE.index())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
pub use bevy_ecs_macros::Bundle;
|
||||
|
||||
use crate::{
|
||||
archetype::ComponentStatus,
|
||||
component::{Component, ComponentId, ComponentTicks, Components, StorageType, TypeInfo},
|
||||
entity::Entity,
|
||||
storage::{SparseSetIndex, SparseSets, Table},
|
||||
archetype::{AddBundle, Archetype, ArchetypeId, Archetypes, ComponentStatus},
|
||||
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
|
||||
entity::{Entities, Entity, EntityLocation},
|
||||
storage::{SparseSetIndex, SparseSets, Storages, Table},
|
||||
};
|
||||
use bevy_ecs_macros::all_tuples;
|
||||
use std::{any::TypeId, collections::HashMap};
|
||||
|
@ -12,8 +12,6 @@ use std::{any::TypeId, collections::HashMap};
|
|||
/// An ordered collection of components, commonly used for spawning entities, and adding and
|
||||
/// removing components in bulk.
|
||||
///
|
||||
/// In order to query for components in a bundle use [crate::query::WithBundle].
|
||||
///
|
||||
/// Typically, you will simply use `#[derive(Bundle)]` when creating your own `Bundle`.
|
||||
/// The `Bundle` trait is automatically implemented for tuples of components:
|
||||
/// `(ComponentA, ComponentB)` is a very convenient shorthand when working with one-off collections
|
||||
|
@ -38,13 +36,13 @@ use std::{any::TypeId, collections::HashMap};
|
|||
/// ```
|
||||
///
|
||||
/// # Safety
|
||||
/// [Bundle::type_info] must return the TypeInfo for each component type in the bundle, in the
|
||||
/// [Bundle::component_id] must return the ComponentId for each component type in the bundle, in the
|
||||
/// _exact_ order that [Bundle::get_components] is called.
|
||||
/// [Bundle::from_components] must call `func` exactly once for each [TypeInfo] returned by
|
||||
/// [Bundle::type_info]
|
||||
/// [Bundle::from_components] must call `func` exactly once for each [ComponentId] returned by
|
||||
/// [Bundle::component_id]
|
||||
pub unsafe trait Bundle: Send + Sync + 'static {
|
||||
/// Gets this [Bundle]'s components type info, in the order of this bundle's Components
|
||||
fn type_info() -> Vec<TypeInfo>;
|
||||
/// Gets this [Bundle]'s component ids, in the order of this bundle's Components
|
||||
fn component_ids(components: &mut Components) -> Vec<ComponentId>;
|
||||
|
||||
/// Calls `func`, which should return data for each component in the bundle, in the order of
|
||||
/// this bundle's Components
|
||||
|
@ -66,8 +64,9 @@ macro_rules! tuple_impl {
|
|||
($($name: ident),*) => {
|
||||
/// SAFE: TypeInfo is returned in tuple-order. [Bundle::from_components] and [Bundle::get_components] use tuple-order
|
||||
unsafe impl<$($name: Component),*> Bundle for ($($name,)*) {
|
||||
fn type_info() -> Vec<TypeInfo> {
|
||||
vec![$(TypeInfo::of::<$name>()),*]
|
||||
#[allow(unused_variables)]
|
||||
fn component_ids(components: &mut Components) -> Vec<ComponentId> {
|
||||
vec![$(components.get_or_insert_id::<$name>()),*]
|
||||
}
|
||||
|
||||
#[allow(unused_variables, unused_mut)]
|
||||
|
@ -123,19 +122,119 @@ pub struct BundleInfo {
|
|||
}
|
||||
|
||||
impl BundleInfo {
|
||||
/// # Safety
|
||||
/// table row must exist, entity must be valid
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn write_components<T: Bundle>(
|
||||
&self,
|
||||
sparse_sets: &mut SparseSets,
|
||||
entity: Entity,
|
||||
table: &mut Table,
|
||||
table_row: usize,
|
||||
bundle_status: &[ComponentStatus],
|
||||
bundle: T,
|
||||
pub fn id(&self) -> BundleId {
|
||||
self.id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn components(&self) -> &[ComponentId] {
|
||||
&self.component_ids
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn storage_types(&self) -> &[StorageType] {
|
||||
&self.storage_types
|
||||
}
|
||||
|
||||
pub(crate) fn get_bundle_inserter<'a, 'b>(
|
||||
&'b self,
|
||||
entities: &'a mut Entities,
|
||||
archetypes: &'a mut Archetypes,
|
||||
components: &mut Components,
|
||||
storages: &'a mut Storages,
|
||||
archetype_id: ArchetypeId,
|
||||
change_tick: u32,
|
||||
) -> BundleInserter<'a, 'b> {
|
||||
let new_archetype_id =
|
||||
self.add_bundle_to_archetype(archetypes, storages, components, archetype_id);
|
||||
let archetypes_ptr = archetypes.archetypes.as_mut_ptr();
|
||||
if new_archetype_id == archetype_id {
|
||||
let archetype = &mut archetypes[archetype_id];
|
||||
let table_id = archetype.table_id();
|
||||
BundleInserter {
|
||||
bundle_info: self,
|
||||
archetype,
|
||||
entities,
|
||||
sparse_sets: &mut storages.sparse_sets,
|
||||
table: &mut storages.tables[table_id],
|
||||
archetypes_ptr,
|
||||
change_tick,
|
||||
result: InsertBundleResult::SameArchetype,
|
||||
}
|
||||
} else {
|
||||
let (archetype, new_archetype) = archetypes.get_2_mut(archetype_id, new_archetype_id);
|
||||
let table_id = archetype.table_id();
|
||||
if table_id == new_archetype.table_id() {
|
||||
BundleInserter {
|
||||
bundle_info: self,
|
||||
archetype,
|
||||
archetypes_ptr,
|
||||
entities,
|
||||
sparse_sets: &mut storages.sparse_sets,
|
||||
table: &mut storages.tables[table_id],
|
||||
change_tick,
|
||||
result: InsertBundleResult::NewArchetypeSameTable { new_archetype },
|
||||
}
|
||||
} else {
|
||||
let (table, new_table) = storages
|
||||
.tables
|
||||
.get_2_mut(table_id, new_archetype.table_id());
|
||||
BundleInserter {
|
||||
bundle_info: self,
|
||||
archetype,
|
||||
sparse_sets: &mut storages.sparse_sets,
|
||||
entities,
|
||||
archetypes_ptr,
|
||||
table,
|
||||
change_tick,
|
||||
result: InsertBundleResult::NewArchetypeNewTable {
|
||||
new_archetype,
|
||||
new_table,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_bundle_spawner<'a, 'b>(
|
||||
&'b self,
|
||||
entities: &'a mut Entities,
|
||||
archetypes: &'a mut Archetypes,
|
||||
components: &mut Components,
|
||||
storages: &'a mut Storages,
|
||||
change_tick: u32,
|
||||
) -> BundleSpawner<'a, 'b> {
|
||||
let new_archetype_id =
|
||||
self.add_bundle_to_archetype(archetypes, storages, components, ArchetypeId::EMPTY);
|
||||
let (empty_archetype, archetype) =
|
||||
archetypes.get_2_mut(ArchetypeId::EMPTY, new_archetype_id);
|
||||
let table = &mut storages.tables[archetype.table_id()];
|
||||
let add_bundle = empty_archetype.edges().get_add_bundle(self.id()).unwrap();
|
||||
BundleSpawner {
|
||||
archetype,
|
||||
add_bundle,
|
||||
bundle_info: self,
|
||||
table,
|
||||
entities,
|
||||
sparse_sets: &mut storages.sparse_sets,
|
||||
change_tick,
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// `table` must be the "new" table for `entity`. `table_row` must have space allocated for the `entity`, `bundle` must match this BundleInfo's type
|
||||
#[inline]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
unsafe fn write_components<T: Bundle>(
|
||||
&self,
|
||||
table: &mut Table,
|
||||
sparse_sets: &mut SparseSets,
|
||||
add_bundle: &AddBundle,
|
||||
entity: Entity,
|
||||
table_row: usize,
|
||||
change_tick: u32,
|
||||
bundle: T,
|
||||
) {
|
||||
// NOTE: get_components calls this closure on each component in "bundle order".
|
||||
// bundle_info.component_ids are also in "bundle order"
|
||||
|
@ -145,7 +244,7 @@ impl BundleInfo {
|
|||
match self.storage_types[bundle_component] {
|
||||
StorageType::Table => {
|
||||
let column = table.get_column_mut(component_id).unwrap();
|
||||
match bundle_status.get_unchecked(bundle_component) {
|
||||
match add_bundle.bundle_status.get_unchecked(bundle_component) {
|
||||
ComponentStatus::Added => {
|
||||
column.initialize(
|
||||
table_row,
|
||||
|
@ -167,19 +266,277 @@ impl BundleInfo {
|
|||
});
|
||||
}
|
||||
|
||||
/// Adds a bundle to the given archetype and returns the resulting archetype. This could be the same
|
||||
/// [ArchetypeId], in the event that adding the given bundle does not result in an Archetype change.
|
||||
/// Results are cached in the Archetype Graph to avoid redundant work.
|
||||
pub(crate) fn add_bundle_to_archetype(
|
||||
&self,
|
||||
archetypes: &mut Archetypes,
|
||||
storages: &mut Storages,
|
||||
components: &mut Components,
|
||||
archetype_id: ArchetypeId,
|
||||
) -> ArchetypeId {
|
||||
if let Some(add_bundle) = archetypes[archetype_id].edges().get_add_bundle(self.id) {
|
||||
return add_bundle.archetype_id;
|
||||
}
|
||||
let mut new_table_components = Vec::new();
|
||||
let mut new_sparse_set_components = Vec::new();
|
||||
let mut bundle_status = Vec::with_capacity(self.component_ids.len());
|
||||
|
||||
let current_archetype = &mut archetypes[archetype_id];
|
||||
for component_id in self.component_ids.iter().cloned() {
|
||||
if current_archetype.contains(component_id) {
|
||||
bundle_status.push(ComponentStatus::Mutated);
|
||||
} else {
|
||||
bundle_status.push(ComponentStatus::Added);
|
||||
// SAFE: component_id exists
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if new_table_components.is_empty() && new_sparse_set_components.is_empty() {
|
||||
let edges = current_archetype.edges_mut();
|
||||
// the archetype does not change when we add this bundle
|
||||
edges.insert_add_bundle(self.id, archetype_id, bundle_status);
|
||||
archetype_id
|
||||
} else {
|
||||
let table_id;
|
||||
let table_components;
|
||||
let sparse_set_components;
|
||||
// the archetype changes when we add this bundle. prepare the new archetype and storages
|
||||
{
|
||||
let current_archetype = &archetypes[archetype_id];
|
||||
table_components = if new_table_components.is_empty() {
|
||||
// if there are no new table components, we can keep using this table
|
||||
table_id = current_archetype.table_id();
|
||||
current_archetype.table_components().to_vec()
|
||||
} else {
|
||||
new_table_components.extend(current_archetype.table_components());
|
||||
// sort to ignore order while hashing
|
||||
new_table_components.sort();
|
||||
// SAFE: all component ids in `new_table_components` exist
|
||||
table_id = unsafe {
|
||||
storages
|
||||
.tables
|
||||
.get_id_or_insert(&new_table_components, components)
|
||||
};
|
||||
|
||||
new_table_components
|
||||
};
|
||||
|
||||
sparse_set_components = if new_sparse_set_components.is_empty() {
|
||||
current_archetype.sparse_set_components().to_vec()
|
||||
} else {
|
||||
new_sparse_set_components.extend(current_archetype.sparse_set_components());
|
||||
// sort to ignore order while hashing
|
||||
new_sparse_set_components.sort();
|
||||
new_sparse_set_components
|
||||
};
|
||||
};
|
||||
let new_archetype_id =
|
||||
archetypes.get_id_or_insert(table_id, table_components, sparse_set_components);
|
||||
// add an edge from the old archetype to the new archetype
|
||||
archetypes[archetype_id].edges_mut().insert_add_bundle(
|
||||
self.id,
|
||||
new_archetype_id,
|
||||
bundle_status,
|
||||
);
|
||||
new_archetype_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct BundleInserter<'a, 'b> {
|
||||
pub(crate) archetype: &'a mut Archetype,
|
||||
pub(crate) entities: &'a mut Entities,
|
||||
bundle_info: &'b BundleInfo,
|
||||
table: &'a mut Table,
|
||||
sparse_sets: &'a mut SparseSets,
|
||||
result: InsertBundleResult<'a>,
|
||||
archetypes_ptr: *mut Archetype,
|
||||
change_tick: u32,
|
||||
}
|
||||
|
||||
pub(crate) enum InsertBundleResult<'a> {
|
||||
SameArchetype,
|
||||
NewArchetypeSameTable {
|
||||
new_archetype: &'a mut Archetype,
|
||||
},
|
||||
NewArchetypeNewTable {
|
||||
new_archetype: &'a mut Archetype,
|
||||
new_table: &'a mut Table,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a, 'b> BundleInserter<'a, 'b> {
|
||||
/// # Safety
|
||||
/// `entity` must currently exist in the source archetype for this inserter. `archetype_index` must be `entity`'s location in the archetype.
|
||||
/// `T` must match this BundleInfo's type
|
||||
#[inline]
|
||||
pub fn id(&self) -> BundleId {
|
||||
self.id
|
||||
pub unsafe fn insert<T: Bundle>(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
archetype_index: usize,
|
||||
bundle: T,
|
||||
) -> EntityLocation {
|
||||
let location = EntityLocation {
|
||||
index: archetype_index,
|
||||
archetype_id: self.archetype.id(),
|
||||
};
|
||||
match &mut self.result {
|
||||
InsertBundleResult::SameArchetype => {
|
||||
// PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty)
|
||||
let add_bundle = self
|
||||
.archetype
|
||||
.edges()
|
||||
.get_add_bundle(self.bundle_info.id)
|
||||
.unwrap();
|
||||
self.bundle_info.write_components(
|
||||
self.table,
|
||||
self.sparse_sets,
|
||||
add_bundle,
|
||||
entity,
|
||||
self.archetype.entity_table_row(archetype_index),
|
||||
self.change_tick,
|
||||
bundle,
|
||||
);
|
||||
location
|
||||
}
|
||||
InsertBundleResult::NewArchetypeSameTable { new_archetype } => {
|
||||
let result = self.archetype.swap_remove(location.index);
|
||||
if let Some(swapped_entity) = result.swapped_entity {
|
||||
self.entities.meta[swapped_entity.id as usize].location = location;
|
||||
}
|
||||
let new_location = new_archetype.allocate(entity, result.table_row);
|
||||
self.entities.meta[entity.id as usize].location = new_location;
|
||||
|
||||
// PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty)
|
||||
let add_bundle = self
|
||||
.archetype
|
||||
.edges()
|
||||
.get_add_bundle(self.bundle_info.id)
|
||||
.unwrap();
|
||||
self.bundle_info.write_components(
|
||||
self.table,
|
||||
self.sparse_sets,
|
||||
add_bundle,
|
||||
entity,
|
||||
result.table_row,
|
||||
self.change_tick,
|
||||
bundle,
|
||||
);
|
||||
new_location
|
||||
}
|
||||
InsertBundleResult::NewArchetypeNewTable {
|
||||
new_archetype,
|
||||
new_table,
|
||||
} => {
|
||||
let result = self.archetype.swap_remove(location.index);
|
||||
if let Some(swapped_entity) = result.swapped_entity {
|
||||
self.entities.meta[swapped_entity.id as usize].location = location;
|
||||
}
|
||||
// PERF: store "non bundle" components in edge, then just move those to avoid
|
||||
// redundant copies
|
||||
let move_result = self
|
||||
.table
|
||||
.move_to_superset_unchecked(result.table_row, &mut *new_table);
|
||||
let new_location = new_archetype.allocate(entity, move_result.new_row);
|
||||
self.entities.meta[entity.id as usize].location = new_location;
|
||||
|
||||
// if an entity was moved into this entity's table spot, update its table row
|
||||
if let Some(swapped_entity) = move_result.swapped_entity {
|
||||
let swapped_location = self.entities.get(swapped_entity).unwrap();
|
||||
let swapped_archetype = if self.archetype.id() == swapped_location.archetype_id
|
||||
{
|
||||
&mut *self.archetype
|
||||
} else if new_archetype.id() == swapped_location.archetype_id {
|
||||
&mut *new_archetype
|
||||
} else {
|
||||
// SAFE: the only two borrowed archetypes are above and we just did collision checks
|
||||
&mut *self
|
||||
.archetypes_ptr
|
||||
.add(swapped_location.archetype_id.index())
|
||||
};
|
||||
|
||||
swapped_archetype
|
||||
.set_entity_table_row(swapped_location.index, result.table_row);
|
||||
}
|
||||
|
||||
// PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty)
|
||||
let add_bundle = self
|
||||
.archetype
|
||||
.edges()
|
||||
.get_add_bundle(self.bundle_info.id)
|
||||
.unwrap();
|
||||
self.bundle_info.write_components(
|
||||
new_table,
|
||||
self.sparse_sets,
|
||||
add_bundle,
|
||||
entity,
|
||||
move_result.new_row,
|
||||
self.change_tick,
|
||||
bundle,
|
||||
);
|
||||
new_location
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct BundleSpawner<'a, 'b> {
|
||||
pub(crate) archetype: &'a mut Archetype,
|
||||
pub(crate) entities: &'a mut Entities,
|
||||
add_bundle: &'a AddBundle,
|
||||
bundle_info: &'b BundleInfo,
|
||||
table: &'a mut Table,
|
||||
sparse_sets: &'a mut SparseSets,
|
||||
change_tick: u32,
|
||||
}
|
||||
|
||||
impl<'a, 'b> BundleSpawner<'a, 'b> {
|
||||
pub fn reserve_storage(&mut self, additional: usize) {
|
||||
self.archetype.reserve(additional);
|
||||
self.table.reserve(additional);
|
||||
}
|
||||
/// # Safety
|
||||
/// `entity` must be allocated (but non existent), `T` must match this BundleInfo's type
|
||||
#[inline]
|
||||
pub unsafe fn spawn_non_existent<T: Bundle>(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
bundle: T,
|
||||
) -> EntityLocation {
|
||||
let table_row = self.table.allocate(entity);
|
||||
let location = self.archetype.allocate(entity, table_row);
|
||||
self.bundle_info.write_components(
|
||||
self.table,
|
||||
self.sparse_sets,
|
||||
self.add_bundle,
|
||||
entity,
|
||||
table_row,
|
||||
self.change_tick,
|
||||
bundle,
|
||||
);
|
||||
self.entities.meta[entity.id as usize].location = location;
|
||||
|
||||
location
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// `T` must match this BundleInfo's type
|
||||
#[inline]
|
||||
pub fn components(&self) -> &[ComponentId] {
|
||||
&self.component_ids
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn storage_types(&self) -> &[StorageType] {
|
||||
&self.storage_types
|
||||
pub unsafe fn spawn<T: Bundle>(&mut self, bundle: T) -> Entity {
|
||||
let entity = self.entities.alloc();
|
||||
// SAFE: entity is allocated (but non-existent), `T` matches this BundleInfo's type
|
||||
self.spawn_non_existent(entity, bundle);
|
||||
entity
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,10 +563,12 @@ impl Bundles {
|
|||
) -> &'a BundleInfo {
|
||||
let bundle_infos = &mut self.bundle_infos;
|
||||
let id = self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
|
||||
let type_info = T::type_info();
|
||||
let component_ids = T::component_ids(components);
|
||||
let id = BundleId(bundle_infos.len());
|
||||
let bundle_info =
|
||||
initialize_bundle(std::any::type_name::<T>(), &type_info, id, components);
|
||||
// SAFE: T::component_id ensures info was created
|
||||
let bundle_info = unsafe {
|
||||
initialize_bundle(std::any::type_name::<T>(), component_ids, id, components)
|
||||
};
|
||||
bundle_infos.push(bundle_info);
|
||||
id
|
||||
});
|
||||
|
@ -218,21 +577,21 @@ impl Bundles {
|
|||
}
|
||||
}
|
||||
|
||||
fn initialize_bundle(
|
||||
/// # Safety
|
||||
///
|
||||
/// `component_id` must be valid [ComponentId]'s
|
||||
unsafe fn initialize_bundle(
|
||||
bundle_type_name: &'static str,
|
||||
type_info: &[TypeInfo],
|
||||
component_ids: Vec<ComponentId>,
|
||||
id: BundleId,
|
||||
components: &mut Components,
|
||||
) -> BundleInfo {
|
||||
let mut component_ids = Vec::new();
|
||||
let mut storage_types = Vec::new();
|
||||
|
||||
for type_info in type_info {
|
||||
let component_id = components.get_or_insert_with(type_info.type_id(), || type_info.clone());
|
||||
// SAFE: get_with_type_info ensures info was created
|
||||
let info = unsafe { components.get_info_unchecked(component_id) };
|
||||
component_ids.push(component_id);
|
||||
storage_types.push(info.storage_type());
|
||||
for &component_id in &component_ids {
|
||||
// SAFE: component_id exists and is therefore valid
|
||||
let component_info = components.get_info_unchecked(component_id);
|
||||
storage_types.push(component_info.storage_type());
|
||||
}
|
||||
|
||||
let mut deduped = component_ids.clone();
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
mod type_info;
|
||||
|
||||
pub use type_info::*;
|
||||
|
||||
use crate::storage::SparseSetIndex;
|
||||
use std::{
|
||||
alloc::Layout,
|
||||
|
@ -56,15 +52,8 @@ impl Default for StorageType {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct ComponentInfo {
|
||||
name: String,
|
||||
id: ComponentId,
|
||||
type_id: Option<TypeId>,
|
||||
// SAFETY: This must remain private. It must only be set to "true" if this component is
|
||||
// actually Send + Sync
|
||||
is_send_and_sync: bool,
|
||||
layout: Layout,
|
||||
drop: unsafe fn(*mut u8),
|
||||
storage_type: StorageType,
|
||||
descriptor: ComponentDescriptor,
|
||||
}
|
||||
|
||||
impl ComponentInfo {
|
||||
|
@ -75,44 +64,36 @@ impl ComponentInfo {
|
|||
|
||||
#[inline]
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
&self.descriptor.name
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn type_id(&self) -> Option<TypeId> {
|
||||
self.type_id
|
||||
self.descriptor.type_id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn layout(&self) -> Layout {
|
||||
self.layout
|
||||
self.descriptor.layout
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drop(&self) -> unsafe fn(*mut u8) {
|
||||
self.drop
|
||||
self.descriptor.drop
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn storage_type(&self) -> StorageType {
|
||||
self.storage_type
|
||||
self.descriptor.storage_type
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_send_and_sync(&self) -> bool {
|
||||
self.is_send_and_sync
|
||||
self.descriptor.is_send_and_sync
|
||||
}
|
||||
|
||||
fn new(id: ComponentId, descriptor: ComponentDescriptor) -> Self {
|
||||
ComponentInfo {
|
||||
id,
|
||||
name: descriptor.name,
|
||||
storage_type: descriptor.storage_type,
|
||||
type_id: descriptor.type_id,
|
||||
is_send_and_sync: descriptor.is_send_and_sync,
|
||||
drop: descriptor.drop,
|
||||
layout: descriptor.layout,
|
||||
}
|
||||
ComponentInfo { id, descriptor }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,6 +123,7 @@ impl SparseSetIndex for ComponentId {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ComponentDescriptor {
|
||||
name: String,
|
||||
storage_type: StorageType,
|
||||
|
@ -154,6 +136,11 @@ pub struct ComponentDescriptor {
|
|||
}
|
||||
|
||||
impl ComponentDescriptor {
|
||||
// SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value.
|
||||
unsafe fn drop_ptr<T>(x: *mut u8) {
|
||||
x.cast::<T>().drop_in_place()
|
||||
}
|
||||
|
||||
pub fn new<T: Component>(storage_type: StorageType) -> Self {
|
||||
Self {
|
||||
name: std::any::type_name::<T>().to_string(),
|
||||
|
@ -161,7 +148,18 @@ impl ComponentDescriptor {
|
|||
is_send_and_sync: true,
|
||||
type_id: Some(TypeId::of::<T>()),
|
||||
layout: Layout::new::<T>(),
|
||||
drop: TypeInfo::drop_ptr::<T>,
|
||||
drop: Self::drop_ptr::<T>,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_non_send<T: Any>(storage_type: StorageType) -> Self {
|
||||
Self {
|
||||
name: std::any::type_name::<T>().to_string(),
|
||||
storage_type,
|
||||
is_send_and_sync: false,
|
||||
type_id: Some(TypeId::of::<T>()),
|
||||
layout: Layout::new::<T>(),
|
||||
drop: Self::drop_ptr::<T>,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,19 +179,6 @@ impl ComponentDescriptor {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<TypeInfo> for ComponentDescriptor {
|
||||
fn from(type_info: TypeInfo) -> Self {
|
||||
Self {
|
||||
name: type_info.type_name().to_string(),
|
||||
storage_type: StorageType::default(),
|
||||
is_send_and_sync: type_info.is_send_and_sync(),
|
||||
type_id: Some(type_info.type_id()),
|
||||
drop: type_info.drop(),
|
||||
layout: type_info.layout(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Components {
|
||||
components: Vec<ComponentInfo>,
|
||||
|
@ -231,7 +216,12 @@ impl Components {
|
|||
|
||||
#[inline]
|
||||
pub fn get_or_insert_id<T: Component>(&mut self) -> ComponentId {
|
||||
self.get_or_insert_with(TypeId::of::<T>(), TypeInfo::of::<T>)
|
||||
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
|
||||
unsafe {
|
||||
self.get_or_insert_with(TypeId::of::<T>(), || {
|
||||
ComponentDescriptor::new::<T>(StorageType::default())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -279,42 +269,58 @@ impl Components {
|
|||
|
||||
#[inline]
|
||||
pub fn get_or_insert_resource_id<T: Component>(&mut self) -> ComponentId {
|
||||
self.get_or_insert_resource_with(TypeId::of::<T>(), TypeInfo::of::<T>)
|
||||
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
|
||||
unsafe {
|
||||
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
|
||||
ComponentDescriptor::new::<T>(StorageType::default())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_or_insert_non_send_resource_id<T: Any>(&mut self) -> ComponentId {
|
||||
self.get_or_insert_resource_with(TypeId::of::<T>(), TypeInfo::of_non_send_and_sync::<T>)
|
||||
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
|
||||
unsafe {
|
||||
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
|
||||
ComponentDescriptor::new_non_send::<T>(StorageType::default())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The [`ComponentDescriptor`] must match the [`TypeId`]
|
||||
#[inline]
|
||||
fn get_or_insert_resource_with(
|
||||
unsafe fn get_or_insert_resource_with(
|
||||
&mut self,
|
||||
type_id: TypeId,
|
||||
func: impl FnOnce() -> TypeInfo,
|
||||
func: impl FnOnce() -> ComponentDescriptor,
|
||||
) -> ComponentId {
|
||||
let components = &mut self.components;
|
||||
let index = self.resource_indices.entry(type_id).or_insert_with(|| {
|
||||
let type_info = func();
|
||||
let descriptor = func();
|
||||
let index = components.len();
|
||||
components.push(ComponentInfo::new(ComponentId(index), type_info.into()));
|
||||
components.push(ComponentInfo::new(ComponentId(index), descriptor));
|
||||
index
|
||||
});
|
||||
|
||||
ComponentId(*index)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The [`ComponentDescriptor`] must match the [`TypeId`]
|
||||
#[inline]
|
||||
pub(crate) fn get_or_insert_with(
|
||||
pub(crate) unsafe fn get_or_insert_with(
|
||||
&mut self,
|
||||
type_id: TypeId,
|
||||
func: impl FnOnce() -> TypeInfo,
|
||||
func: impl FnOnce() -> ComponentDescriptor,
|
||||
) -> ComponentId {
|
||||
let components = &mut self.components;
|
||||
let index = self.indices.entry(type_id).or_insert_with(|| {
|
||||
let type_info = func();
|
||||
let descriptor = func();
|
||||
let index = components.len();
|
||||
components.push(ComponentInfo::new(ComponentId(index), type_info.into()));
|
||||
components.push(ComponentInfo::new(ComponentId(index), descriptor));
|
||||
index
|
||||
});
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
use std::{alloc::Layout, any::TypeId};
|
||||
|
||||
/// Metadata required to store a component.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TypeInfo {
|
||||
type_id: TypeId,
|
||||
layout: Layout,
|
||||
drop: unsafe fn(*mut u8),
|
||||
type_name: &'static str,
|
||||
is_send_and_sync: bool,
|
||||
}
|
||||
|
||||
impl TypeInfo {
|
||||
/// Metadata for `T`.
|
||||
pub fn of<T: Send + Sync + 'static>() -> Self {
|
||||
Self {
|
||||
type_id: TypeId::of::<T>(),
|
||||
layout: Layout::new::<T>(),
|
||||
is_send_and_sync: true,
|
||||
drop: Self::drop_ptr::<T>,
|
||||
type_name: core::any::type_name::<T>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn of_non_send_and_sync<T: 'static>() -> Self {
|
||||
Self {
|
||||
type_id: TypeId::of::<T>(),
|
||||
layout: Layout::new::<T>(),
|
||||
is_send_and_sync: false,
|
||||
drop: Self::drop_ptr::<T>,
|
||||
type_name: core::any::type_name::<T>(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn type_id(&self) -> TypeId {
|
||||
self.type_id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn layout(&self) -> Layout {
|
||||
self.layout
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drop(&self) -> unsafe fn(*mut u8) {
|
||||
self.drop
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_send_and_sync(&self) -> bool {
|
||||
self.is_send_and_sync
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
self.type_name
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn drop_ptr<T>(x: *mut u8) {
|
||||
x.cast::<T>().drop_in_place()
|
||||
}
|
||||
}
|
|
@ -34,6 +34,11 @@ pub enum AllocAtWithoutReplacement {
|
|||
|
||||
impl Entity {
|
||||
/// Creates a new entity reference with a generation of 0.
|
||||
///
|
||||
/// # Note
|
||||
/// Spawning a specific `entity` value is rarely the right choice. Most apps should favor [`Commands::spawn`].
|
||||
/// This method should generally only be used for sharing entities across apps, and only when they have a
|
||||
/// scheme worked out to share an ID space (which doesn't happen by default).
|
||||
pub fn new(id: u32) -> Entity {
|
||||
Entity { id, generation: 0 }
|
||||
}
|
||||
|
@ -168,6 +173,7 @@ pub struct Entities {
|
|||
/// Once `flush()` is done, `free_cursor` will equal `pending.len()`.
|
||||
pending: Vec<u32>,
|
||||
free_cursor: AtomicI64,
|
||||
/// Stores the number of free entities for [`len`](Entities::len)
|
||||
len: u32,
|
||||
}
|
||||
|
||||
|
@ -318,7 +324,7 @@ impl Entities {
|
|||
AllocAtWithoutReplacement::DidNotExist
|
||||
} else {
|
||||
let current_meta = &mut self.meta[entity.id as usize];
|
||||
if current_meta.location.archetype_id == ArchetypeId::invalid() {
|
||||
if current_meta.location.archetype_id == ArchetypeId::INVALID {
|
||||
AllocAtWithoutReplacement::DidNotExist
|
||||
} else if current_meta.generation == entity.generation {
|
||||
AllocAtWithoutReplacement::Exists(current_meta.location)
|
||||
|
@ -364,12 +370,12 @@ impl Entities {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns true if the [`Entities`] contains [`entity`](Entity).
|
||||
// This will return false for entities which have been freed, even if
|
||||
// not reallocated since the generation is incremented in `free`
|
||||
pub fn contains(&self, entity: Entity) -> bool {
|
||||
// Note that out-of-range IDs are considered to be "contained" because
|
||||
// they must be reserved IDs that we haven't flushed yet.
|
||||
self.meta
|
||||
.get(entity.id as usize)
|
||||
.map_or(true, |meta| meta.generation == entity.generation)
|
||||
self.resolve_from_id(entity.id())
|
||||
.map_or(false, |e| e.generation() == entity.generation)
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
|
@ -384,7 +390,7 @@ impl Entities {
|
|||
if (entity.id as usize) < self.meta.len() {
|
||||
let meta = &self.meta[entity.id as usize];
|
||||
if meta.generation != entity.generation
|
||||
|| meta.location.archetype_id == ArchetypeId::invalid()
|
||||
|| meta.location.archetype_id == ArchetypeId::INVALID
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
@ -394,31 +400,23 @@ impl Entities {
|
|||
}
|
||||
}
|
||||
|
||||
/// Panics if the given id would represent an index outside of `meta`.
|
||||
/// Get the [`Entity`] with a given id, if it exists in this [`Entities`] collection
|
||||
/// Returns `None` if this [`Entity`] is outside of the range of currently reserved Entities
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Must only be called for currently allocated `id`s.
|
||||
pub unsafe fn resolve_unknown_gen(&self, id: u32) -> Entity {
|
||||
let meta_len = self.meta.len();
|
||||
|
||||
if meta_len > id as usize {
|
||||
let meta = &self.meta[id as usize];
|
||||
Entity {
|
||||
generation: meta.generation,
|
||||
id,
|
||||
}
|
||||
/// Note: This method may return [`Entities`](Entity) which are currently free
|
||||
/// Note that [`contains`](Entities::contains) will correctly return false for freed
|
||||
/// entities, since it checks the generation
|
||||
pub fn resolve_from_id(&self, id: u32) -> Option<Entity> {
|
||||
let idu = id as usize;
|
||||
if let Some(&EntityMeta { generation, .. }) = self.meta.get(idu) {
|
||||
Some(Entity { generation, id })
|
||||
} else {
|
||||
// See if it's pending, but not yet flushed.
|
||||
// `id` is outside of the meta list - check whether it is reserved but not yet flushed.
|
||||
let free_cursor = self.free_cursor.load(Ordering::Relaxed);
|
||||
let num_pending = std::cmp::max(-free_cursor, 0) as usize;
|
||||
|
||||
if meta_len + num_pending > id as usize {
|
||||
// Pending entities will have generation 0.
|
||||
Entity { generation: 0, id }
|
||||
} else {
|
||||
panic!("entity id is out of range");
|
||||
}
|
||||
// If this entity was manually created, then free_cursor might be positive
|
||||
// Returning None handles that case correctly
|
||||
let num_pending = usize::try_from(-free_cursor).ok()?;
|
||||
(idu < self.meta.len() + num_pending).then(|| Entity { generation: 0, id })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -431,7 +429,7 @@ impl Entities {
|
|||
///
|
||||
/// # Safety
|
||||
/// Flush _must_ set the entity location to the correct ArchetypeId for the given Entity
|
||||
/// each time init is called. This _can_ be ArchetypeId::invalid(), provided the Entity has
|
||||
/// each time init is called. This _can_ be ArchetypeId::INVALID, provided the Entity has
|
||||
/// not been assigned to an Archetype.
|
||||
pub unsafe fn flush(&mut self, mut init: impl FnMut(Entity, &mut EntityLocation)) {
|
||||
let free_cursor = self.free_cursor.get_mut();
|
||||
|
@ -476,7 +474,7 @@ impl Entities {
|
|||
pub fn flush_as_invalid(&mut self) {
|
||||
unsafe {
|
||||
self.flush(|_entity, location| {
|
||||
location.archetype_id = ArchetypeId::invalid();
|
||||
location.archetype_id = ArchetypeId::INVALID;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -502,8 +500,8 @@ impl EntityMeta {
|
|||
const EMPTY: EntityMeta = EntityMeta {
|
||||
generation: 0,
|
||||
location: EntityLocation {
|
||||
archetype_id: ArchetypeId::invalid(),
|
||||
index: usize::max_value(), // dummy value, to be filled in
|
||||
archetype_id: ArchetypeId::INVALID,
|
||||
index: usize::MAX, // dummy value, to be filled in
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ enum State {
|
|||
/// [`Events::update`] exactly once per update/frame.
|
||||
///
|
||||
/// [`Events::update_system`] is a system that does this, typically intialized automatically using
|
||||
/// [`AppBuilder::add_event`]. [EventReader]s are expected to read events from this collection at
|
||||
/// [`App::add_event`]. [EventReader]s are expected to read events from this collection at
|
||||
/// least once per loop/frame.
|
||||
/// Events will persist across a single frame boundary and so ordering of event producers and
|
||||
/// consumers is not critical (although poorly-planned ordering may cause accumulating lag).
|
||||
|
@ -115,9 +115,9 @@ enum State {
|
|||
/// An alternative call pattern would be to call [Events::update] manually across frames to control
|
||||
/// when events are cleared.
|
||||
/// This complicates consumption and risks ever-expanding memory usage if not cleaned up,
|
||||
/// but can be done by adding your event as a resource instead of using [`AppBuilder::add_event`].
|
||||
/// but can be done by adding your event as a resource instead of using [`App::add_event`].
|
||||
///
|
||||
/// [`AppBuilder::add_event`]: https://docs.rs/bevy/*/bevy/app/struct.AppBuilder.html#method.add_event
|
||||
/// [`App::add_event`]: https://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event
|
||||
#[derive(Debug)]
|
||||
pub struct Events<T> {
|
||||
events_a: Vec<EventInstance<T>>,
|
||||
|
@ -151,21 +151,20 @@ fn map_instance_event<T>(event_instance: &EventInstance<T>) -> &T {
|
|||
|
||||
/// Reads events of type `T` in order and tracks which events have already been read.
|
||||
#[derive(SystemParam)]
|
||||
pub struct EventReader<'s, 'w, T: Component> {
|
||||
pub struct EventReader<'w, 's, T: Component> {
|
||||
last_event_count: Local<'s, (usize, PhantomData<T>)>,
|
||||
events: Res<'w, Events<T>>,
|
||||
}
|
||||
|
||||
/// Sends events of type `T`.
|
||||
#[derive(SystemParam)]
|
||||
pub struct EventWriter<'s, 'w, T: Component> {
|
||||
pub struct EventWriter<'w, 's, T: Component> {
|
||||
events: ResMut<'w, Events<T>>,
|
||||
// TODO: this isn't ideal ... maybe the SystemParam derive can be smarter about world and state lifetimes?
|
||||
#[system_param(ignore)]
|
||||
marker: PhantomData<&'s usize>,
|
||||
}
|
||||
|
||||
impl<'s, 'w, T: Component> EventWriter<'s, 'w, T> {
|
||||
impl<'w, 's, T: Component> EventWriter<'w, 's, T> {
|
||||
pub fn send(&mut self, event: T) {
|
||||
self.events.send(event);
|
||||
}
|
||||
|
@ -255,7 +254,7 @@ fn internal_event_reader<'a, T>(
|
|||
}
|
||||
}
|
||||
|
||||
impl<'s, 'w, T: Component> EventReader<'s, 'w, T> {
|
||||
impl<'w, 's, T: Component> 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.
|
||||
|
|
|
@ -22,7 +22,7 @@ pub mod prelude {
|
|||
change_detection::DetectChanges,
|
||||
entity::Entity,
|
||||
event::{EventReader, EventWriter},
|
||||
query::{Added, ChangeTrackers, Changed, Or, QueryState, With, WithBundle, Without},
|
||||
query::{Added, ChangeTrackers, Changed, Or, QueryState, With, Without},
|
||||
schedule::{
|
||||
AmbiguitySetLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion,
|
||||
RunCriteria, RunCriteriaDescriptorCoercion, RunCriteriaLabel, RunCriteriaPiping,
|
||||
|
@ -41,7 +41,7 @@ mod tests {
|
|||
use crate as bevy_ecs;
|
||||
use crate::{
|
||||
bundle::Bundle,
|
||||
component::{Component, ComponentDescriptor, ComponentId, StorageType, TypeInfo},
|
||||
component::{Component, ComponentDescriptor, ComponentId, StorageType},
|
||||
entity::Entity,
|
||||
query::{
|
||||
Added, ChangeTrackers, Changed, FilterFetch, FilteredAccess, With, Without, WorldQuery,
|
||||
|
@ -60,7 +60,9 @@ mod tests {
|
|||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct A(usize);
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct B(usize);
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct C;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -102,21 +104,26 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn bundle_derive() {
|
||||
let mut world = World::new();
|
||||
|
||||
#[derive(Bundle, PartialEq, Debug)]
|
||||
struct Foo {
|
||||
x: &'static str,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
<Foo as Bundle>::type_info(),
|
||||
vec![TypeInfo::of::<&'static str>(), TypeInfo::of::<i32>(),]
|
||||
);
|
||||
|
||||
let mut world = World::new();
|
||||
world
|
||||
.register_component(ComponentDescriptor::new::<i32>(StorageType::SparseSet))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
<Foo as Bundle>::component_ids(world.components_mut()),
|
||||
vec![
|
||||
world.components_mut().get_or_insert_id::<&'static str>(),
|
||||
world.components_mut().get_or_insert_id::<i32>(),
|
||||
]
|
||||
);
|
||||
|
||||
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");
|
||||
|
@ -146,12 +153,12 @@ mod tests {
|
|||
}
|
||||
|
||||
assert_eq!(
|
||||
<Nested as Bundle>::type_info(),
|
||||
<Nested as Bundle>::component_ids(world.components_mut()),
|
||||
vec![
|
||||
TypeInfo::of::<usize>(),
|
||||
TypeInfo::of::<&'static str>(),
|
||||
TypeInfo::of::<i32>(),
|
||||
TypeInfo::of::<u8>(),
|
||||
world.components_mut().get_or_insert_id::<usize>(),
|
||||
world.components_mut().get_or_insert_id::<&'static str>(),
|
||||
world.components_mut().get_or_insert_id::<i32>(),
|
||||
world.components_mut().get_or_insert_id::<u8>(),
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -1393,4 +1400,89 @@ mod tests {
|
|||
"space between original entities and high entities is used for new entity ids"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_or_spawn_batch() {
|
||||
let mut world = World::default();
|
||||
let e0 = world.spawn().insert(A(0)).id();
|
||||
let e1 = Entity::new(1);
|
||||
|
||||
let values = vec![(e0, (B(0), C)), (e1, (B(1), C))];
|
||||
|
||||
world.insert_or_spawn_batch(values).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
world.get::<A>(e0),
|
||||
Some(&A(0)),
|
||||
"existing component was preserved"
|
||||
);
|
||||
assert_eq!(
|
||||
world.get::<B>(e0),
|
||||
Some(&B(0)),
|
||||
"pre-existing entity received correct B component"
|
||||
);
|
||||
assert_eq!(
|
||||
world.get::<B>(e1),
|
||||
Some(&B(1)),
|
||||
"new entity was spawned and received correct B component"
|
||||
);
|
||||
assert_eq!(
|
||||
world.get::<C>(e0),
|
||||
Some(&C),
|
||||
"pre-existing entity received C component"
|
||||
);
|
||||
assert_eq!(
|
||||
world.get::<C>(e1),
|
||||
Some(&C),
|
||||
"new entity was spawned and received C component"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_or_spawn_batch_invalid() {
|
||||
let mut world = World::default();
|
||||
let e0 = world.spawn().insert(A(0)).id();
|
||||
let e1 = Entity::new(1);
|
||||
let e2 = world.spawn().id();
|
||||
let invalid_e2 = Entity {
|
||||
generation: 1,
|
||||
id: e2.id,
|
||||
};
|
||||
|
||||
let values = vec![(e0, (B(0), C)), (e1, (B(1), C)), (invalid_e2, (B(2), C))];
|
||||
|
||||
let result = world.insert_or_spawn_batch(values);
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(vec![invalid_e2]),
|
||||
"e2 failed to be spawned or inserted into"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
world.get::<A>(e0),
|
||||
Some(&A(0)),
|
||||
"existing component was preserved"
|
||||
);
|
||||
assert_eq!(
|
||||
world.get::<B>(e0),
|
||||
Some(&B(0)),
|
||||
"pre-existing entity received correct B component"
|
||||
);
|
||||
assert_eq!(
|
||||
world.get::<B>(e1),
|
||||
Some(&B(1)),
|
||||
"new entity was spawned and received correct B component"
|
||||
);
|
||||
assert_eq!(
|
||||
world.get::<C>(e0),
|
||||
Some(&C),
|
||||
"pre-existing entity received C component"
|
||||
);
|
||||
assert_eq!(
|
||||
world.get::<C>(e1),
|
||||
Some(&C),
|
||||
"new entity was spawned and received C component"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,6 +137,7 @@ impl WorldQuery for Entity {
|
|||
}
|
||||
|
||||
/// The [`Fetch`] of [`Entity`].
|
||||
#[derive(Clone)]
|
||||
pub struct EntityFetch {
|
||||
entities: *const Entity,
|
||||
}
|
||||
|
@ -573,6 +574,7 @@ impl<T: WorldQuery> WorldQuery for Option<T> {
|
|||
}
|
||||
|
||||
/// The [`Fetch`] of `Option<T>`.
|
||||
#[derive(Clone)]
|
||||
pub struct OptionFetch<T> {
|
||||
fetch: T,
|
||||
matches: bool,
|
||||
|
@ -807,6 +809,21 @@ pub struct ChangeTrackersFetch<T> {
|
|||
change_tick: u32,
|
||||
}
|
||||
|
||||
impl<T> Clone for ChangeTrackersFetch<T> {
|
||||
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,
|
||||
sparse_set: self.sparse_set,
|
||||
marker: self.marker,
|
||||
last_change_tick: self.last_change_tick,
|
||||
change_tick: self.change_tick,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// SAFETY: access is read only
|
||||
unsafe impl<T> ReadOnlyFetch for ChangeTrackersFetch<T> {}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::{
|
||||
archetype::{Archetype, ArchetypeComponentId},
|
||||
bundle::Bundle,
|
||||
component::{Component, ComponentId, ComponentTicks, StorageType},
|
||||
entity::Entity,
|
||||
query::{Access, Fetch, FetchState, FilteredAccess, WorldQuery},
|
||||
|
@ -12,7 +11,7 @@ use std::{cell::UnsafeCell, marker::PhantomData, ptr};
|
|||
|
||||
/// Extension trait for [`Fetch`] containing methods used by query filters.
|
||||
/// This trait exists to allow "short circuit" behaviors for relevant query filter fetches.
|
||||
pub trait FilterFetch: for<'world, 'state> Fetch<'world, 'state> {
|
||||
pub trait FilterFetch: for<'w, 's> Fetch<'w, 's> {
|
||||
/// # Safety
|
||||
///
|
||||
/// Must always be called _after_ [`Fetch::set_archetype`]. `archetype_index` must be in the range
|
||||
|
@ -282,106 +281,6 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutFetch<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct WithBundle<T: Bundle>(PhantomData<T>);
|
||||
|
||||
impl<T: Bundle> WorldQuery for WithBundle<T> {
|
||||
type Fetch = WithBundleFetch<T>;
|
||||
type State = WithBundleState<T>;
|
||||
}
|
||||
|
||||
pub struct WithBundleFetch<T: Bundle> {
|
||||
is_dense: bool,
|
||||
marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
pub struct WithBundleState<T: Bundle> {
|
||||
component_ids: Vec<ComponentId>,
|
||||
is_dense: bool,
|
||||
marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
// SAFETY: no component access or archetype component access
|
||||
unsafe impl<T: Bundle> FetchState for WithBundleState<T> {
|
||||
fn init(world: &mut World) -> Self {
|
||||
let bundle_info = world.bundles.init_info::<T>(&mut world.components);
|
||||
let components = &world.components;
|
||||
Self {
|
||||
component_ids: bundle_info.component_ids.clone(),
|
||||
is_dense: !bundle_info.component_ids.iter().any(|id| unsafe {
|
||||
components.get_info_unchecked(*id).storage_type() != StorageType::Table
|
||||
}),
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn update_component_access(&self, access: &mut FilteredAccess<ComponentId>) {
|
||||
for component_id in self.component_ids.iter().cloned() {
|
||||
access.add_with(component_id);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn update_archetype_component_access(
|
||||
&self,
|
||||
_archetype: &Archetype,
|
||||
_access: &mut Access<ArchetypeComponentId>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn matches_archetype(&self, archetype: &Archetype) -> bool {
|
||||
self.component_ids.iter().all(|id| archetype.contains(*id))
|
||||
}
|
||||
|
||||
fn matches_table(&self, table: &Table) -> bool {
|
||||
self.component_ids.iter().all(|id| table.has_column(*id))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'w, 's, T: Bundle> Fetch<'w, 's> for WithBundleFetch<T> {
|
||||
type Item = bool;
|
||||
type State = WithBundleState<T>;
|
||||
|
||||
unsafe fn init(
|
||||
_world: &World,
|
||||
state: &Self::State,
|
||||
_last_change_tick: u32,
|
||||
_change_tick: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
is_dense: state.is_dense,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_dense(&self) -> bool {
|
||||
self.is_dense
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {}
|
||||
|
||||
#[inline]
|
||||
unsafe fn set_archetype(
|
||||
&mut self,
|
||||
_state: &Self::State,
|
||||
_archetype: &Archetype,
|
||||
_tables: &Tables,
|
||||
) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn archetype_fetch(&mut self, _archetype_index: usize) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn table_fetch(&mut self, _table_row: usize) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// A filter that tests if any of the given filters apply.
|
||||
///
|
||||
/// This is useful for example if a system with multiple components in a query only wants to run
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
storage::{TableId, Tables},
|
||||
world::World,
|
||||
};
|
||||
use std::{marker::PhantomData, mem::MaybeUninit};
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
/// An [`Iterator`] over query results of a [`Query`](crate::system::Query).
|
||||
///
|
||||
|
@ -217,7 +217,7 @@ where
|
|||
archetypes: &'w Archetypes,
|
||||
query_state: &'s QueryState<Q, F>,
|
||||
world: &'w World,
|
||||
cursors: [QueryIterationCursor<'w, 's, Q, F>; K],
|
||||
cursors: [QueryIterationCursor<'s, Q, F>; K],
|
||||
}
|
||||
|
||||
impl<'w, 's, Q: WorldQuery, F: WorldQuery, const K: usize> QueryCombinationIter<'w, 's, Q, F, K>
|
||||
|
@ -239,7 +239,7 @@ where
|
|||
// There is no FromIterator on arrays, so instead initialize it manually with MaybeUninit
|
||||
|
||||
// TODO: use MaybeUninit::uninit_array if it stabilizes
|
||||
let mut cursors: [MaybeUninit<QueryIterationCursor<'w, 's, Q, F>>; K] =
|
||||
let mut cursors: [MaybeUninit<QueryIterationCursor<'s, Q, F>>; K] =
|
||||
MaybeUninit::uninit().assume_init();
|
||||
for (i, cursor) in cursors.iter_mut().enumerate() {
|
||||
match i {
|
||||
|
@ -259,8 +259,8 @@ where
|
|||
}
|
||||
|
||||
// TODO: use MaybeUninit::array_assume_init if it stabilizes
|
||||
let cursors: [QueryIterationCursor<'w, 's, Q, F>; K] =
|
||||
(&cursors as *const _ as *const [QueryIterationCursor<'w, 's, Q, F>; K]).read();
|
||||
let cursors: [QueryIterationCursor<'s, Q, F>; K] =
|
||||
(&cursors as *const _ as *const [QueryIterationCursor<'s, Q, F>; K]).read();
|
||||
|
||||
QueryCombinationIter {
|
||||
world,
|
||||
|
@ -277,9 +277,9 @@ where
|
|||
/// references to the same component, leading to unique reference aliasing.
|
||||
///.
|
||||
/// It is always safe for shared access.
|
||||
unsafe fn fetch_next_aliased_unchecked(
|
||||
unsafe fn fetch_next_aliased_unchecked<'a>(
|
||||
&mut self,
|
||||
) -> Option<[<Q::Fetch as Fetch<'w, 's>>::Item; K]>
|
||||
) -> Option<[<Q::Fetch as Fetch<'a, 's>>::Item; K]>
|
||||
where
|
||||
Q::Fetch: Clone,
|
||||
F::Fetch: Clone,
|
||||
|
@ -309,7 +309,7 @@ where
|
|||
}
|
||||
|
||||
// TODO: use MaybeUninit::uninit_array if it stabilizes
|
||||
let mut values: [MaybeUninit<<Q::Fetch as Fetch<'w, 's>>::Item>; K] =
|
||||
let mut values: [MaybeUninit<<Q::Fetch as Fetch<'a, 's>>::Item>; K] =
|
||||
MaybeUninit::uninit().assume_init();
|
||||
|
||||
for (value, cursor) in values.iter_mut().zip(&mut self.cursors) {
|
||||
|
@ -317,15 +317,15 @@ where
|
|||
}
|
||||
|
||||
// TODO: use MaybeUninit::array_assume_init if it stabilizes
|
||||
let values: [<Q::Fetch as Fetch<'w, 's>>::Item; K] =
|
||||
(&values as *const _ as *const [<Q::Fetch as Fetch<'w, 's>>::Item; K]).read();
|
||||
let values: [<Q::Fetch as Fetch<'a, 's>>::Item; K] =
|
||||
(&values as *const _ as *const [<Q::Fetch as Fetch<'a, 's>>::Item; K]).read();
|
||||
|
||||
Some(values)
|
||||
}
|
||||
|
||||
/// Get next combination of queried components
|
||||
#[inline]
|
||||
pub fn fetch_next(&mut self) -> Option<[<Q::Fetch as Fetch<'w, 's>>::Item; K]>
|
||||
pub fn fetch_next(&mut self) -> Option<[<Q::Fetch as Fetch<'_, 's>>::Item; K]>
|
||||
where
|
||||
Q::Fetch: Clone,
|
||||
F::Fetch: Clone,
|
||||
|
@ -400,7 +400,7 @@ impl<'w, 's, Q: WorldQuery> ExactSizeIterator for QueryIter<'w, 's, Q, ()> {
|
|||
}
|
||||
}
|
||||
|
||||
struct QueryIterationCursor<'w, 's, Q: WorldQuery, F: WorldQuery> {
|
||||
struct QueryIterationCursor<'s, Q: WorldQuery, F: WorldQuery> {
|
||||
table_id_iter: std::slice::Iter<'s, TableId>,
|
||||
archetype_id_iter: std::slice::Iter<'s, ArchetypeId>,
|
||||
fetch: Q::Fetch,
|
||||
|
@ -408,10 +408,9 @@ struct QueryIterationCursor<'w, 's, Q: WorldQuery, F: WorldQuery> {
|
|||
current_len: usize,
|
||||
current_index: usize,
|
||||
is_dense: bool,
|
||||
marker: PhantomData<&'w usize>,
|
||||
}
|
||||
|
||||
impl<'w, 's, Q: WorldQuery, F: WorldQuery> Clone for QueryIterationCursor<'w, 's, Q, F>
|
||||
impl<'s, Q: WorldQuery, F: WorldQuery> Clone for QueryIterationCursor<'s, Q, F>
|
||||
where
|
||||
Q::Fetch: Clone,
|
||||
F::Fetch: Clone,
|
||||
|
@ -425,12 +424,11 @@ where
|
|||
current_len: self.current_len,
|
||||
current_index: self.current_index,
|
||||
is_dense: self.is_dense,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'w, 's, Q, F>
|
||||
impl<'s, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'s, Q, F>
|
||||
where
|
||||
F::Fetch: FilterFetch,
|
||||
{
|
||||
|
@ -473,13 +471,12 @@ where
|
|||
archetype_id_iter: query_state.matched_archetype_ids.iter(),
|
||||
current_len: 0,
|
||||
current_index: 0,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// retrieve item returned from most recent `next` call again.
|
||||
#[inline]
|
||||
unsafe fn peek_last(&mut self) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> {
|
||||
unsafe fn peek_last<'w>(&mut self) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> {
|
||||
if self.current_index > 0 {
|
||||
if self.is_dense {
|
||||
Some(self.fetch.table_fetch(self.current_index - 1))
|
||||
|
@ -495,7 +492,7 @@ where
|
|||
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
|
||||
// We can't currently reuse QueryIterationCursor in QueryIter for performance reasons. See #1763 for context.
|
||||
#[inline(always)]
|
||||
unsafe fn next(
|
||||
unsafe fn next<'w>(
|
||||
&mut self,
|
||||
tables: &'w Tables,
|
||||
archetypes: &'w Archetypes,
|
||||
|
|
|
@ -13,6 +13,7 @@ use bevy_tasks::TaskPool;
|
|||
use fixedbitset::FixedBitSet;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Provides scoped access to a [`World`] state according to a given [`WorldQuery`] and query filter.
|
||||
pub struct QueryState<Q: WorldQuery, F: WorldQuery = ()>
|
||||
where
|
||||
F::Fetch: FilterFetch,
|
||||
|
@ -35,6 +36,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F>
|
|||
where
|
||||
F::Fetch: FilterFetch,
|
||||
{
|
||||
/// Creates a new [`QueryState`] from a given [`World`] and inherits the result of `world.id()`.
|
||||
pub fn new(world: &mut World) -> Self {
|
||||
let fetch_state = <Q::State as FetchState>::init(world);
|
||||
let filter_state = <F::State as FetchState>::init(world);
|
||||
|
@ -68,6 +70,7 @@ where
|
|||
state
|
||||
}
|
||||
|
||||
/// Checks if the query is empty for the given [`World`], where the last change and current tick are given.
|
||||
#[inline]
|
||||
pub fn is_empty(&self, world: &World, last_change_tick: u32, change_tick: u32) -> bool {
|
||||
// SAFE: the iterator is instantly consumed via `none_remaining` and the implementation of
|
||||
|
@ -78,6 +81,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Takes a query for the given [`World`], checks if the given world is the same as the query, and
|
||||
/// generates new archetypes for the given world.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the `world.id()` does not equal the current [`QueryState`] internal id.
|
||||
pub fn update_archetypes(&mut self, world: &World) {
|
||||
self.validate_world(world);
|
||||
let archetypes = world.archetypes();
|
||||
|
@ -98,6 +107,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a new [`Archetype`].
|
||||
pub fn new_archetype(&mut self, archetype: &Archetype) {
|
||||
if self.fetch_state.matches_archetype(archetype)
|
||||
&& self.filter_state.matches_archetype(archetype)
|
||||
|
@ -121,6 +131,9 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Gets the query result for the given [`World`] and [`Entity`].
|
||||
///
|
||||
/// This can only be called for read-only queries, see [`Self::get_mut`] for write-queries.
|
||||
#[inline]
|
||||
pub fn get<'w, 's>(
|
||||
&'s mut self,
|
||||
|
@ -134,6 +147,17 @@ where
|
|||
unsafe { self.get_unchecked(world, entity) }
|
||||
}
|
||||
|
||||
/// Gets the query result for the given [`World`] and [`Entity`].
|
||||
#[inline]
|
||||
pub fn get_mut<'w, 's>(
|
||||
&'s mut self,
|
||||
world: &'w mut World,
|
||||
entity: Entity,
|
||||
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> {
|
||||
// SAFETY: query has unique world access
|
||||
unsafe { self.get_unchecked(world, entity) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_manual<'w, 's>(
|
||||
&'s self,
|
||||
|
@ -155,16 +179,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_mut<'w, 's>(
|
||||
&'s mut self,
|
||||
world: &'w mut World,
|
||||
entity: Entity,
|
||||
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> {
|
||||
// SAFETY: query has unique world access
|
||||
unsafe { self.get_unchecked(world, entity) }
|
||||
}
|
||||
|
||||
/// Gets the query result for the given [`World`] and [`Entity`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
|
@ -184,7 +200,11 @@ where
|
|||
)
|
||||
}
|
||||
|
||||
/// Gets the query result for the given [`World`] and [`Entity`], where the last change and
|
||||
/// the current change tick are given.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
/// have unique access to the components they query.
|
||||
pub unsafe fn get_unchecked_manual<'w, 's>(
|
||||
|
@ -219,6 +239,9 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns an [`Iterator`] over the query results for the given [`World`].
|
||||
///
|
||||
/// This can only be called for read-only queries, see [`Self::iter_mut`] for write-queries.
|
||||
#[inline]
|
||||
pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, Q, F>
|
||||
where
|
||||
|
@ -228,6 +251,13 @@ where
|
|||
unsafe { self.iter_unchecked(world) }
|
||||
}
|
||||
|
||||
/// Returns an [`Iterator`] over the query results for the given [`World`].
|
||||
#[inline]
|
||||
pub fn iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> QueryIter<'w, 's, Q, F> {
|
||||
// SAFETY: query has unique world access
|
||||
unsafe { self.iter_unchecked(world) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter_manual<'w, 's>(&'s self, world: &'w World) -> QueryIter<'w, 's, Q, F>
|
||||
where
|
||||
|
@ -240,12 +270,16 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> QueryIter<'w, 's, Q, F> {
|
||||
// SAFETY: query has unique world access
|
||||
unsafe { self.iter_unchecked(world) }
|
||||
}
|
||||
|
||||
/// Returns an [`Iterator`] over all possible combinations of `K` query results without repetition.
|
||||
/// This can only be called for read-only queries.
|
||||
///
|
||||
/// For permutations of size K of query returning N results, you will get:
|
||||
/// - if K == N: one permutation of all query results
|
||||
/// - if K < N: all possible K-sized combinations of query results, without repetition
|
||||
/// - if K > N: empty set (no K-sized combinations exist)
|
||||
///
|
||||
/// This can only be called for read-only queries, see [`Self::iter_combinations_mut`] for
|
||||
/// write-queries.
|
||||
#[inline]
|
||||
pub fn iter_combinations<'w, 's, const K: usize>(
|
||||
&'s mut self,
|
||||
|
@ -258,6 +292,13 @@ where
|
|||
unsafe { self.iter_combinations_unchecked(world) }
|
||||
}
|
||||
|
||||
/// Iterates over all possible combinations of `K` query results for the given [`World`]
|
||||
/// without repetition.
|
||||
///
|
||||
/// For permutations of size K of query returning N results, you will get:
|
||||
/// - if K == N: one permutation of all query results
|
||||
/// - if K < N: all possible K-sized combinations of query results, without repetition
|
||||
/// - if K > N: empty set (no K-sized combinations exist)
|
||||
#[inline]
|
||||
pub fn iter_combinations_mut<'w, 's, const K: usize>(
|
||||
&'s mut self,
|
||||
|
@ -267,6 +308,8 @@ where
|
|||
unsafe { self.iter_combinations_unchecked(world) }
|
||||
}
|
||||
|
||||
/// Returns an [`Iterator`] over the query results for the given [`World`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
|
@ -280,6 +323,10 @@ where
|
|||
self.iter_unchecked_manual(world, world.last_change_tick(), world.read_change_tick())
|
||||
}
|
||||
|
||||
/// Returns an [`Iterator`] over all possible combinations of `K` query results for the
|
||||
/// given [`World`] without repetition.
|
||||
/// This can only be called for read-only queries.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
|
@ -297,7 +344,11 @@ where
|
|||
)
|
||||
}
|
||||
|
||||
/// Returns an [`Iterator`] for the given [`World`], where the last change and
|
||||
/// the current change tick are given.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
/// have unique access to the components they query.
|
||||
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
|
||||
|
@ -312,7 +363,12 @@ where
|
|||
QueryIter::new(world, self, last_change_tick, change_tick)
|
||||
}
|
||||
|
||||
/// Returns an [`Iterator`] over all possible combinations of `K` query results for the
|
||||
/// given [`World`] without repetition.
|
||||
/// This can only be called for read-only queries.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
/// have unique access to the components they query.
|
||||
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
|
||||
|
@ -327,6 +383,10 @@ where
|
|||
QueryCombinationIter::new(world, self, last_change_tick, change_tick)
|
||||
}
|
||||
|
||||
/// Runs `func` on each query result for the given [`World`]. This is faster than the equivalent
|
||||
/// iter() method, but cannot be chained like a normal [`Iterator`].
|
||||
///
|
||||
/// This can only be called for read-only queries, see [`Self::for_each_mut`] for write-queries.
|
||||
#[inline]
|
||||
pub fn for_each<'w, 's>(
|
||||
&'s mut self,
|
||||
|
@ -341,6 +401,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Runs `func` on each query result for the given [`World`]. This is faster than the equivalent
|
||||
/// iter_mut() method, but cannot be chained like a normal [`Iterator`].
|
||||
#[inline]
|
||||
pub fn for_each_mut<'w, 's>(
|
||||
&'s mut self,
|
||||
|
@ -353,6 +415,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Runs `func` on each query result for the given [`World`]. This is faster than the equivalent
|
||||
/// iter() method, but cannot be chained like a normal [`Iterator`].
|
||||
///
|
||||
/// This can only be called for read-only queries.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
|
@ -372,6 +439,10 @@ where
|
|||
);
|
||||
}
|
||||
|
||||
/// Runs `func` on each query result in parallel using the given `task_pool`.
|
||||
///
|
||||
/// This can only be called for read-only queries, see [`Self::par_for_each_mut`] for
|
||||
/// write-queries.
|
||||
#[inline]
|
||||
pub fn par_for_each<'w, 's>(
|
||||
&'s mut self,
|
||||
|
@ -388,6 +459,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Runs `func` on each query result in parallel using the given `task_pool`.
|
||||
#[inline]
|
||||
pub fn par_for_each_mut<'w, 's>(
|
||||
&'s mut self,
|
||||
|
@ -402,6 +474,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Runs `func` on each query result in parallel using the given `task_pool`.
|
||||
///
|
||||
/// This can only be called for read-only queries.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
|
@ -425,6 +501,10 @@ where
|
|||
);
|
||||
}
|
||||
|
||||
/// Runs `func` on each query result for the given [`World`], where the last change and
|
||||
/// the current change tick are given. This is faster than the equivalent
|
||||
/// iter() method, but cannot be chained like a normal [`Iterator`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
|
@ -477,6 +557,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Runs `func` on each query result in parallel for the given [`World`], where the last change and
|
||||
/// the current change tick are given. This is faster than the equivalent
|
||||
/// iter() method, but cannot be chained like a normal [`Iterator`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
|
|
|
@ -10,6 +10,7 @@ use bevy_reflect::{impl_reflect_value, FromType, Reflect, ReflectDeserialize};
|
|||
pub struct ReflectComponent {
|
||||
add_component: fn(&mut World, Entity, &dyn Reflect),
|
||||
apply_component: fn(&mut World, Entity, &dyn Reflect),
|
||||
remove_component: fn(&mut World, Entity),
|
||||
reflect_component: fn(&World, Entity) -> Option<&dyn Reflect>,
|
||||
reflect_component_mut: unsafe fn(&World, Entity) -> Option<ReflectMut>,
|
||||
copy_component: fn(&World, &mut World, Entity, Entity),
|
||||
|
@ -24,6 +25,10 @@ impl ReflectComponent {
|
|||
(self.apply_component)(world, entity, component);
|
||||
}
|
||||
|
||||
pub fn remove_component(&self, world: &mut World, entity: Entity) {
|
||||
(self.remove_component)(world, entity);
|
||||
}
|
||||
|
||||
pub fn reflect_component<'a>(
|
||||
&self,
|
||||
world: &'a World,
|
||||
|
@ -83,6 +88,9 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
|
|||
let mut component = world.get_mut::<C>(entity).unwrap();
|
||||
component.apply(reflected_component);
|
||||
},
|
||||
remove_component: |world, entity| {
|
||||
world.entity_mut(entity).remove::<C>();
|
||||
},
|
||||
copy_component: |source_world, destination_world, source_entity, destination_entity| {
|
||||
let source_component = source_world.get::<C>(source_entity).unwrap();
|
||||
let mut destination_component = C::from_world(destination_world);
|
||||
|
|
|
@ -316,7 +316,7 @@ mod tests {
|
|||
use super::SchedulingEvent::{self, *};
|
||||
use crate::{
|
||||
schedule::{SingleThreadedExecutor, Stage, SystemStage},
|
||||
system::{IntoSystem, NonSend, Query, Res, ResMut},
|
||||
system::{NonSend, Query, Res, ResMut},
|
||||
world::World,
|
||||
};
|
||||
use async_channel::Receiver;
|
||||
|
@ -338,9 +338,9 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
fn wants_for_nothing() {}
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(wants_for_nothing.system())
|
||||
.with_system(wants_for_nothing.system())
|
||||
.with_system(wants_for_nothing.system());
|
||||
.with_system(wants_for_nothing)
|
||||
.with_system(wants_for_nothing)
|
||||
.with_system(wants_for_nothing);
|
||||
stage.run(&mut world);
|
||||
stage.run(&mut world);
|
||||
assert_eq!(
|
||||
|
@ -356,24 +356,24 @@ mod tests {
|
|||
fn wants_mut(_: ResMut<usize>) {}
|
||||
fn wants_ref(_: Res<usize>) {}
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(wants_mut.system())
|
||||
.with_system(wants_mut.system());
|
||||
.with_system(wants_mut)
|
||||
.with_system(wants_mut);
|
||||
stage.run(&mut world);
|
||||
assert_eq!(
|
||||
receive_events(&world),
|
||||
vec![StartedSystems(1), StartedSystems(1),]
|
||||
);
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(wants_mut.system())
|
||||
.with_system(wants_ref.system());
|
||||
.with_system(wants_mut)
|
||||
.with_system(wants_ref);
|
||||
stage.run(&mut world);
|
||||
assert_eq!(
|
||||
receive_events(&world),
|
||||
vec![StartedSystems(1), StartedSystems(1),]
|
||||
);
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(wants_ref.system())
|
||||
.with_system(wants_ref.system());
|
||||
.with_system(wants_ref)
|
||||
.with_system(wants_ref);
|
||||
stage.run(&mut world);
|
||||
assert_eq!(receive_events(&world), vec![StartedSystems(2),]);
|
||||
}
|
||||
|
@ -385,24 +385,24 @@ mod tests {
|
|||
fn wants_mut(_: Query<&mut usize>) {}
|
||||
fn wants_ref(_: Query<&usize>) {}
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(wants_mut.system())
|
||||
.with_system(wants_mut.system());
|
||||
.with_system(wants_mut)
|
||||
.with_system(wants_mut);
|
||||
stage.run(&mut world);
|
||||
assert_eq!(
|
||||
receive_events(&world),
|
||||
vec![StartedSystems(1), StartedSystems(1),]
|
||||
);
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(wants_mut.system())
|
||||
.with_system(wants_ref.system());
|
||||
.with_system(wants_mut)
|
||||
.with_system(wants_ref);
|
||||
stage.run(&mut world);
|
||||
assert_eq!(
|
||||
receive_events(&world),
|
||||
vec![StartedSystems(1), StartedSystems(1),]
|
||||
);
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(wants_ref.system())
|
||||
.with_system(wants_ref.system());
|
||||
.with_system(wants_ref)
|
||||
.with_system(wants_ref);
|
||||
stage.run(&mut world);
|
||||
assert_eq!(receive_events(&world), vec![StartedSystems(2),]);
|
||||
let mut world = World::new();
|
||||
|
@ -410,8 +410,8 @@ mod tests {
|
|||
fn wants_mut_usize(_: Query<(&mut usize, &f32)>) {}
|
||||
fn wants_mut_u32(_: Query<(&mut u32, &f32)>) {}
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(wants_mut_usize.system())
|
||||
.with_system(wants_mut_u32.system());
|
||||
.with_system(wants_mut_usize)
|
||||
.with_system(wants_mut_u32);
|
||||
stage.run(&mut world);
|
||||
assert_eq!(receive_events(&world), vec![StartedSystems(2),]);
|
||||
}
|
||||
|
@ -426,12 +426,12 @@ mod tests {
|
|||
}
|
||||
fn empty() {}
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(non_send.system())
|
||||
.with_system(non_send.system())
|
||||
.with_system(empty.system())
|
||||
.with_system(empty.system())
|
||||
.with_system(non_send.system())
|
||||
.with_system(non_send.system());
|
||||
.with_system(non_send)
|
||||
.with_system(non_send)
|
||||
.with_system(empty)
|
||||
.with_system(empty)
|
||||
.with_system(non_send)
|
||||
.with_system(non_send);
|
||||
stage.run(&mut world);
|
||||
assert_eq!(
|
||||
receive_events(&world),
|
||||
|
|
|
@ -1,15 +1,115 @@
|
|||
pub use bevy_ecs_macros::{AmbiguitySetLabel, RunCriteriaLabel, StageLabel, SystemLabel};
|
||||
|
||||
use bevy_utils::define_label;
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
fmt::Debug,
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
define_label!(StageLabel);
|
||||
pub trait DynEq: Any {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
||||
fn dyn_eq(&self, other: &dyn DynEq) -> bool;
|
||||
}
|
||||
|
||||
impl<T> DynEq for T
|
||||
where
|
||||
T: Any + Eq,
|
||||
{
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn dyn_eq(&self, other: &dyn DynEq) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref::<T>() {
|
||||
return self == other;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DynHash: DynEq {
|
||||
fn as_dyn_eq(&self) -> &dyn DynEq;
|
||||
|
||||
fn dyn_hash(&self, state: &mut dyn Hasher);
|
||||
}
|
||||
|
||||
impl<T> DynHash for T
|
||||
where
|
||||
T: DynEq + Hash,
|
||||
{
|
||||
fn as_dyn_eq(&self) -> &dyn DynEq {
|
||||
self
|
||||
}
|
||||
|
||||
fn dyn_hash(&self, mut state: &mut dyn Hasher) {
|
||||
T::hash(self, &mut state);
|
||||
self.type_id().hash(&mut state);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StageLabel: DynHash + Debug + Send + Sync + 'static {
|
||||
#[doc(hidden)]
|
||||
fn dyn_clone(&self) -> Box<dyn StageLabel>;
|
||||
}
|
||||
pub(crate) type BoxedStageLabel = Box<dyn StageLabel>;
|
||||
|
||||
define_label!(SystemLabel);
|
||||
pub trait SystemLabel: DynHash + Debug + Send + Sync + 'static {
|
||||
#[doc(hidden)]
|
||||
fn dyn_clone(&self) -> Box<dyn SystemLabel>;
|
||||
}
|
||||
pub(crate) type BoxedSystemLabel = Box<dyn SystemLabel>;
|
||||
|
||||
define_label!(AmbiguitySetLabel);
|
||||
pub trait AmbiguitySetLabel: DynHash + Debug + Send + Sync + 'static {
|
||||
#[doc(hidden)]
|
||||
fn dyn_clone(&self) -> Box<dyn AmbiguitySetLabel>;
|
||||
}
|
||||
pub(crate) type BoxedAmbiguitySetLabel = Box<dyn AmbiguitySetLabel>;
|
||||
|
||||
define_label!(RunCriteriaLabel);
|
||||
pub trait RunCriteriaLabel: DynHash + Debug + Send + Sync + 'static {
|
||||
#[doc(hidden)]
|
||||
fn dyn_clone(&self) -> Box<dyn RunCriteriaLabel>;
|
||||
}
|
||||
pub(crate) type BoxedRunCriteriaLabel = Box<dyn RunCriteriaLabel>;
|
||||
|
||||
macro_rules! impl_label {
|
||||
($trait_name:ident) => {
|
||||
impl PartialEq for dyn $trait_name {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.dyn_eq(other.as_dyn_eq())
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for dyn $trait_name {}
|
||||
|
||||
impl Hash for dyn $trait_name {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.dyn_hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Box<dyn $trait_name> {
|
||||
fn clone(&self) -> Self {
|
||||
self.dyn_clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl $trait_name for Cow<'static, str> {
|
||||
fn dyn_clone(&self) -> Box<dyn $trait_name> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl $trait_name for &'static str {
|
||||
fn dyn_clone(&self) -> Box<dyn $trait_name> {
|
||||
Box::new(<&str>::clone(self))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_label!(StageLabel);
|
||||
impl_label!(SystemLabel);
|
||||
impl_label!(AmbiguitySetLabel);
|
||||
impl_label!(RunCriteriaLabel);
|
||||
|
|
|
@ -22,10 +22,7 @@ pub use system_set::*;
|
|||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::{
|
||||
system::{IntoSystem, System},
|
||||
world::World,
|
||||
};
|
||||
use crate::{system::System, world::World};
|
||||
use bevy_utils::HashMap;
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -79,7 +76,7 @@ impl Schedule {
|
|||
&mut self,
|
||||
system: S,
|
||||
) -> &mut Self {
|
||||
self.run_criteria.set(Box::new(system.system()));
|
||||
self.run_criteria.set(Box::new(system));
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -61,12 +61,12 @@ impl Default for BoxedRunCriteria {
|
|||
}
|
||||
|
||||
impl BoxedRunCriteria {
|
||||
pub fn set(&mut self, criteria_system: BoxedSystem<(), ShouldRun>) {
|
||||
pub(crate) fn set(&mut self, criteria_system: BoxedSystem<(), ShouldRun>) {
|
||||
self.criteria_system = Some(criteria_system);
|
||||
self.initialized = false;
|
||||
}
|
||||
|
||||
pub fn should_run(&mut self, world: &mut World) -> ShouldRun {
|
||||
pub(crate) fn should_run(&mut self, world: &mut World) -> ShouldRun {
|
||||
if let Some(ref mut run_criteria) = self.criteria_system {
|
||||
if !self.initialized {
|
||||
run_criteria.initialize(world);
|
||||
|
@ -99,16 +99,16 @@ pub(crate) enum RunCriteriaInner {
|
|||
}
|
||||
|
||||
pub(crate) struct RunCriteriaContainer {
|
||||
pub should_run: ShouldRun,
|
||||
pub inner: RunCriteriaInner,
|
||||
pub label: Option<BoxedRunCriteriaLabel>,
|
||||
pub before: Vec<BoxedRunCriteriaLabel>,
|
||||
pub after: Vec<BoxedRunCriteriaLabel>,
|
||||
pub(crate) should_run: ShouldRun,
|
||||
pub(crate) inner: RunCriteriaInner,
|
||||
pub(crate) label: Option<BoxedRunCriteriaLabel>,
|
||||
pub(crate) before: Vec<BoxedRunCriteriaLabel>,
|
||||
pub(crate) after: Vec<BoxedRunCriteriaLabel>,
|
||||
archetype_generation: ArchetypeGeneration,
|
||||
}
|
||||
|
||||
impl RunCriteriaContainer {
|
||||
pub fn from_descriptor(descriptor: RunCriteriaDescriptor) -> Self {
|
||||
pub(crate) fn from_descriptor(descriptor: RunCriteriaDescriptor) -> Self {
|
||||
Self {
|
||||
should_run: ShouldRun::Yes,
|
||||
inner: match descriptor.system {
|
||||
|
@ -122,21 +122,21 @@ impl RunCriteriaContainer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Cow<'static, str> {
|
||||
pub(crate) fn name(&self) -> Cow<'static, str> {
|
||||
match &self.inner {
|
||||
RunCriteriaInner::Single(system) => system.name(),
|
||||
RunCriteriaInner::Piped { system, .. } => system.name(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initialize(&mut self, world: &mut World) {
|
||||
pub(crate) fn initialize(&mut self, world: &mut World) {
|
||||
match &mut self.inner {
|
||||
RunCriteriaInner::Single(system) => system.initialize(world),
|
||||
RunCriteriaInner::Piped { system, .. } => system.initialize(world),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_archetypes(&mut self, world: &World) {
|
||||
pub(crate) fn update_archetypes(&mut self, world: &World) {
|
||||
let archetypes = world.archetypes();
|
||||
let new_generation = archetypes.generation();
|
||||
let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
component::ComponentId,
|
||||
prelude::IntoSystem,
|
||||
schedule::{
|
||||
graph_utils::{self, DependencyGraphError},
|
||||
BoxedRunCriteria, BoxedRunCriteriaLabel, BoxedSystemLabel, DuplicateLabelStrategy,
|
||||
|
@ -8,7 +9,6 @@ use crate::{
|
|||
RunCriteriaDescriptor, RunCriteriaDescriptorOrLabel, RunCriteriaInner, ShouldRun,
|
||||
SingleThreadedExecutor, SystemContainer, SystemDescriptor, SystemSet,
|
||||
},
|
||||
system::System,
|
||||
world::{World, WorldId},
|
||||
};
|
||||
use bevy_utils::{tracing::info, HashMap, HashSet};
|
||||
|
@ -26,7 +26,7 @@ pub trait Stage: Downcast + Send + Sync {
|
|||
|
||||
impl_downcast!(Stage);
|
||||
|
||||
/// When this resource is present in the `AppBuilder`'s `Resources`,
|
||||
/// When this resource is present in the `App`'s `Resources`,
|
||||
/// each `SystemStage` will log a report containing
|
||||
/// pairs of systems with ambiguous execution order.
|
||||
///
|
||||
|
@ -83,6 +83,7 @@ pub struct SystemStage {
|
|||
uninitialized_parallel: Vec<usize>,
|
||||
/// Saves the value of the World change_tick during the last tick check
|
||||
last_tick_check: u32,
|
||||
/// If true, buffers will be automatically applied at the end of the stage. If false, buffers must be manually applied.
|
||||
apply_buffers: bool,
|
||||
}
|
||||
|
||||
|
@ -301,16 +302,19 @@ impl SystemStage {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_run_criteria<S: System<In = (), Out = ShouldRun>>(mut self, system: S) -> Self {
|
||||
self.set_run_criteria(system);
|
||||
pub fn with_run_criteria<Param, S: IntoSystem<(), ShouldRun, Param>>(
|
||||
mut self,
|
||||
system: S,
|
||||
) -> Self {
|
||||
self.set_run_criteria(system.system());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_run_criteria<S: System<In = (), Out = ShouldRun>>(
|
||||
pub fn set_run_criteria<Param, S: IntoSystem<(), ShouldRun, Param>>(
|
||||
&mut self,
|
||||
system: S,
|
||||
) -> &mut Self {
|
||||
self.stage_run_criteria.set(Box::new(system));
|
||||
self.stage_run_criteria.set(Box::new(system.system()));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -919,14 +923,8 @@ mod tests {
|
|||
move |world| world.get_resource_mut::<Vec<usize>>().unwrap().push(tag)
|
||||
}
|
||||
|
||||
// This is silly. https://github.com/bevyengine/bevy/issues/1029
|
||||
macro_rules! make_parallel {
|
||||
($tag:expr) => {{
|
||||
fn parallel(mut resource: ResMut<Vec<usize>>) {
|
||||
resource.push($tag)
|
||||
}
|
||||
parallel
|
||||
}};
|
||||
fn make_parallel(tag: usize) -> impl FnMut(ResMut<Vec<usize>>) {
|
||||
move |mut resource: ResMut<Vec<usize>>| resource.push(tag)
|
||||
}
|
||||
|
||||
fn every_other_time(mut has_ran: Local<bool>) -> ShouldRun {
|
||||
|
@ -944,7 +942,7 @@ mod tests {
|
|||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(0).exclusive_system().at_start())
|
||||
.with_system(make_parallel!(1).system())
|
||||
.with_system(make_parallel(1))
|
||||
.with_system(make_exclusive(2).exclusive_system().before_commands())
|
||||
.with_system(make_exclusive(3).exclusive_system().at_end());
|
||||
stage.run(&mut world);
|
||||
|
@ -963,7 +961,7 @@ mod tests {
|
|||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_exclusive(2).exclusive_system().before_commands())
|
||||
.with_system(make_exclusive(3).exclusive_system().at_end())
|
||||
.with_system(make_parallel!(1).system())
|
||||
.with_system(make_parallel(1))
|
||||
.with_system(make_exclusive(0).exclusive_system().at_start());
|
||||
stage.run(&mut world);
|
||||
assert_eq!(
|
||||
|
@ -979,10 +977,10 @@ mod tests {
|
|||
|
||||
world.get_resource_mut::<Vec<usize>>().unwrap().clear();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel!(2).exclusive_system().before_commands())
|
||||
.with_system(make_parallel!(3).exclusive_system().at_end())
|
||||
.with_system(make_parallel!(1).system())
|
||||
.with_system(make_parallel!(0).exclusive_system().at_start());
|
||||
.with_system(make_parallel(2).exclusive_system().before_commands())
|
||||
.with_system(make_parallel(3).exclusive_system().at_end())
|
||||
.with_system(make_parallel(1))
|
||||
.with_system(make_parallel(0).exclusive_system().at_start());
|
||||
stage.run(&mut world);
|
||||
assert_eq!(
|
||||
*world.get_resource::<Vec<usize>>().unwrap(),
|
||||
|
@ -1239,9 +1237,9 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel!(1).system().after("0").label("1"))
|
||||
.with_system(make_parallel!(2).system().after("1"))
|
||||
.with_system(make_parallel!(0).system().label("0"));
|
||||
.with_system(make_parallel(1).after("0").label("1"))
|
||||
.with_system(make_parallel(2).after("1"))
|
||||
.with_system(make_parallel(0).label("0"));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -1256,9 +1254,9 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel!(1).system().label("1").before("2"))
|
||||
.with_system(make_parallel!(2).system().label("2"))
|
||||
.with_system(make_parallel!(0).system().before("1"));
|
||||
.with_system(make_parallel(1).label("1").before("2"))
|
||||
.with_system(make_parallel(2).label("2"))
|
||||
.with_system(make_parallel(0).before("1"));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -1273,11 +1271,11 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel!(2).system().label("2"))
|
||||
.with_system(make_parallel!(1).system().after("0").before("2"))
|
||||
.with_system(make_parallel!(0).system().label("0"))
|
||||
.with_system(make_parallel!(4).system().label("4"))
|
||||
.with_system(make_parallel!(3).system().after("2").before("4"));
|
||||
.with_system(make_parallel(2).label("2"))
|
||||
.with_system(make_parallel(1).after("0").before("2"))
|
||||
.with_system(make_parallel(0).label("0"))
|
||||
.with_system(make_parallel(4).label("4"))
|
||||
.with_system(make_parallel(3).after("2").before("4"));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -1292,9 +1290,9 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel!(1).system().label("first").after("0"))
|
||||
.with_system(make_parallel!(2).system().after("first"))
|
||||
.with_system(make_parallel!(0).system().label("first").label("0"));
|
||||
.with_system(make_parallel(1).label("first").after("0"))
|
||||
.with_system(make_parallel(2).after("first"))
|
||||
.with_system(make_parallel(0).label("first").label("0"));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -1305,11 +1303,11 @@ mod tests {
|
|||
|
||||
world.get_resource_mut::<Vec<usize>>().unwrap().clear();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel!(2).system().after("01").label("2"))
|
||||
.with_system(make_parallel!(1).system().label("01").after("0"))
|
||||
.with_system(make_parallel!(0).system().label("01").label("0"))
|
||||
.with_system(make_parallel!(4).system().label("4"))
|
||||
.with_system(make_parallel!(3).system().after("2").before("4"));
|
||||
.with_system(make_parallel(2).after("01").label("2"))
|
||||
.with_system(make_parallel(1).label("01").after("0"))
|
||||
.with_system(make_parallel(0).label("01").label("0"))
|
||||
.with_system(make_parallel(4).label("4"))
|
||||
.with_system(make_parallel(3).after("2").before("4"));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -1320,17 +1318,11 @@ mod tests {
|
|||
|
||||
world.get_resource_mut::<Vec<usize>>().unwrap().clear();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel!(2).system().label("234").label("2"))
|
||||
.with_system(make_parallel!(1).system().before("234").after("0"))
|
||||
.with_system(make_parallel!(0).system().label("0"))
|
||||
.with_system(make_parallel!(4).system().label("234").label("4"))
|
||||
.with_system(
|
||||
make_parallel!(3)
|
||||
.system()
|
||||
.label("234")
|
||||
.after("2")
|
||||
.before("4"),
|
||||
);
|
||||
.with_system(make_parallel(2).label("234").label("2"))
|
||||
.with_system(make_parallel(1).before("234").after("0"))
|
||||
.with_system(make_parallel(0).label("0"))
|
||||
.with_system(make_parallel(4).label("234").label("4"))
|
||||
.with_system(make_parallel(3).label("234").after("2").before("4"));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -1346,24 +1338,22 @@ mod tests {
|
|||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(
|
||||
make_parallel!(2)
|
||||
.system()
|
||||
make_parallel(2)
|
||||
.label("2")
|
||||
.after("1")
|
||||
.before("3")
|
||||
.before("3"),
|
||||
)
|
||||
.with_system(
|
||||
make_parallel!(1)
|
||||
.system()
|
||||
make_parallel(1)
|
||||
.label("1")
|
||||
.after("0")
|
||||
.after("0")
|
||||
.before("2"),
|
||||
)
|
||||
.with_system(make_parallel!(0).system().label("0").before("1"))
|
||||
.with_system(make_parallel!(4).system().label("4").after("3"))
|
||||
.with_system(make_parallel!(3).system().label("3").after("2").before("4"));
|
||||
.with_system(make_parallel(0).label("0").before("1"))
|
||||
.with_system(make_parallel(4).label("4").after("3"))
|
||||
.with_system(make_parallel(3).label("3").after("2").before("4"));
|
||||
stage.run(&mut world);
|
||||
for container in stage.parallel.iter() {
|
||||
assert!(container.dependencies().len() <= 1);
|
||||
|
@ -1381,14 +1371,14 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel!(2).system().label("2"))
|
||||
.with_system(make_parallel(2).label("2"))
|
||||
.with_system_set(
|
||||
SystemSet::new()
|
||||
.with_system(make_parallel!(0).system().label("0"))
|
||||
.with_system(make_parallel!(4).system().label("4"))
|
||||
.with_system(make_parallel!(3).system().after("2").before("4")),
|
||||
.with_system(make_parallel(0).label("0"))
|
||||
.with_system(make_parallel(4).label("4"))
|
||||
.with_system(make_parallel(3).after("2").before("4")),
|
||||
)
|
||||
.with_system(make_parallel!(1).system().after("0").before("2"));
|
||||
.with_system(make_parallel(1).after("0").before("2"));
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
stage.run(&mut world);
|
||||
|
@ -1405,12 +1395,11 @@ mod tests {
|
|||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(
|
||||
make_parallel!(0)
|
||||
.system()
|
||||
make_parallel(0)
|
||||
.label("0")
|
||||
.with_run_criteria(every_other_time),
|
||||
)
|
||||
.with_system(make_parallel!(1).system().after("0"));
|
||||
.with_system(make_parallel(1).after("0"));
|
||||
stage.run(&mut world);
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
|
@ -1423,13 +1412,13 @@ mod tests {
|
|||
|
||||
world.get_resource_mut::<Vec<usize>>().unwrap().clear();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel!(0).system().before("1"))
|
||||
.with_system(make_parallel(0).before("1"))
|
||||
.with_system_set(
|
||||
SystemSet::new()
|
||||
.with_run_criteria(every_other_time.system())
|
||||
.with_system(make_parallel!(1).system().label("1")),
|
||||
.with_run_criteria(every_other_time)
|
||||
.with_system(make_parallel(1).label("1")),
|
||||
)
|
||||
.with_system(make_parallel!(2).system().after("1"));
|
||||
.with_system(make_parallel(2).after("1"));
|
||||
stage.run(&mut world);
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
|
@ -1444,21 +1433,19 @@ mod tests {
|
|||
world.get_resource_mut::<Vec<usize>>().unwrap().clear();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system_run_criteria(every_other_time.label("every other time"))
|
||||
.with_system(make_parallel!(0).system().before("1"))
|
||||
.with_system(make_parallel(0).before("1"))
|
||||
.with_system(
|
||||
make_parallel!(1)
|
||||
.system()
|
||||
make_parallel(1)
|
||||
.label("1")
|
||||
.with_run_criteria("every other time"),
|
||||
)
|
||||
.with_system(
|
||||
make_parallel!(2)
|
||||
.system()
|
||||
make_parallel(2)
|
||||
.label("2")
|
||||
.after("1")
|
||||
.with_run_criteria("every other time"),
|
||||
)
|
||||
.with_system(make_parallel!(3).system().after("2"));
|
||||
.with_system(make_parallel(3).after("2"));
|
||||
stage.run(&mut world);
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
|
@ -1480,34 +1467,26 @@ mod tests {
|
|||
}
|
||||
}
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel!(0).system().label("0"))
|
||||
.with_system(make_parallel(0).label("0"))
|
||||
.with_system(
|
||||
make_parallel!(1)
|
||||
.system()
|
||||
make_parallel(1)
|
||||
.label("1")
|
||||
.after("0")
|
||||
.with_run_criteria(every_other_time.system().label("every other time")),
|
||||
.with_run_criteria(every_other_time.label("every other time")),
|
||||
)
|
||||
.with_system(
|
||||
make_parallel!(2)
|
||||
.system()
|
||||
make_parallel(2)
|
||||
.label("2")
|
||||
.after("1")
|
||||
.with_run_criteria(RunCriteria::pipe("every other time", eot_piped.system())),
|
||||
)
|
||||
.with_system(
|
||||
make_parallel!(3)
|
||||
.system()
|
||||
make_parallel(3)
|
||||
.label("3")
|
||||
.after("2")
|
||||
.with_run_criteria("every other time".pipe(eot_piped.system()).label("piped")),
|
||||
)
|
||||
.with_system(
|
||||
make_parallel!(4)
|
||||
.system()
|
||||
.after("3")
|
||||
.with_run_criteria("piped"),
|
||||
);
|
||||
.with_system(make_parallel(4).after("3").with_run_criteria("piped"));
|
||||
for _ in 0..4 {
|
||||
stage.run(&mut world);
|
||||
}
|
||||
|
@ -1523,27 +1502,16 @@ mod tests {
|
|||
|
||||
// Discarding extra criteria with matching labels.
|
||||
world.get_resource_mut::<Vec<usize>>().unwrap().clear();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel!(0).system().before("1"))
|
||||
.with_system(
|
||||
make_parallel!(1).system().label("1").with_run_criteria(
|
||||
every_other_time
|
||||
.system()
|
||||
.label_discard_if_duplicate("every other time"),
|
||||
),
|
||||
)
|
||||
.with_system(
|
||||
make_parallel!(2)
|
||||
.system()
|
||||
.label("2")
|
||||
.after("1")
|
||||
.with_run_criteria(
|
||||
every_other_time
|
||||
.system()
|
||||
.label_discard_if_duplicate("every other time"),
|
||||
),
|
||||
)
|
||||
.with_system(make_parallel!(3).system().after("2"));
|
||||
let mut stage =
|
||||
SystemStage::parallel()
|
||||
.with_system(make_parallel(0).before("1"))
|
||||
.with_system(make_parallel(1).label("1").with_run_criteria(
|
||||
every_other_time.label_discard_if_duplicate("every other time"),
|
||||
))
|
||||
.with_system(make_parallel(2).label("2").after("1").with_run_criteria(
|
||||
every_other_time.label_discard_if_duplicate("every other time"),
|
||||
))
|
||||
.with_system(make_parallel(3).after("2"));
|
||||
stage.run(&mut world);
|
||||
stage.run(&mut world);
|
||||
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
|
||||
|
@ -1561,8 +1529,8 @@ mod tests {
|
|||
fn duplicate_run_criteria_label_panic() {
|
||||
let mut world = World::new();
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system_run_criteria(every_other_time.system().label("every other time"))
|
||||
.with_system_run_criteria(every_other_time.system().label("every other time"));
|
||||
.with_system_run_criteria(every_other_time.label("every other time"))
|
||||
.with_system_run_criteria(every_other_time.label("every other time"));
|
||||
stage.run(&mut world);
|
||||
}
|
||||
|
||||
|
@ -1571,8 +1539,7 @@ mod tests {
|
|||
fn parallel_cycle_1() {
|
||||
let mut world = World::new();
|
||||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage =
|
||||
SystemStage::parallel().with_system(make_parallel!(0).system().label("0").after("0"));
|
||||
let mut stage = SystemStage::parallel().with_system(make_parallel(0).label("0").after("0"));
|
||||
stage.run(&mut world);
|
||||
}
|
||||
|
||||
|
@ -1582,8 +1549,8 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel!(0).system().label("0").after("1"))
|
||||
.with_system(make_parallel!(1).system().label("1").after("0"));
|
||||
.with_system(make_parallel(0).label("0").after("1"))
|
||||
.with_system(make_parallel(1).label("1").after("0"));
|
||||
stage.run(&mut world);
|
||||
}
|
||||
|
||||
|
@ -1594,9 +1561,9 @@ mod tests {
|
|||
|
||||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(make_parallel!(0).system().label("0"))
|
||||
.with_system(make_parallel!(1).system().after("0").before("2"))
|
||||
.with_system(make_parallel!(2).system().label("2").before("0"));
|
||||
.with_system(make_parallel(0).label("0"))
|
||||
.with_system(make_parallel(1).after("0").before("2"))
|
||||
.with_system(make_parallel(2).label("2").before("0"));
|
||||
stage.run(&mut world);
|
||||
}
|
||||
|
||||
|
@ -1625,21 +1592,21 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(empty.system().label("0"))
|
||||
.with_system(empty.system().label("1").after("0"))
|
||||
.with_system(empty.system().label("2"))
|
||||
.with_system(empty.system().label("3").after("2").before("4"))
|
||||
.with_system(empty.system().label("4"));
|
||||
.with_system(empty.label("0"))
|
||||
.with_system(empty.label("1").after("0"))
|
||||
.with_system(empty.label("2"))
|
||||
.with_system(empty.label("3").after("2").before("4"))
|
||||
.with_system(empty.label("4"));
|
||||
stage.initialize_systems(&mut world);
|
||||
stage.rebuild_orders_and_dependencies();
|
||||
assert_eq!(find_ambiguities(&stage.parallel).len(), 0);
|
||||
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(empty.system().label("0"))
|
||||
.with_system(component.system().label("1").after("0"))
|
||||
.with_system(empty.system().label("2"))
|
||||
.with_system(empty.system().label("3").after("2").before("4"))
|
||||
.with_system(component.system().label("4"));
|
||||
.with_system(empty.label("0"))
|
||||
.with_system(component.label("1").after("0"))
|
||||
.with_system(empty.label("2"))
|
||||
.with_system(empty.label("3").after("2").before("4"))
|
||||
.with_system(component.label("4"));
|
||||
stage.initialize_systems(&mut world);
|
||||
stage.rebuild_orders_and_dependencies();
|
||||
let ambiguities = find_ambiguities_first_labels(&stage.parallel);
|
||||
|
@ -1650,11 +1617,11 @@ mod tests {
|
|||
assert_eq!(ambiguities.len(), 1);
|
||||
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(empty.system().label("0"))
|
||||
.with_system(resource.system().label("1").after("0"))
|
||||
.with_system(empty.system().label("2"))
|
||||
.with_system(empty.system().label("3").after("2").before("4"))
|
||||
.with_system(resource.system().label("4"));
|
||||
.with_system(empty.label("0"))
|
||||
.with_system(resource.label("1").after("0"))
|
||||
.with_system(empty.label("2"))
|
||||
.with_system(empty.label("3").after("2").before("4"))
|
||||
.with_system(resource.label("4"));
|
||||
stage.initialize_systems(&mut world);
|
||||
stage.rebuild_orders_and_dependencies();
|
||||
let ambiguities = find_ambiguities_first_labels(&stage.parallel);
|
||||
|
@ -1665,21 +1632,21 @@ mod tests {
|
|||
assert_eq!(ambiguities.len(), 1);
|
||||
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(empty.system().label("0"))
|
||||
.with_system(resource.system().label("1").after("0"))
|
||||
.with_system(empty.system().label("2"))
|
||||
.with_system(empty.system().label("3").after("2").before("4"))
|
||||
.with_system(component.system().label("4"));
|
||||
.with_system(empty.label("0"))
|
||||
.with_system(resource.label("1").after("0"))
|
||||
.with_system(empty.label("2"))
|
||||
.with_system(empty.label("3").after("2").before("4"))
|
||||
.with_system(component.label("4"));
|
||||
stage.initialize_systems(&mut world);
|
||||
stage.rebuild_orders_and_dependencies();
|
||||
assert_eq!(find_ambiguities(&stage.parallel).len(), 0);
|
||||
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(component.system().label("0"))
|
||||
.with_system(resource.system().label("1").after("0"))
|
||||
.with_system(empty.system().label("2"))
|
||||
.with_system(component.system().label("3").after("2").before("4"))
|
||||
.with_system(resource.system().label("4"));
|
||||
.with_system(component.label("0"))
|
||||
.with_system(resource.label("1").after("0"))
|
||||
.with_system(empty.label("2"))
|
||||
.with_system(component.label("3").after("2").before("4"))
|
||||
.with_system(resource.label("4"));
|
||||
stage.initialize_systems(&mut world);
|
||||
stage.rebuild_orders_and_dependencies();
|
||||
let ambiguities = find_ambiguities_first_labels(&stage.parallel);
|
||||
|
@ -1694,17 +1661,11 @@ mod tests {
|
|||
assert_eq!(ambiguities.len(), 2);
|
||||
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(component.system().label("0"))
|
||||
.with_system(
|
||||
resource
|
||||
.system()
|
||||
.label("1")
|
||||
.after("0")
|
||||
.in_ambiguity_set("a"),
|
||||
)
|
||||
.with_system(empty.system().label("2"))
|
||||
.with_system(component.system().label("3").after("2").before("4"))
|
||||
.with_system(resource.system().label("4").in_ambiguity_set("a"));
|
||||
.with_system(component.label("0"))
|
||||
.with_system(resource.label("1").after("0").in_ambiguity_set("a"))
|
||||
.with_system(empty.label("2"))
|
||||
.with_system(component.label("3").after("2").before("4"))
|
||||
.with_system(resource.label("4").in_ambiguity_set("a"));
|
||||
stage.initialize_systems(&mut world);
|
||||
stage.rebuild_orders_and_dependencies();
|
||||
let ambiguities = find_ambiguities_first_labels(&stage.parallel);
|
||||
|
@ -1715,9 +1676,9 @@ mod tests {
|
|||
assert_eq!(ambiguities.len(), 1);
|
||||
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(component.system().label("0").before("2"))
|
||||
.with_system(component.system().label("1").before("2"))
|
||||
.with_system(component.system().label("2"));
|
||||
.with_system(component.label("0").before("2"))
|
||||
.with_system(component.label("1").before("2"))
|
||||
.with_system(component.label("2"));
|
||||
stage.initialize_systems(&mut world);
|
||||
stage.rebuild_orders_and_dependencies();
|
||||
let ambiguities = find_ambiguities_first_labels(&stage.parallel);
|
||||
|
@ -1728,9 +1689,9 @@ mod tests {
|
|||
assert_eq!(ambiguities.len(), 1);
|
||||
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(component.system().label("0"))
|
||||
.with_system(component.system().label("1").after("0"))
|
||||
.with_system(component.system().label("2").after("0"));
|
||||
.with_system(component.label("0"))
|
||||
.with_system(component.label("1").after("0"))
|
||||
.with_system(component.label("2").after("0"));
|
||||
stage.initialize_systems(&mut world);
|
||||
stage.rebuild_orders_and_dependencies();
|
||||
let ambiguities = find_ambiguities_first_labels(&stage.parallel);
|
||||
|
@ -1741,10 +1702,10 @@ mod tests {
|
|||
assert_eq!(ambiguities.len(), 1);
|
||||
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(component.system().label("0").before("1").before("2"))
|
||||
.with_system(component.system().label("1"))
|
||||
.with_system(component.system().label("2"))
|
||||
.with_system(component.system().label("3").after("1").after("2"));
|
||||
.with_system(component.label("0").before("1").before("2"))
|
||||
.with_system(component.label("1"))
|
||||
.with_system(component.label("2"))
|
||||
.with_system(component.label("3").after("1").after("2"));
|
||||
stage.initialize_systems(&mut world);
|
||||
stage.rebuild_orders_and_dependencies();
|
||||
let ambiguities = find_ambiguities_first_labels(&stage.parallel);
|
||||
|
@ -1755,20 +1716,20 @@ mod tests {
|
|||
assert_eq!(ambiguities.len(), 1);
|
||||
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(component.system().label("0").before("1").before("2"))
|
||||
.with_system(component.system().label("1").in_ambiguity_set("a"))
|
||||
.with_system(component.system().label("2").in_ambiguity_set("a"))
|
||||
.with_system(component.system().label("3").after("1").after("2"));
|
||||
.with_system(component.label("0").before("1").before("2"))
|
||||
.with_system(component.label("1").in_ambiguity_set("a"))
|
||||
.with_system(component.label("2").in_ambiguity_set("a"))
|
||||
.with_system(component.label("3").after("1").after("2"));
|
||||
stage.initialize_systems(&mut world);
|
||||
stage.rebuild_orders_and_dependencies();
|
||||
let ambiguities = find_ambiguities_first_labels(&stage.parallel);
|
||||
assert_eq!(ambiguities.len(), 0);
|
||||
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(component.system().label("0").before("1").before("2"))
|
||||
.with_system(component.system().label("1").in_ambiguity_set("a"))
|
||||
.with_system(component.system().label("2").in_ambiguity_set("b"))
|
||||
.with_system(component.system().label("3").after("1").after("2"));
|
||||
.with_system(component.label("0").before("1").before("2"))
|
||||
.with_system(component.label("1").in_ambiguity_set("a"))
|
||||
.with_system(component.label("2").in_ambiguity_set("b"))
|
||||
.with_system(component.label("3").after("1").after("2"));
|
||||
stage.initialize_systems(&mut world);
|
||||
stage.rebuild_orders_and_dependencies();
|
||||
let ambiguities = find_ambiguities_first_labels(&stage.parallel);
|
||||
|
@ -1781,20 +1742,18 @@ mod tests {
|
|||
let mut stage = SystemStage::parallel()
|
||||
.with_system(
|
||||
component
|
||||
.system()
|
||||
.label("0")
|
||||
.before("1")
|
||||
.before("2")
|
||||
.before("3")
|
||||
.before("4"),
|
||||
)
|
||||
.with_system(component.system().label("1"))
|
||||
.with_system(component.system().label("2"))
|
||||
.with_system(component.system().label("3"))
|
||||
.with_system(component.system().label("4"))
|
||||
.with_system(component.label("1"))
|
||||
.with_system(component.label("2"))
|
||||
.with_system(component.label("3"))
|
||||
.with_system(component.label("4"))
|
||||
.with_system(
|
||||
component
|
||||
.system()
|
||||
.label("5")
|
||||
.after("1")
|
||||
.after("2")
|
||||
|
@ -1833,20 +1792,18 @@ mod tests {
|
|||
let mut stage = SystemStage::parallel()
|
||||
.with_system(
|
||||
component
|
||||
.system()
|
||||
.label("0")
|
||||
.before("1")
|
||||
.before("2")
|
||||
.before("3")
|
||||
.before("4"),
|
||||
)
|
||||
.with_system(component.system().label("1").in_ambiguity_set("a"))
|
||||
.with_system(component.system().label("2").in_ambiguity_set("a"))
|
||||
.with_system(component.system().label("3").in_ambiguity_set("a"))
|
||||
.with_system(component.system().label("4").in_ambiguity_set("a"))
|
||||
.with_system(component.label("1").in_ambiguity_set("a"))
|
||||
.with_system(component.label("2").in_ambiguity_set("a"))
|
||||
.with_system(component.label("3").in_ambiguity_set("a"))
|
||||
.with_system(component.label("4").in_ambiguity_set("a"))
|
||||
.with_system(
|
||||
component
|
||||
.system()
|
||||
.label("5")
|
||||
.after("1")
|
||||
.after("2")
|
||||
|
@ -1861,26 +1818,23 @@ mod tests {
|
|||
let mut stage = SystemStage::parallel()
|
||||
.with_system(
|
||||
component
|
||||
.system()
|
||||
.label("0")
|
||||
.before("1")
|
||||
.before("2")
|
||||
.before("3")
|
||||
.before("4"),
|
||||
)
|
||||
.with_system(component.system().label("1").in_ambiguity_set("a"))
|
||||
.with_system(component.system().label("2").in_ambiguity_set("a"))
|
||||
.with_system(component.label("1").in_ambiguity_set("a"))
|
||||
.with_system(component.label("2").in_ambiguity_set("a"))
|
||||
.with_system(
|
||||
component
|
||||
.system()
|
||||
.label("3")
|
||||
.in_ambiguity_set("a")
|
||||
.in_ambiguity_set("b"),
|
||||
)
|
||||
.with_system(component.system().label("4").in_ambiguity_set("b"))
|
||||
.with_system(component.label("4").in_ambiguity_set("b"))
|
||||
.with_system(
|
||||
component
|
||||
.system()
|
||||
.label("5")
|
||||
.after("1")
|
||||
.after("2")
|
||||
|
@ -2011,7 +1965,7 @@ mod tests {
|
|||
|
||||
let mut world = World::new();
|
||||
world.insert_resource(0_usize);
|
||||
let mut stage = SystemStage::single(query_count_system.system());
|
||||
let mut stage = SystemStage::single(query_count_system);
|
||||
|
||||
let entity = world.spawn().insert_bundle(()).id();
|
||||
stage.run(&mut world);
|
||||
|
@ -2034,7 +1988,7 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.insert_resource(0_usize);
|
||||
let mut stage = SystemStage::parallel();
|
||||
stage.add_system(query_count_system.system());
|
||||
stage.add_system(query_count_system);
|
||||
|
||||
let entity = world.spawn().insert_bundle(()).id();
|
||||
stage.run(&mut world);
|
||||
|
@ -2056,7 +2010,7 @@ mod tests {
|
|||
|
||||
let mut stage = SystemStage::parallel();
|
||||
fn work() {}
|
||||
stage.add_system(work.system());
|
||||
stage.add_system(work);
|
||||
|
||||
// Overflow twice
|
||||
for _ in 0..10 {
|
||||
|
@ -2134,11 +2088,11 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(spawn_entity.system().label("spawn"))
|
||||
.with_system(spawn_entity.label("spawn"))
|
||||
.with_system_set(
|
||||
SystemSet::new()
|
||||
.with_run_criteria(even_number_of_entities_critiera.system())
|
||||
.with_system(count_entities.system().before("spawn")),
|
||||
.with_run_criteria(even_number_of_entities_critiera)
|
||||
.with_system(count_entities.before("spawn")),
|
||||
);
|
||||
stage.run(&mut world);
|
||||
stage.run(&mut world);
|
||||
|
@ -2169,10 +2123,10 @@ mod tests {
|
|||
|
||||
let mut world = World::new();
|
||||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage_spawn = SystemStage::parallel().with_system(spawn_entity.system());
|
||||
let mut stage_spawn = SystemStage::parallel().with_system(spawn_entity);
|
||||
let mut stage_count = SystemStage::parallel()
|
||||
.with_run_criteria(even_number_of_entities_critiera.system())
|
||||
.with_system(count_entities.system());
|
||||
.with_run_criteria(even_number_of_entities_critiera)
|
||||
.with_system(count_entities);
|
||||
stage_count.run(&mut world);
|
||||
stage_spawn.run(&mut world);
|
||||
stage_count.run(&mut world);
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
RunCriteriaDescriptor, RunCriteriaDescriptorCoercion, RunCriteriaLabel, ShouldRun,
|
||||
SystemSet,
|
||||
},
|
||||
system::{In, IntoChainSystem, IntoSystem, Local, Res, ResMut},
|
||||
system::{ConfigurableSystem, In, IntoChainSystem, Local, Res, ResMut},
|
||||
};
|
||||
use std::{any::TypeId, fmt::Debug, hash::Hash};
|
||||
use thiserror::Error;
|
||||
|
@ -97,9 +97,8 @@ where
|
|||
(|state: Res<State<T>>, pred: Local<Option<T>>| {
|
||||
state.stack.last().unwrap() == pred.as_ref().unwrap() && state.transition.is_none()
|
||||
})
|
||||
.system()
|
||||
.config(|(_, pred)| *pred = Some(Some(s.clone())))
|
||||
.chain(should_run_adapter::<T>.system())
|
||||
.chain(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
.label_discard_if_duplicate(StateCallback::Update.into_label(s))
|
||||
}
|
||||
|
@ -118,9 +117,8 @@ where
|
|||
Some(_) => false,
|
||||
None => *is_inactive,
|
||||
})
|
||||
.system()
|
||||
.config(|(_, _, pred)| *pred = Some(Some(s.clone())))
|
||||
.chain(should_run_adapter::<T>.system())
|
||||
.chain(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
.label_discard_if_duplicate(StateCallback::InactiveUpdate.into_label(s))
|
||||
}
|
||||
|
@ -151,9 +149,8 @@ where
|
|||
Some(_) => false,
|
||||
None => *is_in_stack,
|
||||
})
|
||||
.system()
|
||||
.config(|(_, _, pred)| *pred = Some(Some(s.clone())))
|
||||
.chain(should_run_adapter::<T>.system())
|
||||
.chain(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
.label_discard_if_duplicate(StateCallback::InStackUpdate.into_label(s))
|
||||
}
|
||||
|
@ -171,9 +168,8 @@ where
|
|||
_ => false,
|
||||
})
|
||||
})
|
||||
.system()
|
||||
.config(|(_, pred)| *pred = Some(Some(s.clone())))
|
||||
.chain(should_run_adapter::<T>.system())
|
||||
.chain(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
.label_discard_if_duplicate(StateCallback::Enter.into_label(s))
|
||||
}
|
||||
|
@ -189,9 +185,8 @@ where
|
|||
_ => false,
|
||||
})
|
||||
})
|
||||
.system()
|
||||
.config(|(_, pred)| *pred = Some(Some(s.clone())))
|
||||
.chain(should_run_adapter::<T>.system())
|
||||
.chain(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
.label_discard_if_duplicate(StateCallback::Exit.into_label(s))
|
||||
}
|
||||
|
@ -206,9 +201,8 @@ where
|
|||
_ => false,
|
||||
})
|
||||
})
|
||||
.system()
|
||||
.config(|(_, pred)| *pred = Some(Some(s.clone())))
|
||||
.chain(should_run_adapter::<T>.system())
|
||||
.chain(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
.label_discard_if_duplicate(StateCallback::Pause.into_label(s))
|
||||
}
|
||||
|
@ -223,9 +217,8 @@ where
|
|||
_ => false,
|
||||
})
|
||||
})
|
||||
.system()
|
||||
.config(|(_, pred)| *pred = Some(Some(s.clone())))
|
||||
.chain(should_run_adapter::<T>.system())
|
||||
.chain(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
.label_discard_if_duplicate(StateCallback::Resume.into_label(s))
|
||||
}
|
||||
|
@ -259,8 +252,7 @@ where
|
|||
/// Important note: this set must be inserted **before** all other state-dependant sets to work
|
||||
/// properly!
|
||||
pub fn get_driver() -> SystemSet {
|
||||
SystemSet::default()
|
||||
.with_run_criteria(state_cleaner::<T>.system().label(DriverLabel::of::<T>()))
|
||||
SystemSet::default().with_run_criteria(state_cleaner::<T>.label(DriverLabel::of::<T>()))
|
||||
}
|
||||
|
||||
pub fn new(initial: T) -> Self {
|
||||
|
@ -516,81 +508,63 @@ mod test {
|
|||
stage
|
||||
.add_system_set(
|
||||
State::on_enter_set(MyState::S1)
|
||||
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("startup")).system()),
|
||||
)
|
||||
.add_system_set(
|
||||
State::on_update_set(MyState::S1).with_system(
|
||||
(|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
|
||||
r.push("update S1");
|
||||
s.overwrite_replace(MyState::S2).unwrap();
|
||||
})
|
||||
.system(),
|
||||
),
|
||||
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("startup")),
|
||||
)
|
||||
.add_system_set(State::on_update_set(MyState::S1).with_system(
|
||||
|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
|
||||
r.push("update S1");
|
||||
s.overwrite_replace(MyState::S2).unwrap();
|
||||
},
|
||||
))
|
||||
.add_system_set(
|
||||
State::on_enter_set(MyState::S2)
|
||||
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("enter S2")).system()),
|
||||
)
|
||||
.add_system_set(
|
||||
State::on_update_set(MyState::S2).with_system(
|
||||
(|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
|
||||
r.push("update S2");
|
||||
s.overwrite_replace(MyState::S3).unwrap();
|
||||
})
|
||||
.system(),
|
||||
),
|
||||
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("enter S2")),
|
||||
)
|
||||
.add_system_set(State::on_update_set(MyState::S2).with_system(
|
||||
|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
|
||||
r.push("update S2");
|
||||
s.overwrite_replace(MyState::S3).unwrap();
|
||||
},
|
||||
))
|
||||
.add_system_set(
|
||||
State::on_exit_set(MyState::S2)
|
||||
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("exit S2")).system()),
|
||||
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("exit S2")),
|
||||
)
|
||||
.add_system_set(
|
||||
State::on_enter_set(MyState::S3)
|
||||
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("enter S3")).system()),
|
||||
)
|
||||
.add_system_set(
|
||||
State::on_update_set(MyState::S3).with_system(
|
||||
(|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
|
||||
r.push("update S3");
|
||||
s.overwrite_push(MyState::S4).unwrap();
|
||||
})
|
||||
.system(),
|
||||
),
|
||||
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("enter S3")),
|
||||
)
|
||||
.add_system_set(State::on_update_set(MyState::S3).with_system(
|
||||
|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
|
||||
r.push("update S3");
|
||||
s.overwrite_push(MyState::S4).unwrap();
|
||||
},
|
||||
))
|
||||
.add_system_set(
|
||||
State::on_pause_set(MyState::S3)
|
||||
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("pause S3")).system()),
|
||||
)
|
||||
.add_system_set(
|
||||
State::on_update_set(MyState::S4).with_system(
|
||||
(|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
|
||||
r.push("update S4");
|
||||
s.overwrite_push(MyState::S5).unwrap();
|
||||
})
|
||||
.system(),
|
||||
),
|
||||
)
|
||||
.add_system_set(
|
||||
State::on_inactive_update_set(MyState::S4).with_system(
|
||||
(|mut r: ResMut<Vec<&'static str>>| r.push("inactive S4"))
|
||||
.system()
|
||||
.label("inactive s4"),
|
||||
),
|
||||
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("pause S3")),
|
||||
)
|
||||
.add_system_set(State::on_update_set(MyState::S4).with_system(
|
||||
|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
|
||||
r.push("update S4");
|
||||
s.overwrite_push(MyState::S5).unwrap();
|
||||
},
|
||||
))
|
||||
.add_system_set(State::on_inactive_update_set(MyState::S4).with_system(
|
||||
(|mut r: ResMut<Vec<&'static str>>| r.push("inactive S4")).label("inactive s4"),
|
||||
))
|
||||
.add_system_set(
|
||||
State::on_update_set(MyState::S5).with_system(
|
||||
(|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
|
||||
r.push("update S5");
|
||||
s.overwrite_push(MyState::S6).unwrap();
|
||||
})
|
||||
.system()
|
||||
.after("inactive s4"),
|
||||
),
|
||||
)
|
||||
.add_system_set(
|
||||
State::on_inactive_update_set(MyState::S5).with_system(
|
||||
(|mut r: ResMut<Vec<&'static str>>| r.push("inactive S5"))
|
||||
.system()
|
||||
.label("inactive s5")
|
||||
.after("inactive s4"),
|
||||
),
|
||||
|
@ -601,17 +575,16 @@ mod test {
|
|||
r.push("update S6");
|
||||
s.overwrite_push(MyState::Final).unwrap();
|
||||
})
|
||||
.system()
|
||||
.after("inactive s5"),
|
||||
),
|
||||
)
|
||||
.add_system_set(
|
||||
State::on_resume_set(MyState::S4)
|
||||
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("resume S4")).system()),
|
||||
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("resume S4")),
|
||||
)
|
||||
.add_system_set(
|
||||
State::on_exit_set(MyState::S5)
|
||||
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("exit S4")).system()),
|
||||
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("exit S4")),
|
||||
);
|
||||
|
||||
const EXPECTED: &[&str] = &[
|
||||
|
@ -671,7 +644,7 @@ mod test {
|
|||
world.insert_resource(State::new(AppState::Main));
|
||||
world.insert_resource(false);
|
||||
world.insert_resource("control");
|
||||
let mut stage = SystemStage::parallel().with_system(should_run_once.system());
|
||||
let mut stage = SystemStage::parallel().with_system(should_run_once);
|
||||
stage.run(&mut world);
|
||||
assert!(*world.get_resource::<bool>().unwrap(), "after control");
|
||||
|
||||
|
@ -679,7 +652,7 @@ mod test {
|
|||
world.insert_resource("test");
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system_set(State::<AppState>::get_driver())
|
||||
.with_system(should_run_once.system());
|
||||
.with_system(should_run_once);
|
||||
stage.run(&mut world);
|
||||
assert!(*world.get_resource::<bool>().unwrap(), "after test");
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ pub(super) struct ExclusiveSystemContainer {
|
|||
}
|
||||
|
||||
impl ExclusiveSystemContainer {
|
||||
pub fn from_descriptor(descriptor: ExclusiveSystemDescriptor) -> Self {
|
||||
pub(super) fn from_descriptor(descriptor: ExclusiveSystemDescriptor) -> Self {
|
||||
ExclusiveSystemContainer {
|
||||
system: descriptor.system,
|
||||
run_criteria_index: None,
|
||||
|
@ -49,7 +49,7 @@ impl ExclusiveSystemContainer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn system_mut(&mut self) -> &mut Box<dyn ExclusiveSystem> {
|
||||
pub(super) fn system_mut(&mut self) -> &mut Box<dyn ExclusiveSystem> {
|
||||
&mut self.system
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@ use crate::{
|
|||
/// struct Something;
|
||||
///
|
||||
/// SystemStage::parallel()
|
||||
/// .with_system(do_something.system().label(Something))
|
||||
/// .with_system(do_the_other_thing.system().after(Something))
|
||||
/// .with_system(do_something.label(Something))
|
||||
/// .with_system(do_the_other_thing.after(Something))
|
||||
/// .with_system(do_something_else.exclusive_system().at_end());
|
||||
/// ```
|
||||
pub enum SystemDescriptor {
|
||||
|
@ -60,6 +60,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoSystemDescriptor<()> for SystemDescriptor {
|
||||
fn into_descriptor(self) -> SystemDescriptor {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoSystemDescriptor<()> for BoxedSystem<(), ()> {
|
||||
fn into_descriptor(self) -> SystemDescriptor {
|
||||
new_parallel_descriptor(self).into_descriptor()
|
||||
|
|
|
@ -267,9 +267,13 @@ const fn padding_needed_for(layout: &Layout, align: usize) -> usize {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::BlobVec;
|
||||
use crate::component::TypeInfo;
|
||||
use std::{alloc::Layout, cell::RefCell, rc::Rc};
|
||||
|
||||
// SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value.
|
||||
unsafe fn drop_ptr<T>(x: *mut u8) {
|
||||
x.cast::<T>().drop_in_place()
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `blob_vec` must have a layout that matches Layout::new::<T>()
|
||||
|
@ -300,7 +304,7 @@ mod tests {
|
|||
#[test]
|
||||
fn resize_test() {
|
||||
let item_layout = Layout::new::<usize>();
|
||||
let drop = TypeInfo::drop_ptr::<usize>;
|
||||
let drop = drop_ptr::<usize>;
|
||||
let mut blob_vec = BlobVec::new(item_layout, drop, 64);
|
||||
unsafe {
|
||||
for i in 0..1_000 {
|
||||
|
@ -330,7 +334,7 @@ mod tests {
|
|||
let drop_counter = Rc::new(RefCell::new(0));
|
||||
{
|
||||
let item_layout = Layout::new::<Foo>();
|
||||
let drop = TypeInfo::drop_ptr::<Foo>;
|
||||
let drop = drop_ptr::<Foo>;
|
||||
let mut blob_vec = BlobVec::new(item_layout, drop, 2);
|
||||
assert_eq!(blob_vec.capacity(), 2);
|
||||
unsafe {
|
||||
|
@ -390,7 +394,7 @@ mod tests {
|
|||
#[test]
|
||||
fn blob_vec_drop_empty_capacity() {
|
||||
let item_layout = Layout::new::<Foo>();
|
||||
let drop = TypeInfo::drop_ptr::<Foo>;
|
||||
let drop = drop_ptr::<Foo>;
|
||||
let _ = BlobVec::new(item_layout, drop, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -522,17 +522,12 @@ impl IndexMut<TableId> for Tables {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
component::{Components, TypeInfo},
|
||||
entity::Entity,
|
||||
storage::Table,
|
||||
};
|
||||
use crate::{component::Components, entity::Entity, storage::Table};
|
||||
|
||||
#[test]
|
||||
fn table() {
|
||||
let mut components = Components::default();
|
||||
let type_info = TypeInfo::of::<usize>();
|
||||
let component_id = components.get_or_insert_with(type_info.type_id(), || type_info);
|
||||
let component_id = components.get_or_insert_id::<usize>();
|
||||
let columns = &[component_id];
|
||||
let mut table = Table::with_capacity(0, columns.len());
|
||||
table.add_column(components.get_info(component_id).unwrap());
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
entity::{Entities, Entity},
|
||||
world::World,
|
||||
};
|
||||
use bevy_utils::tracing::debug;
|
||||
use bevy_utils::tracing::{error, warn};
|
||||
pub use command_queue::CommandQueue;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
@ -16,12 +16,12 @@ pub trait Command: Send + Sync + 'static {
|
|||
}
|
||||
|
||||
/// A list of commands that will be run to modify a [`World`].
|
||||
pub struct Commands<'s, 'w> {
|
||||
pub struct Commands<'w, 's> {
|
||||
queue: &'s mut CommandQueue,
|
||||
entities: &'w Entities,
|
||||
}
|
||||
|
||||
impl<'s, 'w> Commands<'s, 'w> {
|
||||
impl<'w, 's> Commands<'w, 's> {
|
||||
/// Create a new `Commands` from a queue and a world.
|
||||
pub fn new(queue: &'s mut CommandQueue, world: &'w World) -> Self {
|
||||
Self {
|
||||
|
@ -50,7 +50,7 @@ impl<'s, 'w> Commands<'s, 'w> {
|
|||
/// }
|
||||
/// # example_system.system();
|
||||
/// ```
|
||||
pub fn spawn(&mut self) -> EntityCommands<'s, 'w, '_> {
|
||||
pub fn spawn<'a>(&'a mut self) -> EntityCommands<'w, 's, 'a> {
|
||||
let entity = self.entities.reserve_entity();
|
||||
EntityCommands {
|
||||
entity,
|
||||
|
@ -58,7 +58,14 @@ impl<'s, 'w> Commands<'s, 'w> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands<'s, 'w, '_> {
|
||||
/// Returns an [EntityCommands] for the given `entity` (if it exists) or spawns one if it doesn't exist.
|
||||
/// This will return [None] if the `entity` exists with a different generation.
|
||||
///
|
||||
/// # Note
|
||||
/// Spawning a specific `entity` value is rarely the right choice. Most apps should favor [`Commands::spawn`].
|
||||
/// This method should generally only be used for sharing entities across apps, and only when they have a
|
||||
/// scheme worked out to share an ID space (which doesn't happen by default).
|
||||
pub fn get_or_spawn<'a>(&'a mut self, entity: Entity) -> EntityCommands<'w, 's, 'a> {
|
||||
self.add(GetOrSpawn { entity });
|
||||
EntityCommands {
|
||||
entity,
|
||||
|
@ -66,9 +73,8 @@ impl<'s, 'w> Commands<'s, 'w> {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: this is a hack to work around the "multiple worlds" limitations:
|
||||
// Right now Commands must allocate entities from their "scheduled" world, but Commands might be applied to other worlds,
|
||||
// such as the "render world"
|
||||
/// Spawns a [Bundle] without pre-allocating an [Entity]. The [Entity] will be allocated when
|
||||
/// this [Command] is applied.
|
||||
pub fn spawn_and_forget(&mut self, bundle: impl Bundle) {
|
||||
self.queue.push(Spawn { bundle })
|
||||
}
|
||||
|
@ -113,7 +119,7 @@ impl<'s, 'w> Commands<'s, 'w> {
|
|||
/// }
|
||||
/// # example_system.system();
|
||||
/// ```
|
||||
pub fn spawn_bundle<T: Bundle>(&mut self, bundle: T) -> EntityCommands<'s, 'w, '_> {
|
||||
pub fn spawn_bundle<'a, T: Bundle>(&'a mut self, bundle: T) -> EntityCommands<'w, 's, 'a> {
|
||||
let mut e = self.spawn();
|
||||
e.insert_bundle(bundle);
|
||||
e
|
||||
|
@ -138,7 +144,13 @@ impl<'s, 'w> Commands<'s, 'w> {
|
|||
/// }
|
||||
/// # example_system.system();
|
||||
/// ```
|
||||
pub fn entity(&mut self, entity: Entity) -> EntityCommands<'s, 'w, '_> {
|
||||
#[track_caller]
|
||||
pub fn entity<'a>(&'a mut self, entity: Entity) -> EntityCommands<'w, 's, 'a> {
|
||||
assert!(
|
||||
self.entities.contains(entity),
|
||||
"Attempting to create an EntityCommands for entity {:?}, which doesn't exist.",
|
||||
entity
|
||||
);
|
||||
EntityCommands {
|
||||
entity,
|
||||
commands: self,
|
||||
|
@ -155,6 +167,23 @@ impl<'s, 'w> Commands<'s, 'w> {
|
|||
self.queue.push(SpawnBatch { bundles_iter });
|
||||
}
|
||||
|
||||
/// For a given batch of ([Entity], [Bundle]) pairs, either spawns each [Entity] with the given
|
||||
/// bundle (if the entity does not exist), or inserts the [Bundle] (if the entity already exists).
|
||||
/// This is faster than doing equivalent operations one-by-one.
|
||||
///
|
||||
/// # Note
|
||||
/// Spawning a specific `entity` value is rarely the right choice. Most apps should use [`Commands::spawn_batch`].
|
||||
/// This method should generally only be used for sharing entities across apps, and only when they have a scheme
|
||||
/// worked out to share an ID space (which doesn't happen by default).
|
||||
pub fn insert_or_spawn_batch<I, B>(&mut self, bundles_iter: I)
|
||||
where
|
||||
I: IntoIterator + Send + Sync + 'static,
|
||||
I::IntoIter: Iterator<Item = (Entity, B)>,
|
||||
B: Bundle,
|
||||
{
|
||||
self.queue.push(InsertOrSpawnBatch { bundles_iter });
|
||||
}
|
||||
|
||||
/// See [`World::insert_resource`].
|
||||
pub fn insert_resource<T: Component>(&mut self, resource: T) {
|
||||
self.queue.push(InsertResource { resource })
|
||||
|
@ -174,12 +203,12 @@ impl<'s, 'w> Commands<'s, 'w> {
|
|||
}
|
||||
|
||||
/// A list of commands that will be run to modify an [`Entity`].
|
||||
pub struct EntityCommands<'s, 'w, 'a> {
|
||||
pub struct EntityCommands<'w, 's, 'a> {
|
||||
entity: Entity,
|
||||
commands: &'a mut Commands<'s, 'w>,
|
||||
commands: &'a mut Commands<'w, 's>,
|
||||
}
|
||||
|
||||
impl<'s, 'w, 'a> EntityCommands<'s, 'w, 'a> {
|
||||
impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
|
||||
/// Retrieves the current entity's unique [`Entity`] id.
|
||||
#[inline]
|
||||
pub fn id(&self) -> Entity {
|
||||
|
@ -267,7 +296,7 @@ impl<'s, 'w, 'a> EntityCommands<'s, 'w, 'a> {
|
|||
}
|
||||
|
||||
/// Returns the underlying `[Commands]`.
|
||||
pub fn commands(&mut self) -> &mut Commands<'s, 'w> {
|
||||
pub fn commands(&mut self) -> &mut Commands<'w, 's> {
|
||||
self.commands
|
||||
}
|
||||
}
|
||||
|
@ -314,6 +343,32 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub struct InsertOrSpawnBatch<I, B>
|
||||
where
|
||||
I: IntoIterator + Send + Sync + 'static,
|
||||
B: Bundle,
|
||||
I::IntoIter: Iterator<Item = (Entity, B)>,
|
||||
{
|
||||
pub bundles_iter: I,
|
||||
}
|
||||
|
||||
impl<I, B> Command for InsertOrSpawnBatch<I, B>
|
||||
where
|
||||
I: IntoIterator + Send + Sync + 'static,
|
||||
B: Bundle,
|
||||
I::IntoIter: Iterator<Item = (Entity, B)>,
|
||||
{
|
||||
fn write(self, world: &mut World) {
|
||||
if let Err(invalid_entities) = world.insert_or_spawn_batch(self.bundles_iter) {
|
||||
error!(
|
||||
"Failed to 'insert or spawn' bundle of type {} into the following invalid entities: {:?}",
|
||||
std::any::type_name::<B>(),
|
||||
invalid_entities
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Despawn {
|
||||
pub entity: Entity,
|
||||
|
@ -322,7 +377,9 @@ pub struct Despawn {
|
|||
impl Command for Despawn {
|
||||
fn write(self, world: &mut World) {
|
||||
if !world.despawn(self.entity) {
|
||||
debug!("Failed to despawn non-existent entity {:?}", self.entity);
|
||||
warn!("Could not despawn entity {:?} because it doesn't exist in this World.\n\
|
||||
If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\
|
||||
This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", self.entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -337,7 +394,13 @@ where
|
|||
T: Bundle + 'static,
|
||||
{
|
||||
fn write(self, world: &mut World) {
|
||||
world.entity_mut(self.entity).insert_bundle(self.bundle);
|
||||
if let Some(mut entity) = world.get_entity_mut(self.entity) {
|
||||
entity.insert_bundle(self.bundle);
|
||||
} else {
|
||||
panic!("Could not insert a bundle (of type `{}`) for entity {:?} because it doesn't exist in this World.\n\
|
||||
If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\
|
||||
This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", std::any::type_name::<T>(), self.entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,7 +415,13 @@ where
|
|||
T: Component,
|
||||
{
|
||||
fn write(self, world: &mut World) {
|
||||
world.entity_mut(self.entity).insert(self.component);
|
||||
if let Some(mut entity) = world.get_entity_mut(self.entity) {
|
||||
entity.insert(self.component);
|
||||
} else {
|
||||
panic!("Could not add a component (of type `{}`) to entity {:?} because it doesn't exist in this World.\n\
|
||||
If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\
|
||||
This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", std::any::type_name::<T>(), self.entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -96,6 +96,7 @@ impl ExclusiveSystem for ExclusiveSystemCoerced {
|
|||
for archetype in archetypes.archetypes[archetype_index_range].iter() {
|
||||
self.system.new_archetype(archetype);
|
||||
}
|
||||
|
||||
self.system.run((), world);
|
||||
self.system.apply_buffers(world);
|
||||
}
|
||||
|
@ -127,7 +128,7 @@ mod tests {
|
|||
entity::Entity,
|
||||
query::With,
|
||||
schedule::{Stage, SystemStage},
|
||||
system::{Commands, IntoExclusiveSystem, IntoSystem, Query, ResMut},
|
||||
system::{Commands, IntoExclusiveSystem, Query, ResMut},
|
||||
world::World,
|
||||
};
|
||||
#[test]
|
||||
|
@ -145,7 +146,7 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
let mut stage = SystemStage::parallel().with_system(removal.system());
|
||||
let mut stage = SystemStage::parallel().with_system(removal);
|
||||
world.spawn().insert(0.0f32);
|
||||
world.insert_resource(0usize);
|
||||
stage.run(&mut world);
|
||||
|
@ -175,7 +176,7 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.insert_resource(Vec::<usize>::new());
|
||||
let mut stage = SystemStage::parallel()
|
||||
.with_system(spawn_entity.system())
|
||||
.with_system(spawn_entity)
|
||||
.with_system(count_entities.exclusive_system());
|
||||
stage.run(&mut world);
|
||||
stage.run(&mut world);
|
||||
|
|
|
@ -24,7 +24,7 @@ pub struct SystemMeta {
|
|||
}
|
||||
|
||||
impl SystemMeta {
|
||||
pub fn new<T>() -> Self {
|
||||
fn new<T>() -> Self {
|
||||
Self {
|
||||
name: std::any::type_name::<T>().into(),
|
||||
archetype_component_access: Access::default(),
|
||||
|
@ -87,10 +87,10 @@ impl<Param: SystemParam> SystemState<Param> {
|
|||
|
||||
/// Retrieve the [`SystemParam`] values. This can only be called when all parameters are read-only.
|
||||
#[inline]
|
||||
pub fn get<'s, 'w>(
|
||||
pub fn get<'w, 's>(
|
||||
&'s mut self,
|
||||
world: &'w World,
|
||||
) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item
|
||||
) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item
|
||||
where
|
||||
Param::Fetch: ReadOnlySystemParamFetch,
|
||||
{
|
||||
|
@ -101,10 +101,10 @@ impl<Param: SystemParam> SystemState<Param> {
|
|||
|
||||
/// Retrieve the mutable [`SystemParam`] values.
|
||||
#[inline]
|
||||
pub fn get_mut<'s, 'w>(
|
||||
pub fn get_mut<'w, 's>(
|
||||
&'s mut self,
|
||||
world: &'w mut World,
|
||||
) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item {
|
||||
) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item {
|
||||
self.validate_world_and_update_archetypes(world);
|
||||
// SAFE: World is uniquely borrowed and matches the World this SystemState was created with.
|
||||
unsafe { self.get_unchecked_manual(world) }
|
||||
|
@ -145,10 +145,10 @@ impl<Param: SystemParam> SystemState<Param> {
|
|||
/// access is safe in the context of global [`World`] access. The passed-in [`World`] _must_ be the [`World`] the [`SystemState`] was
|
||||
/// created with.
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked_manual<'s, 'w>(
|
||||
pub unsafe fn get_unchecked_manual<'w, 's>(
|
||||
&'s mut self,
|
||||
world: &'w World,
|
||||
) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item {
|
||||
) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item {
|
||||
let change_tick = world.increment_change_tick();
|
||||
let param = <Param::Fetch as SystemParamFetch>::get_param(
|
||||
&mut self.param_state,
|
||||
|
@ -228,7 +228,7 @@ pub struct InputMarker;
|
|||
///
|
||||
/// You get this by calling [`IntoSystem::system`] on a function that only accepts
|
||||
/// [`SystemParam`]s. The output of the system becomes the functions return type, while the input
|
||||
/// becomes the functions [`In`] tagged parameter or `()` if no such paramater exists.
|
||||
/// becomes the functions [`In`] tagged parameter or `()` if no such parameter exists.
|
||||
pub struct FunctionSystem<In, Out, Param, Marker, F>
|
||||
where
|
||||
Param: SystemParam,
|
||||
|
@ -254,7 +254,7 @@ impl<In, Out, Param: SystemParam, Marker, F> FunctionSystem<In, Out, Param, Mark
|
|||
/// fn local_is_42(local: Local<usize>) {
|
||||
/// assert_eq!(*local, 42);
|
||||
/// }
|
||||
/// let mut system = local_is_42.system().config(|config| config.0 = Some(42));
|
||||
/// let mut system = local_is_42.config(|config| config.0 = Some(42));
|
||||
/// system.initialize(world);
|
||||
/// system.run((), world);
|
||||
/// ```
|
||||
|
|
|
@ -24,11 +24,11 @@ mod tests {
|
|||
bundle::Bundles,
|
||||
component::Components,
|
||||
entity::{Entities, Entity},
|
||||
query::{Added, Changed, Or, With, Without},
|
||||
query::{Added, Changed, Or, QueryState, With, Without},
|
||||
schedule::{Schedule, Stage, SystemStage},
|
||||
system::{
|
||||
ConfigurableSystem, IntoExclusiveSystem, IntoSystem, Local, Query, QuerySet,
|
||||
RemovedComponents, Res, ResMut, System, SystemState,
|
||||
ConfigurableSystem, IntoExclusiveSystem, IntoSystem, Local, NonSend, NonSendMut, Query,
|
||||
QuerySet, RemovedComponents, Res, ResMut, System, SystemState,
|
||||
},
|
||||
world::{FromWorld, World},
|
||||
};
|
||||
|
@ -60,7 +60,7 @@ mod tests {
|
|||
system.run((), &mut world);
|
||||
}
|
||||
|
||||
fn run_system<S: System<In = (), Out = ()>>(world: &mut World, system: S) {
|
||||
fn run_system<Param, S: IntoSystem<(), (), Param>>(world: &mut World, system: S) {
|
||||
let mut schedule = Schedule::default();
|
||||
let mut update = SystemStage::parallel();
|
||||
update.add_system(system);
|
||||
|
@ -121,7 +121,7 @@ mod tests {
|
|||
world.spawn().insert_bundle((A, C));
|
||||
world.spawn().insert_bundle((A, D));
|
||||
|
||||
run_system(&mut world, query_system.system());
|
||||
run_system(&mut world, query_system);
|
||||
|
||||
assert!(*world.get_resource::<bool>().unwrap(), "system ran");
|
||||
}
|
||||
|
@ -131,9 +131,9 @@ mod tests {
|
|||
// Regression test for issue #762
|
||||
fn query_system(
|
||||
mut ran: ResMut<bool>,
|
||||
set: QuerySet<(
|
||||
Query<(), Or<(Changed<A>, Changed<B>)>>,
|
||||
Query<(), Or<(Added<A>, Added<B>)>>,
|
||||
mut set: QuerySet<(
|
||||
QueryState<(), Or<(Changed<A>, Changed<B>)>>,
|
||||
QueryState<(), Or<(Added<A>, Added<B>)>>,
|
||||
)>,
|
||||
) {
|
||||
let changed = set.q0().iter().count();
|
||||
|
@ -149,7 +149,7 @@ mod tests {
|
|||
world.insert_resource(false);
|
||||
world.spawn().insert_bundle((A, B));
|
||||
|
||||
run_system(&mut world, query_system.system());
|
||||
run_system(&mut world, query_system);
|
||||
|
||||
assert!(*world.get_resource::<bool>().unwrap(), "system ran");
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ mod tests {
|
|||
|
||||
let mut schedule = Schedule::default();
|
||||
let mut update = SystemStage::parallel();
|
||||
update.add_system(incr_e_on_flip.system());
|
||||
update.add_system(incr_e_on_flip);
|
||||
schedule.add_stage("update", update);
|
||||
schedule.add_stage(
|
||||
"clear_trackers",
|
||||
|
@ -206,7 +206,7 @@ mod tests {
|
|||
fn sys(_q1: Query<&mut A>, _q2: Query<&mut A>) {}
|
||||
|
||||
let mut world = World::default();
|
||||
run_system(&mut world, sys.system());
|
||||
run_system(&mut world, sys);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -214,7 +214,7 @@ mod tests {
|
|||
fn sys(_q1: Query<&mut A, With<B>>, _q2: Query<&mut A, Without<B>>) {}
|
||||
|
||||
let mut world = World::default();
|
||||
run_system(&mut world, sys.system());
|
||||
run_system(&mut world, sys);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -222,7 +222,7 @@ mod tests {
|
|||
fn sys(_q1: Query<(&mut A, &B)>, _q2: Query<&mut A, Without<B>>) {}
|
||||
|
||||
let mut world = World::default();
|
||||
run_system(&mut world, sys.system());
|
||||
run_system(&mut world, sys);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -231,32 +231,36 @@ mod tests {
|
|||
fn sys(_q1: Query<&A>, _q2: Query<&mut A>) {}
|
||||
|
||||
let mut world = World::default();
|
||||
run_system(&mut world, sys.system());
|
||||
run_system(&mut world, sys);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn query_set_system() {
|
||||
fn sys(mut _set: QuerySet<(Query<&mut A>, Query<&A>)>) {}
|
||||
fn sys(mut _set: QuerySet<(QueryState<&mut A>, QueryState<&A>)>) {}
|
||||
let mut world = World::default();
|
||||
run_system(&mut world, sys.system());
|
||||
run_system(&mut world, sys);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn conflicting_query_with_query_set_system() {
|
||||
fn sys(_query: Query<&mut A>, _set: QuerySet<(Query<&mut A>, Query<&B>)>) {}
|
||||
fn sys(_query: Query<&mut A>, _set: QuerySet<(QueryState<&mut A>, QueryState<&B>)>) {}
|
||||
|
||||
let mut world = World::default();
|
||||
run_system(&mut world, sys.system());
|
||||
run_system(&mut world, sys);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn conflicting_query_sets_system() {
|
||||
fn sys(_set_1: QuerySet<(Query<&mut A>,)>, _set_2: QuerySet<(Query<&mut A>, Query<&B>)>) {}
|
||||
fn sys(
|
||||
_set_1: QuerySet<(QueryState<&mut A>,)>,
|
||||
_set_2: QuerySet<(QueryState<&mut A>, QueryState<&B>)>,
|
||||
) {
|
||||
}
|
||||
|
||||
let mut world = World::default();
|
||||
run_system(&mut world, sys.system());
|
||||
run_system(&mut world, sys);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -264,39 +268,39 @@ mod tests {
|
|||
_buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
fn test_for_conflicting_resources<S: System<In = (), Out = ()>>(sys: S) {
|
||||
fn test_for_conflicting_resources<Param, S: IntoSystem<(), (), Param>>(sys: S) {
|
||||
let mut world = World::default();
|
||||
world.insert_resource(BufferRes::default());
|
||||
world.insert_resource(A);
|
||||
world.insert_resource(B);
|
||||
run_system(&mut world, sys.system());
|
||||
run_system(&mut world, sys);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn conflicting_system_resources() {
|
||||
fn sys(_: ResMut<BufferRes>, _: Res<BufferRes>) {}
|
||||
test_for_conflicting_resources(sys.system())
|
||||
test_for_conflicting_resources(sys)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn conflicting_system_resources_reverse_order() {
|
||||
fn sys(_: Res<BufferRes>, _: ResMut<BufferRes>) {}
|
||||
test_for_conflicting_resources(sys.system())
|
||||
test_for_conflicting_resources(sys)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn conflicting_system_resources_multiple_mutable() {
|
||||
fn sys(_: ResMut<BufferRes>, _: ResMut<BufferRes>) {}
|
||||
test_for_conflicting_resources(sys.system())
|
||||
test_for_conflicting_resources(sys)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nonconflicting_system_resources() {
|
||||
fn sys(_: Local<BufferRes>, _: ResMut<BufferRes>, _: Local<A>, _: ResMut<A>) {}
|
||||
test_for_conflicting_resources(sys.system())
|
||||
test_for_conflicting_resources(sys)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -321,12 +325,54 @@ mod tests {
|
|||
*modified = true;
|
||||
}
|
||||
|
||||
run_system(&mut world, sys.system());
|
||||
run_system(&mut world, sys);
|
||||
|
||||
// ensure the system actually ran
|
||||
assert!(*world.get_resource::<bool>().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_send_option_system() {
|
||||
let mut world = World::default();
|
||||
|
||||
world.insert_resource(false);
|
||||
struct NotSend1(std::rc::Rc<i32>);
|
||||
struct NotSend2(std::rc::Rc<i32>);
|
||||
world.insert_non_send(NotSend1(std::rc::Rc::new(0)));
|
||||
|
||||
fn sys(
|
||||
op: Option<NonSend<NotSend1>>,
|
||||
mut _op2: Option<NonSendMut<NotSend2>>,
|
||||
mut run: ResMut<bool>,
|
||||
) {
|
||||
op.expect("NonSend should exist");
|
||||
*run = true;
|
||||
}
|
||||
|
||||
run_system(&mut world, sys);
|
||||
// ensure the system actually ran
|
||||
assert!(*world.get_resource::<bool>().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_send_system() {
|
||||
let mut world = World::default();
|
||||
|
||||
world.insert_resource(false);
|
||||
struct NotSend1(std::rc::Rc<i32>);
|
||||
struct NotSend2(std::rc::Rc<i32>);
|
||||
|
||||
world.insert_non_send(NotSend1(std::rc::Rc::new(1)));
|
||||
world.insert_non_send(NotSend2(std::rc::Rc::new(2)));
|
||||
|
||||
fn sys(_op: NonSend<NotSend1>, mut _op2: NonSendMut<NotSend2>, mut run: ResMut<bool>) {
|
||||
*run = true;
|
||||
}
|
||||
|
||||
run_system(&mut world, sys);
|
||||
assert!(*world.get_resource::<bool>().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_tracking() {
|
||||
let mut world = World::new();
|
||||
|
@ -352,7 +398,7 @@ mod tests {
|
|||
*ran = true;
|
||||
}
|
||||
|
||||
run_system(&mut world, validate_removed.system());
|
||||
run_system(&mut world, validate_removed);
|
||||
assert!(*world.get_resource::<bool>().unwrap(), "system ran");
|
||||
}
|
||||
|
||||
|
@ -365,18 +411,10 @@ mod tests {
|
|||
*modified = true;
|
||||
}
|
||||
|
||||
run_system(
|
||||
&mut world,
|
||||
sys.system().config(|config| config.0 = Some(42)),
|
||||
);
|
||||
run_system(&mut world, sys.config(|config| config.0 = Some(42)));
|
||||
|
||||
// ensure the system actually ran
|
||||
assert!(*world.get_resource::<bool>().unwrap());
|
||||
|
||||
// Now do the same with omitted `.system()`.
|
||||
world.insert_resource(false);
|
||||
run_system(&mut world, sys.config(|config| config.0 = Some(42)));
|
||||
assert!(*world.get_resource::<bool>().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -417,7 +455,7 @@ mod tests {
|
|||
*modified = true;
|
||||
}
|
||||
|
||||
run_system(&mut world, sys.system());
|
||||
run_system(&mut world, sys);
|
||||
|
||||
// ensure the system actually ran
|
||||
assert!(*world.get_resource::<bool>().unwrap());
|
||||
|
@ -528,12 +566,15 @@ mod tests {
|
|||
world.insert_resource(A(42));
|
||||
world.spawn().insert(B(7));
|
||||
|
||||
let mut system_state: SystemState<(Res<A>, Query<&B>, QuerySet<(Query<&C>, Query<&D>)>)> =
|
||||
SystemState::new(&mut world);
|
||||
let mut system_state: SystemState<(
|
||||
Res<A>,
|
||||
Query<&B>,
|
||||
QuerySet<(QueryState<&C>, QueryState<&D>)>,
|
||||
)> = SystemState::new(&mut world);
|
||||
let (a, query, _) = system_state.get(&world);
|
||||
assert_eq!(*a, A(42), "returned resource matches initial value");
|
||||
assert_eq!(
|
||||
*query.single().unwrap(),
|
||||
*query.single(),
|
||||
B(7),
|
||||
"returned component matches initial value"
|
||||
);
|
||||
|
@ -560,7 +601,7 @@ mod tests {
|
|||
let (a, mut query) = system_state.get_mut(&mut world);
|
||||
assert_eq!(*a, A(42), "returned resource matches initial value");
|
||||
assert_eq!(
|
||||
*query.single_mut().unwrap(),
|
||||
*query.single_mut(),
|
||||
B(7),
|
||||
"returned component matches initial value"
|
||||
);
|
||||
|
@ -577,18 +618,18 @@ mod tests {
|
|||
let mut system_state: SystemState<Query<&A, Changed<A>>> = SystemState::new(&mut world);
|
||||
{
|
||||
let query = system_state.get(&world);
|
||||
assert_eq!(*query.single().unwrap(), A(1));
|
||||
assert_eq!(*query.single(), A(1));
|
||||
}
|
||||
|
||||
{
|
||||
let query = system_state.get(&world);
|
||||
assert!(query.single().is_err());
|
||||
assert!(query.get_single().is_err());
|
||||
}
|
||||
|
||||
world.entity_mut(entity).get_mut::<A>().unwrap().0 = 2;
|
||||
{
|
||||
let query = system_state.get(&world);
|
||||
assert_eq!(*query.single().unwrap(), A(2));
|
||||
assert_eq!(*query.single(), A(2));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -632,4 +673,199 @@ mod tests {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// this test exists to show that read-only world-only queries can return data that lives as long as 'world
|
||||
#[test]
|
||||
#[allow(unused)]
|
||||
fn long_life_test() {
|
||||
struct Holder<'w> {
|
||||
value: &'w A,
|
||||
}
|
||||
|
||||
struct State {
|
||||
state: SystemState<Res<'static, A>>,
|
||||
state_q: SystemState<Query<'static, 'static, &'static A>>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn hold_res<'w>(&mut self, world: &'w World) -> Holder<'w> {
|
||||
let a = self.state.get(world);
|
||||
Holder {
|
||||
value: a.into_inner(),
|
||||
}
|
||||
}
|
||||
fn hold_component<'w>(&mut self, world: &'w World, entity: Entity) -> Holder<'w> {
|
||||
let q = self.state_q.get(world);
|
||||
let a = q.get(entity).unwrap();
|
||||
Holder { value: a }
|
||||
}
|
||||
fn hold_components<'w>(&mut self, world: &'w World) -> Vec<Holder<'w>> {
|
||||
let mut components = Vec::new();
|
||||
let q = self.state_q.get(world);
|
||||
for a in q.iter() {
|
||||
components.push(Holder { value: a });
|
||||
}
|
||||
components
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ```compile_fail
|
||||
/// use bevy_ecs::prelude::*;
|
||||
/// struct A(usize);
|
||||
/// fn system(mut query: Query<&mut A>, e: Res<Entity>) {
|
||||
/// let mut iter = query.iter_mut();
|
||||
/// let a = &mut *iter.next().unwrap();
|
||||
///
|
||||
/// let mut iter2 = query.iter_mut();
|
||||
/// let b = &mut *iter2.next().unwrap();
|
||||
///
|
||||
/// // this should fail to compile
|
||||
/// println!("{}", a.0);
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(unused)]
|
||||
#[cfg(doc)]
|
||||
fn system_query_iter_lifetime_safety_test() {}
|
||||
|
||||
/// ```compile_fail
|
||||
/// use bevy_ecs::prelude::*;
|
||||
/// struct A(usize);
|
||||
/// fn system(mut query: Query<&mut A>, e: Res<Entity>) {
|
||||
/// let mut a1 = query.get_mut(*e).unwrap();
|
||||
/// let mut a2 = query.get_mut(*e).unwrap();
|
||||
/// // this should fail to compile
|
||||
/// println!("{} {}", a1.0, a2.0);
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(unused)]
|
||||
#[cfg(doc)]
|
||||
fn system_query_get_lifetime_safety_test() {}
|
||||
|
||||
/// ```compile_fail
|
||||
/// use bevy_ecs::prelude::*;
|
||||
/// struct A(usize);
|
||||
/// fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res<Entity>) {
|
||||
/// let mut q2 = queries.q0();
|
||||
/// let mut iter2 = q2.iter_mut();
|
||||
/// let mut b = iter2.next().unwrap();
|
||||
///
|
||||
/// let q1 = queries.q1();
|
||||
/// let mut iter = q1.iter();
|
||||
/// let a = &*iter.next().unwrap();
|
||||
///
|
||||
/// // this should fail to compile
|
||||
/// b.0 = a.0
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(unused)]
|
||||
#[cfg(doc)]
|
||||
fn system_query_set_iter_lifetime_safety_test() {}
|
||||
|
||||
/// ```compile_fail
|
||||
/// use bevy_ecs::prelude::*;
|
||||
/// struct A(usize);
|
||||
/// fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res<Entity>) {
|
||||
/// let q1 = queries.q1();
|
||||
/// let mut iter = q1.iter();
|
||||
/// let a = &*iter.next().unwrap();
|
||||
///
|
||||
/// let mut q2 = queries.q0();
|
||||
/// let mut iter2 = q2.iter_mut();
|
||||
/// let mut b = iter2.next().unwrap();
|
||||
///
|
||||
/// // this should fail to compile
|
||||
/// b.0 = a.0;
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(unused)]
|
||||
#[cfg(doc)]
|
||||
fn system_query_set_iter_flip_lifetime_safety_test() {}
|
||||
|
||||
/// ```compile_fail
|
||||
/// use bevy_ecs::prelude::*;
|
||||
/// struct A(usize);
|
||||
/// fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res<Entity>) {
|
||||
/// let mut q2 = queries.q0();
|
||||
/// let mut b = q2.get_mut(*e).unwrap();
|
||||
///
|
||||
/// let q1 = queries.q1();
|
||||
/// let a = q1.get(*e).unwrap();
|
||||
///
|
||||
/// // this should fail to compile
|
||||
/// b.0 = a.0
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(unused)]
|
||||
#[cfg(doc)]
|
||||
fn system_query_set_get_lifetime_safety_test() {}
|
||||
|
||||
/// ```compile_fail
|
||||
/// use bevy_ecs::prelude::*;
|
||||
/// struct A(usize);
|
||||
/// fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res<Entity>) {
|
||||
/// let q1 = queries.q1();
|
||||
/// let a = q1.get(*e).unwrap();
|
||||
///
|
||||
/// let mut q2 = queries.q0();
|
||||
/// let mut b = q2.get_mut(*e).unwrap();
|
||||
/// // this should fail to compile
|
||||
/// b.0 = a.0
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(unused)]
|
||||
#[cfg(doc)]
|
||||
fn system_query_set_get_flip_lifetime_safety_test() {}
|
||||
|
||||
/// ```compile_fail
|
||||
/// use bevy_ecs::prelude::*;
|
||||
/// use bevy_ecs::system::SystemState;
|
||||
/// struct A(usize);
|
||||
/// struct B(usize);
|
||||
/// struct State {
|
||||
/// state_r: SystemState<Query<'static, 'static, &'static A>>,
|
||||
/// state_w: SystemState<Query<'static, 'static, &'static mut A>>,
|
||||
/// }
|
||||
///
|
||||
/// impl State {
|
||||
/// fn get_component<'w>(&mut self, world: &'w mut World, entity: Entity) {
|
||||
/// let q1 = self.state_r.get(&world);
|
||||
/// let a1 = q1.get(entity).unwrap();
|
||||
///
|
||||
/// let mut q2 = self.state_w.get_mut(world);
|
||||
/// let a2 = q2.get_mut(entity).unwrap();
|
||||
///
|
||||
/// // this should fail to compile
|
||||
/// println!("{}", a1.0);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(unused)]
|
||||
#[cfg(doc)]
|
||||
fn system_state_get_lifetime_safety_test() {}
|
||||
|
||||
/// ```compile_fail
|
||||
/// use bevy_ecs::prelude::*;
|
||||
/// use bevy_ecs::system::SystemState;
|
||||
/// struct A(usize);
|
||||
/// struct B(usize);
|
||||
/// struct State {
|
||||
/// state_r: SystemState<Query<'static, 'static, &'static A>>,
|
||||
/// state_w: SystemState<Query<'static, 'static, &'static mut A>>,
|
||||
/// }
|
||||
///
|
||||
/// impl State {
|
||||
/// fn get_components<'w>(&mut self, world: &'w mut World) {
|
||||
/// let q1 = self.state_r.get(&world);
|
||||
/// let a1 = q1.iter().next().unwrap();
|
||||
/// let mut q2 = self.state_w.get_mut(world);
|
||||
/// let a2 = q2.iter_mut().next().unwrap();
|
||||
/// // this should fail to compile
|
||||
/// println!("{}", a1.0);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(unused)]
|
||||
#[cfg(doc)]
|
||||
fn system_state_iter_lifetime_safety_test() {}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
use crate::system::{SystemParam, SystemParamFetch, SystemParamState, SystemState};
|
||||
use crate::world::World;
|
||||
|
||||
pub struct ParamState<Param: SystemParam> {
|
||||
state: SystemState,
|
||||
param_state: <Param as SystemParam>::Fetch,
|
||||
change_tick: u32,
|
||||
}
|
||||
|
||||
impl<Param: SystemParam> ParamState<Param> {
|
||||
pub fn new(world: &mut World) -> Self {
|
||||
let mut state = SystemState::new::<Param>();
|
||||
let config = <Param::Fetch as SystemParamState>::default_config();
|
||||
let param_state = <Param::Fetch as SystemParamState>::init(world, &mut state, config);
|
||||
Self {
|
||||
state,
|
||||
param_state,
|
||||
change_tick: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: THIS IS SUPER UNSAFE PLEASE DON'T MERGE UNTIL IT IS CONSTRAINED TO READ-ONLY PARAMS
|
||||
pub fn get<'a>(&'a mut self, world: &'a World) -> <Param::Fetch as SystemParamFetch<'a>>::Item {
|
||||
let change_tick = world.increment_change_tick();
|
||||
self.change_tick = change_tick;
|
||||
// TODO: add/implement ReadOnlySystemParam and constrain param here
|
||||
unsafe {
|
||||
<Param::Fetch as SystemParamFetch>::get_param(
|
||||
&mut self.param_state,
|
||||
&mut self.state,
|
||||
world,
|
||||
change_tick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -107,12 +107,12 @@ use thiserror::Error;
|
|||
///
|
||||
/// This touches all the basics of queries, make sure to check out all the [`WorldQueries`](WorldQuery)
|
||||
/// bevy has to offer.
|
||||
pub struct Query<'w, 's, Q: WorldQuery, F: WorldQuery = ()>
|
||||
pub struct Query<'world, 'state, Q: WorldQuery, F: WorldQuery = ()>
|
||||
where
|
||||
F::Fetch: FilterFetch,
|
||||
{
|
||||
pub(crate) world: &'w World,
|
||||
pub(crate) state: &'s QueryState<Q, F>,
|
||||
pub(crate) world: &'world World,
|
||||
pub(crate) state: &'state QueryState<Q, F>,
|
||||
pub(crate) last_change_tick: u32,
|
||||
pub(crate) change_tick: u32,
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ where
|
|||
///
|
||||
/// This can only be called for read-only queries, see [`Self::iter_mut`] for write-queries.
|
||||
#[inline]
|
||||
pub fn iter(&self) -> QueryIter<'w, 's, Q, F>
|
||||
pub fn iter(&'s self) -> QueryIter<'w, 's, Q, F>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
|
@ -158,6 +158,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns an [`Iterator`] over the query results.
|
||||
#[inline]
|
||||
pub fn iter_mut(&mut self) -> QueryIter<'_, '_, Q, F> {
|
||||
// SAFE: system runs without conflicts with other systems.
|
||||
// same-system queries have runtime borrow checks when they conflict
|
||||
unsafe {
|
||||
self.state
|
||||
.iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an [`Iterator`] over all possible combinations of `K` query results without repetition.
|
||||
/// This can only be called for read-only queries
|
||||
///
|
||||
|
@ -166,7 +177,7 @@ where
|
|||
/// - if K < N: all possible K-sized combinations of query results, without repetition
|
||||
/// - if K > N: empty set (no K-sized combinations exist)
|
||||
#[inline]
|
||||
pub fn iter_combinations<const K: usize>(&self) -> QueryCombinationIter<'w, 's, Q, F, K>
|
||||
pub fn iter_combinations<const K: usize>(&self) -> QueryCombinationIter<'_, '_, Q, F, K>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
|
@ -181,17 +192,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns an [`Iterator`] over the query results.
|
||||
#[inline]
|
||||
pub fn iter_mut(&mut self) -> QueryIter<'w, 's, Q, F> {
|
||||
// SAFE: system runs without conflicts with other systems.
|
||||
// same-system queries have runtime borrow checks when they conflict
|
||||
unsafe {
|
||||
self.state
|
||||
.iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick)
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates over all possible combinations of `K` query results without repetition.
|
||||
///
|
||||
/// The returned value is not an `Iterator`, because that would lead to aliasing of mutable references.
|
||||
|
@ -216,7 +216,7 @@ where
|
|||
#[inline]
|
||||
pub fn iter_combinations_mut<const K: usize>(
|
||||
&mut self,
|
||||
) -> QueryCombinationIter<'w, 's, Q, F, K> {
|
||||
) -> QueryCombinationIter<'_, '_, Q, F, K> {
|
||||
// SAFE: system runs without conflicts with other systems.
|
||||
// same-system queries have runtime borrow checks when they conflict
|
||||
unsafe {
|
||||
|
@ -235,7 +235,7 @@ where
|
|||
/// This function makes it possible to violate Rust's aliasing guarantees. You must make sure
|
||||
/// this call does not result in multiple mutable references to the same component
|
||||
#[inline]
|
||||
pub unsafe fn iter_unsafe(&self) -> QueryIter<'w, 's, Q, F> {
|
||||
pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, '_, Q, F> {
|
||||
// SEMI-SAFE: system runs without conflicts with other systems.
|
||||
// same-system queries have runtime borrow checks when they conflict
|
||||
self.state
|
||||
|
@ -251,7 +251,7 @@ where
|
|||
#[inline]
|
||||
pub unsafe fn iter_combinations_unsafe<const K: usize>(
|
||||
&self,
|
||||
) -> QueryCombinationIter<'w, 's, Q, F, K> {
|
||||
) -> QueryCombinationIter<'_, '_, Q, F, K> {
|
||||
// SEMI-SAFE: system runs without conflicts with other systems.
|
||||
// same-system queries have runtime borrow checks when they conflict
|
||||
self.state.iter_combinations_unchecked_manual(
|
||||
|
@ -266,7 +266,7 @@ where
|
|||
///
|
||||
/// This can only be called for read-only queries, see [`Self::for_each_mut`] for write-queries.
|
||||
#[inline]
|
||||
pub fn for_each(&self, f: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item))
|
||||
pub fn for_each(&'s self, f: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item))
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
|
@ -285,7 +285,7 @@ where
|
|||
/// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot
|
||||
/// be chained like a normal [`Iterator`].
|
||||
#[inline]
|
||||
pub fn for_each_mut(&mut self, f: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item)) {
|
||||
pub fn for_each_mut<'a>(&'a mut self, f: impl FnMut(<Q::Fetch as Fetch<'a, 'a>>::Item)) {
|
||||
// SAFE: system runs without conflicts with other systems. same-system queries have runtime
|
||||
// borrow checks when they conflict
|
||||
unsafe {
|
||||
|
@ -304,7 +304,7 @@ where
|
|||
/// write-queries.
|
||||
#[inline]
|
||||
pub fn par_for_each(
|
||||
&self,
|
||||
&'s self,
|
||||
task_pool: &TaskPool,
|
||||
batch_size: usize,
|
||||
f: impl Fn(<Q::Fetch as Fetch<'w, 's>>::Item) + Send + Sync + Clone,
|
||||
|
@ -327,11 +327,11 @@ where
|
|||
|
||||
/// Runs `f` on each query result in parallel using the given task pool.
|
||||
#[inline]
|
||||
pub fn par_for_each_mut(
|
||||
&mut self,
|
||||
pub fn par_for_each_mut<'a>(
|
||||
&'a mut self,
|
||||
task_pool: &TaskPool,
|
||||
batch_size: usize,
|
||||
f: impl Fn(<Q::Fetch as Fetch<'w, 's>>::Item) + Send + Sync + Clone,
|
||||
f: impl Fn(<Q::Fetch as Fetch<'a, 'a>>::Item) + Send + Sync + Clone,
|
||||
) {
|
||||
// SAFE: system runs without conflicts with other systems. same-system queries have runtime
|
||||
// borrow checks when they conflict
|
||||
|
@ -351,7 +351,10 @@ where
|
|||
///
|
||||
/// This can only be called for read-only queries, see [`Self::get_mut`] for write-queries.
|
||||
#[inline]
|
||||
pub fn get(&self, entity: Entity) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError>
|
||||
pub fn get(
|
||||
&'s self,
|
||||
entity: Entity,
|
||||
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
|
@ -372,7 +375,7 @@ where
|
|||
pub fn get_mut(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> {
|
||||
) -> Result<<Q::Fetch as Fetch>::Item, QueryEntityError> {
|
||||
// SAFE: system runs without conflicts with other systems.
|
||||
// same-system queries have runtime borrow checks when they conflict
|
||||
unsafe {
|
||||
|
@ -393,7 +396,7 @@ where
|
|||
/// this call does not result in multiple mutable references to the same component
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked(
|
||||
&'s self,
|
||||
&self,
|
||||
entity: Entity,
|
||||
) -> Result<<Q::Fetch as Fetch>::Item, QueryEntityError> {
|
||||
// SEMI-SAFE: system runs without conflicts with other systems.
|
||||
|
@ -442,7 +445,7 @@ where
|
|||
pub fn get_component_mut<T: Component>(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
) -> Result<Mut<'w, T>, QueryComponentError> {
|
||||
) -> Result<Mut<'_, T>, QueryComponentError> {
|
||||
// SAFE: unique access to query (preventing aliased access)
|
||||
unsafe { self.get_component_unchecked_mut(entity) }
|
||||
}
|
||||
|
@ -459,7 +462,7 @@ where
|
|||
pub unsafe fn get_component_unchecked_mut<T: Component>(
|
||||
&self,
|
||||
entity: Entity,
|
||||
) -> Result<Mut<'w, T>, QueryComponentError> {
|
||||
) -> Result<Mut<'_, T>, QueryComponentError> {
|
||||
let world = self.world;
|
||||
let entity_ref = world
|
||||
.get_entity(entity)
|
||||
|
@ -485,6 +488,33 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Gets the result of a single-result query.
|
||||
///
|
||||
/// Assumes this query has only one result and panics if there are no or multiple results.
|
||||
/// Use [`Self::get_single`] to handle the error cases explicitly
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::{IntoSystem, Query, With};
|
||||
/// struct Player;
|
||||
/// struct Position(f32, f32);
|
||||
/// fn player_system(query: Query<&Position, With<Player>>) {
|
||||
/// let player_position = query.single();
|
||||
/// // do something with player_position
|
||||
/// }
|
||||
/// # let _check_that_its_a_system = player_system.system();
|
||||
/// ```
|
||||
///
|
||||
/// This can only be called for read-only queries, see [`Self::single_mut`] for write-queries.
|
||||
#[track_caller]
|
||||
pub fn single(&'s self) -> <Q::Fetch as Fetch<'w, 's>>::Item
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
self.get_single().unwrap()
|
||||
}
|
||||
|
||||
/// Gets the result of a single-result query.
|
||||
///
|
||||
/// If the query has exactly one result, returns the result inside `Ok`
|
||||
|
@ -494,27 +524,28 @@ where
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::{IntoSystem, With};
|
||||
/// # use bevy_ecs::system::{Query, QuerySingleError};
|
||||
/// # use bevy_ecs::prelude::IntoSystem;
|
||||
/// struct PlayerScore(i32);
|
||||
/// fn player_scoring_system(query: Query<&PlayerScore>) {
|
||||
/// match query.single() {
|
||||
/// Ok(PlayerScore(score)) => {
|
||||
/// // do something with score
|
||||
/// struct Player;
|
||||
/// struct Position(f32, f32);
|
||||
/// fn player_system(query: Query<&Position, With<Player>>) {
|
||||
/// match query.get_single() {
|
||||
/// Ok(position) => {
|
||||
/// // do something with position
|
||||
/// }
|
||||
/// Err(QuerySingleError::NoEntities(_)) => {
|
||||
/// // no PlayerScore
|
||||
/// // no position with Player
|
||||
/// }
|
||||
/// Err(QuerySingleError::MultipleEntities(_)) => {
|
||||
/// // multiple PlayerScore
|
||||
/// // multiple position with Player
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// # let _check_that_its_a_system = player_scoring_system.system();
|
||||
/// # let _check_that_its_a_system = player_system.system();
|
||||
/// ```
|
||||
///
|
||||
/// This can only be called for read-only queries, see [`Self::single_mut`] for write-queries.
|
||||
pub fn single(&self) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QuerySingleError>
|
||||
/// This can only be called for read-only queries, see [`Self::get_single_mut`] for write-queries.
|
||||
pub fn get_single(&'s self) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QuerySingleError>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
|
@ -531,9 +562,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Gets the query result if it is only a single result, otherwise panics
|
||||
/// If you want to handle the error case yourself you can use the [`Self::get_single_mut`] variant.
|
||||
#[track_caller]
|
||||
pub fn single_mut(&mut self) -> <Q::Fetch as Fetch<'_, '_>>::Item {
|
||||
self.get_single_mut().unwrap()
|
||||
}
|
||||
|
||||
/// Gets the query result if it is only a single result, otherwise returns a
|
||||
/// [`QuerySingleError`].
|
||||
pub fn single_mut(&mut self) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QuerySingleError> {
|
||||
pub fn get_single_mut(
|
||||
&mut self,
|
||||
) -> Result<<Q::Fetch as Fetch<'_, '_>>::Item, QuerySingleError> {
|
||||
let mut query = self.iter_mut();
|
||||
let first = query.next();
|
||||
let extra = query.next().is_some();
|
||||
|
|
|
@ -24,7 +24,7 @@ impl SystemId {
|
|||
///
|
||||
/// Systems are functions with all arguments implementing [SystemParam](crate::system::SystemParam).
|
||||
///
|
||||
/// Systems are added to an application using `AppBuilder::add_system(my_system.system())`
|
||||
/// Systems are added to an application using `App::add_system(my_system)`
|
||||
/// or similar methods, and will generally run once per pass of the main loop.
|
||||
///
|
||||
/// Systems are executed in parallel, in opportunistic order; data access is managed automatically.
|
||||
|
|
|
@ -27,13 +27,12 @@ use std::{
|
|||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # use std::marker::PhantomData;
|
||||
/// use std::marker::PhantomData;
|
||||
/// use bevy_ecs::system::SystemParam;
|
||||
///
|
||||
/// #[derive(SystemParam)]
|
||||
/// struct MyParam<'s, 'w> {
|
||||
/// struct MyParam<'w, 's> {
|
||||
/// foo: Res<'w, usize>,
|
||||
/// // TODO: this isn't ideal ... maybe the SystemParam derive can be smarter about world and state lifetimes?
|
||||
/// #[system_param(ignore)]
|
||||
/// marker: PhantomData<&'s usize>,
|
||||
/// }
|
||||
|
@ -45,7 +44,7 @@ use std::{
|
|||
/// # my_system.system();
|
||||
/// ```
|
||||
pub trait SystemParam: Sized {
|
||||
type Fetch: for<'s, 'w> SystemParamFetch<'s, 'w>;
|
||||
type Fetch: for<'w, 's> SystemParamFetch<'w, 's>;
|
||||
}
|
||||
|
||||
/// The state of a [`SystemParam`].
|
||||
|
@ -84,21 +83,21 @@ pub unsafe trait SystemParamState: Send + Sync + 'static {
|
|||
/// This must only be implemented for [`SystemParamFetch`] impls that exclusively read the World passed in to [`SystemParamFetch::get_param`]
|
||||
pub unsafe trait ReadOnlySystemParamFetch {}
|
||||
|
||||
pub trait SystemParamFetch<'s, 'w>: SystemParamState {
|
||||
pub trait SystemParamFetch<'world, 'state>: SystemParamState {
|
||||
type Item;
|
||||
/// # Safety
|
||||
///
|
||||
/// This call might access any of the input parameters in an unsafe way. Make sure the data
|
||||
/// access is safe in the context of the system scheduler.
|
||||
unsafe fn get_param(
|
||||
state: &'s mut Self,
|
||||
state: &'state mut Self,
|
||||
system_meta: &SystemMeta,
|
||||
world: &'w World,
|
||||
world: &'world World,
|
||||
change_tick: u32,
|
||||
) -> Self::Item;
|
||||
}
|
||||
|
||||
impl<'s, 'w, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'w, 's, Q, F>
|
||||
impl<'w, 's, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'w, 's, Q, F>
|
||||
where
|
||||
F::Fetch: FilterFetch,
|
||||
{
|
||||
|
@ -150,7 +149,7 @@ where
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'s, 'w>
|
||||
impl<'w, 's, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'w, 's>
|
||||
for QueryState<Q, F>
|
||||
where
|
||||
F::Fetch: FilterFetch,
|
||||
|
@ -189,7 +188,13 @@ fn assert_component_access_compatibility(
|
|||
query_type, filter_type, system_name, accesses);
|
||||
}
|
||||
|
||||
pub struct QuerySet<T>(T);
|
||||
pub struct QuerySet<'w, 's, T> {
|
||||
query_states: &'s T,
|
||||
world: &'w World,
|
||||
last_change_tick: u32,
|
||||
change_tick: u32,
|
||||
}
|
||||
|
||||
pub struct QuerySetState<T>(T);
|
||||
|
||||
impl_query_set!();
|
||||
|
@ -254,7 +259,7 @@ impl<'w, T: Component> AsRef<T> for Res<'w, T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The [`SystemParamState`] of [`Res`].
|
||||
/// The [`SystemParamState`] of [`Res<T>`].
|
||||
pub struct ResState<T> {
|
||||
component_id: ComponentId,
|
||||
marker: PhantomData<T>,
|
||||
|
@ -295,7 +300,7 @@ unsafe impl<T: Component> SystemParamState for ResState<T> {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResState<T> {
|
||||
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResState<T> {
|
||||
type Item = Res<'w, T>;
|
||||
|
||||
#[inline]
|
||||
|
@ -323,7 +328,8 @@ impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResState<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The [`SystemParamState`] of `Option<Res<T>>`.
|
||||
/// The [`SystemParamState`] of [`Option<Res<T>>`].
|
||||
/// See: [`Res<T>`]
|
||||
pub struct OptionResState<T>(ResState<T>);
|
||||
|
||||
impl<'a, T: Component> SystemParam for Option<Res<'a, T>> {
|
||||
|
@ -343,7 +349,7 @@ unsafe impl<T: Component> SystemParamState for OptionResState<T> {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResState<T> {
|
||||
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResState<T> {
|
||||
type Item = Option<Res<'w, T>>;
|
||||
|
||||
#[inline]
|
||||
|
@ -364,7 +370,7 @@ impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResState<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The [`SystemParamState`] of [`ResMut`].
|
||||
/// The [`SystemParamState`] of [`ResMut<T>`].
|
||||
pub struct ResMutState<T> {
|
||||
component_id: ComponentId,
|
||||
marker: PhantomData<T>,
|
||||
|
@ -409,7 +415,7 @@ unsafe impl<T: Component> SystemParamState for ResMutState<T> {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResMutState<T> {
|
||||
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResMutState<T> {
|
||||
type Item = ResMut<'w, T>;
|
||||
|
||||
#[inline]
|
||||
|
@ -439,7 +445,8 @@ impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResMutState<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The [`SystemParamState`] of `Option<ResMut<T>>`.
|
||||
/// The [`SystemParamState`] of [`Option<ResMut<T>>`].
|
||||
/// See: [`ResMut<T>`]
|
||||
pub struct OptionResMutState<T>(ResMutState<T>);
|
||||
|
||||
impl<'a, T: Component> SystemParam for Option<ResMut<'a, T>> {
|
||||
|
@ -456,7 +463,7 @@ unsafe impl<T: Component> SystemParamState for OptionResMutState<T> {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResMutState<T> {
|
||||
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResMutState<T> {
|
||||
type Item = Option<ResMut<'w, T>>;
|
||||
|
||||
#[inline]
|
||||
|
@ -479,7 +486,7 @@ impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResMutState<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'s, 'w> SystemParam for Commands<'s, 'w> {
|
||||
impl<'w, 's> SystemParam for Commands<'w, 's> {
|
||||
type Fetch = CommandQueue;
|
||||
}
|
||||
|
||||
|
@ -501,8 +508,8 @@ unsafe impl SystemParamState for CommandQueue {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w> SystemParamFetch<'s, 'w> for CommandQueue {
|
||||
type Item = Commands<'s, 'w>;
|
||||
impl<'w, 's> SystemParamFetch<'w, 's> for CommandQueue {
|
||||
type Item = Commands<'w, 's>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_param(
|
||||
|
@ -571,7 +578,7 @@ impl<'a, T: Component> DerefMut for Local<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The [`SystemParamState`] of [`Local`].
|
||||
/// The [`SystemParamState`] of [`Local<T>`].
|
||||
pub struct LocalState<T: Component>(T);
|
||||
|
||||
impl<'a, T: Component + FromWorld> SystemParam for Local<'a, T> {
|
||||
|
@ -591,7 +598,7 @@ unsafe impl<T: Component + FromWorld> SystemParamState for LocalState<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'s, 'w, T: Component + FromWorld> SystemParamFetch<'s, 'w> for LocalState<T> {
|
||||
impl<'w, 's, T: Component + FromWorld> SystemParamFetch<'w, 's> for LocalState<T> {
|
||||
type Item = Local<'s, T>;
|
||||
|
||||
#[inline]
|
||||
|
@ -639,7 +646,7 @@ impl<'a, T> RemovedComponents<'a, T> {
|
|||
// SAFE: Only reads World components
|
||||
unsafe impl<T: Component> ReadOnlySystemParamFetch for RemovedComponentsState<T> {}
|
||||
|
||||
/// The [`SystemParamState`] of [`RemovedComponents`].
|
||||
/// The [`SystemParamState`] of [`RemovedComponents<T>`].
|
||||
pub struct RemovedComponentsState<T> {
|
||||
component_id: ComponentId,
|
||||
marker: PhantomData<T>,
|
||||
|
@ -664,7 +671,7 @@ unsafe impl<T: Component> SystemParamState for RemovedComponentsState<T> {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for RemovedComponentsState<T> {
|
||||
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for RemovedComponentsState<T> {
|
||||
type Item = RemovedComponents<'w, T>;
|
||||
|
||||
#[inline]
|
||||
|
@ -736,7 +743,7 @@ impl<'w, T: 'static> Deref for NonSend<'w, T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The [`SystemParamState`] of [`NonSend`].
|
||||
/// The [`SystemParamState`] of [`NonSend<T>`].
|
||||
pub struct NonSendState<T> {
|
||||
component_id: ComponentId,
|
||||
marker: PhantomData<fn() -> T>,
|
||||
|
@ -779,7 +786,7 @@ unsafe impl<T: 'static> SystemParamState for NonSendState<T> {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendState<T> {
|
||||
impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendState<T> {
|
||||
type Item = NonSend<'w, T>;
|
||||
|
||||
#[inline]
|
||||
|
@ -809,10 +816,11 @@ impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendState<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The [`SystemParamState`] of `Option<NonSend<T>>`.
|
||||
/// The [`SystemParamState`] of [`Option<NonSend<T>>`].
|
||||
/// See: [`NonSend<T>`]
|
||||
pub struct OptionNonSendState<T>(NonSendState<T>);
|
||||
|
||||
impl<'a, T: Component> SystemParam for Option<NonSend<'a, T>> {
|
||||
impl<'w, T: 'static> SystemParam for Option<NonSend<'w, T>> {
|
||||
type Fetch = OptionNonSendState<T>;
|
||||
}
|
||||
|
||||
|
@ -829,7 +837,7 @@ unsafe impl<T: 'static> SystemParamState for OptionNonSendState<T> {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for OptionNonSendState<T> {
|
||||
impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendState<T> {
|
||||
type Item = Option<NonSend<'w, T>>;
|
||||
|
||||
#[inline]
|
||||
|
@ -851,7 +859,7 @@ impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for OptionNonSendState<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The [`SystemParamState`] of [`NonSendMut`].
|
||||
/// The [`SystemParamState`] of [`NonSendMut<T>`].
|
||||
pub struct NonSendMutState<T> {
|
||||
component_id: ComponentId,
|
||||
marker: PhantomData<fn() -> T>,
|
||||
|
@ -869,7 +877,7 @@ unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> {
|
|||
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
|
||||
system_meta.set_non_send();
|
||||
|
||||
let component_id = world.components.get_or_insert_non_send_resource_id::<T>();
|
||||
let component_id = world.initialize_non_send_resource::<T>();
|
||||
let combined_access = system_meta.component_access_set.combined_access_mut();
|
||||
if combined_access.has_write(component_id) {
|
||||
panic!(
|
||||
|
@ -898,7 +906,7 @@ unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendMutState<T> {
|
||||
impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendMutState<T> {
|
||||
type Item = NonSendMut<'w, T>;
|
||||
|
||||
#[inline]
|
||||
|
@ -929,7 +937,8 @@ impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendMutState<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The [`SystemParamState`] of `Option<NonSendMut<T>>`.
|
||||
/// The [`SystemParamState`] of [`Option<NonSendMut<T>>`].
|
||||
/// See: [`NonSendMut<T>`]
|
||||
pub struct OptionNonSendMutState<T>(NonSendMutState<T>);
|
||||
|
||||
impl<'a, T: 'static> SystemParam for Option<NonSendMut<'a, T>> {
|
||||
|
@ -946,7 +955,7 @@ unsafe impl<T: 'static> SystemParamState for OptionNonSendMutState<T> {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for OptionNonSendMutState<T> {
|
||||
impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendMutState<T> {
|
||||
type Item = Option<NonSendMut<'w, T>>;
|
||||
|
||||
#[inline]
|
||||
|
@ -991,7 +1000,7 @@ unsafe impl SystemParamState for ArchetypesState {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w> SystemParamFetch<'s, 'w> for ArchetypesState {
|
||||
impl<'w, 's> SystemParamFetch<'w, 's> for ArchetypesState {
|
||||
type Item = &'w Archetypes;
|
||||
|
||||
#[inline]
|
||||
|
@ -1026,7 +1035,7 @@ unsafe impl SystemParamState for ComponentsState {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w> SystemParamFetch<'s, 'w> for ComponentsState {
|
||||
impl<'w, 's> SystemParamFetch<'w, 's> for ComponentsState {
|
||||
type Item = &'w Components;
|
||||
|
||||
#[inline]
|
||||
|
@ -1061,7 +1070,7 @@ unsafe impl SystemParamState for EntitiesState {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w> SystemParamFetch<'s, 'w> for EntitiesState {
|
||||
impl<'w, 's> SystemParamFetch<'w, 's> for EntitiesState {
|
||||
type Item = &'w Entities;
|
||||
|
||||
#[inline]
|
||||
|
@ -1096,7 +1105,7 @@ unsafe impl SystemParamState for BundlesState {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w> SystemParamFetch<'s, 'w> for BundlesState {
|
||||
impl<'w, 's> SystemParamFetch<'w, 's> for BundlesState {
|
||||
type Item = &'w Bundles;
|
||||
|
||||
#[inline]
|
||||
|
@ -1123,7 +1132,7 @@ impl SystemParam for SystemChangeTick {
|
|||
type Fetch = SystemChangeTickState;
|
||||
}
|
||||
|
||||
/// The [`SystemParamState`] of [`SystemChangeTickState`].
|
||||
/// The [`SystemParamState`] of [`SystemChangeTick`].
|
||||
pub struct SystemChangeTickState {}
|
||||
|
||||
unsafe impl SystemParamState for SystemChangeTickState {
|
||||
|
@ -1136,7 +1145,7 @@ unsafe impl SystemParamState for SystemChangeTickState {
|
|||
fn default_config() {}
|
||||
}
|
||||
|
||||
impl<'s, 'w> SystemParamFetch<'s, 'w> for SystemChangeTickState {
|
||||
impl<'w, 's> SystemParamFetch<'w, 's> for SystemChangeTickState {
|
||||
type Item = SystemChangeTick;
|
||||
|
||||
unsafe fn get_param(
|
||||
|
@ -1163,7 +1172,7 @@ macro_rules! impl_system_param_tuple {
|
|||
|
||||
#[allow(unused_variables)]
|
||||
#[allow(non_snake_case)]
|
||||
impl<'s, 'w, $($param: SystemParamFetch<'s, 'w>),*> SystemParamFetch<'s, 'w> for ($($param,)*) {
|
||||
impl<'w, 's, $($param: SystemParamFetch<'w, 's>),*> SystemParamFetch<'w, 's> for ($($param,)*) {
|
||||
type Item = ($($param::Item,)*);
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
archetype::{Archetype, ArchetypeId, Archetypes, ComponentStatus},
|
||||
archetype::{Archetype, ArchetypeId, Archetypes},
|
||||
bundle::{Bundle, BundleInfo},
|
||||
change_detection::Ticks,
|
||||
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
|
||||
|
@ -189,112 +189,29 @@ impl<'w> EntityMut<'w> {
|
|||
})
|
||||
}
|
||||
|
||||
/// # Safety:
|
||||
/// Partially moves the entity to a new archetype based on the provided bundle info
|
||||
/// You must handle the other part of moving the entity yourself
|
||||
unsafe fn get_insert_bundle_info<'a>(
|
||||
entities: &mut Entities,
|
||||
archetypes: &'a mut Archetypes,
|
||||
components: &mut Components,
|
||||
storages: &mut Storages,
|
||||
bundle_info: &BundleInfo,
|
||||
current_location: EntityLocation,
|
||||
entity: Entity,
|
||||
) -> (&'a Archetype, &'a Vec<ComponentStatus>, EntityLocation) {
|
||||
// SAFE: component ids in `bundle_info` and self.location are valid
|
||||
let new_archetype_id = add_bundle_to_archetype(
|
||||
archetypes,
|
||||
storages,
|
||||
components,
|
||||
current_location.archetype_id,
|
||||
bundle_info,
|
||||
);
|
||||
if new_archetype_id == current_location.archetype_id {
|
||||
let archetype = &archetypes[current_location.archetype_id];
|
||||
let edge = archetype.edges().get_add_bundle(bundle_info.id).unwrap();
|
||||
(archetype, &edge.bundle_status, current_location)
|
||||
} else {
|
||||
let (old_table_row, old_table_id) = {
|
||||
let old_archetype = &mut archetypes[current_location.archetype_id];
|
||||
let result = old_archetype.swap_remove(current_location.index);
|
||||
if let Some(swapped_entity) = result.swapped_entity {
|
||||
entities.meta[swapped_entity.id as usize].location = current_location;
|
||||
}
|
||||
(result.table_row, old_archetype.table_id())
|
||||
};
|
||||
|
||||
let new_table_id = archetypes[new_archetype_id].table_id();
|
||||
|
||||
let new_location = if old_table_id == new_table_id {
|
||||
archetypes[new_archetype_id].allocate(entity, old_table_row)
|
||||
} else {
|
||||
let (old_table, new_table) = storages.tables.get_2_mut(old_table_id, new_table_id);
|
||||
// PERF: store "non bundle" components in edge, then just move those to avoid
|
||||
// redundant copies
|
||||
let move_result = old_table.move_to_superset_unchecked(old_table_row, new_table);
|
||||
|
||||
let new_location =
|
||||
archetypes[new_archetype_id].allocate(entity, move_result.new_row);
|
||||
// if an entity was moved into this entity's table spot, update its table row
|
||||
if let Some(swapped_entity) = move_result.swapped_entity {
|
||||
let swapped_location = entities.get(swapped_entity).unwrap();
|
||||
archetypes[swapped_location.archetype_id]
|
||||
.set_entity_table_row(swapped_location.index, old_table_row);
|
||||
}
|
||||
new_location
|
||||
};
|
||||
|
||||
entities.meta[entity.id as usize].location = new_location;
|
||||
let (old_archetype, new_archetype) =
|
||||
archetypes.get_2_mut(current_location.archetype_id, new_archetype_id);
|
||||
let edge = old_archetype
|
||||
.edges()
|
||||
.get_add_bundle(bundle_info.id)
|
||||
.unwrap();
|
||||
(&*new_archetype, &edge.bundle_status, new_location)
|
||||
|
||||
// Sparse set components are intentionally ignored here. They don't need to move
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move relevant methods to World (add/remove bundle)
|
||||
pub fn insert_bundle<T: Bundle>(&mut self, bundle: T) -> &mut Self {
|
||||
let change_tick = self.world.change_tick();
|
||||
let bundle_info = self
|
||||
.world
|
||||
.bundles
|
||||
.init_info::<T>(&mut self.world.components);
|
||||
|
||||
let (archetype, bundle_status, new_location) = unsafe {
|
||||
Self::get_insert_bundle_info(
|
||||
&mut self.world.entities,
|
||||
&mut self.world.archetypes,
|
||||
&mut self.world.components,
|
||||
&mut self.world.storages,
|
||||
bundle_info,
|
||||
self.location,
|
||||
self.entity,
|
||||
)
|
||||
};
|
||||
self.location = new_location;
|
||||
|
||||
let table = &mut self.world.storages.tables[archetype.table_id()];
|
||||
let table_row = archetype.entity_table_row(new_location.index);
|
||||
// SAFE: table row is valid
|
||||
let mut bundle_inserter = bundle_info.get_bundle_inserter(
|
||||
&mut self.world.entities,
|
||||
&mut self.world.archetypes,
|
||||
&mut self.world.components,
|
||||
&mut self.world.storages,
|
||||
self.location.archetype_id,
|
||||
change_tick,
|
||||
);
|
||||
// SAFE: location matches current entity. `T` matches `bundle_info`
|
||||
unsafe {
|
||||
bundle_info.write_components(
|
||||
&mut self.world.storages.sparse_sets,
|
||||
self.entity,
|
||||
table,
|
||||
table_row,
|
||||
bundle_status,
|
||||
bundle,
|
||||
change_tick,
|
||||
)
|
||||
};
|
||||
self.location = bundle_inserter.insert(self.entity, self.location.index, bundle);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: move to BundleInfo
|
||||
pub fn remove_bundle<T: Bundle>(&mut self) -> Option<T> {
|
||||
let archetypes = &mut self.world.archetypes;
|
||||
let storages = &mut self.world.storages;
|
||||
|
@ -340,6 +257,42 @@ impl<'w> EntityMut<'w> {
|
|||
})
|
||||
};
|
||||
|
||||
unsafe {
|
||||
Self::move_entity_from_remove::<false>(
|
||||
entity,
|
||||
&mut self.location,
|
||||
old_location.archetype_id,
|
||||
old_location,
|
||||
entities,
|
||||
archetypes,
|
||||
storages,
|
||||
new_archetype_id,
|
||||
);
|
||||
}
|
||||
|
||||
Some(result)
|
||||
}
|
||||
|
||||
/// Safety: `new_archetype_id` must have the same or a subset of the components
|
||||
/// in `old_archetype_id`. Probably more safety stuff too, audit a call to
|
||||
/// this fn as if the code here was written inline
|
||||
///
|
||||
/// when DROP is true removed components will be dropped otherwise they will be forgotten
|
||||
///
|
||||
// We use a const generic here so that we are less reliant on
|
||||
// inlining for rustc to optimize out the `match DROP`
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
unsafe fn move_entity_from_remove<const DROP: bool>(
|
||||
entity: Entity,
|
||||
self_location: &mut EntityLocation,
|
||||
old_archetype_id: ArchetypeId,
|
||||
old_location: EntityLocation,
|
||||
entities: &mut Entities,
|
||||
archetypes: &mut Archetypes,
|
||||
storages: &mut Storages,
|
||||
new_archetype_id: ArchetypeId,
|
||||
) {
|
||||
let old_archetype = &mut archetypes[old_archetype_id];
|
||||
let remove_result = old_archetype.swap_remove(old_location.index);
|
||||
if let Some(swapped_entity) = remove_result.swapped_entity {
|
||||
entities.meta[swapped_entity.id as usize].location = old_location;
|
||||
|
@ -349,36 +302,37 @@ impl<'w> EntityMut<'w> {
|
|||
let new_archetype = &mut archetypes[new_archetype_id];
|
||||
|
||||
let new_location = if old_table_id == new_archetype.table_id() {
|
||||
unsafe { new_archetype.allocate(entity, old_table_row) }
|
||||
new_archetype.allocate(entity, old_table_row)
|
||||
} else {
|
||||
let (old_table, new_table) = storages
|
||||
.tables
|
||||
.get_2_mut(old_table_id, new_archetype.table_id());
|
||||
|
||||
// SAFE: table_row exists. All "missing" components have been extracted into the bundle
|
||||
// above and the caller takes ownership
|
||||
let move_result =
|
||||
unsafe { old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table) };
|
||||
// SAFE: old_table_row exists
|
||||
let move_result = if DROP {
|
||||
old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table)
|
||||
} else {
|
||||
old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table)
|
||||
};
|
||||
|
||||
// SAFE: new_table_row is a valid position in new_archetype's table
|
||||
let new_location = unsafe { new_archetype.allocate(entity, move_result.new_row) };
|
||||
// SAFE: move_result.new_row is a valid position in new_archetype's table
|
||||
let new_location = new_archetype.allocate(entity, move_result.new_row);
|
||||
|
||||
// if an entity was moved into this entity's table spot, update its table row
|
||||
if let Some(swapped_entity) = move_result.swapped_entity {
|
||||
let swapped_location = entities.get(swapped_entity).unwrap();
|
||||
let archetype = &mut archetypes[swapped_location.archetype_id];
|
||||
archetype.set_entity_table_row(swapped_location.index, old_table_row);
|
||||
archetypes[swapped_location.archetype_id]
|
||||
.set_entity_table_row(swapped_location.index, old_table_row);
|
||||
}
|
||||
|
||||
new_location
|
||||
};
|
||||
|
||||
self.location = new_location;
|
||||
entities.meta[self.entity.id as usize].location = new_location;
|
||||
|
||||
Some(result)
|
||||
*self_location = new_location;
|
||||
entities.meta[entity.id as usize].location = new_location;
|
||||
}
|
||||
|
||||
// TODO: move to BundleInfo
|
||||
/// Remove any components in the bundle that the entity has.
|
||||
pub fn remove_bundle_intersection<T: Bundle>(&mut self) {
|
||||
let archetypes = &mut self.world.archetypes;
|
||||
|
@ -425,40 +379,18 @@ impl<'w> EntityMut<'w> {
|
|||
}
|
||||
}
|
||||
|
||||
let remove_result = old_archetype.swap_remove(old_location.index);
|
||||
if let Some(swapped_entity) = remove_result.swapped_entity {
|
||||
entities.meta[swapped_entity.id as usize].location = old_location;
|
||||
unsafe {
|
||||
Self::move_entity_from_remove::<true>(
|
||||
entity,
|
||||
&mut self.location,
|
||||
old_location.archetype_id,
|
||||
old_location,
|
||||
entities,
|
||||
archetypes,
|
||||
storages,
|
||||
new_archetype_id,
|
||||
)
|
||||
}
|
||||
let old_table_row = remove_result.table_row;
|
||||
let old_table_id = old_archetype.table_id();
|
||||
let new_archetype = &mut archetypes[new_archetype_id];
|
||||
|
||||
let new_location = if old_table_id == new_archetype.table_id() {
|
||||
unsafe { new_archetype.allocate(entity, old_table_row) }
|
||||
} else {
|
||||
let (old_table, new_table) = storages
|
||||
.tables
|
||||
.get_2_mut(old_table_id, new_archetype.table_id());
|
||||
|
||||
// SAFE: table_row exists
|
||||
let move_result =
|
||||
unsafe { old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table) };
|
||||
|
||||
// SAFE: new_table_row is a valid position in new_archetype's table
|
||||
let new_location = unsafe { new_archetype.allocate(entity, move_result.new_row) };
|
||||
|
||||
// if an entity was moved into this entity's table spot, update its table row
|
||||
if let Some(swapped_entity) = move_result.swapped_entity {
|
||||
let swapped_location = entities.get(swapped_entity).unwrap();
|
||||
archetypes[swapped_location.archetype_id]
|
||||
.set_entity_table_row(swapped_location.index, old_table_row);
|
||||
}
|
||||
|
||||
new_location
|
||||
};
|
||||
|
||||
self.location = new_location;
|
||||
entities.meta[self.entity.id as usize].location = new_location;
|
||||
}
|
||||
|
||||
pub fn insert<T: Component>(&mut self, value: T) -> &mut Self {
|
||||
|
@ -531,6 +463,7 @@ impl<'w> EntityMut<'w> {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: move to Storages?
|
||||
/// # Safety
|
||||
/// `entity_location` must be within bounds of the given archetype and `entity` must exist inside
|
||||
/// the archetype
|
||||
|
@ -560,6 +493,7 @@ unsafe fn get_component(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: move to Storages?
|
||||
/// # Safety
|
||||
/// Caller must ensure that `component_id` is valid
|
||||
#[inline]
|
||||
|
@ -590,6 +524,7 @@ unsafe fn get_component_and_ticks(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: move to Storages?
|
||||
/// Moves component data out of storage.
|
||||
///
|
||||
/// This function leaves the underlying memory unchanged, but the component behind
|
||||
|
@ -671,95 +606,6 @@ fn contains_component_with_id(
|
|||
world.archetypes[location.archetype_id].contains(component_id)
|
||||
}
|
||||
|
||||
/// Adds a bundle to the given archetype and returns the resulting archetype. This could be the same
|
||||
/// [ArchetypeId], in the event that adding the given bundle does not result in an Archetype change.
|
||||
/// Results are cached in the Archetype Graph to avoid redundant work.
|
||||
///
|
||||
/// # Safety
|
||||
/// components in `bundle_info` must exist
|
||||
pub(crate) unsafe fn add_bundle_to_archetype(
|
||||
archetypes: &mut Archetypes,
|
||||
storages: &mut Storages,
|
||||
components: &mut Components,
|
||||
archetype_id: ArchetypeId,
|
||||
bundle_info: &BundleInfo,
|
||||
) -> ArchetypeId {
|
||||
if let Some(add_bundle) = archetypes[archetype_id]
|
||||
.edges()
|
||||
.get_add_bundle(bundle_info.id)
|
||||
{
|
||||
return add_bundle.archetype_id;
|
||||
}
|
||||
let mut new_table_components = Vec::new();
|
||||
let mut new_sparse_set_components = Vec::new();
|
||||
let mut bundle_status = Vec::with_capacity(bundle_info.component_ids.len());
|
||||
|
||||
let current_archetype = &mut archetypes[archetype_id];
|
||||
for component_id in bundle_info.component_ids.iter().cloned() {
|
||||
if current_archetype.contains(component_id) {
|
||||
bundle_status.push(ComponentStatus::Mutated);
|
||||
} else {
|
||||
bundle_status.push(ComponentStatus::Added);
|
||||
let component_info = 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if new_table_components.is_empty() && new_sparse_set_components.is_empty() {
|
||||
let edges = current_archetype.edges_mut();
|
||||
// the archetype does not change when we add this bundle
|
||||
edges.set_add_bundle(bundle_info.id, archetype_id, bundle_status);
|
||||
archetype_id
|
||||
} else {
|
||||
let table_id;
|
||||
let table_components;
|
||||
let sparse_set_components;
|
||||
// the archetype changes when we add this bundle. prepare the new archetype and storages
|
||||
{
|
||||
let current_archetype = &archetypes[archetype_id];
|
||||
table_components = if new_table_components.is_empty() {
|
||||
// if there are no new table components, we can keep using this table
|
||||
table_id = current_archetype.table_id();
|
||||
current_archetype.table_components().to_vec()
|
||||
} else {
|
||||
new_table_components.extend(current_archetype.table_components());
|
||||
// sort to ignore order while hashing
|
||||
new_table_components.sort();
|
||||
// SAFE: all component ids in `new_table_components` exist
|
||||
table_id = storages
|
||||
.tables
|
||||
.get_id_or_insert(&new_table_components, components);
|
||||
|
||||
new_table_components
|
||||
};
|
||||
|
||||
sparse_set_components = if new_sparse_set_components.is_empty() {
|
||||
current_archetype.sparse_set_components().to_vec()
|
||||
} else {
|
||||
new_sparse_set_components.extend(current_archetype.sparse_set_components());
|
||||
// sort to ignore order while hashing
|
||||
new_sparse_set_components.sort();
|
||||
new_sparse_set_components
|
||||
};
|
||||
};
|
||||
let new_archetype_id =
|
||||
archetypes.get_id_or_insert(table_id, table_components, sparse_set_components);
|
||||
// add an edge from the old archetype to the new archetype
|
||||
archetypes[archetype_id].edges_mut().set_add_bundle(
|
||||
bundle_info.id,
|
||||
new_archetype_id,
|
||||
bundle_status,
|
||||
);
|
||||
new_archetype_id
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a bundle from the given archetype and returns the resulting archetype (or None if the
|
||||
/// removal was invalid). in the event that adding the given bundle does not result in an Archetype
|
||||
/// change. Results are cached in the Archetype Graph to avoid redundant work.
|
||||
|
@ -814,7 +660,7 @@ unsafe fn remove_bundle_from_archetype(
|
|||
// graph
|
||||
current_archetype
|
||||
.edges_mut()
|
||||
.set_remove_bundle(bundle_info.id, None);
|
||||
.insert_remove_bundle(bundle_info.id, None);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
@ -853,11 +699,11 @@ unsafe fn remove_bundle_from_archetype(
|
|||
if intersection {
|
||||
current_archetype
|
||||
.edges_mut()
|
||||
.set_remove_bundle_intersection(bundle_info.id, result);
|
||||
.insert_remove_bundle_intersection(bundle_info.id, result);
|
||||
} else {
|
||||
current_archetype
|
||||
.edges_mut()
|
||||
.set_remove_bundle(bundle_info.id, result);
|
||||
.insert_remove_bundle(bundle_info.id, result);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
mod entity_ref;
|
||||
mod pointer;
|
||||
mod spawn_batch;
|
||||
mod world_cell;
|
||||
|
||||
pub use crate::change_detection::Mut;
|
||||
pub use entity_ref::*;
|
||||
pub use pointer::*;
|
||||
pub use spawn_batch::*;
|
||||
pub use world_cell::*;
|
||||
|
||||
use crate::{
|
||||
archetype::{ArchetypeComponentId, ArchetypeComponentInfo, ArchetypeId, Archetypes},
|
||||
bundle::{Bundle, Bundles},
|
||||
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
|
||||
change_detection::Ticks,
|
||||
component::{
|
||||
Component, ComponentDescriptor, ComponentId, ComponentTicks, Components, ComponentsError,
|
||||
|
@ -221,8 +220,13 @@ impl World {
|
|||
self.get_entity_mut(entity).expect("Entity does not exist")
|
||||
}
|
||||
|
||||
/// Returns an EntityMut for an existing entity or creates one if it doesn't exist.
|
||||
/// This will return `None` if the entity exists with a different generation.
|
||||
/// Returns an [EntityMut] for the given `entity` (if it exists) or spawns one if it doesn't exist.
|
||||
/// This will return [None] if the `entity` exists with a different generation.
|
||||
///
|
||||
/// # Note
|
||||
/// Spawning a specific `entity` value is rarely the right choice. Most apps should favor [`World::spawn`].
|
||||
/// This method should generally only be used for sharing entities across apps, and only when they have a
|
||||
/// scheme worked out to share an ID space (which doesn't happen by default).
|
||||
#[inline]
|
||||
pub fn get_or_spawn(&mut self, entity: Entity) -> Option<EntityMut> {
|
||||
self.flush();
|
||||
|
@ -379,6 +383,7 @@ impl World {
|
|||
/// .id();
|
||||
/// let position = world.get::<Position>(entity).unwrap();
|
||||
/// assert_eq!(position.x, 0.0);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn get<T: Component>(&self, entity: Entity) -> Option<&T> {
|
||||
self.get_entity(entity)?.get()
|
||||
|
@ -400,6 +405,7 @@ impl World {
|
|||
/// .id();
|
||||
/// let mut position = world.get_mut::<Position>(entity).unwrap();
|
||||
/// position.x = 1.0;
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<Mut<T>> {
|
||||
self.get_entity_mut(entity)?.get_mut()
|
||||
|
@ -699,6 +705,127 @@ impl World {
|
|||
self.get_non_send_unchecked_mut_with_id(component_id)
|
||||
}
|
||||
|
||||
/// For a given batch of ([Entity], [Bundle]) pairs, either spawns each [Entity] with the given
|
||||
/// bundle (if the entity does not exist), or inserts the [Bundle] (if the entity already exists).
|
||||
/// This is faster than doing equivalent operations one-by-one.
|
||||
/// Returns [Ok] if all entities were successfully inserted into or spawned. Otherwise it returns an [Err]
|
||||
/// with a list of entities that could not be spawned or inserted into. A "spawn or insert" operation can
|
||||
/// only fail if an [Entity] is passed in with an "invalid generation" that conflicts with an existing [Entity].
|
||||
///
|
||||
/// # Note
|
||||
/// Spawning a specific `entity` value is rarely the right choice. Most apps should use [`World::spawn_batch`].
|
||||
/// This method should generally only be used for sharing entities across apps, and only when they have a scheme
|
||||
/// worked out to share an ID space (which doesn't happen by default).
|
||||
///
|
||||
/// ```
|
||||
/// use bevy_ecs::{entity::Entity, world::World};
|
||||
///
|
||||
/// let mut world = World::new();
|
||||
/// let e0 = world.spawn().id();
|
||||
/// let e1 = world.spawn().id();
|
||||
/// world.insert_or_spawn_batch(vec![
|
||||
/// (e0, ("a", 0.0)), // the first entity
|
||||
/// (e1, ("b", 1.0)), // the second entity
|
||||
/// ]);
|
||||
///
|
||||
/// assert_eq!(world.get::<f64>(e0), Some(&0.0));
|
||||
/// ```
|
||||
pub fn insert_or_spawn_batch<I, B>(&mut self, iter: I) -> Result<(), Vec<Entity>>
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::IntoIter: Iterator<Item = (Entity, B)>,
|
||||
B: Bundle,
|
||||
{
|
||||
self.flush();
|
||||
|
||||
let iter = iter.into_iter();
|
||||
let change_tick = *self.change_tick.get_mut();
|
||||
|
||||
let bundle_info = self.bundles.init_info::<B>(&mut self.components);
|
||||
enum SpawnOrInsert<'a, 'b> {
|
||||
Spawn(BundleSpawner<'a, 'b>),
|
||||
Insert(BundleInserter<'a, 'b>, ArchetypeId),
|
||||
}
|
||||
|
||||
impl<'a, 'b> SpawnOrInsert<'a, 'b> {
|
||||
fn entities(&mut self) -> &mut Entities {
|
||||
match self {
|
||||
SpawnOrInsert::Spawn(spawner) => spawner.entities,
|
||||
SpawnOrInsert::Insert(inserter, _) => inserter.entities,
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut spawn_or_insert = SpawnOrInsert::Spawn(bundle_info.get_bundle_spawner(
|
||||
&mut self.entities,
|
||||
&mut self.archetypes,
|
||||
&mut self.components,
|
||||
&mut self.storages,
|
||||
change_tick,
|
||||
));
|
||||
|
||||
let mut invalid_entities = Vec::new();
|
||||
for (entity, bundle) in iter {
|
||||
match spawn_or_insert
|
||||
.entities()
|
||||
.alloc_at_without_replacement(entity)
|
||||
{
|
||||
AllocAtWithoutReplacement::Exists(location) => {
|
||||
match spawn_or_insert {
|
||||
SpawnOrInsert::Insert(ref mut inserter, archetype)
|
||||
if location.archetype_id == archetype =>
|
||||
{
|
||||
// SAFE: `entity` is valid, `location` matches entity, bundle matches inserter
|
||||
unsafe { inserter.insert(entity, location.index, bundle) };
|
||||
}
|
||||
_ => {
|
||||
let mut inserter = bundle_info.get_bundle_inserter(
|
||||
&mut self.entities,
|
||||
&mut self.archetypes,
|
||||
&mut self.components,
|
||||
&mut self.storages,
|
||||
location.archetype_id,
|
||||
change_tick,
|
||||
);
|
||||
// SAFE: `entity` is valid, `location` matches entity, bundle matches inserter
|
||||
unsafe { inserter.insert(entity, location.index, bundle) };
|
||||
spawn_or_insert =
|
||||
SpawnOrInsert::Insert(inserter, location.archetype_id);
|
||||
}
|
||||
};
|
||||
}
|
||||
AllocAtWithoutReplacement::DidNotExist => {
|
||||
match spawn_or_insert {
|
||||
SpawnOrInsert::Spawn(ref mut spawner) => {
|
||||
// SAFE: `entity` is allocated (but non existent), bundle matches inserter
|
||||
unsafe { spawner.spawn_non_existent(entity, bundle) };
|
||||
}
|
||||
_ => {
|
||||
let mut spawner = bundle_info.get_bundle_spawner(
|
||||
&mut self.entities,
|
||||
&mut self.archetypes,
|
||||
&mut self.components,
|
||||
&mut self.storages,
|
||||
change_tick,
|
||||
);
|
||||
// SAFE: `entity` is valid, `location` matches entity, bundle matches inserter
|
||||
unsafe { spawner.spawn_non_existent(entity, bundle) };
|
||||
spawn_or_insert = SpawnOrInsert::Spawn(spawner);
|
||||
}
|
||||
};
|
||||
}
|
||||
AllocAtWithoutReplacement::ExistsWithWrongGeneration => {
|
||||
invalid_entities.push(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if invalid_entities.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(invalid_entities)
|
||||
}
|
||||
}
|
||||
|
||||
/// Temporarily removes the requested resource from this [World], then re-adds it before
|
||||
/// returning. This enables safe mutable access to a resource while still providing mutable
|
||||
/// world access
|
||||
|
@ -838,7 +965,7 @@ impl World {
|
|||
let resource_archetype = self
|
||||
.archetypes
|
||||
.archetypes
|
||||
.get_unchecked_mut(ArchetypeId::resource().index());
|
||||
.get_unchecked_mut(ArchetypeId::RESOURCE.index());
|
||||
let resource_archetype_components = &mut resource_archetype.components;
|
||||
let archetype_component_count = &mut self.archetypes.archetype_component_count;
|
||||
let components = &self.components;
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
pub use crate::change_detection::Mut;
|
|
@ -1,9 +1,7 @@
|
|||
use crate::{
|
||||
archetype::{Archetype, ArchetypeId, ComponentStatus},
|
||||
bundle::{Bundle, BundleInfo},
|
||||
entity::{Entities, Entity},
|
||||
storage::{SparseSets, Table},
|
||||
world::{add_bundle_to_archetype, World},
|
||||
bundle::{Bundle, BundleSpawner},
|
||||
entity::Entity,
|
||||
world::World,
|
||||
};
|
||||
|
||||
pub struct SpawnBatchIter<'w, I>
|
||||
|
@ -12,13 +10,7 @@ where
|
|||
I::Item: Bundle,
|
||||
{
|
||||
inner: I,
|
||||
entities: &'w mut Entities,
|
||||
archetype: &'w mut Archetype,
|
||||
table: &'w mut Table,
|
||||
sparse_sets: &'w mut SparseSets,
|
||||
bundle_info: &'w BundleInfo,
|
||||
bundle_status: &'w [ComponentStatus],
|
||||
change_tick: u32,
|
||||
spawner: BundleSpawner<'w, 'w>,
|
||||
}
|
||||
|
||||
impl<'w, I> SpawnBatchIter<'w, I>
|
||||
|
@ -33,40 +25,22 @@ where
|
|||
world.flush();
|
||||
|
||||
let (lower, upper) = iter.size_hint();
|
||||
let length = upper.unwrap_or(lower);
|
||||
|
||||
let bundle_info = world.bundles.init_info::<I::Item>(&mut world.components);
|
||||
|
||||
let length = upper.unwrap_or(lower);
|
||||
// SAFE: empty archetype exists and bundle components were initialized above
|
||||
let archetype_id = unsafe {
|
||||
add_bundle_to_archetype(
|
||||
&mut world.archetypes,
|
||||
&mut world.storages,
|
||||
&mut world.components,
|
||||
ArchetypeId::empty(),
|
||||
bundle_info,
|
||||
)
|
||||
};
|
||||
let (empty_archetype, archetype) = world
|
||||
.archetypes
|
||||
.get_2_mut(ArchetypeId::empty(), archetype_id);
|
||||
let table = &mut world.storages.tables[archetype.table_id()];
|
||||
archetype.reserve(length);
|
||||
table.reserve(length);
|
||||
world.entities.reserve(length as u32);
|
||||
let edge = empty_archetype
|
||||
.edges()
|
||||
.get_add_bundle(bundle_info.id())
|
||||
.unwrap();
|
||||
let mut spawner = bundle_info.get_bundle_spawner(
|
||||
&mut world.entities,
|
||||
&mut world.archetypes,
|
||||
&mut world.components,
|
||||
&mut world.storages,
|
||||
*world.change_tick.get_mut(),
|
||||
);
|
||||
spawner.reserve_storage(length);
|
||||
|
||||
Self {
|
||||
inner: iter,
|
||||
entities: &mut world.entities,
|
||||
archetype,
|
||||
table,
|
||||
sparse_sets: &mut world.storages.sparse_sets,
|
||||
bundle_info,
|
||||
change_tick: *world.change_tick.get_mut(),
|
||||
bundle_status: &edge.bundle_status,
|
||||
spawner,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,24 +64,8 @@ where
|
|||
|
||||
fn next(&mut self) -> Option<Entity> {
|
||||
let bundle = self.inner.next()?;
|
||||
let entity = self.entities.alloc();
|
||||
// SAFE: component values are immediately written to relevant storages (which have been
|
||||
// allocated)
|
||||
unsafe {
|
||||
let table_row = self.table.allocate(entity);
|
||||
let location = self.archetype.allocate(entity, table_row);
|
||||
self.bundle_info.write_components(
|
||||
self.sparse_sets,
|
||||
entity,
|
||||
self.table,
|
||||
table_row,
|
||||
self.bundle_status,
|
||||
bundle,
|
||||
self.change_tick,
|
||||
);
|
||||
self.entities.meta[entity.id as usize].location = location;
|
||||
}
|
||||
Some(entity)
|
||||
// SAFE: bundle matches spawner type
|
||||
unsafe { Some(self.spawner.spawn(bundle)) }
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
|
|
|
@ -296,7 +296,7 @@ mod tests {
|
|||
.components
|
||||
.get_resource_id(TypeId::of::<u32>())
|
||||
.unwrap();
|
||||
let resource_archetype = world.archetypes.get(ArchetypeId::resource()).unwrap();
|
||||
let resource_archetype = world.archetypes.get(ArchetypeId::RESOURCE).unwrap();
|
||||
let u32_archetype_component_id = resource_archetype
|
||||
.get_archetype_component_id(u32_component_id)
|
||||
.unwrap();
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
name = "bevy_render"
|
||||
version = "0.5.0"
|
||||
edition = "2018"
|
||||
authors = [
|
||||
"Bevy Contributors <bevyengine@gmail.com>",
|
||||
"Carter Anderson <mcanders1@gmail.com>",
|
||||
]
|
||||
description = "Provides rendering functionality for Bevy Engine"
|
||||
homepage = "https://bevyengine.org"
|
||||
repository = "https://github.com/bevyengine/bevy"
|
||||
|
|
|
@ -4,9 +4,10 @@ use bevy_ecs::{
|
|||
component::Component,
|
||||
entity::Entity,
|
||||
event::EventReader,
|
||||
prelude::QueryState,
|
||||
query::Added,
|
||||
reflect::ReflectComponent,
|
||||
system::{Query, QuerySet, Res},
|
||||
system::{QuerySet, Res},
|
||||
};
|
||||
use bevy_math::{Mat4, Vec2, Vec3};
|
||||
use bevy_reflect::{Reflect, ReflectDeserialize};
|
||||
|
@ -70,8 +71,8 @@ pub fn camera_system<T: CameraProjection + Component>(
|
|||
mut window_created_events: EventReader<WindowCreated>,
|
||||
windows: Res<Windows>,
|
||||
mut queries: QuerySet<(
|
||||
Query<(Entity, &mut Camera, &mut T)>,
|
||||
Query<Entity, Added<Camera>>,
|
||||
QueryState<(Entity, &mut Camera, &mut T)>,
|
||||
QueryState<Entity, Added<Camera>>,
|
||||
)>,
|
||||
) {
|
||||
let mut changed_window_ids = Vec::new();
|
||||
|
@ -99,7 +100,7 @@ pub fn camera_system<T: CameraProjection + Component>(
|
|||
for entity in &mut queries.q1().iter() {
|
||||
added_cameras.push(entity);
|
||||
}
|
||||
for (entity, mut camera, mut camera_projection) in queries.q0_mut().iter_mut() {
|
||||
for (entity, mut camera, mut camera_projection) in queries.q0().iter_mut() {
|
||||
if let Some(window) = windows.get(camera.window) {
|
||||
if changed_window_ids.contains(&window.id())
|
||||
|| added_cameras.contains(&entity)
|
||||
|
|
|
@ -10,6 +10,7 @@ use bevy_math::{Vec3, Vec4};
|
|||
use bevy_reflect::{Reflect, ReflectDeserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::{Add, AddAssign, Mul, MulAssign};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)]
|
||||
#[reflect(PartialEq, Serialize, Deserialize)]
|
||||
|
@ -1070,10 +1071,12 @@ impl Bytes for Color {
|
|||
|
||||
impl_render_resource_bytes!(Color);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum HexColorError {
|
||||
#[error("Unexpected length of hex string")]
|
||||
Length,
|
||||
Hex(hex::FromHexError),
|
||||
#[error("Error parsing hex value")]
|
||||
Hex(#[from] hex::FromHexError),
|
||||
}
|
||||
|
||||
fn decode_rgb(data: &[u8]) -> Result<Color, HexColorError> {
|
||||
|
|
|
@ -164,7 +164,7 @@ pub enum DrawError {
|
|||
}
|
||||
|
||||
#[derive(SystemParam)]
|
||||
pub struct DrawContext<'s, 'w> {
|
||||
pub struct DrawContext<'w, 's> {
|
||||
pub pipelines: ResMut<'w, Assets<PipelineDescriptor>>,
|
||||
pub shaders: ResMut<'w, Assets<Shader>>,
|
||||
pub asset_render_resource_bindings: ResMut<'w, AssetRenderResourceBindings>,
|
||||
|
@ -177,7 +177,7 @@ pub struct DrawContext<'s, 'w> {
|
|||
marker: PhantomData<&'s usize>,
|
||||
}
|
||||
|
||||
impl<'s, 'w> DrawContext<'s, 'w> {
|
||||
impl<'w, 's> DrawContext<'w, 's> {
|
||||
pub fn get_uniform_buffer<T: RenderResource>(
|
||||
&mut self,
|
||||
render_resource: &T,
|
||||
|
|
|
@ -14,7 +14,7 @@ pub mod wireframe;
|
|||
|
||||
use bevy_ecs::{
|
||||
schedule::{ParallelSystemDescriptorCoercion, SystemStage},
|
||||
system::{IntoExclusiveSystem, IntoSystem, Res},
|
||||
system::{IntoExclusiveSystem, Res},
|
||||
};
|
||||
use bevy_transform::TransformSystem;
|
||||
use bevy_utils::tracing::warn;
|
||||
|
@ -174,58 +174,38 @@ impl Plugin for RenderPlugin {
|
|||
.init_resource::<RenderResourceBindings>()
|
||||
.init_resource::<AssetRenderResourceBindings>()
|
||||
.init_resource::<ActiveCameras>()
|
||||
.add_startup_system_to_stage(
|
||||
StartupStage::PreStartup,
|
||||
check_for_render_resource_context.system(),
|
||||
)
|
||||
.add_system_to_stage(CoreStage::PreUpdate, draw::clear_draw_system.system())
|
||||
.add_startup_system_to_stage(StartupStage::PreStartup, check_for_render_resource_context)
|
||||
.add_system_to_stage(CoreStage::PreUpdate, draw::clear_draw_system)
|
||||
.add_system_to_stage(CoreStage::PostUpdate, camera::active_cameras_system)
|
||||
.add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
camera::active_cameras_system.system(),
|
||||
camera::camera_system::<OrthographicProjection>.before(RenderSystem::VisibleEntities),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
camera::camera_system::<OrthographicProjection>
|
||||
.system()
|
||||
.before(RenderSystem::VisibleEntities),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
camera::camera_system::<PerspectiveProjection>
|
||||
.system()
|
||||
.before(RenderSystem::VisibleEntities),
|
||||
camera::camera_system::<PerspectiveProjection>.before(RenderSystem::VisibleEntities),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
camera::visible_entities_system
|
||||
.system()
|
||||
.label(RenderSystem::VisibleEntities)
|
||||
.after(TransformSystem::TransformPropagate),
|
||||
)
|
||||
.add_system_to_stage(RenderStage::RenderResource, shader::shader_update_system)
|
||||
.add_system_to_stage(
|
||||
RenderStage::RenderResource,
|
||||
shader::shader_update_system.system(),
|
||||
mesh::mesh_resource_provider_system,
|
||||
)
|
||||
.add_system_to_stage(
|
||||
RenderStage::RenderResource,
|
||||
mesh::mesh_resource_provider_system.system(),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
RenderStage::RenderResource,
|
||||
Texture::texture_resource_system.system(),
|
||||
Texture::texture_resource_system,
|
||||
)
|
||||
.add_system_to_stage(
|
||||
RenderStage::RenderGraphSystems,
|
||||
render_graph::render_graph_schedule_executor_system.exclusive_system(),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
RenderStage::Draw,
|
||||
pipeline::draw_render_pipelines_system.system(),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
RenderStage::PostRender,
|
||||
shader::clear_shader_defs_system.system(),
|
||||
);
|
||||
.add_system_to_stage(RenderStage::Draw, pipeline::draw_render_pipelines_system)
|
||||
.add_system_to_stage(RenderStage::PostRender, shader::clear_shader_defs_system);
|
||||
|
||||
if let Some(ref config) = self.base_render_graph_config {
|
||||
crate::base::add_base_graph(config, &mut app.world);
|
||||
|
|
|
@ -9,8 +9,9 @@ use bevy_core::cast_slice;
|
|||
use bevy_ecs::{
|
||||
entity::Entity,
|
||||
event::EventReader,
|
||||
prelude::QueryState,
|
||||
query::{Changed, With},
|
||||
system::{Local, Query, QuerySet, Res},
|
||||
system::{Local, QuerySet, Res},
|
||||
world::Mut,
|
||||
};
|
||||
use bevy_math::*;
|
||||
|
@ -512,8 +513,8 @@ pub fn mesh_resource_provider_system(
|
|||
meshes: Res<Assets<Mesh>>,
|
||||
mut mesh_events: EventReader<AssetEvent<Mesh>>,
|
||||
mut queries: QuerySet<(
|
||||
Query<&mut RenderPipelines, With<Handle<Mesh>>>,
|
||||
Query<(Entity, &Handle<Mesh>, &mut RenderPipelines), Changed<Handle<Mesh>>>,
|
||||
QueryState<&mut RenderPipelines, With<Handle<Mesh>>>,
|
||||
QueryState<(Entity, &Handle<Mesh>, &mut RenderPipelines), Changed<Handle<Mesh>>>,
|
||||
)>,
|
||||
) {
|
||||
let mut changed_meshes = HashSet::default();
|
||||
|
@ -573,7 +574,7 @@ pub fn mesh_resource_provider_system(
|
|||
|
||||
if let Some(mesh_entities) = state.mesh_entities.get_mut(changed_mesh_handle) {
|
||||
for entity in mesh_entities.entities.iter() {
|
||||
if let Ok(render_pipelines) = queries.q0_mut().get_mut(*entity) {
|
||||
if let Ok(render_pipelines) = queries.q0().get_mut(*entity) {
|
||||
update_entity_mesh(
|
||||
render_resource_context,
|
||||
mesh,
|
||||
|
@ -587,7 +588,7 @@ pub fn mesh_resource_provider_system(
|
|||
}
|
||||
|
||||
// handover buffers to pipeline
|
||||
for (entity, handle, render_pipelines) in queries.q1_mut().iter_mut() {
|
||||
for (entity, handle, render_pipelines) in queries.q1().iter_mut() {
|
||||
let mesh_entities = state
|
||||
.mesh_entities
|
||||
.entry(handle.clone_weak())
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
};
|
||||
use bevy_core::bytes_of;
|
||||
use bevy_ecs::{
|
||||
system::{BoxedSystem, IntoSystem, Local, Query, Res, ResMut},
|
||||
system::{BoxedSystem, ConfigurableSystem, Local, Query, Res, ResMut},
|
||||
world::World,
|
||||
};
|
||||
use bevy_transform::prelude::*;
|
||||
|
@ -46,7 +46,7 @@ impl Node for CameraNode {
|
|||
|
||||
impl SystemNode for CameraNode {
|
||||
fn get_system(&self) -> BoxedSystem {
|
||||
let system = camera_node_system.system().config(|config| {
|
||||
let system = camera_node_system.config(|config| {
|
||||
config.0 = Some(CameraNodeState {
|
||||
camera_name: self.camera_name.clone(),
|
||||
command_queue: self.command_queue.clone(),
|
||||
|
|
|
@ -13,8 +13,9 @@ use bevy_app::EventReader;
|
|||
use bevy_asset::{Asset, AssetEvent, Assets, Handle, HandleId};
|
||||
use bevy_ecs::{
|
||||
entity::Entity,
|
||||
prelude::QueryState,
|
||||
query::{Changed, Or, With},
|
||||
system::{BoxedSystem, IntoSystem, Local, Query, QuerySet, RemovedComponents, Res, ResMut},
|
||||
system::{BoxedSystem, ConfigurableSystem, Local, QuerySet, RemovedComponents, Res, ResMut},
|
||||
world::World,
|
||||
};
|
||||
use bevy_utils::HashMap;
|
||||
|
@ -400,7 +401,7 @@ where
|
|||
T: renderer::RenderResources,
|
||||
{
|
||||
fn get_system(&self) -> BoxedSystem {
|
||||
let system = render_resources_node_system::<T>.system().config(|config| {
|
||||
let system = render_resources_node_system::<T>.config(|config| {
|
||||
config.0 = Some(RenderResourcesNodeState {
|
||||
command_queue: self.command_queue.clone(),
|
||||
uniform_buffer_arrays: UniformBufferArrays::<Entity, T>::default(),
|
||||
|
@ -435,8 +436,11 @@ fn render_resources_node_system<T: RenderResources>(
|
|||
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
||||
removed: RemovedComponents<T>,
|
||||
mut queries: QuerySet<(
|
||||
Query<(Entity, &T, &Visible, &mut RenderPipelines), Or<(Changed<T>, Changed<Visible>)>>,
|
||||
Query<(Entity, &T, &Visible, &mut RenderPipelines)>,
|
||||
QueryState<
|
||||
(Entity, &T, &Visible, &mut RenderPipelines),
|
||||
Or<(Changed<T>, Changed<Visible>)>,
|
||||
>,
|
||||
QueryState<(Entity, &T, &Visible, &mut RenderPipelines)>,
|
||||
)>,
|
||||
) {
|
||||
let state = state.deref_mut();
|
||||
|
@ -444,7 +448,7 @@ fn render_resources_node_system<T: RenderResources>(
|
|||
let render_resource_context = &**render_resource_context;
|
||||
uniform_buffer_arrays.begin_update();
|
||||
// initialize uniform buffer arrays using the first RenderResources
|
||||
if let Some((_, first, _, _)) = queries.q0_mut().iter_mut().next() {
|
||||
if let Some((_, first, _, _)) = queries.q0().iter_mut().next() {
|
||||
uniform_buffer_arrays.initialize(first, render_resource_context);
|
||||
}
|
||||
|
||||
|
@ -454,8 +458,7 @@ fn render_resources_node_system<T: RenderResources>(
|
|||
|
||||
// handle entities that were waiting for texture loads on the last update
|
||||
for entity in std::mem::take(&mut *entities_waiting_for_textures) {
|
||||
if let Ok((entity, uniforms, _visible, mut render_pipelines)) =
|
||||
queries.q1_mut().get_mut(entity)
|
||||
if let Ok((entity, uniforms, _visible, mut render_pipelines)) = queries.q1().get_mut(entity)
|
||||
{
|
||||
if !setup_uniform_texture_resources::<T>(
|
||||
uniforms,
|
||||
|
@ -467,7 +470,7 @@ fn render_resources_node_system<T: RenderResources>(
|
|||
}
|
||||
}
|
||||
|
||||
for (entity, uniforms, visible, mut render_pipelines) in queries.q0_mut().iter_mut() {
|
||||
for (entity, uniforms, visible, mut render_pipelines) in queries.q0().iter_mut() {
|
||||
if !visible.is_visible {
|
||||
continue;
|
||||
}
|
||||
|
@ -496,8 +499,7 @@ fn render_resources_node_system<T: RenderResources>(
|
|||
// if the buffer array was resized, write all entities to the new buffer, otherwise
|
||||
// only write changes
|
||||
if resized {
|
||||
for (entity, uniforms, visible, mut render_pipelines) in
|
||||
queries.q1_mut().iter_mut()
|
||||
for (entity, uniforms, visible, mut render_pipelines) in queries.q1().iter_mut()
|
||||
{
|
||||
if !visible.is_visible {
|
||||
continue;
|
||||
|
@ -513,8 +515,7 @@ fn render_resources_node_system<T: RenderResources>(
|
|||
);
|
||||
}
|
||||
} else {
|
||||
for (entity, uniforms, visible, mut render_pipelines) in
|
||||
queries.q0_mut().iter_mut()
|
||||
for (entity, uniforms, visible, mut render_pipelines) in queries.q0().iter_mut()
|
||||
{
|
||||
if !visible.is_visible {
|
||||
continue;
|
||||
|
@ -583,15 +584,13 @@ where
|
|||
T: renderer::RenderResources + Asset,
|
||||
{
|
||||
fn get_system(&self) -> BoxedSystem {
|
||||
let system = asset_render_resources_node_system::<T>
|
||||
.system()
|
||||
.config(|config| {
|
||||
config.0 = Some(RenderResourcesNodeState {
|
||||
command_queue: self.command_queue.clone(),
|
||||
uniform_buffer_arrays: UniformBufferArrays::<HandleId, T>::default(),
|
||||
dynamic_uniforms: self.dynamic_uniforms,
|
||||
})
|
||||
});
|
||||
let system = asset_render_resources_node_system::<T>.config(|config| {
|
||||
config.0 = Some(RenderResourcesNodeState {
|
||||
command_queue: self.command_queue.clone(),
|
||||
uniform_buffer_arrays: UniformBufferArrays::<HandleId, T>::default(),
|
||||
dynamic_uniforms: self.dynamic_uniforms,
|
||||
})
|
||||
});
|
||||
|
||||
Box::new(system)
|
||||
}
|
||||
|
@ -621,8 +620,8 @@ fn asset_render_resources_node_system<T: RenderResources + Asset>(
|
|||
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
||||
removed_handles: RemovedComponents<Handle<T>>,
|
||||
mut queries: QuerySet<(
|
||||
Query<(&Handle<T>, &mut RenderPipelines), Changed<Handle<T>>>,
|
||||
Query<&mut RenderPipelines, With<Handle<T>>>,
|
||||
QueryState<(&Handle<T>, &mut RenderPipelines), Changed<Handle<T>>>,
|
||||
QueryState<&mut RenderPipelines, With<Handle<T>>>,
|
||||
)>,
|
||||
) {
|
||||
let state = state.deref_mut();
|
||||
|
@ -752,7 +751,7 @@ fn asset_render_resources_node_system<T: RenderResources + Asset>(
|
|||
|
||||
// update removed entity asset mapping
|
||||
for entity in removed_handles.iter() {
|
||||
if let Ok(mut render_pipelines) = queries.q1_mut().get_mut(entity) {
|
||||
if let Ok(mut render_pipelines) = queries.q1().get_mut(entity) {
|
||||
render_pipelines
|
||||
.bindings
|
||||
.remove_asset_with_type(TypeId::of::<T>())
|
||||
|
@ -760,7 +759,7 @@ fn asset_render_resources_node_system<T: RenderResources + Asset>(
|
|||
}
|
||||
|
||||
// update changed entity asset mapping
|
||||
for (asset_handle, mut render_pipelines) in queries.q0_mut().iter_mut() {
|
||||
for (asset_handle, mut render_pipelines) in queries.q0().iter_mut() {
|
||||
render_pipelines
|
||||
.bindings
|
||||
.remove_asset_with_type(TypeId::of::<T>());
|
||||
|
|
|
@ -151,7 +151,19 @@ fn reflect_binding(
|
|||
filtering: true,
|
||||
},
|
||||
),
|
||||
_ => panic!("Unsupported bind type {:?}.", binding.descriptor_type),
|
||||
_ => {
|
||||
let ReflectDescriptorBinding {
|
||||
descriptor_type,
|
||||
name,
|
||||
set,
|
||||
binding,
|
||||
..
|
||||
} = binding;
|
||||
panic!(
|
||||
"Unsupported shader bind type {:?} (name '{}', set {}, binding {})",
|
||||
descriptor_type, name, set, binding
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let shader_stage = match shader_stage {
|
||||
|
|
|
@ -8,9 +8,9 @@ use crate::{
|
|||
use bevy_app::prelude::*;
|
||||
use bevy_asset::{Assets, Handle, HandleUntyped};
|
||||
use bevy_ecs::{
|
||||
query::With,
|
||||
query::{QueryState, With},
|
||||
reflect::ReflectComponent,
|
||||
system::{IntoSystem, Query, QuerySet, Res},
|
||||
system::{QuerySet, Res},
|
||||
world::Mut,
|
||||
};
|
||||
use bevy_reflect::{Reflect, TypeUuid};
|
||||
|
@ -27,7 +27,7 @@ pub struct WireframePlugin;
|
|||
impl Plugin for WireframePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<WireframeConfig>()
|
||||
.add_system_to_stage(crate::RenderStage::Draw, draw_wireframes_system.system());
|
||||
.add_system_to_stage(crate::RenderStage::Draw, draw_wireframes_system);
|
||||
let world = app.world.cell();
|
||||
let mut shaders = world.get_resource_mut::<Assets<Shader>>().unwrap();
|
||||
let mut pipelines = world
|
||||
|
@ -62,8 +62,8 @@ pub fn draw_wireframes_system(
|
|||
meshes: Res<Assets<Mesh>>,
|
||||
wireframe_config: Res<WireframeConfig>,
|
||||
mut query: QuerySet<(
|
||||
Query<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible)>,
|
||||
Query<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible), With<Wireframe>>,
|
||||
QueryState<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible)>,
|
||||
QueryState<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible), With<Wireframe>>,
|
||||
)>,
|
||||
) {
|
||||
let iterator = |(mut draw, mut render_pipelines, mesh_handle, visible): (
|
||||
|
@ -123,8 +123,8 @@ pub fn draw_wireframes_system(
|
|||
};
|
||||
|
||||
if wireframe_config.global {
|
||||
query.q0_mut().iter_mut().for_each(iterator);
|
||||
query.q0().iter_mut().for_each(iterator);
|
||||
} else {
|
||||
query.q1_mut().iter_mut().for_each(iterator);
|
||||
query.q1().iter_mut().for_each(iterator);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
name = "bevy_text"
|
||||
version = "0.5.0"
|
||||
edition = "2018"
|
||||
authors = [
|
||||
"Bevy Contributors <bevyengine@gmail.com>",
|
||||
"Carter Anderson <mcanders1@gmail.com>",
|
||||
]
|
||||
description = "Provides text functionality for Bevy Engine"
|
||||
homepage = "https://bevyengine.org"
|
||||
repository = "https://github.com/bevyengine/bevy"
|
||||
|
|
|
@ -29,7 +29,7 @@ pub mod prelude {
|
|||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_asset::AddAsset;
|
||||
use bevy_ecs::{entity::Entity, system::IntoSystem};
|
||||
use bevy_ecs::entity::Entity;
|
||||
use bevy_render::RenderStage;
|
||||
|
||||
pub type DefaultTextPipeline = TextPipeline<Entity>;
|
||||
|
@ -43,7 +43,7 @@ impl Plugin for TextPlugin {
|
|||
.add_asset::<FontAtlasSet>()
|
||||
.init_asset_loader::<FontLoader>()
|
||||
.insert_resource(DefaultTextPipeline::default())
|
||||
.add_system_to_stage(CoreStage::PostUpdate, text2d_system.system())
|
||||
.add_system_to_stage(RenderStage::Draw, text2d::draw_text2d_system.system());
|
||||
.add_system_to_stage(CoreStage::PostUpdate, text2d_system)
|
||||
.add_system_to_stage(RenderStage::Draw, text2d::draw_text2d_system);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use bevy_asset::Assets;
|
|||
use bevy_ecs::{
|
||||
bundle::Bundle,
|
||||
entity::Entity,
|
||||
query::{Changed, With, Without},
|
||||
query::{Changed, QueryState, With, Without},
|
||||
system::{Local, Query, QuerySet, Res, ResMut},
|
||||
};
|
||||
use bevy_math::{Size, Vec3};
|
||||
|
@ -136,12 +136,12 @@ pub fn text2d_system(
|
|||
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
|
||||
mut text_pipeline: ResMut<DefaultTextPipeline>,
|
||||
mut text_queries: QuerySet<(
|
||||
Query<Entity, (With<MainPass>, Changed<Text>)>,
|
||||
Query<(&Text, &mut Text2dSize), With<MainPass>>,
|
||||
QueryState<Entity, (With<MainPass>, Changed<Text>)>,
|
||||
QueryState<(&Text, &mut Text2dSize), With<MainPass>>,
|
||||
)>,
|
||||
) {
|
||||
// Adds all entities where the text or the style has changed to the local queue
|
||||
for entity in text_queries.q0_mut().iter_mut() {
|
||||
for entity in text_queries.q0().iter_mut() {
|
||||
queued_text.entities.push(entity);
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,7 @@ pub fn text2d_system(
|
|||
|
||||
// Computes all text in the local queue
|
||||
let mut new_queue = Vec::new();
|
||||
let query = text_queries.q1_mut();
|
||||
let mut query = text_queries.q1();
|
||||
for entity in queued_text.entities.drain(..) {
|
||||
if let Ok((text, mut calculated_size)) = query.get_mut(entity) {
|
||||
match text_pipeline.queue_text(
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
name = "bevy_ui"
|
||||
version = "0.5.0"
|
||||
edition = "2018"
|
||||
authors = [
|
||||
"Bevy Contributors <bevyengine@gmail.com>",
|
||||
"Carter Anderson <mcanders1@gmail.com>",
|
||||
]
|
||||
description = "A custom ECS-driven UI framework built specifically for Bevy Engine"
|
||||
homepage = "https://bevyengine.org"
|
||||
repository = "https://github.com/bevyengine/bevy"
|
||||
|
|
|
@ -114,8 +114,8 @@ impl From<Direction> for stretch::style::Direction {
|
|||
fn from(value: Direction) -> Self {
|
||||
match value {
|
||||
Direction::Inherit => stretch::style::Direction::Inherit,
|
||||
Direction::Ltr => stretch::style::Direction::LTR,
|
||||
Direction::Rtl => stretch::style::Direction::RTL,
|
||||
Direction::LeftToRight => stretch::style::Direction::LTR,
|
||||
Direction::RightToLeft => stretch::style::Direction::RTL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,7 @@ pub mod prelude {
|
|||
}
|
||||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_ecs::{
|
||||
schedule::{ParallelSystemDescriptorCoercion, SystemLabel},
|
||||
system::IntoSystem,
|
||||
};
|
||||
use bevy_ecs::schedule::{ParallelSystemDescriptorCoercion, SystemLabel};
|
||||
use bevy_input::InputSystem;
|
||||
use bevy_math::{Rect, Size};
|
||||
use bevy_render::RenderStage;
|
||||
|
@ -62,35 +59,30 @@ impl Plugin for UiPlugin {
|
|||
.register_type::<Val>()
|
||||
.add_system_to_stage(
|
||||
CoreStage::PreUpdate,
|
||||
ui_focus_system
|
||||
.system()
|
||||
.label(UiSystem::Focus)
|
||||
.after(InputSystem),
|
||||
ui_focus_system.label(UiSystem::Focus).after(InputSystem),
|
||||
)
|
||||
// add these stages to front because these must run before transform update systems
|
||||
.add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
widget::text_system.system().before(UiSystem::Flex),
|
||||
widget::text_system.before(UiSystem::Flex),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
widget::image_node_system.system().before(UiSystem::Flex),
|
||||
widget::image_node_system.before(UiSystem::Flex),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
flex_node_system
|
||||
.system()
|
||||
.label(UiSystem::Flex)
|
||||
.before(TransformSystem::TransformPropagate),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
ui_z_system
|
||||
.system()
|
||||
.after(UiSystem::Flex)
|
||||
.before(TransformSystem::TransformPropagate),
|
||||
)
|
||||
.add_system_to_stage(RenderStage::Draw, widget::draw_text_system.system());
|
||||
.add_system_to_stage(RenderStage::Draw, widget::draw_text_system);
|
||||
|
||||
crate::render::add_ui_graph(&mut app.world);
|
||||
}
|
||||
|
|
|
@ -155,8 +155,8 @@ impl Default for AlignContent {
|
|||
#[reflect_value(PartialEq, Serialize, Deserialize)]
|
||||
pub enum Direction {
|
||||
Inherit,
|
||||
Ltr,
|
||||
Rtl,
|
||||
LeftToRight,
|
||||
RightToLeft,
|
||||
}
|
||||
|
||||
impl Default for Direction {
|
||||
|
|
|
@ -54,7 +54,7 @@ fn update_hierarchy(
|
|||
mod tests {
|
||||
use bevy_ecs::{
|
||||
schedule::{Schedule, Stage, SystemStage},
|
||||
system::{CommandQueue, Commands, IntoSystem},
|
||||
system::{CommandQueue, Commands},
|
||||
world::World,
|
||||
};
|
||||
use bevy_transform::{components::Transform, hierarchy::BuildChildren};
|
||||
|
@ -122,7 +122,7 @@ mod tests {
|
|||
|
||||
let mut schedule = Schedule::default();
|
||||
let mut update_stage = SystemStage::parallel();
|
||||
update_stage.add_system(ui_z_system.system());
|
||||
update_stage.add_system(ui_z_system);
|
||||
schedule.add_stage("update", update_stage);
|
||||
schedule.run(&mut world);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::{CalculatedSize, Node, Style, Val};
|
|||
use bevy_asset::Assets;
|
||||
use bevy_ecs::{
|
||||
entity::Entity,
|
||||
query::{Changed, Or, With, Without},
|
||||
query::{Changed, Or, QueryState, With, Without},
|
||||
system::{Local, Query, QuerySet, Res, ResMut},
|
||||
};
|
||||
use bevy_math::Size;
|
||||
|
@ -53,9 +53,9 @@ pub fn text_system(
|
|||
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
|
||||
mut text_pipeline: ResMut<DefaultTextPipeline>,
|
||||
mut text_queries: QuerySet<(
|
||||
Query<Entity, Or<(Changed<Text>, Changed<Style>)>>,
|
||||
Query<Entity, (With<Text>, With<Style>)>,
|
||||
Query<(&Text, &Style, &mut CalculatedSize)>,
|
||||
QueryState<Entity, Or<(Changed<Text>, Changed<Style>)>>,
|
||||
QueryState<Entity, (With<Text>, With<Style>)>,
|
||||
QueryState<(&Text, &Style, &mut CalculatedSize)>,
|
||||
)>,
|
||||
) {
|
||||
let scale_factor = if let Some(window) = windows.get_primary() {
|
||||
|
@ -86,7 +86,7 @@ pub fn text_system(
|
|||
|
||||
// Computes all text in the local queue
|
||||
let mut new_queue = Vec::new();
|
||||
let query = text_queries.q2_mut();
|
||||
let mut query = text_queries.q2();
|
||||
for entity in queued_text.entities.drain(..) {
|
||||
if let Ok((text, style, mut calculated_size)) = query.get_mut(entity) {
|
||||
let node_size = Size::new(
|
||||
|
|
|
@ -2,6 +2,7 @@ use bevy::{
|
|||
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
||||
math::Quat,
|
||||
prelude::*,
|
||||
render::camera::Camera,
|
||||
sprite::SpriteSettings,
|
||||
};
|
||||
|
||||
|
@ -9,9 +10,6 @@ use rand::Rng;
|
|||
|
||||
const CAMERA_SPEED: f32 = 1000.0;
|
||||
|
||||
pub struct PrintTimer(Timer);
|
||||
pub struct Position(Transform);
|
||||
|
||||
/// This example is for performance testing purposes.
|
||||
/// See https://github.com/bevyengine/bevy/pull/1492
|
||||
fn main() {
|
||||
|
@ -24,8 +22,8 @@ fn main() {
|
|||
})
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup)
|
||||
.add_system(tick.system().label("Tick"))
|
||||
.add_system(move_camera.system().after("Tick"))
|
||||
.add_system(tick_system.label("Tick"))
|
||||
.add_system(move_camera_system.after("Tick"))
|
||||
.run()
|
||||
}
|
||||
|
||||
|
@ -44,14 +42,15 @@ fn setup(
|
|||
|
||||
let sprite_handle = materials.add(assets.load("branding/icon.png").into());
|
||||
|
||||
// Spawns the camera
|
||||
commands
|
||||
.spawn()
|
||||
.insert_bundle(OrthographicCameraBundle::new_2d())
|
||||
.insert(PrintTimer(Timer::from_seconds(1.0, true)))
|
||||
.insert(Position(Transform::from_translation(Vec3::new(
|
||||
0.0, 0.0, 1000.0,
|
||||
))));
|
||||
.insert(Timer::from_seconds(1.0, true))
|
||||
.insert(Transform::from_xyz(0.0, 0.0, 1000.0));
|
||||
|
||||
// Builds and spawns the sprites
|
||||
let mut sprites = vec![];
|
||||
for y in -half_y..half_y {
|
||||
for x in -half_x..half_x {
|
||||
let position = Vec2::new(x as f32, y as f32);
|
||||
|
@ -59,7 +58,7 @@ fn setup(
|
|||
let rotation = Quat::from_rotation_z(rng.gen::<f32>());
|
||||
let scale = Vec3::splat(rng.gen::<f32>() * 2.0);
|
||||
|
||||
commands.spawn().insert_bundle(SpriteBundle {
|
||||
sprites.push(SpriteBundle {
|
||||
material: sprite_handle.clone(),
|
||||
transform: Transform {
|
||||
translation,
|
||||
|
@ -71,26 +70,23 @@ fn setup(
|
|||
});
|
||||
}
|
||||
}
|
||||
commands.spawn_batch(sprites);
|
||||
}
|
||||
|
||||
fn move_camera(time: Res<Time>, mut query: Query<(&mut Transform, &mut Position)>) {
|
||||
for (mut transform, mut position) in query.iter_mut() {
|
||||
position
|
||||
.0
|
||||
.rotate(Quat::from_rotation_z(time.delta_seconds() * 0.5));
|
||||
position.0 =
|
||||
position.0 * Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_seconds());
|
||||
transform.translation = position.0.translation;
|
||||
transform.rotation *= Quat::from_rotation_z(time.delta_seconds() / 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn tick(time: Res<Time>, sprites: Query<&Sprite>, mut query: Query<&mut PrintTimer>) {
|
||||
for mut timer in query.iter_mut() {
|
||||
timer.0.tick(time.delta());
|
||||
|
||||
if timer.0.just_finished() {
|
||||
info!("Sprites: {}", sprites.iter().count(),);
|
||||
}
|
||||
// System for rotating and translating the camera
|
||||
fn move_camera_system(time: Res<Time>, mut camera_query: Query<&mut Transform, With<Camera>>) {
|
||||
let mut camera_transform = camera_query.single_mut();
|
||||
camera_transform.rotate(Quat::from_rotation_z(time.delta_seconds() * 0.5));
|
||||
*camera_transform = *camera_transform
|
||||
* Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_seconds());
|
||||
}
|
||||
|
||||
// System for printing the number of sprites on every tick of the timer
|
||||
fn tick_system(time: Res<Time>, sprites_query: Query<&Sprite>, mut timer_query: Query<&mut Timer>) {
|
||||
let mut timer = timer_query.single_mut();
|
||||
timer.tick(time.delta());
|
||||
|
||||
if timer.just_finished() {
|
||||
info!("Sprites: {}", sprites_query.iter().count(),);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@ fn main() {
|
|||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
_asset_server: Res<AssetServer>,
|
||||
// mut materials: ResMut<Assets<ColorMaterial>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||
) {
|
||||
// let texture_handle = asset_server.load("branding/icon.png");
|
||||
let texture_handle = asset_server.load("branding/icon.png");
|
||||
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
|
||||
commands.spawn_bundle(SpriteBundle {
|
||||
// material: materials.add(texture_handle.into()),
|
||||
material: materials.add(texture_handle.into()),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ fn main() {
|
|||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ fn main() {
|
|||
})
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_system(rotator_system.system())
|
||||
.add_startup_system(setup)
|
||||
.add_system(rotator_system)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ fn main() {
|
|||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ fn main() {
|
|||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ fn main() {
|
|||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_system(rotator_system.system())
|
||||
.add_startup_system(setup)
|
||||
.add_system(rotator_system)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ fn main() {
|
|||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -226,8 +226,8 @@ fn setup(
|
|||
fn main() {
|
||||
let mut app = App::new();
|
||||
app.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_system(cube_rotator_system.system())
|
||||
.add_system(rotator_system.system())
|
||||
.add_startup_system(setup)
|
||||
.add_system(cube_rotator_system)
|
||||
.add_system(rotator_system)
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -154,11 +154,11 @@ fn toggle_light(
|
|||
fn toggle_shadows(
|
||||
mut commands: Commands,
|
||||
input: Res<Input<KeyCode>>,
|
||||
queries: QuerySet<(
|
||||
Query<Entity, (With<Handle<Mesh>>, With<NotShadowCaster>)>,
|
||||
Query<Entity, (With<Handle<Mesh>>, With<NotShadowReceiver>)>,
|
||||
Query<Entity, (With<Handle<Mesh>>, Without<NotShadowCaster>)>,
|
||||
Query<Entity, (With<Handle<Mesh>>, Without<NotShadowReceiver>)>,
|
||||
mut queries: QuerySet<(
|
||||
QueryState<Entity, (With<Handle<Mesh>>, With<NotShadowCaster>)>,
|
||||
QueryState<Entity, (With<Handle<Mesh>>, With<NotShadowReceiver>)>,
|
||||
QueryState<Entity, (With<Handle<Mesh>>, Without<NotShadowCaster>)>,
|
||||
QueryState<Entity, (With<Handle<Mesh>>, Without<NotShadowReceiver>)>,
|
||||
)>,
|
||||
) {
|
||||
if input.just_pressed(KeyCode::C) {
|
||||
|
|
|
@ -17,8 +17,8 @@ fn main() {
|
|||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(LogDiagnosticsPlugin::default())
|
||||
.add_startup_system(setup.system())
|
||||
.add_system(move_cubes.system())
|
||||
.add_startup_system(setup)
|
||||
.add_system(move_cubes)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use bevy::prelude::*;
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@ fn main() {
|
|||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.insert_resource(SceneInstance::default())
|
||||
.add_startup_system(setup.system())
|
||||
.add_system(scene_update.system())
|
||||
.add_system(move_scene_entities.system())
|
||||
.init_resource::<SceneInstance>()
|
||||
.add_startup_system(setup)
|
||||
.add_system(scene_update)
|
||||
.add_system(move_scene_entities)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ fn main() {
|
|||
})
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(WireframePlugin)
|
||||
.add_startup_system(setup.system())
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -11,9 +11,9 @@ use bevy::{
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_system(rotator_system.system())
|
||||
.add_system(camera_order_color_system.system())
|
||||
.add_startup_system(setup)
|
||||
.add_system(rotator_system)
|
||||
.add_system(camera_order_color_system)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -171,7 +171,6 @@ Example | File | Description
|
|||
`hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities
|
||||
`iter_combinations` | [`ecs/iter_combinations.rs`](./ecs/iter_combinations.rs) | Shows how to iterate over combinations of query results.
|
||||
`parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator`
|
||||
`query_bundle` | [`ecs/query_bundle.rs`](./ecs/query_bundle.rs) | Shows how to query entities that contain components in a `Bundle`
|
||||
`removal_detection` | [`ecs/removal_detection.rs`](./ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame.
|
||||
`startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up)
|
||||
`state` | [`ecs/state.rs`](./ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state
|
||||
|
|
|
@ -6,7 +6,7 @@ fn main() {
|
|||
App::new()
|
||||
.insert_resource(Msaa { samples: 2 })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,6 @@ fn main() {
|
|||
App::new()
|
||||
.insert_resource(Input(String::new()))
|
||||
.set_runner(my_runner)
|
||||
.add_system(print_system.system())
|
||||
.add_system(print_system)
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use bevy::prelude::*;
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_system(file_drag_and_drop_system.system())
|
||||
.add_system(file_drag_and_drop_system)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ fn main() {
|
|||
App::new()
|
||||
.insert_resource(ScheduleRunnerSettings::run_once())
|
||||
.add_plugins(MinimalPlugins)
|
||||
.add_system(hello_world_system.system())
|
||||
.add_system(hello_world_system)
|
||||
.run();
|
||||
|
||||
// this app loops forever at 60 fps
|
||||
|
@ -22,7 +22,7 @@ fn main() {
|
|||
1.0 / 60.0,
|
||||
)))
|
||||
.add_plugins(MinimalPlugins)
|
||||
.add_system(counter.system())
|
||||
.add_system(counter)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ fn main() {
|
|||
// filter: "wgpu=warn,bevy_ecs=info".to_string(),
|
||||
// })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_system(log_system.system())
|
||||
.add_system(log_system)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,8 +28,7 @@ impl Plugin for PrintMessagePlugin {
|
|||
message: self.message.clone(),
|
||||
timer: Timer::new(self.wait_duration, true),
|
||||
};
|
||||
app.insert_resource(state)
|
||||
.add_system(print_message_system.system());
|
||||
app.insert_resource(state).add_system(print_message_system);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ pub struct PrintHelloPlugin;
|
|||
|
||||
impl Plugin for PrintHelloPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_system(print_hello_system.system());
|
||||
app.add_system(print_hello_system);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ pub struct PrintWorldPlugin;
|
|||
|
||||
impl Plugin for PrintWorldPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_system(print_world_system.system());
|
||||
app.add_system(print_world_system);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ fn main() {
|
|||
})
|
||||
.insert_resource(ClearColor(Color::rgb(0.2, 0.2, 0.8)))
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_system(system1.system())
|
||||
.add_system(system1)
|
||||
.run();
|
||||
println!("Running another App.");
|
||||
App::new()
|
||||
|
@ -19,7 +19,7 @@ fn main() {
|
|||
.add_plugins_with(DefaultPlugins, |group| {
|
||||
group.disable::<bevy::log::LogPlugin>()
|
||||
})
|
||||
.add_system(system2.system())
|
||||
.add_system(system2)
|
||||
.run();
|
||||
println!("Done.");
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ fn main() {
|
|||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -39,8 +39,8 @@ fn main() {
|
|||
.init_resource::<State>()
|
||||
.add_asset::<CustomAsset>()
|
||||
.init_asset_loader::<CustomAssetLoader>()
|
||||
.add_startup_system(setup.system())
|
||||
.add_system(print_on_load.system())
|
||||
.add_startup_system(setup)
|
||||
.add_system(print_on_load)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ fn main() {
|
|||
// asset system are initialized correctly.
|
||||
group.add_before::<bevy::asset::AssetPlugin, _>(CustomAssetIoPlugin)
|
||||
})
|
||||
.add_startup_system(setup.system())
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use bevy::prelude::*;
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -12,10 +12,10 @@ fn main() {
|
|||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup_env.system())
|
||||
.add_startup_system(add_assets.system())
|
||||
.add_startup_system(spawn_tasks.system())
|
||||
.add_system(handle_tasks.system())
|
||||
.add_startup_system(setup_env)
|
||||
.add_startup_system(add_assets)
|
||||
.add_startup_system(spawn_tasks)
|
||||
.add_system(handle_tasks)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use bevy::prelude::*;
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ fn main() {
|
|||
// The "print diagnostics" plugin is optional. It just visualizes our diagnostics in the
|
||||
// console
|
||||
.add_plugin(LogDiagnosticsPlugin::default())
|
||||
.add_startup_system(setup_diagnostic_system.system())
|
||||
.add_system(my_system.system())
|
||||
.add_startup_system(setup_diagnostic_system)
|
||||
.add_system(my_system)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@ use rand::Rng;
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_system(change_component.system())
|
||||
.add_system(change_detection.system())
|
||||
.add_system(tracker_monitoring.system())
|
||||
.add_startup_system(setup)
|
||||
.add_system(change_component)
|
||||
.add_system(change_detection)
|
||||
.add_system(tracker_monitoring)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -272,7 +272,7 @@ fn main() {
|
|||
// that :) The plugin below runs our app's "system schedule" once every 5 seconds
|
||||
// (configured above).
|
||||
.add_plugin(ScheduleRunnerPlugin::default())
|
||||
// Resources that implement the Default or FromResources trait can be added like this:
|
||||
// Resources that implement the Default or FromWorld trait can be added like this:
|
||||
.init_resource::<GameState>()
|
||||
// Startup systems run exactly once BEFORE all other systems. These are generally used for
|
||||
// app initialization code (ex: adding entities and resources)
|
||||
|
|
|
@ -7,8 +7,8 @@ fn main() {
|
|||
.add_plugins(DefaultPlugins)
|
||||
.add_event::<MyEvent>()
|
||||
.init_resource::<EventTriggerState>()
|
||||
.add_system(event_trigger_system.system())
|
||||
.add_system(event_listener_system.system())
|
||||
.add_system(event_trigger_system)
|
||||
.add_system(event_listener_system)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ fn main() {
|
|||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
// this system will run once every update (it should match your screen's refresh rate)
|
||||
.add_system(frame_update.system())
|
||||
.add_system(frame_update)
|
||||
// add a new stage that runs twice a second
|
||||
.add_stage_after(
|
||||
CoreStage::Update,
|
||||
|
@ -24,7 +24,7 @@ fn main() {
|
|||
// FixedTimestep state from within a system
|
||||
.with_label(LABEL),
|
||||
)
|
||||
.with_system(fixed_update.system()),
|
||||
.with_system(fixed_update),
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ use bevy::prelude::*;
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.add_system(rotate.system())
|
||||
.add_startup_system(setup)
|
||||
.add_system(rotate)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
|
@ -10,14 +10,14 @@ fn main() {
|
|||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(generate_bodies.system())
|
||||
.add_startup_system(generate_bodies)
|
||||
.add_stage_after(
|
||||
CoreStage::Update,
|
||||
FixedUpdateStage,
|
||||
SystemStage::parallel()
|
||||
.with_run_criteria(FixedTimestep::step(DELTA_TIME))
|
||||
.with_system(interact_bodies.system())
|
||||
.with_system(integrate.system()),
|
||||
.with_system(interact_bodies)
|
||||
.with_system(integrate),
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -70,8 +70,8 @@ fn bounce_system(
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(spawn_system.system())
|
||||
.add_system(move_system.system())
|
||||
.add_system(bounce_system.system())
|
||||
.add_startup_system(spawn_system)
|
||||
.add_system(move_system)
|
||||
.add_system(bounce_system)
|
||||
.run();
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue