Merge branch 'main' into res-acq

This commit is contained in:
MiniaczQ 2024-09-19 20:49:19 +02:00 committed by GitHub
commit b0ed278940
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 435 additions and 102 deletions

View file

@ -4,7 +4,7 @@ use crate::{
};
use bevy_ecs::prelude::*;
use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath};
use bevy_utils::get_short_name;
use bevy_utils::ShortName;
use crossbeam_channel::{Receiver, Sender};
use std::{
any::TypeId,
@ -206,7 +206,7 @@ impl<A: Asset> Default for Handle<A> {
impl<A: Asset> std::fmt::Debug for Handle<A> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = get_short_name(std::any::type_name::<A>());
let name = ShortName::of::<A>();
match self {
Handle::Strong(handle) => {
write!(

View file

@ -17,8 +17,9 @@ use std::time::Duration;
/// (`ci_testing_config.ron` by default) and executes its specified actions. For a reference of the
/// allowed configuration, see [`CiTestingConfig`].
///
/// This plugin is included within `DefaultPlugins` and `MinimalPlugins` when the `bevy_ci_testing`
/// feature is enabled. It is recommended to only used this plugin during testing (manual or
/// This plugin is included within `DefaultPlugins`, `HeadlessPlugins` and `MinimalPlugins`
/// when the `bevy_ci_testing` feature is enabled.
/// It is recommended to only used this plugin during testing (manual or
/// automatic), and disable it during regular development and for production builds.
#[derive(Default)]
pub struct CiTestingPlugin;

View file

@ -628,9 +628,6 @@ impl ExecutorState {
/// # Safety
/// Caller must ensure no systems are currently borrowed.
unsafe fn spawn_exclusive_system_task(&mut self, context: &Context, system_index: usize) {
// SAFETY: `can_run` returned true for this system, which means
// that no other systems currently have access to the world.
let world = unsafe { context.environment.world_cell.world_mut() };
// SAFETY: this system is not running, no other reference exists
let system = unsafe { &mut *context.environment.systems[system_index].get() };
// Move the full context object into the new future.
@ -641,6 +638,9 @@ impl ExecutorState {
let unapplied_systems = self.unapplied_systems.clone();
self.unapplied_systems.clear();
let task = async move {
// SAFETY: `can_run` returned true for this system, which means
// that no other systems currently have access to the world.
let world = unsafe { context.environment.world_cell.world_mut() };
let res = apply_deferred(&unapplied_systems, context.environment.systems, world);
context.system_completed(system_index, res, system);
};
@ -648,6 +648,9 @@ impl ExecutorState {
context.scope.spawn_on_scope(task);
} else {
let task = async move {
// SAFETY: `can_run` returned true for this system, which means
// that no other systems currently have access to the world.
let world = unsafe { context.environment.world_cell.world_mut() };
let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
__rust_begin_short_backtrace::run(&mut **system, world);
}));
@ -808,4 +811,16 @@ mod tests {
schedule.run(&mut world);
assert!(world.get_resource::<R>().is_some());
}
/// Regression test for a weird bug flagged by MIRI in
/// `spawn_exclusive_system_task`, related to a `&mut World` being captured
/// inside an `async` block and somehow remaining alive even after its last use.
#[test]
fn check_spawn_exclusive_system_task_miri() {
let mut world = World::new();
let mut schedule = Schedule::default();
schedule.set_executor_kind(ExecutorKind::MultiThreaded);
schedule.add_systems(((|_: Commands| {}), |_: Commands| {}).chain());
schedule.run(&mut world);
}
}

View file

@ -1608,7 +1608,7 @@ impl ScheduleGraph {
}
};
if self.settings.use_shortnames {
name = bevy_utils::get_short_name(&name);
name = bevy_utils::ShortName(&name).to_string();
}
name
}

View file

@ -1,12 +1,14 @@
mod parallel_scope;
use core::panic::Location;
use std::marker::PhantomData;
use super::{Deferred, IntoObserverSystem, IntoSystem, RegisterSystem, Resource};
use crate::{
self as bevy_ecs,
bundle::{Bundle, InsertMode},
component::{ComponentId, ComponentInfo},
change_detection::Mut,
component::{Component, ComponentId, ComponentInfo},
entity::{Entities, Entity},
event::{Event, SendEvent},
observer::{Observer, TriggerEvent, TriggerTargets},
@ -926,6 +928,38 @@ impl EntityCommands<'_> {
}
}
/// Get an [`EntityEntryCommands`] for the [`Component`] `T`,
/// allowing you to modify it or insert it if it isn't already present.
///
/// See also [`insert_if_new`](Self::insert_if_new), which lets you insert a [`Bundle`] without overwriting it.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # #[derive(Resource)]
/// # struct PlayerEntity { entity: Entity }
/// #[derive(Component)]
/// struct Level(u32);
///
/// fn level_up_system(mut commands: Commands, player: Res<PlayerEntity>) {
/// commands
/// .entity(player.entity)
/// .entry::<Level>()
/// // Modify the component if it exists
/// .and_modify(|mut lvl| lvl.0 += 1)
/// // Otherwise insert a default value
/// .or_insert(Level(0));
/// }
/// # bevy_ecs::system::assert_is_system(level_up_system);
/// ```
pub fn entry<T: Component>(&mut self) -> EntityEntryCommands<T> {
EntityEntryCommands {
entity_commands: self.reborrow(),
marker: PhantomData,
}
}
/// Adds a [`Bundle`] of components to the entity.
///
/// This will overwrite any previous value(s) of the same component type.
@ -1030,6 +1064,9 @@ impl EntityCommands<'_> {
/// components will leave the old values instead of replacing them with new
/// ones.
///
/// See also [`entry`](Self::entry), which lets you modify a [`Component`] if it's present,
/// as well as initialize it with a default value.
///
/// # Panics
///
/// The command will panic when applied if the associated entity does not exist.
@ -1437,6 +1474,113 @@ impl EntityCommands<'_> {
}
}
/// A wrapper around [`EntityCommands`] with convenience methods for working with a specified component type.
pub struct EntityEntryCommands<'a, T> {
entity_commands: EntityCommands<'a>,
marker: PhantomData<T>,
}
impl<'a, T: Component> EntityEntryCommands<'a, T> {
/// Modify the component `T` if it exists, using the the function `modify`.
pub fn and_modify(mut self, modify: impl FnOnce(Mut<T>) + Send + Sync + 'static) -> Self {
self.entity_commands = self
.entity_commands
.queue(move |mut entity: EntityWorldMut| {
if let Some(value) = entity.get_mut() {
modify(value);
}
});
self
}
/// [Insert](EntityCommands::insert) `default` into this entity, if `T` is not already present.
///
/// See also [`or_insert_with`](Self::or_insert_with).
///
/// # Panics
///
/// Panics if the entity does not exist.
/// See [`or_try_insert`](Self::or_try_insert) for a non-panicking version.
#[track_caller]
pub fn or_insert(mut self, default: T) -> Self {
self.entity_commands = self
.entity_commands
.queue(insert(default, InsertMode::Keep));
self
}
/// [Insert](EntityCommands::insert) `default` into this entity, if `T` is not already present.
///
/// Unlike [`or_insert`](Self::or_insert), this will not panic if the entity does not exist.
///
/// See also [`or_insert_with`](Self::or_insert_with).
#[track_caller]
pub fn or_try_insert(mut self, default: T) -> Self {
self.entity_commands = self
.entity_commands
.queue(try_insert(default, InsertMode::Keep));
self
}
/// [Insert](EntityCommands::insert) the value returned from `default` into this entity, if `T` is not already present.
///
/// See also [`or_insert`](Self::or_insert) and [`or_try_insert`](Self::or_try_insert).
///
/// # Panics
///
/// Panics if the entity does not exist.
/// See [`or_try_insert_with`](Self::or_try_insert_with) for a non-panicking version.
#[track_caller]
pub fn or_insert_with(self, default: impl Fn() -> T) -> Self {
self.or_insert(default())
}
/// [Insert](EntityCommands::insert) the value returned from `default` into this entity, if `T` is not already present.
///
/// Unlike [`or_insert_with`](Self::or_insert_with), this will not panic if the entity does not exist.
///
/// See also [`or_insert`](Self::or_insert) and [`or_try_insert`](Self::or_try_insert).
#[track_caller]
pub fn or_try_insert_with(self, default: impl Fn() -> T) -> Self {
self.or_try_insert(default())
}
/// [Insert](EntityCommands::insert) `T::default` into this entity, if `T` is not already present.
///
/// See also [`or_insert`](Self::or_insert) and [`or_from_world`](Self::or_from_world).
///
/// # Panics
///
/// Panics if the entity does not exist.
#[track_caller]
pub fn or_default(self) -> Self
where
T: Default,
{
#[allow(clippy::unwrap_or_default)]
// FIXME: use `expect` once stable
self.or_insert(T::default())
}
/// [Insert](EntityCommands::insert) `T::from_world` into this entity, if `T` is not already present.
///
/// See also [`or_insert`](Self::or_insert) and [`or_default`](Self::or_default).
///
/// # Panics
///
/// Panics if the entity does not exist.
#[track_caller]
pub fn or_from_world(mut self) -> Self
where
T: FromWorld,
{
self.entity_commands = self
.entity_commands
.queue(insert_from_world::<T>(InsertMode::Keep));
self
}
}
impl<F> Command for F
where
F: FnOnce(&mut World) + Send + 'static,
@ -1545,6 +1689,25 @@ fn insert<T: Bundle>(bundle: T, mode: InsertMode) -> impl EntityCommand {
}
}
/// An [`EntityCommand`] that adds the component using its `FromWorld` implementation.
#[track_caller]
fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand {
let caller = Location::caller();
move |entity: Entity, world: &mut World| {
let value = T::from_world(world);
if let Some(mut entity) = world.get_entity_mut(entity) {
entity.insert_with_caller(
value,
mode,
#[cfg(feature = "track_change_detection")]
caller,
);
} else {
panic!("error[B0003]: {caller}: Could not insert a bundle (of type `{}`) for entity {:?} because it doesn't exist in this World. See: https://bevyengine.org/learn/errors/b0003", std::any::type_name::<T>(), entity);
}
}
}
/// An [`EntityCommand`] that attempts to add the components in a [`Bundle`] to an entity.
/// Does nothing if the entity does not exist.
#[track_caller]
@ -1680,7 +1843,7 @@ mod tests {
self as bevy_ecs,
component::Component,
system::{Commands, Resource},
world::{CommandQueue, World},
world::{CommandQueue, FromWorld, World},
};
use std::{
any::TypeId,
@ -1717,6 +1880,50 @@ mod tests {
world.spawn((W(0u32), W(42u64)));
}
impl FromWorld for W<String> {
fn from_world(world: &mut World) -> Self {
let v = world.resource::<W<usize>>();
Self("*".repeat(v.0))
}
}
#[test]
fn entity_commands_entry() {
let mut world = World::default();
let mut queue = CommandQueue::default();
let mut commands = Commands::new(&mut queue, &world);
let entity = commands.spawn_empty().id();
commands
.entity(entity)
.entry::<W<u32>>()
.and_modify(|_| unreachable!());
queue.apply(&mut world);
assert!(!world.entity(entity).contains::<W<u32>>());
let mut commands = Commands::new(&mut queue, &world);
commands
.entity(entity)
.entry::<W<u32>>()
.or_insert(W(0))
.and_modify(|mut val| {
val.0 = 21;
});
queue.apply(&mut world);
assert_eq!(21, world.get::<W<u32>>(entity).unwrap().0);
let mut commands = Commands::new(&mut queue, &world);
commands
.entity(entity)
.entry::<W<u64>>()
.and_modify(|_| unreachable!())
.or_insert(W(42));
queue.apply(&mut world);
assert_eq!(42, world.get::<W<u64>>(entity).unwrap().0);
world.insert_resource(W(5_usize));
let mut commands = Commands::new(&mut queue, &world);
commands.entity(entity).entry::<W<String>>().or_from_world();
queue.apply(&mut world);
assert_eq!("*****", &world.get::<W<String>>(entity).unwrap().0);
}
#[test]
fn commands() {
let mut world = World::default();

View file

@ -162,6 +162,8 @@ impl Drop for World {
drop(unsafe { Box::from_raw(self.command_queue.bytes.as_ptr()) });
// SAFETY: Pointers in internal command queue are only invalidated here
drop(unsafe { Box::from_raw(self.command_queue.cursor.as_ptr()) });
// SAFETY: Pointers in internal command queue are only invalidated here
drop(unsafe { Box::from_raw(self.command_queue.panic_recovery.as_ptr()) });
}
}

View file

@ -4,7 +4,7 @@ use std::marker::PhantomData;
use crate::Parent;
use bevy_ecs::prelude::*;
#[cfg(feature = "bevy_app")]
use bevy_utils::{get_short_name, HashSet};
use bevy_utils::{HashSet, ShortName};
/// When enabled, runs [`check_hierarchy_component_has_valid_parent<T>`].
///
@ -67,7 +67,7 @@ pub fn check_hierarchy_component_has_valid_parent<T: Component>(
bevy_utils::tracing::warn!(
"warning[B0004]: {name} with the {ty_name} component has a parent without {ty_name}.\n\
This will cause inconsistent behaviors! See: https://bevyengine.org/learn/errors/b0004",
ty_name = get_short_name(std::any::type_name::<T>()),
ty_name = ShortName::of::<T>(),
name = name.map_or_else(|| format!("Entity {}", entity), |s| format!("The {s} entity")),
);
}

View file

@ -71,7 +71,56 @@ plugin_group! {
///
/// [`DefaultPlugins`] contains all the plugins typically required to build
/// a *Bevy* application which includes a *window* and presentation components.
/// For *headless* cases without a *window* or presentation, see [`MinimalPlugins`].
/// For *headless* cases without a *window* or presentation, see [`HeadlessPlugins`].
/// For the absolute minimum number of plugins needed to run a Bevy application, see [`MinimalPlugins`].
}
plugin_group! {
/// This plugin group will add all the default plugins for a headless (no *window* or rendering) *Bevy* application:
pub struct HeadlessPlugins {
bevy_app:::PanicHandlerPlugin,
bevy_log:::LogPlugin,
bevy_core:::TaskPoolPlugin,
bevy_core:::TypeRegistrationPlugin,
bevy_core:::FrameCountPlugin,
bevy_time:::TimePlugin,
bevy_transform:::TransformPlugin,
bevy_hierarchy:::HierarchyPlugin,
bevy_diagnostic:::DiagnosticsPlugin,
bevy_app:::ScheduleRunnerPlugin,
#[custom(cfg(not(target_arch = "wasm32")))]
bevy_app:::TerminalCtrlCHandlerPlugin,
#[cfg(feature = "bevy_asset")]
bevy_asset:::AssetPlugin,
#[cfg(feature = "bevy_scene")]
bevy_scene:::ScenePlugin,
#[cfg(feature = "bevy_animation")]
bevy_animation:::AnimationPlugin,
#[cfg(feature = "bevy_state")]
bevy_state::app:::StatesPlugin,
#[cfg(feature = "bevy_ci_testing")]
bevy_dev_tools::ci_testing:::CiTestingPlugin,
#[doc(hidden)]
:IgnoreAmbiguitiesPlugin,
}
/// This group of plugins is intended for use for *headless* programs, for example: dedicated game servers.
/// See the [*Bevy* *headless* example](https://github.com/bevyengine/bevy/blob/main/examples/app/headless.rs)
///
/// [`HeadlessPlugins`] obeys *Cargo* *feature* flags. Users may exert control over this plugin group
/// by disabling `default-features` in their `Cargo.toml` and enabling only those features
/// that they wish to use.
///
/// [`HeadlessPlugins`] contains all the plugins typically required to build
/// a *Bevy* application. In contrast with [`DefaultPlugins`], it leaves out *window* and presentation components.
/// This allows applications built using this plugin group to run on devices that do not have a screen or rendering
/// capabilities.
/// It includes a [schedule runner (`ScheduleRunnerPlugin`)](crate::app::ScheduleRunnerPlugin)
/// to provide functionality that would otherwise be driven by a windowed application's
/// *event loop* or *message loop*.
///
/// Windowed applications that wish to use a reduced set of plugins should consider the
/// [`DefaultPlugins`] plugin group which can be controlled with *Cargo* *feature* flags.
/// For the absolute minimum number of plugins needed to run a Bevy application, see [`MinimalPlugins`].
}
#[derive(Default)]
@ -110,12 +159,12 @@ plugin_group! {
#[cfg(feature = "bevy_ci_testing")]
bevy_dev_tools::ci_testing:::CiTestingPlugin,
}
/// This group of plugins is intended for use for minimal, *headless* programs
/// see the [*Bevy* *headless* example](https://github.com/bevyengine/bevy/blob/main/examples/app/headless.rs)
/// and includes a [schedule runner (`ScheduleRunnerPlugin`)](crate::app::ScheduleRunnerPlugin)
/// This plugin group represents the absolute minimum, bare-bones, bevy application.
/// Use this if you want to have absolute control over the plugins used.
/// If you are looking to make a *headless* application - without a *window* or rendering,
/// it is usually best to use [`HeadlessPlugins`].
///
/// It includes a [schedule runner (`ScheduleRunnerPlugin`)](crate::app::ScheduleRunnerPlugin)
/// to provide functionality that would otherwise be driven by a windowed application's
/// *event loop* or *message loop*.
///
/// Windowed applications that wish to use a reduced set of plugins should consider the
/// [`DefaultPlugins`] plugin group which can be controlled with *Cargo* *feature* flags.
}

