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 - name: CI job
# To run the tests one item at a time for troubleshooting, use # 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 # 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: env:
# -Zrandomize-layout makes sure we dont rely on the layout of anything that might change # -Zrandomize-layout makes sure we dont rely on the layout of anything that might change
RUSTFLAGS: -Zrandomize-layout RUSTFLAGS: -Zrandomize-layout

View File

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

View File

@ -847,7 +847,7 @@ impl ViewNode for SmaaNode {
view_smaa_uniform_offset, view_smaa_uniform_offset,
smaa_textures, smaa_textures,
view_smaa_bind_groups, view_smaa_bind_groups,
): QueryItem<'w, Self::ViewQuery>, ): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World, world: &'w World,
) -> Result<(), NodeRunError> { ) -> Result<(), NodeRunError> {
let pipeline_cache = world.resource::<PipelineCache>(); 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))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
//! Macros for deriving asset traits.
use bevy_macro_utils::BevyManifest; use bevy_macro_utils::BevyManifest;
use proc_macro::{Span, TokenStream}; use proc_macro::{Span, TokenStream};
use quote::{format_ident, quote}; use quote::{format_ident, quote};
@ -12,6 +13,7 @@ pub(crate) fn bevy_asset_path() -> Path {
const DEPENDENCY_ATTRIBUTE: &str = "dependency"; const DEPENDENCY_ATTRIBUTE: &str = "dependency";
/// Implement the `Asset` trait.
#[proc_macro_derive(Asset, attributes(dependency))] #[proc_macro_derive(Asset, attributes(dependency))]
pub fn derive_asset(input: TokenStream) -> TokenStream { pub fn derive_asset(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput); 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))] #[proc_macro_derive(VisitAssetDependencies, attributes(dependency))]
pub fn derive_asset_dependency_visitor(input: TokenStream) -> TokenStream { pub fn derive_asset_dependency_visitor(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput); let ast = parse_macro_input!(input as DeriveInput);

View File

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

View File

@ -1953,6 +1953,14 @@ impl AssetLoaderError {
pub fn path(&self) -> &AssetPath<'static> { pub fn path(&self) -> &AssetPath<'static> {
&self.path &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`. /// An error that occurs while resolving an asset added by `add_async`.

View File

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

View File

@ -227,7 +227,7 @@ impl ExtractComponent for Bloom {
type QueryFilter = With<Hdr>; type QueryFilter = With<Hdr>;
type Out = (Self, BloomUniforms); 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 ( match (
camera.physical_viewport_rect(), camera.physical_viewport_rect(),
camera.physical_viewport_size(), camera.physical_viewport_size(),

View File

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

View File

@ -28,7 +28,7 @@ impl ViewNode for MainTransparentPass2dNode {
&self, &self,
graph: &mut RenderGraphContext, graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>, 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, world: &'w World,
) -> Result<(), NodeRunError> { ) -> Result<(), NodeRunError> {
let Some(transparent_phases) = let Some(transparent_phases) =

View File

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

View File

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

View File

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

View File

@ -61,7 +61,7 @@ impl ViewNode for MsaaWritebackNode {
&self, &self,
_graph: &mut RenderGraphContext, _graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>, 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, world: &'w World,
) -> Result<(), NodeRunError> { ) -> Result<(), NodeRunError> {
if *msaa == Msaa::Off { if *msaa == Msaa::Off {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -74,12 +74,23 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream {
let user_generics = ast.generics.clone(); let user_generics = ast.generics.clone();
let (user_impl_generics, user_ty_generics, user_where_clauses) = user_generics.split_for_impl(); let (user_impl_generics, user_ty_generics, user_where_clauses) = user_generics.split_for_impl();
let user_generics_with_world = { let user_generics_with_world = {
let mut generics = ast.generics; let mut generics = ast.generics.clone();
generics.params.insert(0, parse_quote!('__w)); generics.params.insert(0, parse_quote!('__w));
generics generics
}; };
let (user_impl_generics_with_world, user_ty_generics_with_world, user_where_clauses_with_world) = let (user_impl_generics_with_world, user_ty_generics_with_world, user_where_clauses_with_world) =
user_generics_with_world.split_for_impl(); 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 struct_name = ast.ident;
let read_only_struct_name = if attributes.is_mutable { let read_only_struct_name = if attributes.is_mutable {
@ -164,13 +175,13 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream {
&visibility, &visibility,
&item_struct_name, &item_struct_name,
&field_types, &field_types,
&user_impl_generics_with_world, &user_impl_generics_with_world_and_state,
&field_attrs, &field_attrs,
&field_visibilities, &field_visibilities,
&field_idents, &field_idents,
&user_ty_generics, &user_ty_generics,
&user_ty_generics_with_world, &user_ty_generics_with_world_and_state,
user_where_clauses_with_world, user_where_clauses_with_world_and_state,
); );
let mutable_world_query_impl = world_query_impl( let mutable_world_query_impl = world_query_impl(
&path, &path,
@ -199,13 +210,13 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream {
&visibility, &visibility,
&read_only_item_struct_name, &read_only_item_struct_name,
&read_only_field_types, &read_only_field_types,
&user_impl_generics_with_world, &user_impl_generics_with_world_and_state,
&field_attrs, &field_attrs,
&field_visibilities, &field_visibilities,
&field_idents, &field_idents,
&user_ty_generics, &user_ty_generics,
&user_ty_generics_with_world, &user_ty_generics_with_world_and_state,
user_where_clauses_with_world, user_where_clauses_with_world_and_state,
); );
let readonly_world_query_impl = world_query_impl( let readonly_world_query_impl = world_query_impl(
&path, &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 { for #read_only_struct_name #user_ty_generics #user_where_clauses {
const IS_READ_ONLY: bool = true; const IS_READ_ONLY: bool = true;
type ReadOnly = #read_only_struct_name #user_ty_generics; 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>( fn shrink<'__wlong: '__wshort, '__wshort, '__s>(
item: Self::Item<'__wlong> item: Self::Item<'__wlong, '__s>
) -> Self::Item<'__wshort> { ) -> Self::Item<'__wshort, '__s> {
#read_only_item_struct_name { #read_only_item_struct_name {
#( #(
#field_idents: <#read_only_field_types>::shrink(item.#field_idents), #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`. /// SAFETY: we call `fetch` for each member that implements `Fetch`.
#[inline(always)] #[inline(always)]
unsafe fn fetch<'__w>( unsafe fn fetch<'__w, '__s>(
_state: &'__s Self::State,
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>, _fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
_entity: #path::entity::Entity, _entity: #path::entity::Entity,
_table_row: #path::storage::TableRow, _table_row: #path::storage::TableRow,
) -> Self::Item<'__w> { ) -> Self::Item<'__w, '__s> {
Self::Item { 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 { for #struct_name #user_ty_generics #user_where_clauses {
const IS_READ_ONLY: bool = #is_read_only; const IS_READ_ONLY: bool = #is_read_only;
type ReadOnly = #read_only_struct_name #user_ty_generics; 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>( fn shrink<'__wlong: '__wshort, '__wshort, '__s>(
item: Self::Item<'__wlong> item: Self::Item<'__wlong, '__s>
) -> Self::Item<'__wshort> { ) -> Self::Item<'__wshort, '__s> {
#item_struct_name { #item_struct_name {
#( #(
#field_idents: <#field_types>::shrink(item.#field_idents), #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`. /// SAFETY: we call `fetch` for each member that implements `Fetch`.
#[inline(always)] #[inline(always)]
unsafe fn fetch<'__w>( unsafe fn fetch<'__w, '__s>(
_state: &'__s Self::State,
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>, _fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
_entity: #path::entity::Entity, _entity: #path::entity::Entity,
_table_row: #path::storage::TableRow, _table_row: #path::storage::TableRow,
) -> Self::Item<'__w> { ) -> Self::Item<'__w, '__s> {
Self::Item { 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)] #[allow(unused_variables)]
#[inline(always)] #[inline(always)]
unsafe fn filter_fetch<'__w>( unsafe fn filter_fetch<'__w>(
_state: &Self::State,
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>, _fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
_entity: #path::entity::Entity, _entity: #path::entity::Entity,
_table_row: #path::storage::TableRow, _table_row: #path::storage::TableRow,
) -> bool { ) -> 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, visibility: &Visibility,
item_struct_name: &Ident, item_struct_name: &Ident,
field_types: &Vec<proc_macro2::TokenStream>, 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_attrs: &Vec<Vec<Attribute>>,
field_visibilities: &Vec<Visibility>, field_visibilities: &Vec<Visibility>,
field_idents: &Vec<proc_macro2::TokenStream>, field_idents: &Vec<proc_macro2::TokenStream>,
user_ty_generics: &TypeGenerics, user_ty_generics: &TypeGenerics,
user_ty_generics_with_world: &TypeGenerics, user_ty_generics_with_world_and_state: &TypeGenerics,
user_where_clauses_with_world: Option<&WhereClause>, user_where_clauses_with_world_and_state: Option<&WhereClause>,
) -> proc_macro2::TokenStream { ) -> proc_macro2::TokenStream {
let item_attrs = quote! { let item_attrs = quote! {
#[doc = concat!( #[doc = concat!(
@ -33,20 +33,20 @@ pub(crate) fn item_struct(
Fields::Named(_) => quote! { Fields::Named(_) => quote! {
#derive_macro_call #derive_macro_call
#item_attrs #item_attrs
#visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world { #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>,)* #(#(#field_attrs)* #field_visibilities #field_idents: <#field_types as #path::query::QueryData>::Item<'__w, '__s>,)*
} }
}, },
Fields::Unnamed(_) => quote! { Fields::Unnamed(_) => quote! {
#derive_macro_call #derive_macro_call
#item_attrs #item_attrs
#visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world( #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>, )* #( #field_visibilities <#field_types as #path::query::QueryData>::Item<'__w, '__s>, )*
); );
}, },
Fields::Unit => quote! { Fields::Unit => quote! {
#item_attrs #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;
}, },
} }
} }
@ -79,7 +79,7 @@ pub(crate) fn world_query_impl(
#[automatically_derived] #[automatically_derived]
#visibility struct #fetch_struct_name #user_impl_generics_with_world #user_where_clauses_with_world { #visibility struct #fetch_struct_name #user_impl_generics_with_world #user_where_clauses_with_world {
#(#named_field_idents: <#field_types as #path::query::WorldQuery>::Fetch<'__w>,)* #(#named_field_idents: <#field_types as #path::query::WorldQuery>::Fetch<'__w>,)*
#marker_name: &'__w (), #marker_name: &'__w(),
} }
impl #user_impl_generics_with_world Clone for #fetch_struct_name #user_ty_generics_with_world impl #user_impl_generics_with_world Clone for #fetch_struct_name #user_ty_generics_with_world
@ -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>, _world: #path::world::unsafe_world_cell::UnsafeWorldCell<'__w>,
state: &Self::State, state: &'__s Self::State,
_last_run: #path::component::Tick, _last_run: #path::component::Tick,
_this_run: #path::component::Tick, _this_run: #path::component::Tick,
) -> <Self as #path::query::WorldQuery>::Fetch<'__w> { ) -> <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` /// SAFETY: we call `set_archetype` for each member that implements `Fetch`
#[inline] #[inline]
unsafe fn set_archetype<'__w>( unsafe fn set_archetype<'__w, '__s>(
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>, _fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
_state: &Self::State, _state: &'__s Self::State,
_archetype: &'__w #path::archetype::Archetype, _archetype: &'__w #path::archetype::Archetype,
_table: &'__w #path::storage::Table _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` /// SAFETY: we call `set_table` for each member that implements `Fetch`
#[inline] #[inline]
unsafe fn set_table<'__w>( unsafe fn set_table<'__w, '__s>(
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>, _fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
_state: &Self::State, _state: &'__s Self::State,
_table: &'__w #path::storage::Table _table: &'__w #path::storage::Table
) { ) {
#(<#field_types>::set_table(&mut _fetch.#named_field_idents, &_state.#named_field_idents, _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. // SAFETY: the caller ensures component_id is valid.
unsafe { components.get_info_unchecked(id).name() } unsafe { components.get_info_unchecked(id).name() }
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>();
.join(", ");
panic!("Bundle {bundle_type_name} has duplicate components: {names}"); panic!("Bundle {bundle_type_name} has duplicate components: {names:?}");
} }
// handle explicit components // handle explicit components

View File

