>(
group: &mut BenchmarkGroup,
name: &str,
curve: CubicCurve,
diff --git a/clippy.toml b/clippy.toml
index 2c98e8ed02..372ffbaf0b 100644
--- a/clippy.toml
+++ b/clippy.toml
@@ -41,7 +41,6 @@ disallowed-methods = [
{ path = "f32::asinh", reason = "use bevy_math::ops::asinh instead for libm determinism" },
{ path = "f32::acosh", reason = "use bevy_math::ops::acosh instead for libm determinism" },
{ path = "f32::atanh", reason = "use bevy_math::ops::atanh instead for libm determinism" },
- { path = "criterion::black_box", reason = "use core::hint::black_box instead" },
]
# Require `bevy_ecs::children!` to use `[]` braces, instead of `()` or `{}`.
diff --git a/crates/bevy_a11y/Cargo.toml b/crates/bevy_a11y/Cargo.toml
index 759cf3e787..70ee16cf77 100644
--- a/crates/bevy_a11y/Cargo.toml
+++ b/crates/bevy_a11y/Cargo.toml
@@ -1,9 +1,9 @@
[package]
name = "bevy_a11y"
-version = "0.16.0-dev"
+version = "0.17.0-dev"
edition = "2024"
description = "Provides accessibility support for Bevy Engine"
-homepage = "https://bevyengine.org"
+homepage = "https://bevy.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy", "accessibility", "a11y"]
@@ -40,13 +40,13 @@ critical-section = [
[dependencies]
# bevy
-bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false }
-bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" }
-bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", default-features = false }
-bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", default-features = false, optional = true }
+bevy_app = { path = "../bevy_app", version = "0.17.0-dev", default-features = false }
+bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
+bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev", default-features = false }
+bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", default-features = false, optional = true }
# other
-accesskit = { version = "0.18", default-features = false }
+accesskit = { version = "0.19", default-features = false }
serde = { version = "1", default-features = false, features = [
"alloc",
], optional = true }
diff --git a/crates/bevy_a11y/src/lib.rs b/crates/bevy_a11y/src/lib.rs
index 94468c148c..22b2f71f07 100644
--- a/crates/bevy_a11y/src/lib.rs
+++ b/crates/bevy_a11y/src/lib.rs
@@ -1,8 +1,8 @@
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
- html_logo_url = "https://bevyengine.org/assets/icon.png",
- html_favicon_url = "https://bevyengine.org/assets/icon.png"
+ html_logo_url = "https://bevy.org/assets/icon.png",
+ html_favicon_url = "https://bevy.org/assets/icon.png"
)]
#![no_std]
@@ -26,7 +26,8 @@ use accesskit::Node;
use bevy_app::Plugin;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
- prelude::{Component, Event},
+ component::Component,
+ event::{BufferedEvent, Event},
resource::Resource,
schedule::SystemSet,
};
@@ -44,7 +45,7 @@ use serde::{Deserialize, Serialize};
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
/// Wrapper struct for [`accesskit::ActionRequest`]. Required to allow it to be used as an `Event`.
-#[derive(Event, Deref, DerefMut)]
+#[derive(Event, BufferedEvent, Deref, DerefMut)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct ActionRequest(pub accesskit::ActionRequest);
diff --git a/crates/bevy_animation/Cargo.toml b/crates/bevy_animation/Cargo.toml
index 11e819806c..adf17a8580 100644
--- a/crates/bevy_animation/Cargo.toml
+++ b/crates/bevy_animation/Cargo.toml
@@ -1,38 +1,38 @@
[package]
name = "bevy_animation"
-version = "0.16.0-dev"
+version = "0.17.0-dev"
edition = "2024"
description = "Provides animation functionality for Bevy Engine"
-homepage = "https://bevyengine.org"
+homepage = "https://bevy.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
[dependencies]
# bevy
-bevy_app = { path = "../bevy_app", version = "0.16.0-dev" }
-bevy_asset = { path = "../bevy_asset", version = "0.16.0-dev" }
-bevy_color = { path = "../bevy_color", version = "0.16.0-dev" }
-bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" }
-bevy_log = { path = "../bevy_log", version = "0.16.0-dev" }
-bevy_math = { path = "../bevy_math", version = "0.16.0-dev" }
-bevy_mesh = { path = "../bevy_mesh", version = "0.16.0-dev" }
-bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [
+bevy_app = { path = "../bevy_app", version = "0.17.0-dev" }
+bevy_asset = { path = "../bevy_asset", version = "0.17.0-dev" }
+bevy_color = { path = "../bevy_color", version = "0.17.0-dev" }
+bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
+bevy_log = { path = "../bevy_log", version = "0.17.0-dev" }
+bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
+bevy_mesh = { path = "../bevy_mesh", version = "0.17.0-dev" }
+bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", features = [
"petgraph",
] }
-bevy_render = { path = "../bevy_render", version = "0.16.0-dev" }
-bevy_time = { path = "../bevy_time", version = "0.16.0-dev" }
-bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" }
-bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev" }
-bevy_transform = { path = "../bevy_transform", version = "0.16.0-dev" }
-bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false, features = [
+bevy_render = { path = "../bevy_render", version = "0.17.0-dev" }
+bevy_time = { path = "../bevy_time", version = "0.17.0-dev" }
+bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
+bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
+bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
+bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
"std",
"serialize",
] }
# other
petgraph = { version = "0.7", features = ["serde-1"] }
-ron = "0.8"
+ron = "0.10"
serde = "1"
blake3 = { version = "1.0" }
downcast-rs = { version = "2", default-features = false, features = ["std"] }
diff --git a/crates/bevy_animation/src/gltf_curves.rs b/crates/bevy_animation/src/gltf_curves.rs
index 688011a32c..593ca04d2e 100644
--- a/crates/bevy_animation/src/gltf_curves.rs
+++ b/crates/bevy_animation/src/gltf_curves.rs
@@ -55,7 +55,7 @@ pub struct CubicKeyframeCurve {
impl Curve for CubicKeyframeCurve
where
- V: VectorSpace,
+ V: VectorSpace,
{
#[inline]
fn domain(&self) -> Interval {
@@ -179,7 +179,7 @@ pub struct WideLinearKeyframeCurve {
impl IterableCurve for WideLinearKeyframeCurve
where
- T: VectorSpace,
+ T: VectorSpace,
{
#[inline]
fn domain(&self) -> Interval {
@@ -289,7 +289,7 @@ pub struct WideCubicKeyframeCurve {
impl IterableCurve for WideCubicKeyframeCurve
where
- T: VectorSpace,
+ T: VectorSpace,
{
#[inline]
fn domain(&self) -> Interval {
@@ -406,7 +406,7 @@ fn cubic_spline_interpolation(
step_duration: f32,
) -> T
where
- T: VectorSpace,
+ T: VectorSpace,
{
let coeffs = (vec4(2.0, 1.0, -2.0, 1.0) * lerp + vec4(-3.0, -2.0, 3.0, -1.0)) * lerp;
value_start * (coeffs.x * lerp + 1.0)
@@ -415,7 +415,7 @@ where
+ tangent_in_end * step_duration * lerp * coeffs.w
}
-fn cubic_spline_interpolate_slices<'a, T: VectorSpace>(
+fn cubic_spline_interpolate_slices<'a, T: VectorSpace>(
width: usize,
first: &'a [T],
second: &'a [T],
diff --git a/crates/bevy_animation/src/graph.rs b/crates/bevy_animation/src/graph.rs
index aa6d252fee..a5f4041ac7 100644
--- a/crates/bevy_animation/src/graph.rs
+++ b/crates/bevy_animation/src/graph.rs
@@ -1,10 +1,11 @@
//! The animation graph, which allows animations to be blended together.
use core::{
+ fmt::Write,
iter,
ops::{Index, IndexMut, Range},
};
-use std::io::{self, Write};
+use std::io;
use bevy_asset::{
io::Reader, Asset, AssetEvent, AssetId, AssetLoader, AssetPath, Assets, Handle, LoadContext,
diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs
index 21ea15f96f..ae7ce42ed6 100644
--- a/crates/bevy_animation/src/lib.rs
+++ b/crates/bevy_animation/src/lib.rs
@@ -1,8 +1,8 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
- html_logo_url = "https://bevyengine.org/assets/icon.png",
- html_favicon_url = "https://bevyengine.org/assets/icon.png"
+ html_logo_url = "https://bevy.org/assets/icon.png",
+ html_favicon_url = "https://bevy.org/assets/icon.png"
)]
//! Animation for the game engine Bevy
@@ -324,13 +324,13 @@ impl AnimationClip {
.push(variable_curve);
}
- /// Add a untargeted [`Event`] to this [`AnimationClip`].
+ /// Add an [`EntityEvent`] with no [`AnimationTarget`] to this [`AnimationClip`].
///
/// The `event` will be cloned and triggered on the [`AnimationPlayer`] entity once the `time` (in seconds)
/// is reached in the animation.
///
/// See also [`add_event_to_target`](Self::add_event_to_target).
- pub fn add_event(&mut self, time: f32, event: impl Event + Clone) {
+ pub fn add_event(&mut self, time: f32, event: impl EntityEvent + Clone) {
self.add_event_fn(
time,
move |commands: &mut Commands, entity: Entity, _time: f32, _weight: f32| {
@@ -339,7 +339,7 @@ impl AnimationClip {
);
}
- /// Add an [`Event`] to an [`AnimationTarget`] named by an [`AnimationTargetId`].
+ /// Add an [`EntityEvent`] to an [`AnimationTarget`] named by an [`AnimationTargetId`].
///
/// The `event` will be cloned and triggered on the entity matching the target once the `time` (in seconds)
/// is reached in the animation.
@@ -349,7 +349,7 @@ impl AnimationClip {
&mut self,
target_id: AnimationTargetId,
time: f32,
- event: impl Event + Clone,
+ event: impl EntityEvent + Clone,
) {
self.add_event_fn_to_target(
target_id,
@@ -360,19 +360,19 @@ impl AnimationClip {
);
}
- /// Add a untargeted event function to this [`AnimationClip`].
+ /// Add an event function with no [`AnimationTarget`] to this [`AnimationClip`].
///
/// The `func` will trigger on the [`AnimationPlayer`] entity once the `time` (in seconds)
/// is reached in the animation.
///
- /// For a simpler [`Event`]-based alternative, see [`AnimationClip::add_event`].
+ /// For a simpler [`EntityEvent`]-based alternative, see [`AnimationClip::add_event`].
/// See also [`add_event_to_target`](Self::add_event_to_target).
///
/// ```
/// # use bevy_animation::AnimationClip;
/// # let mut clip = AnimationClip::default();
/// clip.add_event_fn(1.0, |commands, entity, time, weight| {
- /// println!("Animation Event Triggered {entity:#?} at time {time} with weight {weight}");
+ /// println!("Animation event triggered {entity:#?} at time {time} with weight {weight}");
/// })
/// ```
pub fn add_event_fn(
@@ -388,14 +388,14 @@ impl AnimationClip {
/// The `func` will trigger on the entity matching the target once the `time` (in seconds)
/// is reached in the animation.
///
- /// For a simpler [`Event`]-based alternative, see [`AnimationClip::add_event_to_target`].
+ /// For a simpler [`EntityEvent`]-based alternative, see [`AnimationClip::add_event_to_target`].
/// Use [`add_event`](Self::add_event) instead if you don't have a specific target.
///
/// ```
/// # use bevy_animation::{AnimationClip, AnimationTargetId};
/// # let mut clip = AnimationClip::default();
/// clip.add_event_fn_to_target(AnimationTargetId::from_iter(["Arm", "Hand"]), 1.0, |commands, entity, time, weight| {
- /// println!("Animation Event Triggered {entity:#?} at time {time} with weight {weight}");
+ /// println!("Animation event triggered {entity:#?} at time {time} with weight {weight}");
/// })
/// ```
pub fn add_event_fn_to_target(
@@ -1534,7 +1534,7 @@ mod tests {
use super::*;
- #[derive(Event, Reflect, Clone)]
+ #[derive(Event, EntityEvent, Reflect, Clone)]
struct A;
#[track_caller]
diff --git a/crates/bevy_anti_aliasing/Cargo.toml b/crates/bevy_anti_aliasing/Cargo.toml
index 5a8e48ecb5..8c32d70fff 100644
--- a/crates/bevy_anti_aliasing/Cargo.toml
+++ b/crates/bevy_anti_aliasing/Cargo.toml
@@ -1,9 +1,9 @@
[package]
name = "bevy_anti_aliasing"
-version = "0.16.0-dev"
+version = "0.17.0-dev"
edition = "2024"
description = "Provides various anti aliasing implementations for Bevy Engine"
-homepage = "https://bevyengine.org"
+homepage = "https://bevy.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
@@ -16,17 +16,17 @@ smaa_luts = ["bevy_render/ktx2", "bevy_image/ktx2", "bevy_image/zstd"]
[dependencies]
# bevy
-bevy_asset = { path = "../bevy_asset", version = "0.16.0-dev" }
-bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev" }
-bevy_render = { path = "../bevy_render", version = "0.16.0-dev" }
-bevy_math = { path = "../bevy_math", version = "0.16.0-dev" }
-bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" }
-bevy_app = { path = "../bevy_app", version = "0.16.0-dev" }
-bevy_image = { path = "../bevy_image", version = "0.16.0-dev" }
-bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" }
-bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev" }
-bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.16.0-dev" }
-bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.16.0-dev" }
+bevy_asset = { path = "../bevy_asset", version = "0.17.0-dev" }
+bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
+bevy_render = { path = "../bevy_render", version = "0.17.0-dev" }
+bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
+bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
+bevy_app = { path = "../bevy_app", version = "0.17.0-dev" }
+bevy_image = { path = "../bevy_image", version = "0.17.0-dev" }
+bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
+bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
+bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.17.0-dev" }
+bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.17.0-dev" }
# other
tracing = { version = "0.1", default-features = false, features = ["std"] }
diff --git a/crates/bevy_anti_aliasing/src/contrast_adaptive_sharpening/mod.rs b/crates/bevy_anti_aliasing/src/contrast_adaptive_sharpening/mod.rs
index 707d75819d..0b4a99fb59 100644
--- a/crates/bevy_anti_aliasing/src/contrast_adaptive_sharpening/mod.rs
+++ b/crates/bevy_anti_aliasing/src/contrast_adaptive_sharpening/mod.rs
@@ -1,5 +1,5 @@
use bevy_app::prelude::*;
-use bevy_asset::{load_internal_asset, weak_handle, Handle};
+use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
use bevy_core_pipeline::{
core_2d::graph::{Core2d, Node2d},
core_3d::graph::{Core3d, Node3d},
@@ -95,20 +95,12 @@ impl ExtractComponent for ContrastAdaptiveSharpening {
}
}
-const CONTRAST_ADAPTIVE_SHARPENING_SHADER_HANDLE: Handle =
- weak_handle!("ef83f0a5-51df-4b51-9ab7-b5fd1ae5a397");
-
/// Adds Support for Contrast Adaptive Sharpening (CAS).
pub struct CasPlugin;
impl Plugin for CasPlugin {
fn build(&self, app: &mut App) {
- load_internal_asset!(
- app,
- CONTRAST_ADAPTIVE_SHARPENING_SHADER_HANDLE,
- "robust_contrast_adaptive_sharpening.wgsl",
- Shader::from_wgsl
- );
+ embedded_asset!(app, "robust_contrast_adaptive_sharpening.wgsl");
app.register_type::();
app.add_plugins((
@@ -171,6 +163,7 @@ impl Plugin for CasPlugin {
pub struct CasPipeline {
texture_bind_group: BindGroupLayout,
sampler: Sampler,
+ shader: Handle,
}
impl FromWorld for CasPipeline {
@@ -194,6 +187,7 @@ impl FromWorld for CasPipeline {
CasPipeline {
texture_bind_group,
sampler,
+ shader: load_embedded_asset!(render_world, "robust_contrast_adaptive_sharpening.wgsl"),
}
}
}
@@ -217,7 +211,7 @@ impl SpecializedRenderPipeline for CasPipeline {
layout: vec![self.texture_bind_group.clone()],
vertex: fullscreen_shader_vertex_state(),
fragment: Some(FragmentState {
- shader: CONTRAST_ADAPTIVE_SHARPENING_SHADER_HANDLE,
+ shader: self.shader.clone(),
shader_defs,
entry_point: "fragment".into(),
targets: vec![Some(ColorTargetState {
diff --git a/crates/bevy_anti_aliasing/src/experimental/mod.rs b/crates/bevy_anti_aliasing/src/experimental/mod.rs
deleted file mode 100644
index a8dc522c56..0000000000
--- a/crates/bevy_anti_aliasing/src/experimental/mod.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-//! Experimental rendering features.
-//!
-//! Experimental features are features with known problems, missing features,
-//! compatibility issues, low performance, and/or future breaking changes, but
-//! are included nonetheless for testing purposes.
-
-pub mod taa {
- pub use crate::taa::{TemporalAntiAliasNode, TemporalAntiAliasPlugin, TemporalAntiAliasing};
-}
diff --git a/crates/bevy_anti_aliasing/src/fxaa/mod.rs b/crates/bevy_anti_aliasing/src/fxaa/mod.rs
index 4848d3d268..6b914c4e86 100644
--- a/crates/bevy_anti_aliasing/src/fxaa/mod.rs
+++ b/crates/bevy_anti_aliasing/src/fxaa/mod.rs
@@ -1,5 +1,5 @@
use bevy_app::prelude::*;
-use bevy_asset::{load_internal_asset, weak_handle, Handle};
+use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
use bevy_core_pipeline::{
core_2d::graph::{Core2d, Node2d},
core_3d::graph::{Core3d, Node3d},
@@ -80,13 +80,11 @@ impl Default for Fxaa {
}
}
-const FXAA_SHADER_HANDLE: Handle = weak_handle!("fc58c0a8-01c0-46e9-94cc-83a794bae7b0");
-
/// Adds support for Fast Approximate Anti-Aliasing (FXAA)
pub struct FxaaPlugin;
impl Plugin for FxaaPlugin {
fn build(&self, app: &mut App) {
- load_internal_asset!(app, FXAA_SHADER_HANDLE, "fxaa.wgsl", Shader::from_wgsl);
+ embedded_asset!(app, "fxaa.wgsl");
app.register_type::();
app.add_plugins(ExtractComponentPlugin::::default());
@@ -132,6 +130,7 @@ impl Plugin for FxaaPlugin {
pub struct FxaaPipeline {
texture_bind_group: BindGroupLayout,
sampler: Sampler,
+ shader: Handle,
}
impl FromWorld for FxaaPipeline {
@@ -158,6 +157,7 @@ impl FromWorld for FxaaPipeline {
FxaaPipeline {
texture_bind_group,
sampler,
+ shader: load_embedded_asset!(render_world, "fxaa.wgsl"),
}
}
}
@@ -183,7 +183,7 @@ impl SpecializedRenderPipeline for FxaaPipeline {
layout: vec![self.texture_bind_group.clone()],
vertex: fullscreen_shader_vertex_state(),
fragment: Some(FragmentState {
- shader: FXAA_SHADER_HANDLE,
+ shader: self.shader.clone(),
shader_defs: vec![
format!("EDGE_THRESH_{}", key.edge_threshold.get_str()).into(),
format!("EDGE_THRESH_MIN_{}", key.edge_threshold_min.get_str()).into(),
diff --git a/crates/bevy_anti_aliasing/src/lib.rs b/crates/bevy_anti_aliasing/src/lib.rs
index be09a2e5b2..12b7982cb5 100644
--- a/crates/bevy_anti_aliasing/src/lib.rs
+++ b/crates/bevy_anti_aliasing/src/lib.rs
@@ -2,26 +2,25 @@
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
- html_logo_url = "https://bevyengine.org/assets/icon.png",
- html_favicon_url = "https://bevyengine.org/assets/icon.png"
+ html_logo_url = "https://bevy.org/assets/icon.png",
+ html_favicon_url = "https://bevy.org/assets/icon.png"
)]
use bevy_app::Plugin;
use contrast_adaptive_sharpening::CasPlugin;
use fxaa::FxaaPlugin;
use smaa::SmaaPlugin;
+use taa::TemporalAntiAliasPlugin;
pub mod contrast_adaptive_sharpening;
-pub mod experimental;
pub mod fxaa;
pub mod smaa;
-
-mod taa;
+pub mod taa;
#[derive(Default)]
pub struct AntiAliasingPlugin;
impl Plugin for AntiAliasingPlugin {
fn build(&self, app: &mut bevy_app::App) {
- app.add_plugins((FxaaPlugin, CasPlugin, SmaaPlugin));
+ app.add_plugins((FxaaPlugin, SmaaPlugin, TemporalAntiAliasPlugin, CasPlugin));
}
}
diff --git a/crates/bevy_anti_aliasing/src/smaa/mod.rs b/crates/bevy_anti_aliasing/src/smaa/mod.rs
index 4259b5e33d..bb082c5a01 100644
--- a/crates/bevy_anti_aliasing/src/smaa/mod.rs
+++ b/crates/bevy_anti_aliasing/src/smaa/mod.rs
@@ -32,7 +32,7 @@
use bevy_app::{App, Plugin};
#[cfg(feature = "smaa_luts")]
use bevy_asset::load_internal_binary_asset;
-use bevy_asset::{load_internal_asset, weak_handle, Handle};
+use bevy_asset::{embedded_asset, load_embedded_asset, weak_handle, Handle};
#[cfg(not(feature = "smaa_luts"))]
use bevy_core_pipeline::tonemapping::lut_placeholder;
use bevy_core_pipeline::{
@@ -80,8 +80,6 @@ use bevy_render::{
};
use bevy_utils::prelude::default;
-/// The handle of the `smaa.wgsl` shader.
-const SMAA_SHADER_HANDLE: Handle = weak_handle!("fdd9839f-1ab4-4e0d-88a0-240b67da2ddf");
/// The handle of the area LUT, a KTX2 format texture that SMAA uses internally.
const SMAA_AREA_LUT_TEXTURE_HANDLE: Handle =
weak_handle!("569c4d67-c7fa-4958-b1af-0836023603c0");
@@ -147,6 +145,8 @@ struct SmaaEdgeDetectionPipeline {
postprocess_bind_group_layout: BindGroupLayout,
/// The bind group layout for data specific to this pass.
edge_detection_bind_group_layout: BindGroupLayout,
+ /// The shader asset handle.
+ shader: Handle,
}
/// The pipeline data for phase 2 of SMAA: blending weight calculation.
@@ -155,6 +155,8 @@ struct SmaaBlendingWeightCalculationPipeline {
postprocess_bind_group_layout: BindGroupLayout,
/// The bind group layout for data specific to this pass.
blending_weight_calculation_bind_group_layout: BindGroupLayout,
+ /// The shader asset handle.
+ shader: Handle,
}
/// The pipeline data for phase 3 of SMAA: neighborhood blending.
@@ -163,6 +165,8 @@ struct SmaaNeighborhoodBlendingPipeline {
postprocess_bind_group_layout: BindGroupLayout,
/// The bind group layout for data specific to this pass.
neighborhood_blending_bind_group_layout: BindGroupLayout,
+ /// The shader asset handle.
+ shader: Handle,
}
/// A unique identifier for a set of SMAA pipelines.
@@ -287,7 +291,7 @@ pub struct SmaaSpecializedRenderPipelines {
impl Plugin for SmaaPlugin {
fn build(&self, app: &mut App) {
// Load the shader.
- load_internal_asset!(app, SMAA_SHADER_HANDLE, "smaa.wgsl", Shader::from_wgsl);
+ embedded_asset!(app, "smaa.wgsl");
// Load the two lookup textures. These are compressed textures in KTX2
// format.
@@ -431,18 +435,23 @@ impl FromWorld for SmaaPipelines {
),
);
+ let shader = load_embedded_asset!(world, "smaa.wgsl");
+
SmaaPipelines {
edge_detection: SmaaEdgeDetectionPipeline {
postprocess_bind_group_layout: postprocess_bind_group_layout.clone(),
edge_detection_bind_group_layout,
+ shader: shader.clone(),
},
blending_weight_calculation: SmaaBlendingWeightCalculationPipeline {
postprocess_bind_group_layout: postprocess_bind_group_layout.clone(),
blending_weight_calculation_bind_group_layout,
+ shader: shader.clone(),
},
neighborhood_blending: SmaaNeighborhoodBlendingPipeline {
postprocess_bind_group_layout,
neighborhood_blending_bind_group_layout,
+ shader,
},
}
}
@@ -472,13 +481,13 @@ impl SpecializedRenderPipeline for SmaaEdgeDetectionPipeline {
self.edge_detection_bind_group_layout.clone(),
],
vertex: VertexState {
- shader: SMAA_SHADER_HANDLE,
+ shader: self.shader.clone(),
shader_defs: shader_defs.clone(),
entry_point: "edge_detection_vertex_main".into(),
buffers: vec![],
},
fragment: Some(FragmentState {
- shader: SMAA_SHADER_HANDLE,
+ shader: self.shader.clone(),
shader_defs,
entry_point: "luma_edge_detection_fragment_main".into(),
targets: vec![Some(ColorTargetState {
@@ -532,13 +541,13 @@ impl SpecializedRenderPipeline for SmaaBlendingWeightCalculationPipeline {
self.blending_weight_calculation_bind_group_layout.clone(),
],
vertex: VertexState {
- shader: SMAA_SHADER_HANDLE,
+ shader: self.shader.clone(),
shader_defs: shader_defs.clone(),
entry_point: "blending_weight_calculation_vertex_main".into(),
buffers: vec![],
},
fragment: Some(FragmentState {
- shader: SMAA_SHADER_HANDLE,
+ shader: self.shader.clone(),
shader_defs,
entry_point: "blending_weight_calculation_fragment_main".into(),
targets: vec![Some(ColorTargetState {
@@ -580,13 +589,13 @@ impl SpecializedRenderPipeline for SmaaNeighborhoodBlendingPipeline {
self.neighborhood_blending_bind_group_layout.clone(),
],
vertex: VertexState {
- shader: SMAA_SHADER_HANDLE,
+ shader: self.shader.clone(),
shader_defs: shader_defs.clone(),
entry_point: "neighborhood_blending_vertex_main".into(),
buffers: vec![],
},
fragment: Some(FragmentState {
- shader: SMAA_SHADER_HANDLE,
+ shader: self.shader.clone(),
shader_defs,
entry_point: "neighborhood_blending_fragment_main".into(),
targets: vec![Some(ColorTargetState {
@@ -838,7 +847,7 @@ impl ViewNode for SmaaNode {
view_smaa_uniform_offset,
smaa_textures,
view_smaa_bind_groups,
- ): QueryItem<'w, Self::ViewQuery>,
+ ): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let pipeline_cache = world.resource::();
diff --git a/crates/bevy_anti_aliasing/src/taa/mod.rs b/crates/bevy_anti_aliasing/src/taa/mod.rs
index dc12d34423..0f706146b1 100644
--- a/crates/bevy_anti_aliasing/src/taa/mod.rs
+++ b/crates/bevy_anti_aliasing/src/taa/mod.rs
@@ -1,5 +1,5 @@
use bevy_app::{App, Plugin};
-use bevy_asset::{load_internal_asset, weak_handle, Handle};
+use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
use bevy_core_pipeline::{
core_3d::graph::{Core3d, Node3d},
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
@@ -40,8 +40,6 @@ use bevy_render::{
};
use tracing::warn;
-const TAA_SHADER_HANDLE: Handle = weak_handle!("fea20d50-86b6-4069-aa32-374346aec00c");
-
/// Plugin for temporal anti-aliasing.
///
/// See [`TemporalAntiAliasing`] for more details.
@@ -49,7 +47,7 @@ pub struct TemporalAntiAliasPlugin;
impl Plugin for TemporalAntiAliasPlugin {
fn build(&self, app: &mut App) {
- load_internal_asset!(app, TAA_SHADER_HANDLE, "taa.wgsl", Shader::from_wgsl);
+ embedded_asset!(app, "taa.wgsl");
app.register_type::();
@@ -64,7 +62,7 @@ impl Plugin for TemporalAntiAliasPlugin {
.add_systems(
Render,
(
- prepare_taa_jitter_and_mip_bias.in_set(RenderSystems::ManageViews),
+ prepare_taa_jitter.in_set(RenderSystems::ManageViews),
prepare_taa_pipelines.in_set(RenderSystems::Prepare),
prepare_taa_history_textures.in_set(RenderSystems::PrepareResources),
),
@@ -115,7 +113,6 @@ impl Plugin for TemporalAntiAliasPlugin {
///
/// # Usage Notes
///
-/// The [`TemporalAntiAliasPlugin`] must be added to your app.
/// Any camera with this component must also disable [`Msaa`] by setting it to [`Msaa::Off`].
///
/// [Currently](https://github.com/bevyengine/bevy/issues/8423), TAA cannot be used with [`bevy_render::camera::OrthographicProjection`].
@@ -128,11 +125,9 @@ impl Plugin for TemporalAntiAliasPlugin {
///
/// 1. Write particle motion vectors to the motion vectors prepass texture
/// 2. Render particles after TAA
-///
-/// If no [`MipBias`] component is attached to the camera, TAA will add a `MipBias(-1.0)` component.
#[derive(Component, Reflect, Clone)]
#[reflect(Component, Default, Clone)]
-#[require(TemporalJitter, DepthPrepass, MotionVectorPrepass)]
+#[require(TemporalJitter, MipBias, DepthPrepass, MotionVectorPrepass)]
#[doc(alias = "Taa")]
pub struct TemporalAntiAliasing {
/// Set to true to delete the saved temporal history (past frames).
@@ -243,6 +238,7 @@ struct TaaPipeline {
taa_bind_group_layout: BindGroupLayout,
nearest_sampler: Sampler,
linear_sampler: Sampler,
+ shader: Handle,
}
impl FromWorld for TaaPipeline {
@@ -287,6 +283,7 @@ impl FromWorld for TaaPipeline {
taa_bind_group_layout,
nearest_sampler,
linear_sampler,
+ shader: load_embedded_asset!(world, "taa.wgsl"),
}
}
}
@@ -319,7 +316,7 @@ impl SpecializedRenderPipeline for TaaPipeline {
layout: vec![self.taa_bind_group_layout.clone()],
vertex: fullscreen_shader_vertex_state(),
fragment: Some(FragmentState {
- shader: TAA_SHADER_HANDLE,
+ shader: self.shader.clone(),
shader_defs,
entry_point: "taa".into(),
targets: vec![
@@ -345,16 +342,11 @@ impl SpecializedRenderPipeline for TaaPipeline {
}
fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut) {
- let mut cameras_3d = main_world.query_filtered::<(
+ let mut cameras_3d = main_world.query::<(
RenderEntity,
&Camera,
&Projection,
- &mut TemporalAntiAliasing,
- ), (
- With,
- With,
- With,
- With,
+ Option<&mut TemporalAntiAliasing>,
)>();
for (entity, camera, camera_projection, mut taa_settings) in
@@ -364,14 +356,12 @@ fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut();
@@ -379,13 +369,22 @@ fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut,
- mut query: Query<(Entity, &mut TemporalJitter, Option<&MipBias>), With>,
- mut commands: Commands,
+ mut query: Query<
+ &mut TemporalJitter,
+ (
+ With,
+ With,
+ With,
+ With,
+ With,
+ ),
+ >,
) {
- // Halton sequence (2, 3) - 0.5, skipping i = 0
+ // Halton sequence (2, 3) - 0.5
let halton_sequence = [
+ vec2(0.0, 0.0),
vec2(0.0, -0.16666666),
vec2(-0.25, 0.16666669),
vec2(0.25, -0.3888889),
@@ -393,17 +392,12 @@ fn prepare_taa_jitter_and_mip_bias(
vec2(0.125, 0.2777778),
vec2(-0.125, -0.2777778),
vec2(0.375, 0.055555582),
- vec2(-0.4375, 0.3888889),
];
let offset = halton_sequence[frame_count.0 as usize % halton_sequence.len()];
- for (entity, mut jitter, mip_bias) in &mut query {
+ for mut jitter in &mut query {
jitter.offset = offset;
-
- if mip_bias.is_none() {
- commands.entity(entity).insert(MipBias(-1.0));
- }
}
}
diff --git a/crates/bevy_app/Cargo.toml b/crates/bevy_app/Cargo.toml
index f46db94db3..a0c5222b0b 100644
--- a/crates/bevy_app/Cargo.toml
+++ b/crates/bevy_app/Cargo.toml
@@ -1,9 +1,9 @@
[package]
name = "bevy_app"
-version = "0.16.0-dev"
+version = "0.17.0-dev"
edition = "2024"
description = "Provides core App functionality for Bevy Engine"
-homepage = "https://bevyengine.org"
+homepage = "https://bevy.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
@@ -47,7 +47,6 @@ std = [
"bevy_ecs/std",
"dep:ctrlc",
"downcast-rs/std",
- "bevy_utils/std",
"bevy_tasks/std",
"bevy_platform/std",
]
@@ -72,16 +71,20 @@ web = [
"dep:console_error_panic_hook",
]
+hotpatching = [
+ "bevy_ecs/hotpatching",
+ "dep:dioxus-devtools",
+ "dep:crossbeam-channel",
+]
+
[dependencies]
# bevy
-bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" }
-bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", default-features = false }
-bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", default-features = false, optional = true }
-bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features = false, features = [
- "alloc",
-] }
-bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false }
-bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false }
+bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
+bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev", default-features = false }
+bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", default-features = false, optional = true }
+bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev", default-features = false }
+bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev", default-features = false }
+bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false }
# other
downcast-rs = { version = "2", default-features = false }
@@ -90,8 +93,10 @@ variadics_please = "1.1"
tracing = { version = "0.1", default-features = false, optional = true }
log = { version = "0.4", default-features = false }
cfg-if = "1.0.0"
+dioxus-devtools = { version = "0.7.0-alpha.1", optional = true }
+crossbeam-channel = { version = "0.5.0", optional = true }
-[target.'cfg(any(unix, windows))'.dependencies]
+[target.'cfg(any(all(unix, not(target_os = "horizon")), windows))'.dependencies]
ctrlc = { version = "3.4.4", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]
diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs
index 60431ca479..05f3de27b1 100644
--- a/crates/bevy_app/src/app.rs
+++ b/crates/bevy_app/src/app.rs
@@ -10,6 +10,7 @@ use alloc::{
pub use bevy_derive::AppLabel;
use bevy_ecs::{
component::RequiredComponentsError,
+ error::{DefaultErrorHandler, ErrorHandler},
event::{event_update_system, EventCursor},
intern::Interned,
prelude::*,
@@ -85,6 +86,7 @@ pub struct App {
/// [`WinitPlugin`]: https://docs.rs/bevy/latest/bevy/winit/struct.WinitPlugin.html
/// [`ScheduleRunnerPlugin`]: https://docs.rs/bevy/latest/bevy/app/struct.ScheduleRunnerPlugin.html
pub(crate) runner: RunnerFn,
+ default_error_handler: Option,
}
impl Debug for App {
@@ -104,10 +106,13 @@ impl Default for App {
#[cfg(feature = "bevy_reflect")]
{
+ use bevy_ecs::observer::ObservedBy;
+
app.init_resource::();
app.register_type::();
app.register_type::();
app.register_type::();
+ app.register_type::();
}
#[cfg(feature = "reflect_functions")]
@@ -143,6 +148,7 @@ impl App {
sub_apps: HashMap::default(),
},
runner: Box::new(run_once),
+ default_error_handler: None,
}
}
@@ -338,7 +344,7 @@ impl App {
self
}
- /// Initializes `T` event handling by inserting an event queue resource ([`Events::`])
+ /// Initializes [`BufferedEvent`] handling for `T` by inserting an event queue resource ([`Events::`])
/// and scheduling an [`event_update_system`] in [`First`].
///
/// See [`Events`] for information on how to define events.
@@ -349,7 +355,7 @@ impl App {
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
- /// # #[derive(Event)]
+ /// # #[derive(Event, BufferedEvent)]
/// # struct MyEvent;
/// # let mut app = App::new();
/// #
@@ -357,7 +363,7 @@ impl App {
/// ```
pub fn add_event(&mut self) -> &mut Self
where
- T: Event,
+ T: BufferedEvent,
{
self.main_mut().add_event::();
self
@@ -1115,7 +1121,12 @@ impl App {
}
/// Inserts a [`SubApp`] with the given label.
- pub fn insert_sub_app(&mut self, label: impl AppLabel, sub_app: SubApp) {
+ pub fn insert_sub_app(&mut self, label: impl AppLabel, mut sub_app: SubApp) {
+ if let Some(handler) = self.default_error_handler {
+ sub_app
+ .world_mut()
+ .get_resource_or_insert_with(|| DefaultErrorHandler(handler));
+ }
self.sub_apps.sub_apps.insert(label.intern(), sub_app);
}
@@ -1298,6 +1309,8 @@ impl App {
/// Spawns an [`Observer`] entity, which will watch for and respond to the given event.
///
+ /// `observer` can be any system whose first parameter is [`On`].
+ ///
/// # Examples
///
/// ```rust
@@ -1312,14 +1325,14 @@ impl App {
/// # friends_allowed: bool,
/// # };
/// #
- /// # #[derive(Event)]
+ /// # #[derive(Event, EntityEvent)]
/// # struct Invite;
/// #
/// # #[derive(Component)]
/// # struct Friend;
/// #
- /// // An observer system can be any system where the first parameter is a trigger
- /// app.add_observer(|trigger: Trigger, friends: Query>, mut commands: Commands| {
+ ///
+ /// app.add_observer(|trigger: On, friends: Query>, mut commands: Commands| {
/// if trigger.event().friends_allowed {
/// for friend in friends.iter() {
/// commands.trigger_targets(Invite, friend);
@@ -1334,6 +1347,49 @@ impl App {
self.world_mut().add_observer(observer);
self
}
+
+ /// Gets the error handler to set for new supapps.
+ ///
+ /// Note that the error handler of existing subapps may differ.
+ pub fn get_error_handler(&self) -> Option {
+ self.default_error_handler
+ }
+
+ /// Set the [default error handler] for the all subapps (including the main one and future ones)
+ /// that do not have one.
+ ///
+ /// May only be called once and should be set by the application, not by libraries.
+ ///
+ /// The handler will be called when an error is produced and not otherwise handled.
+ ///
+ /// # Panics
+ /// Panics if called multiple times.
+ ///
+ /// # Example
+ /// ```
+ /// # use bevy_app::*;
+ /// # use bevy_ecs::error::warn;
+ /// # fn MyPlugins(_: &mut App) {}
+ /// App::new()
+ /// .set_error_handler(warn)
+ /// .add_plugins(MyPlugins)
+ /// .run();
+ /// ```
+ ///
+ /// [default error handler]: bevy_ecs::error::DefaultErrorHandler
+ pub fn set_error_handler(&mut self, handler: ErrorHandler) -> &mut Self {
+ assert!(
+ self.default_error_handler.is_none(),
+ "`set_error_handler` called multiple times on same `App`"
+ );
+ self.default_error_handler = Some(handler);
+ for sub_app in self.sub_apps.iter_mut() {
+ sub_app
+ .world_mut()
+ .get_resource_or_insert_with(|| DefaultErrorHandler(handler));
+ }
+ self
+ }
}
type RunnerFn = Box AppExit>;
@@ -1351,7 +1407,7 @@ fn run_once(mut app: App) -> AppExit {
app.should_exit().unwrap_or(AppExit::Success)
}
-/// An event that indicates the [`App`] should exit. If one or more of these are present at the end of an update,
+/// A [`BufferedEvent`] that indicates the [`App`] should exit. If one or more of these are present at the end of an update,
/// the [runner](App::set_runner) will end and ([maybe](App::run)) return control to the caller.
///
/// This event can be used to detect when an exit is requested. Make sure that systems listening
@@ -1361,7 +1417,7 @@ fn run_once(mut app: App) -> AppExit {
/// This type is roughly meant to map to a standard definition of a process exit code (0 means success, not 0 means error). Due to portability concerns
/// (see [`ExitCode`](https://doc.rust-lang.org/std/process/struct.ExitCode.html) and [`process::exit`](https://doc.rust-lang.org/std/process/fn.exit.html#))
/// we only allow error codes between 1 and [255](u8::MAX).
-#[derive(Event, Debug, Clone, Default, PartialEq, Eq)]
+#[derive(Event, BufferedEvent, Debug, Clone, Default, PartialEq, Eq)]
pub enum AppExit {
/// [`App`] exited without any problems.
#[default]
@@ -1429,9 +1485,9 @@ mod tests {
change_detection::{DetectChanges, ResMut},
component::Component,
entity::Entity,
- event::{Event, EventWriter, Events},
+ event::{BufferedEvent, Event, EventWriter, Events},
+ lifecycle::RemovedComponents,
query::With,
- removal_detection::RemovedComponents,
resource::Resource,
schedule::{IntoScheduleConfigs, ScheduleLabel},
system::{Commands, Query},
@@ -1526,7 +1582,7 @@ mod tests {
app.add_systems(EnterMainMenu, (foo, bar));
app.world_mut().run_schedule(EnterMainMenu);
- assert_eq!(app.world().entities().len(), 2);
+ assert_eq!(app.world().entity_count(), 2);
}
#[test]
@@ -1795,7 +1851,7 @@ mod tests {
}
#[test]
fn events_should_be_updated_once_per_update() {
- #[derive(Event, Clone)]
+ #[derive(Event, BufferedEvent, Clone)]
struct TestEvent;
let mut app = App::new();
diff --git a/crates/bevy_app/src/hotpatch.rs b/crates/bevy_app/src/hotpatch.rs
new file mode 100644
index 0000000000..1f9da40730
--- /dev/null
+++ b/crates/bevy_app/src/hotpatch.rs
@@ -0,0 +1,42 @@
+//! Utilities for hotpatching code.
+extern crate alloc;
+
+use alloc::sync::Arc;
+
+use bevy_ecs::{event::EventWriter, HotPatched};
+#[cfg(not(target_family = "wasm"))]
+use dioxus_devtools::connect_subsecond;
+use dioxus_devtools::subsecond;
+
+pub use dioxus_devtools::subsecond::{call, HotFunction};
+
+use crate::{Last, Plugin};
+
+/// Plugin connecting to Dioxus CLI to enable hot patching.
+#[derive(Default)]
+pub struct HotPatchPlugin;
+
+impl Plugin for HotPatchPlugin {
+ fn build(&self, app: &mut crate::App) {
+ let (sender, receiver) = crossbeam_channel::bounded::(1);
+
+ // Connects to the dioxus CLI that will handle rebuilds
+ // This will open a connection to the dioxus CLI to receive updated jump tables
+ // Sends a `HotPatched` message through the channel when the jump table is updated
+ #[cfg(not(target_family = "wasm"))]
+ connect_subsecond();
+ subsecond::register_handler(Arc::new(move || {
+ sender.send(HotPatched).unwrap();
+ }));
+
+ // Adds a system that will read the channel for new `HotPatched`, and forward them as event to the ECS
+ app.add_event::().add_systems(
+ Last,
+ move |mut events: EventWriter| {
+ if receiver.try_recv().is_ok() {
+ events.write_default();
+ }
+ },
+ );
+ }
+}
diff --git a/crates/bevy_app/src/lib.rs b/crates/bevy_app/src/lib.rs
index 743806df71..188ba957f6 100644
--- a/crates/bevy_app/src/lib.rs
+++ b/crates/bevy_app/src/lib.rs
@@ -8,8 +8,8 @@
#![cfg_attr(any(docsrs, docsrs_dep), feature(doc_auto_cfg, rustdoc_internals))]
#![forbid(unsafe_code)]
#![doc(
- html_logo_url = "https://bevyengine.org/assets/icon.png",
- html_favicon_url = "https://bevyengine.org/assets/icon.png"
+ html_logo_url = "https://bevy.org/assets/icon.png",
+ html_favicon_url = "https://bevy.org/assets/icon.png"
)]
#![no_std]
@@ -28,21 +28,26 @@ mod main_schedule;
mod panic_handler;
mod plugin;
mod plugin_group;
+mod propagate;
mod schedule_runner;
mod sub_app;
mod task_pool_plugin;
-#[cfg(all(any(unix, windows), feature = "std"))]
+#[cfg(all(any(all(unix, not(target_os = "horizon")), windows), feature = "std"))]
mod terminal_ctrl_c_handler;
+#[cfg(feature = "hotpatching")]
+pub mod hotpatch;
+
pub use app::*;
pub use main_schedule::*;
pub use panic_handler::*;
pub use plugin::*;
pub use plugin_group::*;
+pub use propagate::*;
pub use schedule_runner::*;
pub use sub_app::*;
pub use task_pool_plugin::*;
-#[cfg(all(any(unix, windows), feature = "std"))]
+#[cfg(all(any(all(unix, not(target_os = "horizon")), windows), feature = "std"))]
pub use terminal_ctrl_c_handler::*;
/// The app prelude.
diff --git a/crates/bevy_app/src/panic_handler.rs b/crates/bevy_app/src/panic_handler.rs
index 1021a3dc2e..c35d2333bf 100644
--- a/crates/bevy_app/src/panic_handler.rs
+++ b/crates/bevy_app/src/panic_handler.rs
@@ -1,4 +1,4 @@
-//! This module provides panic handlers for [Bevy](https://bevyengine.org)
+//! This module provides panic handlers for [Bevy](https://bevy.org)
//! apps, and automatically configures platform specifics (i.e. Wasm or Android).
//!
//! By default, the [`PanicHandlerPlugin`] from this crate is included in Bevy's `DefaultPlugins`.
diff --git a/crates/bevy_app/src/propagate.rs b/crates/bevy_app/src/propagate.rs
new file mode 100644
index 0000000000..c6ac5139b9
--- /dev/null
+++ b/crates/bevy_app/src/propagate.rs
@@ -0,0 +1,552 @@
+use alloc::vec::Vec;
+use core::marker::PhantomData;
+
+use crate::{App, Plugin, Update};
+use bevy_ecs::{
+ component::Component,
+ entity::Entity,
+ hierarchy::ChildOf,
+ lifecycle::RemovedComponents,
+ query::{Changed, Or, QueryFilter, With, Without},
+ relationship::{Relationship, RelationshipTarget},
+ schedule::{IntoScheduleConfigs, SystemSet},
+ system::{Commands, Local, Query},
+};
+
+/// Plugin to automatically propagate a component value to all direct and transient relationship
+/// targets (e.g. [`bevy_ecs::hierarchy::Children`]) of entities with a [`Propagate`] component.
+///
+/// The plugin Will maintain the target component over hierarchy changes, adding or removing
+/// `C` when a relationship `R` (e.g. [`ChildOf`]) is added to or removed from a
+/// relationship tree with a [`Propagate`] source, or if the [`Propagate`] component
+/// is added, changed or removed.
+///
+/// Optionally you can include a query filter `F` to restrict the entities that are updated.
+/// Note that the filter is not rechecked dynamically: changes to the filter state will not be
+/// picked up until the [`Propagate`] component is touched, or the hierarchy is changed.
+/// All members of the tree between source and target must match the filter for propagation
+/// to reach a given target.
+/// Individual entities can be skipped or terminate the propagation with the [`PropagateOver`]
+/// and [`PropagateStop`] components.
+pub struct HierarchyPropagatePlugin<
+ C: Component + Clone + PartialEq,
+ F: QueryFilter = (),
+ R: Relationship = ChildOf,
+>(PhantomData (C, F, R)>);
+
+/// Causes the inner component to be added to this entity and all direct and transient relationship
+/// targets. A target with a [`Propagate`] component of its own will override propagation from
+/// that point in the tree.
+#[derive(Component, Clone, PartialEq)]
+pub struct Propagate(pub C);
+
+/// Stops the output component being added to this entity.
+/// Relationship targets will still inherit the component from this entity or its parents.
+#[derive(Component)]
+pub struct PropagateOver(PhantomData C>);
+
+/// Stops the propagation at this entity. Children will not inherit the component.
+#[derive(Component)]
+pub struct PropagateStop(PhantomData C>);
+
+/// The set in which propagation systems are added. You can schedule your logic relative to this set.
+#[derive(SystemSet, Clone, PartialEq, PartialOrd, Ord)]
+pub struct PropagateSet {
+ _p: PhantomData C>,
+}
+
+/// Internal struct for managing propagation
+#[derive(Component, Clone, PartialEq)]
+pub struct Inherited(pub C);
+
+impl Default
+ for HierarchyPropagatePlugin
+{
+ fn default() -> Self {
+ Self(Default::default())
+ }
+}
+
+impl Default for PropagateOver {
+ fn default() -> Self {
+ Self(Default::default())
+ }
+}
+
+impl Default for PropagateStop {
+ fn default() -> Self {
+ Self(Default::default())
+ }
+}
+
+impl core::fmt::Debug for PropagateSet {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ f.debug_struct("PropagateSet")
+ .field("_p", &self._p)
+ .finish()
+ }
+}
+
+impl Eq for PropagateSet {}
+
+impl core::hash::Hash for PropagateSet {
+ fn hash(&self, state: &mut H) {
+ self._p.hash(state);
+ }
+}
+
+impl Default for PropagateSet {
+ fn default() -> Self {
+ Self {
+ _p: Default::default(),
+ }
+ }
+}
+
+impl Plugin
+ for HierarchyPropagatePlugin
+{
+ fn build(&self, app: &mut App) {
+ app.add_systems(
+ Update,
+ (
+ update_source::,
+ update_stopped::,
+ update_reparented::,
+ propagate_inherited::,
+ propagate_output::,
+ )
+ .chain()
+ .in_set(PropagateSet::::default()),
+ );
+ }
+}
+
+/// add/remove `Inherited::` and `C` for entities with a direct `Propagate::`
+pub fn update_source(
+ mut commands: Commands,
+ changed: Query<
+ (Entity, &Propagate),
+ (
+ Or<(Changed>, Without>)>,
+ Without>,
+ ),
+ >,
+ mut removed: RemovedComponents>,
+) {
+ for (entity, source) in &changed {
+ commands
+ .entity(entity)
+ .try_insert(Inherited(source.0.clone()));
+ }
+
+ for removed in removed.read() {
+ if let Ok(mut commands) = commands.get_entity(removed) {
+ commands.remove::<(Inherited, C)>();
+ }
+ }
+}
+
+/// remove `Inherited::` and `C` for entities with a `PropagateStop::`
+pub fn update_stopped(
+ mut commands: Commands,
+ q: Query>, With>, F)>,
+) {
+ for entity in q.iter() {
+ let mut cmds = commands.entity(entity);
+ cmds.remove::<(Inherited, C)>();
+ }
+}
+
+/// add/remove `Inherited::` and `C` for entities which have changed relationship
+pub fn update_reparented(
+ mut commands: Commands,
+ moved: Query<
+ (Entity, &R, Option<&Inherited>),
+ (
+ Changed,
+ Without>,
+ Without>,
+ F,
+ ),
+ >,
+ relations: Query<&Inherited>,
+ orphaned: Query>, Without>, Without, F)>,
+) {
+ for (entity, relation, maybe_inherited) in &moved {
+ if let Ok(inherited) = relations.get(relation.get()) {
+ commands.entity(entity).try_insert(inherited.clone());
+ } else if maybe_inherited.is_some() {
+ commands.entity(entity).remove::<(Inherited, C)>();
+ }
+ }
+
+ for orphan in &orphaned {
+ commands.entity(orphan).remove::<(Inherited, C)>();
+ }
+}
+
+/// add/remove `Inherited::` for targets of entities with modified `Inherited::`
+pub fn propagate_inherited(
+ mut commands: Commands,
+ changed: Query<
+ (&Inherited, &R::RelationshipTarget),
+ (Changed>, Without>, F),
+ >,
+ recurse: Query<
+ (Option<&R::RelationshipTarget>, Option<&Inherited>),
+ (Without>, Without>, F),
+ >,
+ mut removed: RemovedComponents>,
+ mut to_process: Local>)>>,
+) {
+ // gather changed
+ for (inherited, targets) in &changed {
+ to_process.extend(
+ targets
+ .iter()
+ .map(|target| (target, Some(inherited.clone()))),
+ );
+ }
+
+ // and removed
+ for entity in removed.read() {
+ if let Ok((Some(targets), _)) = recurse.get(entity) {
+ to_process.extend(targets.iter().map(|target| (target, None)));
+ }
+ }
+
+ // propagate
+ while let Some((entity, maybe_inherited)) = (*to_process).pop() {
+ let Ok((maybe_targets, maybe_current)) = recurse.get(entity) else {
+ continue;
+ };
+
+ if maybe_current == maybe_inherited.as_ref() {
+ continue;
+ }
+
+ if let Some(targets) = maybe_targets {
+ to_process.extend(
+ targets
+ .iter()
+ .map(|target| (target, maybe_inherited.clone())),
+ );
+ }
+
+ if let Some(inherited) = maybe_inherited {
+ commands.entity(entity).try_insert(inherited.clone());
+ } else {
+ commands.entity(entity).remove::<(Inherited, C)>();
+ }
+ }
+}
+
+/// add `C` to entities with `Inherited::`
+pub fn propagate_output(
+ mut commands: Commands,
+ changed: Query<
+ (Entity, &Inherited, Option<&C>),
+ (Changed>, Without>, F),
+ >,
+) {
+ for (entity, inherited, maybe_current) in &changed {
+ if maybe_current.is_some_and(|c| &inherited.0 == c) {
+ continue;
+ }
+
+ commands.entity(entity).try_insert(inherited.0.clone());
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use bevy_ecs::schedule::Schedule;
+
+ use crate::{App, Update};
+
+ use super::*;
+
+ #[derive(Component, Clone, PartialEq, Debug)]
+ struct TestValue(u32);
+
+ #[test]
+ fn test_simple_propagate() {
+ let mut app = App::new();
+ app.add_schedule(Schedule::new(Update));
+ app.add_plugins(HierarchyPropagatePlugin::::default());
+
+ let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
+ let intermediate = app
+ .world_mut()
+ .spawn_empty()
+ .insert(ChildOf(propagator))
+ .id();
+ let propagatee = app
+ .world_mut()
+ .spawn_empty()
+ .insert(ChildOf(intermediate))
+ .id();
+
+ app.update();
+
+ assert!(app
+ .world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee)
+ .is_ok());
+ }
+
+ #[test]
+ fn test_reparented() {
+ let mut app = App::new();
+ app.add_schedule(Schedule::new(Update));
+ app.add_plugins(HierarchyPropagatePlugin::::default());
+
+ let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
+ let propagatee = app
+ .world_mut()
+ .spawn_empty()
+ .insert(ChildOf(propagator))
+ .id();
+
+ app.update();
+
+ assert!(app
+ .world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee)
+ .is_ok());
+ }
+
+ #[test]
+ fn test_reparented_with_prior() {
+ let mut app = App::new();
+ app.add_schedule(Schedule::new(Update));
+ app.add_plugins(HierarchyPropagatePlugin::::default());
+
+ let propagator_a = app.world_mut().spawn(Propagate(TestValue(1))).id();
+ let propagator_b = app.world_mut().spawn(Propagate(TestValue(2))).id();
+ let propagatee = app
+ .world_mut()
+ .spawn_empty()
+ .insert(ChildOf(propagator_a))
+ .id();
+
+ app.update();
+ assert_eq!(
+ app.world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee),
+ Ok(&TestValue(1))
+ );
+ app.world_mut()
+ .commands()
+ .entity(propagatee)
+ .insert(ChildOf(propagator_b));
+ app.update();
+ assert_eq!(
+ app.world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee),
+ Ok(&TestValue(2))
+ );
+ }
+
+ #[test]
+ fn test_remove_orphan() {
+ let mut app = App::new();
+ app.add_schedule(Schedule::new(Update));
+ app.add_plugins(HierarchyPropagatePlugin::::default());
+
+ let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
+ let propagatee = app
+ .world_mut()
+ .spawn_empty()
+ .insert(ChildOf(propagator))
+ .id();
+
+ app.update();
+ assert!(app
+ .world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee)
+ .is_ok());
+ app.world_mut()
+ .commands()
+ .entity(propagatee)
+ .remove::();
+ app.update();
+ assert!(app
+ .world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee)
+ .is_err());
+ }
+
+ #[test]
+ fn test_remove_propagated() {
+ let mut app = App::new();
+ app.add_schedule(Schedule::new(Update));
+ app.add_plugins(HierarchyPropagatePlugin::::default());
+
+ let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
+ let propagatee = app
+ .world_mut()
+ .spawn_empty()
+ .insert(ChildOf(propagator))
+ .id();
+
+ app.update();
+ assert!(app
+ .world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee)
+ .is_ok());
+ app.world_mut()
+ .commands()
+ .entity(propagator)
+ .remove::>();
+ app.update();
+ assert!(app
+ .world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee)
+ .is_err());
+ }
+
+ #[test]
+ fn test_propagate_over() {
+ let mut app = App::new();
+ app.add_schedule(Schedule::new(Update));
+ app.add_plugins(HierarchyPropagatePlugin::::default());
+
+ let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
+ let propagate_over = app
+ .world_mut()
+ .spawn(TestValue(2))
+ .insert(ChildOf(propagator))
+ .id();
+ let propagatee = app
+ .world_mut()
+ .spawn_empty()
+ .insert(ChildOf(propagate_over))
+ .id();
+
+ app.update();
+ assert_eq!(
+ app.world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee),
+ Ok(&TestValue(1))
+ );
+ }
+
+ #[test]
+ fn test_propagate_stop() {
+ let mut app = App::new();
+ app.add_schedule(Schedule::new(Update));
+ app.add_plugins(HierarchyPropagatePlugin::::default());
+
+ let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
+ let propagate_stop = app
+ .world_mut()
+ .spawn(PropagateStop::::default())
+ .insert(ChildOf(propagator))
+ .id();
+ let no_propagatee = app
+ .world_mut()
+ .spawn_empty()
+ .insert(ChildOf(propagate_stop))
+ .id();
+
+ app.update();
+ assert!(app
+ .world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), no_propagatee)
+ .is_err());
+ }
+
+ #[test]
+ fn test_intermediate_override() {
+ let mut app = App::new();
+ app.add_schedule(Schedule::new(Update));
+ app.add_plugins(HierarchyPropagatePlugin::::default());
+
+ let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
+ let intermediate = app
+ .world_mut()
+ .spawn_empty()
+ .insert(ChildOf(propagator))
+ .id();
+ let propagatee = app
+ .world_mut()
+ .spawn_empty()
+ .insert(ChildOf(intermediate))
+ .id();
+
+ app.update();
+ assert_eq!(
+ app.world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee),
+ Ok(&TestValue(1))
+ );
+
+ app.world_mut()
+ .entity_mut(intermediate)
+ .insert(Propagate(TestValue(2)));
+ app.update();
+ assert_eq!(
+ app.world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee),
+ Ok(&TestValue(2))
+ );
+ }
+
+ #[test]
+ fn test_filter() {
+ #[derive(Component)]
+ struct Marker;
+
+ let mut app = App::new();
+ app.add_schedule(Schedule::new(Update));
+ app.add_plugins(HierarchyPropagatePlugin::>::default());
+
+ let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
+ let propagatee = app
+ .world_mut()
+ .spawn_empty()
+ .insert(ChildOf(propagator))
+ .id();
+
+ app.update();
+ assert!(app
+ .world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee)
+ .is_err());
+
+ // NOTE: changes to the filter condition are not rechecked
+ app.world_mut().entity_mut(propagator).insert(Marker);
+ app.world_mut().entity_mut(propagatee).insert(Marker);
+ app.update();
+ assert!(app
+ .world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee)
+ .is_err());
+
+ app.world_mut()
+ .entity_mut(propagator)
+ .insert(Propagate(TestValue(1)));
+ app.update();
+ assert!(app
+ .world_mut()
+ .query::<&TestValue>()
+ .get(app.world(), propagatee)
+ .is_ok());
+ }
+}
diff --git a/crates/bevy_app/src/sub_app.rs b/crates/bevy_app/src/sub_app.rs
index c340b80654..56d6b43d38 100644
--- a/crates/bevy_app/src/sub_app.rs
+++ b/crates/bevy_app/src/sub_app.rs
@@ -338,7 +338,7 @@ impl SubApp {
/// See [`App::add_event`].
pub fn add_event(&mut self) -> &mut Self
where
- T: Event,
+ T: BufferedEvent,
{
if !self.world.contains_resource::>() {
EventRegistry::register_event::(self.world_mut());
diff --git a/crates/bevy_asset/Cargo.toml b/crates/bevy_asset/Cargo.toml
index 07a45a3f6d..0835b3b49a 100644
--- a/crates/bevy_asset/Cargo.toml
+++ b/crates/bevy_asset/Cargo.toml
@@ -1,9 +1,9 @@
[package]
name = "bevy_asset"
-version = "0.16.0-dev"
+version = "0.17.0-dev"
edition = "2024"
description = "Provides asset functionality for Bevy Engine"
-homepage = "https://bevyengine.org"
+homepage = "https://bevy.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
@@ -19,19 +19,19 @@ watch = []
trace = []
[dependencies]
-bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [
+bevy_app = { path = "../bevy_app", version = "0.17.0-dev", default-features = false, features = [
"bevy_reflect",
] }
-bevy_asset_macros = { path = "macros", version = "0.16.0-dev" }
-bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", default-features = false }
-bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", default-features = false, features = [
+bevy_asset_macros = { path = "macros", version = "0.17.0-dev" }
+bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev", default-features = false }
+bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", default-features = false, features = [
"uuid",
] }
-bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false, features = [
+bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev", default-features = false, features = [
"async_executor",
] }
-bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features = false }
-bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false, features = [
+bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev", default-features = false }
+bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
"std",
] }
@@ -54,7 +54,7 @@ parking_lot = { version = "0.12", default-features = false, features = [
"arc_lock",
"send_guard",
] }
-ron = { version = "0.8", default-features = false }
+ron = { version = "0.10", default-features = false }
serde = { version = "1", default-features = false, features = ["derive"] }
thiserror = { version = "2", default-features = false }
derive_more = { version = "1", default-features = false, features = ["from"] }
@@ -65,7 +65,7 @@ uuid = { version = "1.13.1", default-features = false, features = [
tracing = { version = "0.1", default-features = false }
[target.'cfg(target_os = "android")'.dependencies]
-bevy_window = { path = "../bevy_window", version = "0.16.0-dev" }
+bevy_window = { path = "../bevy_window", version = "0.17.0-dev" }
[target.'cfg(target_arch = "wasm32")'.dependencies]
# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption.
@@ -78,13 +78,13 @@ web-sys = { version = "0.3", features = [
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
uuid = { version = "1.13.1", default-features = false, features = ["js"] }
-bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [
+bevy_app = { path = "../bevy_app", version = "0.17.0-dev", default-features = false, features = [
"web",
] }
-bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false, features = [
+bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev", default-features = false, features = [
"web",
] }
-bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", default-features = false, features = [
+bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", default-features = false, features = [
"web",
] }
diff --git a/crates/bevy_asset/macros/Cargo.toml b/crates/bevy_asset/macros/Cargo.toml
index 43562ae806..0b525b3c1d 100644
--- a/crates/bevy_asset/macros/Cargo.toml
+++ b/crates/bevy_asset/macros/Cargo.toml
@@ -1,9 +1,9 @@
[package]
name = "bevy_asset_macros"
-version = "0.16.0-dev"
+version = "0.17.0-dev"
edition = "2024"
description = "Derive implementations for bevy_asset"
-homepage = "https://bevyengine.org"
+homepage = "https://bevy.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
@@ -12,7 +12,7 @@ keywords = ["bevy"]
proc-macro = true
[dependencies]
-bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.16.0-dev" }
+bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.17.0-dev" }
syn = "2.0"
proc-macro2 = "1.0"
diff --git a/crates/bevy_asset/macros/src/lib.rs b/crates/bevy_asset/macros/src/lib.rs
index 443bd09ab9..a7ea87b752 100644
--- a/crates/bevy_asset/macros/src/lib.rs
+++ b/crates/bevy_asset/macros/src/lib.rs
@@ -1,6 +1,7 @@
-#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
+//! Macros for deriving asset traits.
+
use bevy_macro_utils::BevyManifest;
use proc_macro::{Span, TokenStream};
use quote::{format_ident, quote};
@@ -12,6 +13,7 @@ pub(crate) fn bevy_asset_path() -> Path {
const DEPENDENCY_ATTRIBUTE: &str = "dependency";
+/// Implement the `Asset` trait.
#[proc_macro_derive(Asset, attributes(dependency))]
pub fn derive_asset(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
@@ -30,6 +32,7 @@ pub fn derive_asset(input: TokenStream) -> TokenStream {
})
}
+/// Implement the `VisitAssetDependencies` trait.
#[proc_macro_derive(VisitAssetDependencies, attributes(dependency))]
pub fn derive_asset_dependency_visitor(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
diff --git a/crates/bevy_asset/src/asset_changed.rs b/crates/bevy_asset/src/asset_changed.rs
index 10f298c968..b43a8625e7 100644
--- a/crates/bevy_asset/src/asset_changed.rs
+++ b/crates/bevy_asset/src/asset_changed.rs
@@ -106,7 +106,7 @@ impl<'w, A: AsAssetId> AssetChangeCheck<'w, A> {
/// - Removed assets are not detected.
///
/// The list of changed assets only gets updated in the [`AssetEventSystems`] system set,
-/// which runs in `Last`. Therefore, `AssetChanged` will only pick up asset changes in schedules
+/// which runs in `PostUpdate`. Therefore, `AssetChanged` will only pick up asset changes in schedules
/// following [`AssetEventSystems`] or the next frame. Consider adding the system in the `Last` schedule
/// after [`AssetEventSystems`] if you need to react without frame delay to asset changes.
///
@@ -158,9 +158,9 @@ unsafe impl WorldQuery for AssetChanged {
fetch
}
- unsafe fn init_fetch<'w>(
+ unsafe fn init_fetch<'w, 's>(
world: UnsafeWorldCell<'w>,
- state: &Self::State,
+ state: &'s Self::State,
last_run: Tick,
this_run: Tick,
) -> Self::Fetch<'w> {
@@ -201,9 +201,9 @@ unsafe impl WorldQuery for AssetChanged {
const IS_DENSE: bool = <&A>::IS_DENSE;
- unsafe fn set_archetype<'w>(
+ unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>,
- state: &Self::State,
+ state: &'s Self::State,
archetype: &'w Archetype,
table: &'w Table,
) {
@@ -215,7 +215,11 @@ unsafe impl WorldQuery for AssetChanged {
}
}
- unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) {
+ unsafe fn set_table<'w, 's>(
+ fetch: &mut Self::Fetch<'w>,
+ state: &Self::State,
+ table: &'w Table,
+ ) {
if let Some(inner) = &mut fetch.inner {
// SAFETY: We delegate to the inner `set_table` for `A`
unsafe {
@@ -265,6 +269,7 @@ unsafe impl QueryFilter for AssetChanged {
#[inline]
unsafe fn filter_fetch(
+ state: &Self::State,
fetch: &mut Self::Fetch<'_>,
entity: Entity,
table_row: TableRow,
@@ -272,7 +277,7 @@ unsafe impl QueryFilter for AssetChanged {
fetch.inner.as_mut().is_some_and(|inner| {
// SAFETY: We delegate to the inner `fetch` for `A`
unsafe {
- let handle = <&A>::fetch(inner, entity, table_row);
+ let handle = <&A>::fetch(&state.asset_id, inner, entity, table_row);
fetch.check.has_changed(handle)
}
})
diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs
index 9fa8eb4381..6e5b488ee0 100644
--- a/crates/bevy_asset/src/assets.rs
+++ b/crates/bevy_asset/src/assets.rs
@@ -437,6 +437,18 @@ impl Assets {
result
}
+ /// Retrieves a mutable reference to the [`Asset`] with the given `id`, if it exists.
+ ///
+ /// This is the same as [`Assets::get_mut`] except it doesn't emit [`AssetEvent::Modified`].
+ #[inline]
+ pub fn get_mut_untracked(&mut self, id: impl Into>) -> Option<&mut A> {
+ let id: AssetId = id.into();
+ match id {
+ AssetId::Index { index, .. } => self.dense_storage.get_mut(index),
+ AssetId::Uuid { uuid } => self.hash_map.get_mut(&uuid),
+ }
+ }
+
/// Removes (and returns) the [`Asset`] with the given `id`, if it exists.
/// Note that this supports anything that implements `Into>`, which includes [`Handle`] and [`AssetId`].
pub fn remove(&mut self, id: impl Into>) -> Option {
@@ -450,6 +462,8 @@ impl Assets {
/// Removes (and returns) the [`Asset`] with the given `id`, if it exists. This skips emitting [`AssetEvent::Removed`].
/// Note that this supports anything that implements `Into>`, which includes [`Handle`] and [`AssetId`].
+ ///
+ /// This is the same as [`Assets::remove`] except it doesn't emit [`AssetEvent::Removed`].
pub fn remove_untracked(&mut self, id: impl Into>) -> Option {
let id: AssetId = id.into();
self.duplicate_handles.remove(&id);
diff --git a/crates/bevy_asset/src/direct_access_ext.rs b/crates/bevy_asset/src/direct_access_ext.rs
index 792d523a30..e7e5b993de 100644
--- a/crates/bevy_asset/src/direct_access_ext.rs
+++ b/crates/bevy_asset/src/direct_access_ext.rs
@@ -20,6 +20,7 @@ pub trait DirectAssetAccessExt {
settings: impl Fn(&mut S) + Send + Sync + 'static,
) -> Handle;
}
+
impl DirectAssetAccessExt for World {
/// Insert an asset similarly to [`Assets::add`].
///
diff --git a/crates/bevy_asset/src/event.rs b/crates/bevy_asset/src/event.rs
index 087cb44b5a..42de19fe44 100644
--- a/crates/bevy_asset/src/event.rs
+++ b/crates/bevy_asset/src/event.rs
@@ -1,12 +1,12 @@
use crate::{Asset, AssetId, AssetLoadError, AssetPath, UntypedAssetId};
-use bevy_ecs::event::Event;
+use bevy_ecs::event::{BufferedEvent, Event};
use bevy_reflect::Reflect;
use core::fmt::Debug;
-/// An event emitted when a specific [`Asset`] fails to load.
+/// A [`BufferedEvent`] emitted when a specific [`Asset`] fails to load.
///
/// For an untyped equivalent, see [`UntypedAssetLoadFailedEvent`].
-#[derive(Event, Clone, Debug)]
+#[derive(Event, BufferedEvent, Clone, Debug)]
pub struct AssetLoadFailedEvent {
/// The stable identifier of the asset that failed to load.
pub id: AssetId,
@@ -24,7 +24,7 @@ impl AssetLoadFailedEvent {
}
/// An untyped version of [`AssetLoadFailedEvent`].
-#[derive(Event, Clone, Debug)]
+#[derive(Event, BufferedEvent, Clone, Debug)]
pub struct UntypedAssetLoadFailedEvent {
/// The stable identifier of the asset that failed to load.
pub id: UntypedAssetId,
@@ -44,9 +44,9 @@ impl From<&AssetLoadFailedEvent> for UntypedAssetLoadFailedEvent {
}
}
-/// Events that occur for a specific loaded [`Asset`], such as "value changed" events and "dependency" events.
+/// [`BufferedEvent`]s that occur for a specific loaded [`Asset`], such as "value changed" events and "dependency" events.
#[expect(missing_docs, reason = "Documenting the id fields is unhelpful.")]
-#[derive(Event, Reflect)]
+#[derive(Event, BufferedEvent, Reflect)]
pub enum AssetEvent {
/// Emitted whenever an [`Asset`] is added.
Added { id: AssetId },
diff --git a/crates/bevy_asset/src/io/embedded/embedded_watcher.rs b/crates/bevy_asset/src/io/embedded/embedded_watcher.rs
index f7fb56be74..06a0791a50 100644
--- a/crates/bevy_asset/src/io/embedded/embedded_watcher.rs
+++ b/crates/bevy_asset/src/io/embedded/embedded_watcher.rs
@@ -56,6 +56,7 @@ pub(crate) struct EmbeddedEventHandler {
dir: Dir,
last_event: Option,
}
+
impl FilesystemEventHandler for EmbeddedEventHandler {
fn begin(&mut self) {
self.last_event = None;
diff --git a/crates/bevy_asset/src/io/embedded/mod.rs b/crates/bevy_asset/src/io/embedded/mod.rs
index e63d415342..c49d55ca4a 100644
--- a/crates/bevy_asset/src/io/embedded/mod.rs
+++ b/crates/bevy_asset/src/io/embedded/mod.rs
@@ -8,8 +8,10 @@ use crate::io::{
memory::{Dir, MemoryAssetReader, Value},
AssetSource, AssetSourceBuilders,
};
+use crate::AssetServer;
use alloc::boxed::Box;
-use bevy_ecs::resource::Resource;
+use bevy_app::App;
+use bevy_ecs::{resource::Resource, world::World};
use std::path::{Path, PathBuf};
#[cfg(feature = "embedded_watcher")]
@@ -132,6 +134,74 @@ impl EmbeddedAssetRegistry {
}
}
+/// Trait for the [`load_embedded_asset!`] macro, to access [`AssetServer`]
+/// from arbitrary things.
+///
+/// [`load_embedded_asset!`]: crate::load_embedded_asset
+pub trait GetAssetServer {
+ fn get_asset_server(&self) -> &AssetServer;
+}
+
+impl GetAssetServer for App {
+ fn get_asset_server(&self) -> &AssetServer {
+ self.world().get_asset_server()
+ }
+}
+
+impl GetAssetServer for World {
+ fn get_asset_server(&self) -> &AssetServer {
+ self.resource()
+ }
+}
+
+impl GetAssetServer for AssetServer {
+ fn get_asset_server(&self) -> &AssetServer {
+ self
+ }
+}
+
+/// Load an [embedded asset](crate::embedded_asset).
+///
+/// This is useful if the embedded asset in question is not publicly exposed, but
+/// you need to use it internally.
+///
+/// # Syntax
+///
+/// This macro takes two arguments and an optional third one:
+/// 1. The asset source. It may be `AssetServer`, `World` or `App`.
+/// 2. The path to the asset to embed, as a string literal.
+/// 3. Optionally, a closure of the same type as in [`AssetServer::load_with_settings`].
+/// Consider explicitly typing the closure argument in case of type error.
+///
+/// # Usage
+///
+/// The advantage compared to using directly [`AssetServer::load`] is:
+/// - This also accepts [`World`] and [`App`] arguments.
+/// - This uses the exact same path as `embedded_asset!`, so you can keep it
+/// consistent.
+///
+/// As a rule of thumb:
+/// - If the asset in used in the same module as it is declared using `embedded_asset!`,
+/// use this macro.
+/// - Otherwise, use `AssetServer::load`.
+#[macro_export]
+macro_rules! load_embedded_asset {
+ (@get: $path: literal, $provider: expr) => {{
+ let path = $crate::embedded_path!($path);
+ let path = $crate::AssetPath::from_path_buf(path).with_source("embedded");
+ let asset_server = $crate::io::embedded::GetAssetServer::get_asset_server($provider);
+ (path, asset_server)
+ }};
+ ($provider: expr, $path: literal, $settings: expr) => {{
+ let (path, asset_server) = $crate::load_embedded_asset!(@get: $path, $provider);
+ asset_server.load_with_settings(path, $settings)
+ }};
+ ($provider: expr, $path: literal) => {{
+ let (path, asset_server) = $crate::load_embedded_asset!(@get: $path, $provider);
+ asset_server.load(path)
+ }};
+}
+
/// Returns the [`Path`] for a given `embedded` asset.
/// This is used internally by [`embedded_asset`] and can be used to get a [`Path`]
/// that matches the [`AssetPath`](crate::AssetPath) used by that asset.
@@ -140,7 +210,7 @@ impl EmbeddedAssetRegistry {
#[macro_export]
macro_rules! embedded_path {
($path_str: expr) => {{
- embedded_path!("src", $path_str)
+ $crate::embedded_path!("src", $path_str)
}};
($source_path: expr, $path_str: expr) => {{
@@ -192,7 +262,7 @@ pub fn _embedded_asset_path(
/// Creates a new `embedded` asset by embedding the bytes of the given path into the current binary
/// and registering those bytes with the `embedded` [`AssetSource`].
///
-/// This accepts the current [`App`](bevy_app::App) as the first parameter and a path `&str` (relative to the current file) as the second.
+/// This accepts the current [`App`] as the first parameter and a path `&str` (relative to the current file) as the second.
///
/// By default this will generate an [`AssetPath`] using the following rules:
///
@@ -217,14 +287,19 @@ pub fn _embedded_asset_path(
///
/// `embedded_asset!(app, "rock.wgsl")`
///
-/// `rock.wgsl` can now be loaded by the [`AssetServer`](crate::AssetServer) with the following path:
+/// `rock.wgsl` can now be loaded by the [`AssetServer`] as follows:
///
/// ```no_run
-/// # use bevy_asset::{Asset, AssetServer};
+/// # use bevy_asset::{Asset, AssetServer, load_embedded_asset};
/// # use bevy_reflect::TypePath;
/// # let asset_server: AssetServer = panic!();
/// # #[derive(Asset, TypePath)]
/// # struct Shader;
+/// // If we are loading the shader in the same module we used `embedded_asset!`:
+/// let shader = load_embedded_asset!(&asset_server, "rock.wgsl");
+/// # let _: bevy_asset::Handle = shader;
+///
+/// // If the goal is to expose the asset **to the end user**:
/// let shader = asset_server.load::("embedded://bevy_rock/render/rock.wgsl");
/// ```
///
@@ -258,11 +333,11 @@ pub fn _embedded_asset_path(
/// [`embedded_path`]: crate::embedded_path
#[macro_export]
macro_rules! embedded_asset {
- ($app: ident, $path: expr) => {{
+ ($app: expr, $path: expr) => {{
$crate::embedded_asset!($app, "src", $path)
}};
- ($app: ident, $source_path: expr, $path: expr) => {{
+ ($app: expr, $source_path: expr, $path: expr) => {{
let mut embedded = $app
.world_mut()
.resource_mut::<$crate::io::embedded::EmbeddedAssetRegistry>();
diff --git a/crates/bevy_asset/src/io/wasm.rs b/crates/bevy_asset/src/io/wasm.rs
index c2551a40f1..4080e03ecd 100644
--- a/crates/bevy_asset/src/io/wasm.rs
+++ b/crates/bevy_asset/src/io/wasm.rs
@@ -81,7 +81,10 @@ impl HttpWasmAssetReader {
let reader = VecReader::new(bytes);
Ok(reader)
}
- 404 => Err(AssetReaderError::NotFound(path)),
+ // Some web servers, including itch.io's CDN, return 403 when a requested file isn't present.
+ // TODO: remove handling of 403 as not found when it's easier to configure
+ // see https://github.com/bevyengine/bevy/pull/19268#pullrequestreview-2882410105
+ 403 | 404 => Err(AssetReaderError::NotFound(path)),
status => Err(AssetReaderError::HttpError(status)),
}
}
diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs
index 5b680eb191..4b29beae79 100644
--- a/crates/bevy_asset/src/lib.rs
+++ b/crates/bevy_asset/src/lib.rs
@@ -141,8 +141,8 @@
#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
- html_logo_url = "https://bevyengine.org/assets/icon.png",
- html_favicon_url = "https://bevyengine.org/assets/icon.png"
+ html_logo_url = "https://bevy.org/assets/icon.png",
+ html_favicon_url = "https://bevy.org/assets/icon.png"
)]
#![no_std]
diff --git a/crates/bevy_asset/src/loader.rs b/crates/bevy_asset/src/loader.rs
index 8f4863b885..24405f0657 100644
--- a/crates/bevy_asset/src/loader.rs
+++ b/crates/bevy_asset/src/loader.rs
@@ -12,7 +12,7 @@ use alloc::{
vec::Vec,
};
use atomicow::CowArc;
-use bevy_ecs::world::World;
+use bevy_ecs::{error::BevyError, world::World};
use bevy_platform::collections::{HashMap, HashSet};
use bevy_tasks::{BoxedFuture, ConditionalSendFuture};
use core::any::{Any, TypeId};
@@ -34,7 +34,7 @@ pub trait AssetLoader: Send + Sync + 'static {
/// The settings type used by this [`AssetLoader`].
type Settings: Settings + Default + Serialize + for<'a> Deserialize<'a>;
/// The type of [error](`std::error::Error`) which could be encountered by this loader.
- type Error: Into>;
+ type Error: Into;
/// Asynchronously loads [`AssetLoader::Asset`] (and any other labeled assets) from the bytes provided by [`Reader`].
fn load(
&self,
@@ -58,10 +58,7 @@ pub trait ErasedAssetLoader: Send + Sync + 'static {
reader: &'a mut dyn Reader,
meta: &'a dyn AssetMetaDyn,
load_context: LoadContext<'a>,
- ) -> BoxedFuture<
- 'a,
- Result>,
- >;
+ ) -> BoxedFuture<'a, Result>;
/// Returns a list of extensions supported by this asset loader, without the preceding dot.
fn extensions(&self) -> &[&str];
@@ -89,10 +86,7 @@ where
reader: &'a mut dyn Reader,
meta: &'a dyn AssetMetaDyn,
mut load_context: LoadContext<'a>,
- ) -> BoxedFuture<
- 'a,
- Result>,
- > {
+ ) -> BoxedFuture<'a, Result> {
Box::pin(async move {
let settings = meta
.loader_settings()
@@ -350,7 +344,7 @@ impl<'a> LoadContext<'a> {
/// Begins a new labeled asset load. Use the returned [`LoadContext`] to load
/// dependencies for the new asset and call [`LoadContext::finish`] to finalize the asset load.
- /// When finished, make sure you call [`LoadContext::add_labeled_asset`] to add the results back to the parent
+ /// When finished, make sure you call [`LoadContext::add_loaded_labeled_asset`] to add the results back to the parent
/// context.
/// Prefer [`LoadContext::labeled_asset_scope`] when possible, which will automatically add
/// the labeled [`LoadContext`] back to the parent context.
@@ -366,7 +360,7 @@ impl<'a> LoadContext<'a> {
/// # let load_context: LoadContext = panic!();
/// let mut handles = Vec::new();
/// for i in 0..2 {
- /// let mut labeled = load_context.begin_labeled_asset();
+ /// let labeled = load_context.begin_labeled_asset();
/// handles.push(std::thread::spawn(move || {
/// (i.to_string(), labeled.finish(Image::default()))
/// }));
@@ -391,18 +385,18 @@ impl<'a> LoadContext<'a> {
/// [`LoadedAsset`], which is registered under the `label` label.
///
/// This exists to remove the need to manually call [`LoadContext::begin_labeled_asset`] and then manually register the
- /// result with [`LoadContext::add_labeled_asset`].
+ /// result with [`LoadContext::add_loaded_labeled_asset`].
///
/// See [`AssetPath`] for more on labeled assets.
- pub fn labeled_asset_scope(
+ pub fn labeled_asset_scope(
&mut self,
label: String,
- load: impl FnOnce(&mut LoadContext) -> A,
- ) -> Handle {
+ load: impl FnOnce(&mut LoadContext) -> Result,
+ ) -> Result, E> {
let mut context = self.begin_labeled_asset();
- let asset = load(&mut context);
+ let asset = load(&mut context)?;
let loaded_asset = context.finish(asset);
- self.add_loaded_labeled_asset(label, loaded_asset)
+ Ok(self.add_loaded_labeled_asset(label, loaded_asset))
}
/// This will add the given `asset` as a "labeled [`Asset`]" with the `label` label.
@@ -416,7 +410,8 @@ impl<'a> LoadContext<'a> {
///
/// See [`AssetPath`] for more on labeled assets.
pub fn add_labeled_asset(&mut self, label: String, asset: A) -> Handle {
- self.labeled_asset_scope(label, |_| asset)
+ self.labeled_asset_scope(label, |_| Ok::<_, ()>(asset))
+ .expect("the closure returns Ok")
}
/// Add a [`LoadedAsset`] that is a "labeled sub asset" of the root path of this load context.
diff --git a/crates/bevy_asset/src/path.rs b/crates/bevy_asset/src/path.rs
index ad127812dc..3f780e3fb7 100644
--- a/crates/bevy_asset/src/path.rs
+++ b/crates/bevy_asset/src/path.rs
@@ -223,6 +223,16 @@ impl<'a> AssetPath<'a> {
Ok((source, path, label))
}
+ /// Creates a new [`AssetPath`] from a [`PathBuf`].
+ #[inline]
+ pub fn from_path_buf(path_buf: PathBuf) -> AssetPath<'a> {
+ AssetPath {
+ path: CowArc::Owned(path_buf.into()),
+ source: AssetSourceId::Default,
+ label: None,
+ }
+ }
+
/// Creates a new [`AssetPath`] from a [`Path`].
#[inline]
pub fn from_path(path: &'a Path) -> AssetPath<'a> {
@@ -480,7 +490,7 @@ impl<'a> AssetPath<'a> {
}
/// Returns `true` if this [`AssetPath`] points to a file that is
- /// outside of it's [`AssetSource`](crate::io::AssetSource) folder.
+ /// outside of its [`AssetSource`](crate::io::AssetSource) folder.
///
/// ## Example
/// ```
diff --git a/crates/bevy_asset/src/reflect.rs b/crates/bevy_asset/src/reflect.rs
index 5c436c1061..a3148cecb7 100644
--- a/crates/bevy_asset/src/reflect.rs
+++ b/crates/bevy_asset/src/reflect.rs
@@ -18,16 +18,16 @@ pub struct ReflectAsset {
handle_type_id: TypeId,
assets_resource_type_id: TypeId,
- get: fn(&World, UntypedHandle) -> Option<&dyn Reflect>,
+ get: fn(&World, UntypedAssetId) -> Option<&dyn Reflect>,
// SAFETY:
// - may only be called with an [`UnsafeWorldCell`] which can be used to access the corresponding `Assets` resource mutably
// - may only be used to access **at most one** access at once
- get_unchecked_mut: unsafe fn(UnsafeWorldCell<'_>, UntypedHandle) -> Option<&mut dyn Reflect>,
+ get_unchecked_mut: unsafe fn(UnsafeWorldCell<'_>, UntypedAssetId) -> Option<&mut dyn Reflect>,
add: fn(&mut World, &dyn PartialReflect) -> UntypedHandle,
- insert: fn(&mut World, UntypedHandle, &dyn PartialReflect),
+ insert: fn(&mut World, UntypedAssetId, &dyn PartialReflect),
len: fn(&World) -> usize,
ids: for<'w> fn(&'w World) -> Box + 'w>,
- remove: fn(&mut World, UntypedHandle) -> Option>,
+ remove: fn(&mut World, UntypedAssetId) -> Option>,
}
impl ReflectAsset {
@@ -42,15 +42,19 @@ impl ReflectAsset {
}
/// Equivalent of [`Assets::get`]
- pub fn get<'w>(&self, world: &'w World, handle: UntypedHandle) -> Option<&'w dyn Reflect> {
- (self.get)(world, handle)
+ pub fn get<'w>(
+ &self,
+ world: &'w World,
+ asset_id: impl Into,
+ ) -> Option<&'w dyn Reflect> {
+ (self.get)(world, asset_id.into())
}
/// Equivalent of [`Assets::get_mut`]
pub fn get_mut<'w>(
&self,
world: &'w mut World,
- handle: UntypedHandle,
+ asset_id: impl Into,
) -> Option<&'w mut dyn Reflect> {
// SAFETY: unique world access
#[expect(
@@ -58,7 +62,7 @@ impl ReflectAsset {
reason = "Use of unsafe `Self::get_unchecked_mut()` function."
)]
unsafe {
- (self.get_unchecked_mut)(world.as_unsafe_world_cell(), handle)
+ (self.get_unchecked_mut)(world.as_unsafe_world_cell(), asset_id.into())
}
}
@@ -76,8 +80,8 @@ impl ReflectAsset {
/// # let handle_1: UntypedHandle = unimplemented!();
/// # let handle_2: UntypedHandle = unimplemented!();
/// let unsafe_world_cell = world.as_unsafe_world_cell();
- /// let a = unsafe { reflect_asset.get_unchecked_mut(unsafe_world_cell, handle_1).unwrap() };
- /// let b = unsafe { reflect_asset.get_unchecked_mut(unsafe_world_cell, handle_2).unwrap() };
+ /// let a = unsafe { reflect_asset.get_unchecked_mut(unsafe_world_cell, &handle_1).unwrap() };
+ /// let b = unsafe { reflect_asset.get_unchecked_mut(unsafe_world_cell, &handle_2).unwrap() };
/// // ^ not allowed, two mutable references through the same asset resource, even though the
/// // handles are distinct
///
@@ -96,10 +100,10 @@ impl ReflectAsset {
pub unsafe fn get_unchecked_mut<'w>(
&self,
world: UnsafeWorldCell<'w>,
- handle: UntypedHandle,
+ asset_id: impl Into,
) -> Option<&'w mut dyn Reflect> {
// SAFETY: requirements are deferred to the caller
- unsafe { (self.get_unchecked_mut)(world, handle) }
+ unsafe { (self.get_unchecked_mut)(world, asset_id.into()) }
}
/// Equivalent of [`Assets::add`]
@@ -107,13 +111,22 @@ impl ReflectAsset {
(self.add)(world, value)
}
/// Equivalent of [`Assets::insert`]
- pub fn insert(&self, world: &mut World, handle: UntypedHandle, value: &dyn PartialReflect) {
- (self.insert)(world, handle, value);
+ pub fn insert(
+ &self,
+ world: &mut World,
+ asset_id: impl Into,
+ value: &dyn PartialReflect,
+ ) {
+ (self.insert)(world, asset_id.into(), value);
}
/// Equivalent of [`Assets::remove`]
- pub fn remove(&self, world: &mut World, handle: UntypedHandle) -> Option> {
- (self.remove)(world, handle)
+ pub fn remove(
+ &self,
+ world: &mut World,
+ asset_id: impl Into,
+ ) -> Option> {
+ (self.remove)(world, asset_id.into())
}
/// Equivalent of [`Assets::len`]
@@ -137,17 +150,17 @@ impl FromType for ReflectAsset {
ReflectAsset {
handle_type_id: TypeId::of::>(),
assets_resource_type_id: TypeId::of::>(),
- get: |world, handle| {
+ get: |world, asset_id| {
let assets = world.resource::>();
- let asset = assets.get(&handle.typed_debug_checked());
+ let asset = assets.get(asset_id.typed_debug_checked());
asset.map(|asset| asset as &dyn Reflect)
},
- get_unchecked_mut: |world, handle| {
+ get_unchecked_mut: |world, asset_id| {
// SAFETY: `get_unchecked_mut` must be called with `UnsafeWorldCell` having access to `Assets`,
// and must ensure to only have at most one reference to it live at all times.
#[expect(unsafe_code, reason = "Uses `UnsafeWorldCell::get_resource_mut()`.")]
let assets = unsafe { world.get_resource_mut::>().unwrap().into_inner() };
- let asset = assets.get_mut(&handle.typed_debug_checked());
+ let asset = assets.get_mut(asset_id.typed_debug_checked());
asset.map(|asset| asset as &mut dyn Reflect)
},
add: |world, value| {
@@ -156,11 +169,11 @@ impl FromType for ReflectAsset {
.expect("could not call `FromReflect::from_reflect` in `ReflectAsset::add`");
assets.add(value).untyped()
},
- insert: |world, handle, value| {
+ insert: |world, asset_id, value| {
let mut assets = world.resource_mut::>();
let value: A = FromReflect::from_reflect(value)
.expect("could not call `FromReflect::from_reflect` in `ReflectAsset::set`");
- assets.insert(&handle.typed_debug_checked(), value);
+ assets.insert(asset_id.typed_debug_checked(), value);
},
len: |world| {
let assets = world.resource::>();
@@ -170,9 +183,9 @@ impl FromType for ReflectAsset {
let assets = world.resource::>();
Box::new(assets.ids().map(AssetId::untyped))
},
- remove: |world, handle| {
+ remove: |world, asset_id| {
let mut assets = world.resource_mut::>();
- let value = assets.remove(&handle.typed_debug_checked());
+ let value = assets.remove(asset_id.typed_debug_checked());
value.map(|value| Box::new(value) as Box)
},
}
@@ -200,7 +213,7 @@ impl FromType