ECS: put strings only used for debug behind a feature (#19558)

# Objective

- Many strings in bevy_ecs are created but only used for debug: system
name, component name, ...
- Those strings make a significant part of the final binary and are no
use in a released game

## Solution

- Use [`strings`](https://linux.die.net/man/1/strings) to find ...
strings in a binary
- Try to find where they come from
- Many are made from `type_name::<T>()` and only used in error / debug
messages
- Add a new structure `DebugName` that holds no value if `debug` feature
is disabled
- Replace `core::any::type_name::<T>()` by `DebugName::type_name::<T>()`

## Testing

Measurements were taken without the new feature being enabled by
default, to help with commands

### File Size

I tried building the `breakout` example with `cargo run --release
--example breakout`

|`debug` enabled|`debug` disabled|
|-|-|
|81621776 B|77735728B|
|77.84MB|74.13MB|

### Compilation time

`hyperfine --min-runs 15 --prepare "cargo clean && sleep 5"
'RUSTC_WRAPPER="" cargo build --release --example breakout'
'RUSTC_WRAPPER="" cargo build --release --example breakout --features
debug'`

```
breakout' 'RUSTC_WRAPPER="" cargo build --release --example breakout --features debug'
Benchmark 1: RUSTC_WRAPPER="" cargo build --release --example breakout
  Time (mean ± σ):     84.856 s ±  3.565 s    [User: 1093.817 s, System: 32.547 s]
  Range (min … max):   78.038 s … 89.214 s    15 runs

Benchmark 2: RUSTC_WRAPPER="" cargo build --release --example breakout --features debug
  Time (mean ± σ):     92.303 s ±  2.466 s    [User: 1193.443 s, System: 33.803 s]
  Range (min … max):   90.619 s … 99.684 s    15 runs

Summary
  RUSTC_WRAPPER="" cargo build --release --example breakout ran
    1.09 ± 0.05 times faster than RUSTC_WRAPPER="" cargo build --release --example breakout --features debug
```
This commit is contained in:
François Mockers 2025-06-18 22:15:25 +02:00 committed by GitHub
parent 2119838e27
commit 4e694aea53
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
52 changed files with 363 additions and 231 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

@ -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

@ -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

@ -1,6 +1,7 @@
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;
@ -171,7 +172,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

@ -1,4 +1,5 @@
use alloc::{borrow::Cow, 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::{
@ -301,7 +302,7 @@ impl Observer {
} }
/// Returns the name of the [`Observer`]'s system . /// Returns the name of the [`Observer`]'s system .
pub fn system_name(&self) -> Cow<'static, str> { pub fn system_name(&self) -> DebugName {
self.system.system_name() self.system.system_name()
} }
} }
@ -420,11 +421,11 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
} }
trait AnyNamedSystem: Any + Send + Sync + 'static { trait AnyNamedSystem: Any + Send + Sync + 'static {
fn system_name(&self) -> Cow<'static, str>; fn system_name(&self) -> DebugName;
} }
impl<T: Any + System> AnyNamedSystem for T { impl<T: Any + System> AnyNamedSystem for T {
fn system_name(&self) -> Cow<'static, str> { fn system_name(&self) -> DebugName {
self.name() self.name()
} }
} }

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,4 @@
use bevy_utils::prelude::DebugName;
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
@ -54,10 +55,10 @@ impl core::fmt::Display for 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)]

View File