@ -24,7 +24,7 @@ use bevy_platform::{
use bevy_ptr::{OwningPtr, UnsafeCellDeref}; use bevy_ptr::{OwningPtr, UnsafeCellDeref};
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_utils::TypeIdMap; use bevy_utils::{prelude::DebugName, TypeIdMap};
use core::{ use core::{
alloc::Layout, alloc::Layout,
any::{Any, TypeId}, any::{Any, TypeId},
@ -34,7 +34,6 @@ use core::{
mem::needs_drop, mem::needs_drop,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
}; };
use disqualified::ShortName;
use smallvec::SmallVec; use smallvec::SmallVec;
use thiserror::Error; use thiserror::Error;
@ -678,8 +677,8 @@ impl ComponentInfo {
/// Returns the name of the current component. /// Returns the name of the current component.
#[inline] #[inline]
pub fn name(&self) -> &str { pub fn name(&self) -> DebugName {
&self.descriptor.name self.descriptor.name.clone()
} }
/// Returns `true` if the current component is mutable. /// 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. /// A value describing a component or resource, which may or may not correspond to a Rust type.
#[derive(Clone)] #[derive(Clone)]
pub struct ComponentDescriptor { pub struct ComponentDescriptor {
name: Cow<'static, str>, name: DebugName,
// SAFETY: This must remain private. It must match the statically known StorageType of the // SAFETY: This must remain private. It must match the statically known StorageType of the
// associated rust component type if one exists. // associated rust component type if one exists.
storage_type: StorageType, storage_type: StorageType,
@ -882,7 +881,7 @@ impl ComponentDescriptor {
/// Create a new `ComponentDescriptor` for the type `T`. /// Create a new `ComponentDescriptor` for the type `T`.
pub fn new<T: Component>() -> Self { pub fn new<T: Component>() -> Self {
Self { Self {
name: Cow::Borrowed(core::any::type_name::<T>()), name: DebugName::type_name::<T>(),
storage_type: T::STORAGE_TYPE, storage_type: T::STORAGE_TYPE,
is_send_and_sync: true, is_send_and_sync: true,
type_id: Some(TypeId::of::<T>()), type_id: Some(TypeId::of::<T>()),
@ -907,7 +906,7 @@ impl ComponentDescriptor {
clone_behavior: ComponentCloneBehavior, clone_behavior: ComponentCloneBehavior,
) -> Self { ) -> Self {
Self { Self {
name: name.into(), name: name.into().into(),
storage_type, storage_type,
is_send_and_sync: true, is_send_and_sync: true,
type_id: None, type_id: None,
@ -923,7 +922,7 @@ impl ComponentDescriptor {
/// The [`StorageType`] for resources is always [`StorageType::Table`]. /// The [`StorageType`] for resources is always [`StorageType::Table`].
pub fn new_resource<T: Resource>() -> Self { pub fn new_resource<T: Resource>() -> Self {
Self { Self {
name: Cow::Borrowed(core::any::type_name::<T>()), name: DebugName::type_name::<T>(),
// PERF: `SparseStorage` may actually be a more // PERF: `SparseStorage` may actually be a more
// reasonable choice as `storage_type` for resources. // reasonable choice as `storage_type` for resources.
storage_type: StorageType::Table, storage_type: StorageType::Table,
@ -938,7 +937,7 @@ impl ComponentDescriptor {
fn new_non_send<T: Any>(storage_type: StorageType) -> Self { fn new_non_send<T: Any>(storage_type: StorageType) -> Self {
Self { Self {
name: Cow::Borrowed(core::any::type_name::<T>()), name: DebugName::type_name::<T>(),
storage_type, storage_type,
is_send_and_sync: false, is_send_and_sync: false,
type_id: Some(TypeId::of::<T>()), type_id: Some(TypeId::of::<T>()),
@ -964,8 +963,8 @@ impl ComponentDescriptor {
/// Returns the name of the current component. /// Returns the name of the current component.
#[inline] #[inline]
pub fn name(&self) -> &str { pub fn name(&self) -> DebugName {
self.name.as_ref() self.name.clone()
} }
/// Returns whether this component is mutable. /// 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. /// 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] #[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 self.components
.get(id.0) .get(id.0)
.and_then(|info| { .and_then(|info| info.as_ref().map(|info| info.descriptor.name()))
info.as_ref()
.map(|info| Cow::Borrowed(info.descriptor.name()))
})
.or_else(|| { .or_else(|| {
let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner); let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner);
// first check components, then resources, then dynamic // first check components, then resources, then dynamic
@ -2813,13 +2809,13 @@ pub fn enforce_no_required_components_recursion(
"Recursive required components detected: {}\nhelp: {}", "Recursive required components detected: {}\nhelp: {}",
recursion_check_stack recursion_check_stack
.iter() .iter()
.map(|id| format!("{}", ShortName(&components.get_name(*id).unwrap()))) .map(|id| format!("{}", components.get_name(*id).unwrap().shortname()))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(""), .join(""),
if direct_recursion { if direct_recursion {
format!( format!(
"Remove require({}).", "Remove require({}).",
ShortName(&components.get_name(requiree).unwrap()) components.get_name(requiree).unwrap().shortname()
) )
} else { } else {
"If this is intentional, consider merging the components.".into() "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 alloc::{borrow::ToOwned, boxed::Box, collections::VecDeque, vec::Vec};
use bevy_platform::collections::{HashMap, HashSet}; use bevy_platform::collections::{HashMap, HashSet};
use bevy_ptr::{Ptr, PtrMut}; use bevy_ptr::{Ptr, PtrMut};
use bevy_utils::prelude::DebugName;
use bumpalo::Bump; use bumpalo::Bump;
use core::any::TypeId; use core::any::TypeId;
@ -172,7 +173,8 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> {
/// - `ComponentId` of component being written does not match expected `ComponentId`. /// - `ComponentId` of component being written does not match expected `ComponentId`.
pub fn write_target_component<C: Component>(&mut self, mut component: C) { pub fn write_target_component<C: Component>(&mut self, mut component: C) {
C::map_entities(&mut component, &mut self.mapper); 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 { if self.target_component_written {
panic!("Trying to write component '{short_name}' multiple times") 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::{ use crate::{
entity::Entity, entity::Entity,
@ -31,7 +33,7 @@ where
Err(err) => (error_handler)( Err(err) => (error_handler)(
err.into(), err.into(),
ErrorContext::Command { ErrorContext::Command {
name: type_name::<C>().into(), name: DebugName::type_name::<C>(),
}, },
), ),
} }
@ -43,7 +45,7 @@ where
Err(err) => world.default_error_handler()( Err(err) => world.default_error_handler()(
err.into(), err.into(),
ErrorContext::Command { ErrorContext::Command {
name: type_name::<C>().into(), name: DebugName::type_name::<C>(),
}, },
), ),
} }

View File

@ -1,7 +1,7 @@
use core::fmt::Display; use core::fmt::Display;
use crate::{component::Tick, error::BevyError, prelude::Resource}; use crate::{component::Tick, error::BevyError, prelude::Resource};
use alloc::borrow::Cow; use bevy_utils::prelude::DebugName;
use derive_more::derive::{Deref, DerefMut}; use derive_more::derive::{Deref, DerefMut};
/// Context for a [`BevyError`] to aid in debugging. /// Context for a [`BevyError`] to aid in debugging.
@ -10,26 +10,26 @@ pub enum ErrorContext {
/// The error occurred in a system. /// The error occurred in a system.
System { System {
/// The name of the system that failed. /// The name of the system that failed.
name: Cow<'static, str>, name: DebugName,
/// The last tick that the system was run. /// The last tick that the system was run.
last_run: Tick, last_run: Tick,
}, },
/// The error occurred in a run condition. /// The error occurred in a run condition.
RunCondition { RunCondition {
/// The name of the run condition that failed. /// The name of the run condition that failed.
name: Cow<'static, str>, name: DebugName,
/// The last tick that the run condition was evaluated. /// The last tick that the run condition was evaluated.
last_run: Tick, last_run: Tick,
}, },
/// The error occurred in a command. /// The error occurred in a command.
Command { Command {
/// The name of the command that failed. /// The name of the command that failed.
name: Cow<'static, str>, name: DebugName,
}, },
/// The error occurred in an observer. /// The error occurred in an observer.
Observer { Observer {
/// The name of the observer that failed. /// The name of the observer that failed.
name: Cow<'static, str>, name: DebugName,
/// The last tick that the observer was run. /// The last tick that the observer was run.
last_run: Tick, last_run: Tick,
}, },
@ -54,12 +54,12 @@ impl Display for ErrorContext {
impl ErrorContext { impl ErrorContext {
/// The name of the ECS construct that failed. /// The name of the ECS construct that failed.
pub fn name(&self) -> &str { pub fn name(&self) -> DebugName {
match self { match self {
Self::System { name, .. } Self::System { name, .. }
| Self::Command { name, .. } | Self::Command { name, .. }
| Self::Observer { 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; use bevy_reflect::std_traits::ReflectDefault;
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))] #[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
use bevy_utils::prelude::DebugName;
use core::ops::Deref; use core::ops::Deref;
use core::slice; use core::slice;
use disqualified::ShortName;
use log::warn; use log::warn;
/// Stores the parent entity of this child entity with this component. /// 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 // TODO: print name here once Name lives in bevy_ecs
let name: Option<String> = None; let name: Option<String> = None;
let debug_name = DebugName::type_name::<C>();
warn!( warn!(
"warning[B0004]: {}{name} with the {ty_name} component has a parent without {ty_name}.\n\ "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", This will cause inconsistent behaviors! See: https://bevy.org/learn/errors/b0004",
caller.map(|c| format!("{c}: ")).unwrap_or_default(), caller.map(|c| format!("{c}: ")).unwrap_or_default(),
ty_name = ShortName::of::<C>(), ty_name = debug_name.shortname(),
name = name.map_or_else( name = name.map_or_else(
|| format!("Entity {entity}"), || format!("Entity {entity}"),
|s| format!("The {s} 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 /// Trigger emitted when a component is inserted onto an entity that does not already have that
/// component. Runs before `Insert`. /// component. Runs before `Insert`.
/// See [`crate::lifecycle::ComponentHooks::on_add`] for more information. /// 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", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
#[doc(alias = "OnAdd")] #[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 /// Trigger emitted when a component is inserted, regardless of whether or not the entity already
/// had that component. Runs after `Add`, if it ran. /// had that component. Runs after `Add`, if it ran.
/// See [`crate::lifecycle::ComponentHooks::on_insert`] for more information. /// 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", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
#[doc(alias = "OnInsert")] #[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. /// Runs before the value is replaced, so you can still access the original component data.
/// See [`crate::lifecycle::ComponentHooks::on_replace`] for more information. /// 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", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
#[doc(alias = "OnReplace")] #[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 /// 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. /// removed, so you can still access the component data.
/// See [`crate::lifecycle::ComponentHooks::on_remove`] for more information. /// 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", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
#[doc(alias = "OnRemove")] #[doc(alias = "OnRemove")]
@ -365,7 +365,7 @@ pub struct Remove;
/// Trigger emitted for each component on an entity when it is despawned. /// Trigger emitted for each component on an entity when it is despawned.
/// See [`crate::lifecycle::ComponentHooks::on_despawn`] for more information. /// 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", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
#[doc(alias = "OnDespawn")] #[doc(alias = "OnDespawn")]

View File

@ -141,7 +141,7 @@ pub struct NameOrEntity {
pub entity: Entity, pub entity: Entity,
} }
impl<'a> core::fmt::Display for NameOrEntityItem<'a> { impl<'w, 's> core::fmt::Display for NameOrEntityItem<'w, 's> {
#[inline(always)] #[inline(always)]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self.name { match self.name {
@ -274,9 +274,9 @@ mod tests {
let e2 = world.spawn(name.clone()).id(); let e2 = world.spawn(name.clone()).id();
let mut query = world.query::<NameOrEntity>(); let mut query = world.query::<NameOrEntity>();
let d1 = query.get(&world, e1).unwrap(); 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} // NameOrEntity Display for entities without a Name should be {index}v{generation}
assert_eq!(d1.to_string(), "0v0"); assert_eq!(d1.to_string(), "0v0");
let d2 = query.get(&world, e2).unwrap();
// NameOrEntity Display for entities with a Name should be the Name // NameOrEntity Display for entities with a Name should be the Name
assert_eq!(d2.to_string(), "MyName"); 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 event_types = observer_state.descriptor.events.clone();
let components = observer_state.descriptor.components.clone(); let components = observer_state.descriptor.components.clone();
for event_type in event_types { 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 components.is_empty() {
if let Some(map) = observers.entity_observers.get(&source).cloned() { if let Some(map) = observers.entity_observers.get(&source).cloned() {
observers.entity_observers.insert(target, map); observers.entity_observers.insert(target, map);
@ -108,8 +108,10 @@ fn component_clone_observed_by(_source: &SourceComponent, ctx: &mut ComponentClo
else { else {
continue; continue;
}; };
if let Some(map) = observers.entity_map.get(&source).cloned() { if let Some(map) =
observers.entity_map.insert(target, 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 /// Map between an observer entity and its [`ObserverRunner`]
type ObserverMap = EntityHashMap<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`]. /// This is stored inside of [`CachedObservers`].
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct CachedComponentObservers { pub struct CachedComponentObservers {
// Observers listening to triggers targeting this component // Observers listening to events targeting this component, but not a specific entity
map: ObserverMap, global_observers: ObserverMap,
// Observers listening to triggers targeting this component on a specific entity // Observers listening to events targeting this component on a specific entity
entity_map: EntityHashMap<ObserverMap>, 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. /// This is stored inside of [`Observers`], specialized for each kind of observer.
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct CachedObservers { pub struct CachedObservers {
// Observers listening for any time this trigger is fired // Observers listening for any time this event is fired, regardless of target
map: ObserverMap, // This will also respond to events targeting specific components or entities
global_observers: ObserverMap,
// Observers listening for this trigger fired at a specific component // Observers listening for this trigger fired at a specific component
component_observers: HashMap<ComponentId, CachedComponentObservers>, component_observers: HashMap<ComponentId, CachedComponentObservers>,
// Observers listening for this trigger fired at a specific entity // Observers listening for this trigger fired at a specific entity
entity_observers: EntityHashMap<ObserverMap>, 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. /// An internal lookup table tracking all of the observers in the world.
/// ///
/// Stores a cache mapping trigger ids to the registered observers. /// Stores a cache mapping trigger ids to the registered observers.
/// Some observer kinds (like [lifecycle](crate::lifecycle) observers) have a dedicated field, /// Some observer kinds (like [lifecycle](crate::lifecycle) observers) have a dedicated field,
/// saving lookups for the most common triggers. /// 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)] #[derive(Default, Debug)]
pub struct Observers { pub struct Observers {
// Cached ECS observers to save a lookup most common triggers. // Cached ECS observers to save a lookup most common triggers.
@ -562,7 +594,7 @@ pub struct Observers {
} }
impl 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::*; use crate::lifecycle::*;
match event_type { 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::*; use crate::lifecycle::*;
match event_type { match event_type {
@ -630,7 +666,10 @@ impl Observers {
); );
}; };
// Trigger observers listening for any kind of this trigger // 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 // Trigger entity observers listening for this kind of trigger
if let Some(target_entity) = current_target { if let Some(target_entity) = current_target {
@ -643,12 +682,15 @@ impl Observers {
trigger_for_components.for_each(|id| { trigger_for_components.for_each(|id| {
if let Some(component_observers) = observers.component_observers.get(&id) { if let Some(component_observers) = observers.component_observers.get(&id) {
component_observers component_observers
.map .global_observers
.iter() .iter()
.for_each(&mut trigger_observer); .for_each(&mut trigger_observer);
if let Some(target_entity) = current_target { 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); map.iter().for_each(&mut trigger_observer);
} }
} }
@ -926,10 +968,12 @@ impl World {
let descriptor = &observer_state.descriptor; let descriptor = &observer_state.descriptor;
for &event_type in &descriptor.events { 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() { 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() { } else if descriptor.components.is_empty() {
// Observer is not targeting any components so register it as an entity observer // Observer is not targeting any components so register it as an entity observer
for &watched_entity in &observer_state.descriptor.entities { for &watched_entity in &observer_state.descriptor.entities {
@ -951,11 +995,16 @@ impl World {
}); });
if descriptor.entities.is_empty() { if descriptor.entities.is_empty() {
// Register for all triggers targeting the component // 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 { } else {
// Register for each watched entity // Register for each watched entity
for &watched_entity in &descriptor.entities { 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); map.insert(observer_entity, observer_state.runner);
} }
} }
@ -970,9 +1019,9 @@ impl World {
let observers = &mut self.observers; let observers = &mut self.observers;
for &event_type in &descriptor.events { 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() { if descriptor.components.is_empty() && descriptor.entities.is_empty() {
cache.map.remove(&entity); cache.global_observers.remove(&entity);
} else if descriptor.components.is_empty() { } else if descriptor.components.is_empty() {
for watched_entity in &descriptor.entities { for watched_entity in &descriptor.entities {
// This check should be unnecessary since this observer hasn't been unregistered yet // This check should be unnecessary since this observer hasn't been unregistered yet
@ -990,20 +1039,24 @@ impl World {
continue; continue;
}; };
if descriptor.entities.is_empty() { if descriptor.entities.is_empty() {
observers.map.remove(&entity); observers.global_observers.remove(&entity);
} else { } else {
for watched_entity in &descriptor.entities { 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; continue;
}; };
map.remove(&entity); map.remove(&entity);
if map.is_empty() { 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); cache.component_observers.remove(component);
if let Some(flag) = Observers::is_archetype_cached(event_type) { if let Some(flag) = Observers::is_archetype_cached(event_type) {
if let Some(by_component) = archetypes.by_component.get(component) { if let Some(by_component) = archetypes.by_component.get(component) {
@ -1078,7 +1131,7 @@ mod tests {
struct ChildOf(Entity); struct ChildOf(Entity);
impl<D> Traversal<D> for &'_ ChildOf { 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) Some(item.0)
} }
} }

View File

@ -1,4 +1,5 @@
use alloc::{boxed::Box, vec}; use alloc::{boxed::Box, vec};
use bevy_utils::prelude::DebugName;
use core::any::Any; use core::any::Any;
use crate::{ use crate::{
@ -194,7 +195,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
pub struct Observer { pub struct Observer {
hook_on_add: ComponentHook, hook_on_add: ComponentHook,
error_handler: Option<ErrorHandler>, error_handler: Option<ErrorHandler>,
system: Box<dyn Any + Send + Sync + 'static>, system: Box<dyn AnyNamedSystem>,
pub(crate) descriptor: ObserverDescriptor, pub(crate) descriptor: ObserverDescriptor,
pub(crate) last_trigger_id: u32, pub(crate) last_trigger_id: u32,
pub(crate) despawned_watched_entities: 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 /// Creates a new [`Observer`] with custom runner, this is mostly used for dynamic event observer
pub fn with_dynamic_runner(runner: ObserverRunner) -> Self { pub fn with_dynamic_runner(runner: ObserverRunner) -> Self {
Self { Self {
system: Box::new(|| {}), system: Box::new(IntoSystem::into_system(|| {})),
descriptor: Default::default(), descriptor: Default::default(),
hook_on_add: |mut world, hook_context| { hook_on_add: |mut world, hook_context| {
let default_error_handler = world.default_error_handler(); let default_error_handler = world.default_error_handler();
@ -299,6 +300,11 @@ impl Observer {
pub fn descriptor(&self) -> &ObserverDescriptor { pub fn descriptor(&self) -> &ObserverDescriptor {
&self.descriptor &self.descriptor
} }
/// Returns the name of the [`Observer`]'s system .
pub fn system_name(&self) -> DebugName {
self.system.system_name()
}
} }
impl Component for Observer { 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 was triggered so must have an `Observer` component.
// - observer cannot be dropped or mutated until after the system pointer is already dropped. // - observer cannot be dropped or mutated until after the system pointer is already dropped.
let system: *mut dyn ObserverSystem<E, B> = unsafe { 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 &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`). /// 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 /// 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| { B::component_ids(&mut world.components_registrator(), &mut |id| {
components.push(id); components.push(id);
}); });
if let Some(mut observe) = world.get_mut::<Observer>(entity) { if let Some(mut observer) = world.get_mut::<Observer>(entity) {
observe.descriptor.events.push(event_id); observer.descriptor.events.push(event_id);
observe.descriptor.components.extend(components); 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 // SAFETY: World reference is exclusive and initialize does not touch system, so references do not alias
unsafe { unsafe {
(*system).initialize(world); (*system).initialize(world);

View File

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

View File

@ -1,3 +1,6 @@
use bevy_utils::prelude::DebugName;
use thiserror::Error;
use crate::{ use crate::{
archetype::ArchetypeId, archetype::ArchetypeId,
entity::{ConstructedEntityDoesNotExistError, Entity}, entity::{ConstructedEntityDoesNotExistError, Entity},
@ -28,10 +31,10 @@ pub enum QueryEntityError {
pub enum QuerySingleError { pub enum QuerySingleError {
/// No entity fits the query. /// No entity fits the query.
#[error("No entities fit the query {0}")] #[error("No entities fit the query {0}")]
NoEntities(&'static str), NoEntities(DebugName),
/// Multiple entities fit the query. /// Multiple entities fit the query.
#[error("Multiple entities fit the query {0}")] #[error("Multiple entities fit the query {0}")]
MultipleEntities(&'static str), MultipleEntities(DebugName),
} }
#[cfg(test)] #[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}, world::{unsafe_world_cell::UnsafeWorldCell, World},
}; };
use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref};
use bevy_utils::prelude::DebugName;
use core::{cell::UnsafeCell, marker::PhantomData}; use core::{cell::UnsafeCell, marker::PhantomData};
use variadics_please::all_tuples; 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 /// 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. /// `table_row` must be in the range of the current table and archetype.
unsafe fn filter_fetch( unsafe fn filter_fetch(
state: &Self::State,
fetch: &mut Self::Fetch<'_>, fetch: &mut Self::Fetch<'_>,
entity: Entity, entity: Entity,
table_row: TableRow, table_row: TableRow,
@ -204,6 +206,7 @@ unsafe impl<T: Component> QueryFilter for With<T> {
#[inline(always)] #[inline(always)]
unsafe fn filter_fetch( unsafe fn filter_fetch(
_state: &Self::State,
_fetch: &mut Self::Fetch<'_>, _fetch: &mut Self::Fetch<'_>,
_entity: Entity, _entity: Entity,
_table_row: TableRow, _table_row: TableRow,
@ -304,6 +307,7 @@ unsafe impl<T: Component> QueryFilter for Without<T> {
#[inline(always)] #[inline(always)]
unsafe fn filter_fetch( unsafe fn filter_fetch(
_state: &Self::State,
_fetch: &mut Self::Fetch<'_>, _fetch: &mut Self::Fetch<'_>,
_entity: Entity, _entity: Entity,
_table_row: TableRow, _table_row: TableRow,
@ -400,7 +404,7 @@ macro_rules! impl_or_query_filter {
const IS_DENSE: bool = true $(&& $filter::IS_DENSE)*; const IS_DENSE: bool = true $(&& $filter::IS_DENSE)*;
#[inline] #[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; let ($($filter,)*) = state;
($(OrFetch { ($(OrFetch {
// SAFETY: The invariants are upheld by the caller. // SAFETY: The invariants are upheld by the caller.
@ -410,7 +414,7 @@ macro_rules! impl_or_query_filter {
} }
#[inline] #[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 ($($filter,)*) = fetch;
let ($($state,)*) = state; let ($($state,)*) = state;
$( $(
@ -423,9 +427,9 @@ macro_rules! impl_or_query_filter {
} }
#[inline] #[inline]
unsafe fn set_archetype<'w>( unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>, fetch: &mut Self::Fetch<'w>,
state: & Self::State, state: &'s Self::State,
archetype: &'w Archetype, archetype: &'w Archetype,
table: &'w Table table: &'w Table
) { ) {
@ -495,20 +499,22 @@ macro_rules! impl_or_query_filter {
#[inline(always)] #[inline(always)]
unsafe fn filter_fetch( unsafe fn filter_fetch(
state: &Self::State,
fetch: &mut Self::Fetch<'_>, fetch: &mut Self::Fetch<'_>,
entity: Entity, entity: Entity,
table_row: TableRow table_row: TableRow
) -> bool { ) -> bool {
let ($($state,)*) = state;
let ($($filter,)*) = fetch; let ($($filter,)*) = fetch;
// SAFETY: The invariants are upheld by the caller. // 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 { macro_rules! impl_tuple_query_filter {
($(#[$meta:meta])* $($name: ident),*) => { ($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => {
#[expect( #[expect(
clippy::allow_attributes, clippy::allow_attributes,
reason = "This is a tuple-related macro; as such the lints below may not always apply." 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)] #[inline(always)]
unsafe fn filter_fetch( unsafe fn filter_fetch(
state: &Self::State,
fetch: &mut Self::Fetch<'_>, fetch: &mut Self::Fetch<'_>,
entity: Entity, entity: Entity,
table_row: TableRow table_row: TableRow
) -> bool { ) -> bool {
let ($($state,)*) = state;
let ($($name,)*) = fetch; let ($($name,)*) = fetch;
// SAFETY: The invariants are upheld by the caller. // 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, impl_tuple_query_filter,
0, 0,
15, 15,
F F,
S
); );
all_tuples!( all_tuples!(
#[doc(fake_variadic)] #[doc(fake_variadic)]
@ -609,7 +618,12 @@ unsafe impl<T: Component> QueryFilter for Allows<T> {
const IS_ARCHETYPAL: bool = true; const IS_ARCHETYPAL: bool = true;
#[inline(always)] #[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 true
} }
} }
@ -718,9 +732,9 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
} }
#[inline] #[inline]
unsafe fn init_fetch<'w>( unsafe fn init_fetch<'w, 's>(
world: UnsafeWorldCell<'w>, world: UnsafeWorldCell<'w>,
&id: &ComponentId, &id: &'s ComponentId,
last_run: Tick, last_run: Tick,
this_run: Tick, this_run: Tick,
) -> Self::Fetch<'w> { ) -> Self::Fetch<'w> {
@ -748,9 +762,9 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
}; };
#[inline] #[inline]
unsafe fn set_archetype<'w>( unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>, fetch: &mut Self::Fetch<'w>,
component_id: &ComponentId, component_id: &'s ComponentId,
_archetype: &'w Archetype, _archetype: &'w Archetype,
table: &'w Table, table: &'w Table,
) { ) {
@ -763,9 +777,9 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
} }
#[inline] #[inline]
unsafe fn set_table<'w>( unsafe fn set_table<'w, 's>(
fetch: &mut Self::Fetch<'w>, fetch: &mut Self::Fetch<'w>,
&component_id: &ComponentId, &component_id: &'s ComponentId,
table: &'w Table, table: &'w Table,
) { ) {
let table_ticks = Some( let table_ticks = Some(
@ -781,7 +795,7 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
#[inline] #[inline]
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) { fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
if access.access().has_component_write(id) { 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); access.add_component_read(id);
} }
@ -807,6 +821,7 @@ unsafe impl<T: Component> QueryFilter for Added<T> {
const IS_ARCHETYPAL: bool = false; const IS_ARCHETYPAL: bool = false;
#[inline(always)] #[inline(always)]
unsafe fn filter_fetch( unsafe fn filter_fetch(
_state: &Self::State,
fetch: &mut Self::Fetch<'_>, fetch: &mut Self::Fetch<'_>,
entity: Entity, entity: Entity,
table_row: TableRow, table_row: TableRow,
@ -944,9 +959,9 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
} }
#[inline] #[inline]
unsafe fn init_fetch<'w>( unsafe fn init_fetch<'w, 's>(
world: UnsafeWorldCell<'w>, world: UnsafeWorldCell<'w>,
&id: &ComponentId, &id: &'s ComponentId,
last_run: Tick, last_run: Tick,
this_run: Tick, this_run: Tick,
) -> Self::Fetch<'w> { ) -> Self::Fetch<'w> {
@ -974,9 +989,9 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
}; };
#[inline] #[inline]
unsafe fn set_archetype<'w>( unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>, fetch: &mut Self::Fetch<'w>,
component_id: &ComponentId, component_id: &'s ComponentId,
_archetype: &'w Archetype, _archetype: &'w Archetype,
table: &'w Table, table: &'w Table,
) { ) {
@ -989,9 +1004,9 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
} }
#[inline] #[inline]
unsafe fn set_table<'w>( unsafe fn set_table<'w, 's>(
fetch: &mut Self::Fetch<'w>, fetch: &mut Self::Fetch<'w>,
&component_id: &ComponentId, &component_id: &'s ComponentId,
table: &'w Table, table: &'w Table,
) { ) {
let table_ticks = Some( let table_ticks = Some(
@ -1007,7 +1022,7 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
#[inline] #[inline]
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) { fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
if access.access().has_component_write(id) { 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); access.add_component_read(id);
} }
@ -1034,6 +1049,7 @@ unsafe impl<T: Component> QueryFilter for Changed<T> {
#[inline(always)] #[inline(always)]
unsafe fn filter_fetch( unsafe fn filter_fetch(
_state: &Self::State,
fetch: &mut Self::Fetch<'_>, fetch: &mut Self::Fetch<'_>,
entity: Entity, entity: Entity,
table_row: TableRow, table_row: TableRow,
@ -1141,9 +1157,9 @@ unsafe impl WorldQuery for Spawned {
} }
#[inline] #[inline]
unsafe fn init_fetch<'w>( unsafe fn init_fetch<'w, 's>(
world: UnsafeWorldCell<'w>, world: UnsafeWorldCell<'w>,
_state: &(), _state: &'s (),
last_run: Tick, last_run: Tick,
this_run: Tick, this_run: Tick,
) -> Self::Fetch<'w> { ) -> Self::Fetch<'w> {
@ -1157,16 +1173,16 @@ unsafe impl WorldQuery for Spawned {
const IS_DENSE: bool = true; const IS_DENSE: bool = true;
#[inline] #[inline]
unsafe fn set_archetype<'w>( unsafe fn set_archetype<'w, 's>(
_fetch: &mut Self::Fetch<'w>, _fetch: &mut Self::Fetch<'w>,
_state: &(), _state: &'s (),
_archetype: &'w Archetype, _archetype: &'w Archetype,
_table: &'w Table, _table: &'w Table,
) { ) {
} }
#[inline] #[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] #[inline]
fn update_component_access(_state: &(), _access: &mut FilteredAccess<ComponentId>) {} fn update_component_access(_state: &(), _access: &mut FilteredAccess<ComponentId>) {}
@ -1188,6 +1204,7 @@ unsafe impl QueryFilter for Spawned {
#[inline(always)] #[inline(always)]
unsafe fn filter_fetch( unsafe fn filter_fetch(
_state: &Self::State,
fetch: &mut Self::Fetch<'_>, fetch: &mut Self::Fetch<'_>,
entity: Entity, entity: Entity,
_table_row: TableRow, _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>>, range: Option<Range<u32>>,
) -> B ) -> B
where where
Func: FnMut(B, D::Item<'w>) -> B, Func: FnMut(B, D::Item<'w, 's>) -> B,
{ {
if self.cursor.is_dense { if self.cursor.is_dense {
// SAFETY: `self.cursor.is_dense` is true, so storage ids are guaranteed to be table ids. // 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>, rows: Range<u32>,
) -> B ) -> B
where where
Func: FnMut(B, D::Item<'w>) -> B, Func: FnMut(B, D::Item<'w, 's>) -> B,
{ {
if table.is_empty() { if table.is_empty() {
return accum; return accum;
@ -225,14 +225,26 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
// SAFETY: set_table was called prior. // SAFETY: set_table was called prior.
// Caller assures `row` in range of the current archetype. // 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 { if fetched {
continue; continue;
} }
// SAFETY: set_table was called prior. // SAFETY: set_table was called prior.
// Caller assures `row` in range of the current archetype. // 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); accum = func(accum, item);
} }
@ -255,7 +267,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
indices: Range<u32>, indices: Range<u32>,
) -> B ) -> B
where where
Func: FnMut(B, D::Item<'w>) -> B, Func: FnMut(B, D::Item<'w, 's>) -> B,
{ {
if archetype.is_empty() { if archetype.is_empty() {
return accum; 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. // Caller assures `index` in range of the current archetype.
let fetched = unsafe { let fetched = unsafe {
!F::filter_fetch( !F::filter_fetch(
&self.query_state.filter_state,
&mut self.cursor.filter, &mut self.cursor.filter,
archetype_entity.id(), archetype_entity.id(),
archetype_entity.table_row(), 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. // Caller assures `index` in range of the current archetype.
let item = unsafe { let item = unsafe {
D::fetch( D::fetch(
&self.query_state.fetch_state,
&mut self.cursor.fetch, &mut self.cursor.fetch,
archetype_entity.id(), archetype_entity.id(),
archetype_entity.table_row(), archetype_entity.table_row(),
@ -324,7 +338,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
rows: Range<u32>, rows: Range<u32>,
) -> B ) -> B
where where
Func: FnMut(B, D::Item<'w>) -> B, Func: FnMut(B, D::Item<'w, 's>) -> B,
{ {
if archetype.is_empty() { if archetype.is_empty() {
return accum; return accum;
@ -356,14 +370,26 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
// SAFETY: set_table was called prior. // SAFETY: set_table was called prior.
// Caller assures `row` in range of the current archetype. // 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 { if !filter_matched {
continue; continue;
} }
// SAFETY: set_table was called prior. // SAFETY: set_table was called prior.
// Caller assures `row` in range of the current archetype. // 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); 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, impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
> >
where where
for<'lw> L::Item<'lw>: Ord, for<'lw, 'ls> L::Item<'lw, 'ls>: Ord,
{ {
self.sort_impl::<L>(|keyed_query| keyed_query.sort()) 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, impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
> >
where 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()) 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>( pub fn sort_by<L: ReadOnlyQueryData + 'w>(
self, self,
mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering, mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering,
) -> QuerySortedIter< ) -> QuerySortedIter<
'w, 'w,
's, '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. /// 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>( pub fn sort_unstable_by<L: ReadOnlyQueryData + 'w>(
self, self,
mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering, mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering,
) -> QuerySortedIter< ) -> QuerySortedIter<
'w, 'w,
's, '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>( pub fn sort_by_key<L: ReadOnlyQueryData + 'w, K>(
self, self,
mut f: impl FnMut(&L::Item<'_>) -> K, mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedIter< ) -> QuerySortedIter<
'w, 'w,
's, '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. /// 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>( pub fn sort_unstable_by_key<L: ReadOnlyQueryData + 'w, K>(
self, self,
mut f: impl FnMut(&L::Item<'_>) -> K, mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedIter< ) -> QuerySortedIter<
'w, 'w,
's, '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. /// 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>( pub fn sort_by_cached_key<L: ReadOnlyQueryData + 'w, K>(
self, self,
mut f: impl FnMut(&L::Item<'_>) -> K, mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedIter< ) -> QuerySortedIter<
'w, 'w,
's, '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. /// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty.
fn sort_impl<L: ReadOnlyQueryData + 'w>( fn sort_impl<L: ReadOnlyQueryData + 'w>(
self, self,
f: impl FnOnce(&mut Vec<(L::Item<'_>, NeutralOrd<Entity>)>), f: impl FnOnce(&mut Vec<(L::Item<'_, '_>, NeutralOrd<Entity>)>),
) -> QuerySortedIter< ) -> QuerySortedIter<
'w, 'w,
's, 's,
@ -856,7 +882,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
.map(|(key, entity)| (key, NeutralOrd(entity))) .map(|(key, entity)| (key, NeutralOrd(entity)))
.collect(); .collect();
f(&mut keyed_query); 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: // SAFETY:
// `self.world` has permission to access the required components. // `self.world` has permission to access the required components.
// Each lens query item is dropped before the respective actual query item is accessed. // 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> { 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)] #[inline(always)]
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
@ -1010,7 +1040,7 @@ where
/// # Safety /// # Safety
/// `entity` must stem from `self.entity_iter`, and not have been passed before. /// `entity` must stem from `self.entity_iter`, and not have been passed before.
#[inline(always)] #[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); let (location, archetype, table);
// SAFETY: // SAFETY:
// `tables` and `archetypes` belong to the same world that the [`QueryIter`] // `tables` and `archetypes` belong to the same world that the [`QueryIter`]
@ -1039,7 +1069,14 @@ where
// SAFETY: // SAFETY:
// - set_archetype was called prior, `location.archetype_row` is an archetype index in range of the current archetype // - 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. // - 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 where
I: Iterator<Item = Entity>, I: Iterator<Item = Entity>,
{ {
type Item = D::Item<'w>; type Item = D::Item<'w, 's>;
#[inline(always)] #[inline(always)]
fn next(&mut self) -> Option<Self::Item> { 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>, fetch: &mut D::Fetch<'w>,
filter: &mut F::Fetch<'w>, filter: &mut F::Fetch<'w>,
query_state: &'s QueryState<D, F>, query_state: &'s QueryState<D, F>,
) -> Option<D::Item<'w>> { ) -> Option<D::Item<'w, 's>> {
for entity_borrow in entity_iter { for entity_borrow in entity_iter {
let entity = entity_borrow.entity(); let entity = entity_borrow.entity();
let Ok(location) = entities.get_constructed(entity) else { 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. // 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 // `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: // SAFETY:
// - set_archetype was called prior, `location.archetype_row` is an archetype index in range of the current archetype // - 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. // - 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 None
@ -1212,7 +1258,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
/// Get next result from the query /// Get next result from the query
#[inline(always)] #[inline(always)]
pub fn fetch_next(&mut self) -> Option<D::Item<'_>> { pub fn fetch_next(&mut self) -> Option<D::Item<'_, 's>> {
// SAFETY: // SAFETY:
// All arguments stem from self. // All arguments stem from self.
// We are limiting the returned reference to 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, impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
> >
where where
for<'lw> L::Item<'lw>: Ord, for<'lw, 'ls> L::Item<'lw, 'ls>: Ord,
{ {
self.sort_impl::<L>(|keyed_query| keyed_query.sort()) 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, impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
> >
where 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()) 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>( pub fn sort_by<L: ReadOnlyQueryData + 'w>(
self, self,
mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering, mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering,
) -> QuerySortedManyIter< ) -> QuerySortedManyIter<
'w, 'w,
's, 's,
@ -1482,7 +1528,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
/// called on [`QueryManyIter`] before. /// called on [`QueryManyIter`] before.
pub fn sort_unstable_by<L: ReadOnlyQueryData + 'w>( pub fn sort_unstable_by<L: ReadOnlyQueryData + 'w>(
self, self,
mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering, mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering,
) -> QuerySortedManyIter< ) -> QuerySortedManyIter<
'w, 'w,
's, '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>( pub fn sort_by_key<L: ReadOnlyQueryData + 'w, K>(
self, self,
mut f: impl FnMut(&L::Item<'_>) -> K, mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedManyIter< ) -> QuerySortedManyIter<
'w, 'w,
's, 's,
@ -1608,7 +1654,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
/// called on [`QueryManyIter`] before. /// called on [`QueryManyIter`] before.
pub fn sort_unstable_by_key<L: ReadOnlyQueryData + 'w, K>( pub fn sort_unstable_by_key<L: ReadOnlyQueryData + 'w, K>(
self, self,
mut f: impl FnMut(&L::Item<'_>) -> K, mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedManyIter< ) -> QuerySortedManyIter<
'w, 'w,
's, 's,
@ -1642,7 +1688,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
/// called on [`QueryManyIter`] before. /// called on [`QueryManyIter`] before.
pub fn sort_by_cached_key<L: ReadOnlyQueryData + 'w, K>( pub fn sort_by_cached_key<L: ReadOnlyQueryData + 'w, K>(
self, self,
mut f: impl FnMut(&L::Item<'_>) -> K, mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedManyIter< ) -> QuerySortedManyIter<
'w, 'w,
's, 's,
@ -1671,7 +1717,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
/// called on [`QueryManyIter`] before. /// called on [`QueryManyIter`] before.
fn sort_impl<L: ReadOnlyQueryData + 'w>( fn sort_impl<L: ReadOnlyQueryData + 'w>(
self, self,
f: impl FnOnce(&mut Vec<(L::Item<'_>, NeutralOrd<Entity>)>), f: impl FnOnce(&mut Vec<(L::Item<'_, '_>, NeutralOrd<Entity>)>),
) -> QuerySortedManyIter< ) -> QuerySortedManyIter<
'w, 'w,
's, '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 /// Get next result from the back of the query
#[inline(always)] #[inline(always)]
pub fn fetch_next_back(&mut self) -> Option<D::Item<'_>> { pub fn fetch_next_back(&mut self) -> Option<D::Item<'_, 's>> {
// SAFETY: // SAFETY:
// All arguments stem from self. // All arguments stem from self.
// We are limiting the returned reference to 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 impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>> Iterator
for QueryManyIter<'w, 's, D, F, I> for QueryManyIter<'w, 's, D, F, I>
{ {
type Item = D::Item<'w>; type Item = D::Item<'w, 's>;
#[inline(always)] #[inline(always)]
fn next(&mut self) -> Option<Self::Item> { 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 impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> Iterator
for QueryManyUniqueIter<'w, 's, D, F, I> for QueryManyUniqueIter<'w, 's, D, F, I>
{ {
type Item = D::Item<'w>; type Item = D::Item<'w, 's>;
#[inline(always)] #[inline(always)]
fn next(&mut self) -> Option<Self::Item> { 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. /// It is always safe for shared access.
/// `entity` must stem from `self.entity_iter`, and not have been passed before. /// `entity` must stem from `self.entity_iter`, and not have been passed before.
#[inline(always)] #[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); let (location, archetype, table);
// SAFETY: // SAFETY:
// `tables` and `archetypes` belong to the same world that the [`QueryIter`] // `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: // SAFETY:
// - set_archetype was called prior, `location.archetype_row` is an archetype index in range of the current archetype // - 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. // - 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 /// Get next result from the query
#[inline(always)] #[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()?; let entity = self.entity_iter.next()?;
// SAFETY: // SAFETY:
@ -2007,7 +2060,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator<Item = Entity>
{ {
/// Get next result from the query /// Get next result from the query
#[inline(always)] #[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()?; let entity = self.entity_iter.next_back()?;
// SAFETY: // 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 impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: Iterator<Item = Entity>> Iterator
for QuerySortedManyIter<'w, 's, D, F, I> for QuerySortedManyIter<'w, 's, D, F, I>
{ {
type Item = D::Item<'w>; type Item = D::Item<'w, 's>;
#[inline(always)] #[inline(always)]
fn next(&mut self) -> Option<Self::Item> { 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. /// It is always safe for shared access.
#[inline] #[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()` // PERF: can speed up the following code using `cursor.remaining()` instead of `next_item.is_none()`
// when D::IS_ARCHETYPAL && F::IS_ARCHETYPAL // 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() { 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()) 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 /// Get next combination of queried components
#[inline] #[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, // SAFETY: we are limiting the returned reference to self,
// making sure this method cannot be called multiple times without getting rid // making sure this method cannot be called multiple times without getting rid
// of any previously returned unique references first, thus preventing aliasing. // 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 impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, const K: usize> Iterator
for QueryCombinationIter<'w, 's, D, F, K> for QueryCombinationIter<'w, 's, D, F, K>
{ {
type Item = [D::Item<'w>; K]; type Item = [D::Item<'w, 's>; K];
#[inline] #[inline]
fn next(&mut self) -> Option<Self::Item> { 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 /// The result of `next` and any previous calls to `peek_last` with this row must have been
/// dropped to prevent aliasing mutable references. /// dropped to prevent aliasing mutable references.
#[inline] #[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 { if self.current_row > 0 {
let index = self.current_row - 1; let index = self.current_row - 1;
if self.is_dense { 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. // - `*entity` and `index` are in the current table.
unsafe { unsafe {
Some(D::fetch( Some(D::fetch(
&query_state.fetch_state,
&mut self.fetch, &mut self.fetch,
*entity, *entity,
// SAFETY: This is from an exclusive range, so it can't be max. // 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. // - `archetype_entity.id()` and `archetype_entity.table_row()` are in the current archetype.
unsafe { unsafe {
Some(D::fetch( Some(D::fetch(
&query_state.fetch_state,
&mut self.fetch, &mut self.fetch,
archetype_entity.id(), archetype_entity.id(),
archetype_entity.table_row(), archetype_entity.table_row(),
@ -2457,7 +2513,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
tables: &'w Tables, tables: &'w Tables,
archetypes: &'w Archetypes, archetypes: &'w Archetypes,
query_state: &'s QueryState<D, F>, query_state: &'s QueryState<D, F>,
) -> Option<D::Item<'w>> { ) -> Option<D::Item<'w, 's>> {
if self.is_dense { if self.is_dense {
loop { loop {
// we are on the beginning of the query, or finished processing a table, so skip to the next // 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) }; 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. // 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)) }; 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; self.current_row += 1;
continue; 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, // - `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. // because if it was not, then the above would have been executed.
// - fetch is only called once for each `entity`. // - 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; self.current_row += 1;
return Some(item); 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) .get_unchecked(self.current_row as usize)
}; };
if !F::filter_fetch( if !F::filter_fetch(
&query_state.filter_state,
&mut self.filter, &mut self.filter,
archetype_entity.id(), archetype_entity.id(),
archetype_entity.table_row(), 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`. // - fetch is only called once for each `archetype_entity`.
let item = unsafe { let item = unsafe {
D::fetch( D::fetch(
&query_state.fetch_state,
&mut self.fetch, &mut self.fetch,
archetype_entity.id(), archetype_entity.id(),
archetype_entity.table_row(), archetype_entity.table_row(),

View File

@ -824,9 +824,9 @@ mod tests {
fn shrink_fetch<'wlong: 'wshort, 'wshort>(_: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {} 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>, _world: UnsafeWorldCell<'w>,
_state: &Self::State, _state: &'s Self::State,
_last_run: Tick, _last_run: Tick,
_this_run: Tick, _this_run: Tick,
) -> Self::Fetch<'w> { ) -> Self::Fetch<'w> {
@ -835,18 +835,18 @@ mod tests {
const IS_DENSE: bool = true; const IS_DENSE: bool = true;
#[inline] #[inline]
unsafe fn set_archetype<'w>( unsafe fn set_archetype<'w, 's>(
_fetch: &mut Self::Fetch<'w>, _fetch: &mut Self::Fetch<'w>,
_state: &Self::State, _state: &'s Self::State,
_archetype: &'w Archetype, _archetype: &'w Archetype,
_table: &Table, _table: &Table,
) { ) {
} }
#[inline] #[inline]
unsafe fn set_table<'w>( unsafe fn set_table<'w, 's>(
_fetch: &mut Self::Fetch<'w>, _fetch: &mut Self::Fetch<'w>,
_state: &Self::State, _state: &'s Self::State,
_table: &'w Table, _table: &'w Table,
) { ) {
} }
@ -882,16 +882,20 @@ mod tests {
unsafe impl QueryData for ReadsRData { unsafe impl QueryData for ReadsRData {
const IS_READ_ONLY: bool = true; const IS_READ_ONLY: bool = true;
type ReadOnly = Self; 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)] #[inline(always)]
unsafe fn fetch<'w>( unsafe fn fetch<'w, 's>(
_state: &'s Self::State,
_fetch: &mut Self::Fetch<'w>, _fetch: &mut Self::Fetch<'w>,
_entity: Entity, _entity: Entity,
_table_row: TableRow, _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 /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
#[inline] #[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)); self.for_each_init(|| {}, |_, item| func(item));
} }
@ -76,7 +76,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryParIter<'w, 's, D, F> {
#[inline] #[inline]
pub fn for_each_init<FN, INIT, T>(self, init: INIT, func: FN) pub fn for_each_init<FN, INIT, T>(self, init: INIT, func: FN)
where 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, INIT: Fn() -> T + Sync + Send + Clone,
{ {
let func = |mut init, item| { let func = |mut init, item| {
@ -190,7 +190,7 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, E: EntityEquivalent + Sync>
/// ///
/// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
#[inline] #[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)); self.for_each_init(|| {}, |_, item| func(item));
} }
@ -247,7 +247,7 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, E: EntityEquivalent + Sync>
#[inline] #[inline]
pub fn for_each_init<FN, INIT, T>(self, init: INIT, func: FN) pub fn for_each_init<FN, INIT, T>(self, init: INIT, func: FN)
where 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, INIT: Fn() -> T + Sync + Send + Clone,
{ {
let func = |mut init, item| { let func = |mut init, item| {
@ -345,7 +345,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, E: EntityEquivalent + Sync>
/// ///
/// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
#[inline] #[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)); self.for_each_init(|| {}, |_, item| func(item));
} }
@ -402,7 +402,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, E: EntityEquivalent + Sync>
#[inline] #[inline]
pub fn for_each_init<FN, INIT, T>(self, init: INIT, func: FN) pub fn for_each_init<FN, INIT, T>(self, init: INIT, func: FN)
where 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, INIT: Fn() -> T + Sync + Send + Clone,
{ {
let func = |mut init, item| { let func = |mut init, item| {

View File

@ -14,6 +14,7 @@ use crate::{
use crate::entity::UniqueEntityEquivalentSlice; use crate::entity::UniqueEntityEquivalentSlice;
use alloc::vec::Vec; use alloc::vec::Vec;
use bevy_utils::prelude::DebugName;
use core::{fmt, ptr}; use core::{fmt, ptr};
use fixedbitset::FixedBitSet; use fixedbitset::FixedBitSet;
use log::warn; use log::warn;
@ -672,7 +673,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
assert!( assert!(
component_access.is_subset(&self_access), component_access.is_subset(&self_access),
"Transmuted state for {} attempts to access terms that are not allowed by original state {}.", "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 { QueryState {
@ -791,7 +792,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
assert!( assert!(
component_access.is_subset(&joined_component_access), component_access.is_subset(&joined_component_access),
"Joined state for {} attempts to access terms that are not allowed by state {} joined with {}.", "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 { 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. /// 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. /// This is always guaranteed to run in `O(1)` time.
#[inline] #[inline]
pub fn get<'w>( pub fn get<'w>(
&mut self, &mut self,
world: &'w World, world: &'w World,
entity: Entity, entity: Entity,
) -> Result<ROQueryItem<'w, D>, QueryEntityError> { ) -> Result<ROQueryItem<'w, '_, D>, QueryEntityError> {
self.query(world).get_inner(entity) self.query(world).get_inner(entity)
} }
@ -892,7 +897,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&mut self, &mut self,
world: &'w World, world: &'w World,
entities: [Entity; N], entities: [Entity; N],
) -> Result<[ROQueryItem<'w, D>; N], QueryEntityError> { ) -> Result<[ROQueryItem<'w, '_, D>; N], QueryEntityError> {
self.query(world).get_many_inner(entities) self.query(world).get_many_inner(entities)
} }
@ -930,7 +935,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&mut self, &mut self,
world: &'w World, world: &'w World,
entities: UniqueEntityArray<N>, entities: UniqueEntityArray<N>,
) -> Result<[ROQueryItem<'w, D>; N], QueryEntityError> { ) -> Result<[ROQueryItem<'w, '_, D>; N], QueryEntityError> {
self.query(world).get_many_unique_inner(entities) self.query(world).get_many_unique_inner(entities)
} }
@ -942,7 +947,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&mut self, &mut self,
world: &'w mut World, world: &'w mut World,
entity: Entity, entity: Entity,
) -> Result<D::Item<'w>, QueryEntityError> { ) -> Result<D::Item<'w, '_>, QueryEntityError> {
self.query_mut(world).get_inner(entity) self.query_mut(world).get_inner(entity)
} }
@ -989,7 +994,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&mut self, &mut self,
world: &'w mut World, world: &'w mut World,
entities: [Entity; N], entities: [Entity; N],
) -> Result<[D::Item<'w>; N], QueryEntityError> { ) -> Result<[D::Item<'w, '_>; N], QueryEntityError> {
self.query_mut(world).get_many_mut_inner(entities) self.query_mut(world).get_many_mut_inner(entities)
} }
@ -1034,7 +1039,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&mut self, &mut self,
world: &'w mut World, world: &'w mut World,
entities: UniqueEntityArray<N>, entities: UniqueEntityArray<N>,
) -> Result<[D::Item<'w>; N], QueryEntityError> { ) -> Result<[D::Item<'w, '_>; N], QueryEntityError> {
self.query_mut(world).get_many_unique_inner(entities) self.query_mut(world).get_many_unique_inner(entities)
} }
@ -1056,7 +1061,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&self, &self,
world: &'w World, world: &'w World,
entity: Entity, entity: Entity,
) -> Result<ROQueryItem<'w, D>, QueryEntityError> { ) -> Result<ROQueryItem<'w, '_, D>, QueryEntityError> {
self.query_manual(world).get_inner(entity) self.query_manual(world).get_inner(entity)
} }
@ -1073,13 +1078,16 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
&mut self, &mut self,
world: UnsafeWorldCell<'w>, world: UnsafeWorldCell<'w>,
entity: Entity, entity: Entity,
) -> Result<D::Item<'w>, QueryEntityError> { ) -> Result<D::Item<'w, '_>, QueryEntityError> {
self.query_unchecked(world).get_inner(entity) self.query_unchecked(world).get_inner(entity)
} }
/// Returns an [`Iterator`] over the query results for the given [`World`]. /// 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. /// 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] #[inline]
pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, D::ReadOnly, F> { pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, D::ReadOnly, F> {
self.query(world).into_iter() 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. /// Items are returned in the order of the list of entities.
/// Entities that don't match the query are skipped. /// 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 /// # See also
/// ///
/// - [`iter_many_mut`](Self::iter_many_mut) to get mutable query items. /// - [`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 /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
#[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))] #[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))]
pub(crate) unsafe fn par_fold_init_unchecked_manual<'w, T, FN, INIT>( pub(crate) unsafe fn par_fold_init_unchecked_manual<'w, 's, T, FN, INIT>(
&self, &'s self,
init_accum: INIT, init_accum: INIT,
world: UnsafeWorldCell<'w>, world: UnsafeWorldCell<'w>,
batch_size: u32, batch_size: u32,
@ -1396,7 +1407,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
last_run: Tick, last_run: Tick,
this_run: Tick, this_run: Tick,
) where ) 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, INIT: Fn() -> T + Sync + Send + Clone,
{ {
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant: // 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 /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
#[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))] #[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>( pub(crate) unsafe fn par_many_unique_fold_init_unchecked_manual<'w, 's, T, FN, INIT, E>(
&self, &'s self,
init_accum: INIT, init_accum: INIT,
world: UnsafeWorldCell<'w>, world: UnsafeWorldCell<'w>,
entity_list: &UniqueEntityEquivalentSlice<E>, entity_list: &UniqueEntityEquivalentSlice<E>,
@ -1511,7 +1522,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
last_run: Tick, last_run: Tick,
this_run: Tick, this_run: Tick,
) where ) 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, INIT: Fn() -> T + Sync + Send + Clone,
E: EntityEquivalent + Sync, E: EntityEquivalent + Sync,
{ {
@ -1564,8 +1575,8 @@ impl<D: ReadOnlyQueryData, F: QueryFilter> QueryState<D, F> {
/// ///
/// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
#[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))] #[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))]
pub(crate) unsafe fn par_many_fold_init_unchecked_manual<'w, T, FN, INIT, E>( pub(crate) unsafe fn par_many_fold_init_unchecked_manual<'w, 's, T, FN, INIT, E>(
&self, &'s self,
init_accum: INIT, init_accum: INIT,
world: UnsafeWorldCell<'w>, world: UnsafeWorldCell<'w>,
entity_list: &[E], entity_list: &[E],
@ -1574,7 +1585,7 @@ impl<D: ReadOnlyQueryData, F: QueryFilter> QueryState<D, F> {
last_run: Tick, last_run: Tick,
this_run: Tick, this_run: Tick,
) where ) 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, INIT: Fn() -> T + Sync + Send + Clone,
E: EntityEquivalent + Sync, 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. /// Simply unwrapping the [`Result`] also works, but should generally be reserved for tests.
#[inline] #[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() self.query(world).single_inner()
} }
@ -1703,7 +1717,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
pub fn single_mut<'w>( pub fn single_mut<'w>(
&mut self, &mut self,
world: &'w mut World, world: &'w mut World,
) -> Result<D::Item<'w>, QuerySingleError> { ) -> Result<D::Item<'w, '_>, QuerySingleError> {
self.query_mut(world).single_inner() self.query_mut(world).single_inner()
} }
@ -1720,7 +1734,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
pub unsafe fn single_unchecked<'w>( pub unsafe fn single_unchecked<'w>(
&mut self, &mut self,
world: UnsafeWorldCell<'w>, world: UnsafeWorldCell<'w>,
) -> Result<D::Item<'w>, QuerySingleError> { ) -> Result<D::Item<'w, '_>, QuerySingleError> {
self.query_unchecked(world).single_inner() self.query_unchecked(world).single_inner()
} }
@ -1742,7 +1756,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
world: UnsafeWorldCell<'w>, world: UnsafeWorldCell<'w>,
last_run: Tick, last_run: Tick,
this_run: Tick, this_run: Tick,
) -> Result<D::Item<'w>, QuerySingleError> { ) -> Result<D::Item<'w, '_>, QuerySingleError> {
// SAFETY: // SAFETY:
// - The caller ensured we have the correct access to the world. // - The caller ensured we have the correct access to the world.
// - The caller ensured that the world matches. // - The caller ensured that the world matches.

View File

@ -42,7 +42,7 @@ use variadics_please::all_tuples;
/// [`QueryFilter`]: crate::query::QueryFilter /// [`QueryFilter`]: crate::query::QueryFilter
pub unsafe trait WorldQuery { pub unsafe trait WorldQuery {
/// Per archetype/table state retrieved by this [`WorldQuery`] to compute [`Self::Item`](crate::query::QueryData::Item) for each entity. /// 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), /// 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 /// 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. /// in to this function.
/// - `world` must have the **right** to access any access registered in `update_component_access`. /// - `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`]. /// - 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>, world: UnsafeWorldCell<'w>,
state: &Self::State, state: &'s Self::State,
last_run: Tick, last_run: Tick,
this_run: Tick, this_run: Tick,
) -> Self::Fetch<'w>; ) -> 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. /// - `archetype` and `tables` must be from the same [`World`] that [`WorldQuery::init_state`] was called on.
/// - `table` must correspond to `archetype`. /// - `table` must correspond to `archetype`.
/// - `state` must be the [`State`](Self::State) that `fetch` was initialized with. /// - `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>, fetch: &mut Self::Fetch<'w>,
state: &Self::State, state: &'s Self::State,
archetype: &'w Archetype, archetype: &'w Archetype,
table: &'w Table, 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. /// - `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. /// - `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`. /// Adds any component accesses used by this [`WorldQuery`] to `access`.
/// ///
@ -166,7 +170,7 @@ macro_rules! impl_tuple_world_query {
} }
#[inline] #[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; let ($($name,)*) = state;
// SAFETY: The invariants are upheld by the caller. // SAFETY: The invariants are upheld by the caller.
($(unsafe { $name::init_fetch(world, $name, last_run, this_run) },)*) ($(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)*; const IS_DENSE: bool = true $(&& $name::IS_DENSE)*;
#[inline] #[inline]
unsafe fn set_archetype<'w>( unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>, fetch: &mut Self::Fetch<'w>,
state: &Self::State, state: &'s Self::State,
archetype: &'w Archetype, archetype: &'w Archetype,
table: &'w Table table: &'w Table
) { ) {
@ -188,7 +192,7 @@ macro_rules! impl_tuple_world_query {
} }
#[inline] #[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 ($($name,)*) = fetch;
let ($($state,)*) = state; let ($($state,)*) = state;
// SAFETY: The invariants are upheld by the caller. // SAFETY: The invariants are upheld by the caller.

View File

@ -5,6 +5,7 @@
//! //!
//! Same as [`super::component`], but for bundles. //! Same as [`super::component`], but for bundles.
use alloc::boxed::Box; use alloc::boxed::Box;
use bevy_utils::prelude::DebugName;
use core::any::{Any, TypeId}; use core::any::{Any, TypeId};
use crate::{ use crate::{
@ -172,7 +173,7 @@ impl<B: Bundle + Reflect + TypePath + BundleFromComponents> FromType<B> for Refl
_ => panic!( _ => panic!(
"expected bundle `{}` to be named struct or tuple", "expected bundle `{}` to be named struct or tuple",
// FIXME: once we have unique reflect, use `TypePath`. // 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!( _ => panic!(
"expected bundle `{}` to be a named struct or tuple", "expected bundle `{}` to be a named struct or tuple",
// FIXME: once we have unique reflect, use `TypePath`. // 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 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. /// 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| { apply: |mut entity, reflected_component| {
if !C::Mutability::MUTABLE { 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"); 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: |entity| entity.get::<C>().map(|c| c as &dyn Reflect),
reflect_mut: |entity| { reflect_mut: |entity| {
if !C::Mutability::MUTABLE { 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"); 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| { reflect_unchecked_mut: |entity| {
if !C::Mutability::MUTABLE { 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"); 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 map_entities;
mod resource; mod resource;
use bevy_utils::prelude::DebugName;
pub use bundle::{ReflectBundle, ReflectBundleFns}; pub use bundle::{ReflectBundle, ReflectBundleFns};
pub use component::{ReflectComponent, ReflectComponentFns}; pub use component::{ReflectComponent, ReflectComponentFns};
pub use entity_commands::ReflectCommandExt; 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)]` \ `Default` or `FromWorld` traits. Are you perhaps missing a `#[reflect(Default)]` \
or `#[reflect(FromWorld)]`?", or `#[reflect(FromWorld)]`?",
// FIXME: once we have unique reflect, use `TypePath`. // 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 alloc::format;
use bevy_utils::prelude::DebugName;
pub use related_methods::*; pub use related_methods::*;
pub use relationship_query::*; pub use relationship_query::*;
pub use relationship_source_collection::*; pub use relationship_source_collection::*;
@ -105,8 +106,8 @@ pub trait Relationship: Component + Sized {
warn!( warn!(
"{}The {}({target_entity:?}) relationship on entity {entity:?} points to itself. The invalid {} relationship has been removed.", "{}The {}({target_entity:?}) relationship on entity {entity:?} points to itself. The invalid {} relationship has been removed.",
caller.map(|location|format!("{location}: ")).unwrap_or_default(), caller.map(|location|format!("{location}: ")).unwrap_or_default(),
core::any::type_name::<Self>(), DebugName::type_name::<Self>(),
core::any::type_name::<Self>() DebugName::type_name::<Self>()
); );
world.commands().entity(entity).remove::<Self>(); world.commands().entity(entity).remove::<Self>();
return; return;
@ -125,8 +126,8 @@ pub trait Relationship: Component + Sized {
warn!( warn!(
"{}The {}({target_entity:?}) relationship on entity {entity:?} relates to an entity that does not exist. The invalid {} relationship has been removed.", "{}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(), caller.map(|location|format!("{location}: ")).unwrap_or_default(),
core::any::type_name::<Self>(), DebugName::type_name::<Self>(),
core::any::type_name::<Self>() DebugName::type_name::<Self>()
); );
world.commands().entity(entity).remove::<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. /// target entity of that relationship.
pub fn related<R: Relationship>(&'w self, entity: Entity) -> Option<Entity> pub fn related<R: Relationship>(&'w self, entity: Entity) -> Option<Entity>
where 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() 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, entity: Entity,
) -> impl Iterator<Item = Entity> + 'w ) -> impl Iterator<Item = Entity> + 'w
where where
<D as QueryData>::ReadOnly: QueryData<Item<'w> = &'w S>, <D as QueryData>::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
{ {
self.get(entity) self.get(entity)
.into_iter() .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. /// 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 pub fn root_ancestor<R: Relationship>(&'w self, entity: Entity) -> Entity
where 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 // Recursively search up the tree until we're out of parents
match self.get(entity) { 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>( pub fn iter_leaves<S: RelationshipTarget>(
&'w self, &'w self,
entity: Entity, entity: Entity,
) -> impl Iterator<Item = Entity> + 'w ) -> impl Iterator<Item = Entity> + use<'w, 's, S, D, F>
where where
<D as QueryData>::ReadOnly: QueryData<Item<'w> = &'w S>, <D as QueryData>::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
SourceIter<'w, S>: DoubleEndedIterator, SourceIter<'w, S>: DoubleEndedIterator,
{ {
self.iter_descendants_depth_first(entity).filter(|entity| { 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, entity: Entity,
) -> impl Iterator<Item = Entity> + 'w ) -> impl Iterator<Item = Entity> + 'w
where 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) self.get(entity)
.ok() .ok()
@ -103,7 +103,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
entity: Entity, entity: Entity,
) -> DescendantIter<'w, 's, D, F, S> ) -> DescendantIter<'w, 's, D, F, S>
where where
D::ReadOnly: QueryData<Item<'w> = &'w S>, D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
{ {
DescendantIter::new(self, entity) DescendantIter::new(self, entity)
} }
@ -120,7 +120,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
entity: Entity, entity: Entity,
) -> DescendantDepthFirstIter<'w, 's, D, F, S> ) -> DescendantDepthFirstIter<'w, 's, D, F, S>
where where
D::ReadOnly: QueryData<Item<'w> = &'w S>, D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
SourceIter<'w, S>: DoubleEndedIterator, SourceIter<'w, S>: DoubleEndedIterator,
{ {
DescendantDepthFirstIter::new(self, entity) DescendantDepthFirstIter::new(self, entity)
@ -137,7 +137,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
entity: Entity, entity: Entity,
) -> AncestorIter<'w, 's, D, F, R> ) -> AncestorIter<'w, 's, D, F, R>
where where
D::ReadOnly: QueryData<Item<'w> = &'w R>, D::ReadOnly: QueryData<Item<'w, 's> = &'w R>,
{ {
AncestorIter::new(self, entity) 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. /// Traverses the hierarchy breadth-first.
pub struct DescendantIter<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> pub struct DescendantIter<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget>
where where
D::ReadOnly: QueryData<Item<'w> = &'w S>, D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
{ {
children_query: &'w Query<'w, 's, D, F>, children_query: &'w Query<'w, 's, D, F>,
vecdeque: VecDeque<Entity>, vecdeque: VecDeque<Entity>,
@ -156,7 +156,7 @@ where
impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> DescendantIter<'w, 's, D, F, S> impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> DescendantIter<'w, 's, D, F, S>
where where
D::ReadOnly: QueryData<Item<'w> = &'w S>, D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
{ {
/// Returns a new [`DescendantIter`]. /// Returns a new [`DescendantIter`].
pub fn new(children_query: &'w Query<'w, 's, D, F>, entity: Entity) -> Self { 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 impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> Iterator
for DescendantIter<'w, 's, D, F, S> for DescendantIter<'w, 's, D, F, S>
where where
D::ReadOnly: QueryData<Item<'w> = &'w S>, D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
{ {
type Item = Entity; type Item = Entity;
@ -194,7 +194,7 @@ where
/// Traverses the hierarchy depth-first. /// Traverses the hierarchy depth-first.
pub struct DescendantDepthFirstIter<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> pub struct DescendantDepthFirstIter<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget>
where where
D::ReadOnly: QueryData<Item<'w> = &'w S>, D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
{ {
children_query: &'w Query<'w, 's, D, F>, children_query: &'w Query<'w, 's, D, F>,
stack: SmallVec<[Entity; 8]>, stack: SmallVec<[Entity; 8]>,
@ -203,7 +203,7 @@ where
impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget>
DescendantDepthFirstIter<'w, 's, D, F, S> DescendantDepthFirstIter<'w, 's, D, F, S>
where where
D::ReadOnly: QueryData<Item<'w> = &'w S>, D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
SourceIter<'w, S>: DoubleEndedIterator, SourceIter<'w, S>: DoubleEndedIterator,
{ {
/// Returns a new [`DescendantDepthFirstIter`]. /// Returns a new [`DescendantDepthFirstIter`].
@ -220,7 +220,7 @@ where
impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> Iterator impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> Iterator
for DescendantDepthFirstIter<'w, 's, D, F, S> for DescendantDepthFirstIter<'w, 's, D, F, S>
where where
D::ReadOnly: QueryData<Item<'w> = &'w S>, D::ReadOnly: QueryData<Item<'w, 's> = &'w S>,
SourceIter<'w, S>: DoubleEndedIterator, SourceIter<'w, S>: DoubleEndedIterator,
{ {
type Item = Entity; type Item = Entity;
@ -239,7 +239,7 @@ where
/// An [`Iterator`] of [`Entity`]s over the ancestors of an [`Entity`]. /// An [`Iterator`] of [`Entity`]s over the ancestors of an [`Entity`].
pub struct AncestorIter<'w, 's, D: QueryData, F: QueryFilter, R: Relationship> pub struct AncestorIter<'w, 's, D: QueryData, F: QueryFilter, R: Relationship>
where where
D::ReadOnly: QueryData<Item<'w> = &'w R>, D::ReadOnly: QueryData<Item<'w, 's> = &'w R>,
{ {
parent_query: &'w Query<'w, 's, D, F>, parent_query: &'w Query<'w, 's, D, F>,
next: Option<Entity>, next: Option<Entity>,
@ -247,7 +247,7 @@ where
impl<'w, 's, D: QueryData, F: QueryFilter, R: Relationship> AncestorIter<'w, 's, D, F, R> impl<'w, 's, D: QueryData, F: QueryFilter, R: Relationship> AncestorIter<'w, 's, D, F, R>
where where
D::ReadOnly: QueryData<Item<'w> = &'w R>, D::ReadOnly: QueryData<Item<'w, 's> = &'w R>,
{ {
/// Returns a new [`AncestorIter`]. /// Returns a new [`AncestorIter`].
pub fn new(parent_query: &'w Query<'w, 's, D, F>, entity: Entity) -> Self { 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 impl<'w, 's, D: QueryData, F: QueryFilter, R: Relationship> Iterator
for AncestorIter<'w, 's, D, F, R> for AncestorIter<'w, 's, D, F, R>
where where
D::ReadOnly: QueryData<Item<'w> = &'w R>, D::ReadOnly: QueryData<Item<'w, 's> = &'w R>,
{ {
type Item = Entity; 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 core::ops::Not;
use crate::system::{ use crate::system::{
@ -154,7 +155,7 @@ pub trait SystemCondition<Marker, In: SystemInput = (), Out = bool>:
let a = IntoSystem::into_system(self); let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(and); let b = IntoSystem::into_system(and);
let name = format!("{} && {}", a.name(), b.name()); 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` /// 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 a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(nand); let b = IntoSystem::into_system(nand);
let name = format!("!({} && {})", a.name(), b.name()); 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` /// 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 a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(nor); let b = IntoSystem::into_system(nor);
let name = format!("!({} || {})", a.name(), b.name()); 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` /// 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 a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(or); let b = IntoSystem::into_system(or);
let name = format!("{} || {}", a.name(), b.name()); 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` /// 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 a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(xnor); let b = IntoSystem::into_system(xnor);
let name = format!("!({} ^ {})", a.name(), b.name()); 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` /// 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 a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(xor); let b = IntoSystem::into_system(xor);
let name = format!("({} ^ {})", a.name(), b.name()); 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 simple;
mod single_threaded; mod single_threaded;
use alloc::{borrow::Cow, vec, vec::Vec}; use alloc::{vec, vec::Vec};
use bevy_utils::prelude::DebugName;
use core::any::TypeId; use core::any::TypeId;
#[expect(deprecated, reason = "We still need to support this.")] #[expect(deprecated, reason = "We still need to support this.")]
@ -158,8 +159,8 @@ impl System for ApplyDeferred {
type In = (); type In = ();
type Out = Result<()>; type Out = Result<()>;
fn name(&self) -> Cow<'static, str> { fn name(&self) -> DebugName {
Cow::Borrowed("bevy_ecs::apply_deferred") DebugName::borrowed("bevy_ecs::apply_deferred")
} }
fn flags(&self) -> SystemStateFlags { fn flags(&self) -> SystemStateFlags {

View File

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

View File

@ -73,7 +73,7 @@ impl SystemExecutor for SimpleExecutor {
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
let name = schedule.systems[system_index].system.name(); let name = schedule.systems[system_index].system.name();
#[cfg(feature = "trace")] #[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); let mut should_run = !self.completed_systems.contains(system_index);
for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() { 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.")] #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")]
{ {
if let Err(payload) = std::panic::catch_unwind(f) { 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); std::panic::resume_unwind(payload);
} }
} }

View File

@ -74,7 +74,7 @@ impl SystemExecutor for SingleThreadedExecutor {
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
let name = schedule.systems[system_index].system.name(); let name = schedule.systems[system_index].system.name();
#[cfg(feature = "trace")] #[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); let mut should_run = !self.completed_systems.contains(system_index);
for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() { 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.")] #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")]
{ {
if let Err(payload) = std::panic::catch_unwind(f) { 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); std::panic::resume_unwind(payload);
} }
} }

View File

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

View File

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

View File

@ -895,9 +895,9 @@ mod tests {
($schedule:expr, $skipped_systems:expr, $($system:expr),*) => { ($schedule:expr, $skipped_systems:expr, $($system:expr),*) => {
// pull an ordered list of systems in the schedule, and save the // pull an ordered list of systems in the schedule, and save the
// system TypeId, and name. // 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)| { .map(|(_, system)| {
(system.type_id(), system.name()) (system.type_id(), system.name().as_string())
}) })
.collect(); .collect();

View File

@ -3,8 +3,8 @@ use crate::{
component::{CheckChangeTicks, ComponentId, ComponentTicks, Components, Tick, TickCells}, component::{CheckChangeTicks, ComponentId, ComponentTicks, Components, Tick, TickCells},
storage::{blob_vec::BlobVec, SparseSet}, storage::{blob_vec::BlobVec, SparseSet},
}; };
use alloc::string::String;
use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
use bevy_utils::prelude::DebugName;
use core::{cell::UnsafeCell, mem::ManuallyDrop, panic::Location}; use core::{cell::UnsafeCell, mem::ManuallyDrop, panic::Location};
#[cfg(feature = "std")] #[cfg(feature = "std")]
@ -23,7 +23,7 @@ pub struct ResourceData<const SEND: bool> {
not(feature = "std"), not(feature = "std"),
expect(dead_code, reason = "currently only used with the std feature") expect(dead_code, reason = "currently only used with the std feature")
)] )]
type_name: String, type_name: DebugName,
#[cfg(feature = "std")] #[cfg(feature = "std")]
origin_thread_id: Option<ThreadId>, origin_thread_id: Option<ThreadId>,
changed_by: MaybeLocation<UnsafeCell<&'static Location<'static>>>, changed_by: MaybeLocation<UnsafeCell<&'static Location<'static>>>,
@ -385,7 +385,7 @@ impl<const SEND: bool> Resources<SEND> {
data: ManuallyDrop::new(data), data: ManuallyDrop::new(data),
added_ticks: UnsafeCell::new(Tick::new(0)), added_ticks: UnsafeCell::new(Tick::new(0)),
changed_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")] #[cfg(feature = "std")]
origin_thread_id: None, origin_thread_id: None,
changed_by: MaybeLocation::caller().map(UnsafeCell::new), 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 super::{IntoSystem, ReadOnlySystem, System, SystemParamValidationError};
use crate::{ use crate::{
@ -101,7 +102,7 @@ where
pub struct AdapterSystem<Func, S> { pub struct AdapterSystem<Func, S> {
func: Func, func: Func,
system: S, system: S,
name: Cow<'static, str>, name: DebugName,
} }
impl<Func, S> AdapterSystem<Func, S> impl<Func, S> AdapterSystem<Func, S>
@ -110,7 +111,7 @@ where
S: System, S: System,
{ {
/// Creates a new [`System`] that uses `func` to adapt `system`, via the [`Adapt`] trait. /// 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 } Self { func, system, name }
} }
} }
@ -123,7 +124,7 @@ where
type In = Func::In; type In = Func::In;
type Out = Func::Out; type Out = Func::Out;
fn name(&self) -> Cow<'static, str> { fn name(&self) -> DebugName {
self.name.clone() 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 core::marker::PhantomData;
use crate::{ use crate::{
@ -55,7 +56,7 @@ use super::{IntoSystem, ReadOnlySystem, System};
/// IntoSystem::into_system(resource_equals(A(1))), /// IntoSystem::into_system(resource_equals(A(1))),
/// IntoSystem::into_system(resource_equals(B(1))), /// IntoSystem::into_system(resource_equals(B(1))),
/// // The name of the combined system. /// // 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; } /// # fn my_system(mut flag: ResMut<RanFlag>) { flag.0 = true; }
/// # /// #
@ -112,14 +113,14 @@ pub struct CombinatorSystem<Func, A, B> {
_marker: PhantomData<fn() -> Func>, _marker: PhantomData<fn() -> Func>,
a: A, a: A,
b: B, b: B,
name: Cow<'static, str>, name: DebugName,
} }
impl<Func, A, B> CombinatorSystem<Func, A, B> { impl<Func, A, B> CombinatorSystem<Func, A, B> {
/// Creates a new system that combines two inner systems. /// Creates a new system that combines two inner systems.
/// ///
/// The returned system will only be usable if `Func` implements [`Combine<A, B>`]. /// 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 { Self {
_marker: PhantomData, _marker: PhantomData,
a, a,
@ -138,7 +139,7 @@ where
type In = Func::In; type In = Func::In;
type Out = Func::Out; type Out = Func::Out;
fn name(&self) -> Cow<'static, str> { fn name(&self) -> DebugName {
self.name.clone() self.name.clone()
} }
@ -271,7 +272,7 @@ where
let system_a = IntoSystem::into_system(this.a); let system_a = IntoSystem::into_system(this.a);
let system_b = IntoSystem::into_system(this.b); let system_b = IntoSystem::into_system(this.b);
let name = format!("Pipe({}, {})", system_a.name(), system_b.name()); 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> { pub struct PipeSystem<A, B> {
a: A, a: A,
b: B, b: B,
name: Cow<'static, str>, name: DebugName,
} }
impl<A, B> PipeSystem<A, B> impl<A, B> PipeSystem<A, B>
@ -327,7 +328,7 @@ where
for<'a> B::In: SystemInput<Inner<'a> = A::Out>, for<'a> B::In: SystemInput<Inner<'a> = A::Out>,
{ {
/// Creates a new system that pipes two inner systems. /// 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 } Self { a, b, name }
} }
} }
@ -341,7 +342,7 @@ where
type In = A::In; type In = A::In;
type Out = B::Out; type Out = B::Out;
fn name(&self) -> Cow<'static, str> { fn name(&self) -> DebugName {
self.name.clone() self.name.clone()
} }

View File

@ -144,10 +144,11 @@ where
/// A [`Command`] that runs the given system, /// A [`Command`] that runs the given system,
/// caching its [`SystemId`] in a [`CachedSystemId`](crate::system::CachedSystemId) resource. /// 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 where
O: 'static,
M: 'static, M: 'static,
S: IntoSystem<(), (), M> + Send + 'static, S: IntoSystem<(), O, M> + Send + 'static,
{ {
move |world: &mut World| -> Result { move |world: &mut World| -> Result {
world.run_system_cached(system)?; world.run_system_cached(system)?;
@ -157,11 +158,15 @@ where
/// A [`Command`] that runs the given system with the given input value, /// A [`Command`] that runs the given system with the given input value,
/// caching its [`SystemId`] in a [`CachedSystemId`](crate::system::CachedSystemId) resource. /// 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 where
I: SystemInput<Inner<'static>: Send> + Send + 'static, I: SystemInput<Inner<'static>: Send> + Send + 'static,
O: 'static,
M: 'static, M: 'static,
S: IntoSystem<I, (), M> + Send + 'static, S: IntoSystem<I, O, M> + Send + 'static,
{ {
move |world: &mut World| -> Result { move |world: &mut World| -> Result {
world.run_system_cached_with(system, input)?; 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> pub fn unregister_system<I, O>(system_id: SystemId<I, O>) -> impl Command<Result>
where where
I: SystemInput + Send + 'static, I: SystemInput + Send + 'static,
O: Send + 'static, O: 'static,
{ {
move |world: &mut World| -> Result { move |world: &mut World| -> Result {
world.unregister_system(system_id)?; 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), /// It will internally return a [`RegisteredSystemError`](crate::system::system_registry::RegisteredSystemError),
/// which will be handled by [logging the error at the `warn` level](warn). /// 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)); self.queue(command::run_system(id).handle_error_with(warn));
} }
@ -1010,7 +1010,7 @@ impl<'w, 's> Commands<'w, 's> {
) -> SystemId<I, O> ) -> SystemId<I, O>
where where
I: SystemInput + Send + 'static, I: SystemInput + Send + 'static,
O: Send + 'static, O: 'static,
{ {
let entity = self.spawn_empty().id(); let entity = self.spawn_empty().id();
let system = RegisteredSystem::<I, O>::new(Box::new(IntoSystem::into_system(system))); 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>) pub fn unregister_system<I, O>(&mut self, system_id: SystemId<I, O>)
where where
I: SystemInput + Send + 'static, I: SystemInput + Send + 'static,
O: Send + 'static, O: 'static,
{ {
self.queue(command::unregister_system(system_id).handle_error_with(warn)); 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`]. /// consider passing them in as inputs via [`Commands::run_system_cached_with`].
/// ///
/// If that's not an option, consider [`Commands::register_system`] instead. /// 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 where
O: 'static,
M: '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)); 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. /// consider passing them in as inputs.
/// ///
/// If that's not an option, consider [`Commands::register_system`] instead. /// 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 where
I: SystemInput<Inner<'static>: Send> + Send + 'static, I: SystemInput<Inner<'static>: Send> + Send + 'static,
O: 'static,
M: '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)); 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 alloc::{borrow::Cow, vec, vec::Vec};
use bevy_utils::prelude::DebugName;
use core::marker::PhantomData; use core::marker::PhantomData;
use variadics_please::all_tuples; use variadics_please::all_tuples;
@ -42,7 +43,7 @@ where
/// ///
/// Useful to give closure systems more readable and unique names for debugging and tracing. /// 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 { 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 self
} }
} }
@ -83,7 +84,7 @@ where
type Out = F::Out; type Out = F::Out;
#[inline] #[inline]
fn name(&self) -> Cow<'static, str> { fn name(&self) -> DebugName {
self.system_meta.name.clone() self.system_meta.name.clone()
} }
@ -181,7 +182,7 @@ where
check_system_change_tick( check_system_change_tick(
&mut self.system_meta.last_run, &mut self.system_meta.last_run,
check, 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 alloc::{borrow::Cow, vec, vec::Vec};
use bevy_utils::prelude::DebugName;
use core::marker::PhantomData; use core::marker::PhantomData;
use variadics_please::all_tuples; use variadics_please::all_tuples;
@ -24,7 +25,7 @@ use super::{
/// The metadata of a [`System`]. /// The metadata of a [`System`].
#[derive(Clone)] #[derive(Clone)]
pub struct SystemMeta { 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 // NOTE: this must be kept private. making a SystemMeta non-send is irreversible to prevent
// SystemParams from overriding each other // SystemParams from overriding each other
flags: SystemStateFlags, flags: SystemStateFlags,
@ -37,21 +38,21 @@ pub struct SystemMeta {
impl SystemMeta { impl SystemMeta {
pub(crate) fn new<T>() -> Self { pub(crate) fn new<T>() -> Self {
let name = core::any::type_name::<T>(); let name = DebugName::type_name::<T>();
Self { 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(), flags: SystemStateFlags::empty(),
last_run: Tick::new(0), 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 /// Returns the system's name
#[inline] #[inline]
pub fn name(&self) -> &str { pub fn name(&self) -> &DebugName {
&self.name &self.name
} }
@ -67,7 +68,7 @@ impl SystemMeta {
self.system_span = info_span!("system", name = name); self.system_span = info_span!("system", name = name);
self.commands_span = info_span!("system_commands", 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`]. /// Returns true if the system is [`Send`].
@ -600,7 +601,7 @@ where
type Out = F::Out; type Out = F::Out;
#[inline] #[inline]
fn name(&self) -> Cow<'static, str> { fn name(&self) -> DebugName {
self.system_meta.name.clone() self.system_meta.name.clone()
} }
@ -712,7 +713,7 @@ where
check_system_change_tick( check_system_change_tick(
&mut self.system_meta.last_run, &mut self.system_meta.last_run,
check, 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 core::marker::PhantomData;
use crate::{ use crate::{
@ -112,7 +113,7 @@ where
type Out = Result; type Out = Result;
#[inline] #[inline]
fn name(&self) -> Cow<'static, str> { fn name(&self) -> DebugName {
self.observer.name() self.observer.name()
} }

View File

@ -1,3 +1,5 @@
use bevy_utils::prelude::DebugName;
use crate::{ use crate::{
batching::BatchingStrategy, batching::BatchingStrategy,
component::Tick, 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 /// [`par_iter_mut`]: Self::par_iter_mut
/// [`World`]: crate::world::World /// [`World`]: crate::world::World
#[inline] #[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() 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 /// [`par_iter`]: Self::par_iter
/// [`World`]: crate::world::World /// [`World`]: crate::world::World
#[inline] #[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() 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>>( pub fn par_iter_many<EntityList: IntoIterator<Item: EntityEquivalent>>(
&self, &self,
entities: EntityList, entities: EntityList,
) -> QueryParManyIter<'_, '_, D::ReadOnly, F, EntityList::Item> { ) -> QueryParManyIter<'_, 's, D::ReadOnly, F, EntityList::Item> {
QueryParManyIter { QueryParManyIter {
world: self.world, world: self.world,
state: self.state.as_readonly(), 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>>( pub fn par_iter_many_unique<EntityList: EntitySet<Item: Sync>>(
&self, &self,
entities: EntityList, entities: EntityList,
) -> QueryParManyUniqueIter<'_, '_, D::ReadOnly, F, EntityList::Item> { ) -> QueryParManyUniqueIter<'_, 's, D::ReadOnly, F, EntityList::Item> {
QueryParManyUniqueIter { QueryParManyUniqueIter {
world: self.world, world: self.world,
state: self.state.as_readonly(), 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>>( pub fn par_iter_many_unique_mut<EntityList: EntitySet<Item: Sync>>(
&mut self, &mut self,
entities: EntityList, entities: EntityList,
) -> QueryParManyUniqueIter<'_, '_, D, F, EntityList::Item> { ) -> QueryParManyUniqueIter<'_, 's, D, F, EntityList::Item> {
QueryParManyUniqueIter { QueryParManyUniqueIter {
world: self.world, world: self.world,
state: self.state, 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. /// - [`get_mut`](Self::get_mut) to get a mutable query item.
#[inline] #[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) 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>( pub fn get_many<const N: usize>(
&self, &self,
entities: [Entity; N], 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` // Note that we call a separate `*_inner` method from `get_many_mut`
// because we don't need to check for duplicates. // because we don't need to check for duplicates.
self.as_readonly().get_many_inner(entities) 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>( pub fn get_many_unique<const N: usize>(
&self, &self,
entities: UniqueEntityArray<N>, entities: UniqueEntityArray<N>,
) -> Result<[ROQueryItem<'_, D>; N], QueryEntityError> { ) -> Result<[ROQueryItem<'_, 's, D>; N], QueryEntityError> {
self.as_readonly().get_many_unique_inner(entities) 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. /// - [`get`](Self::get) to get a read-only query item.
#[inline] #[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) 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`]. /// - [`get_mut`](Self::get_mut) to get the item using a mutable borrow of the [`Query`].
#[inline] #[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. // SAFETY: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict // same-system queries have runtime borrow checks when they conflict
unsafe { 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); D::set_archetype(&mut fetch, &self.state.fetch_state, archetype, table);
F::set_archetype(&mut filter, &self.state.filter_state, archetype, table); F::set_archetype(&mut filter, &self.state.filter_state, archetype, table);
if F::filter_fetch(&mut filter, entity, location.table_row) { if F::filter_fetch(
Ok(D::fetch(&mut fetch, entity, location.table_row)) &self.state.filter_state,
&mut filter,
entity,
location.table_row,
) {
Ok(D::fetch(
&self.state.fetch_state,
&mut fetch,
entity,
location.table_row,
))
} else { } else {
Err(QueryEntityError::QueryDoesNotMatch( Err(QueryEntityError::QueryDoesNotMatch(
entity, 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>( pub fn get_many_mut<const N: usize>(
&mut self, &mut self,
entities: [Entity; N], entities: [Entity; N],
) -> Result<[D::Item<'_>; N], QueryEntityError> { ) -> Result<[D::Item<'_, 's>; N], QueryEntityError> {
self.reborrow().get_many_mut_inner(entities) 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>( pub fn get_many_unique_mut<const N: usize>(
&mut self, &mut self,
entities: UniqueEntityArray<N>, entities: UniqueEntityArray<N>,
) -> Result<[D::Item<'_>; N], QueryEntityError> { ) -> Result<[D::Item<'_, 's>; N], QueryEntityError> {
self.reborrow().get_many_unique_inner(entities) 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>( pub fn get_many_mut_inner<const N: usize>(
self, self,
entities: [Entity; N], entities: [Entity; N],
) -> Result<[D::Item<'w>; N], QueryEntityError> { ) -> Result<[D::Item<'w, 's>; N], QueryEntityError> {
// Verify that all entities are unique // Verify that all entities are unique
for i in 0..N { for i in 0..N {
for j in 0..i { 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>( pub fn get_many_inner<const N: usize>(
self, self,
entities: [Entity; N], entities: [Entity; N],
) -> Result<[D::Item<'w>; N], QueryEntityError> ) -> Result<[D::Item<'w, 's>; N], QueryEntityError>
where where
D: ReadOnlyQueryData, 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>( pub fn get_many_unique_inner<const N: usize>(
self, self,
entities: UniqueEntityArray<N>, 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. // SAFETY: All entities are unique, so the results don't alias.
unsafe { self.get_many_impl(entities.into_inner()) } 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>( unsafe fn get_many_impl<const N: usize>(
self, self,
entities: [Entity; N], entities: [Entity; N],
) -> Result<[D::Item<'w>; N], QueryEntityError> { ) -> Result<[D::Item<'w, 's>; N], QueryEntityError> {
let mut values = [(); N].map(|_| MaybeUninit::uninit()); let mut values = [(); N].map(|_| MaybeUninit::uninit());
for (value, entity) in core::iter::zip(&mut values, entities) { 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. /// - [`get_mut`](Self::get_mut) for the safe version.
#[inline] #[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. // SAFETY: The caller promises that this will not result in multiple mutable references.
unsafe { self.reborrow_unsafe() }.get_inner(entity) 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. /// - [`single_mut`](Self::single_mut) to get the mutable query item.
#[inline] #[inline]
pub fn single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> { pub fn single(&self) -> Result<ROQueryItem<'_, 's, D>, QuerySingleError> {
self.as_readonly().single_inner() 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. /// - [`single`](Self::single) to get the read-only query item.
#[inline] #[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() 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_mut`](Self::single_mut) to get the mutable query item.
/// - [`single_inner`](Self::single_inner) for the panicking version. /// - [`single_inner`](Self::single_inner) for the panicking version.
#[inline] #[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 mut query = self.into_iter();
let first = query.next(); let first = query.next();
let extra = query.next().is_some(); let extra = query.next().is_some();
match (first, extra) { match (first, extra) {
(Some(r), false) => Ok(r), (Some(r), false) => Ok(r),
(None, _) => Err(QuerySingleError::NoEntities(core::any::type_name::<Self>())), (None, _) => Err(QuerySingleError::NoEntities(DebugName::type_name::<Self>())),
(Some(_), _) => Err(QuerySingleError::MultipleEntities(core::any::type_name::< (Some(_), _) => Err(QuerySingleError::MultipleEntities(DebugName::type_name::<
Self, 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> { 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>; type IntoIter = QueryIter<'w, 's, D, F>;
fn into_iter(self) -> Self::IntoIter { 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> { 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>; type IntoIter = QueryIter<'w, 's, D::ReadOnly, F>;
fn into_iter(self) -> Self::IntoIter { 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> { 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>; type IntoIter = QueryIter<'w, 's, D, F>;
fn into_iter(self) -> Self::IntoIter { 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. /// 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. /// 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 struct Single<'w, 's, D: QueryData, F: QueryFilter = ()> {
pub(crate) item: D::Item<'w>, pub(crate) item: D::Item<'w, 's>,
pub(crate) _filter: PhantomData<F>, pub(crate) _filter: PhantomData<F>,
} }
impl<'w, D: QueryData, F: QueryFilter> Deref for Single<'w, D, F> { impl<'w, 's, D: QueryData, F: QueryFilter> Deref for Single<'w, 's, D, F> {
type Target = D::Item<'w>; type Target = D::Item<'w, 's>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.item &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 { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.item &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. /// 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 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::{ use crate::{
component::{CheckChangeTicks, ComponentId, Tick}, component::{CheckChangeTicks, ComponentId, Tick},
@ -25,7 +26,7 @@ impl<S: System<In = ()>> System for InfallibleSystemWrapper<S> {
type Out = Result; type Out = Result;
#[inline] #[inline]
fn name(&self) -> Cow<'static, str> { fn name(&self) -> DebugName {
self.0.name() self.0.name()
} }
@ -137,10 +138,9 @@ where
T: Send + Sync + 'static, T: Send + Sync + 'static,
{ {
type In = (); type In = ();
type Out = S::Out; type Out = S::Out;
fn name(&self) -> Cow<'static, str> { fn name(&self) -> DebugName {
self.system.name() self.system.name()
} }
@ -231,10 +231,9 @@ where
T: FromWorld + Send + Sync + 'static, T: FromWorld + Send + Sync + 'static,
{ {
type In = (); type In = ();
type Out = S::Out; type Out = S::Out;
fn name(&self) -> Cow<'static, str> { fn name(&self) -> DebugName {
self.system.name() self.system.name()
} }

View File

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

View File

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

View File

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

View File

@ -651,6 +651,19 @@ mod tests {
assert_eq!(output, NonCopy(3)); 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] #[test]
fn exclusive_system() { fn exclusive_system() {
let mut world = World::new(); let mut world = World::new();
@ -751,19 +764,54 @@ mod tests {
assert!(matches!(output, Ok(x) if x == four())); 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] #[test]
fn cached_system_commands() { fn cached_system_commands() {
fn sys(mut counter: ResMut<Counter>) { fn sys(mut counter: ResMut<Counter>) {
counter.0 = 1; counter.0 += 1;
} }
let mut world = World::new(); let mut world = World::new();
world.insert_resource(Counter(0)); world.insert_resource(Counter(0));
world.commands().run_system_cached(sys); world.commands().run_system_cached(sys);
world.flush_commands(); world.flush_commands();
assert_eq!(world.resource::<Counter>().0, 1); 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] #[test]

View File

@ -1,6 +1,10 @@
//! A trait for components that let you traverse the ECS. //! 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. /// 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 /// [specify the direction]: crate::event::EntityEvent::Traversal
/// [event propagation]: crate::observer::On::propagate /// [event propagation]: crate::observer::On::propagate
/// [observers]: crate::observer::Observer /// [observers]: crate::observer::Observer
pub trait Traversal<D: ?Sized>: ReadOnlyQueryData { pub trait Traversal<D: ?Sized>: ReadOnlyQueryData + ReleaseStateQueryData {
/// Returns the next entity to visit. /// 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 () { impl<D> Traversal<D> for () {
fn traverse(_: Self::Item<'_>, _data: &D) -> Option<Entity> { fn traverse(_: Self::Item<'_, '_>, _data: &D) -> Option<Entity> {
None None
} }
} }
@ -39,7 +43,7 @@ impl<D> Traversal<D> for () {
/// ///
/// [event propagation]: crate::observer::On::propagate /// [event propagation]: crate::observer::On::propagate
impl<R: Relationship, D> Traversal<D> for &R { 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()) Some(item.get())
} }
} }

View File

@ -1,5 +1,7 @@
use core::ops::Deref; use core::ops::Deref;
use bevy_utils::prelude::DebugName;
use crate::{ use crate::{
archetype::Archetype, archetype::Archetype,
change_detection::{MaybeLocation, MutUntyped}, 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`? Did you forget to add it using `app.insert_resource` / `app.init_resource`?
Resources are also implicitly added via `app.add_event`, Resources are also implicitly added via `app.add_event`,
and can be added by plugins.", 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`. "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`? 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.", 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 { let Some(mut events_resource) = self.get_resource_mut::<Events<E>>() else {
log::error!( 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 ", "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; return None;
}; };

View File

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

View File

@ -1,6 +1,7 @@
//! Contains error types returned by bevy's schedule. //! Contains error types returned by bevy's schedule.
use alloc::vec::Vec; use alloc::vec::Vec;
use bevy_utils::prelude::DebugName;
use crate::{ use crate::{
component::ComponentId, 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:?}")] #[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 { pub struct TryInsertBatchError {
/// The bundles' type name. /// The bundles' type name.
pub bundle_type: &'static str, pub bundle_type: DebugName,
/// The IDs of the provided entities that do not exist. /// The IDs of the provided entities that do not exist.
pub entities: Vec<Entity>, pub entities: Vec<Entity>,
} }

View File

@ -25,6 +25,7 @@ use crate::{
prelude::{Add, Despawn, Insert, Remove, Replace}, prelude::{Add, Despawn, Insert, Remove, Replace},
}; };
pub use bevy_ecs_macros::FromWorld; pub use bevy_ecs_macros::FromWorld;
use bevy_utils::prelude::DebugName;
pub use deferred_world::DeferredWorld; pub use deferred_world::DeferredWorld;
pub use entity_fetch::{EntityFetcher, WorldEntityFetch}; pub use entity_fetch::{EntityFetcher, WorldEntityFetch};
pub use entity_ref::{ pub use entity_ref::{
@ -264,6 +265,12 @@ impl World {
&self.removed_components &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 /// Creates a new [`Commands`] instance that writes to the world's command queue
/// Use [`World::flush`] to apply all queued commands /// Use [`World::flush`] to apply all queued commands
#[inline] #[inline]
@ -2087,7 +2094,7 @@ impl World {
Did you forget to add it using `app.insert_resource` / `app.init_resource`? Did you forget to add it using `app.insert_resource` / `app.init_resource`?
Resources are also implicitly added via `app.add_event`, Resources are also implicitly added via `app.add_event`,
and can be added by plugins.", 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`? Did you forget to add it using `app.insert_resource` / `app.init_resource`?
Resources are also implicitly added via `app.add_event`, Resources are also implicitly added via `app.add_event`,
and can be added by plugins.", 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`? Did you forget to add it using `app.insert_resource` / `app.init_resource`?
Resources are also implicitly added via `app.add_event`, Resources are also implicitly added via `app.add_event`,
and can be added by plugins.", 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`. "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`? 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.", 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`. "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`? 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.", 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(()) Ok(())
} else { } else {
Err(TryInsertBatchError { Err(TryInsertBatchError {
bundle_type: core::any::type_name::<B>(), bundle_type: DebugName::type_name::<B>(),
entities: invalid_entities, entities: invalid_entities,
}) })
} }
@ -2694,7 +2701,7 @@ impl World {
#[track_caller] #[track_caller]
pub fn resource_scope<R: Resource, U>(&mut self, f: impl FnOnce(&mut World, Mut<R>) -> U) -> U { pub fn resource_scope<R: Resource, U>(&mut self, f: impl FnOnce(&mut World, Mut<R>) -> U) -> U {
self.try_resource_scope(f) 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, /// 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>(), assert!(!self.contains_resource::<R>(),
"Resource `{}` was inserted during a call to World::resource_scope.\n\ "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.", 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| { OwningPtr::make(value, |ptr| {
// SAFETY: pointer is of type R // SAFETY: pointer is of type R
@ -2775,7 +2782,7 @@ impl World {
let Some(mut events_resource) = self.get_resource_mut::<Events<E>>() else { let Some(mut events_resource) = self.get_resource_mut::<Events<E>>() else {
log::error!( 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 ", "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; return None;
}; };
@ -3752,6 +3759,7 @@ mod tests {
}; };
use bevy_ecs_macros::Component; use bevy_ecs_macros::Component;
use bevy_platform::collections::{HashMap, HashSet}; use bevy_platform::collections::{HashMap, HashSet};
use bevy_utils::prelude::DebugName;
use core::{ use core::{
any::TypeId, any::TypeId,
panic, panic,
@ -3935,12 +3943,12 @@ mod tests {
let mut iter = world.iter_resources(); let mut iter = world.iter_resources();
let (info, ptr) = iter.next().unwrap(); 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` // SAFETY: We know that the resource is of type `TestResource`
assert_eq!(unsafe { ptr.deref::<TestResource>().0 }, 42); assert_eq!(unsafe { ptr.deref::<TestResource>().0 }, 42);
let (info, ptr) = iter.next().unwrap(); 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!( assert_eq!(
// SAFETY: We know that the resource is of type `TestResource2` // SAFETY: We know that the resource is of type `TestResource2`
unsafe { &ptr.deref::<TestResource2>().0 }, unsafe { &ptr.deref::<TestResource2>().0 },
@ -3963,14 +3971,14 @@ mod tests {
let mut iter = world.iter_resources_mut(); let mut iter = world.iter_resources_mut();
let (info, mut mut_untyped) = iter.next().unwrap(); 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` // SAFETY: We know that the resource is of type `TestResource`
unsafe { unsafe {
mut_untyped.as_mut().deref_mut::<TestResource>().0 = 43; mut_untyped.as_mut().deref_mut::<TestResource>().0 = 43;
}; };
let (info, mut mut_untyped) = iter.next().unwrap(); 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` // SAFETY: We know that the resource is of type `TestResource2`
unsafe { unsafe {
mut_untyped.as_mut().deref_mut::<TestResource2>().0 = "Hello, world?".to_string(); 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 thiserror::Error;
use alloc::string::{String, ToString};
use bevy_reflect::{Reflect, ReflectFromPtr}; use bevy_reflect::{Reflect, ReflectFromPtr};
use bevy_utils::prelude::DebugName;
use crate::{prelude::*, world::ComponentId}; use crate::{prelude::*, world::ComponentId};
@ -77,10 +77,7 @@ impl World {
}; };
let Some(comp_ptr) = self.get_by_id(entity, component_id) else { let Some(comp_ptr) = self.get_by_id(entity, component_id) else {
let component_name = self let component_name = self.components().get_name(component_id);
.components()
.get_name(component_id)
.map(|name| name.to_string());
return Err(GetComponentReflectError::EntityDoesNotHaveComponent { return Err(GetComponentReflectError::EntityDoesNotHaveComponent {
entity, entity,
@ -166,10 +163,7 @@ impl World {
// HACK: Only required for the `None`-case/`else`-branch, but it borrows `self`, which will // 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. // already be mutably borrowed by `self.get_mut_by_id()`, and I didn't find a way around it.
let component_name = self let component_name = self.components().get_name(component_id).clone();
.components()
.get_name(component_id)
.map(|name| name.to_string());
let Some(comp_mut_untyped) = self.get_mut_by_id(entity, component_id) else { let Some(comp_mut_untyped) = self.get_mut_by_id(entity, component_id) else {
return Err(GetComponentReflectError::EntityDoesNotHaveComponent { return Err(GetComponentReflectError::EntityDoesNotHaveComponent {
@ -223,7 +217,7 @@ pub enum GetComponentReflectError {
component_id: ComponentId, component_id: ComponentId,
/// The name corresponding to the [`Component`] with the given [`TypeId`], or `None` /// The name corresponding to the [`Component`] with the given [`TypeId`], or `None`
/// if not available. /// if not available.
component_name: Option<String>, component_name: Option<DebugName>,
}, },
/// The [`World`] was missing the [`AppTypeRegistry`] resource. /// The [`World`] was missing the [`AppTypeRegistry`] resource.

View File

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

View File

@ -631,8 +631,8 @@ impl<const I: usize, P: PhaseItem> RenderCommand<P> for SetLineGizmoBindGroup<I>
#[inline] #[inline]
fn render<'w>( fn render<'w>(
_item: &P, _item: &P,
_view: ROQueryItem<'w, Self::ViewQuery>, _view: ROQueryItem<'w, '_, Self::ViewQuery>,
uniform_index: Option<ROQueryItem<'w, Self::ItemQuery>>, uniform_index: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
bind_group: SystemParamItem<'w, '_, Self::Param>, bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
@ -662,8 +662,8 @@ impl<P: PhaseItem, const STRIP: bool> RenderCommand<P> for DrawLineGizmo<STRIP>
#[inline] #[inline]
fn render<'w>( fn render<'w>(
_item: &P, _item: &P,
_view: ROQueryItem<'w, Self::ViewQuery>, _view: ROQueryItem<'w, '_, Self::ViewQuery>,
config: Option<ROQueryItem<'w, Self::ItemQuery>>, config: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
line_gizmos: SystemParamItem<'w, '_, Self::Param>, line_gizmos: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
@ -725,8 +725,8 @@ impl<P: PhaseItem> RenderCommand<P> for DrawLineJointGizmo {
#[inline] #[inline]
fn render<'w>( fn render<'w>(
_item: &P, _item: &P,
_view: ROQueryItem<'w, Self::ViewQuery>, _view: ROQueryItem<'w, '_, Self::ViewQuery>,
config: Option<ROQueryItem<'w, Self::ItemQuery>>, config: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
line_gizmos: SystemParamItem<'w, '_, Self::Param>, line_gizmos: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> 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. //! You can use [`GltfAssetLabel`] to ensure you are using the correct label.
mod assets; mod assets;
mod convert_coordinates;
mod label; mod label;
mod loader; mod loader;
mod vertex_attributes; mod vertex_attributes;

View File

@ -10,7 +10,10 @@ use itertools::Itertools;
#[cfg(feature = "bevy_animation")] #[cfg(feature = "bevy_animation")]
use bevy_platform::collections::{HashMap, HashSet}; 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 { pub(crate) fn node_name(node: &Node) -> Name {
let name = node 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 /// 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 /// if `libm` feature of `bevy_math` crate is enabled also handles cross
/// platform determinism properly. /// platform determinism properly.
pub(crate) fn node_transform(node: &Node) -> Transform { pub(crate) fn node_transform(node: &Node, convert_coordinates: bool) -> Transform {
match node.transform() { let transform = match node.transform() {
gltf::scene::Transform::Matrix { matrix } => { gltf::scene::Transform::Matrix { matrix } => {
Transform::from_matrix(Mat4::from_cols_array_2d(&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), rotation: bevy_math::Quat::from_array(rotation),
scale: Vec3::from(scale), 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}, texture::{texture_handle, texture_sampler, texture_transform_to_affine2},
}, },
}; };
use crate::convert_coordinates::ConvertCoordinates as _;
/// An error that occurs when loading a glTF file. /// An error that occurs when loading a glTF file.
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -191,6 +192,16 @@ pub struct GltfLoaderSettings {
pub default_sampler: Option<ImageSamplerDescriptor>, pub default_sampler: Option<ImageSamplerDescriptor>,
/// If true, the loader will ignore sampler data from gltf and use the default sampler. /// If true, the loader will ignore sampler data from gltf and use the default sampler.
pub override_sampler: bool, 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 { impl Default for GltfLoaderSettings {
@ -203,6 +214,7 @@ impl Default for GltfLoaderSettings {
include_source: false, include_source: false,
default_sampler: None, default_sampler: None,
override_sampler: false, override_sampler: false,
convert_coordinates: false,
} }
} }
} }
@ -303,7 +315,16 @@ async fn load_gltf<'a, 'b, 'c>(
match outputs { match outputs {
ReadOutputs::Translations(tr) => { ReadOutputs::Translations(tr) => {
let translation_property = animated_field!(Transform::translation); 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 { if keyframe_timestamps.len() == 1 {
Some(VariableCurve::new(AnimatableCurve::new( Some(VariableCurve::new(AnimatableCurve::new(
translation_property, translation_property,
@ -350,8 +371,17 @@ async fn load_gltf<'a, 'b, 'c>(
} }
ReadOutputs::Rotations(rots) => { ReadOutputs::Rotations(rots) => {
let rotation_property = animated_field!(Transform::rotation); let rotation_property = animated_field!(Transform::rotation);
let rotations: Vec<Quat> = let rotations: Vec<Quat> = rots
rots.into_f32().map(Quat::from_array).collect(); .into_f32()
.map(Quat::from_array)
.map(|quat| {
if settings.convert_coordinates {
Quat::convert_coordinates(quat)
} else {
quat
}
})
.collect();
if keyframe_timestamps.len() == 1 { if keyframe_timestamps.len() == 1 {
Some(VariableCurve::new(AnimatableCurve::new( Some(VariableCurve::new(AnimatableCurve::new(
rotation_property, rotation_property,
@ -633,6 +663,7 @@ async fn load_gltf<'a, 'b, 'c>(
accessor, accessor,
&buffer_data, &buffer_data,
&loader.custom_vertex_attributes, &loader.custom_vertex_attributes,
settings.convert_coordinates,
) { ) {
Ok((attribute, values)) => mesh.insert_attribute(attribute, values), Ok((attribute, values)) => mesh.insert_attribute(attribute, values),
Err(err) => warn!("{}", err), 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 reader = gltf_skin.reader(|buffer| Some(&buffer_data[buffer.index()]));
let local_to_bone_bind_matrices: Vec<Mat4> = reader let local_to_bone_bind_matrices: Vec<Mat4> = reader
.read_inverse_bind_matrices() .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(|| { .unwrap_or_else(|| {
core::iter::repeat_n(Mat4::IDENTITY, gltf_skin.joints().len()).collect() core::iter::repeat_n(Mat4::IDENTITY, gltf_skin.joints().len()).collect()
}); });
@ -834,7 +875,7 @@ async fn load_gltf<'a, 'b, 'c>(
&node, &node,
children, children,
mesh, mesh,
node_transform(&node), node_transform(&node, settings.convert_coordinates),
skin, skin,
node.extras().as_deref().map(GltfExtras::from), node.extras().as_deref().map(GltfExtras::from),
); );
@ -1306,7 +1347,7 @@ fn load_node(
document: &Document, document: &Document,
) -> Result<(), GltfError> { ) -> Result<(), GltfError> {
let mut gltf_error = None; 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; let world_transform = *parent_transform * transform;
// according to https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#instantiation, // 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 // 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() ..OrthographicProjection::default_3d()
}; };
Projection::Orthographic(orthographic_projection) Projection::Orthographic(orthographic_projection)
} }
gltf::camera::Projection::Perspective(perspective) => { gltf::camera::Projection::Perspective(perspective) => {
@ -1377,6 +1417,7 @@ fn load_node(
Projection::Perspective(perspective_projection) Projection::Perspective(perspective_projection)
} }
}; };
node.insert(( node.insert((
Camera3d::default(), Camera3d::default(),
projection, projection,

View File

@ -6,6 +6,8 @@ use gltf::{
}; };
use thiserror::Error; use thiserror::Error;
use crate::convert_coordinates::ConvertCoordinates;
/// Represents whether integer data requires normalization /// Represents whether integer data requires normalization
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct Normalization(bool); struct Normalization(bool);
@ -132,15 +134,23 @@ impl<'a> VertexAttributeIter<'a> {
} }
/// Materializes values for any supported format of vertex attribute /// 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 { match self {
VertexAttributeIter::F32(it) => Ok(Values::Float32(it.collect())), VertexAttributeIter::F32(it) => Ok(Values::Float32(it.collect())),
VertexAttributeIter::U32(it) => Ok(Values::Uint32(it.collect())), VertexAttributeIter::U32(it) => Ok(Values::Uint32(it.collect())),
VertexAttributeIter::F32x2(it) => Ok(Values::Float32x2(it.collect())), VertexAttributeIter::F32x2(it) => Ok(Values::Float32x2(it.collect())),
VertexAttributeIter::U32x2(it) => Ok(Values::Uint32x2(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::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::U32x4(it) => Ok(Values::Uint32x4(it.collect())),
VertexAttributeIter::S16x2(it, n) => { VertexAttributeIter::S16x2(it, n) => {
Ok(n.apply_either(it.collect(), Values::Snorm16x2, Values::Sint16x2)) 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( VertexAttributeIter::U16x4(it, Normalization(true)) => Ok(Values::Float32x4(
ReadColors::RgbaU16(it).into_rgba_f32().collect(), 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)) => { VertexAttributeIter::U8x4(it, Normalization(false)) => {
Ok(Values::Uint16x4(ReadJoints::U8(it).into_u16().collect())) 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)) => { VertexAttributeIter::U16x4(it, Normalization(true)) => {
Ok(Values::Float32x4(ReadWeights::U16(it).into_f32().collect())) 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( VertexAttributeIter::U16x2(it, Normalization(true)) => Ok(Values::Float32x2(
ReadTexCoords::U16(it).into_f32().collect(), 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, accessor: gltf::Accessor,
buffer_data: &Vec<Vec<u8>>, buffer_data: &Vec<Vec<u8>>,
custom_vertex_attributes: &HashMap<Box<str>, MeshVertexAttribute>, custom_vertex_attributes: &HashMap<Box<str>, MeshVertexAttribute>,
convert_coordinates: bool,
) -> Result<(MeshVertexAttribute, Values), ConvertAttributeError> { ) -> Result<(MeshVertexAttribute, Values), ConvertAttributeError> {
if let Some((attribute, conversion)) = match &semantic { if let Some((attribute, conversion, convert_coordinates)) = match &semantic {
gltf::Semantic::Positions => Some((Mesh::ATTRIBUTE_POSITION, ConversionMode::Any)), gltf::Semantic::Positions => Some((
gltf::Semantic::Normals => Some((Mesh::ATTRIBUTE_NORMAL, ConversionMode::Any)), Mesh::ATTRIBUTE_POSITION,
gltf::Semantic::Tangents => Some((Mesh::ATTRIBUTE_TANGENT, ConversionMode::Any)), ConversionMode::Any,
gltf::Semantic::Colors(0) => Some((Mesh::ATTRIBUTE_COLOR, ConversionMode::Rgba)), convert_coordinates,
gltf::Semantic::TexCoords(0) => Some((Mesh::ATTRIBUTE_UV_0, ConversionMode::TexCoord)), )),
gltf::Semantic::TexCoords(1) => Some((Mesh::ATTRIBUTE_UV_1, ConversionMode::TexCoord)), gltf::Semantic::Normals => Some((
gltf::Semantic::Joints(0) => { Mesh::ATTRIBUTE_NORMAL,
Some((Mesh::ATTRIBUTE_JOINT_INDEX, ConversionMode::JointIndex)) 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) => { gltf::Semantic::TexCoords(1) => {
Some((Mesh::ATTRIBUTE_JOINT_WEIGHT, ConversionMode::JointWeight)) 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 gltf::Semantic::Extras(name) => custom_vertex_attributes
.get(name.as_str()) .get(name.as_str())
.map(|attr| (*attr, ConversionMode::Any)), .map(|attr| (*attr, ConversionMode::Any, false)),
_ => None, _ => None,
} { } {
let raw_iter = VertexAttributeIter::from_accessor(accessor.clone(), buffer_data); let raw_iter = VertexAttributeIter::from_accessor(accessor.clone(), buffer_data);
let converted_values = raw_iter.and_then(|iter| match conversion { 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::Rgba => iter.into_rgba_values(),
ConversionMode::TexCoord => iter.into_tex_coord_values(), ConversionMode::TexCoord => iter.into_tex_coord_values(),
ConversionMode::JointIndex => iter.into_joint_index_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 /// [`DetectChangesMut::bypass_change_detection`]: bevy_ecs::change_detection::DetectChangesMut::bypass_change_detection
#[derive(Debug, Clone, Resource)] #[derive(Debug, Clone, Resource)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Default, 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. /// A collection of every button that is currently being pressed.
pressed: HashSet<T>, pressed: HashSet<T>,
/// A collection of every button that has just been pressed. /// 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>, 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 { fn default() -> Self {
Self { Self {
pressed: Default::default(), pressed: Default::default(),
@ -143,12 +143,12 @@ impl<T: Copy + Eq + Hash + Send + Sync + 'static> Default for ButtonInput<T> {
impl<T> ButtonInput<T> impl<T> ButtonInput<T>
where where
T: Copy + Eq + Hash + Send + Sync + 'static, T: Clone + Eq + Hash + Send + Sync + 'static,
{ {
/// Registers a press for the given `input`. /// Registers a press for the given `input`.
pub fn press(&mut self, input: T) { pub fn press(&mut self, input: T) {
// Returns `true` if the `input` wasn't pressed. // Returns `true` if the `input` wasn't pressed.
if self.pressed.insert(input) { if self.pressed.insert(input.clone()) {
self.just_pressed.insert(input); self.just_pressed.insert(input);
} }
} }

View File

@ -92,8 +92,9 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
/// ///
/// ## Usage /// ## Usage
/// ///
/// The event is consumed inside of the [`keyboard_input_system`] /// The event is consumed inside of the [`keyboard_input_system`] to update the
/// to update the [`ButtonInput<KeyCode>`](ButtonInput<KeyCode>) resource. /// [`ButtonInput<KeyCode>`](ButtonInput<KeyCode>) and
/// [`ButtonInput<Key>`](ButtonInput<Key>) resources.
#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq, Hash)] #[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr( #[cfg_attr(
feature = "bevy_reflect", feature = "bevy_reflect",
@ -107,8 +108,12 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
)] )]
pub struct KeyboardInput { pub struct KeyboardInput {
/// The physical key code of the key. /// The physical key code of the key.
///
/// This corresponds to the location of the key independent of the keyboard layout.
pub key_code: KeyCode, 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, pub logical_key: Key,
/// The press state of the key. /// The press state of the key.
pub state: ButtonState, pub state: ButtonState,
@ -148,32 +153,46 @@ pub struct KeyboardInput {
)] )]
pub struct KeyboardFocusLost; 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 /// ## 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. /// 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( 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 keyboard_input_events: EventReader<KeyboardInput>,
mut focus_events: EventReader<KeyboardFocusLost>, 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(); key_input.bypass_change_detection().clear();
for event in keyboard_input_events.read() { for event in keyboard_input_events.read() {
let KeyboardInput { let KeyboardInput {
key_code, state, .. key_code,
logical_key,
state,
..
} = event; } = event;
match state { match state {
ButtonState::Pressed => key_input.press(*key_code), ButtonState::Pressed => {
ButtonState::Released => key_input.release(*key_code), 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 // Release all cached input to avoid having stuck input when switching between windows in os
if !focus_events.is_empty() { if !focus_events.is_empty() {
key_input.release_all(); keycode_input.release_all();
focus_events.clear(); 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>>`. /// 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 /// 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: /// exceptions:
/// - The keys that the specification calls `MetaLeft` and `MetaRight` are named `SuperLeft` and /// - The keys that the specification calls `MetaLeft` and `MetaRight` are named `SuperLeft` and
/// `SuperRight` here. /// `SuperRight` here.
/// - The key that the specification calls "Super" is reported as `Unidentified` 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 /// ## Updating
/// ///
@ -756,6 +775,19 @@ pub enum NativeKey {
/// The logical key code of a [`KeyboardInput`]. /// 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 /// ## Technical
/// ///
/// Its values map 1 to 1 to winit's Key. /// Its values map 1 to 1 to winit's Key.

View File

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

View File

@ -155,7 +155,7 @@ pub struct WindowTraversal {
} }
impl<E: BufferedEvent + Clone> Traversal<FocusedInput<E>> for 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; let WindowTraversalItem { child_of, window } = item;
// Send event to parent, if it has one. // Send event to parent, if it has one.

View File

@ -346,6 +346,8 @@ web = [
hotpatching = ["bevy_app/hotpatching", "bevy_ecs/hotpatching"] hotpatching = ["bevy_app/hotpatching", "bevy_ecs/hotpatching"]
debug = ["bevy_utils/debug"]
[dependencies] [dependencies]
# bevy (no_std) # bevy (no_std)
bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ 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; 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()) Some(item.clone())
} }
} }
@ -405,7 +405,7 @@ impl ExtractComponent for AtmosphereSettings {
type Out = 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()) Some(item.clone())
} }
} }

View File

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

View File

@ -560,7 +560,7 @@ pub(super) fn prepare_atmosphere_transforms(
}; };
for (entity, view) in &views { 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_z = world_from_view.z_axis.truncate();
let camera_y = world_from_view.y_axis.truncate(); let camera_y = world_from_view.y_axis.truncate();
let atmo_z = camera_z 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 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 = camera_transform.compute_transform().scale.recip();
let view_from_world_scale_max = view_from_world_scale.abs().max_element(); let view_from_world_scale_max = view_from_world_scale.abs().max_element();
let view_from_world = world_from_view.inverse(); let view_from_world = world_from_view.inverse();

View File

@ -341,7 +341,7 @@ pub fn build_directional_light_cascades(
.iter() .iter()
.filter_map(|(entity, transform, projection, camera)| { .filter_map(|(entity, transform, projection, camera)| {
if camera.is_active { if camera.is_active {
Some((entity, projection, transform.compute_matrix())) Some((entity, projection, transform.to_matrix()))
} else { } else {
None None
} }
@ -357,7 +357,7 @@ pub fn build_directional_light_cascades(
// light_to_world has orthogonal upper-left 3x3 and zero translation. // 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 // 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 // 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. // Instead, we directly create a good matrix from just the rotation.
let world_from_light = Mat4::from_quat(transform.compute_transform().rotation); let world_from_light = Mat4::from_quat(transform.compute_transform().rotation);
let light_to_world_inverse = world_from_light.inverse(); 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()) { for (view_rotation, frustum) in view_rotations.iter().zip(cubemap_frusta.iter_mut()) {
let world_from_view = view_translation * *view_rotation; 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( *frustum = Frustum::from_clip_from_world_custom_far(
&clip_from_world, &clip_from_world,

View File

@ -192,7 +192,7 @@ impl ExtractInstance for EnvironmentMapIds {
type QueryFilter = (); type QueryFilter = ();
fn extract(item: QueryItem<'_, Self::QueryData>) -> Option<Self> { fn extract(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self> {
Some(EnvironmentMapIds { Some(EnvironmentMapIds {
diffuse: item.diffuse_map.id(), diffuse: item.diffuse_map.id(),
specular: item.specular_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 { let environment_map_uniform = if let Some(environment_map_light) = environment_map_light {
EnvironmentMapUniform { EnvironmentMapUniform {
transform: Transform::from_rotation(environment_map_light.rotation) transform: Transform::from_rotation(environment_map_light.rotation)
.compute_matrix() .to_matrix()
.inverse(), .inverse(),
} }
} else { } else {
@ -595,7 +595,7 @@ where
) -> Option<LightProbeInfo<C>> { ) -> Option<LightProbeInfo<C>> {
environment_map.id(image_assets).map(|id| LightProbeInfo { environment_map.id(image_assets).map(|id| LightProbeInfo {
world_from_light: light_probe_transform.affine(), 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, asset_id: id,
intensity: environment_map.intensity(), intensity: environment_map.intensity(),
affects_lightmapped_mesh_diffuse: environment_map.affects_lightmapped_mesh_diffuse(), 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>)>>, query: Query<(Entity, &Camera, &GlobalTransform), Or<(With<Camera3d>, With<ShadowView>)>>,
) { ) {
for (entity, camera, camera_transform) in &query { 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_world = world_from_view.inverse();
let view_from_clip = camera.clip_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 { let prev_view_data = match maybe_previous_view_uniforms {
Some(previous_view) => previous_view.clone(), Some(previous_view) => previous_view.clone(),
None => { 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_world = world_from_view.inverse();
let view_from_clip = camera.clip_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, not_shadow_caster,
no_automatic_batching, no_automatic_batching,
visibility_range, visibility_range,
): <GpuMeshExtractionQuery as QueryData>::Item<'_>, ): <GpuMeshExtractionQuery as QueryData>::Item<'_, '_>,
render_visibility_ranges: &RenderVisibilityRanges, render_visibility_ranges: &RenderVisibilityRanges,
render_mesh_instances: &RenderMeshInstancesGpu, render_mesh_instances: &RenderMeshInstancesGpu,
queue: &mut RenderMeshInstanceGpuQueue, queue: &mut RenderMeshInstanceGpuQueue,
@ -2874,7 +2874,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I>
view_environment_map, view_environment_map,
mesh_view_bind_group, mesh_view_bind_group,
maybe_oit_layers_count_offset, maybe_oit_layers_count_offset,
): ROQueryItem<'w, Self::ViewQuery>, ): ROQueryItem<'w, '_, Self::ViewQuery>,
_entity: Option<()>, _entity: Option<()>,
_: SystemParamItem<'w, '_, Self::Param>, _: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,

View File

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

View File

@ -347,7 +347,7 @@ impl ViewNode for VolumetricFogNode {
view_ssr_offset, view_ssr_offset,
msaa, msaa,
view_environment_map_offset, view_environment_map_offset,
): QueryItem<'w, Self::ViewQuery>, ): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World, world: &'w World,
) -> Result<(), NodeRunError> { ) -> Result<(), NodeRunError> {
let pipeline_cache = world.resource::<PipelineCache>(); 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. // Do this up front to avoid O(n^2) matrix inversion.
local_from_world_matrices.clear(); local_from_world_matrices.clear();
for (_, _, fog_transform) in fog_volumes.iter() { 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(); 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() { 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![]; let mut view_fog_volumes = vec![];

View File

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

View File

@ -86,7 +86,7 @@ impl<E> Traversal<Pointer<E>> for PointerTraversal
where where
E: Debug + Clone + Reflect, 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; let PointerTraversalItem { child_of, window } = item;
// Send event to parent, if it has one. // Send event to parent, if it has one.

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