View file

@ -2,7 +2,8 @@
pub use crate::{
app::prelude::*, core::prelude::*, ecs::prelude::*, hierarchy::prelude::*, input::prelude::*,
log::prelude::*, math::prelude::*, reflect::prelude::*, time::prelude::*,
transform::prelude::*, utils::prelude::*, window::prelude::*, DefaultPlugins, MinimalPlugins,
transform::prelude::*, utils::prelude::*, window::prelude::*, DefaultPlugins, HeadlessPlugins,
MinimalPlugins,
};
pub use bevy_derive::{bevy_main, Deref, DerefMut};

View file

@ -2366,9 +2366,7 @@ bevy_reflect::tests::Test {
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| {
bevy_utils::get_short_name(std::any::type_name::<Self>())
})
CELL.get_or_insert::<Self, _>(|| bevy_utils::ShortName::of::<Self>().to_string())
}
fn type_ident() -> Option<&'static str> {

View file

@ -10,7 +10,6 @@ use crate::TextureSlicer;
/// This is commonly used as a component within [`SpriteBundle`](crate::bundle::SpriteBundle).
#[derive(Component, Debug, Default, Clone, Reflect)]
#[reflect(Component, Default, Debug)]
#[repr(C)]
pub struct Sprite {
/// The sprite's color tint
pub color: Color,

View file

@ -21,7 +21,6 @@ use bevy_utils::HashMap;
#[derive(Asset, Reflect, Debug, Clone)]
#[reflect(Debug)]
pub struct TextureAtlasLayout {
// TODO: add support to Uniforms derive to write dimensions and sprites to the same buffer
pub size: UVec2,
/// The specific areas of the atlas where each texture can be found
pub textures: Vec<URect>,

View file

@ -22,10 +22,8 @@ pub mod prelude {
}
pub mod futures;
#[cfg(feature = "alloc")]
mod short_names;
#[cfg(feature = "alloc")]
pub use short_names::get_short_name;
pub use short_names::ShortName;
pub mod synccell;
pub mod syncunsafecell;

View file

@ -1,62 +1,115 @@
use alloc::string::String;
/// Shortens a type name to remove all module paths.
/// Lazily shortens a type name to remove all module paths.
///
/// The short name of a type is its full name as returned by
/// [`std::any::type_name`], but with the prefix of all paths removed. For
/// example, the short name of `alloc::vec::Vec<core::option::Option<u32>>`
/// would be `Vec<Option<u32>>`.
pub fn get_short_name(full_name: &str) -> String {
// Generics result in nested paths within <..> blocks.
// Consider "bevy_render::camera::camera::extract_cameras<bevy_render::camera::bundle::Camera3d>".
// To tackle this, we parse the string from left to right, collapsing as we go.
let mut index: usize = 0;
let end_of_string = full_name.len();
let mut parsed_name = String::new();
///
/// Shortening is performed lazily without allocation.
#[cfg_attr(
feature = "alloc",
doc = r#" To get a [`String`] from this type, use the [`to_string`](`alloc::string::ToString::to_string`) method."#
)]
///
/// # Examples
///
/// ```rust
/// # use bevy_utils::ShortName;
/// #
/// # mod foo {
/// # pub mod bar {
/// # pub struct Baz;
/// # }
/// # }
/// // Baz
/// let short_name = ShortName::of::<foo::bar::Baz>();
/// ```
#[derive(Clone, Copy)]
pub struct ShortName<'a>(pub &'a str);
while index < end_of_string {
let rest_of_string = full_name.get(index..end_of_string).unwrap_or_default();
// Collapse everything up to the next special character,
// then skip over it
if let Some(special_character_index) = rest_of_string.find(|c: char| {
(c == ' ')
|| (c == '<')
|| (c == '>')
|| (c == '(')
|| (c == ')')
|| (c == '[')
|| (c == ']')
|| (c == ',')
|| (c == ';')
}) {
let segment_to_collapse = rest_of_string
.get(0..special_character_index)
.unwrap_or_default();
parsed_name += collapse_type_name(segment_to_collapse);
// Insert the special character
let special_character =
&rest_of_string[special_character_index..=special_character_index];
parsed_name.push_str(special_character);
match special_character {
">" | ")" | "]"
if rest_of_string[special_character_index + 1..].starts_with("::") =>
{
parsed_name.push_str("::");
// Move the index past the "::"
index += special_character_index + 3;
}
// Move the index just past the special character
_ => index += special_character_index + 1,
}
} else {
// If there are no special characters left, we're done!
parsed_name += collapse_type_name(rest_of_string);
index = end_of_string;
}
impl ShortName<'static> {
/// Gets a shortened version of the name of the type `T`.
pub fn of<T: ?Sized>() -> Self {
Self(core::any::type_name::<T>())
}
}
impl<'a> ShortName<'a> {
/// Gets the original name before shortening.
pub const fn original(&self) -> &'a str {
self.0
}
}
impl<'a> From<&'a str> for ShortName<'a> {
fn from(value: &'a str) -> Self {
Self(value)
}
}
impl<'a> core::fmt::Debug for ShortName<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let &ShortName(full_name) = self;
// Generics result in nested paths within <..> blocks.
// Consider "bevy_render::camera::camera::extract_cameras<bevy_render::camera::bundle::Camera3d>".
// To tackle this, we parse the string from left to right, collapsing as we go.
let mut index: usize = 0;
let end_of_string = full_name.len();
while index < end_of_string {
let rest_of_string = full_name.get(index..end_of_string).unwrap_or_default();
// Collapse everything up to the next special character,
// then skip over it
if let Some(special_character_index) = rest_of_string.find(|c: char| {
(c == ' ')
|| (c == '<')
|| (c == '>')
|| (c == '(')
|| (c == ')')
|| (c == '[')
|| (c == ']')
|| (c == ',')
|| (c == ';')
}) {
let segment_to_collapse = rest_of_string
.get(0..special_character_index)
.unwrap_or_default();
f.write_str(collapse_type_name(segment_to_collapse))?;
// Insert the special character
let special_character =
&rest_of_string[special_character_index..=special_character_index];
f.write_str(special_character)?;
match special_character {
">" | ")" | "]"
if rest_of_string[special_character_index + 1..].starts_with("::") =>
{
f.write_str("::")?;
// Move the index past the "::"
index += special_character_index + 3;
}
// Move the index just past the special character
_ => index += special_character_index + 1,
}
} else {
// If there are no special characters left, we're done!
f.write_str(collapse_type_name(rest_of_string))?;
index = end_of_string;
}
}
Ok(())
}
}
impl<'a> core::fmt::Display for ShortName<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
<Self as core::fmt::Debug>::fmt(self, f)
}
parsed_name
}
#[inline(always)]
@ -77,49 +130,52 @@ fn collapse_type_name(string: &str) -> &str {
}
}
#[cfg(test)]
#[cfg(all(test, feature = "alloc"))]
mod name_formatting_tests {
use super::get_short_name;
use super::ShortName;
#[test]
fn trivial() {
assert_eq!(get_short_name("test_system"), "test_system");
assert_eq!(ShortName("test_system").to_string(), "test_system");
}
#[test]
fn path_separated() {
assert_eq!(
get_short_name("bevy_prelude::make_fun_game"),
ShortName("bevy_prelude::make_fun_game").to_string(),
"make_fun_game"
);
}
#[test]
fn tuple_type() {
assert_eq!(get_short_name("(String, String)"), "(String, String)");
assert_eq!(
ShortName("(String, String)").to_string(),
"(String, String)"
);
}
#[test]
fn array_type() {
assert_eq!(get_short_name("[i32; 3]"), "[i32; 3]");
assert_eq!(ShortName("[i32; 3]").to_string(), "[i32; 3]");
}
#[test]
fn trivial_generics() {
assert_eq!(get_short_name("a<B>"), "a<B>");
assert_eq!(ShortName("a<B>").to_string(), "a<B>");
}
#[test]
fn multiple_type_parameters() {
assert_eq!(get_short_name("a<B, C>"), "a<B, C>");
assert_eq!(ShortName("a<B, C>").to_string(), "a<B, C>");
}
#[test]
fn enums() {
assert_eq!(get_short_name("Option::None"), "Option::None");
assert_eq!(get_short_name("Option::Some(2)"), "Option::Some(2)");
assert_eq!(ShortName("Option::None").to_string(), "Option::None");
assert_eq!(ShortName("Option::Some(2)").to_string(), "Option::Some(2)");
assert_eq!(
get_short_name("bevy_render::RenderSet::Prepare"),
ShortName("bevy_render::RenderSet::Prepare").to_string(),
"RenderSet::Prepare"
);
}
@ -127,7 +183,7 @@ mod name_formatting_tests {
#[test]
fn generics() {
assert_eq!(
get_short_name("bevy_render::camera::camera::extract_cameras<bevy_render::camera::bundle::Camera3d>"),
ShortName("bevy_render::camera::camera::extract_cameras<bevy_render::camera::bundle::Camera3d>").to_string(),
"extract_cameras<Camera3d>"
);
}
@ -135,7 +191,7 @@ mod name_formatting_tests {
#[test]
fn nested_generics() {
assert_eq!(
get_short_name("bevy::mad_science::do_mad_science<mad_science::Test<mad_science::Tube>, bavy::TypeSystemAbuse>"),
ShortName("bevy::mad_science::do_mad_science<mad_science::Test<mad_science::Tube>, bavy::TypeSystemAbuse>").to_string(),
"do_mad_science<Test<Tube>, TypeSystemAbuse>"
);
}
@ -143,13 +199,16 @@ mod name_formatting_tests {
#[test]
fn sub_path_after_closing_bracket() {
assert_eq!(
get_short_name("bevy_asset::assets::Assets<bevy_scene::dynamic_scene::DynamicScene>::asset_event_system"),
ShortName("bevy_asset::assets::Assets<bevy_scene::dynamic_scene::DynamicScene>::asset_event_system").to_string(),
"Assets<DynamicScene>::asset_event_system"
);
assert_eq!(
get_short_name("(String, String)::default"),
ShortName("(String, String)::default").to_string(),
"(String, String)::default"
);
assert_eq!(get_short_name("[i32; 16]::default"), "[i32; 16]::default");
assert_eq!(
ShortName("[i32; 16]::default").to_string(),
"[i32; 16]::default"
);
}
}

View file

@ -8,21 +8,26 @@
//! # replace "*" with the most recent version of bevy
//! ```
use bevy::{app::ScheduleRunnerPlugin, prelude::*, utils::Duration};
use bevy::{app::ScheduleRunnerPlugin, log::LogPlugin, prelude::*, utils::Duration};
fn main() {
// This app runs once
App::new()
.add_plugins(MinimalPlugins.set(ScheduleRunnerPlugin::run_once()))
.add_plugins(HeadlessPlugins.set(ScheduleRunnerPlugin::run_once()))
.add_systems(Update, hello_world_system)
.run();
// This app loops forever at 60 fps
App::new()
.add_plugins(
MinimalPlugins.set(ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(
1.0 / 60.0,
))),
HeadlessPlugins
.set(ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(
1.0 / 60.0,
)))
// The log and ctrl+c plugin can only be registered once globally,
// which means we need to disable it here, because it was already registered with the
// app that runs once.
.disable::<LogPlugin>(),
)
.add_systems(Update, counter)
.run();

View file

@ -1,5 +1,5 @@
//! Demonstrates picking for sprites and sprite atlases. The picking backend only tests against the
//! sprite bounds, so the sprite atlas can be picked by clicking on its trnasparent areas.
//! sprite bounds, so the sprite atlas can be picked by clicking on its transparent areas.
use bevy::{prelude::*, sprite::Anchor};
use std::fmt::Debug;