@ -12,6 +12,7 @@ use crate::{
}, },
}; };
use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref};
use bevy_utils::prelude::DebugName;
use core::{cell::UnsafeCell, marker::PhantomData, panic::Location}; use core::{cell::UnsafeCell, marker::PhantomData, panic::Location};
use smallvec::SmallVec; use smallvec::SmallVec;
use variadics_please::all_tuples; use variadics_please::all_tuples;
@ -1232,7 +1233,7 @@ where
assert!( assert!(
access.is_compatible(&my_access), access.is_compatible(&my_access),
"`EntityRefExcept<{}>` conflicts with a previous access in this query.", "`EntityRefExcept<{}>` conflicts with a previous access in this query.",
core::any::type_name::<B>(), DebugName::type_name::<B>(),
); );
access.extend(&my_access); access.extend(&my_access);
} }
@ -1343,7 +1344,7 @@ where
assert!( assert!(
access.is_compatible(&my_access), access.is_compatible(&my_access),
"`EntityMutExcept<{}>` conflicts with a previous access in this query.", "`EntityMutExcept<{}>` conflicts with a previous access in this query.",
core::any::type_name::<B>() DebugName::type_name::<B>()
); );
access.extend(&my_access); access.extend(&my_access);
} }
@ -1589,7 +1590,7 @@ unsafe impl<T: Component> WorldQuery for &T {
assert!( assert!(
!access.access().has_component_write(component_id), !access.access().has_component_write(component_id),
"&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
core::any::type_name::<T>(), DebugName::type_name::<T>(),
); );
access.add_component_read(component_id); access.add_component_read(component_id);
} }
@ -1775,7 +1776,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
assert!( assert!(
!access.access().has_component_write(component_id), !access.access().has_component_write(component_id),
"&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
core::any::type_name::<T>(), DebugName::type_name::<T>(),
); );
access.add_component_read(component_id); access.add_component_read(component_id);
} }
@ -1984,7 +1985,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
assert!( assert!(
!access.access().has_component_read(component_id), !access.access().has_component_read(component_id),
"&mut {} conflicts with a previous access in this query. Mutable component access must be unique.", "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.",
core::any::type_name::<T>(), DebugName::type_name::<T>(),
); );
access.add_component_write(component_id); access.add_component_write(component_id);
} }
@ -2134,7 +2135,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Mut<'__w, T> {
assert!( assert!(
!access.access().has_component_read(component_id), !access.access().has_component_read(component_id),
"Mut<{}> conflicts with a previous access in this query. Mutable component access mut be unique.", "Mut<{}> conflicts with a previous access in this query. Mutable component access mut be unique.",
core::any::type_name::<T>(), DebugName::type_name::<T>(),
); );
access.add_component_write(component_id); access.add_component_write(component_id);
} }
@ -2401,7 +2402,7 @@ pub struct Has<T>(PhantomData<T>);
impl<T> core::fmt::Debug for Has<T> { impl<T> core::fmt::Debug for Has<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(f, "Has<{}>", core::any::type_name::<T>()) write!(f, "Has<{}>", DebugName::type_name::<T>())
} }
} }

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;
@ -792,7 +793,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);
} }
@ -1020,7 +1021,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);
} }

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 {

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

@ -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

@ -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,
@ -1949,8 +1951,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
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,
>())), >())),
} }

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()
} }
@ -139,7 +140,7 @@ where
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()
} }
@ -232,7 +233,7 @@ where
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,7 +384,7 @@ 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
@ -767,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);
} }
@ -807,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(),
@ -843,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);
} }
@ -885,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>(),
@ -1435,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);
@ -1475,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>()
) )
}); });
@ -1511,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);
} }
@ -1554,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(),
@ -2786,7 +2787,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.
@ -2818,7 +2819,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(),
} }
} }
@ -2829,7 +2830,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

@ -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},
@ -451,7 +453,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>()
), ),
} }
} }
@ -480,7 +482,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>()
), ),
} }
} }
@ -523,7 +525,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

@ -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

@ -24,6 +24,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::{
@ -1948,7 +1949,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>()
), ),
} }
} }
@ -1972,7 +1973,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>()
), ),
} }
} }
@ -1996,7 +1997,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>()
), ),
} }
} }
@ -2160,7 +2161,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>()
), ),
} }
} }
@ -2182,7 +2183,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>()
), ),
} }
} }
@ -2349,11 +2350,11 @@ impl World {
) )
}; };
} else { } else {
panic!("error[B0003]: Could not insert a bundle (of type `{}`) for entity {entity}, which {}. See: https://bevy.org/learn/errors/b0003", core::any::type_name::<B>(), self.entities.entity_does_not_exist_error_details(entity)); panic!("error[B0003]: Could not insert a bundle (of type `{}`) for entity {entity}, which {}. See: https://bevy.org/learn/errors/b0003", DebugName::type_name::<B>(), self.entities.entity_does_not_exist_error_details(entity));
} }
} }
} else { } else {
panic!("error[B0003]: Could not insert a bundle (of type `{}`) for entity {first_entity}, which {}. See: https://bevy.org/learn/errors/b0003", core::any::type_name::<B>(), self.entities.entity_does_not_exist_error_details(first_entity)); panic!("error[B0003]: Could not insert a bundle (of type `{}`) for entity {first_entity}, which {}. See: https://bevy.org/learn/errors/b0003", DebugName::type_name::<B>(), self.entities.entity_does_not_exist_error_details(first_entity));
} }
} }
} }
@ -2517,7 +2518,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,
}) })
} }
@ -2551,7 +2552,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,
@ -2591,7 +2592,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
@ -2632,7 +2633,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;
}; };
@ -3633,6 +3634,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,
@ -3816,12 +3818,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 },
@ -3844,14 +3846,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

@ -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

