Merge branch 'main' into Remove-entity-reserving/pending/flushing-system

This commit is contained in:
Eagster 2025-06-19 11:26:13 -04:00 committed by GitHub
commit 6a596cb1e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
205 changed files with 2923 additions and 1112 deletions

View File

@ -95,7 +95,7 @@ jobs:
- name: CI job
# To run the tests one item at a time for troubleshooting, use
# cargo --quiet test --lib -- --list | sed 's/: test$//' | MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation" xargs -n1 cargo miri test -p bevy_ecs --lib -- --exact
run: cargo miri test -p bevy_ecs
run: cargo miri test -p bevy_ecs --features bevy_utils/debug
env:
# -Zrandomize-layout makes sure we dont rely on the layout of anything that might change
RUSTFLAGS: -Zrandomize-layout

View File

@ -165,6 +165,7 @@ default = [
"vorbis",
"webgl2",
"x11",
"debug",
]
# Recommended defaults for no_std applications
@ -507,7 +508,10 @@ file_watcher = ["bevy_internal/file_watcher"]
embedded_watcher = ["bevy_internal/embedded_watcher"]
# Enable stepping-based debugging of Bevy systems
bevy_debug_stepping = ["bevy_internal/bevy_debug_stepping"]
bevy_debug_stepping = [
"bevy_internal/bevy_debug_stepping",
"bevy_internal/debug",
]
# Enables the meshlet renderer for dense high-poly scenes (experimental)
meshlet = ["bevy_internal/meshlet"]
@ -551,6 +555,9 @@ web = ["bevy_internal/web"]
# Enable hotpatching of Bevy systems
hotpatching = ["bevy_internal/hotpatching"]
# Enable collecting debug information about systems and components to help with diagnostics
debug = ["bevy_internal/debug"]
[dependencies]
bevy_internal = { path = "crates/bevy_internal", version = "0.16.0-dev", default-features = false }
tracing = { version = "0.1", default-features = false, optional = true }
@ -2098,6 +2105,7 @@ wasm = false
name = "dynamic"
path = "examples/ecs/dynamic.rs"
doc-scrape-examples = true
required-features = ["debug"]
[package.metadata.example.dynamic]
name = "Dynamic ECS"

View File

@ -847,7 +847,7 @@ impl ViewNode for SmaaNode {
view_smaa_uniform_offset,
smaa_textures,
view_smaa_bind_groups,
): QueryItem<'w, Self::ViewQuery>,
): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let pipeline_cache = world.resource::<PipelineCache>();

View File

@ -1,6 +1,7 @@
#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
//! Macros for deriving asset traits.
use bevy_macro_utils::BevyManifest;
use proc_macro::{Span, TokenStream};
use quote::{format_ident, quote};
@ -12,6 +13,7 @@ pub(crate) fn bevy_asset_path() -> Path {
const DEPENDENCY_ATTRIBUTE: &str = "dependency";
/// Implement the `Asset` trait.
#[proc_macro_derive(Asset, attributes(dependency))]
pub fn derive_asset(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
@ -30,6 +32,7 @@ pub fn derive_asset(input: TokenStream) -> TokenStream {
})
}
/// Implement the `VisitAssetDependencies` trait.
#[proc_macro_derive(VisitAssetDependencies, attributes(dependency))]
pub fn derive_asset_dependency_visitor(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);

View File