@ -21,7 +21,9 @@ bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", features = [
] } ] }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev" } bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev" }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev" } bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", features = [
"debug",
] }
bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false, features = [ bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false, features = [
"std", "std",
"serialize", "serialize",

View File

@ -1130,7 +1130,7 @@ pub fn process_remote_list_request(In(params): In<Option<Value>>, world: &World)
let Some(component_info) = world.components().get_info(component_id) else { let Some(component_info) = world.components().get_info(component_id) else {
continue; continue;
}; };
response.push(component_info.name().to_owned()); response.push(component_info.name().to_string());
} }
} }
// If `None`, list all registered components. // If `None`, list all registered components.
@ -1189,7 +1189,7 @@ pub fn process_remote_list_watching_request(
let Some(component_info) = world.components().get_info(component_id) else { let Some(component_info) = world.components().get_info(component_id) else {
continue; continue;
}; };
response.added.push(component_info.name().to_owned()); response.added.push(component_info.name().to_string());
} }
} }
@ -1202,7 +1202,7 @@ pub fn process_remote_list_watching_request(
let Some(component_info) = world.components().get_info(*component_id) else { let Some(component_info) = world.components().get_info(*component_id) else {
continue; continue;
}; };
response.removed.push(component_info.name().to_owned()); response.removed.push(component_info.name().to_string());
} }
} }
} }

View File

@ -93,7 +93,7 @@ impl Scene {
type_registry type_registry
.get(type_id) .get(type_id)
.ok_or_else(|| SceneSpawnError::UnregisteredType { .ok_or_else(|| SceneSpawnError::UnregisteredType {
std_type_name: component_info.name().to_string(), std_type_name: component_info.name(),
})?; })?;
let reflect_resource = registration.data::<ReflectResource>().ok_or_else(|| { let reflect_resource = registration.data::<ReflectResource>().ok_or_else(|| {
SceneSpawnError::UnregisteredResource { SceneSpawnError::UnregisteredResource {
@ -133,7 +133,7 @@ impl Scene {
let registration = type_registry let registration = type_registry
.get(component_info.type_id().unwrap()) .get(component_info.type_id().unwrap())
.ok_or_else(|| SceneSpawnError::UnregisteredType { .ok_or_else(|| SceneSpawnError::UnregisteredType {
std_type_name: component_info.name().to_string(), std_type_name: component_info.name(),
})?; })?;
let reflect_component = let reflect_component =
registration.data::<ReflectComponent>().ok_or_else(|| { registration.data::<ReflectComponent>().ok_or_else(|| {

View File

@ -10,6 +10,7 @@ use bevy_ecs::{
}; };
use bevy_platform::collections::{HashMap, HashSet}; use bevy_platform::collections::{HashMap, HashSet};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_utils::prelude::DebugName;
use thiserror::Error; use thiserror::Error;
use uuid::Uuid; use uuid::Uuid;
@ -105,7 +106,7 @@ pub enum SceneSpawnError {
)] )]
UnregisteredType { UnregisteredType {
/// The [type name](std::any::type_name) for the unregistered type. /// The [type name](std::any::type_name) for the unregistered type.
std_type_name: String, std_type_name: DebugName,
}, },
/// Scene contains an unregistered type which has a `TypePath`. /// Scene contains an unregistered type which has a `TypePath`.
#[error( #[error(

View File

@ -16,9 +16,14 @@ wgpu_wrapper = ["dep:send_wrapper"]
# Provides access to the `Parallel` type. # Provides access to the `Parallel` type.
parallel = ["bevy_platform/std", "dep:thread_local"] parallel = ["bevy_platform/std", "dep:thread_local"]
std = ["disqualified/alloc"]
debug = []
[dependencies] [dependencies]
bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false } bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false }
disqualified = { version = "1.0", default-features = false }
thread_local = { version = "1.0", optional = true } thread_local = { version = "1.0", optional = true }
[target.'cfg(all(target_arch = "wasm32", target_feature = "atomics"))'.dependencies] [target.'cfg(all(target_arch = "wasm32", target_feature = "atomics"))'.dependencies]

View File

@ -0,0 +1,102 @@
use alloc::{borrow::Cow, fmt, string::String};
#[cfg(feature = "debug")]
use core::any::type_name;
use disqualified::ShortName;
#[cfg(not(feature = "debug"))]
const FEATURE_DISABLED: &'static str = "Enable the debug feature to see the name";
/// Wrapper to help debugging ECS issues. This is used to display the names of systems, components, ...
///
/// * If the `debug` feature is enabled, the actual name will be used
/// * If it is disabled, a string mentioning the disabled feature will be used
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DebugName {
#[cfg(feature = "debug")]
name: Cow<'static, str>,
}
impl fmt::Display for DebugName {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
#[cfg(feature = "debug")]
f.write_str(self.name.as_ref())?;
#[cfg(not(feature = "debug"))]
f.write_str(FEATURE_DISABLED)?;
Ok(())
}
}
impl DebugName {
/// Create a new `DebugName` from a `&str`
///
/// The value will be ignored if the `debug` feature is not enabled
#[cfg_attr(not(feature = "debug"), expect(unused_variables))]
pub fn borrowed(value: &'static str) -> Self {
DebugName {
#[cfg(feature = "debug")]
name: Cow::Borrowed(value),
}
}
/// Create a new `DebugName` from a `String`
///
/// The value will be ignored if the `debug` feature is not enabled
#[cfg_attr(not(feature = "debug"), expect(unused_variables))]
pub fn owned(value: String) -> Self {
DebugName {
#[cfg(feature = "debug")]
name: Cow::Owned(value),
}
}
/// Create a new `DebugName` from a type by using its [`core::any::type_name`]
///
/// The value will be ignored if the `debug` feature is not enabled
pub fn type_name<T>() -> Self {
DebugName {
#[cfg(feature = "debug")]
name: Cow::Borrowed(type_name::<T>()),
}
}
/// Get the [`ShortName`] corresping to this debug name
///
/// The value will be a static string if the `debug` feature is not enabled
pub fn shortname(&self) -> ShortName {
#[cfg(feature = "debug")]
return ShortName(self.name.as_ref());
#[cfg(not(feature = "debug"))]
return ShortName(FEATURE_DISABLED);
}
/// Return the string hold by this `DebugName`
///
/// This is intended for debugging purpose, and only available if the `debug` feature is enabled
#[cfg(feature = "debug")]
pub fn as_string(&self) -> String {
self.name.clone().into_owned()
}
}
impl From<Cow<'static, str>> for DebugName {
#[cfg_attr(not(feature = "debug"), expect(unused_variables))]
fn from(value: Cow<'static, str>) -> Self {
Self {
#[cfg(feature = "debug")]
name: value,
}
}
}
impl From<String> for DebugName {
fn from(value: String) -> Self {
Self::owned(value)
}
}
impl From<&'static str> for DebugName {
fn from(value: &'static str) -> Self {
Self::borrowed(value)
}
}

View File

@ -43,12 +43,14 @@ cfg::parallel! {
/// ///
/// This includes the most common types in this crate, re-exported for your convenience. /// This includes the most common types in this crate, re-exported for your convenience.
pub mod prelude { pub mod prelude {
pub use crate::debug_info::DebugName;
pub use crate::default; pub use crate::default;
} }
#[cfg(feature = "wgpu_wrapper")] #[cfg(feature = "wgpu_wrapper")]
mod wgpu_wrapper; mod wgpu_wrapper;
mod debug_info;
mod default; mod default;
mod once; mod once;

View File

@ -41,6 +41,7 @@ The default feature set enables most of the expected features of a game engine,
|bevy_window|Windowing layer| |bevy_window|Windowing layer|
|bevy_winit|winit window and input backend| |bevy_winit|winit window and input backend|
|custom_cursor|Enable winit custom cursor support| |custom_cursor|Enable winit custom cursor support|
|debug|Enable collecting debug information about systems and components to help with diagnostics|
|default_font|Include a default font, containing only ASCII characters, at the cost of a 20kB binary size increase| |default_font|Include a default font, containing only ASCII characters, at the cost of a 20kB binary size increase|
|hdr|HDR image format support| |hdr|HDR image format support|
|ktx2|KTX2 compressed texture support| |ktx2|KTX2 compressed texture support|

View File

@ -104,7 +104,10 @@ fn build_ui(
mut state: ResMut<State>, mut state: ResMut<State>,
) { ) {
let mut text_spans = Vec::new(); let mut text_spans = Vec::new();
let mut always_run = Vec::new(); let mut always_run: Vec<(
bevy_ecs::intern::Interned<dyn ScheduleLabel + 'static>,
NodeId,
)> = Vec::new();
let Ok(schedule_order) = stepping.schedules() else { let Ok(schedule_order) = stepping.schedules() else {
return; return;
@ -131,7 +134,8 @@ fn build_ui(
for (node_id, system) in systems { for (node_id, system) in systems {
// skip bevy default systems; we don't want to step those // skip bevy default systems; we don't want to step those
if system.name().starts_with("bevy") { #[cfg(feature = "debug")]
if system.name().as_string().starts_with("bevy") {
always_run.push((*label, node_id)); always_run.push((*label, node_id));
continue; continue;
} }