@ -158,9 +158,9 @@ unsafe impl<A: AsAssetId> WorldQuery for AssetChanged<A> {
fetch
}
unsafe fn init_fetch<'w>(
unsafe fn init_fetch<'w, 's>(
world: UnsafeWorldCell<'w>,
state: &Self::State,
state: &'s Self::State,
last_run: Tick,
this_run: Tick,
) -> Self::Fetch<'w> {
@ -201,9 +201,9 @@ unsafe impl<A: AsAssetId> WorldQuery for AssetChanged<A> {
const IS_DENSE: bool = <&A>::IS_DENSE;
unsafe fn set_archetype<'w>(
unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>,
state: &Self::State,
state: &'s Self::State,
archetype: &'w Archetype,
table: &'w Table,
) {
@ -215,7 +215,11 @@ unsafe impl<A: AsAssetId> WorldQuery for AssetChanged<A> {
}
}
unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) {
unsafe fn set_table<'w, 's>(
fetch: &mut Self::Fetch<'w>,
state: &Self::State,
table: &'w Table,
) {
if let Some(inner) = &mut fetch.inner {
// SAFETY: We delegate to the inner `set_table` for `A`
unsafe {
@ -265,6 +269,7 @@ unsafe impl<A: AsAssetId> QueryFilter for AssetChanged<A> {
#[inline]
unsafe fn filter_fetch(
state: &Self::State,
fetch: &mut Self::Fetch<'_>,
entity: Entity,
table_row: TableRow,
@ -272,7 +277,7 @@ unsafe impl<A: AsAssetId> QueryFilter for AssetChanged<A> {
fetch.inner.as_mut().is_some_and(|inner| {
// SAFETY: We delegate to the inner `fetch` for `A`
unsafe {
let handle = <&A>::fetch(inner, entity, table_row);
let handle = <&A>::fetch(&state.asset_id, inner, entity, table_row);
fetch.check.has_changed(handle)
}
})

View File

@ -1953,6 +1953,14 @@ impl AssetLoaderError {
pub fn path(&self) -> &AssetPath<'static> {
&self.path
}
/// The error the loader reported when attempting to load the asset.
///
/// If you know the type of the error the asset loader returned, you can use
/// [`BevyError::downcast_ref()`] to get it.
pub fn error(&self) -> &BevyError {
&self.error
}
}
/// An error that occurs while resolving an asset added by `add_async`.

View File

@ -121,7 +121,7 @@ impl ViewNode for BloomNode {
bloom_settings,
upsampling_pipeline_ids,
downsampling_pipeline_ids,
): QueryItem<'w, Self::ViewQuery>,
): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
if bloom_settings.intensity == 0.0 {

View File

@ -227,7 +227,7 @@ impl ExtractComponent for Bloom {
type QueryFilter = With<Hdr>;
type Out = (Self, BloomUniforms);
fn extract_component((bloom, camera): QueryItem<'_, Self::QueryData>) -> Option<Self::Out> {
fn extract_component((bloom, camera): QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
match (
camera.physical_viewport_rect(),
camera.physical_viewport_size(),

View File

@ -31,7 +31,7 @@ impl ViewNode for MainOpaquePass2dNode {
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
(camera, view, target, depth): QueryItem<'w, Self::ViewQuery>,
(camera, view, target, depth): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let (Some(opaque_phases), Some(alpha_mask_phases)) = (

View File

@ -28,7 +28,7 @@ impl ViewNode for MainTransparentPass2dNode {
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
(camera, view, target, depth): bevy_ecs::query::QueryItem<'w, Self::ViewQuery>,
(camera, view, target, depth): bevy_ecs::query::QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let Some(transparent_phases) =

View File

@ -45,7 +45,7 @@ impl ViewNode for MainOpaquePass3dNode {
skybox_pipeline,
skybox_bind_group,
view_uniform_offset,
): QueryItem<'w, Self::ViewQuery>,
): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let (Some(opaque_phases), Some(alpha_mask_phases)) = (

View File

@ -36,7 +36,7 @@ impl ViewNode for EarlyDeferredGBufferPrepassNode {
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
view_query: QueryItem<'w, Self::ViewQuery>,
view_query: QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
run_deferred_prepass(
@ -74,7 +74,7 @@ impl ViewNode for LateDeferredGBufferPrepassNode {
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
view_query: QueryItem<'w, Self::ViewQuery>,
view_query: QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let (_, _, _, _, occlusion_culling, no_indirect_drawing) = view_query;
@ -107,6 +107,7 @@ fn run_deferred_prepass<'w>(
render_context: &mut RenderContext<'w>,
(camera, extracted_view, view_depth_texture, view_prepass_textures, _, _): QueryItem<
'w,
'_,
<LateDeferredGBufferPrepassNode as ViewNode>::ViewQuery,
>,
is_late: bool,

View File

@ -352,7 +352,7 @@ impl ViewNode for DepthOfFieldNode {
view_bind_group_layouts,
depth_of_field_uniform_index,
auxiliary_dof_texture,
): QueryItem<'w, Self::ViewQuery>,
): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let pipeline_cache = world.resource::<PipelineCache>();

View File

@ -61,7 +61,7 @@ impl ViewNode for MsaaWritebackNode {
&self,
_graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
(target, blit_pipeline_id, msaa): QueryItem<'w, Self::ViewQuery>,
(target, blit_pipeline_id, msaa): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
if *msaa == Msaa::Off {

View File

@ -352,7 +352,7 @@ impl ViewNode for PostProcessingNode {
&self,
_: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
(view_target, pipeline_id, chromatic_aberration, post_processing_uniform_buffer_offsets): QueryItem<'w, Self::ViewQuery>,
(view_target, pipeline_id, chromatic_aberration, post_processing_uniform_buffer_offsets): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let pipeline_cache = world.resource::<PipelineCache>();
@ -485,7 +485,7 @@ impl ExtractComponent for ChromaticAberration {
type Out = ChromaticAberration;
fn extract_component(
chromatic_aberration: QueryItem<'_, Self::QueryData>,
chromatic_aberration: QueryItem<'_, '_, Self::QueryData>,
) -> Option<Self::Out> {
// Skip the postprocessing phase entirely if the intensity is zero.
if chromatic_aberration.intensity > 0.0 {

View File

@ -36,7 +36,7 @@ impl ViewNode for EarlyPrepassNode {
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
view_query: QueryItem<'w, Self::ViewQuery>,
view_query: QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
run_prepass(graph, render_context, view_query, world, "early prepass")
@ -73,7 +73,7 @@ impl ViewNode for LatePrepassNode {
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
query: QueryItem<'w, Self::ViewQuery>,
query: QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
// We only need a late prepass if we have occlusion culling and indirect
@ -112,7 +112,7 @@ fn run_prepass<'w>(
_,
_,
has_deferred,
): QueryItem<'w, <LatePrepassNode as ViewNode>::ViewQuery>,
): QueryItem<'w, '_, <LatePrepassNode as ViewNode>::ViewQuery>,
world: &'w World,
label: &'static str,
) -> Result<(), NodeRunError> {

View File

@ -113,7 +113,9 @@ impl ExtractComponent for Skybox {
type QueryFilter = ();
type Out = (Self, SkyboxUniforms);
fn extract_component((skybox, exposure): QueryItem<'_, Self::QueryData>) -> Option<Self::Out> {
fn extract_component(
(skybox, exposure): QueryItem<'_, '_, Self::QueryData>,
) -> Option<Self::Out> {
let exposure = exposure
.map(Exposure::exposure)
.unwrap_or_else(|| Exposure::default().exposure());
@ -123,7 +125,7 @@ impl ExtractComponent for Skybox {
SkyboxUniforms {
brightness: skybox.brightness * exposure,
transform: Transform::from_rotation(skybox.rotation)
.compute_matrix()
.to_matrix()
.inverse(),
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
_wasm_padding_8b: 0,

View File

@ -211,6 +211,7 @@ pub(crate) fn slider_on_pointer_down(
focus: Option<ResMut<InputFocus>>,
focus_visible: Option<ResMut<InputFocusVisible>>,
mut commands: Commands,
ui_scale: Res<UiScale>,
) {
if q_thumb.contains(trigger.target()) {
// Thumb click, stop propagation to prevent track click.
@ -255,7 +256,7 @@ pub(crate) fn slider_on_pointer_down(
// Detect track click.
let local_pos = transform.try_inverse().unwrap().transform_point2(
trigger.event().pointer_location.position * node_target.scale_factor(),
trigger.event().pointer_location.position * node_target.scale_factor() / ui_scale.0,
);
let track_width = node.size().x - thumb_size;
// Avoid division by zero
@ -394,6 +395,10 @@ fn slider_on_key_input(
trigger.propagate(false);
if let Some(on_change) = slider.on_change {
commands.run_system_with(on_change, new_value);
} else {
commands
.entity(trigger.target())
.insert(SliderValue(new_value));
}
}
}
@ -456,7 +461,7 @@ pub(crate) fn slider_on_insert_step(trigger: On<Insert, SliderStep>, mut world:
/// commands.trigger_targets(SetSliderValue::Relative(-0.25), slider);
/// }
/// ```
#[derive(Event, EntityEvent)]
#[derive(Event, EntityEvent, Clone)]
pub enum SetSliderValue {
/// Set the slider value to a specific value.
Absolute(f32),

View File

@ -35,7 +35,7 @@ backtrace = ["std"]
## Enables `tracing` integration, allowing spans and other metrics to be reported
## through that framework.
trace = ["std", "dep:tracing"]
trace = ["std", "dep:tracing", "bevy_utils/debug"]
## Enables a more detailed set of traces which may be noisy if left on by default.
detailed_trace = ["trace"]
@ -63,9 +63,9 @@ std = [
"bevy_reflect?/std",
"bevy_tasks/std",
"bevy_utils/parallel",
"bevy_utils/std",
"bitflags/std",
"concurrent-queue/std",
"disqualified/alloc",
"fixedbitset/std",
"indexmap/std",
"serde?/std",
@ -98,7 +98,6 @@ bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-fea
] }
bitflags = { version = "2.3", default-features = false }
disqualified = { version = "1.0", default-features = false }
fixedbitset = { version = "0.5", default-features = false }
serde = { version = "1", default-features = false, features = [
"alloc",

View File

@ -74,12 +74,23 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream {
let user_generics = ast.generics.clone();
let (user_impl_generics, user_ty_generics, user_where_clauses) = user_generics.split_for_impl();
let user_generics_with_world = {
let mut generics = ast.generics;
let mut generics = ast.generics.clone();
generics.params.insert(0, parse_quote!('__w));
generics
};
let (user_impl_generics_with_world, user_ty_generics_with_world, user_where_clauses_with_world) =
user_generics_with_world.split_for_impl();
let user_generics_with_world_and_state = {
let mut generics = ast.generics;
generics.params.insert(0, parse_quote!('__w));
generics.params.insert(0, parse_quote!('__s));
generics
};
let (
user_impl_generics_with_world_and_state,
user_ty_generics_with_world_and_state,
user_where_clauses_with_world_and_state,
) = user_generics_with_world_and_state.split_for_impl();
let struct_name = ast.ident;
let read_only_struct_name = if attributes.is_mutable {
@ -164,13 +175,13 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream {
&visibility,
&item_struct_name,
&field_types,
&user_impl_generics_with_world,
&user_impl_generics_with_world_and_state,
&field_attrs,
&field_visibilities,
&field_idents,
&user_ty_generics,
&user_ty_generics_with_world,
user_where_clauses_with_world,
&user_ty_generics_with_world_and_state,
user_where_clauses_with_world_and_state,
);
let mutable_world_query_impl = world_query_impl(
&path,
@ -199,13 +210,13 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream {
&visibility,
&read_only_item_struct_name,
&read_only_field_types,
&user_impl_generics_with_world,
&user_impl_generics_with_world_and_state,
&field_attrs,
&field_visibilities,
&field_idents,
&user_ty_generics,
&user_ty_generics_with_world,
user_where_clauses_with_world,
&user_ty_generics_with_world_and_state,
user_where_clauses_with_world_and_state,
);
let readonly_world_query_impl = world_query_impl(
&path,
@ -256,11 +267,11 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream {
for #read_only_struct_name #user_ty_generics #user_where_clauses {
const IS_READ_ONLY: bool = true;
type ReadOnly = #read_only_struct_name #user_ty_generics;
type Item<'__w> = #read_only_item_struct_name #user_ty_generics_with_world;
type Item<'__w, '__s> = #read_only_item_struct_name #user_ty_generics_with_world_and_state;
fn shrink<'__wlong: '__wshort, '__wshort>(
item: Self::Item<'__wlong>
) -> Self::Item<'__wshort> {
fn shrink<'__wlong: '__wshort, '__wshort, '__s>(
item: Self::Item<'__wlong, '__s>
) -> Self::Item<'__wshort, '__s> {
#read_only_item_struct_name {
#(
#field_idents: <#read_only_field_types>::shrink(item.#field_idents),
@ -278,13 +289,26 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream {
/// SAFETY: we call `fetch` for each member that implements `Fetch`.
#[inline(always)]
unsafe fn fetch<'__w>(
unsafe fn fetch<'__w, '__s>(
_state: &'__s Self::State,
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
_entity: #path::entity::Entity,
_table_row: #path::storage::TableRow,
) -> Self::Item<'__w> {
) -> Self::Item<'__w, '__s> {
Self::Item {
#(#field_idents: <#read_only_field_types>::fetch(&mut _fetch.#named_field_idents, _entity, _table_row),)*
#(#field_idents: <#read_only_field_types>::fetch(&_state.#named_field_idents, &mut _fetch.#named_field_idents, _entity, _table_row),)*
}
}
}
impl #user_impl_generics #path::query::ReleaseStateQueryData
for #read_only_struct_name #user_ty_generics #user_where_clauses
// Make these HRTBs with an unused lifetime parameter to allow trivial constraints
// See https://github.com/rust-lang/rust/issues/48214
where #(for<'__a> #field_types: #path::query::QueryData<ReadOnly: #path::query::ReleaseStateQueryData>,)* {
fn release_state<'__w>(_item: Self::Item<'__w, '_>) -> Self::Item<'__w, 'static> {
Self::Item {
#(#field_idents: <#read_only_field_types>::release_state(_item.#field_idents),)*
}
}
}
@ -301,11 +325,11 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream {
for #struct_name #user_ty_generics #user_where_clauses {
const IS_READ_ONLY: bool = #is_read_only;
type ReadOnly = #read_only_struct_name #user_ty_generics;
type Item<'__w> = #item_struct_name #user_ty_generics_with_world;
type Item<'__w, '__s> = #item_struct_name #user_ty_generics_with_world_and_state;
fn shrink<'__wlong: '__wshort, '__wshort>(
item: Self::Item<'__wlong>
) -> Self::Item<'__wshort> {
fn shrink<'__wlong: '__wshort, '__wshort, '__s>(
item: Self::Item<'__wlong, '__s>
) -> Self::Item<'__wshort, '__s> {
#item_struct_name {
#(
#field_idents: <#field_types>::shrink(item.#field_idents),
@ -323,13 +347,26 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream {
/// SAFETY: we call `fetch` for each member that implements `Fetch`.
#[inline(always)]
unsafe fn fetch<'__w>(
unsafe fn fetch<'__w, '__s>(
_state: &'__s Self::State,
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
_entity: #path::entity::Entity,
_table_row: #path::storage::TableRow,
) -> Self::Item<'__w> {
) -> Self::Item<'__w, '__s> {
Self::Item {
#(#field_idents: <#field_types>::fetch(&mut _fetch.#named_field_idents, _entity, _table_row),)*
#(#field_idents: <#field_types>::fetch(&_state.#named_field_idents, &mut _fetch.#named_field_idents, _entity, _table_row),)*
}
}
}
impl #user_impl_generics #path::query::ReleaseStateQueryData
for #struct_name #user_ty_generics #user_where_clauses
// Make these HRTBs with an unused lifetime parameter to allow trivial constraints
// See https://github.com/rust-lang/rust/issues/48214
where #(for<'__a> #field_types: #path::query::ReleaseStateQueryData,)* {
fn release_state<'__w>(_item: Self::Item<'__w, '_>) -> Self::Item<'__w, 'static> {
Self::Item {
#(#field_idents: <#field_types>::release_state(_item.#field_idents),)*
}
}
}

View File

@ -102,11 +102,12 @@ pub fn derive_query_filter_impl(input: TokenStream) -> TokenStream {
#[allow(unused_variables)]
#[inline(always)]
unsafe fn filter_fetch<'__w>(
_state: &Self::State,
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
_entity: #path::entity::Entity,
_table_row: #path::storage::TableRow,
) -> bool {
true #(&& <#field_types>::filter_fetch(&mut _fetch.#named_field_idents, _entity, _table_row))*
true #(&& <#field_types>::filter_fetch(&_state.#named_field_idents, &mut _fetch.#named_field_idents, _entity, _table_row))*
}
}
};

View File

@ -10,13 +10,13 @@ pub(crate) fn item_struct(
visibility: &Visibility,
item_struct_name: &Ident,
field_types: &Vec<proc_macro2::TokenStream>,
user_impl_generics_with_world: &ImplGenerics,
user_impl_generics_with_world_and_state: &ImplGenerics,
field_attrs: &Vec<Vec<Attribute>>,
field_visibilities: &Vec<Visibility>,
field_idents: &Vec<proc_macro2::TokenStream>,
user_ty_generics: &TypeGenerics,
user_ty_generics_with_world: &TypeGenerics,
user_where_clauses_with_world: Option<&WhereClause>,
user_ty_generics_with_world_and_state: &TypeGenerics,
user_where_clauses_with_world_and_state: Option<&WhereClause>,
) -> proc_macro2::TokenStream {
let item_attrs = quote! {
#[doc = concat!(
@ -33,20 +33,20 @@ pub(crate) fn item_struct(
Fields::Named(_) => quote! {
#derive_macro_call
#item_attrs
#visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world {
#(#(#field_attrs)* #field_visibilities #field_idents: <#field_types as #path::query::QueryData>::Item<'__w>,)*
#visibility struct #item_struct_name #user_impl_generics_with_world_and_state #user_where_clauses_with_world_and_state {
#(#(#field_attrs)* #field_visibilities #field_idents: <#field_types as #path::query::QueryData>::Item<'__w, '__s>,)*
}
},
Fields::Unnamed(_) => quote! {
#derive_macro_call
#item_attrs
#visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world(
#( #field_visibilities <#field_types as #path::query::QueryData>::Item<'__w>, )*
#visibility struct #item_struct_name #user_impl_generics_with_world_and_state #user_where_clauses_with_world_and_state(
#( #field_visibilities <#field_types as #path::query::QueryData>::Item<'__w, '__s>, )*
);
},
Fields::Unit => quote! {
#item_attrs
#visibility type #item_struct_name #user_ty_generics_with_world = #struct_name #user_ty_generics;
#visibility type #item_struct_name #user_ty_generics_with_world_and_state = #struct_name #user_ty_generics;
},
}
}
@ -110,9 +110,9 @@ pub(crate) fn world_query_impl(
}
}
unsafe fn init_fetch<'__w>(
unsafe fn init_fetch<'__w, '__s>(
_world: #path::world::unsafe_world_cell::UnsafeWorldCell<'__w>,
state: &Self::State,
state: &'__s Self::State,
_last_run: #path::component::Tick,
_this_run: #path::component::Tick,
) -> <Self as #path::query::WorldQuery>::Fetch<'__w> {
@ -133,9 +133,9 @@ pub(crate) fn world_query_impl(
/// SAFETY: we call `set_archetype` for each member that implements `Fetch`
#[inline]
unsafe fn set_archetype<'__w>(
unsafe fn set_archetype<'__w, '__s>(
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
_state: &Self::State,
_state: &'__s Self::State,
_archetype: &'__w #path::archetype::Archetype,
_table: &'__w #path::storage::Table
) {
@ -144,9 +144,9 @@ pub(crate) fn world_query_impl(
/// SAFETY: we call `set_table` for each member that implements `Fetch`
#[inline]
unsafe fn set_table<'__w>(
unsafe fn set_table<'__w, '__s>(
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
_state: &Self::State,
_state: &'__s Self::State,
_table: &'__w #path::storage::Table
) {
#(<#field_types>::set_table(&mut _fetch.#named_field_idents, &_state.#named_field_idents, _table);)*

View File

@ -550,10 +550,9 @@ impl BundleInfo {
// SAFETY: the caller ensures component_id is valid.
unsafe { components.get_info_unchecked(id).name() }
})
.collect::<Vec<_>>()
.join(", ");
.collect::<Vec<_>>();
panic!("Bundle {bundle_type_name} has duplicate components: {names}");
panic!("Bundle {bundle_type_name} has duplicate components: {names:?}");
}
// handle explicit components

View File

@ -24,7 +24,7 @@ use bevy_platform::{
use bevy_ptr::{OwningPtr, UnsafeCellDeref};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use bevy_utils::TypeIdMap;
use bevy_utils::{prelude::DebugName, TypeIdMap};
use core::{
alloc::Layout,
any::{Any, TypeId},
@ -34,7 +34,6 @@ use core::{
mem::needs_drop,
ops::{Deref, DerefMut},
};
use disqualified::ShortName;
use smallvec::SmallVec;
use thiserror::Error;
@ -678,8 +677,8 @@ impl ComponentInfo {
/// Returns the name of the current component.
#[inline]
pub fn name(&self) -> &str {
&self.descriptor.name
pub fn name(&self) -> DebugName {
self.descriptor.name.clone()
}
/// Returns `true` if the current component is mutable.
@ -836,7 +835,7 @@ impl SparseSetIndex for ComponentId {
/// A value describing a component or resource, which may or may not correspond to a Rust type.
#[derive(Clone)]
pub struct ComponentDescriptor {
name: Cow<'static, str>,
name: DebugName,
// SAFETY: This must remain private. It must match the statically known StorageType of the
// associated rust component type if one exists.
storage_type: StorageType,
@ -882,7 +881,7 @@ impl ComponentDescriptor {
/// Create a new `ComponentDescriptor` for the type `T`.
pub fn new<T: Component>() -> Self {
Self {
name: Cow::Borrowed(core::any::type_name::<T>()),
name: DebugName::type_name::<T>(),
storage_type: T::STORAGE_TYPE,
is_send_and_sync: true,
type_id: Some(TypeId::of::<T>()),
@ -907,7 +906,7 @@ impl ComponentDescriptor {
clone_behavior: ComponentCloneBehavior,
) -> Self {
Self {
name: name.into(),
name: name.into().into(),
storage_type,
is_send_and_sync: true,
type_id: None,
@ -923,7 +922,7 @@ impl ComponentDescriptor {
/// The [`StorageType`] for resources is always [`StorageType::Table`].
pub fn new_resource<T: Resource>() -> Self {
Self {
name: Cow::Borrowed(core::any::type_name::<T>()),
name: DebugName::type_name::<T>(),
// PERF: `SparseStorage` may actually be a more
// reasonable choice as `storage_type` for resources.
storage_type: StorageType::Table,
@ -938,7 +937,7 @@ impl ComponentDescriptor {
fn new_non_send<T: Any>(storage_type: StorageType) -> Self {
Self {
name: Cow::Borrowed(core::any::type_name::<T>()),
name: DebugName::type_name::<T>(),
storage_type,
is_send_and_sync: false,
type_id: Some(TypeId::of::<T>()),
@ -964,8 +963,8 @@ impl ComponentDescriptor {
/// Returns the name of the current component.
#[inline]
pub fn name(&self) -> &str {
self.name.as_ref()
pub fn name(&self) -> DebugName {
self.name.clone()
}
/// Returns whether this component is mutable.
@ -1854,13 +1853,10 @@ impl Components {
///
/// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value.
#[inline]
pub fn get_name<'a>(&'a self, id: ComponentId) -> Option<Cow<'a, str>> {
pub fn get_name<'a>(&'a self, id: ComponentId) -> Option<DebugName> {
self.components
.get(id.0)
.and_then(|info| {
info.as_ref()
.map(|info| Cow::Borrowed(info.descriptor.name()))
})
.and_then(|info| info.as_ref().map(|info| info.descriptor.name()))
.or_else(|| {
let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner);
// first check components, then resources, then dynamic
@ -2813,13 +2809,13 @@ pub fn enforce_no_required_components_recursion(
"Recursive required components detected: {}\nhelp: {}",
recursion_check_stack
.iter()
.map(|id| format!("{}", ShortName(&components.get_name(*id).unwrap())))
.map(|id| format!("{}", components.get_name(*id).unwrap().shortname()))
.collect::<Vec<_>>()
.join(""),
if direct_recursion {
format!(
"Remove require({}).",
ShortName(&components.get_name(requiree).unwrap())
components.get_name(requiree).unwrap().shortname()
)
} else {
"If this is intentional, consider merging the components.".into()

View File

@ -10,6 +10,7 @@ use crate::{
use alloc::{borrow::ToOwned, boxed::Box, collections::VecDeque, vec::Vec};
use bevy_platform::collections::{HashMap, HashSet};
use bevy_ptr::{Ptr, PtrMut};
use bevy_utils::prelude::DebugName;
use bumpalo::Bump;
use core::any::TypeId;
@ -172,7 +173,8 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> {
/// - `ComponentId` of component being written does not match expected `ComponentId`.
pub fn write_target_component<C: Component>(&mut self, mut component: C) {
C::map_entities(&mut component, &mut self.mapper);
let short_name = disqualified::ShortName::of::<C>();
let debug_name = DebugName::type_name::<C>();
let short_name = debug_name.shortname();
if self.target_component_written {
panic!("Trying to write component '{short_name}' multiple times")
}

View File

@ -1,4 +1,6 @@
use core::{any::type_name, fmt};
use core::fmt;
use bevy_utils::prelude::DebugName;
use crate::{
entity::Entity,
@ -31,7 +33,7 @@ where
Err(err) => (error_handler)(
err.into(),
ErrorContext::Command {
name: type_name::<C>().into(),
name: DebugName::type_name::<C>(),
},
),
}
@ -43,7 +45,7 @@ where
Err(err) => world.default_error_handler()(
err.into(),
ErrorContext::Command {
name: type_name::<C>().into(),
name: DebugName::type_name::<C>(),
},
),
}

View File

@ -1,7 +1,7 @@
use core::fmt::Display;
use crate::{component::Tick, error::BevyError, prelude::Resource};
use alloc::borrow::Cow;
use bevy_utils::prelude::DebugName;
use derive_more::derive::{Deref, DerefMut};
/// Context for a [`BevyError`] to aid in debugging.
@ -10,26 +10,26 @@ pub enum ErrorContext {
/// The error occurred in a system.
System {
/// The name of the system that failed.
name: Cow<'static, str>,
name: DebugName,
/// The last tick that the system was run.
last_run: Tick,
},
/// The error occurred in a run condition.
RunCondition {
/// The name of the run condition that failed.
name: Cow<'static, str>,
name: DebugName,
/// The last tick that the run condition was evaluated.
last_run: Tick,
},
/// The error occurred in a command.
Command {
/// The name of the command that failed.
name: Cow<'static, str>,
name: DebugName,
},
/// The error occurred in an observer.
Observer {
/// The name of the observer that failed.
name: Cow<'static, str>,
name: DebugName,
/// The last tick that the observer was run.
last_run: Tick,
},
@ -54,12 +54,12 @@ impl Display for ErrorContext {
impl ErrorContext {
/// The name of the ECS construct that failed.
pub fn name(&self) -> &str {
pub fn name(&self) -> DebugName {
match self {
Self::System { name, .. }
| Self::Command { name, .. }
| Self::Observer { name, .. }
| Self::RunCondition { name, .. } => name,
| Self::RunCondition { name, .. } => name.clone(),
}
}

View File

@ -22,9 +22,9 @@ use alloc::{format, string::String, vec::Vec};
use bevy_reflect::std_traits::ReflectDefault;
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
use bevy_utils::prelude::DebugName;
use core::ops::Deref;
use core::slice;
use disqualified::ShortName;
use log::warn;
/// Stores the parent entity of this child entity with this component.
@ -461,11 +461,12 @@ pub fn validate_parent_has_component<C: Component>(
{
// TODO: print name here once Name lives in bevy_ecs
let name: Option<String> = None;
let debug_name = DebugName::type_name::<C>();
warn!(
"warning[B0004]: {}{name} with the {ty_name} component has a parent without {ty_name}.\n\
This will cause inconsistent behaviors! See: https://bevy.org/learn/errors/b0004",
caller.map(|c| format!("{c}: ")).unwrap_or_default(),
ty_name = ShortName::of::<C>(),
ty_name = debug_name.shortname(),
name = name.map_or_else(
|| format!("Entity {entity}"),
|s| format!("The {s} entity")

View File

@ -328,7 +328,7 @@ pub const DESPAWN: ComponentId = ComponentId::new(4);
/// Trigger emitted when a component is inserted onto an entity that does not already have that
/// component. Runs before `Insert`.
/// See [`crate::lifecycle::ComponentHooks::on_add`] for more information.
#[derive(Event, EntityEvent, Debug)]
#[derive(Event, EntityEvent, Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
#[doc(alias = "OnAdd")]
@ -337,7 +337,7 @@ pub struct Add;
/// Trigger emitted when a component is inserted, regardless of whether or not the entity already
/// had that component. Runs after `Add`, if it ran.
/// See [`crate::lifecycle::ComponentHooks::on_insert`] for more information.
#[derive(Event, EntityEvent, Debug)]
#[derive(Event, EntityEvent, Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
#[doc(alias = "OnInsert")]
@ -348,7 +348,7 @@ pub struct Insert;
///
/// Runs before the value is replaced, so you can still access the original component data.
/// See [`crate::lifecycle::ComponentHooks::on_replace`] for more information.
#[derive(Event, EntityEvent, Debug)]
#[derive(Event, EntityEvent, Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
#[doc(alias = "OnReplace")]
@ -357,7 +357,7 @@ pub struct Replace;
/// Trigger emitted when a component is removed from an entity, and runs before the component is
/// removed, so you can still access the component data.
/// See [`crate::lifecycle::ComponentHooks::on_remove`] for more information.
#[derive(Event, EntityEvent, Debug)]
#[derive(Event, EntityEvent, Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
#[doc(alias = "OnRemove")]
@ -365,7 +365,7 @@ pub struct Remove;
/// Trigger emitted for each component on an entity when it is despawned.
/// See [`crate::lifecycle::ComponentHooks::on_despawn`] for more information.
#[derive(Event, EntityEvent, Debug)]
#[derive(Event, EntityEvent, Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
#[doc(alias = "OnDespawn")]

View File

@ -141,7 +141,7 @@ pub struct NameOrEntity {
pub entity: Entity,
}
impl<'a> core::fmt::Display for NameOrEntityItem<'a> {
impl<'w, 's> core::fmt::Display for NameOrEntityItem<'w, 's> {
#[inline(always)]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self.name {
@ -274,9 +274,9 @@ mod tests {
let e2 = world.spawn(name.clone()).id();
let mut query = world.query::<NameOrEntity>();
let d1 = query.get(&world, e1).unwrap();
let d2 = query.get(&world, e2).unwrap();
// NameOrEntity Display for entities without a Name should be {index}v{generation}
assert_eq!(d1.to_string(), "0v0");
let d2 = query.get(&world, e2).unwrap();
// NameOrEntity Display for entities with a Name should be the Name
assert_eq!(d2.to_string(), "MyName");
}

View File

@ -97,7 +97,7 @@ fn component_clone_observed_by(_source: &SourceComponent, ctx: &mut ComponentClo
let event_types = observer_state.descriptor.events.clone();
let components = observer_state.descriptor.components.clone();
for event_type in event_types {
let observers = world.observers.get_observers(event_type);
let observers = world.observers.get_observers_mut(event_type);
if components.is_empty() {
if let Some(map) = observers.entity_observers.get(&source).cloned() {
observers.entity_observers.insert(target, map);
@ -108,8 +108,10 @@ fn component_clone_observed_by(_source: &SourceComponent, ctx: &mut ComponentClo
else {
continue;
};
if let Some(map) = observers.entity_map.get(&source).cloned() {
observers.entity_map.insert(target, map);
if let Some(map) =
observers.entity_component_observers.get(&source).cloned()
{
observers.entity_component_observers.insert(target, map);
}
}
}

View File

@ -515,40 +515,72 @@ impl ObserverTrigger {
}
}
// Map between an observer entity and its runner
type ObserverMap = EntityHashMap<ObserverRunner>;
/// Map between an observer entity and its [`ObserverRunner`]
pub type ObserverMap = EntityHashMap<ObserverRunner>;
/// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular trigger targeted at a specific component.
/// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular event targeted at a specific component.
///
/// This is stored inside of [`CachedObservers`].
#[derive(Default, Debug)]
pub struct CachedComponentObservers {
// Observers listening to triggers targeting this component
map: ObserverMap,
// Observers listening to triggers targeting this component on a specific entity
entity_map: EntityHashMap<ObserverMap>,
// Observers listening to events targeting this component, but not a specific entity
global_observers: ObserverMap,
// Observers listening to events targeting this component on a specific entity
entity_component_observers: EntityHashMap<ObserverMap>,
}
/// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular trigger.
impl CachedComponentObservers {
/// Returns the observers listening for this trigger, regardless of target.
/// These observers will also respond to events targeting specific entities.
pub fn global_observers(&self) -> &ObserverMap {
&self.global_observers
}
/// Returns the observers listening for this trigger targeting this component on a specific entity.
pub fn entity_component_observers(&self) -> &EntityHashMap<ObserverMap> {
&self.entity_component_observers
}
}
/// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular event.
///
/// This is stored inside of [`Observers`], specialized for each kind of observer.
#[derive(Default, Debug)]
pub struct CachedObservers {
// Observers listening for any time this trigger is fired
map: ObserverMap,
// Observers listening for any time this event is fired, regardless of target
// This will also respond to events targeting specific components or entities
global_observers: ObserverMap,
// Observers listening for this trigger fired at a specific component
component_observers: HashMap<ComponentId, CachedComponentObservers>,
// Observers listening for this trigger fired at a specific entity
entity_observers: EntityHashMap<ObserverMap>,
}
impl CachedObservers {
/// Returns the observers listening for this trigger, regardless of target.
/// These observers will also respond to events targeting specific components or entities.
pub fn global_observers(&self) -> &ObserverMap {
&self.global_observers
}
/// Returns the observers listening for this trigger targeting components.
pub fn get_component_observers(&self) -> &HashMap<ComponentId, CachedComponentObservers> {
&self.component_observers
}
/// Returns the observers listening for this trigger targeting entities.
pub fn entity_observers(&self) -> &HashMap<ComponentId, CachedComponentObservers> {
&self.component_observers
}
}
/// An internal lookup table tracking all of the observers in the world.
///
/// Stores a cache mapping trigger ids to the registered observers.
/// Some observer kinds (like [lifecycle](crate::lifecycle) observers) have a dedicated field,
/// saving lookups for the most common triggers.
///
/// This is stored as a field of the [`World`].
/// This can be accessed via [`World::observers`].
#[derive(Default, Debug)]
pub struct Observers {
// Cached ECS observers to save a lookup most common triggers.
@ -562,7 +594,7 @@ pub struct Observers {
}
impl Observers {
pub(crate) fn get_observers(&mut self, event_type: ComponentId) -> &mut CachedObservers {
pub(crate) fn get_observers_mut(&mut self, event_type: ComponentId) -> &mut CachedObservers {
use crate::lifecycle::*;
match event_type {
@ -575,7 +607,11 @@ impl Observers {
}
}
pub(crate) fn try_get_observers(&self, event_type: ComponentId) -> Option<&CachedObservers> {
/// Attempts to get the observers for the given `event_type`.
///
/// When accessing the observers for lifecycle events, such as [`Add`], [`Insert`], [`Replace`], [`Remove`], and [`Despawn`],
/// use the [`ComponentId`] constants from the [`lifecycle`](crate::lifecycle) module.
pub fn try_get_observers(&self, event_type: ComponentId) -> Option<&CachedObservers> {
use crate::lifecycle::*;
match event_type {
@ -630,7 +666,10 @@ impl Observers {
);
};
// Trigger observers listening for any kind of this trigger
observers.map.iter().for_each(&mut trigger_observer);
observers
.global_observers
.iter()
.for_each(&mut trigger_observer);
// Trigger entity observers listening for this kind of trigger
if let Some(target_entity) = current_target {
@ -643,12 +682,15 @@ impl Observers {
trigger_for_components.for_each(|id| {
if let Some(component_observers) = observers.component_observers.get(&id) {
component_observers
.map
.global_observers
.iter()
.for_each(&mut trigger_observer);
if let Some(target_entity) = current_target {
if let Some(map) = component_observers.entity_map.get(&target_entity) {
if let Some(map) = component_observers
.entity_component_observers
.get(&target_entity)
{
map.iter().for_each(&mut trigger_observer);
}
}
@ -926,10 +968,12 @@ impl World {
let descriptor = &observer_state.descriptor;
for &event_type in &descriptor.events {
let cache = observers.get_observers(event_type);
let cache = observers.get_observers_mut(event_type);
if descriptor.components.is_empty() && descriptor.entities.is_empty() {
cache.map.insert(observer_entity, observer_state.runner);
cache
.global_observers
.insert(observer_entity, observer_state.runner);
} else if descriptor.components.is_empty() {
// Observer is not targeting any components so register it as an entity observer
for &watched_entity in &observer_state.descriptor.entities {
@ -951,11 +995,16 @@ impl World {
});
if descriptor.entities.is_empty() {
// Register for all triggers targeting the component
observers.map.insert(observer_entity, observer_state.runner);
observers
.global_observers
.insert(observer_entity, observer_state.runner);
} else {
// Register for each watched entity
for &watched_entity in &descriptor.entities {
let map = observers.entity_map.entry(watched_entity).or_default();
let map = observers
.entity_component_observers
.entry(watched_entity)
.or_default();
map.insert(observer_entity, observer_state.runner);
}
}
@ -970,9 +1019,9 @@ impl World {
let observers = &mut self.observers;
for &event_type in &descriptor.events {
let cache = observers.get_observers(event_type);
let cache = observers.get_observers_mut(event_type);
if descriptor.components.is_empty() && descriptor.entities.is_empty() {
cache.map.remove(&entity);
cache.global_observers.remove(&entity);
} else if descriptor.components.is_empty() {
for watched_entity in &descriptor.entities {
// This check should be unnecessary since this observer hasn't been unregistered yet
@ -990,20 +1039,24 @@ impl World {
continue;
};
if descriptor.entities.is_empty() {
observers.map.remove(&entity);
observers.global_observers.remove(&entity);
} else {
for watched_entity in &descriptor.entities {
let Some(map) = observers.entity_map.get_mut(watched_entity) else {
let Some(map) =
observers.entity_component_observers.get_mut(watched_entity)
else {
continue;
};
map.remove(&entity);
if map.is_empty() {
observers.entity_map.remove(watched_entity);
observers.entity_component_observers.remove(watched_entity);
}
}
}
if observers.map.is_empty() && observers.entity_map.is_empty() {
if observers.global_observers.is_empty()
&& observers.entity_component_observers.is_empty()
{
cache.component_observers.remove(component);
if let Some(flag) = Observers::is_archetype_cached(event_type) {
if let Some(by_component) = archetypes.by_component.get(component) {
@ -1078,7 +1131,7 @@ mod tests {
struct ChildOf(Entity);
impl<D> Traversal<D> for &'_ ChildOf {
fn traverse(item: Self::Item<'_>, _: &D) -> Option<Entity> {
fn traverse(item: Self::Item<'_, '_>, _: &D) -> Option<Entity> {
Some(item.0)
}
}

View File

@ -1,4 +1,5 @@
use alloc::{boxed::Box, vec};
use bevy_utils::prelude::DebugName;
use core::any::Any;
use crate::{
@ -194,7 +195,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
pub struct Observer {
hook_on_add: ComponentHook,
error_handler: Option<ErrorHandler>,
system: Box<dyn Any + Send + Sync + 'static>,
system: Box<dyn AnyNamedSystem>,
pub(crate) descriptor: ObserverDescriptor,
pub(crate) last_trigger_id: u32,
pub(crate) despawned_watched_entities: u32,
@ -232,7 +233,7 @@ impl Observer {
/// Creates a new [`Observer`] with custom runner, this is mostly used for dynamic event observer
pub fn with_dynamic_runner(runner: ObserverRunner) -> Self {
Self {
system: Box::new(|| {}),
system: Box::new(IntoSystem::into_system(|| {})),
descriptor: Default::default(),
hook_on_add: |mut world, hook_context| {
let default_error_handler = world.default_error_handler();
@ -299,6 +300,11 @@ impl Observer {
pub fn descriptor(&self) -> &ObserverDescriptor {
&self.descriptor
}
/// Returns the name of the [`Observer`]'s system .
pub fn system_name(&self) -> DebugName {
self.system.system_name()
}
}
impl Component for Observer {
@ -364,7 +370,8 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
// - observer was triggered so must have an `Observer` component.
// - observer cannot be dropped or mutated until after the system pointer is already dropped.
let system: *mut dyn ObserverSystem<E, B> = unsafe {
let system = state.system.downcast_mut::<S>().debug_checked_unwrap();
let system: &mut dyn Any = state.system.as_mut();
let system = system.downcast_mut::<S>().debug_checked_unwrap();
&mut *system
};
@ -413,6 +420,16 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
}
}
trait AnyNamedSystem: Any + Send + Sync + 'static {
fn system_name(&self) -> DebugName;
}
impl<T: Any + System> AnyNamedSystem for T {
fn system_name(&self) -> DebugName {
self.name()
}
}
/// A [`ComponentHook`] used by [`Observer`] to handle its [`on-add`](`crate::lifecycle::ComponentHooks::on_add`).
///
/// This function exists separate from [`Observer`] to allow [`Observer`] to have its type parameters
@ -431,11 +448,12 @@ fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
B::component_ids(&mut world.components_registrator(), &mut |id| {
components.push(id);
});
if let Some(mut observe) = world.get_mut::<Observer>(entity) {
observe.descriptor.events.push(event_id);
observe.descriptor.components.extend(components);
if let Some(mut observer) = world.get_mut::<Observer>(entity) {
observer.descriptor.events.push(event_id);
observer.descriptor.components.extend(components);
let system: *mut dyn ObserverSystem<E, B> = observe.system.downcast_mut::<S>().unwrap();
let system: &mut dyn Any = observer.system.as_mut();
let system: *mut dyn ObserverSystem<E, B> = system.downcast_mut::<S>().unwrap();
// SAFETY: World reference is exclusive and initialize does not touch system, so references do not alias
unsafe {
(*system).initialize(world);

View File

@ -4,7 +4,6 @@ use crate::world::World;
use alloc::{format, string::String, vec, vec::Vec};
use core::{fmt, fmt::Debug, marker::PhantomData};
use derive_more::From;
use disqualified::ShortName;
use fixedbitset::FixedBitSet;
use thiserror::Error;
@ -999,12 +998,11 @@ impl AccessConflicts {
.map(|index| {
format!(
"{}",
ShortName(
&world
world
.components
.get_name(ComponentId::get_sparse_set_index(index))
.unwrap()
)
.shortname()
)
})
.collect::<Vec<_>>()

View File

@ -1,3 +1,6 @@
use bevy_utils::prelude::DebugName;
use thiserror::Error;
use crate::{
archetype::ArchetypeId,
entity::{ConstructedEntityDoesNotExistError, Entity},
@ -28,10 +31,10 @@ pub enum QueryEntityError {
pub enum QuerySingleError {
/// No entity fits the query.
#[error("No entities fit the query {0}")]
NoEntities(&'static str),
NoEntities(DebugName),
/// Multiple entities fit the query.
#[error("Multiple entities fit the query {0}")]
MultipleEntities(&'static str),
MultipleEntities(DebugName),
}
#[cfg(test)]

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@ use crate::{
world::{unsafe_world_cell::UnsafeWorldCell, World},
};
use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref};
use bevy_utils::prelude::DebugName;
use core::{cell::UnsafeCell, marker::PhantomData};
use variadics_please::all_tuples;
@ -103,6 +104,7 @@ pub unsafe trait QueryFilter: WorldQuery {
/// Must always be called _after_ [`WorldQuery::set_table`] or [`WorldQuery::set_archetype`]. `entity` and
/// `table_row` must be in the range of the current table and archetype.
unsafe fn filter_fetch(
state: &Self::State,
fetch: &mut Self::Fetch<'_>,
entity: Entity,
table_row: TableRow,
@ -204,6 +206,7 @@ unsafe impl<T: Component> QueryFilter for With<T> {
#[inline(always)]
unsafe fn filter_fetch(
_state: &Self::State,
_fetch: &mut Self::Fetch<'_>,
_entity: Entity,
_table_row: TableRow,
@ -304,6 +307,7 @@ unsafe impl<T: Component> QueryFilter for Without<T> {
#[inline(always)]
unsafe fn filter_fetch(
_state: &Self::State,
_fetch: &mut Self::Fetch<'_>,
_entity: Entity,
_table_row: TableRow,
@ -400,7 +404,7 @@ macro_rules! impl_or_query_filter {
const IS_DENSE: bool = true $(&& $filter::IS_DENSE)*;
#[inline]
unsafe fn init_fetch<'w>(world: UnsafeWorldCell<'w>, state: &Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> {
unsafe fn init_fetch<'w, 's>(world: UnsafeWorldCell<'w>, state: &'s Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> {
let ($($filter,)*) = state;
($(OrFetch {
// SAFETY: The invariants are upheld by the caller.
@ -410,7 +414,7 @@ macro_rules! impl_or_query_filter {
}
#[inline]
unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) {
unsafe fn set_table<'w, 's>(fetch: &mut Self::Fetch<'w>, state: &'s Self::State, table: &'w Table) {
let ($($filter,)*) = fetch;
let ($($state,)*) = state;
$(
@ -423,9 +427,9 @@ macro_rules! impl_or_query_filter {
}
#[inline]
unsafe fn set_archetype<'w>(
unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>,
state: & Self::State,
state: &'s Self::State,
archetype: &'w Archetype,
table: &'w Table
) {
@ -495,20 +499,22 @@ macro_rules! impl_or_query_filter {
#[inline(always)]
unsafe fn filter_fetch(
state: &Self::State,
fetch: &mut Self::Fetch<'_>,
entity: Entity,
table_row: TableRow
) -> bool {
let ($($state,)*) = state;
let ($($filter,)*) = fetch;
// SAFETY: The invariants are upheld by the caller.
false $(|| ($filter.matches && unsafe { $filter::filter_fetch(&mut $filter.fetch, entity, table_row) }))*
false $(|| ($filter.matches && unsafe { $filter::filter_fetch($state, &mut $filter.fetch, entity, table_row) }))*
}
}
};
}
macro_rules! impl_tuple_query_filter {
($(#[$meta:meta])* $($name: ident),*) => {
($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => {
#[expect(
clippy::allow_attributes,
reason = "This is a tuple-related macro; as such the lints below may not always apply."
@ -528,13 +534,15 @@ macro_rules! impl_tuple_query_filter {
#[inline(always)]
unsafe fn filter_fetch(
state: &Self::State,
fetch: &mut Self::Fetch<'_>,
entity: Entity,
table_row: TableRow
) -> bool {
let ($($state,)*) = state;
let ($($name,)*) = fetch;
// SAFETY: The invariants are upheld by the caller.
true $(&& unsafe { $name::filter_fetch($name, entity, table_row) })*
true $(&& unsafe { $name::filter_fetch($state, $name, entity, table_row) })*
}
}
@ -546,7 +554,8 @@ all_tuples!(
impl_tuple_query_filter,
0,
15,
F
F,
S
);
all_tuples!(
#[doc(fake_variadic)]
@ -609,7 +618,12 @@ unsafe impl<T: Component> QueryFilter for Allows<T> {
const IS_ARCHETYPAL: bool = true;
#[inline(always)]
unsafe fn filter_fetch(_: &mut Self::Fetch<'_>, _: Entity, _: TableRow) -> bool {
unsafe fn filter_fetch(
_: &Self::State,
_: &mut Self::Fetch<'_>,
_: Entity,
_: TableRow,
) -> bool {
true
}
}
@ -718,9 +732,9 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
}
#[inline]
unsafe fn init_fetch<'w>(
unsafe fn init_fetch<'w, 's>(
world: UnsafeWorldCell<'w>,
&id: &ComponentId,
&id: &'s ComponentId,
last_run: Tick,
this_run: Tick,
) -> Self::Fetch<'w> {
@ -748,9 +762,9 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
};
#[inline]
unsafe fn set_archetype<'w>(
unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>,
component_id: &ComponentId,
component_id: &'s ComponentId,
_archetype: &'w Archetype,
table: &'w Table,
) {
@ -763,9 +777,9 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
}
#[inline]
unsafe fn set_table<'w>(
unsafe fn set_table<'w, 's>(
fetch: &mut Self::Fetch<'w>,
&component_id: &ComponentId,
&component_id: &'s ComponentId,
table: &'w Table,
) {
let table_ticks = Some(
@ -781,7 +795,7 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
#[inline]
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
if access.access().has_component_write(id) {
panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",core::any::type_name::<T>());
panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", DebugName::type_name::<T>());
}
access.add_component_read(id);
}
@ -807,6 +821,7 @@ unsafe impl<T: Component> QueryFilter for Added<T> {
const IS_ARCHETYPAL: bool = false;
#[inline(always)]
unsafe fn filter_fetch(
_state: &Self::State,
fetch: &mut Self::Fetch<'_>,
entity: Entity,
table_row: TableRow,
@ -944,9 +959,9 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
}
#[inline]
unsafe fn init_fetch<'w>(
unsafe fn init_fetch<'w, 's>(
world: UnsafeWorldCell<'w>,
&id: &ComponentId,
&id: &'s ComponentId,
last_run: Tick,
this_run: Tick,
) -> Self::Fetch<'w> {
@ -974,9 +989,9 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
};
#[inline]
unsafe fn set_archetype<'w>(
unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>,
component_id: &ComponentId,
component_id: &'s ComponentId,
_archetype: &'w Archetype,
table: &'w Table,
) {
@ -989,9 +1004,9 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
}
#[inline]
unsafe fn set_table<'w>(
unsafe fn set_table<'w, 's>(
fetch: &mut Self::Fetch<'w>,
&component_id: &ComponentId,
&component_id: &'s ComponentId,
table: &'w Table,
) {
let table_ticks = Some(
@ -1007,7 +1022,7 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
#[inline]
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
if access.access().has_component_write(id) {
panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",core::any::type_name::<T>());
panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", DebugName::type_name::<T>());
}
access.add_component_read(id);
}
@ -1034,6 +1049,7 @@ unsafe impl<T: Component> QueryFilter for Changed<T> {
#[inline(always)]
unsafe fn filter_fetch(
_state: &Self::State,
fetch: &mut Self::Fetch<'_>,
entity: Entity,
table_row: TableRow,
@ -1141,9 +1157,9 @@ unsafe impl WorldQuery for Spawned {
}
#[inline]
unsafe fn init_fetch<'w>(
unsafe fn init_fetch<'w, 's>(
world: UnsafeWorldCell<'w>,
_state: &(),
_state: &'s (),
last_run: Tick,
this_run: Tick,
) -> Self::Fetch<'w> {
@ -1157,16 +1173,16 @@ unsafe impl WorldQuery for Spawned {
const IS_DENSE: bool = true;
#[inline]
unsafe fn set_archetype<'w>(
unsafe fn set_archetype<'w, 's>(
_fetch: &mut Self::Fetch<'w>,
_state: &(),
_state: &'s (),
_archetype: &'w Archetype,
_table: &'w Table,
) {
}
#[inline]
unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &(), _table: &'w Table) {}
unsafe fn set_table<'w, 's>(_fetch: &mut Self::Fetch<'w>, _state: &'s (), _table: &'w Table) {}
#[inline]
fn update_component_access(_state: &(), _access: &mut FilteredAccess<ComponentId>) {}
@ -1188,6 +1204,7 @@ unsafe impl QueryFilter for Spawned {
#[inline(always)]
unsafe fn filter_fetch(
_state: &Self::State,
fetch: &mut Self::Fetch<'_>,
entity: Entity,
_table_row: TableRow,

View File

@ -140,7 +140,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
range: Option<Range<u32>>,
) -> B
where
Func: FnMut(B, D::Item<'w>) -> B,
Func: FnMut(B, D::Item<'w, 's>) -> B,
{
if self.cursor.is_dense {
// SAFETY: `self.cursor.is_dense` is true, so storage ids are guaranteed to be table ids.
@ -203,7 +203,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
rows: Range<u32>,
) -> B
where
Func: FnMut(B, D::Item<'w>) -> B,
Func: FnMut(B, D::Item<'w, 's>) -> B,
{
if table.is_empty() {
return accum;
@ -225,14 +225,26 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
// SAFETY: set_table was called prior.
// Caller assures `row` in range of the current archetype.
let fetched = unsafe { !F::filter_fetch(&mut self.cursor.filter, *entity, row) };
let fetched = unsafe {
!F::filter_fetch(
&self.query_state.filter_state,
&mut self.cursor.filter,
*entity,
row,
)
};
if fetched {
continue;
}
// SAFETY: set_table was called prior.
// Caller assures `row` in range of the current archetype.
let item = D::fetch(&mut self.cursor.fetch, *entity, row);
let item = D::fetch(
&self.query_state.fetch_state,
&mut self.cursor.fetch,
*entity,
row,
);
accum = func(accum, item);
}
@ -255,7 +267,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
indices: Range<u32>,
) -> B
where
Func: FnMut(B, D::Item<'w>) -> B,
Func: FnMut(B, D::Item<'w, 's>) -> B,
{
if archetype.is_empty() {
return accum;
@ -283,6 +295,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
// Caller assures `index` in range of the current archetype.
let fetched = unsafe {
!F::filter_fetch(
&self.query_state.filter_state,
&mut self.cursor.filter,
archetype_entity.id(),
archetype_entity.table_row(),
@ -296,6 +309,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
// Caller assures `index` in range of the current archetype.
let item = unsafe {
D::fetch(
&self.query_state.fetch_state,
&mut self.cursor.fetch,
archetype_entity.id(),
archetype_entity.table_row(),
@ -324,7 +338,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
rows: Range<u32>,
) -> B
where
Func: FnMut(B, D::Item<'w>) -> B,
Func: FnMut(B, D::Item<'w, 's>) -> B,
{
if archetype.is_empty() {
return accum;
@ -356,14 +370,26 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
// SAFETY: set_table was called prior.
// Caller assures `row` in range of the current archetype.
let filter_matched = unsafe { F::filter_fetch(&mut self.cursor.filter, entity, row) };
let filter_matched = unsafe {
F::filter_fetch(
&self.query_state.filter_state,
&mut self.cursor.filter,
entity,
row,
)
};
if !filter_matched {
continue;
}
// SAFETY: set_table was called prior.
// Caller assures `row` in range of the current archetype.
let item = D::fetch(&mut self.cursor.fetch, entity, row);
let item = D::fetch(
&self.query_state.fetch_state,
&mut self.cursor.fetch,
entity,
row,
);
accum = func(accum, item);
}
@ -492,7 +518,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
for<'lw> L::Item<'lw>: Ord,
for<'lw, 'ls> L::Item<'lw, 'ls>: Ord,
{
self.sort_impl::<L>(|keyed_query| keyed_query.sort())
}
@ -549,7 +575,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
for<'lw> L::Item<'lw>: Ord,
for<'lw, 'ls> L::Item<'lw, 'ls>: Ord,
{
self.sort_impl::<L>(|keyed_query| keyed_query.sort_unstable())
}
@ -605,7 +631,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
/// ```
pub fn sort_by<L: ReadOnlyQueryData + 'w>(
self,
mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering,
mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering,
) -> QuerySortedIter<
'w,
's,
@ -637,7 +663,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
/// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty.
pub fn sort_unstable_by<L: ReadOnlyQueryData + 'w>(
self,
mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering,
mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering,
) -> QuerySortedIter<
'w,
's,
@ -729,7 +755,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
/// ```
pub fn sort_by_key<L: ReadOnlyQueryData + 'w, K>(
self,
mut f: impl FnMut(&L::Item<'_>) -> K,
mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedIter<
'w,
's,
@ -762,7 +788,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
/// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty.
pub fn sort_unstable_by_key<L: ReadOnlyQueryData + 'w, K>(
self,
mut f: impl FnMut(&L::Item<'_>) -> K,
mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedIter<
'w,
's,
@ -797,7 +823,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
/// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty.
pub fn sort_by_cached_key<L: ReadOnlyQueryData + 'w, K>(
self,
mut f: impl FnMut(&L::Item<'_>) -> K,
mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedIter<
'w,
's,
@ -827,7 +853,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
/// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty.
fn sort_impl<L: ReadOnlyQueryData + 'w>(
self,
f: impl FnOnce(&mut Vec<(L::Item<'_>, NeutralOrd<Entity>)>),
f: impl FnOnce(&mut Vec<(L::Item<'_, '_>, NeutralOrd<Entity>)>),
) -> QuerySortedIter<
'w,
's,
@ -856,7 +882,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
.map(|(key, entity)| (key, NeutralOrd(entity)))
.collect();
f(&mut keyed_query);
let entity_iter = keyed_query.into_iter().map(|(.., entity)| entity.0);
let entity_iter = keyed_query
.into_iter()
.map(|(.., entity)| entity.0)
.collect::<Vec<_>>()
.into_iter();
// SAFETY:
// `self.world` has permission to access the required components.
// Each lens query item is dropped before the respective actual query item is accessed.
@ -873,7 +903,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
}
impl<'w, 's, D: QueryData, F: QueryFilter> Iterator for QueryIter<'w, 's, D, F> {
type Item = D::Item<'w>;
type Item = D::Item<'w, 's>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
@ -1010,7 +1040,7 @@ where
/// # Safety
/// `entity` must stem from `self.entity_iter`, and not have been passed before.
#[inline(always)]
unsafe fn fetch_next(&mut self, entity: Entity) -> D::Item<'w> {
unsafe fn fetch_next(&mut self, entity: Entity) -> D::Item<'w, 's> {
let (location, archetype, table);
// SAFETY:
// `tables` and `archetypes` belong to the same world that the [`QueryIter`]
@ -1039,7 +1069,14 @@ where
// SAFETY:
// - set_archetype was called prior, `location.archetype_row` is an archetype index in range of the current archetype
// - fetch is only called once for each entity.
unsafe { D::fetch(&mut self.fetch, entity, location.table_row) }
unsafe {
D::fetch(
&self.query_state.fetch_state,
&mut self.fetch,
entity,
location.table_row,
)
}
}
}
@ -1048,7 +1085,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> Iterator
where
I: Iterator<Item = Entity>,
{
type Item = D::Item<'w>;
type Item = D::Item<'w, 's>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
@ -1170,7 +1207,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
fetch: &mut D::Fetch<'w>,
filter: &mut F::Fetch<'w>,
query_state: &'s QueryState<D, F>,
) -> Option<D::Item<'w>> {
) -> Option<D::Item<'w, 's>> {
for entity_borrow in entity_iter {
let entity = entity_borrow.entity();
let Ok(location) = entities.get_constructed(entity) else {
@ -1200,11 +1237,20 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
// SAFETY: set_archetype was called prior.
// `location.archetype_row` is an archetype index row in range of the current archetype, because if it was not, the match above would have `continue`d
if unsafe { F::filter_fetch(filter, entity, location.table_row) } {
if unsafe {
F::filter_fetch(
&query_state.filter_state,
filter,
entity,
location.table_row,
)
} {
// SAFETY:
// - set_archetype was called prior, `location.archetype_row` is an archetype index in range of the current archetype
// - fetch is only called once for each entity.
return Some(unsafe { D::fetch(fetch, entity, location.table_row) });
return Some(unsafe {
D::fetch(&query_state.fetch_state, fetch, entity, location.table_row)
});
}
}
None
@ -1212,7 +1258,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
/// Get next result from the query
#[inline(always)]
pub fn fetch_next(&mut self) -> Option<D::Item<'_>> {
pub fn fetch_next(&mut self) -> Option<D::Item<'_, 's>> {
// SAFETY:
// All arguments stem from self.
// We are limiting the returned reference to self,
@ -1336,7 +1382,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
for<'lw> L::Item<'lw>: Ord,
for<'lw, 'ls> L::Item<'lw, 'ls>: Ord,
{
self.sort_impl::<L>(|keyed_query| keyed_query.sort())
}
@ -1394,7 +1440,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
for<'lw> L::Item<'lw>: Ord,
for<'lw, 'ls> L::Item<'lw, 'ls>: Ord,
{
self.sort_impl::<L>(|keyed_query| keyed_query.sort_unstable())
}
@ -1451,7 +1497,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
/// ```
pub fn sort_by<L: ReadOnlyQueryData + 'w>(
self,
mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering,
mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering,
) -> QuerySortedManyIter<
'w,
's,
@ -1482,7 +1528,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
/// called on [`QueryManyIter`] before.
pub fn sort_unstable_by<L: ReadOnlyQueryData + 'w>(
self,
mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering,
mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering,
) -> QuerySortedManyIter<
'w,
's,
@ -1576,7 +1622,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
/// ```
pub fn sort_by_key<L: ReadOnlyQueryData + 'w, K>(
self,
mut f: impl FnMut(&L::Item<'_>) -> K,
mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedManyIter<
'w,
's,
@ -1608,7 +1654,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
/// called on [`QueryManyIter`] before.
pub fn sort_unstable_by_key<L: ReadOnlyQueryData + 'w, K>(
self,
mut f: impl FnMut(&L::Item<'_>) -> K,
mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedManyIter<
'w,
's,
@ -1642,7 +1688,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
/// called on [`QueryManyIter`] before.
pub fn sort_by_cached_key<L: ReadOnlyQueryData + 'w, K>(
self,
mut f: impl FnMut(&L::Item<'_>) -> K,
mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedManyIter<
'w,
's,
@ -1671,7 +1717,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
/// called on [`QueryManyIter`] before.
fn sort_impl<L: ReadOnlyQueryData + 'w>(
self,
f: impl FnOnce(&mut Vec<(L::Item<'_>, NeutralOrd<Entity>)>),
f: impl FnOnce(&mut Vec<(L::Item<'_, '_>, NeutralOrd<Entity>)>),
) -> QuerySortedManyIter<
'w,
's,
@ -1721,7 +1767,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator<Item: EntityEq
{
/// Get next result from the back of the query
#[inline(always)]
pub fn fetch_next_back(&mut self) -> Option<D::Item<'_>> {
pub fn fetch_next_back(&mut self) -> Option<D::Item<'_, 's>> {
// SAFETY:
// All arguments stem from self.
// We are limiting the returned reference to self,
@ -1745,7 +1791,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator<Item: EntityEq
impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>> Iterator
for QueryManyIter<'w, 's, D, F, I>
{
type Item = D::Item<'w>;
type Item = D::Item<'w, 's>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
@ -1861,7 +1907,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator>
impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> Iterator
for QueryManyUniqueIter<'w, 's, D, F, I>
{
type Item = D::Item<'w>;
type Item = D::Item<'w, 's>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
@ -1954,7 +2000,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item = Entity>>
/// It is always safe for shared access.
/// `entity` must stem from `self.entity_iter`, and not have been passed before.
#[inline(always)]
unsafe fn fetch_next_aliased_unchecked(&mut self, entity: Entity) -> D::Item<'w> {
unsafe fn fetch_next_aliased_unchecked(&mut self, entity: Entity) -> D::Item<'w, 's> {
let (location, archetype, table);
// SAFETY:
// `tables` and `archetypes` belong to the same world that the [`QueryIter`]
@ -1983,12 +2029,19 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item = Entity>>
// SAFETY:
// - set_archetype was called prior, `location.archetype_row` is an archetype index in range of the current archetype
// - fetch is only called once for each entity.
unsafe { D::fetch(&mut self.fetch, entity, location.table_row) }
unsafe {
D::fetch(
&self.query_state.fetch_state,
&mut self.fetch,
entity,
location.table_row,
)
}
}
/// Get next result from the query
#[inline(always)]
pub fn fetch_next(&mut self) -> Option<D::Item<'_>> {
pub fn fetch_next(&mut self) -> Option<D::Item<'_, 's>> {
let entity = self.entity_iter.next()?;
// SAFETY:
@ -2007,7 +2060,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator<Item = Entity>
{
/// Get next result from the query
#[inline(always)]
pub fn fetch_next_back(&mut self) -> Option<D::Item<'_>> {
pub fn fetch_next_back(&mut self) -> Option<D::Item<'_, 's>> {
let entity = self.entity_iter.next_back()?;
// SAFETY:
@ -2024,7 +2077,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator<Item = Entity>
impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: Iterator<Item = Entity>> Iterator
for QuerySortedManyIter<'w, 's, D, F, I>
{
type Item = D::Item<'w>;
type Item = D::Item<'w, 's>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
@ -2185,7 +2238,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter<
/// .
/// It is always safe for shared access.
#[inline]
unsafe fn fetch_next_aliased_unchecked(&mut self) -> Option<[D::Item<'w>; K]> {
unsafe fn fetch_next_aliased_unchecked(&mut self) -> Option<[D::Item<'w, 's>; K]> {
// PERF: can speed up the following code using `cursor.remaining()` instead of `next_item.is_none()`
// when D::IS_ARCHETYPAL && F::IS_ARCHETYPAL
//
@ -2211,11 +2264,12 @@ impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter<
}
}
let mut values = MaybeUninit::<[D::Item<'w>; K]>::uninit();
let mut values = MaybeUninit::<[D::Item<'w, 's>; K]>::uninit();
let ptr = values.as_mut_ptr().cast::<D::Item<'w>>();
let ptr = values.as_mut_ptr().cast::<D::Item<'w, 's>>();
for (offset, cursor) in self.cursors.iter_mut().enumerate() {
ptr.add(offset).write(cursor.peek_last().unwrap());
ptr.add(offset)
.write(cursor.peek_last(self.query_state).unwrap());
}
Some(values.assume_init())
@ -2223,7 +2277,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter<
/// Get next combination of queried components
#[inline]
pub fn fetch_next(&mut self) -> Option<[D::Item<'_>; K]> {
pub fn fetch_next(&mut self) -> Option<[D::Item<'_, 's>; K]> {
// SAFETY: we are limiting the returned reference to self,
// making sure this method cannot be called multiple times without getting rid
// of any previously returned unique references first, thus preventing aliasing.
@ -2240,7 +2294,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter<
impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, const K: usize> Iterator
for QueryCombinationIter<'w, 's, D, F, K>
{
type Item = [D::Item<'w>; K];
type Item = [D::Item<'w, 's>; K];
#[inline]
fn next(&mut self) -> Option<Self::Item> {
@ -2390,7 +2444,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
/// The result of `next` and any previous calls to `peek_last` with this row must have been
/// dropped to prevent aliasing mutable references.
#[inline]
unsafe fn peek_last(&mut self) -> Option<D::Item<'w>> {
unsafe fn peek_last(&mut self, query_state: &'s QueryState<D, F>) -> Option<D::Item<'w, 's>> {
if self.current_row > 0 {
let index = self.current_row - 1;
if self.is_dense {
@ -2401,6 +2455,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// - `*entity` and `index` are in the current table.
unsafe {
Some(D::fetch(
&query_state.fetch_state,
&mut self.fetch,
*entity,
// SAFETY: This is from an exclusive range, so it can't be max.
@ -2416,6 +2471,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// - `archetype_entity.id()` and `archetype_entity.table_row()` are in the current archetype.
unsafe {
Some(D::fetch(
&query_state.fetch_state,
&mut self.fetch,
archetype_entity.id(),
archetype_entity.table_row(),
@ -2457,7 +2513,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
tables: &'w Tables,
archetypes: &'w Archetypes,
query_state: &'s QueryState<D, F>,
) -> Option<D::Item<'w>> {
) -> Option<D::Item<'w, 's>> {
if self.is_dense {
loop {
// we are on the beginning of the query, or finished processing a table, so skip to the next
@ -2484,7 +2540,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
unsafe { self.table_entities.get_unchecked(self.current_row as usize) };
// SAFETY: The row is less than the u32 len, so it must not be max.
let row = unsafe { TableRow::new(NonMaxU32::new_unchecked(self.current_row)) };
if !F::filter_fetch(&mut self.filter, *entity, row) {
if !F::filter_fetch(&query_state.filter_state, &mut self.filter, *entity, row) {
self.current_row += 1;
continue;
}
@ -2494,7 +2550,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// - `current_row` must be a table row in range of the current table,
// because if it was not, then the above would have been executed.
// - fetch is only called once for each `entity`.
let item = unsafe { D::fetch(&mut self.fetch, *entity, row) };
let item =
unsafe { D::fetch(&query_state.fetch_state, &mut self.fetch, *entity, row) };
self.current_row += 1;
return Some(item);
@ -2536,6 +2593,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
.get_unchecked(self.current_row as usize)
};
if !F::filter_fetch(
&query_state.filter_state,
&mut self.filter,
archetype_entity.id(),
archetype_entity.table_row(),
@ -2551,6 +2609,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// - fetch is only called once for each `archetype_entity`.
let item = unsafe {
D::fetch(
&query_state.fetch_state,
&mut self.fetch,
archetype_entity.id(),
archetype_entity.table_row(),

View File

@ -824,9 +824,9 @@ mod tests {
fn shrink_fetch<'wlong: 'wshort, 'wshort>(_: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {}
unsafe fn init_fetch<'w>(
unsafe fn init_fetch<'w, 's>(
_world: UnsafeWorldCell<'w>,
_state: &Self::State,
_state: &'s Self::State,
_last_run: Tick,
_this_run: Tick,
) -> Self::Fetch<'w> {
@ -835,18 +835,18 @@ mod tests {
const IS_DENSE: bool = true;
#[inline]
unsafe fn set_archetype<'w>(
unsafe fn set_archetype<'w, 's>(
_fetch: &mut Self::Fetch<'w>,
_state: &Self::State,
_state: &'s Self::State,
_archetype: &'w Archetype,
_table: &Table,
) {
}
#[inline]
unsafe fn set_table<'w>(
unsafe fn set_table<'w, 's>(
_fetch: &mut Self::Fetch<'w>,
_state: &Self::State,
_state: &'s Self::State,
_table: &'w Table,
) {
}
@ -882,16 +882,20 @@ mod tests {
unsafe impl QueryData for ReadsRData {
const IS_READ_ONLY: bool = true;
type ReadOnly = Self;
type Item<'w> = ();
type Item<'w, 's> = ();
fn shrink<'wlong: 'wshort, 'wshort>(_item: Self::Item<'wlong>) -> Self::Item<'wshort> {}
fn shrink<'wlong: 'wshort, 'wshort, 's>(
_item: Self::Item<'wlong, 's>,
) -> Self::Item<'wshort, 's> {
}
#[inline(always)]
unsafe fn fetch<'w>(
unsafe fn fetch<'w, 's>(
_state: &'s Self::State,
_fetch: &mut Self::Fetch<'w>,
_entity: Entity,
_table_row: TableRow,
) -> Self::Item<'w> {
) -> Self::Item<'w, 's> {
}
}

View File

@ -39,7 +39,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryParIter<'w, 's, D, F> {
///
/// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
#[inline]
pub fn for_each<FN: Fn(QueryItem<'w, D>) + Send + Sync + Clone>(self, func: FN) {
pub fn for_each<FN: Fn(QueryItem<'w, 's, D>) + Send + Sync + Clone>(self, func: FN) {
self.for_each_init(|| {}, |_, item| func(item));
}
@ -76,7 +76,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryParIter<'w, 's, D, F> {
#[inline]
pub fn for_each_init<FN, INIT, T>(self, init: INIT, func: FN)
where
FN: Fn(&mut T, QueryItem<'w, D>) + Send + Sync + Clone,
FN: Fn(&mut T, QueryItem<'w, 's, D>) + Send + Sync + Clone,
INIT: Fn() -> T + Sync + Send + Clone,
{
let func = |mut init, item| {
@ -190,7 +190,7 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, E: EntityEquivalent + Sync>
///
/// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
#[inline]
pub fn for_each<FN: Fn(QueryItem<'w, D>) + Send + Sync + Clone>(self, func: FN) {
pub fn for_each<FN: Fn(QueryItem<'w, 's, D>) + Send + Sync + Clone>(self, func: FN) {
self.for_each_init(|| {}, |_, item| func(item));
}
@ -247,7 +247,7 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, E: EntityEquivalent + Sync>
#[inline]
pub fn for_each_init<FN, INIT, T>(self, init: INIT, func: FN)
where
FN: Fn(&mut T, QueryItem<'w, D>) + Send + Sync + Clone,
FN: Fn(&mut T, QueryItem<'w, 's, D>) + Send + Sync + Clone,
INIT: Fn() -> T + Sync + Send + Clone,
{
let func = |mut init, item| {
@ -345,7 +345,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, E: EntityEquivalent + Sync>
///
/// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
#[inline]
pub fn for_each<FN: Fn(QueryItem<'w, D>) + Send + Sync + Clone>(self, func: FN) {
pub fn for_each<FN: Fn(QueryItem<'w, 's, D>) + Send + Sync + Clone>(self, func: FN) {
self.for_each_init(|| {}, |_, item| func(item));
}
@ -402,7 +402,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, E: EntityEquivalent + Sync>
#[inline]
pub fn for_each_init<FN, INIT, T>(self, init: INIT, func: FN)
where
FN: Fn(&mut T, QueryItem<'w, D>) + Send + Sync + Clone,
FN: Fn(&mut T, QueryItem<'w, 's, D>) + Send + Sync + Clone,
INIT: Fn() -> T + Sync + Send + Clone,
{
let func = |mut init, item| {

View File

@ -14,6 +14,7 @@ use crate::{
use crate::entity::UniqueEntityEquivalentSlice;
use alloc::vec::Vec;
use bevy_utils::prelude::DebugName;
use core::{fmt, ptr};
use fixedbitset::FixedBitSet;
use log::warn;
@ -672,7 +673,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
assert!(
component_access.is_subset(&self_access),
"Transmuted state for {} attempts to access terms that are not allowed by original state {}.",
core::any::type_name::<(NewD, NewF)>(), core::any::type_name::<(D, F)>()
DebugName::type_name::<(NewD, NewF)>(), DebugName::type_name::<(D, F)>()
);
QueryState {
@ -791,7 +792,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
assert!(
component_access.is_subset(&joined_component_access),
"Joined state for {} attempts to access terms that are not allowed by state {} joined with {}.",
core::any::type_name::<(NewD, NewF)>(), core::any::type_name::<(D, F)>(), core::any::type_name::<(OtherD, OtherF)>()
DebugName::type_name::<(NewD, NewF)>(), DebugName::type_name::<(D, F)>(), DebugName::type_name::<(OtherD, OtherF)>()
);
if self.archetype_generation != other.archetype_generation {
@ -845,13 +846,17 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
///
/// This can only be called for read-only queries, see [`Self::get_mut`] for write-queries.
///
/// If you need to get multiple items at once but get borrowing errors,
/// consider using [`Self::update_archetypes`] followed by multiple [`Self::get_manual`] calls,
/// or making a single call with [`Self::get_many`] or [`Self::iter_many`].
///
/// This is always guaranteed to run in `O(1)` time.
#[inline]
pub fn get<'w>(
&mut self,
world: &'w World,
entity: Entity,
) -> Result<ROQueryItem<'w, D>, QueryEntityError> {
) -> Result<ROQueryItem<'w, '_, D>, QueryEntityError> {
self.query(world).get_inner(entity)
}
@ -892,7 +897,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&mut self,
world: &'w World,
entities: [Entity; N],
) -> Result<[ROQueryItem<'w, D>; N], QueryEntityError> {
) -> Result<[ROQueryItem<'w, '_, D>; N], QueryEntityError> {
self.query(world).get_many_inner(entities)
}
@ -930,7 +935,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&mut self,
world: &'w World,
entities: UniqueEntityArray<N>,
) -> Result<[ROQueryItem<'w, D>; N], QueryEntityError> {
) -> Result<[ROQueryItem<'w, '_, D>; N], QueryEntityError> {
self.query(world).get_many_unique_inner(entities)
}
@ -942,7 +947,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&mut self,
world: &'w mut World,
entity: Entity,
) -> Result<D::Item<'w>, QueryEntityError> {
) -> Result<D::Item<'w, '_>, QueryEntityError> {
self.query_mut(world).get_inner(entity)
}
@ -989,7 +994,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&mut self,
world: &'w mut World,
entities: [Entity; N],
) -> Result<[D::Item<'w>; N], QueryEntityError> {
) -> Result<[D::Item<'w, '_>; N], QueryEntityError> {
self.query_mut(world).get_many_mut_inner(entities)
}
@ -1034,7 +1039,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&mut self,
world: &'w mut World,
entities: UniqueEntityArray<N>,
) -> Result<[D::Item<'w>; N], QueryEntityError> {
) -> Result<[D::Item<'w, '_>; N], QueryEntityError> {
self.query_mut(world).get_many_unique_inner(entities)
}
@ -1056,7 +1061,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&self,
world: &'w World,
entity: Entity,
) -> Result<ROQueryItem<'w, D>, QueryEntityError> {
) -> Result<ROQueryItem<'w, '_, D>, QueryEntityError> {
self.query_manual(world).get_inner(entity)
}
@ -1073,13 +1078,16 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&mut self,
world: UnsafeWorldCell<'w>,
entity: Entity,
) -> Result<D::Item<'w>, QueryEntityError> {
) -> Result<D::Item<'w, '_>, QueryEntityError> {
self.query_unchecked(world).get_inner(entity)
}
/// 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.
///
/// If you need to iterate multiple times at once but get borrowing errors,
/// consider using [`Self::update_archetypes`] followed by multiple [`Self::iter_manual`] calls.
#[inline]
pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, D::ReadOnly, F> {
self.query(world).into_iter()
@ -1168,6 +1176,9 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
/// Items are returned in the order of the list of entities.
/// Entities that don't match the query are skipped.
///
/// If you need to iterate multiple times at once but get borrowing errors,
/// consider using [`Self::update_archetypes`] followed by multiple [`Self::iter_many_manual`] calls.
///
/// # See also
///
/// - [`iter_many_mut`](Self::iter_many_mut) to get mutable query items.
@ -1387,8 +1398,8 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
///
/// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
#[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))]
pub(crate) unsafe fn par_fold_init_unchecked_manual<'w, T, FN, INIT>(
&self,
pub(crate) unsafe fn par_fold_init_unchecked_manual<'w, 's, T, FN, INIT>(
&'s self,
init_accum: INIT,
world: UnsafeWorldCell<'w>,
batch_size: u32,
@ -1396,7 +1407,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
last_run: Tick,
this_run: Tick,
) where
FN: Fn(T, D::Item<'w>) -> T + Send + Sync + Clone,
FN: Fn(T, D::Item<'w, 's>) -> T + Send + Sync + Clone,
INIT: Fn() -> T + Sync + Send + Clone,
{
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
@ -1501,8 +1512,8 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
///
/// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
#[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))]
pub(crate) unsafe fn par_many_unique_fold_init_unchecked_manual<'w, T, FN, INIT, E>(
&self,
pub(crate) unsafe fn par_many_unique_fold_init_unchecked_manual<'w, 's, T, FN, INIT, E>(
&'s self,
init_accum: INIT,
world: UnsafeWorldCell<'w>,
entity_list: &UniqueEntityEquivalentSlice<E>,
@ -1511,7 +1522,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
last_run: Tick,
this_run: Tick,
) where
FN: Fn(T, D::Item<'w>) -> T + Send + Sync + Clone,
FN: Fn(T, D::Item<'w, 's>) -> T + Send + Sync + Clone,
INIT: Fn() -> T + Sync + Send + Clone,
E: EntityEquivalent + Sync,
{
@ -1564,8 +1575,8 @@ impl<D: ReadOnlyQueryData, F: QueryFilter> QueryState<D, F> {
///
/// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
#[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))]
pub(crate) unsafe fn par_many_fold_init_unchecked_manual<'w, T, FN, INIT, E>(
&self,
pub(crate) unsafe fn par_many_fold_init_unchecked_manual<'w, 's, T, FN, INIT, E>(
&'s self,
init_accum: INIT,
world: UnsafeWorldCell<'w>,
entity_list: &[E],
@ -1574,7 +1585,7 @@ impl<D: ReadOnlyQueryData, F: QueryFilter> QueryState<D, F> {
last_run: Tick,
this_run: Tick,
) where
FN: Fn(T, D::Item<'w>) -> T + Send + Sync + Clone,
FN: Fn(T, D::Item<'w, 's>) -> T + Send + Sync + Clone,
INIT: Fn() -> T + Sync + Send + Clone,
E: EntityEquivalent + Sync,
{
@ -1686,7 +1697,10 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
///
/// Simply unwrapping the [`Result`] also works, but should generally be reserved for tests.
#[inline]
pub fn single<'w>(&mut self, world: &'w World) -> Result<ROQueryItem<'w, D>, QuerySingleError> {
pub fn single<'w>(
&mut self,
world: &'w World,
) -> Result<ROQueryItem<'w, '_, D>, QuerySingleError> {
self.query(world).single_inner()
}
@ -1703,7 +1717,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
pub fn single_mut<'w>(
&mut self,
world: &'w mut World,
) -> Result<D::Item<'w>, QuerySingleError> {
) -> Result<D::Item<'w, '_>, QuerySingleError> {
self.query_mut(world).single_inner()
}
@ -1720,7 +1734,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
pub unsafe fn single_unchecked<'w>(
&mut self,
world: UnsafeWorldCell<'w>,
) -> Result<D::Item<'w>, QuerySingleError> {
) -> Result<D::Item<'w, '_>, QuerySingleError> {
self.query_unchecked(world).single_inner()
}
@ -1742,7 +1756,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
world: UnsafeWorldCell<'w>,
last_run: Tick,
this_run: Tick,
) -> Result<D::Item<'w>, QuerySingleError> {
) -> Result<D::Item<'w, '_>, QuerySingleError> {
// SAFETY:
// - The caller ensured we have the correct access to the world.
// - The caller ensured that the world matches.

View File

@ -42,7 +42,7 @@ use variadics_please::all_tuples;
/// [`QueryFilter`]: crate::query::QueryFilter
pub unsafe trait WorldQuery {
/// Per archetype/table state retrieved by this [`WorldQuery`] to compute [`Self::Item`](crate::query::QueryData::Item) for each entity.
type Fetch<'a>: Clone;
type Fetch<'w>: Clone;
/// State used to construct a [`Self::Fetch`](WorldQuery::Fetch). This will be cached inside [`QueryState`](crate::query::QueryState),
/// so it is best to move as much data / computation here as possible to reduce the cost of
@ -62,9 +62,9 @@ pub unsafe trait WorldQuery {
/// in to this function.
/// - `world` must have the **right** to access any access registered in `update_component_access`.
/// - There must not be simultaneous resource access conflicting with readonly resource access registered in [`WorldQuery::update_component_access`].
unsafe fn init_fetch<'w>(
unsafe fn init_fetch<'w, 's>(
world: UnsafeWorldCell<'w>,
state: &Self::State,
state: &'s Self::State,
last_run: Tick,
this_run: Tick,
) -> Self::Fetch<'w>;
@ -87,9 +87,9 @@ pub unsafe trait WorldQuery {
/// - `archetype` and `tables` must be from the same [`World`] that [`WorldQuery::init_state`] was called on.
/// - `table` must correspond to `archetype`.
/// - `state` must be the [`State`](Self::State) that `fetch` was initialized with.
unsafe fn set_archetype<'w>(
unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>,
state: &Self::State,
state: &'s Self::State,
archetype: &'w Archetype,
table: &'w Table,
);
@ -101,7 +101,11 @@ pub unsafe trait WorldQuery {
///
/// - `table` must be from the same [`World`] that [`WorldQuery::init_state`] was called on.
/// - `state` must be the [`State`](Self::State) that `fetch` was initialized with.
unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table);
unsafe fn set_table<'w, 's>(
fetch: &mut Self::Fetch<'w>,
state: &'s Self::State,
table: &'w Table,
);
/// Adds any component accesses used by this [`WorldQuery`] to `access`.
///
@ -166,7 +170,7 @@ macro_rules! impl_tuple_world_query {
}
#[inline]
unsafe fn init_fetch<'w>(world: UnsafeWorldCell<'w>, state: &Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> {
unsafe fn init_fetch<'w, 's>(world: UnsafeWorldCell<'w>, state: &'s Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> {
let ($($name,)*) = state;
// SAFETY: The invariants are upheld by the caller.
($(unsafe { $name::init_fetch(world, $name, last_run, this_run) },)*)
@ -175,9 +179,9 @@ macro_rules! impl_tuple_world_query {
const IS_DENSE: bool = true $(&& $name::IS_DENSE)*;
#[inline]
unsafe fn set_archetype<'w>(
unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>,
state: &Self::State,
state: &'s Self::State,
archetype: &'w Archetype,
table: &'w Table
) {
@ -188,7 +192,7 @@ macro_rules! impl_tuple_world_query {
}
#[inline]
unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) {
unsafe fn set_table<'w, 's>(fetch: &mut Self::Fetch<'w>, state: &'s Self::State, table: &'w Table) {
let ($($name,)*) = fetch;
let ($($state,)*) = state;
// SAFETY: The invariants are upheld by the caller.

View File

@ -5,6 +5,7 @@
//!
//! Same as [`super::component`], but for bundles.
use alloc::boxed::Box;
use bevy_utils::prelude::DebugName;
use core::any::{Any, TypeId};
use crate::{
@ -172,7 +173,7 @@ impl<B: Bundle + Reflect + TypePath + BundleFromComponents> FromType<B> for Refl
_ => panic!(
"expected bundle `{}` to be named struct or tuple",
// FIXME: once we have unique reflect, use `TypePath`.
core::any::type_name::<B>(),
DebugName::type_name::<B>(),
),
}
}
@ -215,7 +216,7 @@ impl<B: Bundle + Reflect + TypePath + BundleFromComponents> FromType<B> for Refl
_ => panic!(
"expected bundle `{}` to be a named struct or tuple",
// FIXME: once we have unique reflect, use `TypePath`.
core::any::type_name::<B>(),
DebugName::type_name::<B>(),
),
}
}

View File

@ -70,7 +70,7 @@ use crate::{
},
};
use bevy_reflect::{FromReflect, FromType, PartialReflect, Reflect, TypePath, TypeRegistry};
use disqualified::ShortName;
use bevy_utils::prelude::DebugName;
/// A struct used to operate on reflected [`Component`] trait of a type.
///
@ -308,7 +308,8 @@ impl<C: Component + Reflect + TypePath> FromType<C> for ReflectComponent {
},
apply: |mut entity, reflected_component| {
if !C::Mutability::MUTABLE {
let name = ShortName::of::<C>();
let name = DebugName::type_name::<C>();
let name = name.shortname();
panic!("Cannot call `ReflectComponent::apply` on component {name}. It is immutable, and cannot modified through reflection");
}
@ -357,7 +358,8 @@ impl<C: Component + Reflect + TypePath> FromType<C> for ReflectComponent {
reflect: |entity| entity.get::<C>().map(|c| c as &dyn Reflect),
reflect_mut: |entity| {
if !C::Mutability::MUTABLE {
let name = ShortName::of::<C>();
let name = DebugName::type_name::<C>();
let name = name.shortname();
panic!("Cannot call `ReflectComponent::reflect_mut` on component {name}. It is immutable, and cannot modified through reflection");
}
@ -370,7 +372,8 @@ impl<C: Component + Reflect + TypePath> FromType<C> for ReflectComponent {
},
reflect_unchecked_mut: |entity| {
if !C::Mutability::MUTABLE {
let name = ShortName::of::<C>();
let name = DebugName::type_name::<C>();
let name = name.shortname();
panic!("Cannot call `ReflectComponent::reflect_unchecked_mut` on component {name}. It is immutable, and cannot modified through reflection");
}

View File

@ -18,6 +18,7 @@ mod from_world;
mod map_entities;
mod resource;
use bevy_utils::prelude::DebugName;
pub use bundle::{ReflectBundle, ReflectBundleFns};
pub use component::{ReflectComponent, ReflectComponentFns};
pub use entity_commands::ReflectCommandExt;
@ -136,7 +137,7 @@ pub fn from_reflect_with_fallback<T: Reflect + TypePath>(
`Default` or `FromWorld` traits. Are you perhaps missing a `#[reflect(Default)]` \
or `#[reflect(FromWorld)]`?",
// FIXME: once we have unique reflect, use `TypePath`.
core::any::type_name::<T>(),
DebugName::type_name::<T>(),
);
};

View File

@ -6,6 +6,7 @@ mod relationship_source_collection;
use alloc::format;
use bevy_utils::prelude::DebugName;
pub use related_methods::*;
pub use relationship_query::*;
pub use relationship_source_collection::*;
@ -105,8 +106,8 @@ pub trait Relationship: Component + Sized {
warn!(
"{}The {}({target_entity:?}) relationship on entity {entity:?} points to itself. The invalid {} relationship has been removed.",
caller.map(|location|format!("{location}: ")).unwrap_or_default(),
core::any::type_name::<Self>(),
core::any::type_name::<Self>()
DebugName::type_name::<Self>(),
DebugName::type_name::<Self>()
);
world.commands().entity(entity).remove::<Self>();
return;
@ -125,8 +126,8 @@ pub trait Relationship: Component + Sized {
warn!(
"{}The {}({target_entity:?}) relationship on entity {entity:?} relates to an entity that does not exist. The invalid {} relationship has been removed.",
caller.map(|location|format!("{location}: ")).unwrap_or_default(),
core::any::type_name::<Self>(),
core::any::type_name::<Self>()
DebugName::type_name::<Self>(),
DebugName::type_name::<Self>()
);
world.commands().entity(entity).remove::<Self>();
}

View File

@ -14,7 +14,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// target entity of that relationship.
pub fn related<R: Relationship>(&'w self, entity: Entity) -> Option<Entity>
where
<D as QueryData>::ReadOnly: QueryData<Item<'w> = &'w R>,
<D as QueryData>::ReadOnly: QueryData<Item<'w, 's> = &'w R>,
{
self.get(entity).map(R::get).ok()
}
@ -26,7 +26,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
entity: Entity,
) -> impl Iterator<Item = Entity> + 'w
where
<D as QueryData>::ReadOnly: QueryData<Item<'w> = &'w S>,
<D as QueryData>::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
{
self.get(entity)
.into_iter()
@ -42,7 +42,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// If your relationship is not a tree (like Bevy's hierarchy), be sure to stop if you encounter a duplicate entity.
pub fn root_ancestor<R: Relationship>(&'w self, entity: Entity) -> Entity
where
<D as QueryData>::ReadOnly: QueryData<Item<'w> = &'w R>,
<D as QueryData>::ReadOnly: QueryData<Item<'w, 's> = &'w R>,
{
// Recursively search up the tree until we're out of parents
match self.get(entity) {
@ -60,9 +60,9 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
pub fn iter_leaves<S: RelationshipTarget>(
&'w self,
entity: Entity,
) -> impl Iterator<Item = Entity> + 'w
) -> impl Iterator<Item = Entity> + use<'w, 's, S, D, F>
where
<D as QueryData>::ReadOnly: QueryData<Item<'w> = &'w S>,
<D as QueryData>::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
SourceIter<'w, S>: DoubleEndedIterator,
{
self.iter_descendants_depth_first(entity).filter(|entity| {
@ -80,7 +80,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
entity: Entity,
) -> impl Iterator<Item = Entity> + 'w
where
D::ReadOnly: QueryData<Item<'w> = (Option<&'w R>, Option<&'w R::RelationshipTarget>)>,
D::ReadOnly: QueryData<Item<'w, 's> = (Option<&'w R>, Option<&'w R::RelationshipTarget>)>,
{
self.get(entity)
.ok()
@ -103,7 +103,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
entity: Entity,
) -> DescendantIter<'w, 's, D, F, S>
where
D::ReadOnly: QueryData<Item<'w> = &'w S>,
D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
{
DescendantIter::new(self, entity)
}
@ -120,7 +120,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
entity: Entity,
) -> DescendantDepthFirstIter<'w, 's, D, F, S>
where
D::ReadOnly: QueryData<Item<'w> = &'w S>,
D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
SourceIter<'w, S>: DoubleEndedIterator,
{
DescendantDepthFirstIter::new(self, entity)
@ -137,7 +137,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
entity: Entity,
) -> AncestorIter<'w, 's, D, F, R>
where
D::ReadOnly: QueryData<Item<'w> = &'w R>,
D::ReadOnly: QueryData<Item<'w, 's> = &'w R>,
{
AncestorIter::new(self, entity)
}
@ -148,7 +148,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// Traverses the hierarchy breadth-first.
pub struct DescendantIter<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget>
where
D::ReadOnly: QueryData<Item<'w> = &'w S>,
D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
{
children_query: &'w Query<'w, 's, D, F>,
vecdeque: VecDeque<Entity>,
@ -156,7 +156,7 @@ where
impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> DescendantIter<'w, 's, D, F, S>
where
D::ReadOnly: QueryData<Item<'w> = &'w S>,
D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
{
/// Returns a new [`DescendantIter`].
pub fn new(children_query: &'w Query<'w, 's, D, F>, entity: Entity) -> Self {
@ -174,7 +174,7 @@ where
impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> Iterator
for DescendantIter<'w, 's, D, F, S>
where
D::ReadOnly: QueryData<Item<'w> = &'w S>,
D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
{
type Item = Entity;
@ -194,7 +194,7 @@ where
/// Traverses the hierarchy depth-first.
pub struct DescendantDepthFirstIter<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget>
where
D::ReadOnly: QueryData<Item<'w> = &'w S>,
D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
{
children_query: &'w Query<'w, 's, D, F>,
stack: SmallVec<[Entity; 8]>,
@ -203,7 +203,7 @@ where
impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget>
DescendantDepthFirstIter<'w, 's, D, F, S>
where
D::ReadOnly: QueryData<Item<'w> = &'w S>,
D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
SourceIter<'w, S>: DoubleEndedIterator,
{
/// Returns a new [`DescendantDepthFirstIter`].
@ -220,7 +220,7 @@ where
impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> Iterator
for DescendantDepthFirstIter<'w, 's, D, F, S>
where
D::ReadOnly: QueryData<Item<'w> = &'w S>,
D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
SourceIter<'w, S>: DoubleEndedIterator,
{
type Item = Entity;
@ -239,7 +239,7 @@ where
/// An [`Iterator`] of [`Entity`]s over the ancestors of an [`Entity`].
pub struct AncestorIter<'w, 's, D: QueryData, F: QueryFilter, R: Relationship>
where
D::ReadOnly: QueryData<Item<'w> = &'w R>,
D::ReadOnly: QueryData<Item<'w, 's> = &'w R>,
{
parent_query: &'w Query<'w, 's, D, F>,
next: Option<Entity>,
@ -247,7 +247,7 @@ where
impl<'w, 's, D: QueryData, F: QueryFilter, R: Relationship> AncestorIter<'w, 's, D, F, R>
where
D::ReadOnly: QueryData<Item<'w> = &'w R>,
D::ReadOnly: QueryData<Item<'w, 's> = &'w R>,
{
/// Returns a new [`AncestorIter`].
pub fn new(parent_query: &'w Query<'w, 's, D, F>, entity: Entity) -> Self {
@ -261,7 +261,7 @@ where
impl<'w, 's, D: QueryData, F: QueryFilter, R: Relationship> Iterator
for AncestorIter<'w, 's, D, F, R>
where
D::ReadOnly: QueryData<Item<'w> = &'w R>,
D::ReadOnly: QueryData<Item<'w, 's> = &'w R>,
{
type Item = Entity;

View File

@ -1,4 +1,5 @@
use alloc::{borrow::Cow, boxed::Box, format};
use alloc::{boxed::Box, format};
use bevy_utils::prelude::DebugName;
use core::ops::Not;
use crate::system::{
@ -154,7 +155,7 @@ pub trait SystemCondition<Marker, In: SystemInput = (), Out = bool>:
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(and);
let name = format!("{} && {}", a.name(), b.name());
CombinatorSystem::new(a, b, Cow::Owned(name))
CombinatorSystem::new(a, b, DebugName::owned(name))
}
/// Returns a new run condition that only returns `false`
@ -206,7 +207,7 @@ pub trait SystemCondition<Marker, In: SystemInput = (), Out = bool>:
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(nand);
let name = format!("!({} && {})", a.name(), b.name());
CombinatorSystem::new(a, b, Cow::Owned(name))
CombinatorSystem::new(a, b, DebugName::owned(name))
}
/// Returns a new run condition that only returns `true`
@ -258,7 +259,7 @@ pub trait SystemCondition<Marker, In: SystemInput = (), Out = bool>:
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(nor);
let name = format!("!({} || {})", a.name(), b.name());
CombinatorSystem::new(a, b, Cow::Owned(name))
CombinatorSystem::new(a, b, DebugName::owned(name))
}
/// Returns a new run condition that returns `true`
@ -305,7 +306,7 @@ pub trait SystemCondition<Marker, In: SystemInput = (), Out = bool>:
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(or);
let name = format!("{} || {}", a.name(), b.name());
CombinatorSystem::new(a, b, Cow::Owned(name))
CombinatorSystem::new(a, b, DebugName::owned(name))
}
/// Returns a new run condition that only returns `true`
@ -357,7 +358,7 @@ pub trait SystemCondition<Marker, In: SystemInput = (), Out = bool>:
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(xnor);
let name = format!("!({} ^ {})", a.name(), b.name());
CombinatorSystem::new(a, b, Cow::Owned(name))
CombinatorSystem::new(a, b, DebugName::owned(name))
}
/// Returns a new run condition that only returns `true`
@ -399,7 +400,7 @@ pub trait SystemCondition<Marker, In: SystemInput = (), Out = bool>:
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(xor);
let name = format!("({} ^ {})", a.name(), b.name());
CombinatorSystem::new(a, b, Cow::Owned(name))
CombinatorSystem::new(a, b, DebugName::owned(name))
}
}

View File

@ -3,7 +3,8 @@ mod multi_threaded;
mod simple;
mod single_threaded;
use alloc::{borrow::Cow, vec, vec::Vec};
use alloc::{vec, vec::Vec};
use bevy_utils::prelude::DebugName;
use core::any::TypeId;
#[expect(deprecated, reason = "We still need to support this.")]
@ -158,8 +159,8 @@ impl System for ApplyDeferred {
type In = ();
type Out = Result<()>;
fn name(&self) -> Cow<'static, str> {
Cow::Borrowed("bevy_ecs::apply_deferred")
fn name(&self) -> DebugName {
DebugName::borrowed("bevy_ecs::apply_deferred")
}
fn flags(&self) -> SystemStateFlags {

View File

@ -342,7 +342,7 @@ impl<'scope, 'env: 'scope, 'sys> Context<'scope, 'env, 'sys> {
#[cfg(feature = "std")]
#[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")]
{
eprintln!("Encountered a panic in system `{}`!", &*system.name());
eprintln!("Encountered a panic in system `{}`!", system.name());
}
// set the payload to propagate the error
{
@ -799,7 +799,7 @@ fn apply_deferred(
{
eprintln!(
"Encountered a panic when applying buffers for system `{}`!",
&*system.name()
system.name()
);
}
return Err(payload);

View File

@ -73,7 +73,7 @@ impl SystemExecutor for SimpleExecutor {
#[cfg(feature = "trace")]
let name = schedule.systems[system_index].system.name();
#[cfg(feature = "trace")]
let should_run_span = info_span!("check_conditions", name = &*name).entered();
let should_run_span = info_span!("check_conditions", name = name.as_string()).entered();
let mut should_run = !self.completed_systems.contains(system_index);
for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() {
@ -161,7 +161,7 @@ impl SystemExecutor for SimpleExecutor {
#[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")]
{
if let Err(payload) = std::panic::catch_unwind(f) {
eprintln!("Encountered a panic in system `{}`!", &*system.name());
eprintln!("Encountered a panic in system `{}`!", system.name());
std::panic::resume_unwind(payload);
}
}

View File

@ -74,7 +74,7 @@ impl SystemExecutor for SingleThreadedExecutor {
#[cfg(feature = "trace")]
let name = schedule.systems[system_index].system.name();
#[cfg(feature = "trace")]
let should_run_span = info_span!("check_conditions", name = &*name).entered();
let should_run_span = info_span!("check_conditions", name = name.as_string()).entered();
let mut should_run = !self.completed_systems.contains(system_index);
for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() {
@ -166,7 +166,7 @@ impl SystemExecutor for SingleThreadedExecutor {
#[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")]
{
if let Err(payload) = std::panic::catch_unwind(f) {
eprintln!("Encountered a panic in system `{}`!", &*system.name());
eprintln!("Encountered a panic in system `{}`!", system.name());
std::panic::resume_unwind(payload);
}
}

View File

@ -2,7 +2,6 @@
clippy::module_inception,
reason = "This instance of module inception is being discussed; see #17344."
)]
use alloc::borrow::Cow;
use alloc::{
boxed::Box,
collections::{BTreeMap, BTreeSet},
@ -12,12 +11,11 @@ use alloc::{
vec::Vec,
};
use bevy_platform::collections::{HashMap, HashSet};
use bevy_utils::{default, TypeIdMap};
use bevy_utils::{default, prelude::DebugName, TypeIdMap};
use core::{
any::{Any, TypeId},
fmt::{Debug, Write},
};
use disqualified::ShortName;
use fixedbitset::FixedBitSet;
use log::{error, info, warn};
use pass::ScheduleBuildPassObj;
@ -1694,14 +1692,14 @@ impl ScheduleGraph {
#[inline]
fn get_node_name_inner(&self, id: &NodeId, report_sets: bool) -> String {
let name = match id {
match id {
NodeId::System(_) => {
let name = self.systems[id.index()]
.get()
.unwrap()
.system
.name()
.to_string();
let name = self.systems[id.index()].get().unwrap().system.name();
let name = if self.settings.use_shortnames {
name.shortname().to_string()
} else {
name.to_string()
};
if report_sets {
let sets = self.names_of_sets_containing_node(id);
if sets.is_empty() {
@ -1723,11 +1721,6 @@ impl ScheduleGraph {
set.name()
}
}
};
if self.settings.use_shortnames {
ShortName(&name).to_string()
} else {
name
}
}
@ -2007,7 +2000,7 @@ impl ScheduleGraph {
&'a self,
ambiguities: &'a [(NodeId, NodeId, Vec<ComponentId>)],
components: &'a Components,
) -> impl Iterator<Item = (String, String, Vec<Cow<'a, str>>)> + 'a {
) -> impl Iterator<Item = (String, String, Vec<DebugName>)> + 'a {
ambiguities
.iter()
.map(move |(system_a, system_b, conflicts)| {

View File

@ -1,4 +1,5 @@
use alloc::boxed::Box;
use bevy_utils::prelude::DebugName;
use core::{
any::TypeId,
fmt::Debug,
@ -196,7 +197,7 @@ impl<T: 'static> SystemTypeSet<T> {
impl<T> Debug for SystemTypeSet<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("SystemTypeSet")
.field(&format_args!("fn {}()", &core::any::type_name::<T>()))
.field(&format_args!("fn {}()", DebugName::type_name::<T>()))
.finish()
}
}

View File

@ -895,9 +895,9 @@ mod tests {
($schedule:expr, $skipped_systems:expr, $($system:expr),*) => {
// pull an ordered list of systems in the schedule, and save the
// system TypeId, and name.
let systems: Vec<(TypeId, alloc::borrow::Cow<'static, str>)> = $schedule.systems().unwrap()
let systems: Vec<(TypeId, alloc::string::String)> = $schedule.systems().unwrap()
.map(|(_, system)| {
(system.type_id(), system.name())
(system.type_id(), system.name().as_string())
})
.collect();

View File

@ -3,8 +3,8 @@ use crate::{
component::{CheckChangeTicks, ComponentId, ComponentTicks, Components, Tick, TickCells},
storage::{blob_vec::BlobVec, SparseSet},
};
use alloc::string::String;
use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
use bevy_utils::prelude::DebugName;
use core::{cell::UnsafeCell, mem::ManuallyDrop, panic::Location};
#[cfg(feature = "std")]
@ -23,7 +23,7 @@ pub struct ResourceData<const SEND: bool> {
not(feature = "std"),
expect(dead_code, reason = "currently only used with the std feature")
)]
type_name: String,
type_name: DebugName,
#[cfg(feature = "std")]
origin_thread_id: Option<ThreadId>,
changed_by: MaybeLocation<UnsafeCell<&'static Location<'static>>>,
@ -385,7 +385,7 @@ impl<const SEND: bool> Resources<SEND> {
data: ManuallyDrop::new(data),
added_ticks: UnsafeCell::new(Tick::new(0)),
changed_ticks: UnsafeCell::new(Tick::new(0)),
type_name: String::from(component_info.name()),
type_name: component_info.name(),
#[cfg(feature = "std")]
origin_thread_id: None,
changed_by: MaybeLocation::caller().map(UnsafeCell::new),

View File

@ -1,4 +1,5 @@
use alloc::{borrow::Cow, vec::Vec};
use alloc::vec::Vec;
use bevy_utils::prelude::DebugName;
use super::{IntoSystem, ReadOnlySystem, System, SystemParamValidationError};
use crate::{
@ -101,7 +102,7 @@ where
pub struct AdapterSystem<Func, S> {
func: Func,
system: S,
name: Cow<'static, str>,
name: DebugName,
}
impl<Func, S> AdapterSystem<Func, S>
@ -110,7 +111,7 @@ where
S: System,
{
/// Creates a new [`System`] that uses `func` to adapt `system`, via the [`Adapt`] trait.
pub const fn new(func: Func, system: S, name: Cow<'static, str>) -> Self {
pub const fn new(func: Func, system: S, name: DebugName) -> Self {
Self { func, system, name }
}
}
@ -123,7 +124,7 @@ where
type In = Func::In;
type Out = Func::Out;
fn name(&self) -> Cow<'static, str> {
fn name(&self) -> DebugName {
self.name.clone()
}

View File

@ -1,4 +1,5 @@
use alloc::{borrow::Cow, format, vec::Vec};
use alloc::{format, vec::Vec};
use bevy_utils::prelude::DebugName;
use core::marker::PhantomData;
use crate::{
@ -55,7 +56,7 @@ use super::{IntoSystem, ReadOnlySystem, System};
/// IntoSystem::into_system(resource_equals(A(1))),
/// IntoSystem::into_system(resource_equals(B(1))),
/// // The name of the combined system.
/// std::borrow::Cow::Borrowed("a ^ b"),
/// "a ^ b".into(),
/// )));
/// # fn my_system(mut flag: ResMut<RanFlag>) { flag.0 = true; }
/// #
@ -112,14 +113,14 @@ pub struct CombinatorSystem<Func, A, B> {
_marker: PhantomData<fn() -> Func>,
a: A,
b: B,
name: Cow<'static, str>,
name: DebugName,
}
impl<Func, A, B> CombinatorSystem<Func, A, B> {
/// Creates a new system that combines two inner systems.
///
/// The returned system will only be usable if `Func` implements [`Combine<A, B>`].
pub fn new(a: A, b: B, name: Cow<'static, str>) -> Self {
pub fn new(a: A, b: B, name: DebugName) -> Self {
Self {
_marker: PhantomData,
a,
@ -138,7 +139,7 @@ where
type In = Func::In;
type Out = Func::Out;
fn name(&self) -> Cow<'static, str> {
fn name(&self) -> DebugName {
self.name.clone()
}
@ -271,7 +272,7 @@ where
let system_a = IntoSystem::into_system(this.a);
let system_b = IntoSystem::into_system(this.b);
let name = format!("Pipe({}, {})", system_a.name(), system_b.name());
PipeSystem::new(system_a, system_b, Cow::Owned(name))
PipeSystem::new(system_a, system_b, DebugName::owned(name))
}
}
@ -317,7 +318,7 @@ where
pub struct PipeSystem<A, B> {
a: A,
b: B,
name: Cow<'static, str>,
name: DebugName,
}
impl<A, B> PipeSystem<A, B>
@ -327,7 +328,7 @@ where
for<'a> B::In: SystemInput<Inner<'a> = A::Out>,
{
/// Creates a new system that pipes two inner systems.
pub fn new(a: A, b: B, name: Cow<'static, str>) -> Self {
pub fn new(a: A, b: B, name: DebugName) -> Self {
Self { a, b, name }
}
}
@ -341,7 +342,7 @@ where
type In = A::In;
type Out = B::Out;
fn name(&self) -> Cow<'static, str> {
fn name(&self) -> DebugName {
self.name.clone()
}

View File

@ -144,10 +144,11 @@ where
/// A [`Command`] that runs the given system,
/// caching its [`SystemId`] in a [`CachedSystemId`](crate::system::CachedSystemId) resource.
pub fn run_system_cached<M, S>(system: S) -> impl Command<Result>
pub fn run_system_cached<O, M, S>(system: S) -> impl Command<Result>
where
O: 'static,
M: 'static,
S: IntoSystem<(), (), M> + Send + 'static,
S: IntoSystem<(), O, M> + Send + 'static,
{
move |world: &mut World| -> Result {
world.run_system_cached(system)?;
@ -157,11 +158,15 @@ where
/// A [`Command`] that runs the given system with the given input value,
/// caching its [`SystemId`] in a [`CachedSystemId`](crate::system::CachedSystemId) resource.
pub fn run_system_cached_with<I, M, S>(system: S, input: I::Inner<'static>) -> impl Command<Result>
pub fn run_system_cached_with<I, O, M, S>(
system: S,
input: I::Inner<'static>,
) -> impl Command<Result>
where
I: SystemInput<Inner<'static>: Send> + Send + 'static,
O: 'static,
M: 'static,
S: IntoSystem<I, (), M> + Send + 'static,
S: IntoSystem<I, O, M> + Send + 'static,
{
move |world: &mut World| -> Result {
world.run_system_cached_with(system, input)?;
@ -175,7 +180,7 @@ where
pub fn unregister_system<I, O>(system_id: SystemId<I, O>) -> impl Command<Result>
where
I: SystemInput + Send + 'static,
O: Send + 'static,
O: 'static,
{
move |world: &mut World| -> Result {
world.unregister_system(system_id)?;

View File

@ -917,7 +917,7 @@ impl<'w, 's> Commands<'w, 's> {
///
/// It will internally return a [`RegisteredSystemError`](crate::system::system_registry::RegisteredSystemError),
/// which will be handled by [logging the error at the `warn` level](warn).
pub fn run_system(&mut self, id: SystemId) {
pub fn run_system<O: 'static>(&mut self, id: SystemId<(), O>) {
self.queue(command::run_system(id).handle_error_with(warn));
}
@ -1010,7 +1010,7 @@ impl<'w, 's> Commands<'w, 's> {
) -> SystemId<I, O>
where
I: SystemInput + Send + 'static,
O: Send + 'static,
O: 'static,
{
let entity = self.spawn_empty().id();
let system = RegisteredSystem::<I, O>::new(Box::new(IntoSystem::into_system(system)));
@ -1035,7 +1035,7 @@ impl<'w, 's> Commands<'w, 's> {
pub fn unregister_system<I, O>(&mut self, system_id: SystemId<I, O>)
where
I: SystemInput + Send + 'static,
O: Send + 'static,
O: 'static,
{
self.queue(command::unregister_system(system_id).handle_error_with(warn));
}
@ -1084,10 +1084,11 @@ impl<'w, 's> Commands<'w, 's> {
/// consider passing them in as inputs via [`Commands::run_system_cached_with`].
///
/// If that's not an option, consider [`Commands::register_system`] instead.
pub fn run_system_cached<M, S>(&mut self, system: S)
pub fn run_system_cached<O, M, S>(&mut self, system: S)
where
O: 'static,
M: 'static,
S: IntoSystem<(), (), M> + Send + 'static,
S: IntoSystem<(), O, M> + Send + 'static,
{
self.queue(command::run_system_cached(system).handle_error_with(warn));
}
@ -1114,11 +1115,12 @@ impl<'w, 's> Commands<'w, 's> {
/// consider passing them in as inputs.
///
/// If that's not an option, consider [`Commands::register_system`] instead.
pub fn run_system_cached_with<I, M, S>(&mut self, system: S, input: I::Inner<'static>)
pub fn run_system_cached_with<I, O, M, S>(&mut self, system: S, input: I::Inner<'static>)
where
I: SystemInput<Inner<'static>: Send> + Send + 'static,
O: 'static,
M: 'static,
S: IntoSystem<I, (), M> + Send + 'static,
S: IntoSystem<I, O, M> + Send + 'static,
{
self.queue(command::run_system_cached_with(system, input).handle_error_with(warn));
}

View File

@ -10,6 +10,7 @@ use crate::{
};
use alloc::{borrow::Cow, vec, vec::Vec};
use bevy_utils::prelude::DebugName;
use core::marker::PhantomData;
use variadics_please::all_tuples;
@ -42,7 +43,7 @@ where
///
/// Useful to give closure systems more readable and unique names for debugging and tracing.
pub fn with_name(mut self, new_name: impl Into<Cow<'static, str>>) -> Self {
self.system_meta.set_name(new_name.into());
self.system_meta.set_name(new_name);
self
}
}
@ -83,7 +84,7 @@ where
type Out = F::Out;
#[inline]
fn name(&self) -> Cow<'static, str> {
fn name(&self) -> DebugName {
self.system_meta.name.clone()
}
@ -181,7 +182,7 @@ where
check_system_change_tick(
&mut self.system_meta.last_run,
check,
self.system_meta.name.as_ref(),
self.system_meta.name.clone(),
);
}

View File

@ -11,6 +11,7 @@ use crate::{
};
use alloc::{borrow::Cow, vec, vec::Vec};
use bevy_utils::prelude::DebugName;
use core::marker::PhantomData;
use variadics_please::all_tuples;
@ -24,7 +25,7 @@ use super::{
/// The metadata of a [`System`].
#[derive(Clone)]
pub struct SystemMeta {
pub(crate) name: Cow<'static, str>,
pub(crate) name: DebugName,
// NOTE: this must be kept private. making a SystemMeta non-send is irreversible to prevent
// SystemParams from overriding each other
flags: SystemStateFlags,
@ -37,21 +38,21 @@ pub struct SystemMeta {
impl SystemMeta {
pub(crate) fn new<T>() -> Self {
let name = core::any::type_name::<T>();
let name = DebugName::type_name::<T>();
Self {
name: name.into(),
#[cfg(feature = "trace")]
system_span: info_span!("system", name = name.clone().as_string()),
#[cfg(feature = "trace")]
commands_span: info_span!("system_commands", name = name.clone().as_string()),
name,
flags: SystemStateFlags::empty(),
last_run: Tick::new(0),
#[cfg(feature = "trace")]
system_span: info_span!("system", name = name),
#[cfg(feature = "trace")]
commands_span: info_span!("system_commands", name = name),
}
}
/// Returns the system's name
#[inline]
pub fn name(&self) -> &str {
pub fn name(&self) -> &DebugName {
&self.name
}
@ -67,7 +68,7 @@ impl SystemMeta {
self.system_span = info_span!("system", name = name);
self.commands_span = info_span!("system_commands", name = name);
}
self.name = new_name;
self.name = new_name.into();
}
/// Returns true if the system is [`Send`].
@ -600,7 +601,7 @@ where
type Out = F::Out;
#[inline]
fn name(&self) -> Cow<'static, str> {
fn name(&self) -> DebugName {
self.system_meta.name.clone()
}
@ -712,7 +713,7 @@ where
check_system_change_tick(
&mut self.system_meta.last_run,
check,
self.system_meta.name.as_ref(),
self.system_meta.name.clone(),
);
}

View File

@ -1,4 +1,5 @@
use alloc::{borrow::Cow, vec::Vec};
use alloc::vec::Vec;
use bevy_utils::prelude::DebugName;
use core::marker::PhantomData;
use crate::{
@ -112,7 +113,7 @@ where
type Out = Result;
#[inline]
fn name(&self) -> Cow<'static, str> {
fn name(&self) -> DebugName {
self.observer.name()
}

View File

@ -1,3 +1,5 @@
use bevy_utils::prelude::DebugName;
use crate::{
batching::BatchingStrategy,
component::Tick,
@ -1185,7 +1187,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// [`par_iter_mut`]: Self::par_iter_mut
/// [`World`]: crate::world::World
#[inline]
pub fn par_iter(&self) -> QueryParIter<'_, '_, D::ReadOnly, F> {
pub fn par_iter(&self) -> QueryParIter<'_, 's, D::ReadOnly, F> {
self.as_readonly().par_iter_inner()
}
@ -1220,7 +1222,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// [`par_iter`]: Self::par_iter
/// [`World`]: crate::world::World
#[inline]
pub fn par_iter_mut(&mut self) -> QueryParIter<'_, '_, D, F> {
pub fn par_iter_mut(&mut self) -> QueryParIter<'_, 's, D, F> {
self.reborrow().par_iter_inner()
}
@ -1280,7 +1282,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
pub fn par_iter_many<EntityList: IntoIterator<Item: EntityEquivalent>>(
&self,
entities: EntityList,
) -> QueryParManyIter<'_, '_, D::ReadOnly, F, EntityList::Item> {
) -> QueryParManyIter<'_, 's, D::ReadOnly, F, EntityList::Item> {
QueryParManyIter {
world: self.world,
state: self.state.as_readonly(),
@ -1309,7 +1311,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
pub fn par_iter_many_unique<EntityList: EntitySet<Item: Sync>>(
&self,
entities: EntityList,
) -> QueryParManyUniqueIter<'_, '_, D::ReadOnly, F, EntityList::Item> {
) -> QueryParManyUniqueIter<'_, 's, D::ReadOnly, F, EntityList::Item> {
QueryParManyUniqueIter {
world: self.world,
state: self.state.as_readonly(),
@ -1338,7 +1340,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
pub fn par_iter_many_unique_mut<EntityList: EntitySet<Item: Sync>>(
&mut self,
entities: EntityList,
) -> QueryParManyUniqueIter<'_, '_, D, F, EntityList::Item> {
) -> QueryParManyUniqueIter<'_, 's, D, F, EntityList::Item> {
QueryParManyUniqueIter {
world: self.world,
state: self.state,
@ -1383,7 +1385,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
///
/// - [`get_mut`](Self::get_mut) to get a mutable query item.
#[inline]
pub fn get(&self, entity: Entity) -> Result<ROQueryItem<'_, D>, QueryEntityError> {
pub fn get(&self, entity: Entity) -> Result<ROQueryItem<'_, 's, D>, QueryEntityError> {
self.as_readonly().get_inner(entity)
}
@ -1434,7 +1436,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
pub fn get_many<const N: usize>(
&self,
entities: [Entity; N],
) -> Result<[ROQueryItem<'_, D>; N], QueryEntityError> {
) -> Result<[ROQueryItem<'_, 's, D>; N], QueryEntityError> {
// Note that we call a separate `*_inner` method from `get_many_mut`
// because we don't need to check for duplicates.
self.as_readonly().get_many_inner(entities)
@ -1485,7 +1487,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
pub fn get_many_unique<const N: usize>(
&self,
entities: UniqueEntityArray<N>,
) -> Result<[ROQueryItem<'_, D>; N], QueryEntityError> {
) -> Result<[ROQueryItem<'_, 's, D>; N], QueryEntityError> {
self.as_readonly().get_many_unique_inner(entities)
}
@ -1519,7 +1521,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
///
/// - [`get`](Self::get) to get a read-only query item.
#[inline]
pub fn get_mut(&mut self, entity: Entity) -> Result<D::Item<'_>, QueryEntityError> {
pub fn get_mut(&mut self, entity: Entity) -> Result<D::Item<'_, 's>, QueryEntityError> {
self.reborrow().get_inner(entity)
}
@ -1534,7 +1536,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
///
/// - [`get_mut`](Self::get_mut) to get the item using a mutable borrow of the [`Query`].
#[inline]
pub fn get_inner(self, entity: Entity) -> Result<D::Item<'w>, QueryEntityError> {
pub fn get_inner(self, entity: Entity) -> Result<D::Item<'w, 's>, QueryEntityError> {
// SAFETY: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
unsafe {
@ -1576,8 +1578,18 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
D::set_archetype(&mut fetch, &self.state.fetch_state, archetype, table);
F::set_archetype(&mut filter, &self.state.filter_state, archetype, table);
if F::filter_fetch(&mut filter, entity, location.table_row) {
Ok(D::fetch(&mut fetch, entity, location.table_row))
if F::filter_fetch(
&self.state.filter_state,
&mut filter,
entity,
location.table_row,
) {
Ok(D::fetch(
&self.state.fetch_state,
&mut fetch,
entity,
location.table_row,
))
} else {
Err(QueryEntityError::QueryDoesNotMatch(
entity,
@ -1658,7 +1670,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
pub fn get_many_mut<const N: usize>(
&mut self,
entities: [Entity; N],
) -> Result<[D::Item<'_>; N], QueryEntityError> {
) -> Result<[D::Item<'_, 's>; N], QueryEntityError> {
self.reborrow().get_many_mut_inner(entities)
}
@ -1726,7 +1738,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
pub fn get_many_unique_mut<const N: usize>(
&mut self,
entities: UniqueEntityArray<N>,
) -> Result<[D::Item<'_>; N], QueryEntityError> {
) -> Result<[D::Item<'_, 's>; N], QueryEntityError> {
self.reborrow().get_many_unique_inner(entities)
}
@ -1745,7 +1757,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
pub fn get_many_mut_inner<const N: usize>(
self,
entities: [Entity; N],
) -> Result<[D::Item<'w>; N], QueryEntityError> {
) -> Result<[D::Item<'w, 's>; N], QueryEntityError> {
// Verify that all entities are unique
for i in 0..N {
for j in 0..i {
@ -1773,7 +1785,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
pub fn get_many_inner<const N: usize>(
self,
entities: [Entity; N],
) -> Result<[D::Item<'w>; N], QueryEntityError>
) -> Result<[D::Item<'w, 's>; N], QueryEntityError>
where
D: ReadOnlyQueryData,
{
@ -1795,7 +1807,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
pub fn get_many_unique_inner<const N: usize>(
self,
entities: UniqueEntityArray<N>,
) -> Result<[D::Item<'w>; N], QueryEntityError> {
) -> Result<[D::Item<'w, 's>; N], QueryEntityError> {
// SAFETY: All entities are unique, so the results don't alias.
unsafe { self.get_many_impl(entities.into_inner()) }
}
@ -1810,7 +1822,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
unsafe fn get_many_impl<const N: usize>(
self,
entities: [Entity; N],
) -> Result<[D::Item<'w>; N], QueryEntityError> {
) -> Result<[D::Item<'w, 's>; N], QueryEntityError> {
let mut values = [(); N].map(|_| MaybeUninit::uninit());
for (value, entity) in core::iter::zip(&mut values, entities) {
@ -1838,7 +1850,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
///
/// - [`get_mut`](Self::get_mut) for the safe version.
#[inline]
pub unsafe fn get_unchecked(&self, entity: Entity) -> Result<D::Item<'_>, QueryEntityError> {
pub unsafe fn get_unchecked(
&self,
entity: Entity,
) -> Result<D::Item<'_, 's>, QueryEntityError> {
// SAFETY: The caller promises that this will not result in multiple mutable references.
unsafe { self.reborrow_unsafe() }.get_inner(entity)
}
@ -1874,7 +1889,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
///
/// - [`single_mut`](Self::single_mut) to get the mutable query item.
#[inline]
pub fn single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> {
pub fn single(&self) -> Result<ROQueryItem<'_, 's, D>, QuerySingleError> {
self.as_readonly().single_inner()
}
@ -1903,7 +1918,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
///
/// - [`single`](Self::single) to get the read-only query item.
#[inline]
pub fn single_mut(&mut self) -> Result<D::Item<'_>, QuerySingleError> {
pub fn single_mut(&mut self) -> Result<D::Item<'_, 's>, QuerySingleError> {
self.reborrow().single_inner()
}
@ -1935,15 +1950,15 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// - [`single_mut`](Self::single_mut) to get the mutable query item.
/// - [`single_inner`](Self::single_inner) for the panicking version.
#[inline]
pub fn single_inner(self) -> Result<D::Item<'w>, QuerySingleError> {
pub fn single_inner(self) -> Result<D::Item<'w, 's>, QuerySingleError> {
let mut query = self.into_iter();
let first = query.next();
let extra = query.next().is_some();
match (first, extra) {
(Some(r), false) => Ok(r),
(None, _) => Err(QuerySingleError::NoEntities(core::any::type_name::<Self>())),
(Some(_), _) => Err(QuerySingleError::MultipleEntities(core::any::type_name::<
(None, _) => Err(QuerySingleError::NoEntities(DebugName::type_name::<Self>())),
(Some(_), _) => Err(QuerySingleError::MultipleEntities(DebugName::type_name::<
Self,
>())),
}
@ -2447,7 +2462,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
}
impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for Query<'w, 's, D, F> {
type Item = D::Item<'w>;
type Item = D::Item<'w, 's>;
type IntoIter = QueryIter<'w, 's, D, F>;
fn into_iter(self) -> Self::IntoIter {
@ -2460,7 +2475,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for Query<'w, 's, D, F>
}
impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for &'w Query<'_, 's, D, F> {
type Item = ROQueryItem<'w, D>;
type Item = ROQueryItem<'w, 's, D>;
type IntoIter = QueryIter<'w, 's, D::ReadOnly, F>;
fn into_iter(self) -> Self::IntoIter {
@ -2469,7 +2484,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for &'w Query<'_, 's, D,
}
impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for &'w mut Query<'_, 's, D, F> {
type Item = D::Item<'w>;
type Item = D::Item<'w, 's>;
type IntoIter = QueryIter<'w, 's, D, F>;
fn into_iter(self) -> Self::IntoIter {
@ -2584,28 +2599,28 @@ impl<'w, 'q, Q: QueryData, F: QueryFilter> From<&'q mut Query<'w, '_, Q, F>>
/// ```
/// Note that because [`Single`] implements [`Deref`] and [`DerefMut`], methods and fields like `health` can be accessed directly.
/// You can also access the underlying data manually, by calling `.deref`/`.deref_mut`, or by using the `*` operator.
pub struct Single<'w, D: QueryData, F: QueryFilter = ()> {
pub(crate) item: D::Item<'w>,
pub struct Single<'w, 's, D: QueryData, F: QueryFilter = ()> {
pub(crate) item: D::Item<'w, 's>,
pub(crate) _filter: PhantomData<F>,
}
impl<'w, D: QueryData, F: QueryFilter> Deref for Single<'w, D, F> {
type Target = D::Item<'w>;
impl<'w, 's, D: QueryData, F: QueryFilter> Deref for Single<'w, 's, D, F> {
type Target = D::Item<'w, 's>;
fn deref(&self) -> &Self::Target {
&self.item
}
}
impl<'w, D: QueryData, F: QueryFilter> DerefMut for Single<'w, D, F> {
impl<'w, 's, D: QueryData, F: QueryFilter> DerefMut for Single<'w, 's, D, F> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.item
}
}
impl<'w, D: QueryData, F: QueryFilter> Single<'w, D, F> {
impl<'w, 's, D: QueryData, F: QueryFilter> Single<'w, 's, D, F> {
/// Returns the inner item with ownership.
pub fn into_inner(self) -> D::Item<'w> {
pub fn into_inner(self) -> D::Item<'w, 's> {
self.item
}
}

View File

@ -1,4 +1,5 @@
use alloc::{borrow::Cow, vec::Vec};
use alloc::vec::Vec;
use bevy_utils::prelude::DebugName;
use crate::{
component::{CheckChangeTicks, ComponentId, Tick},
@ -25,7 +26,7 @@ impl<S: System<In = ()>> System for InfallibleSystemWrapper<S> {
type Out = Result;
#[inline]
fn name(&self) -> Cow<'static, str> {
fn name(&self) -> DebugName {
self.0.name()
}
@ -137,10 +138,9 @@ where
T: Send + Sync + 'static,
{
type In = ();
type Out = S::Out;
fn name(&self) -> Cow<'static, str> {
fn name(&self) -> DebugName {
self.system.name()
}
@ -231,10 +231,9 @@ where
T: FromWorld + Send + Sync + 'static,
{
type In = ();
type Out = S::Out;
fn name(&self) -> Cow<'static, str> {
fn name(&self) -> DebugName {
self.system.name()
}

View File

@ -2,6 +2,7 @@
clippy::module_inception,
reason = "This instance of module inception is being discussed; see #17353."
)]
use bevy_utils::prelude::DebugName;
use bitflags::bitflags;
use core::fmt::Debug;
use log::warn;
@ -15,7 +16,7 @@ use crate::{
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
};
use alloc::{borrow::Cow, boxed::Box, vec::Vec};
use alloc::{boxed::Box, vec::Vec};
use core::any::TypeId;
use super::{IntoSystem, SystemParamValidationError};
@ -49,8 +50,9 @@ pub trait System: Send + Sync + 'static {
type In: SystemInput;
/// The system's output.
type Out;
/// Returns the system's name.
fn name(&self) -> Cow<'static, str>;
fn name(&self) -> DebugName;
/// Returns the [`TypeId`] of the underlying system type.
#[inline]
fn type_id(&self) -> TypeId {
@ -227,7 +229,7 @@ pub type BoxedSystem<In = (), Out = ()> = Box<dyn System<In = In, Out = Out>>;
pub(crate) fn check_system_change_tick(
last_run: &mut Tick,
check: CheckChangeTicks,
system_name: &str,
system_name: DebugName,
) {
if last_run.check_tick(check) {
let age = check.present_tick().relative_to(*last_run).get();
@ -398,7 +400,7 @@ pub enum RunSystemError {
#[error("System {system} did not run due to failed parameter validation: {err}")]
InvalidParams {
/// The identifier of the system that was run.
system: Cow<'static, str>,
system: DebugName,
/// The returned parameter validation error.
err: SystemParamValidationError,
},

View File

@ -5,9 +5,8 @@ use crate::{
system::{ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam},
world::unsafe_world_cell::UnsafeWorldCell,
};
use alloc::borrow::Cow;
use core::ops::Deref;
use derive_more::derive::{AsRef, Display, Into};
use bevy_utils::prelude::DebugName;
use derive_more::derive::{Display, Into};
/// [`SystemParam`] that returns the name of the system which it is used in.
///
@ -35,21 +34,13 @@ use derive_more::derive::{AsRef, Display, Into};
/// logger.log("Hello");
/// }
/// ```
#[derive(Debug, Into, Display, AsRef)]
#[as_ref(str)]
pub struct SystemName(Cow<'static, str>);
#[derive(Debug, Into, Display)]
pub struct SystemName(DebugName);
impl SystemName {
/// Gets the name of the system.
pub fn name(&self) -> &str {
&self.0
}
}
impl Deref for SystemName {
type Target = str;
fn deref(&self) -> &Self::Target {
self.name()
pub fn name(&self) -> DebugName {
self.0.clone()
}
}
@ -104,7 +95,7 @@ mod tests {
#[test]
fn test_system_name_regular_param() {
fn testing(name: SystemName) -> String {
name.name().to_owned()
name.name().as_string()
}
let mut world = World::default();
@ -116,7 +107,7 @@ mod tests {
#[test]
fn test_system_name_exclusive_param() {
fn testing(_world: &mut World, name: SystemName) -> String {
name.name().to_owned()
name.name().as_string()
}
let mut world = World::default();
@ -130,7 +121,7 @@ mod tests {
let mut world = World::default();
let system =
IntoSystem::into_system(|name: SystemName| name.name().to_owned()).with_name("testing");
let name = world.run_system_once(system).unwrap();
let name = world.run_system_once(system).unwrap().as_string();
assert_eq!(name, "testing");
}
@ -140,7 +131,7 @@ mod tests {
let system =
IntoSystem::into_system(|_world: &mut World, name: SystemName| name.name().to_owned())
.with_name("testing");
let name = world.run_system_once(system).unwrap();
let name = world.run_system_once(system).unwrap().as_string();
assert_eq!(name, "testing");
}
}

View File

@ -25,6 +25,7 @@ use alloc::{
pub use bevy_ecs_macros::SystemParam;
use bevy_platform::cell::SyncCell;
use bevy_ptr::UnsafeCellDeref;
use bevy_utils::prelude::DebugName;
use core::{
any::Any,
fmt::{Debug, Display},
@ -32,7 +33,6 @@ use core::{
ops::{Deref, DerefMut},
panic::Location,
};
use disqualified::ShortName;
use thiserror::Error;
use super::Populated;
@ -343,8 +343,8 @@ unsafe impl<D: QueryData + 'static, F: QueryFilter + 'static> SystemParam for Qu
) {
assert_component_access_compatibility(
&system_meta.name,
core::any::type_name::<D>(),
core::any::type_name::<F>(),
DebugName::type_name::<D>(),
DebugName::type_name::<F>(),
component_access_set,
&state.component_access,
world,
@ -368,9 +368,9 @@ unsafe impl<D: QueryData + 'static, F: QueryFilter + 'static> SystemParam for Qu
}
fn assert_component_access_compatibility(
system_name: &str,
query_type: &'static str,
filter_type: &'static str,
system_name: &DebugName,
query_type: DebugName,
filter_type: DebugName,
system_access: &FilteredAccessSet<ComponentId>,
current: &FilteredAccess<ComponentId>,
world: &World,
@ -384,14 +384,16 @@ fn assert_component_access_compatibility(
if !accesses.is_empty() {
accesses.push(' ');
}
panic!("error[B0001]: Query<{}, {}> in system {system_name} accesses component(s) {accesses}in a way that conflicts with a previous system parameter. Consider using `Without<T>` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevy.org/learn/errors/b0001", ShortName(query_type), ShortName(filter_type));
panic!("error[B0001]: Query<{}, {}> in system {system_name} accesses component(s) {accesses}in a way that conflicts with a previous system parameter. Consider using `Without<T>` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevy.org/learn/errors/b0001", query_type.shortname(), filter_type.shortname());
}
// SAFETY: Relevant query ComponentId access is applied to SystemMeta. If
// this Query conflicts with any prior access, a panic will occur.
unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam for Single<'a, D, F> {
unsafe impl<'a, 'b, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam
for Single<'a, 'b, D, F>
{
type State = QueryState<D, F>;
type Item<'w, 's> = Single<'w, D, F>;
type Item<'w, 's> = Single<'w, 's, D, F>;
fn init_state(world: &mut World) -> Self::State {
Query::init_state(world)
@ -451,8 +453,8 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam fo
}
// SAFETY: QueryState is constrained to read-only fetches, so it only reads World.
unsafe impl<'a, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> ReadOnlySystemParam
for Single<'a, D, F>
unsafe impl<'a, 'b, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> ReadOnlySystemParam
for Single<'a, 'b, D, F>
{
}
@ -765,9 +767,10 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> {
assert!(
!combined_access.has_resource_write(component_id),
"error[B0002]: Res<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002",
core::any::type_name::<T>(),
DebugName::type_name::<T>(),
system_meta.name,
);
component_access_set.add_unfiltered_resource_read(component_id);
}
@ -805,8 +808,8 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> {
panic!(
"Resource requested by {} does not exist: {}",
system_meta.name,
core::any::type_name::<T>()
)
DebugName::type_name::<T>()
);
});
Res {
value: ptr.deref(),
@ -841,11 +844,11 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> {
if combined_access.has_resource_write(component_id) {
panic!(
"error[B0002]: ResMut<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002",
core::any::type_name::<T>(), system_meta.name);
DebugName::type_name::<T>(), system_meta.name);
} else if combined_access.has_resource_read(component_id) {
panic!(
"error[B0002]: ResMut<{}> in system {} conflicts with a previous Res<{0}> access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002",
core::any::type_name::<T>(), system_meta.name);
DebugName::type_name::<T>(), system_meta.name);
}
component_access_set.add_unfiltered_resource_write(component_id);
}
@ -883,8 +886,8 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> {
panic!(
"Resource requested by {} does not exist: {}",
system_meta.name,
core::any::type_name::<T>()
)
DebugName::type_name::<T>()
);
});
ResMut {
value: value.value.deref_mut::<T>(),
@ -1433,7 +1436,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> {
assert!(
!combined_access.has_resource_write(component_id),
"error[B0002]: NonSend<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002",
core::any::type_name::<T>(),
DebugName::type_name::<T>(),
system_meta.name,
);
component_access_set.add_unfiltered_resource_read(component_id);
@ -1473,7 +1476,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> {
panic!(
"Non-send resource requested by {} does not exist: {}",
system_meta.name,
core::any::type_name::<T>()
DebugName::type_name::<T>()
)
});
@ -1509,11 +1512,11 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> {
if combined_access.has_component_write(component_id) {
panic!(
"error[B0002]: NonSendMut<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002",
core::any::type_name::<T>(), system_meta.name);
DebugName::type_name::<T>(), system_meta.name);
} else if combined_access.has_component_read(component_id) {
panic!(
"error[B0002]: NonSendMut<{}> in system {} conflicts with a previous immutable resource access ({0}). Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002",
core::any::type_name::<T>(), system_meta.name);
DebugName::type_name::<T>(), system_meta.name);
}
component_access_set.add_unfiltered_resource_write(component_id);
}
@ -1552,8 +1555,8 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> {
panic!(
"Non-send resource requested by {} does not exist: {}",
system_meta.name,
core::any::type_name::<T>()
)
DebugName::type_name::<T>()
);
});
NonSendMut {
value: ptr.assert_unique().deref_mut(),
@ -2805,7 +2808,7 @@ pub struct SystemParamValidationError {
/// A string identifying the invalid parameter.
/// This is usually the type name of the parameter.
pub param: Cow<'static, str>,
pub param: DebugName,
/// A string identifying the field within a parameter using `#[derive(SystemParam)]`.
/// This will be an empty string for other parameters.
@ -2837,7 +2840,7 @@ impl SystemParamValidationError {
Self {
skipped,
message: message.into(),
param: Cow::Borrowed(core::any::type_name::<T>()),
param: DebugName::type_name::<T>(),
field: field.into(),
}
}
@ -2848,7 +2851,7 @@ impl Display for SystemParamValidationError {
write!(
fmt,
"Parameter `{}{}` failed validation: {}",
ShortName(&self.param),
self.param.shortname(),
self.field,
self.message
)?;

View File

@ -651,6 +651,19 @@ mod tests {
assert_eq!(output, NonCopy(3));
}
#[test]
fn fallible_system() {
fn sys() -> Result<()> {
Err("error")?;
Ok(())
}
let mut world = World::new();
let fallible_system_id = world.register_system(sys);
let output = world.run_system(fallible_system_id);
assert!(matches!(output, Ok(Err(_))));
}
#[test]
fn exclusive_system() {
let mut world = World::new();
@ -751,19 +764,54 @@ mod tests {
assert!(matches!(output, Ok(x) if x == four()));
}
#[test]
fn cached_fallible_system() {
fn sys() -> Result<()> {
Err("error")?;
Ok(())
}
let mut world = World::new();
let fallible_system_id = world.register_system_cached(sys);
let output = world.run_system(fallible_system_id);
assert!(matches!(output, Ok(Err(_))));
let output = world.run_system_cached(sys);
assert!(matches!(output, Ok(Err(_))));
let output = world.run_system_cached_with(sys, ());
assert!(matches!(output, Ok(Err(_))));
}
#[test]
fn cached_system_commands() {
fn sys(mut counter: ResMut<Counter>) {
counter.0 = 1;
counter.0 += 1;
}
let mut world = World::new();
world.insert_resource(Counter(0));
world.commands().run_system_cached(sys);
world.flush_commands();
assert_eq!(world.resource::<Counter>().0, 1);
world.commands().run_system_cached_with(sys, ());
world.flush_commands();
assert_eq!(world.resource::<Counter>().0, 2);
}
#[test]
fn cached_fallible_system_commands() {
fn sys(mut counter: ResMut<Counter>) -> Result {
counter.0 += 1;
Ok(())
}
let mut world = World::new();
world.insert_resource(Counter(0));
world.commands().run_system_cached(sys);
world.flush_commands();
assert_eq!(world.resource::<Counter>().0, 1);
world.commands().run_system_cached_with(sys, ());
world.flush_commands();
assert_eq!(world.resource::<Counter>().0, 2);
}
#[test]

View File

@ -1,6 +1,10 @@
//! A trait for components that let you traverse the ECS.
use crate::{entity::Entity, query::ReadOnlyQueryData, relationship::Relationship};
use crate::{
entity::Entity,
query::{ReadOnlyQueryData, ReleaseStateQueryData},
relationship::Relationship,
};
/// A component that can point to another entity, and which can be used to define a path through the ECS.
///
@ -20,13 +24,13 @@ use crate::{entity::Entity, query::ReadOnlyQueryData, relationship::Relationship
/// [specify the direction]: crate::event::EntityEvent::Traversal
/// [event propagation]: crate::observer::On::propagate
/// [observers]: crate::observer::Observer
pub trait Traversal<D: ?Sized>: ReadOnlyQueryData {
pub trait Traversal<D: ?Sized>: ReadOnlyQueryData + ReleaseStateQueryData {
/// Returns the next entity to visit.
fn traverse(item: Self::Item<'_>, data: &D) -> Option<Entity>;
fn traverse(item: Self::Item<'_, '_>, data: &D) -> Option<Entity>;
}
impl<D> Traversal<D> for () {
fn traverse(_: Self::Item<'_>, _data: &D) -> Option<Entity> {
fn traverse(_: Self::Item<'_, '_>, _data: &D) -> Option<Entity> {
None
}
}
@ -39,7 +43,7 @@ impl<D> Traversal<D> for () {
///
/// [event propagation]: crate::observer::On::propagate
impl<R: Relationship, D> Traversal<D> for &R {
fn traverse(item: Self::Item<'_>, _data: &D) -> Option<Entity> {
fn traverse(item: Self::Item<'_, '_>, _data: &D) -> Option<Entity> {
Some(item.get())
}
}

View File

@ -1,5 +1,7 @@
use core::ops::Deref;
use bevy_utils::prelude::DebugName;
use crate::{
archetype::Archetype,
change_detection::{MaybeLocation, MutUntyped},
@ -460,7 +462,7 @@ impl<'w> DeferredWorld<'w> {
Did you forget to add it using `app.insert_resource` / `app.init_resource`?
Resources are also implicitly added via `app.add_event`,
and can be added by plugins.",
core::any::type_name::<R>()
DebugName::type_name::<R>()
),
}
}
@ -489,7 +491,7 @@ impl<'w> DeferredWorld<'w> {
"Requested non-send resource {} does not exist in the `World`.
Did you forget to add it using `app.insert_non_send_resource` / `app.init_non_send_resource`?
Non-send resources can also be added by plugins.",
core::any::type_name::<R>()
DebugName::type_name::<R>()
),
}
}
@ -532,7 +534,7 @@ impl<'w> DeferredWorld<'w> {
let Some(mut events_resource) = self.get_resource_mut::<Events<E>>() else {
log::error!(
"Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ",
core::any::type_name::<E>()
DebugName::type_name::<E>()
);
return None;
};

View File

@ -16,7 +16,7 @@ use crate::{
event::EntityEvent,
lifecycle::{DESPAWN, REMOVE, REPLACE},
observer::Observer,
query::{Access, DebugCheckedUnwrap, ReadOnlyQueryData},
query::{Access, DebugCheckedUnwrap, ReadOnlyQueryData, ReleaseStateQueryData},
relationship::RelationshipHookMode,
resource::Resource,
system::IntoObserverSystem,
@ -285,14 +285,16 @@ impl<'w> EntityRef<'w> {
/// # Panics
///
/// If the entity does not have the components required by the query `Q`.
pub fn components<Q: ReadOnlyQueryData>(&self) -> Q::Item<'w> {
pub fn components<Q: ReadOnlyQueryData + ReleaseStateQueryData>(&self) -> Q::Item<'w, 'static> {
self.get_components::<Q>()
.expect("Query does not match the current entity")
}
/// Returns read-only components for the current entity that match the query `Q`,
/// or `None` if the entity does not have the components required by the query `Q`.
pub fn get_components<Q: ReadOnlyQueryData>(&self) -> Option<Q::Item<'w>> {
pub fn get_components<Q: ReadOnlyQueryData + ReleaseStateQueryData>(
&self,
) -> Option<Q::Item<'w, 'static>> {
// SAFETY: We have read-only access to all components of this entity.
unsafe { self.cell.get_components::<Q>() }
}
@ -558,13 +560,15 @@ impl<'w> EntityMut<'w> {
/// # Panics
///
/// If the entity does not have the components required by the query `Q`.
pub fn components<Q: ReadOnlyQueryData>(&self) -> Q::Item<'_> {
pub fn components<Q: ReadOnlyQueryData + ReleaseStateQueryData>(&self) -> Q::Item<'_, 'static> {
self.as_readonly().components::<Q>()
}
/// Returns read-only components for the current entity that match the query `Q`,
/// or `None` if the entity does not have the components required by the query `Q`.
pub fn get_components<Q: ReadOnlyQueryData>(&self) -> Option<Q::Item<'_>> {
pub fn get_components<Q: ReadOnlyQueryData + ReleaseStateQueryData>(
&self,
) -> Option<Q::Item<'_, 'static>> {
self.as_readonly().get_components::<Q>()
}
@ -1325,7 +1329,7 @@ impl<'w> EntityWorldMut<'w> {
/// If the entity does not have the components required by the query `Q` or if the entity
/// has been despawned while this `EntityWorldMut` is still alive.
#[inline]
pub fn components<Q: ReadOnlyQueryData>(&self) -> Q::Item<'_> {
pub fn components<Q: ReadOnlyQueryData + ReleaseStateQueryData>(&self) -> Q::Item<'_, 'static> {
self.as_readonly().components::<Q>()
}
@ -1336,7 +1340,9 @@ impl<'w> EntityWorldMut<'w> {
///
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
#[inline]
pub fn get_components<Q: ReadOnlyQueryData>(&self) -> Option<Q::Item<'_>> {
pub fn get_components<Q: ReadOnlyQueryData + ReleaseStateQueryData>(
&self,
) -> Option<Q::Item<'_, 'static>> {
self.as_readonly().get_components::<Q>()
}

View File

@ -1,6 +1,7 @@
//! Contains error types returned by bevy's schedule.
use alloc::vec::Vec;
use bevy_utils::prelude::DebugName;
use crate::{
component::ComponentId,
@ -24,7 +25,7 @@ pub struct TryRunScheduleError(pub InternedScheduleLabel);
#[error("Could not insert bundles of type {bundle_type} into the entities with the following IDs because they do not exist: {entities:?}")]
pub struct TryInsertBatchError {
/// The bundles' type name.
pub bundle_type: &'static str,
pub bundle_type: DebugName,
/// The IDs of the provided entities that do not exist.
pub entities: Vec<Entity>,
}

View File

@ -25,6 +25,7 @@ use crate::{
prelude::{Add, Despawn, Insert, Remove, Replace},
};
pub use bevy_ecs_macros::FromWorld;
use bevy_utils::prelude::DebugName;
pub use deferred_world::DeferredWorld;
pub use entity_fetch::{EntityFetcher, WorldEntityFetch};
pub use entity_ref::{
@ -264,6 +265,12 @@ impl World {
&self.removed_components
}
/// Retrieves this world's [`Observers`] list
#[inline]
pub fn observers(&self) -> &Observers {
&self.observers
}
/// Creates a new [`Commands`] instance that writes to the world's command queue
/// Use [`World::flush`] to apply all queued commands
#[inline]
@ -2087,7 +2094,7 @@ impl World {
Did you forget to add it using `app.insert_resource` / `app.init_resource`?
Resources are also implicitly added via `app.add_event`,
and can be added by plugins.",
core::any::type_name::<R>()
DebugName::type_name::<R>()
),
}
}
@ -2111,7 +2118,7 @@ impl World {
Did you forget to add it using `app.insert_resource` / `app.init_resource`?
Resources are also implicitly added via `app.add_event`,
and can be added by plugins.",
core::any::type_name::<R>()
DebugName::type_name::<R>()
),
}
}
@ -2135,7 +2142,7 @@ impl World {
Did you forget to add it using `app.insert_resource` / `app.init_resource`?
Resources are also implicitly added via `app.add_event`,
and can be added by plugins.",
core::any::type_name::<R>()
DebugName::type_name::<R>()
),
}
}
@ -2299,7 +2306,7 @@ impl World {
"Requested non-send resource {} does not exist in the `World`.
Did you forget to add it using `app.insert_non_send_resource` / `app.init_non_send_resource`?
Non-send resources can also be added by plugins.",
core::any::type_name::<R>()
DebugName::type_name::<R>()
),
}
}
@ -2321,7 +2328,7 @@ impl World {
"Requested non-send resource {} does not exist in the `World`.
Did you forget to add it using `app.insert_non_send_resource` / `app.init_non_send_resource`?
Non-send resources can also be added by plugins.",
core::any::type_name::<R>()
DebugName::type_name::<R>()
),
}
}
@ -2660,7 +2667,7 @@ impl World {
Ok(())
} else {
Err(TryInsertBatchError {
bundle_type: core::any::type_name::<B>(),
bundle_type: DebugName::type_name::<B>(),
entities: invalid_entities,
})
}
@ -2694,7 +2701,7 @@ impl World {
#[track_caller]
pub fn resource_scope<R: Resource, U>(&mut self, f: impl FnOnce(&mut World, Mut<R>) -> U) -> U {
self.try_resource_scope(f)
.unwrap_or_else(|| panic!("resource does not exist: {}", core::any::type_name::<R>()))
.unwrap_or_else(|| panic!("resource does not exist: {}", DebugName::type_name::<R>()))
}
/// Temporarily removes the requested resource from this [`World`] if it exists, runs custom user code,
@ -2734,7 +2741,7 @@ impl World {
assert!(!self.contains_resource::<R>(),
"Resource `{}` was inserted during a call to World::resource_scope.\n\
This is not allowed as the original resource is reinserted to the world after the closure is invoked.",
core::any::type_name::<R>());
DebugName::type_name::<R>());
OwningPtr::make(value, |ptr| {
// SAFETY: pointer is of type R
@ -2775,7 +2782,7 @@ impl World {
let Some(mut events_resource) = self.get_resource_mut::<Events<E>>() else {
log::error!(
"Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ",
core::any::type_name::<E>()
DebugName::type_name::<E>()
);
return None;
};
@ -3752,6 +3759,7 @@ mod tests {
};
use bevy_ecs_macros::Component;
use bevy_platform::collections::{HashMap, HashSet};
use bevy_utils::prelude::DebugName;
use core::{
any::TypeId,
panic,
@ -3935,12 +3943,12 @@ mod tests {
let mut iter = world.iter_resources();
let (info, ptr) = iter.next().unwrap();
assert_eq!(info.name(), core::any::type_name::<TestResource>());
assert_eq!(info.name(), DebugName::type_name::<TestResource>());
// SAFETY: We know that the resource is of type `TestResource`
assert_eq!(unsafe { ptr.deref::<TestResource>().0 }, 42);
let (info, ptr) = iter.next().unwrap();
assert_eq!(info.name(), core::any::type_name::<TestResource2>());
assert_eq!(info.name(), DebugName::type_name::<TestResource2>());
assert_eq!(
// SAFETY: We know that the resource is of type `TestResource2`
unsafe { &ptr.deref::<TestResource2>().0 },
@ -3963,14 +3971,14 @@ mod tests {
let mut iter = world.iter_resources_mut();
let (info, mut mut_untyped) = iter.next().unwrap();
assert_eq!(info.name(), core::any::type_name::<TestResource>());
assert_eq!(info.name(), DebugName::type_name::<TestResource>());
// SAFETY: We know that the resource is of type `TestResource`
unsafe {
mut_untyped.as_mut().deref_mut::<TestResource>().0 = 43;
};
let (info, mut mut_untyped) = iter.next().unwrap();
assert_eq!(info.name(), core::any::type_name::<TestResource2>());
assert_eq!(info.name(), DebugName::type_name::<TestResource2>());
// SAFETY: We know that the resource is of type `TestResource2`
unsafe {
mut_untyped.as_mut().deref_mut::<TestResource2>().0 = "Hello, world?".to_string();

View File

@ -4,8 +4,8 @@ use core::any::TypeId;
use thiserror::Error;
use alloc::string::{String, ToString};
use bevy_reflect::{Reflect, ReflectFromPtr};
use bevy_utils::prelude::DebugName;
use crate::{prelude::*, world::ComponentId};
@ -77,10 +77,7 @@ impl World {
};
let Some(comp_ptr) = self.get_by_id(entity, component_id) else {
let component_name = self
.components()
.get_name(component_id)
.map(|name| name.to_string());
let component_name = self.components().get_name(component_id);
return Err(GetComponentReflectError::EntityDoesNotHaveComponent {
entity,
@ -166,10 +163,7 @@ impl World {
// HACK: Only required for the `None`-case/`else`-branch, but it borrows `self`, which will
// already be mutably borrowed by `self.get_mut_by_id()`, and I didn't find a way around it.
let component_name = self
.components()
.get_name(component_id)
.map(|name| name.to_string());
let component_name = self.components().get_name(component_id).clone();
let Some(comp_mut_untyped) = self.get_mut_by_id(entity, component_id) else {
return Err(GetComponentReflectError::EntityDoesNotHaveComponent {
@ -223,7 +217,7 @@ pub enum GetComponentReflectError {
component_id: ComponentId,
/// The name corresponding to the [`Component`] with the given [`TypeId`], or `None`
/// if not available.
component_name: Option<String>,
component_name: Option<DebugName>,
},
/// The [`World`] was missing the [`AppTypeRegistry`] resource.

View File

@ -14,7 +14,7 @@ use crate::{
lifecycle::RemovedComponentEvents,
observer::Observers,
prelude::Component,
query::{DebugCheckedUnwrap, ReadOnlyQueryData},
query::{DebugCheckedUnwrap, ReadOnlyQueryData, ReleaseStateQueryData},
resource::Resource,
storage::{ComponentSparseSet, Storages, Table},
world::RawCommandQueue,
@ -1005,7 +1005,9 @@ impl<'w> UnsafeEntityCell<'w> {
/// It is the caller's responsibility to ensure that
/// - the [`UnsafeEntityCell`] has permission to access the queried data immutably
/// - no mutable references to the queried data exist at the same time
pub(crate) unsafe fn get_components<Q: ReadOnlyQueryData>(&self) -> Option<Q::Item<'w>> {
pub(crate) unsafe fn get_components<Q: ReadOnlyQueryData + ReleaseStateQueryData>(
&self,
) -> Option<Q::Item<'w, 'static>> {
// SAFETY: World is only used to access query data and initialize query state
let state = unsafe {
let world = self.world().world();
@ -1035,7 +1037,8 @@ impl<'w> UnsafeEntityCell<'w> {
// Table corresponds to archetype. State is the same state used to init fetch above.
unsafe { Q::set_archetype(&mut fetch, &state, archetype, table) }
// SAFETY: Called after set_archetype above. Entity and location are guaranteed to exist.
unsafe { Some(Q::fetch(&mut fetch, self.id(), location.table_row)) }
let item = unsafe { Q::fetch(&state, &mut fetch, self.id(), location.table_row) };
Some(Q::release_state(item))
} else {
None
}

View File

@ -631,8 +631,8 @@ impl<const I: usize, P: PhaseItem> RenderCommand<P> for SetLineGizmoBindGroup<I>
#[inline]
fn render<'w>(
_item: &P,
_view: ROQueryItem<'w, Self::ViewQuery>,
uniform_index: Option<ROQueryItem<'w, Self::ItemQuery>>,
_view: ROQueryItem<'w, '_, Self::ViewQuery>,
uniform_index: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
@ -662,8 +662,8 @@ impl<P: PhaseItem, const STRIP: bool> RenderCommand<P> for DrawLineGizmo<STRIP>
#[inline]
fn render<'w>(
_item: &P,
_view: ROQueryItem<'w, Self::ViewQuery>,
config: Option<ROQueryItem<'w, Self::ItemQuery>>,
_view: ROQueryItem<'w, '_, Self::ViewQuery>,
config: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
line_gizmos: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
@ -725,8 +725,8 @@ impl<P: PhaseItem> RenderCommand<P> for DrawLineJointGizmo {
#[inline]
fn render<'w>(
_item: &P,
_view: ROQueryItem<'w, Self::ViewQuery>,
config: Option<ROQueryItem<'w, Self::ItemQuery>>,
_view: ROQueryItem<'w, '_, Self::ViewQuery>,
config: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
line_gizmos: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {

View File

@ -0,0 +1,80 @@
use core::f32::consts::PI;
use bevy_math::{Mat4, Quat, Vec3};
use bevy_transform::components::Transform;
pub(crate) trait ConvertCoordinates {
/// Converts the glTF coordinates to Bevy's coordinate system.
/// - glTF:
/// - forward: Z
/// - up: Y
/// - right: -X
/// - Bevy:
/// - forward: -Z
/// - up: Y
/// - right: X
///
/// See <https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#coordinate-system-and-units>
fn convert_coordinates(self) -> Self;
}
pub(crate) trait ConvertCameraCoordinates {
/// Like `convert_coordinates`, but uses the following for the lens rotation:
/// - forward: -Z
/// - up: Y
/// - right: X
///
/// See <https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#view-matrix>
fn convert_camera_coordinates(self) -> Self;
}
impl ConvertCoordinates for Vec3 {
fn convert_coordinates(self) -> Self {
Vec3::new(-self.x, self.y, -self.z)
}
}
impl ConvertCoordinates for [f32; 3] {
fn convert_coordinates(self) -> Self {
[-self[0], self[1], -self[2]]
}
}
impl ConvertCoordinates for [f32; 4] {
fn convert_coordinates(self) -> Self {
// Solution of q' = r q r*
[-self[0], self[1], -self[2], self[3]]
}
}
impl ConvertCoordinates for Quat {
fn convert_coordinates(self) -> Self {
// Solution of q' = r q r*
Quat::from_array([-self.x, self.y, -self.z, self.w])
}
}
impl ConvertCoordinates for Mat4 {
fn convert_coordinates(self) -> Self {
let m: Mat4 = Mat4::from_scale(Vec3::new(-1.0, 1.0, -1.0));
// Same as the original matrix
let m_inv = m;
m_inv * self * m
}
}
impl ConvertCoordinates for Transform {
fn convert_coordinates(mut self) -> Self {
self.translation = self.translation.convert_coordinates();
self.rotation = self.rotation.convert_coordinates();
self
}
}
impl ConvertCameraCoordinates for Transform {
fn convert_camera_coordinates(mut self) -> Self {
self.translation = self.translation.convert_coordinates();
self.rotate_y(PI);
self
}
}

View File

@ -91,6 +91,7 @@
//! You can use [`GltfAssetLabel`] to ensure you are using the correct label.
mod assets;
mod convert_coordinates;
mod label;
mod loader;
mod vertex_attributes;

View File

@ -10,7 +10,10 @@ use itertools::Itertools;
#[cfg(feature = "bevy_animation")]
use bevy_platform::collections::{HashMap, HashSet};
use crate::GltfError;
use crate::{
convert_coordinates::{ConvertCameraCoordinates as _, ConvertCoordinates as _},
GltfError,
};
pub(crate) fn node_name(node: &Node) -> Name {
let name = node
@ -26,8 +29,8 @@ pub(crate) fn node_name(node: &Node) -> Name {
/// on [`Node::transform()`](gltf::Node::transform) directly because it uses optimized glam types and
/// if `libm` feature of `bevy_math` crate is enabled also handles cross
/// platform determinism properly.
pub(crate) fn node_transform(node: &Node) -> Transform {
match node.transform() {
pub(crate) fn node_transform(node: &Node, convert_coordinates: bool) -> Transform {
let transform = match node.transform() {
gltf::scene::Transform::Matrix { matrix } => {
Transform::from_matrix(Mat4::from_cols_array_2d(&matrix))
}
@ -40,6 +43,15 @@ pub(crate) fn node_transform(node: &Node) -> Transform {
rotation: bevy_math::Quat::from_array(rotation),
scale: Vec3::from(scale),
},
};
if convert_coordinates {
if node.camera().is_some() {
transform.convert_camera_coordinates()
} else {
transform.convert_coordinates()
}
} else {
transform
}
}

View File

@ -84,6 +84,7 @@ use self::{
texture::{texture_handle, texture_sampler, texture_transform_to_affine2},
},
};
use crate::convert_coordinates::ConvertCoordinates as _;
/// An error that occurs when loading a glTF file.
#[derive(Error, Debug)]
@ -191,6 +192,16 @@ pub struct GltfLoaderSettings {
pub default_sampler: Option<ImageSamplerDescriptor>,
/// If true, the loader will ignore sampler data from gltf and use the default sampler.
pub override_sampler: bool,
/// If true, the loader will convert glTF coordinates to Bevy's coordinate system.
/// - glTF:
/// - forward: Z
/// - up: Y
/// - right: -X
/// - Bevy:
/// - forward: -Z
/// - up: Y
/// - right: X
pub convert_coordinates: bool,
}
impl Default for GltfLoaderSettings {
@ -203,6 +214,7 @@ impl Default for GltfLoaderSettings {
include_source: false,
default_sampler: None,
override_sampler: false,
convert_coordinates: false,
}
}
}
@ -303,7 +315,16 @@ async fn load_gltf<'a, 'b, 'c>(
match outputs {
ReadOutputs::Translations(tr) => {
let translation_property = animated_field!(Transform::translation);
let translations: Vec<Vec3> = tr.map(Vec3::from).collect();
let translations: Vec<Vec3> = tr
.map(Vec3::from)
.map(|verts| {
if settings.convert_coordinates {
Vec3::convert_coordinates(verts)
} else {
verts
}
})
.collect();
if keyframe_timestamps.len() == 1 {
Some(VariableCurve::new(AnimatableCurve::new(
translation_property,
@ -350,8 +371,17 @@ async fn load_gltf<'a, 'b, 'c>(
}
ReadOutputs::Rotations(rots) => {
let rotation_property = animated_field!(Transform::rotation);
let rotations: Vec<Quat> =
rots.into_f32().map(Quat::from_array).collect();
let rotations: Vec<Quat> = rots
.into_f32()
.map(Quat::from_array)
.map(|quat| {
if settings.convert_coordinates {
Quat::convert_coordinates(quat)
} else {
quat
}
})
.collect();
if keyframe_timestamps.len() == 1 {
Some(VariableCurve::new(AnimatableCurve::new(
rotation_property,
@ -633,6 +663,7 @@ async fn load_gltf<'a, 'b, 'c>(
accessor,
&buffer_data,
&loader.custom_vertex_attributes,
settings.convert_coordinates,
) {
Ok((attribute, values)) => mesh.insert_attribute(attribute, values),
Err(err) => warn!("{}", err),
@ -752,7 +783,17 @@ async fn load_gltf<'a, 'b, 'c>(
let reader = gltf_skin.reader(|buffer| Some(&buffer_data[buffer.index()]));
let local_to_bone_bind_matrices: Vec<Mat4> = reader
.read_inverse_bind_matrices()
.map(|mats| mats.map(|mat| Mat4::from_cols_array_2d(&mat)).collect())
.map(|mats| {
mats.map(|mat| Mat4::from_cols_array_2d(&mat))
.map(|mat| {
if settings.convert_coordinates {
mat.convert_coordinates()
} else {
mat
}
})
.collect()
})
.unwrap_or_else(|| {
core::iter::repeat_n(Mat4::IDENTITY, gltf_skin.joints().len()).collect()
});
@ -834,7 +875,7 @@ async fn load_gltf<'a, 'b, 'c>(
&node,
children,
mesh,
node_transform(&node),
node_transform(&node, settings.convert_coordinates),
skin,
node.extras().as_deref().map(GltfExtras::from),
);
@ -1306,7 +1347,7 @@ fn load_node(
document: &Document,
) -> Result<(), GltfError> {
let mut gltf_error = None;
let transform = node_transform(gltf_node);
let transform = node_transform(gltf_node, settings.convert_coordinates);
let world_transform = *parent_transform * transform;
// according to https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#instantiation,
// if the determinant of the transform is negative we must invert the winding order of
@ -1359,7 +1400,6 @@ fn load_node(
},
..OrthographicProjection::default_3d()
};
Projection::Orthographic(orthographic_projection)
}
gltf::camera::Projection::Perspective(perspective) => {
@ -1377,6 +1417,7 @@ fn load_node(
Projection::Perspective(perspective_projection)
}
};
node.insert((
Camera3d::default(),
projection,

View File

@ -6,6 +6,8 @@ use gltf::{
};
use thiserror::Error;
use crate::convert_coordinates::ConvertCoordinates;
/// Represents whether integer data requires normalization
#[derive(Copy, Clone)]
struct Normalization(bool);
@ -132,15 +134,23 @@ impl<'a> VertexAttributeIter<'a> {
}
/// Materializes values for any supported format of vertex attribute
fn into_any_values(self) -> Result<Values, AccessFailed> {
fn into_any_values(self, convert_coordinates: bool) -> Result<Values, AccessFailed> {
match self {
VertexAttributeIter::F32(it) => Ok(Values::Float32(it.collect())),
VertexAttributeIter::U32(it) => Ok(Values::Uint32(it.collect())),
VertexAttributeIter::F32x2(it) => Ok(Values::Float32x2(it.collect())),
VertexAttributeIter::U32x2(it) => Ok(Values::Uint32x2(it.collect())),
VertexAttributeIter::F32x3(it) => Ok(Values::Float32x3(it.collect())),
VertexAttributeIter::F32x3(it) => Ok(if convert_coordinates {
Values::Float32x3(it.map(ConvertCoordinates::convert_coordinates).collect())
} else {
Values::Float32x3(it.collect())
}),
VertexAttributeIter::U32x3(it) => Ok(Values::Uint32x3(it.collect())),
VertexAttributeIter::F32x4(it) => Ok(Values::Float32x4(it.collect())),
VertexAttributeIter::F32x4(it) => Ok(if convert_coordinates {
Values::Float32x4(it.map(ConvertCoordinates::convert_coordinates).collect())
} else {
Values::Float32x4(it.collect())
}),
VertexAttributeIter::U32x4(it) => Ok(Values::Uint32x4(it.collect())),
VertexAttributeIter::S16x2(it, n) => {
Ok(n.apply_either(it.collect(), Values::Snorm16x2, Values::Sint16x2))
@ -188,7 +198,7 @@ impl<'a> VertexAttributeIter<'a> {
VertexAttributeIter::U16x4(it, Normalization(true)) => Ok(Values::Float32x4(
ReadColors::RgbaU16(it).into_rgba_f32().collect(),
)),
s => s.into_any_values(),
s => s.into_any_values(false),
}
}
@ -198,7 +208,7 @@ impl<'a> VertexAttributeIter<'a> {
VertexAttributeIter::U8x4(it, Normalization(false)) => {
Ok(Values::Uint16x4(ReadJoints::U8(it).into_u16().collect()))
}
s => s.into_any_values(),
s => s.into_any_values(false),
}
}
@ -211,7 +221,7 @@ impl<'a> VertexAttributeIter<'a> {
VertexAttributeIter::U16x4(it, Normalization(true)) => {
Ok(Values::Float32x4(ReadWeights::U16(it).into_f32().collect()))
}
s => s.into_any_values(),
s => s.into_any_values(false),
}
}
@ -224,7 +234,7 @@ impl<'a> VertexAttributeIter<'a> {
VertexAttributeIter::U16x2(it, Normalization(true)) => Ok(Values::Float32x2(
ReadTexCoords::U16(it).into_f32().collect(),
)),
s => s.into_any_values(),
s => s.into_any_values(false),
}
}
}
@ -252,28 +262,49 @@ pub(crate) fn convert_attribute(
accessor: gltf::Accessor,
buffer_data: &Vec<Vec<u8>>,
custom_vertex_attributes: &HashMap<Box<str>, MeshVertexAttribute>,
convert_coordinates: bool,
) -> Result<(MeshVertexAttribute, Values), ConvertAttributeError> {
if let Some((attribute, conversion)) = match &semantic {
gltf::Semantic::Positions => Some((Mesh::ATTRIBUTE_POSITION, ConversionMode::Any)),
gltf::Semantic::Normals => Some((Mesh::ATTRIBUTE_NORMAL, ConversionMode::Any)),
gltf::Semantic::Tangents => Some((Mesh::ATTRIBUTE_TANGENT, ConversionMode::Any)),
gltf::Semantic::Colors(0) => Some((Mesh::ATTRIBUTE_COLOR, ConversionMode::Rgba)),
gltf::Semantic::TexCoords(0) => Some((Mesh::ATTRIBUTE_UV_0, ConversionMode::TexCoord)),
gltf::Semantic::TexCoords(1) => Some((Mesh::ATTRIBUTE_UV_1, ConversionMode::TexCoord)),
gltf::Semantic::Joints(0) => {
Some((Mesh::ATTRIBUTE_JOINT_INDEX, ConversionMode::JointIndex))
if let Some((attribute, conversion, convert_coordinates)) = match &semantic {
gltf::Semantic::Positions => Some((
Mesh::ATTRIBUTE_POSITION,
ConversionMode::Any,
convert_coordinates,
)),
gltf::Semantic::Normals => Some((
Mesh::ATTRIBUTE_NORMAL,
ConversionMode::Any,
convert_coordinates,
)),
gltf::Semantic::Tangents => Some((
Mesh::ATTRIBUTE_TANGENT,
ConversionMode::Any,
convert_coordinates,
)),
gltf::Semantic::Colors(0) => Some((Mesh::ATTRIBUTE_COLOR, ConversionMode::Rgba, false)),
gltf::Semantic::TexCoords(0) => {
Some((Mesh::ATTRIBUTE_UV_0, ConversionMode::TexCoord, false))
}
gltf::Semantic::Weights(0) => {
Some((Mesh::ATTRIBUTE_JOINT_WEIGHT, ConversionMode::JointWeight))
gltf::Semantic::TexCoords(1) => {
Some((Mesh::ATTRIBUTE_UV_1, ConversionMode::TexCoord, false))
}
gltf::Semantic::Joints(0) => Some((
Mesh::ATTRIBUTE_JOINT_INDEX,
ConversionMode::JointIndex,
false,
)),
gltf::Semantic::Weights(0) => Some((
Mesh::ATTRIBUTE_JOINT_WEIGHT,
ConversionMode::JointWeight,
false,
)),
gltf::Semantic::Extras(name) => custom_vertex_attributes
.get(name.as_str())
.map(|attr| (*attr, ConversionMode::Any)),
.map(|attr| (*attr, ConversionMode::Any, false)),
_ => None,
} {
let raw_iter = VertexAttributeIter::from_accessor(accessor.clone(), buffer_data);
let converted_values = raw_iter.and_then(|iter| match conversion {
ConversionMode::Any => iter.into_any_values(),
ConversionMode::Any => iter.into_any_values(convert_coordinates),
ConversionMode::Rgba => iter.into_rgba_values(),
ConversionMode::TexCoord => iter.into_tex_coord_values(),
ConversionMode::JointIndex => iter.into_joint_index_values(),

View File

@ -122,7 +122,7 @@ use {
/// [`DetectChangesMut::bypass_change_detection`]: bevy_ecs::change_detection::DetectChangesMut::bypass_change_detection
#[derive(Debug, Clone, Resource)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Default, Resource))]
pub struct ButtonInput<T: Copy + Eq + Hash + Send + Sync + 'static> {
pub struct ButtonInput<T: Clone + Eq + Hash + Send + Sync + 'static> {
/// A collection of every button that is currently being pressed.
pressed: HashSet<T>,
/// A collection of every button that has just been pressed.
@ -131,7 +131,7 @@ pub struct ButtonInput<T: Copy + Eq + Hash + Send + Sync + 'static> {
just_released: HashSet<T>,
}
impl<T: Copy + Eq + Hash + Send + Sync + 'static> Default for ButtonInput<T> {
impl<T: Clone + Eq + Hash + Send + Sync + 'static> Default for ButtonInput<T> {
fn default() -> Self {
Self {
pressed: Default::default(),
@ -143,12 +143,12 @@ impl<T: Copy + Eq + Hash + Send + Sync + 'static> Default for ButtonInput<T> {
impl<T> ButtonInput<T>
where
T: Copy + Eq + Hash + Send + Sync + 'static,
T: Clone + Eq + Hash + Send + Sync + 'static,
{
/// Registers a press for the given `input`.
pub fn press(&mut self, input: T) {
// Returns `true` if the `input` wasn't pressed.
if self.pressed.insert(input) {
if self.pressed.insert(input.clone()) {
self.just_pressed.insert(input);
}
}

View File

@ -92,8 +92,9 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
///
/// ## Usage
///
/// The event is consumed inside of the [`keyboard_input_system`]
/// to update the [`ButtonInput<KeyCode>`](ButtonInput<KeyCode>) resource.
/// The event is consumed inside of the [`keyboard_input_system`] to update the
/// [`ButtonInput<KeyCode>`](ButtonInput<KeyCode>) and
/// [`ButtonInput<Key>`](ButtonInput<Key>) resources.
#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "bevy_reflect",
@ -107,8 +108,12 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
)]
pub struct KeyboardInput {
/// The physical key code of the key.
///
/// This corresponds to the location of the key independent of the keyboard layout.
pub key_code: KeyCode,
/// The logical key of the input
/// The logical key of the input.
///
/// This corresponds to the actual key taking keyboard layout into account.
pub logical_key: Key,
/// The press state of the key.
pub state: ButtonState,
@ -148,32 +153,46 @@ pub struct KeyboardInput {
)]
pub struct KeyboardFocusLost;
/// Updates the [`ButtonInput<KeyCode>`] resource with the latest [`KeyboardInput`] events.
/// Updates the [`ButtonInput<KeyCode>`] and [`ButtonInput<Key>`] resources with the latest [`KeyboardInput`] events.
///
/// ## Differences
///
/// The main difference between the [`KeyboardInput`] event and the [`ButtonInput<KeyCode>`] resources is that
/// The main difference between the [`KeyboardInput`] event and the [`ButtonInput`] resources are that
/// the latter has convenient functions such as [`ButtonInput::pressed`], [`ButtonInput::just_pressed`] and [`ButtonInput::just_released`] and is window id agnostic.
///
/// There is a [`ButtonInput`] for both [`KeyCode`] and [`Key`] as they are both useful in different situations, see their documentation for the details.
pub fn keyboard_input_system(
mut key_input: ResMut<ButtonInput<KeyCode>>,
mut keycode_input: ResMut<ButtonInput<KeyCode>>,
mut key_input: ResMut<ButtonInput<Key>>,
mut keyboard_input_events: EventReader<KeyboardInput>,
mut focus_events: EventReader<KeyboardFocusLost>,
) {
// Avoid clearing if it's not empty to ensure change detection is not triggered.
// Avoid clearing if not empty to ensure change detection is not triggered.
keycode_input.bypass_change_detection().clear();
key_input.bypass_change_detection().clear();
for event in keyboard_input_events.read() {
let KeyboardInput {
key_code, state, ..
key_code,
logical_key,
state,
..
} = event;
match state {
ButtonState::Pressed => key_input.press(*key_code),
ButtonState::Released => key_input.release(*key_code),
ButtonState::Pressed => {
keycode_input.press(*key_code);
key_input.press(logical_key.clone());
}
ButtonState::Released => {
keycode_input.release(*key_code);
key_input.release(logical_key.clone());
}
}
}
// Release all cached input to avoid having stuck input when switching between windows in os
if !focus_events.is_empty() {
key_input.release_all();
keycode_input.release_all();
focus_events.clear();
}
}
@ -220,13 +239,13 @@ pub enum NativeKeyCode {
/// It is used as the generic `T` value of an [`ButtonInput`] to create a `Res<ButtonInput<KeyCode>>`.
///
/// Code representing the location of a physical key
/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.code`] with a few
/// This mostly conforms to the [`UI Events Specification's KeyboardEvent.code`] with a few
/// exceptions:
/// - The keys that the specification calls `MetaLeft` and `MetaRight` are named `SuperLeft` and
/// `SuperRight` here.
/// - The key that the specification calls "Super" is reported as `Unidentified` here.
///
/// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables
/// [`UI Events Specification's KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables
///
/// ## Updating
///
@ -756,6 +775,19 @@ pub enum NativeKey {
/// The logical key code of a [`KeyboardInput`].
///
/// This contains the actual value that is produced by pressing the key. This is
/// useful when you need the actual letters, and for symbols like `+` and `-`
/// when implementing zoom, as they can be in different locations depending on
/// the keyboard layout.
///
/// In many cases you want the key location instead, for example when
/// implementing WASD controls so the keys are located the same place on QWERTY
/// and other layouts. In that case use [`KeyCode`] instead.
///
/// ## Usage
///
/// It is used as the generic `T` value of an [`ButtonInput`] to create a `Res<ButtonInput<Key>>`.
///
/// ## Technical
///
/// Its values map 1 to 1 to winit's Key.

View File

@ -49,7 +49,7 @@ use bevy_ecs::prelude::*;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use gestures::*;
use keyboard::{keyboard_input_system, KeyCode, KeyboardFocusLost, KeyboardInput};
use keyboard::{keyboard_input_system, Key, KeyCode, KeyboardFocusLost, KeyboardInput};
use mouse::{
accumulate_mouse_motion_system, accumulate_mouse_scroll_system, mouse_button_input_system,
AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButton, MouseButtonInput, MouseMotion,
@ -89,6 +89,7 @@ impl Plugin for InputPlugin {
.add_event::<KeyboardInput>()
.add_event::<KeyboardFocusLost>()
.init_resource::<ButtonInput<KeyCode>>()
.init_resource::<ButtonInput<Key>>()
.add_systems(PreUpdate, keyboard_input_system.in_set(InputSystems))
// mouse
.add_event::<MouseButtonInput>()

View File

@ -155,7 +155,7 @@ pub struct WindowTraversal {
}
impl<E: BufferedEvent + Clone> Traversal<FocusedInput<E>> for WindowTraversal {
fn traverse(item: Self::Item<'_>, event: &FocusedInput<E>) -> Option<Entity> {
fn traverse(item: Self::Item<'_, '_>, event: &FocusedInput<E>) -> Option<Entity> {
let WindowTraversalItem { child_of, window } = item;
// Send event to parent, if it has one.

View File

@ -346,6 +346,8 @@ web = [
hotpatching = ["bevy_app/hotpatching", "bevy_ecs/hotpatching"]
debug = ["bevy_utils/debug"]
[dependencies]
# bevy (no_std)
bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [

View File

@ -309,7 +309,7 @@ impl ExtractComponent for Atmosphere {
type Out = Atmosphere;
fn extract_component(item: QueryItem<'_, Self::QueryData>) -> Option<Self::Out> {
fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
Some(item.clone())
}
}
@ -405,7 +405,7 @@ impl ExtractComponent for AtmosphereSettings {
type Out = AtmosphereSettings;
fn extract_component(item: QueryItem<'_, Self::QueryData>) -> Option<Self::Out> {
fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
Some(item.clone())
}
}

View File

@ -181,7 +181,7 @@ impl ViewNode for RenderSkyNode {
view_uniforms_offset,
lights_uniforms_offset,
render_sky_pipeline_id,
): QueryItem<'w, Self::ViewQuery>,
): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let pipeline_cache = world.resource::<PipelineCache>();

View File

@ -560,7 +560,7 @@ pub(super) fn prepare_atmosphere_transforms(
};
for (entity, view) in &views {
let world_from_view = view.world_from_view.compute_matrix();
let world_from_view = view.world_from_view.to_matrix();
let camera_z = world_from_view.z_axis.truncate();
let camera_y = world_from_view.y_axis.truncate();
let atmo_z = camera_z

View File

@ -353,7 +353,7 @@ pub(crate) fn assign_objects_to_clusters(
let mut requested_cluster_dimensions = config.dimensions_for_screen_size(screen_size);
let world_from_view = camera_transform.compute_matrix();
let world_from_view = camera_transform.to_matrix();
let view_from_world_scale = camera_transform.compute_transform().scale.recip();
let view_from_world_scale_max = view_from_world_scale.abs().max_element();
let view_from_world = world_from_view.inverse();

View File

@ -341,7 +341,7 @@ pub fn build_directional_light_cascades(
.iter()
.filter_map(|(entity, transform, projection, camera)| {
if camera.is_active {
Some((entity, projection, transform.compute_matrix()))
Some((entity, projection, transform.to_matrix()))
} else {
None
}
@ -357,7 +357,7 @@ pub fn build_directional_light_cascades(
// light_to_world has orthogonal upper-left 3x3 and zero translation.
// Even though only the direction (i.e. rotation) of the light matters, we don't constrain
// users to not change any other aspects of the transform - there's no guarantee
// `transform.compute_matrix()` will give us a matrix with our desired properties.
// `transform.to_matrix()` will give us a matrix with our desired properties.
// Instead, we directly create a good matrix from just the rotation.
let world_from_light = Mat4::from_quat(transform.compute_transform().rotation);
let light_to_world_inverse = world_from_light.inverse();
@ -628,7 +628,7 @@ pub fn update_point_light_frusta(
for (view_rotation, frustum) in view_rotations.iter().zip(cubemap_frusta.iter_mut()) {
let world_from_view = view_translation * *view_rotation;
let clip_from_world = clip_from_view * world_from_view.compute_matrix().inverse();
let clip_from_world = clip_from_view * world_from_view.to_matrix().inverse();
*frustum = Frustum::from_clip_from_world_custom_far(
&clip_from_world,

View File

@ -192,7 +192,7 @@ impl ExtractInstance for EnvironmentMapIds {
type QueryFilter = ();
fn extract(item: QueryItem<'_, Self::QueryData>) -> Option<Self> {
fn extract(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self> {
Some(EnvironmentMapIds {
diffuse: item.diffuse_map.id(),
specular: item.specular_map.id(),

View File

@ -378,7 +378,7 @@ fn gather_environment_map_uniform(
let environment_map_uniform = if let Some(environment_map_light) = environment_map_light {
EnvironmentMapUniform {
transform: Transform::from_rotation(environment_map_light.rotation)
.compute_matrix()
.to_matrix()
.inverse(),
}
} else {
@ -595,7 +595,7 @@ where
) -> Option<LightProbeInfo<C>> {
environment_map.id(image_assets).map(|id| LightProbeInfo {
world_from_light: light_probe_transform.affine(),
light_from_world: light_probe_transform.compute_matrix().inverse(),
light_from_world: light_probe_transform.to_matrix().inverse(),
asset_id: id,
intensity: environment_map.intensity(),
affects_lightmapped_mesh_diffuse: environment_map.affects_lightmapped_mesh_diffuse(),

View File

@ -216,7 +216,7 @@ pub fn update_previous_view_data(
query: Query<(Entity, &Camera, &GlobalTransform), Or<(With<Camera3d>, With<ShadowView>)>>,
) {
for (entity, camera, camera_transform) in &query {
let world_from_view = camera_transform.compute_matrix();
let world_from_view = camera_transform.to_matrix();
let view_from_world = world_from_view.inverse();
let view_from_clip = camera.clip_from_view().inverse();
@ -703,7 +703,7 @@ pub fn prepare_previous_view_uniforms(
let prev_view_data = match maybe_previous_view_uniforms {
Some(previous_view) => previous_view.clone(),
None => {
let world_from_view = camera.world_from_view.compute_matrix();
let world_from_view = camera.world_from_view.to_matrix();
let view_from_world = world_from_view.inverse();
let view_from_clip = camera.clip_from_view.inverse();

View File

@ -1540,7 +1540,7 @@ fn extract_mesh_for_gpu_building(
not_shadow_caster,
no_automatic_batching,
visibility_range,
): <GpuMeshExtractionQuery as QueryData>::Item<'_>,
): <GpuMeshExtractionQuery as QueryData>::Item<'_, '_>,
render_visibility_ranges: &RenderVisibilityRanges,
render_mesh_instances: &RenderMeshInstancesGpu,
queue: &mut RenderMeshInstanceGpuQueue,
@ -2874,7 +2874,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I>
view_environment_map,
mesh_view_bind_group,
maybe_oit_layers_count_offset,
): ROQueryItem<'w, Self::ViewQuery>,
): ROQueryItem<'w, '_, Self::ViewQuery>,
_entity: Option<()>,
_: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,

View File

@ -280,7 +280,7 @@ impl ViewNode for ScreenSpaceReflectionsNode {
view_environment_map_offset,
view_bind_group,
ssr_pipeline_id,
): QueryItem<'w, Self::ViewQuery>,
): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
// Grab the render pipeline.
@ -498,7 +498,7 @@ impl ExtractComponent for ScreenSpaceReflections {
type Out = ScreenSpaceReflectionsUniform;
fn extract_component(settings: QueryItem<'_, Self::QueryData>) -> Option<Self::Out> {
fn extract_component(settings: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
if !DEPTH_TEXTURE_SAMPLING_SUPPORTED {
once!(info!(
"Disabling screen-space reflections on this platform because depth textures \

View File

@ -347,7 +347,7 @@ impl ViewNode for VolumetricFogNode {
view_ssr_offset,
msaa,
view_environment_map_offset,
): QueryItem<'w, Self::ViewQuery>,
): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let pipeline_cache = world.resource::<PipelineCache>();
@ -700,7 +700,7 @@ pub fn prepare_volumetric_fog_uniforms(
// Do this up front to avoid O(n^2) matrix inversion.
local_from_world_matrices.clear();
for (_, _, fog_transform) in fog_volumes.iter() {
local_from_world_matrices.push(fog_transform.compute_matrix().inverse());
local_from_world_matrices.push(fog_transform.to_matrix().inverse());
}
let uniform_count = view_targets.iter().len() * local_from_world_matrices.len();
@ -712,7 +712,7 @@ pub fn prepare_volumetric_fog_uniforms(
};
for (view_entity, extracted_view, volumetric_fog) in view_targets.iter() {
let world_from_view = extracted_view.world_from_view.compute_matrix();
let world_from_view = extracted_view.world_from_view.to_matrix();
let mut view_fog_volumes = vec![];

View File

@ -374,7 +374,7 @@ impl ViewNode for Wireframe3dNode {
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
(camera, view, target, depth): QueryItem<'w, Self::ViewQuery>,
(camera, view, target, depth): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let Some(wireframe_phase) = world.get_resource::<ViewBinnedRenderPhases<Wireframe3d>>()

View File

@ -86,7 +86,7 @@ impl<E> Traversal<Pointer<E>> for PointerTraversal
where
E: Debug + Clone + Reflect,
{
fn traverse(item: Self::Item<'_>, pointer: &Pointer<E>) -> Option<Entity> {
fn traverse(item: Self::Item<'_, '_>, pointer: &Pointer<E>) -> Option<Entity> {
let PointerTraversalItem { child_of, window } = item;
// Send event to parent, if it has one.

Some files were not shown because too many files have changed in this diff Show More